# About Injective
Source: https://docs.injective.network/about
Injective is a high-performance, interoperable layer-one blockchain for building premier Web3 financial applications. Learn what makes Injective different, how performant it is, frequently asked questions, what you can do, how to get started, and where to get help.
## What is Injective?
Injective is the **blockchain** built for **finance**.
## What makes Injective different?
Injective offers a fully on-chain orderbook exchange,
MEV-resistant transaction ordering via frequent batch auctions (FBA),
cross-chain interoperability with 110+ IBC networks and Ethereum,
EVM smart contracts,
and generative AI and agentic builder tools.
Injective is the only blockchain where developers can use pre-built, customizable
[modules](./developers-native/) to create dynamic applications that aren't possible on other networks.
Combined with optimizations to its core architecture and enhanced cross-chain interoperability,
Injective offers a high-performance network,
ready to efficiently and securely bring the global financial system on-chain.
## How performant is Injective?
Injective is highly scalable too:
| | |
| ----------------------------------- | ----------------------------------------- |
| Throughput, sustained | 25,000 tx/s |
| Throughput, theoretical upper bound | 600,000 tx/s |
| Block time | 650ms |
| Transaction finality | Instant, via custom Tendermint (CometBFT) |
| Standard transaction fee | \~ \$0.0003 |
| Average transaction fee | \< \$0.01 |
The above stats are accurate as of April 2026.
Note that they may change based on market movements and usage.
## Frequently asked questions about Injective
More questions about Injective?
Check out the [FAQ page](/faq/).
## What can you do on Injective?
If you are a blockchain user, DeFi trader, infrastructure provider, developer,
or simply interested in joining the community, we have you covered:
Learn how to create a wallet
Learn how to start trading on Injective
Learn how to run sentry and validator nodes
Learn how to build on Injective
Join the Injective Discord
Join the Developer Telegram
## How to get started on Injective?
Welcome to your journey on Injective!
Use the search functionality to find what you need.
Our goal is to make the documentation self-sufficient!
If you are unable to find what you are after, get in touch with us.
Learn how to create a wallet on Injective and see the supported wallets on Injective
Learn about different Token Standards on Injective
Learn how to prepare, sign and submit transactions on Injective
## Need help building on Injective or using Injective?
Reach out to us on Discord or Telegram:
* Join the [Injective Discord server](https://discord.gg/injective), and find the relevant channel.
* Join the [Injective Developer Telegram channel](https://t.me/+8Y_0HOFLhnRlZDU9).
# Add Network to Your Wallet
Source: https://docs.injective.network/add-network-to-wallet
Add Injective networks to your browser wallet in one click. Interactive wallet buttons for Injective EVM (MetaMask and other EVM wallets) and Injective native (Keplr, Leap), covering mainnet and testnet.
Add Injective to your browser wallet with a single click, or use the manual
configuration details below. Injective exposes two interoperable interfaces:
* **Injective EVM** is an Ethereum-compatible JSON-RPC layer. Use it with EVM
wallets such as MetaMask, Rabby, or any injected `window.ethereum` wallet.
* **Injective (CosmWasm / native)** is the native Cosmos SDK chain. Use it with
Cosmos wallets such as Keplr and Leap.
Injective EVM and Injective native map to the same network. They use different
chain IDs but share the same underlying state. See
[Network Information](/developers/network-information) and
[EVM Network Information](/developers-evm/network-information) for the full reference.
## Injective EVM
Injective EVM works with EVM wallets like MetaMask, Rabby, and SubWallet. Pick a
network and click your wallet — it will prompt you to add or switch to Injective.
Injective EVM MainnetChain ID 1776
Injective EVM TestnetChain ID 1439
The button uses the standard `wallet_addEthereumChain` request. If the network
is already added, your wallet shows a switch prompt instead. If nothing happens,
add it manually with the values below.
### Manual configuration
| Property | Value |
| --------------- | ------------------------------------------- |
| Network Name | Injective |
| Chain ID | `1776` |
| RPC URL | `https://sentry.evm-rpc.injective.network/` |
| WS Endpoint | `wss://sentry.evm-ws.injective.network` |
| Currency Symbol | `INJ` |
| Decimals | `18` |
| Block Explorer | `https://blockscout.injective.network/` |
| Property | Value |
| --------------- | ------------------------------------------------------------------------------- |
| Network Name | Injective Testnet |
| Chain ID | `1439` |
| RPC URL | `https://k8s.testnet.json-rpc.injective.network/` |
| WS Endpoint | `wss://k8s.testnet.ws.injective.network/` |
| Currency Symbol | `INJ` |
| Decimals | `18` |
| Block Explorer | `https://testnet.blockscout.injective.network/` |
| Faucet | [`testnet.faucet.injective.network`](https://testnet.faucet.injective.network/) |
## Injective (CosmWasm / Native)
The native Injective chain works with Cosmos wallets like Keplr and Leap.
Injective ships built into both wallets, so it is available out of the box.
Pick a network and click your wallet to unlock the chain.
Injective MainnetChain ID injective-1
Injective TestnetChain ID injective-888
Keplr and Leap include Injective by default, so the button simply unlocks the
chain in your wallet. For a full wallet integration guide, see
[Wallets](/defi/wallet/index).
### Manual configuration
| Property | Value |
| --------------- | ----------------------------------------- |
| Chain ID | `injective-1` |
| RPC / Node | `https://sentry.tm.injective.network:443` |
| Currency Symbol | `INJ` |
| Decimals | `18` |
| Explorer | [`injscan.com`](https://injscan.com/) |
Injective natively uses the chain ID `injective-1`. Its EVM interface uses the
numeric chain ID `1776`. Both map to the same mainnet.
| Property | Value |
| --------------- | ----------------------------------------------------------------------------------- |
| Chain ID | `injective-888` |
| RPC / Node | `https://testnet.sentry.tm.injective.network:443` |
| Currency Symbol | `INJ` |
| Decimals | `18` |
| Explorer | [`testnet.explorer.injective.network`](https://testnet.explorer.injective.network/) |
| Faucet | [`testnet.faucet.injective.network`](https://testnet.faucet.injective.network/) |
Injective natively uses the chain ID `injective-888` on testnet. Its EVM
interface uses the numeric chain ID `1439`. Both map to the same testnet.
# Bridge
Source: https://docs.injective.network/defi/bridge
How to bridge assets to and from Injective via IBC, the Injective Bridge, and supported networks including Ethereum and Solana.
Bridging assets into the Injective ecosystem is effortless, with support for 23+ networks and growing.
Here, you'll find step-by-step instructional guides for bridging assets from Ethereum, Solana, via IBC,
and using the Wormhole protocol, making it easy to connect with Injective's ecosystem.
# Community Buyback
Source: https://docs.injective.network/defi/community-buyback
### What is the Community Buyback?
The Community Buyback is a monthly onchain event that allows anyone to take part in Injective’s deflationary mechanism. Participants commit INJ, and in return receive a pro rata share of the revenue generated across the Injective ecosystem. The INJ exchanged is then permanently burned, reducing the total supply.
This process rewards the community, increases scarcity of INJ, and aligns long-term value with ecosystem success. The Community Buyback evolved from the original Burn Auction, replacing the winner-take-all model with a simpler, more accessible, and community-driven design.
### Key Details at a Glance
A quick reference box (almost like a cheat sheet) with the most important parameters:
* Cadence: 28 days
* Slots: Fixed number
* Funding Source: Portion of ecosystem-generated revenue
* Commitment: INJ only, within posted min/max limits
* Distribution: Pro rata, based on INJ commitment
* Outcome: INJ burned + assets distributed to participants
* Transparency: Fully onchain
### Why It Matters
* Deflationary Design – Each round burns INJ, reducing total supply
* Democratized Access – Shared opportunity vs. one auction winner
* Aligned Incentives – Rewards scale with ecosystem activity, rather than network congestion (txn fees stay low regardless)
* Onchain Proof – Everything is transparent and verifiable
### How It Works
1. **Secure Your Spot**\
Once per month, a fixed number of reservation slots open. Anyone can reserve a slot while they remain available.
2. **Commit INJ**\
Reserve your slot by committing INJ within the displayed minimum and maximum limits. Commitments are made onchain during the event window and cannot be altered once submitted.
3. **Claim Earnings**\
When the event concludes, all INJ commitments are exchanged for the revenue collected that month. This revenue, made up of multiple different tokens, is distributed pro rata to all participants.\
\
To claim, simply go to Injective Hub → Community Buyback, and press Claim next to the round you participated in under Burn History.
4. **INJ Buyback**\
The total INJ collected from all participants is permanently burned, reducing the total supply of INJ.
5. **Get Ready for Next Month**\
Track your earnings, monitor stats from previous rounds, and stay updated on the next Community Buyback directly from your Injective Hub dashboard. All slot reservations, commitments, burn transactions, and distributions are publicly visible onchain.
### FAQ
**What determines my share?**\
Your share equals your committed INJ divided by the total INJ committed across all participants (pro rata).
**Can I withdraw or change my commitment once made?**\
No, commitments are final once submitted.
**How do I know the burn happened?**\
The burn transaction is onchain and can be tracked via the [Injective Hub](https://injhub.com/community-burn)
**What if I miss a round?**\
You’ll need to wait until next month’s event opens.
# Governance
Source: https://docs.injective.network/defi/governance
How on-chain governance works on Injective, including proposal submission, voting thresholds (quorum and approval), and parameter change procedures.
## Governance
Injective is a community-run blockchain,
and users who have staked INJ can participate in governance as it relates to the blockchain.
Proposals can be submitted to make revisions to Injective programs, tech upgrades,
or any other Injective related changes that impact the entire Injective ecosystem.
Within Injective Hub, new proposals are submitted through the governance portal and voted on by the entire community.
### Submitting a request
To submit a new request, you must create a proposal that outlines what you would like to change,
what areas within Injective this change may impact, and why you are requesting this change.
Upon creating a proposal, you must deposit at least 10 INJ (10e19 inj) to submit it for governance.
This is to ensure that you are an active participant of the Injective community, and thus eligible to make proposal requests.
For the proposal to pass to the voting stage, 100 INJ (10e20 inj) must also be deposited.
This can be deposited directly by you or in collaboration with other community members.
### Voting Period
Once the proposal and deposit is submitted, the proposal will go through a four-day voting period.
33.4% of INJ holders must vote on a proposal for the vote to be considered,
and 50% of those votes must be “yes” for the proposal to pass.
### Voting Outcome
If a proposal passes, Injective contributors will begin to work together to put into effect the new request.
If 33.4% of the total vote is `NoWithVeto`, quorum is not met, or the minimum deposit isn't reached,
the deposit will be burned.
All other vote outcomes get their deposits refunded.
### Get Started
* For more details on the governance proposal process, read Injective's
[blog](https://injective.com/blog/injective-governance-proposal-procedure/).
* To participate in blockchain governance, join the [Injective Discord](https://discord.com/invite/NK4qdbv)
or the [Injective Governance Forum](https://gov.injective.network).
* Visit the [Injective Hub](https://injhub.com/governance) to view Injective proposals and participate in voting.
# Overview
Source: https://docs.injective.network/defi/index
Overview of decentralized finance features on Injective for developers and traders, including trading instruments, staking, governance, and token standards
**Interact with DeFi on Injective:** Use the [Injective Hub](https://hub.injective.network) to stake, govern, and bridge assets. Use the [Injective Explorer](https://explorer.injective.network) to view transactions, blocks, validators, and markets on-chain.
This section provides comprehensive documentation for developers, traders, and
market makers building and participating in Injective's decentralized finance ecosystem.
Whether you're integrating wallets, creating trading strategies, launching tokens, or providing liquidity,
you'll find the technical specifications, guides, and resources needed to interact with
Injective's on-chain exchange module and financial primitives.
Injective is purpose-built for finance, offering the world's first truly on-chain orderbook environment that
brings shared liquidity across all DeFi applications.
Unlike traditional DEXs that rely on automated market makers (AMMs),
Injective uses a fully decentralized central limit orderbook (CLOB) that achieves
capital-efficient price discovery and instant finality.
With sub-second block times, zero gas fees for makers, and cross-chain interoperability via IBC and Ethereum bridges,
Injective eliminates the trade-offs between performance and decentralization,
enabling sophisticated trading strategies and institutional-grade financial applications that were previously impossible in DeFi.
## Key Concepts
| Term | Definition |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Exchange Module** | Injective's core on-chain orderbook module that enables fully decentralized trading of spot and derivative markets with shared liquidity across all dApps. |
| **Denom** | The unique identifier for assets on Injective (e.g., `inj`, `peggy{ERC20}`, `ibc/{hash}`), used within the Bank module for transfers, trading, and staking. |
| **Open Liquidity Program (OLP)** | Injective's market-maker incentive program that rewards participants with INJ for providing orderbook liquidity, with minimal barriers to entry. |
| **Token Factory** | A permissionless module allowing any account to create new native tokens with the format `factory/{creator}/{subdenom}`, enabling custom asset issuance. |
| **EIP-712** | The Ethereum signing standard used by Injective to enable seamless transaction signing with Ethereum wallets like MetaMask while broadcasting to the Cosmos chain. |
## Navigation
* **[Wallet](/defi/wallet/)** —
Learn about supported wallet types including Ethereum-native (MetaMask, Ledger), Cosmos-native (Keplr, Leap),
and Injective-native wallets, plus how account management and transaction signing work on Injective.
* **[Trading](/defi/trading/)** — Explore Injective's on-chain exchange capabilities including spot markets, perpetual futures,
expiry futures, margin trading, order types, and the fee structure for makers and takers.
* **[Token Standards](/defi/tokens/)** — Understand the different token types on Injective including native INJ,
Peggy assets from Ethereum, IBC assets from Cosmos chains, CW20 tokens,
and how to create custom tokens using the Token Factory module.
* **[Open Liquidity Program (OLP)](/defi/open-liquidity-program/)** —
Discover how to earn INJ rewards by providing liquidity to Injective's orderbook, including eligibility requirements,
scoring methodology, epochs, and reward disbursements.
# Staking
Source: https://docs.injective.network/defi/staking
How to stake INJ, delegate to validators, and earn staking rewards on the Injective network.
### What is Staking?
Staking is the process of locking up your assets (in this case INJ) in order to validate transactions on the blockchain.
Users who stake their assets are usually eligible for staking rewards.
Injective follows a proof-of-stake mechanism, in which users can stake their INJ tokens with certain Injective nodes
that validate transactions (validators). In return, the user can obtain staking rewards in INJ.
### Stake INJ
[Stake](https://injhub.com/stake) your INJ to a validator to start earning rewards.
### Withdraw staking rewards
Rewards start accruing the moment you stake INJ. Monitor your rewards in the staking section of
[Injective Hub](https://injhub.com/stake). Once you have earned a sufficient amount of rewards,
you may withdraw them at any time.
### Redelegate
Redelegating lets you instantly transfer staked INJ from one validator to another without undergoing the
21-day unstaking period.
### Undelegate
Undelegating INJ is the process of unstaking your assets from a validator, which takes 21 days to complete.
# CW20 Standard
Source: https://docs.injective.network/defi/tokens/cw20-standard
The CW20 token standard provides a framework for the permissionless creation and management of fungible tokens that more closely resembles the [ERC20 standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/). As stated above, the TokenFactory is encouraged due to its native integration with the Cosmos SDK, but should you wish to use the CW20 standard for any reason, you can convert CW20 tokens to TokenFactory tokens and vice versa using the [CW20 Adapter](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md). For more information regarding the CW20 standard, see its formal specification [here](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md).
# Token Standards
Source: https://docs.injective.network/defi/tokens/index
Injective provides a variety of different token standards one can use when creating a dApp. In this document, we will cover the different types of tokens, as well as recommendations and guidance for using each.
## Denom
A denom is how assets are represented within the Bank module of Injective. These assets can be used for trading, creating new markets on the exchange module, participating in auctions, transferring to another address, etc.
Depending on the origin of the denom and how it was created on Injective, we have different types of denoms:
* **Native denoms** - there is only one denom of this type, the `inj` denom which represented the native coin of Injective,
* **Peggy denoms** - these denoms represent assets bridged over from Ethereum to Injective using the Peggy bridge. They have the following format `peggy{ERC20_CONTRACT_ADDRESS}`
* **IBC denoms** - these denoms represent assets bridged over from other IBC compatible chains through IBC. They have the following format `ibc/{hash}`.
* **Insurance Fund Denoms** - these denoms represent token shares of the insurance funds created on Injective. They have the following format `share{id}`
* **Factory Denoms** - these `tokenfactory` denoms allows any account to create a new token with the name `factory/{creator address}/{subdenom}`. Because tokens are namespaced by creator address, this allows token minting to be permissionless, due to not needing to resolve name collisions. A special use case for these denoms is representing a CW20 token from Cosmwasm on the Injective native bank module. They have the following format `factory/{CW20_ADAPTER_CONTRACT}/{CW20_CONTRACT_ADDRESS}` where the `CW20_ADAPTER_CONTRACT` is the adapter contract address that converts CW20 and the native Bank module.
We'll share more details about these denom types later on in this document.
Learn how to get [denom metadata](/developers/assets/token-metadata/).
# INJ coin
Source: https://docs.injective.network/defi/tokens/inj-coin
The native INJ token: its utility, staking mechanics, and role in Injective's fee and governance model.
INJ is the native asset powering Injective and its broader ecosystem.
Each component of INJ is deliberately engineered to cultivate a thriving Web3 ecosystem.
As the native asset of the blockchain, INJ plays a central role in facilitating various operations on Injective.
Integral to Injective’s custom implementation of the Tendermint Proof-of-Stake (PoS) consensus framework,
INJ is crucial for securing the network through staking.
Additionally, INJ functions as Injective’s governance token and serves as a means of exchange
within the broader Injective ecosystem.
Notably, INJ distinguishes itself from other native assets on PoS chains by leveraging core Injective modules
to engineer deflationary characteristics through an innovative burn and a dynamic supply mechanism.
### Base Denomination
INJ uses [Atto](https://en.wikipedia.org/wiki/Atto-) as the base denomination to maintain parity with Ethereum.
```
1 inj = 1×10⁻¹⁸ INJ
```
This matches Ethereum's denomination:
```
1 wei = 1x10⁻¹⁸ ETH
```
### Injective Tokenomics and Utility
#### 1. Security and Staking
Injective is secured via staking, which is an essential use case for INJ. Validators and delegators can freely participate in the Injective network via staking. Validators operate nodes on Injective, and delegators can assign INJ to a particular node of choice. Staked INJ enables a robust decentralized environment in which security is ensured via penalty and reward systems.
A validator’s staked INJ is subject to slashing in the event of malicious behavior or failure to effectively fulfill responsibilities. Additionally, INJ is used to reward validators for participation in transaction validation and block creation. Rewards for validators comprise newly minted INJ (block rewards) and a portion of the associated transaction fees.
Holders of INJ may also participate in staking without necessarily having to operate a node to earn a share of validator rewards. To do so, users delegate INJ to validator(s), which can be done through supported browser wallets, or directly through the Injective Hub. In return for locking up INJ, users earn a share of the validator’s INJ rewards, less the fee charged by the selected validator (commission), distributed pro rata. A user’s staked INJ is also subject to slashing in the event the validator delegated to incurs a slashing event. This ensures that both validators and delegators are aligned in contributing to the overall security of the network.
Beyond securing the Injective chain, INJ also extends its security serviceability to the broader ecosystem through Electro Chains. These Injective-based rollups offer a myriad of technical advantages, such as supporting multiple virtual machines, as seen with inEVM. Since these rollups settle to Injective, INJ powers the foundational security layer for these networks. This interconnected security framework underscores the pivotal role of INJ in maintaining the integrity and robustness of not only the Injective network, but also the diverse ecosystem of Electro Chains.
#### 2. Governance
INJ is utilized for community led governance across all parameters of the chain. Injective uniquely has a permissioning layer for smart contract uploads as well, meaning that the community of stakers must vote in order to instantiate a smart contract on mainnet. This empowers the community to directly govern all parameters of Injective as a whole.
For governance, INJ is used for proposal creation and token-weighted voting on active proposals. As a spam deterrent, Injective requires a minimum deposit, made in INJ, for the proposal to move on to the voting stage. This deposit threshold can either be met entirely by the proposer, or cumulatively by other users contributing INJ to the proposal deposit. If the minimum deposit amount is not reached by the time the maximum deposit period elapses, the proposal will be automatically rejected, and the deposit(s) burned. Additionally, if the proposal does not pass upon voting period expiry, the proposal deposit is burned.
Proposal voting occurs during a preset voting period, which is set via governance and invariably applied to all governance votes. During the voting process, only staked INJ is eligible to participate in voting. Hence, only validators and delegators can vote on active proposals. Voting power is token-weighted, meaning that 1 INJ equals 1 vote. Delegators are not required to actively participate in governance to maintain their status. However, they have the option to vote directly on proposals. If a delegator does not vote, their voting power will automatically be inherited by the validator to whom they have delegated, for that specific voting event.
INJ is used to govern all aspects of the chain, including:
* Auction Module Parameters
* Exchange Module Custom proposals and Parameters
* Insurance Module Parameters
* Oracle Module Custom proposals
* Peggy Module Parameters
* Wasmx Module Parameters
* Software upgrades
* Cosmos-SDK module parameters for the [auth](https://docs.cosmos.network/main/modules/auth#parameters), [bank](https://docs.cosmos.network/main/modules/bank), [crisis](https://docs.cosmos.network/main/modules/crisis), [distribution](https://docs.cosmos.network/main/modules/distribution), [gov](https://docs.cosmos.network/main/modules/gov), [mint](https://docs.cosmos.network/main/modules/mint), [slashing](https://docs.cosmos.network/main/modules/slashing), and [staking](https://docs.cosmos.network/main/modules/staking) modules.
Full details on the governance process can be found [here](https://blog.injectiveprotocol.com/injective-governance-proposal-procedure).
#### 3. Medium of Exchange
INJ is used as the default asset to facilitate the purchase and sale of goods and services between parties on the blockchain. Common examples of this are paying for transaction fees (gas), buying/selling NFTs, paying for trading fees, or depositing the asset as collateral. While most goods and services can be denominated in any asset, all transaction fees incurred on Injective are paid in INJ. Additionally, all protocol revenue generated by applications leveraging Injective’s shared liquidity layer via the exchange module is accumulated in INJ.
#### 4. Exchange dApps Incentives
The exchange protocol implements a global minimum trading fee of 0.1% for makers and 0.2% for takers. As an incentive mechanism to encourage exchange dApps to source trading activity on the exchange protocol, exchange dApps that originate orders into the shared orderbook are rewarded with 40% of the trading fees arising from all orders that they source.
#### 5. Exchange Fee Value Accrual
The remaining 60% of the exchange fee will undergo an on-chain buy-back-and-burn event where the aggregate exchange fee basket is auctioned off to the highest bidder in exchange for INJ. The INJ proceeds of this auction are then burned, thus deflating the total INJ supply.
More details on the auction mechanism can be found [here](../community-buyback/).
#### 6. Backing Collateral for Derivatives
INJ can be utilized as an alternative to stablecoins as margin and collateral for Injective's derivatives markets. In some derivative markets, INJ can also be used as backing collateral for insurance pool staking, where stakers can earn interest on their locked tokens.
# Token Factory
Source: https://docs.injective.network/defi/tokens/token-factory
Create and manage custom tokens on Injective without deploying a smart contract, using the native Token Factory module.
TokenFactory tokens are natively integrated into the bank module of the chain.
Their name takes on the format `factory/{creatorAddress}/{subdenom}`.
Because tokens are namespaced by the creator address, this allows for permissionless token minting,
due to not needing to resolve name collisions.
This integration provides support for tracking and querying the total supply of all assets, unlike the CW20 standard,
which requires querying the smart contract directly.
For this reason, using the TokenFactory standard is recommended.
Products such as Helix or Mito, for example, are built on the Injective exchange module, which exclusively uses bank tokens.
TokenFactory tokens can be created via the injectived CLI, as well as via smart contract.
Tokens bridged into Injective via Wormhole are also TokenFactory tokens.
## CW20 adapter
The `CW20AdapterContract` allows CW20 assets to be natively represented on Injective as Token Factory denoms. CW20 assets are held by the adapter contract and minted as a factory denom for the Injective address. When redeeming back to CW20, they are burned from the bank module and unlocked from the adapter contract back to the owner address.
### Redeem a factory denom to CW20
```ts theme={null}
import {
MsgExecuteContractCompat,
ExecArgCW20AdapterRedeemAndTransfer,
} from '@injectivelabs/sdk-ts/core/modules'
const CW20_ADAPTER_CONTRACT = 'inj...'
const contractCw20Address = 'inj...'
const injectiveAddress = 'inj...'
const message = MsgExecuteContractCompat.fromJSON({
sender: injectiveAddress,
contractAddress: CW20_ADAPTER_CONTRACT,
funds: {
denom: `factory/${CW20_ADAPTER_CONTRACT}/${contractCw20Address}`,
amount: actualAmount.toFixed(),
},
execArgs: ExecArgCW20AdapterRedeemAndTransfer.fromJSON({
recipient: injectiveAddress,
}),
})
// Then pack the message in a transaction, sign it and broadcast to the chain
```
### Convert CW20 to a factory denom
```ts theme={null}
import {
ExecArgCW20Send,
MsgExecuteContractCompat,
} from '@injectivelabs/sdk-ts/core/modules'
const CW20_ADAPTER_CONTRACT = 'inj...'
const contractCw20Address = 'inj...'
const injectiveAddress = 'inj...'
const amount = '1000000' // 1 USDT represented as on the chain as it has 6 decimals
const message = MsgExecuteContractCompat.fromJSON({
contractAddress: contractCw20Address,
sender: injectiveAddress,
execArgs: ExecArgCW20Send.fromJSON({
amount,
contractAddress: CW20_ADAPTER_CONTRACT,
}),
})
// Then pack the message in a transaction, sign it and broadcast to the chain
```
## Further reading
* [Token launch guide](../../developers-defi/token-launch/) - step-by-step guide to creating your token on Injective
* [Token Factory module reference](../../developers-native/injective/tokenfactory/) - detailed module specification
# 24/5 Equity Feeds
Source: https://docs.injective.network/defi/trading/24-5-equity-feeds
How 24/5 pricing for onchain equities works on Injective.
Injective now supports 24/5 pricing for selected US equities by aggregating multiple Pyth session feeds into a single, unified oracle stream. This enables smooth, continuous trading for equity perpetuals, even when the underlying stock moves across pre-market, regular hours, after-hours, and overnight sessions.
### Overview
US equities trade across four distinct sessions. Pyth publishes a separate price feed for each:
* Pre-market
* Regular trading hours
* After-hours
* Overnight / off-exchange liquidity
Injective uses SEDA to combine these four session feeds into a single oracle price per symbol, updating approximately every 2 seconds. This unified stream becomes the canonical index price for Injective equity perpetuals.
Currently supported onchain equities:
* TRADFI
* TSLA
* META
* TTI
* AAPL
* NVDA
* MSTR
* AMZN
* COIN
* GOOGL
* HOOD
* CRCL
* MSFT
* PLTR
More equities will migrate to this model in the coming weeks, and this list will be updated as onchain governance formalizes the upgrades.
### How the Aggregation Works
For each supported stock, SEDA:
1. Subscribes to the four relevant Pyth feeds
2. Selects the correct session feed based on live market phase
3. Applies lightweight sanity and continuity checks
4. Outputs one continuous price to Injective
This ensures:
* No session-to-session gaps
* Continuous charts and oracle flow
* Consistent mark/index pricing for 24/5 trading
* Minimal noise during session transitions
The aggregation cadence is approximately 2 seconds, giving traders near real-time responsiveness across all sessions.
### Benefits for Traders & Market Makers
1. **True 24/5 Equity Trading**: Equity perpetuals on Injective can track price action across all four US equity sessions without interruption.
2. **Cleaner Pricing**: No juggling multiple feeds, no abrupt jumps caused by switching sessions, as SEDA standardizes everything into one continuous stream.
3. **Better Risk Modeling**: Margin, liquidations, and mark-price logic use one consistent oracle, improving predictability and reducing false risk triggers.
4. **Professional-grade extended hours coverage**: Thin-liquidity periods like pre-market or after-hours are still reflected accurately via Pyth’s specialized feeds.
### Trading Behavior & Considerations
* Volatility varies dramatically by session. Pre-market and after-hours can be more illiquid, so expect wider spreads despite continuous pricing.
* Gaps still occur when news hits between sessions. The unified feed simply ensures the chart reflects the actual first traded price, not a “feed boundary” artifact.
* 24/5, not 24/7. The oracle pauses only on weekends, aligned with the underlying equity market structure.
# Derivatives
Source: https://docs.injective.network/defi/trading/derivatives
Injective natively supports trading of multiple types of derivatives:
* [Perpetuals](./derivatives-perpetuals.md)
* [Expiry Futures](./derivatives-expiry-futures.md)
* [Election Perpetual](./derivatives-election-perpetual.md)
* [Pre-Launch Futures](./derivatives-pre-launch-futures.md)
* [Index Perpetual Futures](./derivatives-index-perpetual-futures.md)
* [iAssets](./derivatives-iassets.md)
# Election Perpetual
Source: https://docs.injective.network/defi/trading/derivatives-election-perpetual
An election perpetual futures contract - or an election perp - is a type of derivative financial instrument on Injective that allows users to gain leveraged exposure to an elections market. An election perp is a perpetual futures contract that tracks the price of a market on Polymarket, rather than a traditional crypto asset.
Unlike traditional futures, these contracts don't have an expiry date. They can be held indefinitely. Trades are settled in USDT. LIke other perps, election perps use a funding rate mechanism to keep the contract price close to the underlying index price.
### How do Election Perpetual Futures Work?
* The contract tracks a market on Polymarket (e.g., [Presidential Election Winner 2024](https://polymarket.com/event/presidential-election-winner-2024)).
* Traders can go long (buy) or short (sell) the election perp, with up to 3x leverage.
* Periodic funding payments occur between long and short holders to align the contract price with the index.
Expiry futures require mark prices to track liquidation and settlement prices. Because mark prices are typically based on the spot prices of the underlying assets, regular oracle price feeds cannot be used for election perps as the index price typically does not exist in popular oracle feeds. However, an mark price is still needed prior to this time to inform liquidation prices. To solve this, election perpetual futures will use a proprietary oracle feed as the mark price.
### Mark Price Mechanism
The mark price for election perps on Injective is based on a proprietary oracle feed provided by Stork. In the example of the 2024ELECTION PERP, Stork queries the midpoint of the 2024 Presidential Election market on Polymarket. They then apply a six-hour time weighted average price (TWAP) to prevent drastic swings in the mark price. That price is then scaled down to a more human readable format (i.e. a price between $0 and $1), and used as the mark price.
### Market Settlement
Election perps are not expiry instruments, however the trading pairs have roughly specific lifespans (i.e. at the conclusion of an election). When the underlying market on Polymarket reaches settlement (i.e. the token price settles to $0 or $1), trading activity on an election perp will likely trend towards zero as there will not be any money to be made. At that point, an interested party may submit a governance proposal to settle the market. If and when such a proposal passes, the market would be settled and any traders with outstanding positions would be force liquidated at the mark price of the oracle, which will have settled at $0 or $1 accordingly.
In regard to 2024ELECTION PERP specifically, the trading pair was listed with TRUMPWIN as the underlying asset. Rather than listing two or more assets (i.e. one for both the Democratic and Republican nominees) and fragmenting liquidity, a decision was made to list one asset. For this asset, the mark price will settle to $1 if the aforementioned market on Polymarket settles to "yes" for Donald J. Trump (i.e. Donald J. Trump wins the 2024 Presidential election), and $0 if the market on Polymarket settles to "no" for Donald J. Trump (i.e. if Kamala Harris or another candidate wins the Presidential election).
# Expiry Futures
Source: https://docs.injective.network/defi/trading/derivatives-expiry-futures
Learn about expiry futures on Injective
In TradFi[^1], a futures contract is an agreement that requires two parties to transact an asset at a predetermined price at a specified time in the future. This allows traders to lock in a future price of the underlying asset to hedge or speculate on price movements. Injective offers a completely decentralized form of these futures contracts—expiry futures.
Similar to perpetual futures, expiry futures on Injective are traded with margin, allowing traders to access leverage. However, unlike perpetual futures, expiry futures have expiration dates and do not require funding payments, though liquidations may still occur if the maintenance margin threshold is not met.
Upon expiration, expiry futures markets are settled using oracle prices, typically set to the spot prices of the underlying assets. As a result, the price of futures tend to converge upon the spot price as the expiration date nears.
An interesting use of expiry futures on Injective are Pre-Launch Futures. Read on for more information.
[^1]: Traditional Finance
# iAssets
Source: https://docs.injective.network/defi/trading/derivatives-iassets
iAssets are a new class of real-world asset (RWA) derivatives that bring traditional markets - such as equities, commodities, and FX - onto Injective in a fully on-chain, composable, and capital-efficient form.
Unlike basic tokenized representations of RWAs, iAssets are programmable financial primitives with second-order utility. This means they aren’t just static mirrors of off-chain assets, they’re designed to enable:
* Dynamic liquidity allocation
* Position-based exposure
* Cross-market composability (e.g. combining iAssets with other on-chain derivatives and DeFi strategies)
iAssets do not require pre-funding or wrapping of the underlying asset. Instead, they exist purely as synthetic derivatives, powered by Injective’s on-chain perpetual futures engine and decentralized oracle infrastructure. More information on iAssets can be found in [the whitepaper](https://injective.com/iAssets_Paper.pdf).
iAssets trade identically to other Injective perpetual futures contracts:
* Margin is posted in USDT (or other supported stablecoins)
* Leverage is available (varies by market, but is typically 25x for equities, 50x for commodities, and 100x for FX)
* Positions are USDT-settled, not physically delivered
* Liquidations follow Injective’s auction-based mechanism
The key difference between iAssets and crypto perps lies in mark price behavior. A general overview of the differences can be found in the chart below, though there are exceptions for maintenance windows, and trading holidays.
| Asset Class | Price Feed Hours | Trading Hours |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| Crypto | 24/7 | 24/7 |
| iAssets (Equities) | 24/5 for supported equities (pre-market through overnight via SEDA aggregation); see [24/5 Equity Feeds](/defi/trading/24-5-equity-feeds) | 24/7 |
| iAssets (FX/commodities) | From Sunday 6PM ET to Friday 5PM ET | 24/7 |
* iAssets continue trading 24/7 on Injective, even when the mark price is not updating. Users are always warned of the potential risks when trading iAssets through a frontend like Helix, which checks for oracle liveness.
* For supported equities, Injective now provides 24/5 pricing by aggregating pre-market, regular hours, after-hours, and overnight Pyth feeds into a single continuous stream via SEDA. See [24/5 Equity Feeds](/defi/trading/24-5-equity-feeds) for the full list of supported markets and how the aggregation works.
* For equities not yet on the 24/5 feed, and for FX and commodity markets, price feeds are held constant outside market hours. Because the mark price does not update during these periods, it is virtually impossible to get liquidated outside of them.
* This allows users to take or unwind positions around the clock, but PNL will not shift until the next price update cycle resumes.
Mark prices for iAssets are sourced via decentralized oracles such as Pyth, which aggregate high-fidelity, low-latency price data from primary market sources. For more information on the price feeds used for iAssets, please visit [Pyth](https://docs.pyth.network/price-feeds/market-hours).
# Index Perpetual Futures
Source: https://docs.injective.network/defi/trading/derivatives-index-perpetual-futures
An index perpetual futures contract, often referred to as an "index perp," is a type of derivative financial instrument commonly used in cryptocurrency markets. An index perp is a perpetual futures contract that tracks the price of an index, rather than a single asset. In the context of cryptocurrencies, this index typically represents a basket of cryptocurrencies or instead - as in the case of the BUIDL/USDT Index Perp - the total supply of a product on chain, as dictated by the token's smart contract.
Unlike traditional futures, these contracts don't have an expiry date. They can be held indefinitely. Trades are settled in USDT. Like other perps, index perps use a funding rate mechanism to keep the contract price close to the underlying index price.
### How do Index Perpetual Futures Work?
* The contract tracks an index (e.g., the total supply of Blackrock's BUIDL fund).
* Traders can go long (buy) or short (sell) the index, with up to 5x leverage.
* Periodic funding payments occur between long and short holders to align the contract price with the index.
Futures contracts require mark prices to track liquidation and settlement prices. Because mark prices are typically based on the spot prices of the underlying assets, regular oracle price feeds cannot be used for index perps as the index price typically does not exist in popular oracle feeds. However, a mark price is still needed prior to this time to inform liquidation prices. To solve this, index perps will use a proprietary oracle feed as the mark price.
### Mark Price Mechanism
The mark price for index perps on Injective is based on a proprietary oracle feed provided by Stork. In the example of the BUIDL/USDT Index Perp - which is a NAV (net asset value) Index Perp - Stork queries the total supply of the BUIDL fund according to the [smart contract](https://etherscan.io/token/0x7712c34205737192402172409a8f7ccef8aa2aec) on Ethereum. They then apply a one-hour time weighted average price (TWAP) to prevent drastic swings in the mark price. That price is then scaled down to a more human readable format (the actual NAV is divided in this case by 10^5, for example, if the supply of the BUIDL fund is 500 million, the price of the BUIDL/USDT Index PERP will be 5000), and used as the mark price.
# Perpetuals
Source: https://docs.injective.network/defi/trading/derivatives-perpetuals
Learn about perpetual futures on Injective
We've already established that an expiry futures contract is an agreement requiring two parties to transact an asset at a predetermined price at a specified time in the future, allowing traders to lock in a future price of the underlying asset to hedge or speculate on price movements. Injective offers a completely decentralized form of not only expiry futures, but perpetual futures as well.
Perpetual futures on Injective are traded with margin, allowing traders to access leverage. Unlike expiry futures, perpetual futures have no specific expiry date. As such, they require funding payments. In addition, liquidations may occur if the maintenance margin threshold is not met. Perpetual futures are cash-settled, which means the contract is settled in cash rather than delivery of the underlying asset. This makes them more flexible than traditional expiry futures contracts, which have a predetermined expiry date and must be settled by delivering the underlying asset.
On Injective, perpetual futures contracts are margined with stablecoins such as USDT. As such, traders do not need to own or store the underlying asset in order to trade the contract. Perpetual futures are also more liquid than traditional futures contracts, typically resulting in less slippage.
Perpetual futures also use a funding mechanism to ensure that the price of the contract remains close to the price of the underlying asset. This can lead to funding fees, which are paid by traders who are on the wrong side of the funding rate. If the price of the perpetual futures contract deviates significantly from the price of the underlying, a funding gap emerges. The funding rate is calculated based on the gap, with positive rates paid by long positions to short positions, and vice-versa for negative rates. Funding payments are typically exchanged every few hours, and they are settled directly between long and short positions.
The purpose of funding payments is to incentivise traders to keep the price of the perpetual futures contract aligned with the underlying asset (spot). This prevents the contract from being artificially overpriced or underpriced.
An interesting use of perpetual futures on Injective are Pre-Launch Perpetual Futures. Read on for more information.
# Pre-Launch Futures
Source: https://docs.injective.network/defi/trading/derivatives-pre-launch-futures
Asset speculation before release
Many assets generate large amounts of trading activity when they are publicly launched but are generally unavailable to be traded prior to public release. To capture escalating interest and allow investors to speculate on assets prior to their public release dates, Injective has created Pre-Launch Futures (PLF). The first Pre-Launch Futures market on Injective will be based on an expiry futures contract, though PLFs[^1] can also take the form of a perpetual futures contract.
### How do Pre-Launch Futures Work?
Expiry futures require mark prices to track liquidation and settlement prices. Because mark prices are typically based on the spot prices of the underlying assets, regular oracle price feeds cannot be used for Pre-Launch Futures as the spot price does not exist before the token has launched. Expiry futures based PLFs[^2] are designed to be traded near the public launch date so a spot price exists prior to the market expiry date (not applicable to perpetual futures based PLFs[^3]). This is so that upon the asset being publicly traded, the mark price can be set to the public spot price and the market can eventually settle at the spot price upon expiry. However, a mark price is still needed prior to this time to inform liquidation prices.
To solve this, Pre-Launch Futures will initially use a 24-hour exponentially weighted moving average of the last day's minutely last traded price as the mark price.
### Mark Price Mechanism
The mark price is based on two price feeds: 1) EWMA (Exponentially Weighted Moving Average) price feed and 2) CEX API price feed. The CEX used is one of Binance, OKX or Bybit, whichever lists the underlying asset first.
And during the various phases of the timeline, a different price feed would be used.
* Before asset is listed on CEX → EWMA price feed
* Within 24 hours of asset is listed on CEX → EWMA price feed
* 24 hours after asset is listed on CEX → CEX API price feed
This design is used to prevent a sudden distortion in mark price if the difference between EWMA price feed and CEX API price feed is great.
**The following formula is used to calculate the EWMA price:**
$\mathrm{Price_t = \sum \limits_{i=0}^{1439} [(t-i_{minutes} < t_{init} ?\ assumed\ price : last\ traded\ price _{t-i_{minutes}}) \cdot e^{-i/1440} ] \cdot \frac{1-e^{-1/1440} }{ 1-e^{-1}}}$
Where:
* `t_init` is the time of the first trade in the underlying market.
* `assumed price` is the price assumption of the underlying asset. This price is used when there is no `last traded price` 24 hours prior the first trade in the underlying market. In other words, after the first 24 hours, if the underlying market has traded already, then the assumed price would no longer have an impact to the mark price.
* Assumed price used for TIA/USDT Pre Launch Futures is `2.5`.
* Assumed price used for PYTH/USDT PLF is `0.3`.
* Assumed price used for JUP/USDT PLF is `0.55`.
* Assumed price used for ZRO/USDT PLF is `5`.
* Assumed price used for W/USDT PLF is `2`.
* Assumed price used for OMNI/USDT PLF is `40`.
* `last traded price` is the last price traded in the underlying market.
[^1]: Pre-Launch Futures
[^2]: Pre-Launch Futures
[^3]: Pre-Launch Futures
# Trading Fees and Rebates
Source: https://docs.injective.network/defi/trading/fees
All trades on Injective are subject to fee rebates, whereby the fee recipient always receives a flat 40% of the trading fee regardless of whether the trade constitutes maker or taker flow. Relayers who originate an order can set the fee recipient. Therefore, if an API trader sets their own address as the fee recipient, that 40% goes to the trader's bank (subaccount 0) address. If the trade is done on Exchange dApps such as Helix, the 40% fee goes to the address that the particular Exchange dApp has set as the fee recipient.
For maker trades, the trading fee for some pairs is currently negative (we will use -0.01% as the example value for the rest of this page). This consists of 60% to the maker and 40% to the fee recipient (again, on Exchange dApps such as Helix, this will be an address set by the Exchange dApp). Therefore, 0.006% of the trade value is rebated to the maker, and 0.004% to the fee recipient. For trades that set themselves as the fee recipient, makers receive the full benefit of the -0.01% fee for these pairs.
Consider an example whereby a trader places a limit order to purchase 1 BTC/USDT PERP at $50,000. If this limit order constitutes maker flow, it is eligible for the -0.01% maker trading fee. Assuming this trade takes place on Helix, the fee recipient is not the trader's own bank address. Therefore, the fee rebate on this trade is $50,000 \* 0.0001 \* 0.6 = $3. However, if this trade goes through by self relaying, the fee rebate is $5 (the full -0.01% of \$50,000).
Let's assume a trader places a market buy order for 1 BTC/USDT PERP. Since this order constitutes taker flow, the trader is not eligible for the negative maker trading rate, but rather pays a small fee (we will use 0.05% as the example value for the rest of this page). If BTC is $50,000, this trading fee is $25. Of this $25, $10 (40%) goes to the fee recipient. If this trade goes through by self relaying, they receive that $10 and their effective trading fee drops to $15, resulting in an effective trading fee of 0.03%.
If the trader in the example above has [tier 4 status](https://helixapp.com/fee-discounts), their taker fee is discounted by 20%, resulting in a trading fee of only $20 for the same trade. Of this $20, $8 goes to the trader's own address as the fee recipient, bringing the effective trading fee to 0.024% or $12.
# Trading
Source: https://docs.injective.network/defi/trading/index
Documentation for Injective's on-chain orderbook exchange, covering spot markets, perpetual futures, expiry futures, and order types.
This section provides comprehensive documentation for traders and developers building on Injective's exchange infrastructure.
Whether you're executing trades via API, integrating orderbook functionality into your dApp,
or learning about margin requirements and derivatives, you'll find detailed specifications for
order types, fee structures, and trading instruments.
Injective redefines decentralized trading by combining the efficiency of centralized exchanges
with the transparency and security of blockchain.
Through Frequent Batch Auctions (FBA), Injective eliminates miner extractable value (MEV) and front-running
by aggregating orders into discrete batches that execute at a single clearing price.
Unlike traditional DEXs that rely on liquidity pools with constant product formulas,
Injective's on-chain CLOB enables precise limit orders, advanced order types, and institutional-grade execution.
The innovative negative maker fee model—where liquidity providers earn rebates instead of paying fees—creates powerful incentives
for market makers, resulting in tighter spreads and deeper liquidity than typical decentralized exchanges.
## Key Concepts
| Term | Definition |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Central Limit Orderbook (CLOB)** | A fully on-chain order matching system that enables transparent price discovery and shared liquidity across all Injective dApps, unlike AMM-based DEXs. |
| **Frequent Batch Auctions (FBA)** | Injective's order execution mechanism that aggregates orders over discrete time intervals and clears them at a uniform price, eliminating MEV and front-running. |
| **Negative Maker Fees** | A fee structure where liquidity providers (makers) receive rebates instead of paying fees—currently -0.01% on eligible pairs—incentivizing deep orderbook liquidity. |
| **Perpetual Futures** | Derivative contracts without expiry dates that track the price of an underlying asset, typically using funding rates to align the contract price with spot markets. |
| **Initial/Maintenance Margin** | The collateral required to open (initial) and keep open (maintenance) a leveraged position; falling below maintenance triggers liquidation to protect the protocol. |
## Navigation
* **[Order Types](/defi/trading/order-types/)** —
Reference guide for all supported order types including market, limit, stop-loss, take-profit, post-only, and atomic orders,
with details on execution behavior.
* **[Trading Fees and Rebates](/defi/trading/fees/)** —
Detailed breakdown of the fee structure including negative maker fees (-0.01%), taker fees, fee recipient rebates (40%),
and tier-based discounts for high-volume traders.
* **[Margin Trading](/defi/trading/margin/)** —
Explains how leveraged trading works on Injective, covering initial margin requirements, maintenance margin,
liquidation mechanics, and funding rates for perpetual positions.
* **[Derivatives](/defi/trading/derivatives/)** —
Overview of the derivative instruments available on Injective including perpetuals, expiry futures, election perpetuals,
pre-launch futures, index perpetuals, and iAssets.
# Margin Trading
Source: https://docs.injective.network/defi/trading/margin
Multiple types of futures on Injective involve margin trading, which is defined as using borrowed capital to amplify your potential returns and risks when trading certain futures contracts. However, it's crucial to understand the risks involved before diving in, as margin trading can amplify losses just as easily as gains.
In essence, margin trading allows you to control a larger position in a futures contract than your own capital would normally allow. This is achieved by borrowing capital, placing a leveraged bet on the future price of the underlying asset.
**Here's how it works:**
1. **Deposit Margin:** You deposit a portion of the total contract value as initial margin. This deposit - which is typically denominated in USDT on Injective - acts as collateral for the borrowed capital.
2. **Control a Larger Position:** With your initial margin, you can control a futures contract with a much higher notional value. For example, if a Bitcoin futures contract is worth $100,000 and the margin requirement is 10%, you can control the entire contract by depositing only $10,000.
3. **Amplified Profits and Losses:** Any price movement in the underlying asset will be magnified for your position. If the price moves in your favor, your profits will be multiplied compared to simply holding the underlying asset. Conversely, if the price moves against you, your losses will also be amplified, and your initial margin could be wiped out if the price falls too far.
**Understanding Margin Requirements:**
The amount of margin required for a futures contract varies depending on several factors, including:
* **Volatility of the underlying asset:** More volatile assets typically require higher margin amounts.
* **Contract terms:** Different futures contracts on the same asset may have different margin requirements.
* **Exchange or clearinghouse:** Each exchange or clearinghouse may set its own margin requirements.
It's important to remember that margin trading is not for everyone. It's a high-risk strategy that requires a deep understanding of the markets and risk management techniques. If you're not comfortable with the potential for significant losses, it's best to stick to traditional trading methods.
**Here are some additional things to keep in mind when considering margin trading on Helix:**
* **Liquidation:** If the price of the underlying asset moves against you and your margin falls below a certain threshold (maintenance margin), your position will be liquidated to cover your losses. This means you could lose your entire initial margin deposit.
* **Funding Rates:** In some cases, you may also be charged funding rates, which are fees paid to maintain your leveraged position. These fees can vary depending on market conditions and can eat into your profits.
* **Manage Your Risk:** Always use stop-loss orders and other risk management tools to limit your potential losses when margin trading.
Margin trading can be a powerful tool for experienced traders, but it's important to use it responsibly and with a clear understanding of the risks involved. If you're considering margin trading on Injective, be sure to do your research and only trade with capital you can afford to lose.
# Funding Rates
Source: https://docs.injective.network/defi/trading/margin-funding-rates
While margin trading unlocks the door to amplified gains, it also introduces another layer of complexity - funding requirements. Often overshadowed by margin requirements, understanding funding rates is crucial for responsible leveraged trading on Helix.
**What are Funding Rates?**
In traditional futures contracts, the price on the exchange converges with the spot price over time. In contrast, perpetual futures on Injective never expire, creating a potential disconnect between the contract price and the underlying asset's spot price. To keep these prices in sync, a mechanism called funding payments kicks in.
Funding rates are essentially periodic fees exchanged between long and short positions. The direction of these payments depends on the prevailing market sentiment:
* **Positive Funding Rates :** If a significant majority of traders are long, long positions pay funding fees to short positions. This incentivises trading activity that could potentially bring the contract price down towards the spot price.
* **Negative Funding Rates :** Conversely, if most traders are short, long positions receive funding fees from short positions. This encourages trading activity that could potentially push the contract price up towards the spot price.
The specific calculation of funding rates is a formula that considers the difference between the contract price and the index price (a reference point representing the spot price), along with an interest rate component. While these rates may seem small at first glance, they can accumulate over time and significantly impact your trading experience.
For **long position holders**, positive funding rates represent an additional cost. You'll be paying funding fees to short positions on each funding interval (e.g. at the top of the hour, each hour). Conversely, negative funding rates translate to receiving payments, essentially earning passive income on your open position.
For **short position holders**, the funding dynamic flips. Positive funding rates become a source of income, while negative funding rates translate to periodic payments you owe to long positions. Therefore, it's crucial to factor potential funding costs into your margin calculations and risk management strategies.
## How Funding is Calculated
Funding is calculated as:
$FundingRate = cap(TWAP + HourlyInterestRate,\ HourlyFundingRateCap)$
*where*
$TWAP = \frac{CumulativePrice}{(LastTimestamp + FundingInterval - NextFundingTimestamp) \times 24}$
The denominator represents the number of seconds between the start of the funding interval and the last trade,
multiplied by 24 to normalize from a daily to hourly basis. If no trades occurred during the entire funding interval, the TWAP is zero and only the `HourlyInterestRate` contributes to the funding rate.
The system computes a *Premium* each block,
representing how far the contract is trading from the mark price as a percentage:
$Premium = \frac{VWAP - MarkPrice}{MarkPrice}$
where *VWAP* is the volume-weighted average price of all trades executed in that block,
across market buy executions, market sell executions, limit order matches, and atomic order executions.
Using this figure, a *Cumulative Price* is accumulated throughout the hour.
At the end of each block where trades occur, the current *Premium* is multiplied by the time passed (in seconds)
since the last update:
$CumulativePrice_{new} = CumulativePrice_{old} + Premium \times \Delta t$
Blocks with no trades do not update the cumulative price or the last timestamp.
The premium is not carried forward - the TWAP denominator only counts time up to the last trade within the interval.
**Example :**
Let's say you are long 10,000 USDT notional of INJ/USDT PERP (regardless of leverage), and the funding rate is +0.02%, at the top of the hour, you will make a funding payment of 2 USDT. Conversely, if you are short 10,000 notional of INJ/USDT PERP with the same funding rate, you will receive a funding payment of 2 USDT.
Let's say you are short 20,000 USDT notional of BTC/USDT PERP, and the funding rate is -0.0035%. At the top of the hour, you make a funding payment of 0.7 USDT. Conversely, if you are long the same amount, you will receive a funding payment of 0.7 USDT.
Note, in some rare cases of extreme price volatility, there may be a small discrepancy between the estimated funding rate that you see on Helix, and the actual funding fee billed at the top of the hour.
# Liquidation
Source: https://docs.injective.network/defi/trading/margin-liquidation
Leveraging the power of margin trading comes with the risk of liquidation. This mechanism acts as a failsafe for both the trader and the DEX, automatically closing your position when your equity dips below a critical threshold. This is done to prevent further losses and protect the system's stability.
Liquidation is triggered when your account's maintenance margin drops below a certain level. This maintenance margin is a percentage of the total contract value, typically lower than the initial margin you deposited. It acts as a buffer against price movements.
**Maintenance Margin Requirement**
The margin must fulfill *Margin ≥ InitialMarginRatio \* Price \* Quantity*, e.g. in a market with maximally 20x leverage, the initial margin ratio would be 0.05. Any new position will have a margin which is at least 5% of its notional.
The margin must fulfill the mark price requirement:
*Margin ≥ Quantity \* (InitialMarginRatio \* MarkPrice - PNL)*
PNL is the expected profit and loss of the position if it was closed at the current MarkPrice. Solved for MarkPrice this results in:
* For Buys: *MarkPrice ≥ (Margin - Price \* Quantity) / ((InitialMarginRatio - 1) \* Quantity)*
* For Sells: *MarkPrice ≤ (Margin + Price \* Quantity) / ((InitialMarginRatio + 1) \* Quantity)*
Throughout the lifecycle of an active position, if the following margin requirement is not met, the position is subject to liquidation. (Note: For simplicity of notation but without loss of generality, we assume the position considered does not have any funding.)
* For Longs: *Margin ≥ Quantity \* MaintenanceMarginRatio \* Mark Price - (MarkPrice - EntryPrice)*
* For Shorts : *Margin ≥ Quantity \* MaintenanceMarginRatio \* Mark Price - (EntryPrice - MarkPrice)*
For example, let's say you use 10% margin for a Bitcoin futures contract worth $100,000. Your initial margin would be $10,000, and your maintenance margin might be 5% ($5,000). If the price of Bitcoin falls significantly, causing your equity in the contract to drop below $5,000, your position will be automatically liquidated.
**How Does Liquidation Work?**
When liquidation is triggered:
1. **The exchange will force-close your position.** This means selling your futures contract, regardless of the current market price.
2. **The proceeds from the sale will be used to cover your outstanding debt to the platform.** This includes the initial margin, any unpaid funding fees, and the loss incurred on the position.
3. **Any remaining funds will be credited back to your account.** However, it's crucial to remember that liquidation can potentially wipe out your entire initial margin deposit.
To avoid the painful sting of liquidation:
* **Monitor your margin:** Keep a close eye on your account's margin level and the market movements affecting your positions.
* **Use stop-loss orders:** These pre-set orders automatically sell your position when the price reaches a certain point, potentially minimizing losses and preventing liquidation.
* **Maintain adequate margins:** Avoid over-leveraging your positions. Higher margins provide a larger buffer against price fluctuations.
* **Understand funding rates:** Factor potential funding costs into your risk management calculations, especially in volatile markets.
# Performing Liquidations
Source: https://docs.injective.network/defi/trading/margin-performing-liquidations
This guide details how traders on Injective can utilize the `MsgLiquidatePosition` function to liquidate underwater positions.
**Before proceeding, ensure you understand the following:**
* **Liquidation Mechanics:** Injective employs a dynamic liquidation mechanism where positions exceeding a specific collateralization ratio (i.e. below threshold) become eligible for liquidation by any market participant. There are benefits for performing liquidations, which requires substantial upfront capital.
* **MsgLiquidatePosition Function:** This function allows traders to initiate liquidations on eligible positions, offering them an opportunity to capture a liquidation fee.
**Different Cases for Liquidations:**
There are two different cases depending on the state of the position. In both cases, it is required that the entire position is liquidated.
#### 1) Position has Positive or Zero Equity
The position will be sold using a market order with a worst price equal to the bankruptcy price. The liquidator only needs to submit a limit order if the entire position cannot be liquidated using the bankruptcy price as the worst price.
**Benefits**
* Guaranteed zero loss to the insurance fund when position is not bankrupt.
* Existing orderbook liquidity is used and the liquidator still has an incentive to liquidate by getting a potential discount on the position up to bankruptcy (arbitrage).
**Downsides**
* Taking over at bankruptcy price may not be attractive enough for liquidators, especially when the mark price is very close to the bankruptcy price.
* This concern is mitigated if one assumes there will always be at least one “white knight” liquidator, as there currently is on Injective.
**Example**
Consider the following long position in a market with a 5% maintenance margin ratio.
Quantity
Entry Price
Margin
Liquidation Price
Bankruptcy Price
1
10
2
8.42
8
The position is liquidateable and has non-negative equity when $8 ≤ Oracle Price ≤ $8.42
The liquidator can liquidate the position if the position can be sold on the orderbook using a market order with a worst price of \$8.
The liquidator can choose to submit their own order as well, if they desire to participate in the liquidation, but this is not necessary if the orderbook already has sufficient liquidity. If the orderbook does not have sufficient liquidity, then it is required that the liquidator submit their own order (which must have a price ≥ \$8) to be used as a part of the liquidation.
#### 2) Position has Negative Equity
The position will be sold using a market order with a worst price equal to the oracle price. The liquidator only needs to submit a limit order if the entire position cannot be liquidated using the oracle price as the worst price.
**Benefits**
* The insurance fund will never suffer an uncontrollable loss from market selling the position at an extreme price. Instead the insurance fund only loses capital based on the oracle price movements.
**Downsides**
* Similar to the positive equity case (but even worse), taking over the position at oracle price may not be attractive at all for liquidators, especially now since there is no implicit arbitrage. This can result in liquidations being delayed.
**Example**
Consider the following long position.
Quantity
Entry Price
Margin
Liquidation Price
Bankruptcy Price
1
10
2
8.42
8
The position has negative equity when Oracle Price \< $8. Assume the oracle price is $7.50.
The liquidator can liquidate the position if the position can be sold on the orderbook using a market order with a worst price of \$7.50.
Similar to the case above, the liquidator can choose to submit their own order as well, if they desire to participate in the liquidation, but this is not necessary if the orderbook already has sufficient liquidity. If the orderbook does NOT have sufficient liquidity, then it is required that the liquidator submit their own order (which must have a price ≥ \$7.50) to be used as a part of the liquidation.
**Steps to Liquidate Positions:**
1. **Identify Liquidatable Positions:** Utilize Injective's `LiquidablePositions` endpoint to identify positions with a collateralization ratio below the liquidation threshold. Relevant data points include:
* **Collateral:** Total value of tokens deposited as collateral for the position.
* **Liabilities:** Total value of borrowed tokens in the position.
* **Liquidation Threshold:** Minimum collateralization ratio required to avoid liquidation.
An example can be found [here for Go ](https://github.com/InjectiveLabs/sdk-go/blob/master/examples/exchange/derivatives/20_LiquidablePositions/example.go)and [here for Python](https://github.com/InjectiveLabs/sdk-python/blob/master/examples/exchange_client/derivative_exchange_rpc/23_LiquidablePositions.py).
2. **Prepare Liquidation Transaction:** Construct an order transaction using the `MsgLiquidatePosition` function, specifying the parameters listed in the [API docs](https://api.injective.exchange/?python#derivatives-msgliquidateposition). While not compulsory, a limit transaction is highly recommended over a market transaction.
Note, performing a liquidation requires a limit order. By following these steps and considering the outlined factors, liquidators can effectively utilize the `MsgLiquidatePosition` function to participate in Injective's liquidation mechanism and capture potential profit opportunities.
# Oracle Feed Sources
Source: https://docs.injective.network/defi/trading/oracle-feed-sources
Oracle feed sources and contract specifications for non-crypto perpetual markets on Injective.
The following table lists non-crypto perpetual markets available on Injective, including their asset descriptions, maximum leverage, and the underlying oracle feed powering each market's index price.
## Equities
| Instrument | Description | Underlying | Max Leverage |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------ |
| CRCL/USDC PERP | CRCL tracks the value of 1 share of common stock in Circle Internet Group, Inc. Circle is a global digital financial services company and the issuer of USDC, one of the world's largest US dollar-backed stablecoins. | [CRCL / USD](https://app.pyth.com/explore/Equity.US.CRCL%2FUSD) | 25x |
| RKLB/USDC PERP | RKLB tracks the value of 1 share of common stock in Rocket Lab USA, Inc. Rocket Lab provides launch services, spacecraft components, and space systems for commercial, civil, and defense customers. | [RKLB / USD](https://app.pyth.com/explore/Equity.US.RKLB%2FUSD) | 10x |
| AMZN/USDC PERP | AMZN tracks the value of 1 share of common stock in Amazon.com, Inc. Amazon runs a global online retail business, AWS cloud infrastructure, digital media services, and logistics operations. | [AMZN / USD](https://app.pyth.com/explore/Equity.US.AMZN%2FUSD) | 25x |
| NVDA/USDC PERP | NVDA tracks the value of 1 share of common stock in NVIDIA Corporation. NVIDIA builds GPUs, accelerated computing systems, and AI platforms used across gaming, data centers, machine learning, and advanced computing. | [NVDA / USD](https://app.pyth.com/explore/Equity.US.NVDA%2FUSD) | 25x |
| COIN/USDC PERP | COIN tracks the value of 1 share of common stock in Coinbase Global, Inc. Coinbase operates a leading cryptocurrency exchange and digital asset platform, providing trading, custody, and staking services to retail and institutional clients. | [COIN / USD](https://app.pyth.com/explore/Equity.US.COIN%2FUSD) | 25x |
| ORCL/USDC PERP | ORCL tracks the value of 1 share of common stock in Oracle Corporation. Oracle provides enterprise software, cloud infrastructure, and database management systems used by businesses and governments worldwide. | [ORCL / USD](https://app.pyth.com/explore/Equity.US.ORCL%2FUSD) | 10x |
| INTC/USDC PERP | INTC tracks the value of 1 share of common stock in Intel Corporation. Intel is one of the world's largest semiconductor manufacturers, producing microprocessors, chipsets, and integrated circuits for computing and communications. | [INTC / USD](https://app.pyth.com/explore/Equity.US.INTC%2FUSD) | 10x |
| MU/USDC PERP | MU tracks the value of 1 share of common stock in Micron Technology, Inc. Micron designs and manufactures memory and storage semiconductors, including DRAM and NAND flash products used in computers, mobile devices, and data centers. | [MU / USD](https://app.pyth.com/explore/Equity.US.MU%2FUSD) | 10x |
| DELL/USDC PERP | DELL tracks the value of 1 share of common stock in Dell Technologies Inc. Dell provides IT infrastructure, personal computers, servers, storage solutions, and cloud services to enterprises and consumers globally. | [DELL / USD](https://app.pyth.com/explore/Equity.US.DELL%2FUSD) | 10x |
| SNDK/USDC PERP | SNDK tracks the value of 1 share of common stock in SanDisk Corporation. SanDisk designs and manufactures flash storage products including solid-state drives and NAND memory solutions for consumer and enterprise applications. | [SNDK / USD](https://app.pyth.com/explore/Equity.US.SNDK%2FUSD) | 10x |
| AAPL/USDC PERP | AAPL tracks the value of 1 share of common stock in Apple Inc. Apple develops and sells consumer electronics, software, and services including the iPhone, Mac, iPad, and Apple Watch, alongside a growing digital services ecosystem. | [AAPL / USD](https://app.pyth.com/explore/Equity.US.AAPL%2FUSD) | 25x |
| TSLA/USDC PERP | TSLA tracks the value of 1 share of common stock in Tesla, Inc. Tesla designs and manufactures electric vehicles, battery energy storage systems, and solar products, and develops autonomous driving software. | [TSLA / USD](https://app.pyth.com/explore/Equity.US.TSLA%2FUSD) | 25x |
| MSFT/USDC PERP | MSFT tracks the value of 1 share of common stock in Microsoft Corporation. Microsoft provides software, cloud computing services, and hardware, including Windows, Azure, Microsoft 365, and the Xbox gaming platform. | [MSFT / USD](https://app.pyth.com/explore/Equity.US.MSFT%2FUSD) | 25x |
| META/USDC PERP | META tracks the value of 1 share of common stock in Meta Platforms, Inc. Meta owns major social and messaging platforms, including Facebook, Instagram, and WhatsApp, and invests in advertising, AI, virtual reality, and metaverse products. | [META / USD](https://app.pyth.com/explore/Equity.US.META%2FUSD) | 25x |
| GOOGL/USDC PERP | GOOGL tracks the value of 1 share of common stock in Alphabet Inc. Alphabet is the parent company of Google, operating search, advertising, cloud computing, and AI businesses, alongside investments in autonomous vehicles and life sciences. | [GOOGL / USD](https://app.pyth.com/explore/Equity.US.GOOGL%2FUSD) | 25x |
| PLTR/USDC PERP | PLTR tracks the value of 1 share of common stock in Palantir Technologies Inc. Palantir develops data analytics and AI software platforms used by government agencies and large enterprises for intelligence, operations, and decision-making. | [PLTR / USD](https://app.pyth.com/explore/Equity.US.PLTR%2FUSD) | 25x |
| HOOD/USDC PERP | HOOD tracks the value of 1 share of common stock in Robinhood Markets, Inc. Robinhood operates a US-based retail brokerage platform offering commission-free trading in stocks, options, and cryptocurrencies. | [HOOD / USD](https://app.pyth.com/explore/Equity.US.HOOD%2FUSD) | 25x |
| AVGO/USDC PERP | AVGO tracks the value of 1 share of common stock in Broadcom Inc. Broadcom designs and supplies a broad range of semiconductor and infrastructure software solutions for data centers, networking, storage, and wireless communications. | [AVGO / USD](https://app.pyth.com/explore/Equity.US.AVGO%2FUSD) | 10x |
## Pre-IPO
| Instrument | Description | Underlying | Max Leverage |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------ |
| SPCX/USDC PERP | SPCX represents a pre-IPO market for the implied per-share value of Space Exploration Technologies Corp. Class A common stock. SpaceX builds and launches rockets and spacecraft, runs the Starlink broadband network, and owns xAI. | SPCX/USDC | 5x |
## Indices
| Instrument | Description | Underlying | Max Leverage |
| ---------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- | ------------ |
| TRADFI/USDC PERP | TRADFI provides exposure to a broad U.S. large-cap equity benchmark. | [Pyth Feed](https://app.pyth.com/explore/Equity.US.SPY%2FUSD) | 50x |
| TTI/USDC PERP | TTI provides exposure to a technology-heavy U.S. equity benchmark. | [Pyth Feed](https://app.pyth.com/explore/Equity.US.QQQ%2FUSD) | 50x |
## ETFs
| Instrument | Description | Underlying | Max Leverage |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------ |
| GLD/USDC PERP | GLD tracks the value of 1 share of a gold-backed exchange-traded product. The instrument is designed to reflect the performance of the price of physical gold bullion, providing exposure to gold without holding the metal directly. | [GLD / USD](https://app.pyth.com/explore/Equity.US.GLD%2FUSD) | 50x |
## Commodities
| Instrument | Description | Underlying | Max Leverage |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | ------------ |
| XAU/USDC PERP | XAU tracks the value of 1 troy ounce of gold. Gold is a globally traded precious metal used in reserves, investment products, jewelry, and inflation-sensitive portfolios. | [XAU / USD](https://app.pyth.com/explore/Metal.XAU%2FUSD) | 50x |
| XAG/USDC PERP | XAG tracks the value of 1 troy ounce of silver. Silver is a precious metal with widespread industrial applications in electronics, solar panels, and medical devices, as well as use as an investment asset. | [XAG / USD](https://app.pyth.com/explore/Metal.XAG%2FUSD) | 50x |
## Forex
| Instrument | Description | Underlying | Max Leverage |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ------------ |
| EURUSD/USDC PERP | EURUSD tracks the EUR/USD exchange rate. The euro (EUR) is used across the Eurozone and is among the most liquid currencies in global foreign exchange trading. | [EUR / USD](https://app.pyth.com/explore/FX.EUR%2FUSD) | 100x |
| GBPUSD/USDC PERP | GBPUSD tracks the GBP/USD exchange rate. The British pound sterling (GBP) is the official currency of the United Kingdom and one of the oldest and most traded currencies in the world. | [GBP / USD](https://app.pyth.com/explore/FX.GBP%2FUSD) | 100x |
| AUDUSD/USDC PERP | AUDUSD tracks the AUD/USD exchange rate. The Australian dollar (AUD) is the official currency of Australia and is closely tied to commodity exports, particularly iron ore and coal. | [AUD / USD](https://app.pyth.com/explore/FX.AUD%2FUSD) | 100x |
| NZDUSD/USDC PERP | NZDUSD tracks the NZD/USD exchange rate. The New Zealand dollar (NZD) is the official currency of New Zealand and is influenced by agricultural exports and global risk sentiment. | [NZD / USD](https://app.pyth.com/explore/FX.NZD%2FUSD) | 100x |
| USDCHF/USDC PERP | USDCHF tracks the USD/CHF exchange rate. The Swiss franc (CHF) is the official currency of Switzerland, widely regarded as a safe-haven currency due to Switzerland's political neutrality and financial stability. | [USD / CHF](https://app.pyth.com/explore/FX.USD%2FCHF) | 100x |
| USDCAD/USDC PERP | USDCAD tracks the USD/CAD exchange rate. The Canadian dollar (CAD) is the official currency of Canada and is closely correlated with oil prices given Canada's significant crude oil exports. | [USD / CAD](https://app.pyth.com/explore/FX.USD%2FCAD) | 100x |
| USDJPY/USDC PERP | USDJPY tracks the USD/JPY exchange rate. The Japanese yen (JPY) is the official currency of Japan and one of the most liquid currencies in the world, often used as a safe-haven asset during periods of market stress. | [USD / JPY](https://app.pyth.com/explore/FX.USD%2FJPY) | 100x |
| USDSEK/USDC PERP | USDSEK tracks the USD/SEK exchange rate. The Swedish krona (SEK) is the official currency of Sweden and is sensitive to global risk appetite and European economic conditions. | [USD / SEK](https://app.pyth.com/explore/FX.USD%2FSEK) | 50x |
| USDNOK/USDC PERP | USDNOK tracks the USD/NOK exchange rate. The Norwegian krone (NOK) is the official currency of Norway and is closely linked to oil prices, reflecting the country's status as a major petroleum exporter. | [USD / NOK](https://app.pyth.com/explore/FX.USD%2FNOK) | 50x |
| USDSGD/USDC PERP | USDSGD tracks the USD/SGD exchange rate. The Singapore dollar (SGD) is the official currency of Singapore, managed under a monitored float policy by the Monetary Authority of Singapore. | [USD / SGD](https://app.pyth.com/explore/FX.USD%2FSGD) | 50x |
| USDHKD/USDC PERP | USDHKD tracks the USD/HKD exchange rate. The Hong Kong dollar (HKD) is the official currency of Hong Kong and operates under a currency board system pegged to the US dollar. | [USD / HKD](https://app.pyth.com/explore/FX.USD%2FHKD) | 50x |
# Order Types
Source: https://docs.injective.network/defi/trading/order-types
Read about order types on Injective
The following list describes the supported order types on Injective:
* **BUY (1):** A standard buy order to purchase an asset at either the current market price or a set limit price. Market orders in Injective also have a price to establish a limit to the market price the order will be executed with.
* **SELL (2):** A standard sell order to sell an asset at either the current market price or a set limit price. Market orders in Injective also have a price to establish a limit to the market price the order will be executed with.
* **STOP\_BUY (3):** A stop-loss buy order converts into a regular buy order once the oracle price reaches or surpasses a specified trigger price.
* **STOP\_SELL (4):** A stop-loss sell order becomes a regular sell order once the oracle price drops to or below a specified trigger price.
* **TAKE\_BUY (5):** A take-profit buy order converts into a regular buy order once the oracle price reaches or surpasses a specified trigger price.
* **TAKE\_SELL (6):** A take-profit sell order becomes a regular sell order once the oracle price drops to or below a specified trigger price.
* **BUY\_PO (7):** Post-Only Buy. This order type ensures that the order will only be added to the order book and not match with a pre-existing order. It guarantees that you will be the market "maker" and not the "taker".
* **SELL\_PO (8):** Post-Only Sell. Similar to BUY\_PO, this ensures that your sell order will only add liquidity to the order book and not match with a pre-existing order.
* **BUY\_ATOMIC (9):** An atomic buy order is a market order that gets executed instantly, bypassing the Frequent Batch Auctions (FBA). It's intended for smart contracts that need to execute a trade instantly. A higher fee is paid defined in the global exchange parameters (currently it is two times the normal trading fee).
* **SELL\_ATOMIC (10):** An atomic sell order is similar to a BUY\_ATOMIC, and it gets executed instantly at the current market price, bypassing the FBA.
Additional notes:
* **Immediate-Or-Cancel (IOC):** IOC orders are not yet supported. The closest order type is a market order (**BUY**).
* The worst price for a market order is the "price" parameter.
# Transactions
Source: https://docs.injective.network/defi/transactions
When users want to interact with Injective and make state changes, they create transactions. Once the transaction is created, it requires a signature from the private key linked to the account initiating the particular state change. Following the signature, the transaction is broadcasted to Injective.
After being broadcasted and passing all validations (including signature validation, values validations, etc.), the transaction is included in a block that undergoes network approval via the consensus process.
### Messages
In simpler terms, messages are the instructions given to Injective about the desired state change. Messages are module-specific objects that trigger state transitions within the scope of the module they belong to. Every transaction must have at least one message.
**Additionally, multiple messages can be packed within the same transaction.** Available Messages from each module can be found in the [native developers](/developers-native) section.
### Transaction Context
Besides Messages, every transaction has a context. The context includes `fees`, `accountDetails`, `memo`, `signatures`, etc.
### Transaction Flow
Every transaction we want to broadcast to Injective has the same flow. The flow consists of three steps: preparing, signing, and broadcasting the transaction. When the transaction is included in a block, the state change that was specified using the Message is applied on Injective.
# Accounts
Source: https://docs.injective.network/defi/wallet/accounts
This section describes the built-in accounts system of Injective.
This document describes the built-in accounts system of Injective.
Pre-requisite Readings:
* [Cosmos SDK Accounts](https://docs.cosmos.network/main/basics/accounts)
* [Ethereum Accounts](https://ethereum.org/en/whitepaper/#ethereum-accounts)
Injective defines its custom `Account` type that uses Ethereum's ECDSA secp256k1 curve for keys. This satisfies the [EIP84](https://github.com/ethereum/EIPs/issues/84) for full [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) paths. The root HD path for Injective-based accounts is `m/44'/60'/0'/0`.
### Addresses and Public Keys
There are 3 main types of `Addresses`/`PubKeys` available by default on Injective:
* Addresses and Keys for **accounts**, that identify users (i.e., the sender of a `message`). They are derived using the **`eth_secp256k1`** curve.
* Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`eth_secp256k1`** curve.
* Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve.
| | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length |
| ------------------ | --------------------- | -------------------- | --------------- | ------------------- | ------------------ |
| Accounts | `inj` | `injpub` | `eth_secp256k1` | `20` | `33` (compressed) |
| Validator Operator | `injvaloper` | `injvaloperpub` | `eth_secp256k1` | `20` | `33` (compressed) |
| Consensus Nodes | `injvalcons` | `injvalconspub` | `ed25519` | `20` | `32` |
### Address formats for clients
`EthAccount`s can be represented in both [Bech32](https://en.bitcoin.it/wiki/Bech32) and hex format for Ethereum's Web3 tooling compatibility.
The Bech32 format is the default format for Cosmos-SDK queries and transactions through CLI and REST clients. The hex format is the Ethereum `common.Address` representation of a Cosmos `sdk.AccAddress`.
* Address (Bech32): `inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku`
* Address ([EIP55](https://eips.ethereum.org/EIPS/eip-55) Hex): `0xAF79152AC5dF276D9A8e1E2E22822f9713474902`
* Compressed Public Key: `{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"ApNNebT58zlZxO2yjHiRTJ7a7ufjIzeq5HhLrbmtg9Y/"}`
You can query an account address using the Cosmos CLI or REST clients:
```bash theme={null}
# NOTE: the --output (-o) flag will define the output format in JSON or YAML (text)
injectived q auth account $(injectived keys show -a) -o text
|
'@type': /injective.types.v1beta1.EthAccount
base_account:
account_number: "3"
address: inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku
pub_key: null
sequence: "0"
code_hash: xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=
```
```bash theme={null}
# GET /cosmos/auth/v1beta1/accounts/{address}
curl -X GET "http://localhost:10337/cosmos/auth/v1beta1/accounts/inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku" -H "accept: application/json"
```
See the [Swagger API](https://lcd.injective.network/swagger/) reference for the full docs on the accounts API.
The Cosmos SDK Keyring output (i.e `injectived keys`) only supports addresses in Bech32 format.
### Derive Injective Account from a private key/mnemonic
Below is an example of how to derive an Injective Account from a private key and/or a mnemonic phrase:
```js theme={null}
import { Wallet } from 'ethers'
import { Address as EthereumUtilsAddress } from 'ethereumjs-util'
const mnemonic = "indoor dish desk flag debris potato excuse depart ticket judge file exit"
const privateKey = "afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890"
const defaultDerivationPath = "m/44'/60'/0'/0/0"
const defaultBech32Prefix = 'inj'
const isPrivateKey: boolean = true /* just for the example */
const wallet = isPrivateKey ? Wallet.fromMnemonic(mnemonic, defaultDerivationPath) : new Wallet(privateKey)
const ethereumAddress = wallet.address
const addressBuffer = EthereumUtilsAddress.fromString(ethereumAddress.toString()).toBuffer()
const injectiveAddress = bech32.encode(defaultBech32Prefix, bech32.toWords(addressBuffer))
```
Below is an example of how to derive a public key from a private key:
```js theme={null}
import secp256k1 from 'secp256k1'
const privateKey = "afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890"
const privateKeyHex = Buffer.from(privateKey.toString(), 'hex')
const publicKeyByte = secp256k1.publicKeyCreate(privateKeyHex)
const buf1 = Buffer.from([10])
const buf2 = Buffer.from([publicKeyByte.length])
const buf3 = Buffer.from(publicKeyByte)
const publicKey = Buffer.concat([buf1, buf2, buf3]).toString('base64')
const type = '/injective.crypto.v1beta1.ethsecp256k1.PubKey'
```
## Subaccounts
Injective subaccounts allow a single main wallet address to manage multiple, isolated trading accounts. This is useful for power users, especially professional traders and market makers.
For technical implementation details on subaccounts, see the [Trading Account](/developers/concepts/trading-account) developer documentation.
### Key features and explanation
* **Programmatic access**: This feature is designed to be highly accessible for programmatic trading via Injective's native APIs, catering to financial application developers.
* **Advanced account management**: The subaccounts feature provides sophisticated account management capabilities, enabling users (e.g., institutions or algorithmic traders) to segregate funds and trading strategies within a single, primary Injective address.
* **Isolation and organization**: Funds and orders within one subaccount are isolated from others, which is critical for managing risk, running different trading bots, or applying distinct strategies simultaneously without interference.
* **Seamless transfers**: Users can easily transfer assets between their main account balance and their various subaccounts, as well as between different subaccounts, using specific messages on the Injective network.
* **Integration with exchange modules**: The subaccounts functionality is part of Injective's core exchange module, which includes an on-chain order book and matching engine for spot, perpetual, futures, and options markets.
Subaccounts function like separate, linked "portfolios" controlled by a single user account. This provides flexibility and operational control for participants in Injective's DeFi ecosystem.
# Create a wallet
Source: https://docs.injective.network/defi/wallet/create
Learn how to create a wallet on Injective using different approaches.
Creating a wallet on Injective is as simple as sending some funds to your Injective address. In this document we'll outline how to create a wallet using some popular wallet solutions (Metamask, Ledger, etc.), but also how to create and fund a wallet using `injectived` in a local CLI environment.
### Browser Wallets
You can create an Injective wallet using your preferred wallet by following the [tutorial on our blog](https://injective.com/blog/how-to-create-an-injective-wallet-2/).
### Local Wallets
You can create an Injective wallet for your development purposes using [injectived](/developers/injectived/) in a CLI environment. You can do that using the `injectived keys` [command](/developers/injectived/advanced/#keys).
You can also learn more about keyring management using `injectived` on this page [set-up-keyring](/infra/set-up-keyring/).
# Wallet
Source: https://docs.injective.network/defi/wallet/index
The Injective Wallet allows you to monitor your assets on Injective. Assets can be native tokens on Injective, as well as bridged assets from Ethereum, Solana, Polygon and various IBC-enabled chains. See [Injective Hub Staking Walkthrough](https://injective.com/blog/injective-hub/)
There are a variety of different wallets that are supported on Injective. Users can choose to submit transactions on Injective using either their Ethereum or Cosmos-native wallets.
### Overview
Injective's `Account` type uses Ethereum's ECDSA secp256k1 curve for keys. Simply put, Injective's Account is native (compatible) with Ethereum accounts, allowing Ethereum-native wallets, such as MetaMask, to interact with Injective. Popular Cosmos wallets like Keplr and Leap have also integrated with Injective.
#### Ethereum-Based Wallets
As explained above, Ethereum-based wallets can be used to interact with Injective. Right now, the most popular Ethereum-based wallets are supported on Injective. These include:
1. [MetaMask](https://metamask.io/)
2. [Ledger](https://www.ledger.com/)
3. [Trezor](https://trezor.io/)
4. [Torus](https://tor.us/index.html)
The process of signing transactions on Injective using an Ethereum-native wallet consists of:
1. Converting the transaction into EIP712 TypedData,
2. Signing the EIP712 TypedData using an Ethereum-native wallet,
3. Packing the transaction into a native Cosmos transaction (including the signature), and broadcasting the transaction to the chain.
This process is abstracted away from the end-user. If you've previously used an Ethereum-native wallet, the user experience will be the same.
#### Cosmos-Based Wallets
Injective supports the leading wallets compatible with Cosmos and IBC, including:
1. [Cosmostation](https://cosmostation.io/)
2. [Keplr](https://www.keplr.app/)
3. [Leap](https://www.leapwallet.io/)
#### Injective-Native Wallets
Currently, [Ninji Wallet](https://ninji.xyz/) is the only Injective-native wallet. Such a wallet is built to synergize specifically with the greater Injective ecosystem.
#### CEX-Based Wallets
There are also several wallets developed by centralized exchanges (CEXs) that now support Injective. If you are an active user of these CEXs, using their wallets can provide a more seamless web3 experience. Currently, CEX-based wallets that support Injective are:
1. [Bitget Wallet](https://web3.bitget.com/en/)
2. [OKX Wallet](https://www.okx.com/web3)
# Denom
Source: https://docs.injective.network/developers/assets/denom
A denom is how assets are represented within the Bank module of Injective. These assets can be used for trading, creating new markets on the exchange module, participating in auctions, transferring to another address, etc.
Depending on the origin of the denom and how it was created on Injective we have different types of denoms:
* **Native denoms** - there is only one denom of this type, the `inj` denom which represented the native coin of Injective,
* **Peggy denoms** - these denoms represent assets bridged over from Ethereum to Injective using the Peggy bridge. They have the following format `peggy{ERC20_CONTRACT_ADDRESS}`
* **IBC denoms** - these denoms represent assets bridged over from other Cosmos chains through IBC. They have the following format `ibc/{hash}`.
* **Insurance Fund Denoms** - these denoms represent token shares of the insurance funds created on Injective. They have the following format `share{id}`
* **Factory Denoms** - these denoms are a representation of a CW20 token from Cosmwasm on the Injective native bank module. They have the following format `factory/{OWNER}/{SUBDENOM}` where the `OWNER` is the owner who created the factory denom. One example is the CW20 token factory denom `factory/{CW20_ADAPTER_CONTRACT}/{CW20_CONTRACT_ADDRESS}` where the `CW20_ADAPTER_CONTRACT` is the adapter contract address which does the conversion between CW20 and the native Bank module.
## Token
Token is simply a denom on the Injective chain with some meta information. The metadata includes information like symbol, name, decimals, logo for the particular denom, etc. The metadata of the denom is quite important for a dApp developer as information on the chain is stored in its raw form (for example `1inj` on the chain is represented as `1*10^18inj`) so we need to have a way to show the user human-readable information (numbers, logo, symbol, etc).
**Deprecation Notice**
Note that there was a "Denom Client" available within the Injective SDK.
This has been deprecated in favour of [Injective List](/developers/assets/injective-list).
# Assets
Source: https://docs.injective.network/developers/assets/index
Token denoms, metadata, and asset registry on Injective
Assets on Injective are represented as denoms in the bank module. Understanding how denoms work is essential for building applications that interact with tokens on the chain.
**Looking for token metadata?** The [Injective List](./injective-list) repository aggregates on-chain denom metadata from multiple sources into a comprehensive master list. This is the recommended way to resolve denom information in your application.
## In this section
* [**Denom**](./denom) - how assets are represented on-chain (native, peggy, IBC, factory denoms)
* [**Injective List**](./injective-list) - the canonical token metadata registry and how to integrate it
* [**Token Metadata**](./token-metadata) - attaching human-readable metadata to denoms
* [**Creating Tokens**](./token-create) - creating new tokens using the Token Factory module
# Injective List
Source: https://docs.injective.network/developers/assets/injective-list
We have moved the on-chain denoms token metadata to the [injective-list](https://github.com/InjectiveLabs/injective-lists) repository. This repository will aggregate data from several sources and produce a comprehensive token metadata master list.
Here is an example of how to integrate injective-list with the TokenFactoryStatic class:
1. Download the [Injective list JSON file](https://github.com/InjectiveLabs/injective-lists?tab=readme-ov-file#-usage) from GitHub
2. Use the `TokenFactoryStatic` class from the `sdk-ts` package
```ts theme={null}
import { TokenFactoryStatic } from "@injectivelabs/sdk-ts/service";
import { TokenType, TokenStatic } from "@injectivelabs/sdk-ts/types";
import { tokens } from "../data/tokens.json"; // json file downloaded from step 1
export const tokenFactoryStatic = new TokenFactoryStatic(
tokens as TokenStatic[]
);
// After instantiating, we can start using it in our dApp
const denom = "peggy0x...";
const token = tokenFactoryStatic.toToken(denom);
console.log(token);
```
There are few edge cases that we have to consider while using the `TokenFactoryStatic`:
* If you are trying to query token metadata for a denom that doesn't exist in the [list of tokens](https://github.com/InjectiveLabs/injective-lists) the `TokenFactoryStatic` will return undefined. If so, you should follow our [CONTRIBUTION guide](https://github.com/InjectiveLabs/injective-lists/blob/master/CONTRIBUTING.md) to add the token metadata information in the package.
# Creating Tokens
Source: https://docs.injective.network/developers/assets/token-create
The easiest way to create your own token on Injective is by using the `tokenfactory` module. The `tokenfactory` module allows any account to create a new token with the name `factory/{creator address}/{subdenom}`. Because tokens are namespaced by creator address, this allows token minting to be permissionless, due to not needing to resolve name collisions.
A single account can create multiple denoms, by providing a unique subdenom for each created denom. Once a denom is created, the original creator is given "admin" privileges over the asset. This allows them to:
* Mint their denom to any account
* Burn their denom from any account
* Create a transfer of their denom between any two accounts
* Change the admin. In the future, more admin capabilities may be added. Admins can choose to share admin privileges with other accounts using the authz module. The ChangeAdmin functionality allows changing the master admin account, or even setting it to the zero address `inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49`, meaning no account has admin privileges over the asset.
* Set their token metadata on chain
One special use case for the factory denoms is the `CW20_ADAPTER`. Using this adapter, assets represented as CW20 can be converted to a bank denom which then can be used to launch a market, send easily on chain, etc.
The denom for a CW20 asset is always in the `factory/{CW20_ADAPTER_CONTRACT_ADDRESS}/{CW20_ASSET_ADDRESS}` where `CW20_ADAPTER_CONTRACT_ADDRESS=inj14ejqjyq8um4p3xfqj74yld5waqljf88f9eneuk` for mainnet.
To start creating your denoms, head to our [TokenFactory Core Module page](/developers-native/examples/token-factory/) to see examples.
# Token Metadata
Source: https://docs.injective.network/developers/assets/token-metadata
Assets on Injective are represented as denoms. Denoms (and the amounts) are not human readable and this is why we need to have a way to "attach" token metadata information for a particular denom.
Let's recap the types of denoms we have in the Getting Started section:
* **Native denoms** - there is only one denom of this type, the `inj` denom which represented the native coin of Injective,
* **Peggy denoms** - these denoms represent assets bridged over from Ethereum to Injective using the Peggy bridge. They have the following format `peggy{ERC20_CONTRACT_ADDRESS}`
* **IBC denoms** - these denoms represent assets bridged over from other Cosmos chains through IBC. They have the following format `ibc/{hash}`.
* **Insurance Fund Denoms** - these denoms represent token shares of the insurance funds created on Injective. The have the following format `share{id}`
* **Factory Denoms** - these denoms represent a CW20 token from Cosmwasm on the Injective native bank module. They have the following format `factory/{CW20_ADAPTER_CONTRACT}/{CW20_CONTRACT_ADDRESS}` where the `CW20_ADAPTER_CONTRACT` is the adapter contract address that converts CW20 and the native Bank module.
We maintain our token metadata list off-chain for faster access to the[ injective-lists](https://github.com/InjectiveLabs/injective-lists/tree/master/tokens) repository.
## Token Verification
Verifying your token's metadata can be done in a couple of ways. Here are the verification levels and what they mean:
* **Verified** → Your asset metadata has been **submitted and verified** to the `@injectivelabs/token-metadata` package. You can find a tutorial on how to add your token's metadata to the package [here](https://github.com/InjectiveLabs/injective-lists/blob/master/CONTRIBUTING.md).
* **Internal** → Your asset's metadata has been verified on-chain using the `MsgSetDenomMetadata` message, as explained [here](/developers-native/examples/token-factory).
* **External** → Your asset's metadata has been verified on some external source like from Ethereum's contract details, etc.
* **Unverified** → Your asset's metadata has not been provided anywhere.
# Indexer API
Source: https://docs.injective.network/developers/concepts/indexer-api
The Indexer API is a collection of microservices that serve data indexed from the Injective chain. The Injective Chain emits events when a transaction is included and there is an event listener within the Indexer API that listens for these events, processes them, and stores the data in a MongoDB. Querying the chain directly is an expensive (and less performant) API call than querying an API serving data from a MongoDB which is why the Indexer API exists.
Another benefit of using the Indexer API is streaming. MongoDB can stream updates in the collections/documents which can be quite beneficial for a nice user experience. This way we don't need to poll for the data, instead, we can subscribe for a stream and update the state of our dApp on updates broadcasted within the stream.
Finally, the Indexer API can serve historical data or processed data over a period of time (ex: for drawing charts, etc).
## Running your own indexer
If you want to run your own Injective Indexer service, see the [Indexer Service Setup Guide](https://injective.notion.site/Injective-Indexer-Service-Setup-Guide-7e59980634d54991862300670583d46a).
# Trading Account
Source: https://docs.injective.network/developers/concepts/trading-account
Subaccounts or Trading Accounts are a concept that allows you to decouple the funds in the native Injective Bank module (which can be used for staking, bidding on auctions, participating in governance, creating markets, etc) into an isolated trading account from which you can execute trades. One Injective address can have an unlimited number of trading accounts. The way they are represented is `${ethereumAddress}${subaccountNonce}` where the `ethereumAddress` is the `hex` version of the `bech32` Injective address and the `subaccountNonce` is the nonce represented in 12 bytes. An example trading account at nonce 1 would be `0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001`.
For a general overview of subaccounts and their key features, see the [Accounts](/defi/wallet/accounts#subaccounts) page.
Starting the v1.10.0 chain upgrade, the Bank balance and the default trading account (at nonce = 0) will be merged and the Bank funds will be directly used when executing trades originating from the default trading account.
# Quickstart
Source: https://docs.injective.network/index
Get started building on or operating Injective
Injective is a high-performance layer-one blockchain built for finance, with sub-second finality, built-in DeFi primitives, and native cross-chain interoperability.
Set up a validator or full node, sync with mainnet, and operate and upgrade network infrastructure
Hub, Explorer, Operator Portal, endpoints, snapshots, and other ecosystem tools
Learn about trading, derivatives, staking, and token standards on Injective's on-chain exchange
Build products on top of Injective's DeFi APIs, launch markets, and integrate the exchange module
***
## Network at a glance
| | Mainnet | Testnet |
| -------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| **Chain ID** | `injective-1` | `injective-888` |
| **Block time** | \~650ms | \~650ms |
| **Binary** | [`injectived`](https://github.com/InjectiveFoundation/injective-core/releases/latest) | [`injectived`](https://github.com/InjectiveLabs/testnet/releases) |
| **Snapshots** | [Chain Portal](https://portal.injective.network/snapshots) | [Polkachu](https://polkachu.com/testnets/injective/snapshots) |
| **Endpoints** | [Chain Portal](https://portal.injective.network/rpcs) | [Public Endpoints](/infra/public-endpoints) |
## Learn more
What makes Injective different, performance benchmarks, and frequently asked questions
Tutorials, guides, and resources on the Injective developer learning portal
# Archival Node Setup and Recovery
Source: https://docs.injective.network/infra/archival-setup
Setup, configuration, and recovery guide for Injective archival nodes. Covers deploying a segmented archival fleet behind a gateway proxy, as well as restoring archival nodes after coordinated upgrades or unscheduled halts.
**Looking for pruned snapshots or validator recovery?** See the [Validator Troubleshooting Guide](/infra/coordinated-upgrades) for pruned snapshot resources and coordinated security upgrade recovery procedures.
**Restoring after an upgrade or halt?** Skip to [Restoring Archival Nodes After an Upgrade or Halt](#restoring-archival-nodes-after-an-upgrade-or-halt) for recovery procedures.
## Archival Setup
To make serving archival data more accessible we split the data into smaller segments. These segments are stored in `s3://injective-snapshots/mainnet/subnode`
The bucket is publicly accessible. No AWS credentials are required:
```bash theme={null}
# List all available segments using AWS CLI
aws s3 ls --no-sign-request s3://injective-snapshots/mainnet/subnode/
# Or without AWS CLI, using curl
curl -s "https://injective-snapshots.s3.amazonaws.com/?prefix=mainnet/subnode/&delimiter=/" \
| tr '<' '\n' | sed -n 's/^Prefix>mainnet\/subnode\/\([^/]*\)\/.*/\1/p' | sort -n
```
#### Naming convention
Directory names encode the block height range in millions. For example, `138150` contains blocks from 138M to 150M. Where segments overlap (e.g., `8896` vs `8898`, or `119127` vs `119141` vs `119143`), the newer or wider segment typically includes corrections or extends coverage. Pick the segment that best covers your target range without unnecessary overlap.
#### Available segments
This table may not include the latest segments. Run the `aws s3 ls` command above to check for newly published segments.
| Snapshot Dir | Height Range | Injective Version | Recommended Disk Size |
| ------------ | ------------ | ----------------- | --------------------- |
| `0073` | 0 – 73M | v1.12.1 | 42 TiB |
| `6068` | 60M – 68M | v1.12.1 | 7 TiB |
| `7380` | 73M – 80M | v1.12.1 | 7 TiB |
| `8088` | 80M – 88M | v1.13.3 | 7 TiB |
| `8896` | 88M – 96M | v1.13.3 | 7 TiB |
| `8898` | 88M – 98M | v1.13.3 | 7 TiB |
| `98106` | 98M – 106M | v1.13.3 | 7 TiB |
| `98107` | 98M – 107M | v1.14.0 | 7.5 TiB |
| `66101` | 66M – 101M | v1.14.0 | 27 TiB |
| `105116` | 105M – 116M | v1.15.0 | 7.5 TiB |
| `113127` | 113M – 127M | v1.15.0 | 11 TiB |
| `119127` | 119M – 127M | TBD | TBD |
| `119141` | 119M – 141M | TBD | TBD |
| `119143` | 119M – 143M | v1.17.0 | 16 TiB |
| `138150` | 138M – 150M | v1.17.2 | 5.8 TiB |
| `150181` | 150M – 181M | Coming soon | Coming soon |
Rows marked **TBD** or **Coming soon** are segments that exist in S3 but have not yet been verified for version and disk size. Contact the infra team or check the segment metadata before using these.
#### Choosing segments for full coverage
Several segments overlap in height range. You do not need all of them. Here is a recommended minimal set for full archival coverage from genesis to the current chain tip:
| Node | Segment | Height Range | Notes |
| ---- | -------- | ------------- | ------------------------------------------------------------------------------ |
| 1 | `0073` | 0 – 73M | Genesis through early chain history |
| 2 | `7380` | 73M – 80M | |
| 3 | `8088` | 80M – 88M | |
| 4 | `8898` | 88M – 98M | Use `8898` over `8896` for wider coverage |
| 5 | `98107` | 98M – 107M | Use `98107` over `98106` for wider coverage and newer binary |
| 6 | `105116` | 105M – 116M | Overlaps with previous, provides redundancy |
| 7 | `119143` | 119M – 143M | Use widest segment to minimize node count |
| 8 | `138150` | 138M – 150M | |
| 9 | `150181` | 150M – 181M | Latest segment, covers up to recent chain height |
| Tip | pruned | latest blocks | A pruned node covers the gap between the newest segment and the live chain tip |
The **pruned tip node** is essential. Archival segments are static snapshots that do not sync with the live chain. The gateway routes queries for recent blocks to the pruned node, which stays synced via p2p. See the [gateway config](#gateway-configuration) below for how to configure the tip node with `blocks: [1000]`.
These segments are stitched together via gateway which is an aggregator proxy that routes queries to the appropriate node based on block range
## System Requirements
Each node hosting a slice of archival data should have the following minimum requirements
| Component | Minimum Specification | Notes |
| ----------- | --------------------- | ---------------------------------------------------------- |
| **CPU** | AMD EPYC™ 9454P | 48 cores / 96 threads |
| **Memory** | 128 GB DDR5 ECC | DDR5-5200 MHz or higher, ECC for data integrity |
| **Storage** | 7 – 40 TB NVMe Gen 4 | PCIe 4.0 drives, can be single drives or in a RAID-0 array |
## Setup Steps
### On each node hosting an archival segment:
#### 1. Download the archival segments with the history your setup requires using
```bash theme={null}
# SNAPSHOT_DIR matches the "Snapshot Dir" column in the table above (0073, 138150)
aws s3 cp --no-sign-request --recursive s3://injective-snapshots/mainnet/subnode/$SNAPSHOT_DIR $INJ_HOME
```
#### 2. Download or set the appropriate injective binary or image tag based on the table above
#### 3. Generate your config folder
```bash theme={null}
injectived init $MONIKER --chain-id injective-1 --home $INJ_HOME --overwrite
```
#### 4. Disable pruning in your app.toml file and block p2p and set the log level to error in your config.toml files.
This ensures that the data does not get pruned and the node stays in a halted state. Setting the log level to error lessens disk ops and improves performance.
```bash theme={null}
# Disable pruning in app.toml
sed -i 's/^pruning *= *.*/pruning = "nothing"/' $INJ_HOME/config/app.toml
# Disable p2p and disable create empty blocks on config.toml
awk '
BEGIN { section = "" }
/^\[/ {
section = $0
}
section == "[p2p]" {
if ($1 ~ /^laddr/) $0 = "laddr = \"tcp://0.0.0.0:26656\""
if ($1 ~ /^max_num_inbound_peers/) $0 = "max_num_inbound_peers = 0"
if ($1 ~ /^min_num_inbound_peers/) $0 = "min_num_inbound_peers = 0"
if ($1 ~ /^pex/) $0 = "pex = false"
if ($1 ~ /^seed_mode/) $0 = "seed_mode = false"
}
section == "[consensus]" {
if ($1 ~ /^create_empty_blocks/) $0 = "create_empty_blocks = false"
}
{ print }
' $INJ_HOME/config/config.toml > $INJ_HOME/config/config.tmp && mv $INJ_HOME/config/config.tmp $INJ_HOME/config/config.toml
# Set log level to error (less disk writes = better performance)
sed -i 's/^log_level *= *.*/log_level = "error"/' $INJ_HOME/config/app.toml
```
#### 5. Run your node
```bash theme={null}
injectived start --home $INJ_HOME
```
### Gateway configuration
Gateway is a reverse proxy that routes RPC, gRPC, and API queries to the correct archival node based on the requested block height. The reference implementation below is from [Decentrio](https://github.com/decentrio/gateway), an ecosystem contributor.
Gateway it inspects incoming requests, determines the block height, and forwards to the upstream node that holds that range. Any height aware reverse proxy (nginx, Caddy, HAProxy with custom routing) can serve the same purpose.
#### 1. Clone the gateway repository
```bash theme={null}
git clone https://github.com/decentrio/gateway
```
#### 2. Build gateway
```bash theme={null}
make build
```
#### 3. Create your config file
```yaml theme={null}
upstream:
# example node 1 holds blocks 0-80M while node 2 holds blocks 80-88M
- rpc: "http://$NODE1:$RPC_PORT"
grpc: "$NODE1:$GRPC_PORT"
api: "http://$NODE1:$API_PORT"
blocks: [0,80000000]
- rpc: "http://$NODE2:$RPC_PORT"
grpc: "$NODE2:$GRPC_PORT"
api: "http://$NODE2:$API_PORT"
blocks: [80000000,88000000]
#
# Archival tip, this serves the latest x blocks, usually set as a pruned node
- rpc: "http://$PRUNED_NODE:$RPC_PORT"
grpc: "$PRUNED_NODE:$GRPC_PORT"
api: "http://$PRUNED_NODE:$API_PORT"
blocks: [1000]
ports:
rpc: $RPC_PORT
api: $API_PORT
grpc: $GRPC_PORT
# Leave these as zero to disable for now
jsonrpc: 0
jsonrpc_ws: 0
```
#### 4. Run Gateway
```bash theme={null}
gateway start --config $CONFIG_FILE
```
***
## Restoring Archival Nodes After an Upgrade or Halt
Archival segment nodes are static so they do not participate in consensus or sync via p2p. However, they can still be affected by coordinated upgrades or unscheduled chain halts, particularly the **pruned tip node** and any segment nodes that cover recent block heights.
### Pruned tip node
The pruned tip node is the only node in the archival fleet that actively syncs with the live chain. During a coordinated upgrade or unscheduled halt, treat it the same as any other full node:
1. Stop the node
2. Swap to the new binary
3. Verify: `injectived version`
4. Start the node
The tip node does not have `priv_validator_state.json` (it is not a validator), so there is no double-signing risk. If the tip node's state is corrupted (AppHash mismatch), restore it from a pruned snapshot. See [Validator Troubleshooting & Snapshot Resources](/infra/coordinated-upgrades#snapshot-resources).
### Segment nodes covering recent heights
If an upgrade changes how historical blocks are processed or stored (a state migration that affects query results), segment nodes covering heights near the upgrade boundary may return inconsistent data. In this case:
1. Stop the affected segment node
2. Download the updated segment from S3 (the infra team may publish a corrected segment after the upgrade):
```bash theme={null}
aws s3 cp --no-sign-request --recursive s3://injective-snapshots/mainnet/subnode/$SNAPSHOT_DIR $INJ_HOME
```
3. Update the binary to the version specified for that segment
4. Restart the node
### Older segment nodes
Segment nodes covering historical block ranges (`0073`, `8088`) are generally unaffected by chain upgrades. They serve pre-existing data with the binary version that produced it. No action is needed unless the upgrade explicitly changes how historical queries are handled.
### Gateway
The gateway itself is stateless and does not need to be upgraded during a chain upgrade. However, if you add or replace segment nodes, update the gateway config to reflect the new upstream endpoints and restart it.
After any restoration, verify that the gateway is routing correctly by querying a block from each segment range and confirming the response. A misconfigured gateway can silently serve errors or stale data for specific height ranges.
# Validator Troubleshooting
Source: https://docs.injective.network/infra/coordinated-upgrades/index
Troubleshooting reference and operational guide for Injective validators and node operators. Covers common failure modes, recovery procedures, upgrade checklists, and critical safety practices.
***
## Before You Begin: Critical Safety Rules
**What to never do**
1. **Never run two nodes with the same `priv_validator_key.json` simultaneously.** This causes double-signing, 0% slash, but permanent tombstoning. The validator can never rejoin the active set.
2. **Never use `unsafe-reset-all` on a validator node** unless you fully understand the consequences. It wipes consensus state and can produce conflicting votes that lead to tombstoning.
3. **Never back up `priv_validator_state.json` while the node is still running.** The file changes continuously during consensus. Stop the node fully first, then copy it.
4. **Never start a node after snapshot recovery without restoring your backed up `priv_validator_state.json`.** Snapshots do not include this file. Starting without it resets your signing state to height 0, which causes double-signing.
5. **Never rollback an archival or large history node without testing first.** Rollback on large databases can take hours or brick the node entirely, especially with PebbleDB.
**What to always do**
1. **Always back up `priv_validator_state.json` after fully stopping the node** and before any recovery operation (rollback, snapshot restore, binary upgrade).
2. **Always restore `priv_validator_state.json`** to its original location after rollback or snapshot recovery, before starting the node.
3. **Always verify `priv_validator_state.json` is intact before starting the node.** Check that the height, round, and step values are sensible.
4. **Always set `--halt-height` for coordinated upgrades.** Nodes that skip the halt-height flag require a harder recovery path.
5. **Always verify the binary version** with `injectived version` before starting after an upgrade.
6. **Always undo temporary consensus overrides** (like `--unsafe-consensus-timeout-precommit-delta=1ms`) once the chain stabilizes.
**Need a snapshot to recover?** See [Snapshot Resources](#snapshot-resources) at the bottom of this page for pruned snapshots and community providers. For archival segment snapshots, see the [Archival Setup](/infra/archival-setup) page.
***
## Common Problems and Solutions
### Node stuck at upgrade height
**Symptoms:**
* Node logs show the chain halted at the expected upgrade height
* `latest_block_height` does not advance past the halt height
**Cause:** The node is running the old binary. The chain requires the upgraded binary to proceed past the upgrade height.
**Solution:**
1. Stop the node
2. Back up `~/.injectived/data/priv_validator_state.json`
3. Install the new binary
4. Verify: `injectived version`
5. Start the node
***
### Block header hash mismatch after upgrade
**Symptoms:**
```
ERR prevote step: consensus deems this block invalid; prevoting nil
err="wrong Block.Header.LastResultsHash. Expected 284C339C..., got 694706724..."
```
The node continuously prevotes nil and cannot finalize the next block.
**Cause:** The node's local state at the upgrade height is inconsistent with the new binary's expected state. This happens when the node processed the halt-height block with the old binary logic before upgrading.
**Solution:**
1. Stop the node
2. Back up `priv_validator_state.json`
3. Rollback one block:
```bash theme={null}
injectived rollback
```
4. Restore `priv_validator_state.json`
5. Start the node with the new binary
Rollback on archival nodes with large databases (hundreds of GB) can take a very long time or fail. For these nodes, recover from a snapshot instead.
***
### Conflicting vote warnings
**Symptoms:**
```
ERR Found conflicting vote from ourselves; did you unsafe_reset a validator?
height=181027006 module=consensus round=40 type=SIGNED_MSG_TYPE_PREVOTE
```
This message repeats continuously.
**Cause:** The node's `priv_validator_state.json` was reset or corrupted, so it is resigning at rounds it already signed for, producing conflicting votes.
**Common triggers:**
* Ran `unsafe-reset-all` on a validator
* Restored an old or incorrect `priv_validator_state.json`
* Backed up `priv_validator_state.json` while the node was still running (stale copy)
* Rollback command reset the validator state
**Solution:**
* If you have a correct backup of `priv_validator_state.json` (taken after fully stopping the node): restore it and restart.
* If you do not have a correct backup: **stop the node immediately and wait for block production to resume before restarting.** Starting with incorrect state risks double-signing.
* Check signing info:
```bash theme={null}
injectived query slashing signing-info
```
If conflicting votes are detected on-chain, the validator will be tombstoned (permanently jailed). There is no on-chain unjail for double-signing.
***
### Validator tombstoned (double-signed)
**Symptoms:**
```
injectived query slashing signing-info injvalcons1...
tombstoned: true
jailed_until: "9999-12-31T23:59:59Z"
```
**Cause:** The validator signed two different blocks or prevotes at the same height. Common triggers:
* Running two instances of the node with the same signing key
* Restoring an old `priv_validator_state.json` that caused re-signing at a previously signed height
* Rollback resetting signing state, followed by the node signing a conflicting block
**Resolution:**
There is no standard on-chain unjail for tombstoned validators. Resolution paths:
* A governance proposal or upgrade handler that explicitly un-tombstones affected validators
* Creating a new validator with a new operator key (loses existing delegations)
**If you were tombstoned during a coordinated security upgrade**, reach out to the Injective team in the verified validators channels immediately. During coordinated upgrades, the team may include an un-tombstone handler in the upgrade binary to restore affected validators. Time sensitive, report as soon as possible so you can be included before the upgrade handler is finalized.
**Prevention:**
* Use TMKMS or Horcrux for hardware-enforced single-signer semantics
* Always back up `priv_validator_state.json` after stopping the node
* Never run two nodes with the same signing key simultaneously
***
### Recovering from a snapshot
**When to use:** When rollback fails, takes too long, or the node state is corrupted beyond repair (apphash mismatch).
**Procedure:**
1. Stop the node completely
2. Back up `~/.injectived/data/priv_validator_state.json`
3. Back up `~/.injectived/config/priv_validator_key.json` if not already backed up elsewhere
4. Download a snapshot from a trusted provider:
* Injective Team may also share emergency snapshots before or during security upgrades
* [Polkachu Injective Snapshots](https://polkachu.com/tendermint_snapshots/injective) (typically goleveldb)
* Community validators may share emergency snapshots during incidents
5. Remove old data:
```bash theme={null}
rm -rf ~/.injectived/data
```
6. Extract the snapshot into `~/.injectived/data/`
7. **Restore your backed-up `priv_validator_state.json` into `~/.injectived/data/`**
8. Verify `priv_validator_state.json` is present and correct
9. Start the node
#### Why you must keep your own `priv_validator_state.json`
Snapshots contain blockchain data (blocks, application state) but never include your validator's signing state. The signing state tracks the last height, round, and step your validator signed at. If you lose it or replace it with a blank one, your node doesn't know what it already signed and may sign a conflicting block -- causing double-signing and permanent tombstoning.
Here's a practical example showing why the signing state must always be ahead of (or equal to) the snapshot height.
**Setup:** A coordinated chain upgrade with halt-height at block 125.
```
Block: 100 105 110 115 120 125 126
| | | | | | |
| | | | | | Chain resumes with new binary
| | | | | Chain halts here (upgrade height)
| | | | |
| | | Snapshot taken at block 115
| | |
Your node has been signing every block...
```
At the halt, your `priv_validator_state.json` reads:
```json theme={null}
{
"height": "126",
"round": 42,
"step": 3,
"signature": "...",
"signbytes": "..."
}
```
**Why does it say height 126 if the halt height was 125?** The `--halt-height` flag prevents block 126 from being *committed* (finalized), but the CometBFT consensus engine still enters height 126 and begins its rounds -- proposing, prevoting, and precommitting -- before the application layer refuses to process the block and the node shuts down. The high round number (42) reflects the network churning through rounds as validators gradually stop and consensus can't be reached. This is normal behavior: your validator cast real votes at height 126 even though that block was never finalized.
This means your validator has already cast votes at height 126, round 42. The available snapshot is from height 115.
**What happens if you restore the snapshot WITHOUT your signing state:**
The snapshot comes with either no `priv_validator_state.json` or a blank one (height 0). If you start the node this way:
1. Your node loads blockchain data from height 115
2. It catches up to height 126 by replaying blocks 116-125
3. At height 126, it enters consensus and starts signing from round 0
4. But the network already has your votes from round 42 at this height
5. Your node signs a different block proposal at height 126 (because it replayed with the new binary and may produce a different result)
6. The network detects two conflicting signatures from your validator key
7. **Your validator is double-signed and tombstoned. Permanently.**
**The correct procedure:**
1. Stop the node
2. Back up your `priv_validator_state.json` (it says height 126, round 42)
3. Delete your data directory
4. Extract the snapshot (blockchain data from height 115)
5. **Copy your backed-up `priv_validator_state.json` back into the data directory**
6. Start the node
Now when the node starts:
1. It loads blockchain data from height 115
2. It catches up to height 126 by replaying blocks 116-125
3. At height 126, it reads the signing state: "I already signed up to round 42"
4. It skips rounds 0-42 and waits for round 43 before signing anything new
5. No conflicting votes. Your validator is safe.
**Key rule:** The signing state must always reflect the highest point your validator has ever signed at. It is safe for the signing state to be ahead of the snapshot (the node will catch up). It is never safe for the signing state to be behind what the validator actually signed on the network.
| Scenario | Signing state height | Snapshot height | Safe? |
| --------------------------- | -------------------- | --------------- | ------------------------------------------------------------------------ |
| Normal recovery | 126 | 115 | Yes -- node replays 116-125, skips already-signed rounds at 126 |
| Fresh snapshot, kept state | 126 | 120 | Yes -- same logic, fewer blocks to replay |
| Snapshot without state | 0 | 115 | **No** -- will re-sign at heights it already signed, causing double-sign |
| Stale backup (taken at 110) | 110 | 115 | **No** -- will re-sign at heights 111-126 |
**Database backend compatibility:** Snapshots are backend specific. A goleveldb snapshot will not work on a node configured for pebbledb, and vice versa. Check your configuration:
```bash theme={null}
grep db_backend ~/.injectived/config/config.toml
```
***
### Rollback hangs or fails
**Symptoms:** `injectived rollback` runs but never completes, or produces errors.
**Causes:**
* **PebbleDB backend:** Rollback with pebble has known issues and may hang indefinitely
* **Very large database:** Rollback time scales with database size
* **Corrupted WAL** (Write-Ahead Log)
**Solutions:**
* For PebbleDB nodes: recover from a snapshot instead
* For large nodes: if rollback hasn't completed in 30+ minutes, abort and use a snapshot
* Consider switching to goleveldb. PebbleDB is more space efficient but less tested for rollback
***
### Round regression errors during catchup
**Symptoms:**
```
ERR Failed signing vote err="error signing vote: round regression at height 181027006.
Got 108, last round 141"
```
**Cause:** The node is catching up to the current consensus round. This is normal and expected when restarting a node during an active consensus round that has advanced through many rounds.
**Solution:** No action needed. The node will catch up. To speed up round catch-up:
```bash theme={null}
# Flag
--unsafe-consensus-timeout-precommit-delta=1ms
# Or environment variable
INJECTIVED_UNSAFE_CONSENSUS_TIMEOUT_PRECOMMIT_DELTA=1ms
```
**Remove this override once the chain stabilizes and the node is caught up.** Revert to the default (100ms) and restart.
If left on during normal operation, the 1ms delta causes rounds to advance far faster than votes can propagate across the network. Your validator moves to the next round before other validators' precommits arrive, so its votes miss the actual commit round. The result is a steadily climbing missed-block counter, and if enough blocks are missed (typically 500 of the last 10,000), **the validator will be jailed for downtime**.
***
### AppHash mismatch
**Symptoms:**
```
panic: Tendermint state.AppHash does not match AppHash after replay
```
**Cause:** The application state diverged from the consensus state. Can result from an incomplete upgrade, a crash during block execution, a corrupted database, or a missing `wasm/` directory after state sync (see below).
**Solution:**
1. Try rollback: `injectived rollback`
2. If rollback fails or the error persists: recover from a snapshot
3. Always restore `priv_validator_state.json` after recovery
***
### AppHash mismatch after CometBFT state sync
**Symptoms:** AppHash error immediately after a successful CometBFT state sync restore, at the very first block.
**Cause:** CometBFT state sync (the P2P protocol configured in `[statesync]` in `config.toml`) restores the IAVL state tree but does **not** restore the `wasm/` directory containing compiled CosmWasm contract bytecode. Without these files, the locally computed app hash will not match the network's.
**Solution:** Use a manual snapshot restore instead of state sync. Injective's official snapshots include the `wasm/` directory. If you must use state sync, obtain the `wasm/` directory from a trusted source and place it at `~/.injectived/data/wasm/` before starting the node. See [Joining a Network — Sync Methods](/infra/join-a-network) for details on the difference between state sync and snapshot restore.
***
### Sentry nodes stuck after upgrade
**Symptoms:** The validator node is upgraded and signing, but sentry nodes remain stuck at the old block height.
**Cause:** Sentry nodes also need the new binary to process blocks past the upgrade height.
**Solution:** Upgrade sentry nodes to the same binary version as the validator. Sentries do not have `priv_validator_state.json` (they don't sign), but they do require the correct binary.
***
### Chain stalled after upgrade (insufficient voting power)
**Symptoms:**
* The chain does not produce new blocks after the upgrade height
* Consensus round numbers climb continuously (round 50, 100, 200+)
* Nodes log prevotes and precommits but no block is committed
* `latest_block_height` remains stuck at the halt height
**Cause:** CometBFT requires 2/3+ of total voting power to be online and participating with the correct binary to finalize a block. During a coordinated upgrade, there is a window where validators are upgrading at different speeds. Until enough voting power is running the new binary, the network cannot reach consensus.
What happens during this window:
1. The chain halts at the upgrade height (ex: block 125)
2. Validators that have upgraded start participating in consensus at height 126
3. Each round, the network attempts to finalize block 126 but fails because less than 2/3 of voting power is voting
4. The round number increments and the cycle repeats, round 1, 2, 3, ... 50, ... 100+
5. This continues until enough validators complete the upgrade and come online
**This is normal during upgrades.** The chain is not broken, it is waiting for quorum.
**What to do:**
* **If you have already upgraded:** No action needed. Your node is participating and will automatically finalize the next block once quorum is reached. You will see round regression errors in the logs as your node catches up through rounds. This is expected (see [Round regression errors during catchup](#round-regression-errors-during-catchup)).
* **If you have not yet upgraded:** You are part of the missing voting power. Complete the upgrade as quickly as possible to help the chain resume.
* **Monitor progress:** Check how many validators are online and participating:
```bash theme={null}
curl -s localhost:26657/consensus_state | jq '.result.round_state.votes'
```
* **Coordinate in validator channels:** During major upgrades, validators typically coordinate in the verified validators channels to track upgrade progress and voting power percentage.
The longer this window lasts, the higher the round numbers climb. Validators that come online later will need to catch up through all those rounds, which is why the `--unsafe-consensus-timeout-precommit-delta=1ms` flag exists but remember to [remove it after the chain stabilizes](#round-regression-errors-during-catchup).
***
### Jailed for downtime (not tombstoned)
**Symptoms:**
* Validator shows as inactive or jailed
* `tombstoned: false` in signing info
* Missed blocks counter is high
**Cause:** The validator missed too many consecutive blocks (typically 500 of the last 10,000).
**Solution:**
1. Ensure the node is running and fully caught up to the latest block
2. Unjail:
```bash theme={null}
injectived tx slashing unjail \
--from= \
--chain-id=injective-1 \
--gas=auto \
--gas-adjustment=1.5
```
3. Verify the validator is active again:
```bash theme={null}
injectived query staking validator
```
***
## Coordinated Upgrade Checklist
### Before the upgrade height
* [ ] Confirm the target halt-height from the governance proposal or chain team
* [ ] Download and verify the new binary (check sha256 checksums)
* [ ] Set `--halt-height=` in node config or CLI flags
* [ ] If using Cosmovisor: place the new binary in the correct upgrade directory
* [ ] Back up `priv_validator_state.json`
* [ ] Identify snapshot providers in case rollback is needed (see [Snapshots - Chain Portal](https://portal.injective.network/snapshots))
* [ ] If planning to use CometBFT state sync for recovery: ensure you have a copy of `~/.injectived/data/wasm/`. State sync does not restore this directory and its absence causes AppHash mismatches
* [ ] Monitor chain progression toward the halt height
### At the upgrade height
* [ ] Confirm the node has halted at the expected height
* [ ] Stop the node fully (verify the process is dead)
```bash theme={null}
ps aux | grep injectived
```
* [ ] Back up `priv_validator_state.json`
* [ ] Install the new binary
* [ ] Verify the version:
```bash theme={null}
injectived version
```
### After the upgrade
* [ ] Start the node with the new binary
* [ ] Monitor logs for consensus participation
* [ ] Confirm prevotes are correct (not voting nil on every round)
* [ ] Monitor voting power on an explorer ([Mintscan](https://www.mintscan.io/injective/validators))
* [ ] If seeing `wrong Block.Header.LastResultsHash`: stop, rollback 1 block, restore `priv_validator_state.json`, restart
* [ ] If using round-catchup overrides (`--unsafe-consensus-timeout-precommit-delta`): remove them once stable
* [ ] Verify the validator is signing:
```bash theme={null}
curl -s localhost:26657/consensus_state | jq '.result.round_state["height/round/step"]'
```
***
## Monitoring Reference
### Consensus and sync status
```bash theme={null}
# Current consensus state (height, round, step)
curl -s localhost:26657/consensus_state \
| jq '.result.round_state["height/round/step"] | split("/") | {height: .[0], round: .[1], step: .[2]}'
# Latest block height
curl -s localhost:26657/status | jq '.result.sync_info.latest_block_height'
# Whether the node is still catching up
curl -s localhost:26657/status | jq '.result.sync_info.catching_up'
```
### Validator health
```bash theme={null}
# Signing info (missed blocks, jail status, tombstone status)
injectived query slashing signing-info $(injectived tendermint show-validator)
# Validator status and jail state
injectived query staking validator --output json | jq '.status, .jailed'
```
### Node configuration
```bash theme={null}
# Current binary version
injectived version
# Current priv_validator_state (check height/round/step)
cat ~/.injectived/data/priv_validator_state.json | jq
# Database backend
grep db_backend ~/.injectived/config/config.toml
```
***
## Pruned vs Archival Nodes
| | Pruned Node | Archival Node |
| ------------------------ | ------------------------------------ | ------------------------------------- |
| **Rollback** | Fast (minutes) | Slow (minutes to hours), may fail |
| **Snapshot recovery** | Fast (10-30 min) | Very slow (hours, snapshots 100+ GB) |
| **Typical DB size** | 10-50 GB | 500+ GB |
| **Recommended recovery** | Rollback first, snapshot as fallback | Snapshot preferred, rollback is risky |
***
## Additional Resources
* [Run an Injective Node](/infra/run-node)
* [Cosmovisor Setup](/infra/cosmovisor)
* [Upgrade a Node](/infra/upgrade-node)
* [Cosmos Validators FAQ](https://github.com/cosmos/cosmos/blob/master/VALIDATORS_FAQ.md)
* [CometBFT Running in Production](https://docs.tendermint.com/v0.34/tendermint-core/running-in-production.html)
***
## Snapshot Resources
**[Snapshots - Chain Portal](https://portal.injective.network/snapshots)**
Download links for Injective-provided pruned snapshots (EU and Asia regions), community snapshot providers, archival segment data, and usage instructions. The Chain Portal is the canonical, up-to-date source for all snapshot resources.
For archival segment snapshots and segmented node fleet setup, see the [Archival Setup](/infra/archival-setup) page.
During coordinated security upgrades, the Injective team may also share emergency snapshots in the verified validators channels.
**Database backend compatibility:** Snapshots are backend specific. A goleveldb snapshot will not work on a node configured for pebbledb, and vice versa. Injective's official snapshots use goleveldb. PebbleDB snapshots are not maintained. Check your configuration before downloading:
```bash theme={null}
grep db_backend ~/.injectived/config/config.toml
```
**You must restore your backed up `priv_validator_state.json`** into the data directory before starting the node after a snapshot recovery. See [Recovering from a snapshot](#recovering-from-a-snapshot) above for the full procedure.
# Cosmovisor Setup Guide for the Injective Network
Source: https://docs.injective.network/infra/cosmovisor
Cosmovisor is a process manager designed for Cosmos SDK–based blockchains that simplifies the management of binary (chain) upgrades. This guide provides step‐by‐step instructions to set up Cosmovisor for your Injective Network node.
> **Note:** These instructions assume you already have an existing chain binary (e.g., `injectived`) and a working Go environment if you choose to install Cosmovisor from source. Adjust the names and paths as needed for your specific setup.
***
## Table of Contents
1. [Installation](#installation)
* [Installing via Go](#installing-via-go)
2. [Environment Variables](#environment-variables)
3. [Directory Structure](#directory-structure)
4. [Running Cosmovisor](#running-cosmovisor)
5. [Handling Chain Upgrades](#handling-chain-upgrades)
6. [Running Cosmovisor as a Systemd Service](#running-cosmovisor-as-a-systemd-service)
***
## Installation
### Installing via Go
If you have Go installed, you can install Cosmovisor with the following command:
```bash theme={null}
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0
```
> **Tip:** Ensure that your Go binary installation path (commonly `$GOPATH/bin` or `$HOME/go/bin`) is added to your system’s `PATH`. You can verify the installation by running:
>
> ```bash theme={null}
> which cosmovisor
> ```
## Environment Variables
Set up the following environment variables so that Cosmovisor knows which binary to run and where to locate it:
* **`DAEMON_NAME`**\
The name of your chain’s binary (e.g., `injectived`).
* **`DAEMON_HOME`**\
The home directory for your node (e.g., `~/.injectived`).
You can set these variables in your shell’s profile (like `~/.bashrc` or `~/.profile`) or export them directly in your terminal session:
```bash theme={null}
export DAEMON_NAME=injectived
export DAEMON_HOME=~/.injectived
```
***
## Directory Structure
Cosmovisor expects a specific folder structure in your node’s home directory:
1. **Create the Genesis Directory**
This directory holds the initial (genesis) binary.
```bash theme={null}
mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin
```
2. **Copy Your Current Binary**
Place your current chain binary (e.g., `injectived`) into the genesis folder. Make sure the file name matches the `DAEMON_NAME` value (see next section).
```bash theme={null}
cp $(which injectived) $DAEMON_HOME/cosmovisor/genesis/bin/injectived
```
***
## Running Cosmovisor
Instead of running your chain’s binary directly, start your node with Cosmovisor by executing:
```bash theme={null}
cosmovisor run start
```
Cosmovisor will:
* Look for the binary in `$DAEMON_HOME/cosmovisor/genesis/bin` (or the appropriate upgrade folder).
* Start your node using that binary.
* Monitor for any on-chain upgrade signals and automatically switch binaries when needed.
***
## Handling Chain Upgrades
When an upgrade is announced on-chain, prepare the new binary so Cosmovisor can switch to it automatically:
1. **Create an Upgrade Directory**
Use the upgrade name provided on-chain (e.g., `v1.14.0`):
```bash theme={null}
mkdir -p $DAEMON_HOME/cosmovisor/upgrades//bin
```
2. **Place the New Binary**
Compile or download the new binary, then copy it into the upgrade directory. Ensure the binary name matches `DAEMON_NAME`.
```bash theme={null}
cp /path/to/new/injectived $DAEMON_HOME/cosmovisor/upgrades//bin
cp /path/to/new/libwasmvm.x86_64.so $DAEMON_HOME/cosmovisor/upgrades//bin
```
> **TIP:** If you have downloaded the `injectived` binary package from GitHub, we copy `libwasmvm.x86_64.so` to the upgrade `bin` directory. An environment variable will be later added to the systemd service to add this directory to `LD_LIBRARY_PATH`.
3. **Upgrade Process**
When the upgrade height is reached, Cosmovisor will detect the scheduled upgrade and automatically switch to the binary located in the corresponding upgrade folder.
***
## Running Cosmovisor as a Systemd Service
For production environments, it is common to run your node as a systemd service. Below is an example service file.
1. **Create the Service File**
Create a file (e.g., `/etc/systemd/system/injectived.service`) with the following content. Adjust the paths and `` accordingly:
```ini theme={null}
[Unit]
Description=Injective Daemon managed by Cosmovisor
After=network-online.target
[Service]
User=
ExecStart=/home//go/bin/cosmovisor run start
Restart=always
RestartSec=3
Environment="DAEMON_NAME=injectived"
Environment="DAEMON_HOME=/home//.injectived"
Environment="PATH=/usr/local/bin:/home//go/bin:$PATH"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=false"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="LD_LIBRARY_PATH=/home//.injectived/cosmovisor/current/bin"
[Install]
WantedBy=multi-user.target
```
2. **Enable and Start the Service**
```bash theme={null}
sudo systemctl daemon-reload
sudo systemctl enable injectived.service
sudo systemctl start injectived.service
```
3. **Check Logs**
Verify that your service is running smoothly:
```bash theme={null}
journalctl -u injectived.service -f
```
***
# Hardware Requirements
Source: https://docs.injective.network/infra/hardware
Minimum and recommended hardware specifications for running Injective nodes
Injective's sub-second block times and high-throughput transaction processing place significant demands on hardware. Underprovisioned nodes will fall behind the chain tip, miss blocks (validators), or serve stale data (RPC nodes).
## Validator nodes
Validator nodes participate in consensus and must maintain consistent performance to avoid jailing. Bare metal servers are strongly recommended.
| *Minimum* | *Recommendation* |
| :-------------------: | :-------------------: |
| RAM Memory 128GB | RAM Memory 128GB |
| CPU 12 cores | CPU 16 cores |
| CPU base clock 3.7GHz | CPU base clock 4.2GHz |
| Storage 2TB NVMe | Storage 2TB NVMe |
| Network 1Gbps+ | Network 1Gbps+ |
### Production validator reference
For reference, production validators on the Injective mainnet typically run hardware in the following range:
| Component | Typical configuration |
| ----------- | ---------------------------------------------------------------------------- |
| **CPU** | AMD EPYC 4585PX or AMD Ryzen 9 9950X (16 cores / 32 threads, 5.75 GHz boost) |
| **RAM** | 128 GB DDR5 @ 3600 MT/s |
| **Storage** | 2x \~2 TB NVMe in RAID0, \~3.5 TiB usable |
| **Network** | 10-25 Gbps |
High single-thread performance (boost clock) is more important than core count for block processing. The network requirements are well above the 1 Gbps minimum listed in the spec table; production validators benefit from 10 Gbps+ links for consistent peer connectivity and fast block propagation.
## RPC nodes
Non-validator nodes serving live chain data have similar requirements. While they don't risk slashing, underpowered nodes will lag behind the chain tip and serve unreliable data.
| *Minimum* | *Recommendation* |
| :-------------------: | :--------------------: |
| RAM Memory 64GB | RAM Memory 128GB |
| CPU 8 cores | CPU 16 cores |
| CPU base clock 3.4GHz | CPU base clock 4.0GHz+ |
| Storage 1TB NVMe/SSD | Storage 2TB NVMe |
| Network 1Gbps+ | Network 1Gbps+ |
**CPU clock speed matters more than core count.** Injective's block processing is largely single-threaded during execution. Nodes running on low-clock CPUs (e.g. 2.6GHz cloud instances) will consistently fall behind the chain tip regardless of core count.
## Storage
### Disk I/O requirements
Disk throughput is the most common bottleneck for Injective nodes. The chain produces blocks every \~650ms with potentially large transaction payloads, requiring sustained high IOPS.
* **Bare metal:** Local NVMe drives are ideal. RAID0 across multiple NVMe drives provides the best throughput.
* **Cloud (GCP):** Standard persistent disks (`pd-balanced`, `pd-ssd`) often cannot sustain the required IOPS. Use **local SSDs in RAID0** for the chain data directory. Local SSDs are ephemeral but chain state is easily recoverable from a [snapshot](https://chain-portal.injective.network/snapshots). Alternatively, provision `hyperdisk-balanced` or `hyperdisk-extreme` with explicitly provisioned IOPS and throughput.
* **Cloud (AWS):** Use `io2` or `gp3` volumes with provisioned IOPS. Instance store NVMe (e.g. `i3`, `i4i` instances) provides the best performance.
### Chain data growth
Injective chain data grows at approximately 10-15 GB per day. For pruned nodes, it is recommended to restore from a fresh snapshot every 300-400 GB to reclaim disk space and maintain performance. For snapshot download links, see the [Snapshots page on the Chain Portal](https://portal.injective.network/snapshots).
### Pruning configuration
Pruning removes old state to save disk space but is itself CPU and I/O intensive. On underpowered hardware, the pruning process can cause the node to fall behind the chain tip.
If your node is lagging and you suspect hardware limitations:
* Consider setting `pruning = "nothing"` in `app.toml` to eliminate the pruning overhead
* Periodically stop the node and restore from a snapshot to manage disk usage instead
* For zero-downtime operation, run two nodes and do rolling snapshot recovery
### Storage layout
Production validators typically run two NVMe drives in one of these configurations:
| Layout | How it works | Tradeoff |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **RAID0** | Stripes data across both drives in fixed chunks. Both drives are read/written in parallel, doubling potential IOPS and throughput. | No redundancy. If either drive fails, the array is lost. Requires full snapshot recovery. |
| **LVM linear** | Concatenates drives into a single volume. Fills one drive first, then spills onto the next. No parallel I/O speed gains. | No redundancy. If either drive fails, the entire Volume Group breaks and the database is corrupted. Constrains performance to a single drive's speed. |
Neither layout provides redundancy, but chain state does not require it. RAID0 is recommended to meet Injective's sub-second block time requirements. If a drive fails, recover from a [snapshot](https://chain-portal.injective.network/snapshots).
## Bare metal vs cloud
Bare metal servers are strongly recommended for validator nodes. The consistent CPU clock speeds and low-latency NVMe storage required for sub-second block times are difficult to guarantee on shared cloud infrastructure.
Cloud deployments can work for non-validator nodes if provisioned correctly (see storage guidance above), but operators should expect to invest more in monitoring and operational tooling to maintain reliability.
### Reference cloud instance types
The following instance types have been validated for Injective nodes. Use these as a starting point when provisioning.
| Provider | Instance | vCPU | RAM | Disk | Suitable for |
| -------- | -------------------------- | -------------------------------------------- | ------ | --------------------------- | ------------------------------------ |
| **GCP** | `c4-standard-16-lssd` | 16 vCPU (Emerald Rapids) | 64 GB | Local SSD (NVMe) | Sentries, RPC nodes |
| **OVH** | `b3-32` | 8 vCPU | 32 GB | Local SSD | Sentries (undersized for validators) |
| **OVH** | Dedicated (e.g. Advance-1) | 12 vCPU (Xeon E-2386G @3.5GHz or EPYC 4244P) | 128 GB | Physical NVMe (RAID0/RAID1) | Validators, full nodes |
Cloud instances with local SSDs (not persistent disks) are critical for performance. Standard persistent disks (`pd-balanced`, `pd-ssd`) cannot sustain the IOPS required for Injective's block throughput. Local SSDs are ephemeral, but chain state is recoverable from a snapshot.
## Networking and peering
Nodes need a healthy set of peers to stay in sync. Insufficient peering, especially in regions far from the majority of the validator set, can cause intermittent sync lag.
* Maintain **15-25 active peers** minimum. Check with `curl -s localhost:26657/net_info | jq '.result.n_peers'`
* Configure **persistent peers** in `config.toml` to ensure your node always has known-good connections. See [Peer discovery](/infra/join-a-network#peer-discovery) for community-maintained peer lists
* Nodes in regions with fewer Injective peers (e.g. Asia-Pacific) may need additional persistent peers to maintain consistent sync
* Use **Premium Tier networking** on cloud providers for lower latency and more consistent routing
# Overview
Source: https://docs.injective.network/infra/index
Run, upgrade, and maintain Injective validator nodes, sentry nodes, and supporting infrastructure.
This section covers everything needed to operate on the Injective network: setting up nodes, managing upgrades, running the bridge orchestrator, and configuring indexer services.
Node types, network topology, consensus layer, and process management
Set up your keyring, configure validators, interact with nodes, and manage Cosmovisor
Install the binary, sync with mainnet or testnet via snapshots, state sync, or block sync
Chain upgrade procedures, Coordinated Upgrades troubleshooting, and the Operator Portal
***
## Architecture
Injective uses CometBFT (formerly Tendermint) consensus with a Byzantine Fault Tolerant (BFT) mechanism, achieving sub-second finality. The network requires bare metal servers with enterprise-grade specifications (128GB RAM, high-clock-speed CPUs, NVMe storage) to handle high-throughput trading activity.
### Node types
| Node | Role |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Validator Node** | Participates in consensus by proposing and voting on blocks. Requires staked INJ as collateral and high-availability hardware. |
| **[Sentry Node](/developers/concepts/sentry-node)** | A full node that shields validators from direct public exposure by relaying traffic, acting as a gateway between the validator and the public network. |
### Key components
| Component | Purpose |
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **[Cosmovisor](/infra/cosmovisor)** | Process manager that automates chain binary upgrades, enabling zero-downtime transitions when governance-approved upgrades occur. See [Node Operation](/infra/run-node) for setup. |
| **[Peggo](/infra/validator-mainnet/peggo)** | Bridge orchestrator that relays messages between Ethereum and Injective. Mandatory for validators. |
| **[WebSocket Server](https://github.com/InjectiveFoundation/injective-core)** | Streams real-time chain events over WebSocket, wrapping the gRPC Chain Stream server for standard WebSocket connections. |
### Chain upgrades
Injective periodically undergoes coordinated software upgrades at predetermined block heights. All nodes must switch to the new binary to maintain consensus. See [Upgrades](/infra/upgrade-node) for procedures and the [Coordinated Upgrades](/infra/coordinated-upgrades) guide for troubleshooting.
# Interact with a node using the CLI
Source: https://docs.injective.network/infra/interact-node/command-line
You can use the `injectived` CLI to interact with a node. If you are interacting with a node in your local private network, make sure the node is running in the terminal before you use the CLI.
For more details on how to use `injectived`, see [using injectived](/developers/injectived/use/ "mention").
# Interact with a node programmatically with Go
Source: https://docs.injective.network/infra/interact-node/go
The following examples are in Go, but the Python and TS SDKs can also be used to programatically interact with a node/Injective.
* [TypeScript Examples](/developers-native/examples/)
* [Python Examples](https://github.com/InjectiveLabs/sdk-python/tree/master/examples)
The following snippet shows how to query the state using gRPC inside a Go program. The idea is to create a gRPC connection, and use the Protobuf-generated client code to query the gRPC server.
```go theme={null}
import (
"context"
"fmt"
"google.golang.org/grpc"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
)
func queryState() error {
myAddress, err := sdk.AccAddressFromBech32("inj...")
if err != nil {
return err
}
// Create a connection to the gRPC server.
grpcConn := grpc.Dial(
"127.0.0.1:9090", // your gRPC server address.
grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism.
)
defer grpcConn.Close()
// This creates a gRPC client to query the x/bank service.
bankClient := banktypes.NewQueryClient(grpcConn)
bankRes, err := bankClient.Balance(
context.Background(),
&banktypes.QueryBalanceRequest{Address: myAddress, Denom: "inj"},
)
if err != nil {
return err
}
fmt.Println(bankRes.GetBalance()) // Prints the account balance
return nil
}
```
#### **Query for historical state using Go**
Querying for historical blocks is done by adding the block height metadata in the gRPC request.
```go theme={null}
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
"github.com/cosmos/cosmos-sdk/types/tx"
)
func queryState() error {
// --snip--
var header metadata.MD
bankRes, err = bankClient.Balance(
metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "12"), // Add metadata to request
&banktypes.QueryBalanceRequest{Address: myAddress, Denom: denom},
grpc.Header(&header), // Retrieve header from response
)
if err != nil {
return err
}
blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader)
fmt.Println(blockHeight) // Prints the block height (12)
return nil
}
```
# Interact with a node using gRPC
Source: https://docs.injective.network/infra/interact-node/grpc
The Protobuf ecosystem developed tools for different use cases, including code-generation from `*.proto` files into various languages. These tools allow clients to be built easily. Often, the client connection (i.e. the transport) can be plugged and replaced easily. Let's explore a popular transport method, gRPC.
Since the code generation library largely depends on your own tech stack, we will only present two alternatives:
* `grpcurl` for generic debugging and testing
* Programmatically via Go, Python, or TS
## grpcurl
[grpcurl](https://github.com/fullstorydev/grpcurl) is like `curl`, but for gRPC. It is also available as a Go library, but we will use it only as a CLI command for debugging and testing purposes. Follow the instructions in the previous link to install it.
Assuming you have a local node running (either a localnet, or connected to a live network), you should be able to run the following command to list the Protobuf services available. You can replace `localhost:9090` by the gRPC server endpoint of another node, which is configured under the `grpc.address` field inside `app.toml`:
```bash theme={null}
grpcurl -plaintext localhost:9090 list
```
You should see a list of gRPC services, like `cosmos.bank.v1beta1.Query`. This is called reflection, which is a Protobuf endpoint returning a description of all available endpoints. Each of these represents a different Protobuf service, and each service exposes multiple RPC methods you can query against.
In order to get a description of the service, you can run the following command:
```bash theme={null}
# Service we want to inspect
grpcurl \
localhost:9090 \
describe cosmos.bank.v1beta1.Query
```
It's also possible to execute an RPC call to query the node for information:
```bash theme={null}
grpcurl \
-plaintext
-d '{"address":"$MY_VALIDATOR"}' \
localhost:9090 \
cosmos.bank.v1beta1.Query/AllBalances
```
## Query for historical state using grpcurl
You may also query for historical data by passing some [gRPC metadata](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) to the query: the `x-cosmos-block-height` metadata should contain the block to query. Using grpcurl as above, the command looks like:
```bash theme={null}
grpcurl \
-plaintext \
-H "x-cosmos-block-height: 279256" \
-d '{"address":"$MY_VALIDATOR"}' \
localhost:9090 \
cosmos.bank.v1beta1.Query/AllBalances
```
Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response.
## Sending Transactions
Sending transactions using gRPC and REST requires some additional steps: generating the transaction, signing it, and finally broadcasting it.
You can learn more in [transactions](/defi/transactions/ "mention").
# Interact with nodes
Source: https://docs.injective.network/infra/interact-node/index
This section covers different methods for interacting with Injective nodes.
* [Command line](/infra/interact-node/command-line)
* [gRPC](/infra/interact-node/grpc)
* [Go](/infra/interact-node/go)
* [REST](/infra/interact-node/rest)
# Interact with a node using REST Endpoints
Source: https://docs.injective.network/infra/interact-node/rest
All gRPC services on the Cosmos SDK are made available for more convenient REST-based queries through gRPC-gateway. The format of the URL path is based on the Protobuf service method's full-qualified name, but may contain small customizations so that final URLs look more idiomatic. For example, the REST endpoint for the `cosmos.bank.v1beta1.Query/AllBalances` method is `GET /cosmos/bank/v1beta1/balances/{address}`. Request arguments are passed as query parameters.
The following examples assume you are using REST Endpoints to interact with your node in your local private network. You can change the domain to public networks.
As a concrete example, the `curl` command to make balances request is:
```bash theme={null}
curl \
-X GET \
-H "Content-Type: application/json" \
http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR
```
Make sure to replace `localhost:1317` with the REST endpoint of your node, configured under the `api.address` field.
The list of all available REST endpoints is available as a Swagger specification file; it can be viewed at `localhost:1317/swagger`. Make sure that the `api.swagger` field is set to true in your `app.toml` file.
## Query for historical state using REST
Querying for historical state is done using the HTTP header `x-cosmos-block-height`. For example, a curl command would look like:
```bash theme={null}
curl \
-X GET \
-H "Content-Type: application/json" \
-H "x-cosmos-block-height: 279256" \
http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR
```
Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response.
## Cross-Origin Resource Sharing (CORS)
[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server , we recommend you provide a reverse proxy. This can be done with [nginx](https://www.nginx.com/). For testing and development purposes, there is an `enabled-unsafe-cors` field inside `app.toml`.
## Sending Transactions
Sending transactions using gRPC and REST requires some additional steps: generating the transaction, signing it, and finally broadcasting it.
You can learn more in [transactions](/defi/transactions/ "mention").
# Join a Network
Source: https://docs.injective.network/infra/join-a-network
Set up and sync an Injective node on Mainnet, Testnet, or a local development network.
Injective Mainnet has a chain-id of `injective-1`.
Snapshot restore, state sync, and block sync
Install injectived, configure, and run
Minimum and recommended specifications
***
### Sync Options
Choose how to bootstrap your node with chain data. For most operators, snapshot restore is the fastest path to a running node.
**Option 1. Snapshot Restore (recommended)**
Download a pre-built snapshot and extract it into your data directory. This is the fastest and most reliable way to sync a new node.
Injective provides official pruned snapshots (goleveldb, \~2-3 GB) and the community maintains additional options. See the [Snapshots page on the Chain Portal](https://portal.injective.network/snapshots) for download links, usage instructions, and community providers.
**Validators:** You must back up and restore `priv_validator_state.json` during snapshot recovery. See the [Validator Troubleshooting Guide](/infra/coordinated-upgrades#recovering-from-a-snapshot) for the full procedure.
```bash theme={null}
# Example: download and extract
sudo systemctl stop injectived
rm -rf ~/.injectived/data
wget -O snapshot.lz4
lz4 -d snapshot.lz4 | tar xf - -C ~/.injectived/
sudo systemctl start injectived
```
**Option 2. State Sync (CometBFT P2P)**
State sync bootstraps a node by downloading a state machine snapshot from peers via the CometBFT P2P layer, rather than replaying all historical blocks. It requires two trusted RPC servers. See the [RPC Providers page on the Chain Portal](https://portal.injective.network/rpcs) for available endpoints. Configure it in `config.toml` under `[statesync]`:
```toml theme={null}
[statesync]
enable = true
rpc_servers = ","
trust_height =
trust_hash = ""
trust_period = "168h0m0s"
```
To obtain the trust height and hash:
```bash theme={null}
# Get a recent height and its hash from a trusted RPC
LATEST=$(curl -s https://sentry.tm.injective.network/block | jq -r '.result.block.header.height')
TRUST_HEIGHT=$((LATEST - 2000))
TRUST_HASH=$(curl -s "https://sentry.tm.injective.network/block?height=$TRUST_HEIGHT" | jq -r '.result.block_id.hash')
echo "trust_height = $TRUST_HEIGHT"
echo "trust_hash = \"$TRUST_HASH\""
```
**State sync does not restore the `wasm/` directory.** CometBFT state sync restores the IAVL state tree but not the CosmWasm compiled contract bytecode stored in `~/.injectived/data/wasm/`. Without these files, the node will produce an **AppHash mismatch** immediately after sync completes.
If you encounter this, either:
1. Switch to **snapshot restore** (Option 1). Injective's official snapshots include the `wasm/` directory
2. Obtain the `wasm/` directory from a trusted source and place it at `~/.injectived/data/wasm/` before starting the node
**PebbleDB users:** Injective's official snapshots use goleveldb only. PebbleDB snapshots are not maintained. If you run PebbleDB and cannot use a snapshot restore, you must source the `wasm/` directory separately when using state sync.
**Option 3. Block Sync (full replay)**
The default behavior. The node downloads and replays every block from peers. Slowest method but requires no external snapshots. Not practical for mainnet unless syncing from a recent snapshot height.
**Sync method comparison:**
| Method | Speed | Includes wasm? | Notes |
| -------------------- | -------------- | ------------------------------------------------------------ | ----------------------------------------------- |
| **Snapshot Restore** | Fast (minutes) | Yes (official snapshots) | Recommended for most operators |
| **State Sync** | Fast (minutes) | **No** - causes AppHash mismatch without manual wasm restore | Configured in `config.toml` `[statesync]` |
| **Block Sync** | Slow (days) | N/A (builds from execution) | Default, not practical for mainnet from genesis |
#### Peer discovery
Your node needs peers to sync with the network. Add seeds or persistent peers to the `[p2p]` section of `~/.injectived/config/config.toml`. Community providers maintain up-to-date seed and peer lists:
* [Polkachu Injective Seeds](https://polkachu.com/seeds/injective)
* [Decentrio Injective Infrastructure](https://decentrio.ventures/infrastructure/injective) (seeds, peers, address book)
You can also download a pre-built address book for faster peer discovery:
```bash theme={null}
# Decentrio address book
wget -O ~/.injectived/config/addrbook.json \
https://backend.decentrio.ventures/snapshots/injective/addrbook.json
```
***
### Node Software
#### Install `injectived`
Each [Injective core release](https://github.com/InjectiveFoundation/injective-core/releases/latest) includes `injectived`, [`peggo`](/infra/validator-mainnet/peggo) (the Peggy bridge orchestrator), and `libwasmvm` in a single zip. Validators must run `peggo` alongside their node. Non-validator node operators only need `injectived`.
```bash theme={null}
# Download the latest release
wget https://github.com/InjectiveFoundation/injective-core/releases/latest/download/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
# Validators also need peggo
sudo mv peggo /usr/bin
```
#### Initialize a New Injective Node
Before running an Injective node, initialize the chain and genesis file:
```bash theme={null}
# The argument is the custom username of your node. It should be human-readable.
export MONIKER=
injectived init $MONIKER --chain-id injective-1
```
Running the `init` command will create `injectived` default configuration files at `~/.injectived`.
#### Prepare Configuration to Join Mainnet
Download the mainnet genesis file. Injective provides this alongside the official snapshots:
```bash theme={null}
# Download the mainnet genesis file
wget -O ~/.injectived/config/genesis.json \
http://injective-mainnet-snapshots.s3-website.gra.io.cloud.ovh.net/genesis.json
```
Verify the checksum:
```bash theme={null}
sha256sum ~/.injectived/config/genesis.json
# Expected: 573b89727e42b41d43156cd6605c0c8ad4a1ce16d9aad1e1604b02864015d528
```
Configure seeds for peer discovery (see [Peer discovery](#peer-discovery) in the Sync Options section) and set `timeout_commit` to `300ms` in `~/.injectived/config/config.toml`.
#### Configuring the node using `app.toml` and `config.toml`
Two configuration files are automatically generated inside `~/.injectived/config`:
* `config.toml`: configures the consensus layer (CometBFT). [Learn more in the consensus layer documentation](https://docs.cometbft.com/v0.38/core/configuration).
* `app.toml`: configures the application and execution layer of the node, including state pruning strategies,
telemetry, gRPC and REST server configurations, state sync, and more.
#### Configure `systemd` service for `injectived` (optional)
Edit the config at `/etc/systemd/system/injectived.service`:
```bash theme={null}
[Unit]
Description=injectived
[Service]
WorkingDirectory=/usr/bin
ExecStart=/bin/bash -c '/usr/bin/injectived --log-level=error start'
Type=simple
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
```
Starting and restarting the systemd service:
```bash theme={null}
sudo systemctl daemon-reload
sudo systemctl restart injectived
sudo systemctl status injectived
# enable start on system boot
sudo systemctl enable injectived
# To check Logs
journalctl -u injectived -f
```
The service should be stopped before and started after snapshot data has been loaded into the correct directory.
```bash theme={null}
# to stop the node
sudo systemctl stop injectived
# to start the node
sudo systemctl start injectived
```
***
### Hardware
Node operators should deploy bare metal servers to achieve optimal performance.
Validator nodes must meet the recommended hardware specifications, particularly the CPU requirements,
to ensure high uptime.
| *Minimum* | *Recommendation* |
| :-------------------: | :-------------------: |
| RAM Memory 128GB | RAM Memory 128GB |
| CPU 12 cores | CPU 16 cores |
| CPU base clock 3.7GHz | CPU base clock 4.2GHz |
| Storage 2TB NVMe | Storage 2TB NVMe |
| Network 1Gbps+ | Network 1Gbps+ |
***
**Support**
For any further questions, you can always connect with the Injective Team via [Discord](https://discord.gg/injective),
[Telegram](https://t.me/joininjective), or [email](mailto:contact@injectivelabs.org)
#### Hardware Specification
Node operators should deploy bare metal servers to achieve optimal performance.
Additionally, validator nodes must meet the recommended hardware specifications and particularly the CPU requirements,
to ensure high uptime.
| *Minimum* | *Recommendation* |
| :-------------------: | :-------------------: |
| RAM Memory 128GB | RAM Memory 128GB |
| CPU 12 cores | CPU 16 cores |
| CPU base clock 3.7GHz | CPU base clock 4.2GHz |
| Storage 2TB NVMe | Storage 2TB NVMe |
| Network 1Gbps+ | Network 1Gbps+ |
#### Install `injectived` and `peggo`
See the [Injective releases repo](https://github.com/InjectiveLabs/testnet/releases) for the most recent releases.
Non-validator node operators do not need to install `peggo`.
```bash theme={null}
wget https://github.com/InjectiveLabs/testnet/releases/latest/download/linux-amd64.zip
unzip linux-amd64.zip
sudo mv peggo /usr/bin
sudo mv injectived /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
#### Initialize a New Injective Chain Node
Before running Injective node, we need to initialize the chain as well as the node's genesis file:
```bash theme={null}
# The argument is the custom username of your node, it should be human-readable.
export MONIKER=
# Injective Testnet has a chain-id of "injective-888"
injectived init $MONIKER --chain-id injective-888
```
Running the `init` command will create `injectived` default configuration files at `~/.injectived`.
#### Prepare Configuration to Join Testnet
You should now update the default configuration with the Testnet's genesis file and application config file,
as well as configure your persistent peers with seed nodes.
```bash theme={null}
git clone https://github.com/InjectiveLabs/testnet.git
# copy genesis file to config directory
aws s3 cp --no-sign-request s3://injective-snapshots/testnet/genesis.json .
mv genesis.json ~/.injectived/config/
# copy config file to config directory
cp testnet/corfu/70001/app.toml ~/.injectived/config/app.toml
cp testnet/corfu/70001/config.toml ~/.injectived/config/config.toml
```
You can also run verify the checksum of the genesis checksum - a4abe4e1f5511d4c2f821c1c05ecb44b493eec185c0eec13b1dcd03d36e1a779
```bash theme={null}
sha256sum ~/.injectived/config/genesis.json
```
#### Configure `systemd` Service for `injectived`
Edit the config at `/etc/systemd/system/injectived.service`:
```bash theme={null}
[Unit]
Description=injectived
[Service]
WorkingDirectory=/usr/bin
ExecStart=/bin/bash -c '/usr/bin/injectived --log-level=error start'
Type=simple
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
```
Starting and restarting the systemd service
```bash theme={null}
sudo systemctl daemon-reload
sudo systemctl restart injectived
sudo systemctl status injectived
# enable start on system boot
sudo systemctl enable injectived
# To check Logs
journalctl -u injectived -f
```
#### Sync with the network
Refer to the [Polkachu Injective Testnet Node Snapshot](https://polkachu.com/testnets/injective/snapshots)
to download a snapshot and sync with the network.
**Support**
For any further questions, you can always connect with the Injective Team via [Discord](https://discord.gg/injective),
[Telegram](https://t.me/joininjective), or [email](mailto:contact@injectivelabs.org).
To easily set up a local node, download and run the `setup.sh` script from the
[injective-core](https://github.com/InjectiveFoundation/injective-core) repository.
Use the script version that matches your installed `injectived` version.
```bash theme={null}
# Replace the version tag with the one matching your injectived binary
# For example, to use the script for v1.18.3:
wget https://raw.githubusercontent.com/InjectiveFoundation/injective-core/v1.18.3/setup.sh
chmod +x ./setup.sh # Make the script executable
./setup.sh
```
The `setup.sh` script in the legacy [injective-chain-releases](https://github.com/InjectiveLabs/injective-chain-releases)
repository is outdated and only compatible with `injectived` v1.14.1 and earlier.
Always use the script from the [InjectiveFoundation/injective-core](https://github.com/InjectiveFoundation/injective-core)
repository that corresponds to your chain version.
Start the node by running:
```bash theme={null}
injectived start # Blocks should start coming in after running this
```
For further explanation on what the script is doing and more fine-grained control over the setup process, continue reading below.
#### Initialize the Chain
Before running Injective node, we need to initialize the chain as well as the node's genesis file:
```bash theme={null}
# The argument is the custom username of your node. It should be human-readable.
injectived init --chain-id=injective-1
```
The command above creates all the configuration files needed for your node to run as well as a default genesis file,
which defines the initial state of the network.
All these configuration files are in `~/.injectived` by default,
but you can overwrite the location of this folder by passing the `--home` flag.
Note that if you choose to use a different directory other than `~/.injectived`,
you must specify the location with the `--home` flag each time an `injectived` command is run.
If you already have a genesis file, you can overwrite it with the `--overwrite` or `-o` flag.
The `~/.injectived` folder has the following structure:
```bash theme={null}
. # ~/.injectived
|- data # Contains the databases used by the node.
|- config/
|- app.toml # Application-related configuration file.
|- config.toml # Tendermint-related configuration file.
|- genesis.json # The genesis file.
|- node_key.json # Private key to use for node authentication in the p2p protocol.
|- priv_validator_key.json # Private key to use as a validator in the consensus protocol.
```
#### Modify the `genesis.json` File
At this point, a modification is required in the `genesis.json` file:
* Change the staking `bond_denom`, crisis `denom`, gov `denom`, and mint `denom` values to `"inj"`, since that is the native token of Injective.
This can easily be done by running the following commands:
```bash theme={null}
cat $HOME/.injectived/config/genesis.json | jq '.app_state["staking"]["params"]["bond_denom"]="inj"' > $HOME/.injectived/config/tmp_genesis.json && mv $HOME/.injectived/config/tmp_genesis.json $HOME/.injectived/config/genesis.json
cat $HOME/.injectived/config/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="inj"' > $HOME/.injectived/config/tmp_genesis.json && mv $HOME/.injectived/config/tmp_genesis.json $HOME/.injectived/config/genesis.json
cat $HOME/.injectived/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="inj"' > $HOME/.injectived/config/tmp_genesis.json && mv $HOME/.injectived/config/tmp_genesis.json $HOME/.injectived/config/genesis.json
cat $HOME/.injectived/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="inj"' > $HOME/.injectived/config/tmp_genesis.json && mv $HOME/.injectived/config/tmp_genesis.json $HOME/.injectived/config/genesis.json
```
The commands above will only work if the default `.injectived` directory is used.
For a specific directory, either modify the commands above or manually edit the `genesis.json` file to reflect the changes.
#### Create Keys for the Validator Account
Before starting the chain, you need to populate the state with at least one account.
To do so, first create a new account in the keyring named `my_validator` under the `test` keyring backend
(feel free to choose another name and another backend):
```bash theme={null}
injectived keys add my_validator --keyring-backend=test
# Put the generated address in a variable for later use.
MY_VALIDATOR_ADDRESS=$(injectived keys show my_validator -a --keyring-backend=test)
```
Now that you have created a local account, go ahead and grant it some `inj` tokens in your chain's genesis file.
Doing so will also make sure your chain is aware of this account's existence from the genesis of the chain:
```bash theme={null}
injectived add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000000000000000000inj --chain-id=injective-1
```
`$MY_VALIDATOR_ADDRESS` is the variable that holds the address of the `my_validator` key in the keyring.
Tokens in Injective have the `{amount}{denom}` format: `amount` is an 18-digit-precision decimal number,
and `denom` is the unique token identifier with its denomination key (e.g. `inj`).
Here, we are granting `inj` tokens, as `inj` is the token identifier used for staking in `injectived`.
#### Add the Validator to the Chain
Now that your account has some tokens, you need to add a validator to your chain.
Validators are special full-nodes that participate in the consensus process in order to add new blocks to the chain.
Any account can declare its intention to become a validator operator,
but only those with sufficient delegation get to enter the active set.
For this guide, you will add your local node (created via the `init` command above) as a validator of your chain.
Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`:
```bash theme={null}
# Create a gentx.
injectived genesis gentx my_validator 1000000000000000000000inj --chain-id=injective-1 --keyring-backend=test
# Add the gentx to the genesis file.
injectived genesis collect-gentxs
```
A `gentx` does three things:
1. Registers the `validator` account you created as a validator operator account (i.e. the account that controls the validator).
2. Self-delegates the provided `amount` of staking tokens.
3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks.
If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `injectived init` command above.
For more information on `gentx`, use the following command:
```bash theme={null}
injectived genesis gentx --help
```
#### Configuring the Node Using `app.toml` and `config.toml`
Two configuration files are automatically generated inside `~/.injectived/config`:
* `config.toml`: used to configure Tendermint
(learn more on [Tendermint's documentation](https://docs.tendermint.com/v0.34/tendermint-core/configuration.html)), and
* `app.toml`: generated by the Cosmos SDK (which Injective is built on), and used for configurations such as state pruning strategies,
telemetry, gRPC and REST server configurations, state sync, and more.
Both files are heavily commented. Refer to them directly to tweak your node.
One example config to tweak is the `minimum-gas-prices` field inside `app.toml`,
which defines the minimum gas prices the validator node is willing to accept for processing a transaction.
If it's empty, make sure to edit the field with some value, for example `10inj`,
or else the node will halt on startup.
For this tutorial, let's set the minimum gas price to 0:
```toml theme={null}
# The minimum gas prices a validator is willing to accept for processing a
# transaction. A transaction's fees must meet the minimum of any denomination
# specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = "0inj"
```
#### Run a Localnet
Now that everything is set up, you can finally start your node:
```bash theme={null}
injectived start # Blocks should start coming in after running this
```
This command allows you to run a single node, which is is enough to interact with the chain through the node,
but you may wish to run multiple nodes at the same time to see how consensus occurs between them.
# Overview
Source: https://docs.injective.network/infra/run-node
It is highly recommended that you set up a local private network before joining a public network. This will help you get familiar with the setup process and provide an environment for testing.
### **Private Network**
* Join by setting up a standalone network locally
### **Public Network**
* Use the network via public endpoints; or
* Join by running a node
Anyone can set up their node with endpoints to communicate with the Injective blockchain. For convenience, there are also some public endpoints available to querying the chain. These are recommended for development and testing purposes. For maximum control and reliability, running your node is recommended.
## Preparation For Running a Node
If you choose to run a node (either to set up a private network or join the public network), you must set up the keyring. You can also choose to install Cosmovisor, which assists with chain upgrades for minimal downtime.
## Interacting With The Node
Once the node is up and running, there are a few ways to interact with a node, namely using the gPRC endpoints, REST endpoints, or `injectived` CLI. You can learn more in the [Interact with nodes](/infra/interact-node/) section.
## Guides on Running Your Node
Learn how to setup your keyring
Learn how to join a network
Learn how to upgrade your node
# Setting up the keyring
Source: https://docs.injective.network/infra/set-up-keyring
This document describes how to configure and use the keyring and its various backends for an Injective node. `injectived` should be installed prior to setting up the keyring. See the [Install `injectived` page](../developers/injectived/install/) for more information.
The keyring holds the private/public keypairs used to interact with the node. For instance, a validator key needs to be set up before running the Injective node, so that blocks can be correctly signed. The private key can be stored in different locations, called "backends", such as a file or the operating system's own key storage.
### Available backends for the keyring
#### The `os` backend
The `os` backend relies on operating system-specific defaults to handle key storage securely. Typically, an operating system's credential sub-system handles password prompts, private keys storage, and user sessions according to the user's password policies. Here is a list of the most popular operating systems and their respective passwords manager:
* macOS (since Mac OS 8.6): [Keychain](https://support.apple.com/en-gb/guide/keychain-access/welcome/mac)
* Windows: [Credentials Management API](https://docs.microsoft.com/en-us/windows/win32/secauthn/credentials-management)
* GNU/Linux:
* [libsecret](https://gitlab.gnome.org/GNOME/libsecret)
* [kwallet](https://api.kde.org/frameworks/kwallet/html/index.html)
GNU/Linux distributions that use GNOME as default desktop environment typically come with [Seahorse](https://wiki.gnome.org/Apps/Seahorse). Users of KDE based distributions are commonly provided with [KDE Wallet Manager](https://userbase.kde.org/KDE_Wallet_Manager). Whilst the former is in fact a `libsecret` convenient frontend, the latter is a `kwallet` client.
`os` is the default option since operating system's default credentials managers are designed to meet users' most common needs and provide them with a comfortable experience without compromising on security.
The recommended backends for headless environments are `file` and `pass`.
#### The `file` backend
The `file` stores the keyring encrypted within the app's configuration directory. This keyring will request a password each time it is accessed, which may occur multiple times in a single command resulting in repeated password prompts. If using bash scripts to execute commands using the `file` option you may want to utilize the following format for multiple prompts:
```bash theme={null}
# assuming that KEYPASSWD is set in the environment
yes $KEYPASSWD | injectived keys add me
yes $KEYPASSWD | injectived keys show me
# start injectived with keyring-backend flag
injectived --keyring-backend=file start
```
The first time you add a key to an empty keyring, you will be prompted to type the password twice.
#### The `pass` backend
The `pass` backend uses the [pass](https://www.passwordstore.org/) utility to manage on-disk encryption of keys' sensitive data and metadata. Keys are stored inside `gpg` encrypted files within app-specific directories. `pass` is available for the most popular UNIX operating systems as well as GNU/Linux distributions. Please refer to its manual page for information on how to download and install it.
`pass` uses [GnuPG](https://gnupg.org/) for encryption. `gpg` automatically invokes the `gpg-agent` daemon upon execution, which handles the caching of GnuPG credentials. Please refer to `gpg-agent` man page for more information on how to configure cache parameters such as credentials TTL and passphrase expiration.
The password store must be set up prior to first use:
```sh theme={null}
pass init
```
Replace `` with your GPG key ID. You can use your personal GPG key or an alternative one you may want to use specifically to encrypt the password store.
#### The `kwallet` backend
The `kwallet` backend uses `KDE Wallet Manager`, which comes installed by default on the GNU/Linux distributions that ships KDE as default desktop environment. Please refer to[ KWallet Handbook](https://docs.kde.org/stable/en/kdeutils/kwallet/index.html) for more information.
#### The `test` backend
The `test` backend is a password-less variation of the `file` backend. Keys are stored unencrypted on disk.
**Provided for testing purposes only. The `test` backend is not recommended for use in production environments**.
#### The `memory` backend
The `memory` backend stores keys in memory. The keys are immediately deleted after the program has exited.
**Provided for testing purposes only. The `memory` backend is not recommended for use in production environments**.
### Adding keys to the keyring
You can use `injectived keys` for help about the keys command and `injectived keys [command] --help` for more information about a particular subcommand.
You can also enable auto-completion with the `injectived completion` command. For example, at the start of a bash session, run `. <(injectived completion)`, and all `injectived` subcommands will be auto-completed.
To create a new key in the keyring, run the `add` subcommand with a `` argument. For the purpose of this tutorial, we will solely use the `test` backend, and call our new key `my_validator`. This key will be used in the next section.
```bash theme={null}
$ injectived keys add my_validator --keyring-backend test
# Put the generated address in a variable for later use.
MY_VALIDATOR_ADDRESS=$(injectived keys show my_validator -a --keyring-backend test)
```
This command generates a new 24-word mnemonic phrase, persists it to the relevant backend, and outputs information about the keypair. If this keypair will be used to hold value-bearing tokens, be sure to write down the mnemonic phrase somewhere safe!
By default, the keyring generates a `eth_secp256k1` keypair. The keyring also supports `ed25519` keys, which may be created by passing the `--algo ed25519` flag. A keyring can of course hold both types of keys simultaneously.
# Overview
Source: https://docs.injective.network/infra/upgrade-node
Track active and upcoming network upgrades, governance proposals, and activation timelines
Download the latest and prior injectived and peggo binaries from InjectiveFoundation/injective-core
**Validators:** Before upgrading, read the [Validator Troubleshooting Guide](/infra/coordinated-upgrades) for critical safety rules, common failure modes, and recovery procedures. Mishandling `priv_validator_state.json` during upgrades can lead to double-signing and permanent tombstoning.
If you are not already a member of the verified validator channels, reach out to the Injective team via [Discord](https://discord.gg/injective) to get added. Upgrade coordination, emergency snapshots, and incident communication happen in these channels.
### Chain Upgrades
Injective periodically undergoes software upgrades. When a chain upgrade governance proposal is passed, a block height will be specified at which all nodes will automatically panic and stop running. At this point, the upgraded `injectived` binaries can be installed, and the node can be restarted.
### Node Upgrade Directions
To summarize, follow these steps to upgrade your node:
1. Sync your node to the block height predetermined by the upgrade governance proposal.
2. The node will automatically panic/stop at the predetermined upgrade height.
3. Remove the old binaries and install the new release binaries.
4. Restart the node.
If the upgrade fails or a sentry node becomes corrupted, see the [Snapshots page](https://chain-portal.injective.network/snapshots) for options to resync your node from a fresh snapshot.
### Upgrading with Cosmovisor
To manage chain upgrades, use [Cosmovisor](./cosmovisor/).
### Node Maintenance (Managing Storage)
As Injective state grows, your disk space may fill up. It's recommended you periodically prune the chain data by downloading new snapshots. Beyond the overhead on the disk, the node is more performant when the chain state is smaller.
Injective validators take daily light snapshots that you can use to clean the chain state, which grows at about 10-15 GB daily. These snapshots are normally only around 2-3 GB. We recommend pruning the chain data every 300-400 GB. For snapshot download links and usage instructions, see the [Snapshots page on the Chain Portal](https://portal.injective.network/snapshots). For directions on syncing your node, see [Join A Network](./join-a-network/).
# Validating Upgrade Releases
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade
## Verify release versions
### Releases on Github
If you would like to verify `injectived` or `peggo` version numbers via Docker,
follow the instructions in the [`verify-injective-release`](https://github.com/injective-dev/snippets-inj/tree/main/verify-injective-release) code snippet.
This is useful if you are on an operating system other than Linux,
and would like to independently verify the binaries in each release.
For example, for `v1.16.1`, it should produce the following output:
```text theme={null}
injectived version
Version v1.16.1 (8be67e82d)
Compiled at 20250802-1910 using Go go1.23.9 (amd64)
peggo version
Version v1.16.1 (8be67e82d)
Compiled at 20250802-1913 using Go go1.23.9 (amd64)
```
### Releases on Docker
These are more straightforward, as each binary needs a single command.
For `injectived`, use the following command:
```shell theme={null}
docker run -it --rm injectivelabs/injective-core:v1.16.1 injectived version
```
This should produce output similar to:
```text theme={null}
Version v1.16.1 (8be67e8)
Compiled at 20250802-1909 using Go go1.23.9 (arm64)
```
For `peggo`, use the following command:
```shell theme={null}
docker run -it --rm injectivelabs/injective-core:v1.16.1 peggo version
```
This should produce output similar to:
```text theme={null}
Version v1.16.1 (8be67e8)
Compiled at 20250802-1911 using Go go1.23.9 (arm64)
```
Note that you should replace `v1.16.1` in the commands above
with your intended Injective release version number.
### Checking for matches
Note that the output from the above commmands contain
the following in addition to the version numbers (e.g. `v1.16.1`):
* The binary release hashes (e.g. `8be67e82d`)
* The compiled time stamp (e.g. `20250802-1910`)
* The compiler (e.g. `Go go1.23.9 (amd64)`)
You can verify that these **match** the values stated in the [Injective chain releases](https://github.com/InjectiveLabs/injective-chain-releases/releases) page on Github.
## Deprecated flags
**Deprecated `statsd` flags (v1.19.0+)**
The following `injectived start` flags are no longer supported as of v1.19.0 and must be removed from your start command or systemd unit file before upgrading:
`statsdAgent`, `statsdEnabled`, `statsd-prefix`, `statsd-address`, `statsd-stuck-func`, `statsd-tracing-enabled`, `statsd-profiling-enabled`, `trace-recorder-threshold`
If any of these flags are present, `injectived` will fail to start after the upgrade. An external validator on testnet encountered this issue informally — remove these flags **before** running `injectived start` on the new version.
# Upgrade to v1.13.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.13.0
Thursday, August 1st, 2024
Following [proposal 420](https://injhub.com/proposals/420/) This indicates that the upgrade procedure should be performed on block number **80319200**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validator-operator)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, August 1st, 2024, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **80319200**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [v1.13.0-1722157491](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.13.0-1722157491)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.12.1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.12.1-1705909076) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`c1a64b7ed`) of `injectived`:
```bash theme={null}
injectived version
Version dev (c1a64b7ed)
Compiled at 20240122-0743 using Go go1.19.3 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.13.0 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.13.0-1722157491/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`c1a64b7ed`) of `injectived` after downloading the v1.13.0 release:
```bash theme={null}
injectived version
Version dev (af924ca9)
Compiled at 20240728-0905 using Go go1.22.5 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`ead1119`) of `peggo` after downloading the v1.13.0 release:
```bash theme={null}
peggo version
Version dev (ead1119)
Compiled at 20240728-0905 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.13.2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.13.2
Tuesday, August 20th, 2024
Following [proposal 424](https://injhub.com/proposal/424/) This indicates that the upgrade procedure should be performed on block number **82830000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, August 20th, 2024, 15:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **82830000**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [v1.13.2-1723753267](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.13.2-1723753267)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.13.2 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.13.2-1723753267) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`af924ca9`) of `injectived`:
```bash theme={null}
injectived version
Version dev (af924ca9)
Compiled at 20240728-0905 using Go go1.22.5 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.13.2 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.13.2-1723753267/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`6f57bf03`) of `injectived` after downloading the v1.13.2 release:
```bash theme={null}
injectived version
Version dev (6f57bf03)
Compiled at 20240815-2021 using Go go1.22.5 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`ead1119`) of `peggo` after downloading the v1.13.2 release:
```bash theme={null}
peggo version
Version dev (ead1119)
Compiled at 20240815-2021 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.13.3
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.13.3
Thursday, Dec 19th, 2024
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
## Summary
The v1.13.3 version is a non-consensus breaking upgrade of v1.13.2.
The following is a short summary of the upgrade steps:
1. Stop the binary.
2. Back up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [release-prod-1734610315](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/release-prod-1734610315)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.13.2 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.13.2-1723753267) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`6f57bf03`) of `injectived`:
```bash theme={null}
Version dev (6f57bf03)
Compiled at 20240815-2021 using Go go1.22.5 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.13.3 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/release-prod-1734610315/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`d92e96087`) of `injectived` after downloading the v1.13.3 release:
```bash theme={null}
injectived version
Version v1.13.3 (d92e96087)
Compiled at 20241219-1212 using Go go1.22.5 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`ead1119`) of `peggo` after downloading the v1.13.3 release:
```bash theme={null}
peggo version
Version dev (ead1119)
Compiled at 20240815-2021 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.14.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.14.0
Monday, February 17th, 2025
Following [Proposal 494](https://injhub.com/proposals/494/) This indicates that the upgrade procedure should be performed on block number **106315000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Monday, February 17th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **106315000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.14.0-1739303348](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.14.0-1739303348)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.13.3 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/release-prod-1734610315) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`d92e96087`) of `injectived`:
```bash theme={null}
injectived version
Version v1.13.3 (d92e96087)
Compiled at 20241219-1212 using Go go1.22.5 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.14.0 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.14.0-1739303348/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`4139d7dcd`) of `injectived` after downloading the v1.14.0 release:
```bash theme={null}
injectived version
Version v1.14.0 (4139d7dcd)
Compiled at 20250211-1725 using Go go1.23.1 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`5317d5c`) of `peggo` after downloading the v1.14.0 release:
```bash theme={null}
peggo version
Version dev (5317d5c)
Compiled at 20250211-1726 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.14.1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.14.1
Tuesday, March 4th, 2025
Following [Proposal 500](https://injhub.com/proposals/500) This indicates that the upgrade procedure should be performed on block number **108175000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Tuesday, March 4th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **108175000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.14.1-1740773301](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.14.1-1740773301)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.14.0 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.14.1-1740773301) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`4139d7dcd`) of `injectived`:
```bash theme={null}
injectived version
Version v1.14.0 (4139d7dcd)
Compiled at 20250211-1725 using Go go1.23.1 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.14.1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.14.1-1740773301/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`0fe59376d`) of `injectived` after downloading the v1.14.1 release:
```bash theme={null}
injectived version
Version v1.14.1 (0fe59376d)
Compiled at 20250228-2008 using Go go1.23.1 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`5317d5c`) of `peggo` after downloading the v1.14.1 release:
```bash theme={null}
peggo version
Version dev (5317d5c)
Compiled at 20250211-1726 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.15.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.15.0
Tuesday, April 22th, 2025
Following [Proposal 518](https://injhub.com/proposals/518) This indicates that the upgrade procedure should be performed on block number **114590000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Tuesday, April 22th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **114590000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.15.0-1744722790](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.15.0-1744722790)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.14.1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.14.1-1740773301) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`4139d7dcd`) of `injectived`:
```bash theme={null}
injectived version
Version v1.14.1 (0fe59376d)
Compiled at 20250228-2008 using Go go1.23.1 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.15.0 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.15.0-1744722790/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`013606f41`) of `injectived` after downloading the v1.15.0 release:
```bash theme={null}
injectived version
Version v1.15.0 (013606f41)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`5317d5c`) of `peggo` after downloading the v1.15.0 release:
```bash theme={null}
peggo version
Version dev (5317d5c)
Compiled at 20250415-1313 using Go go1.22.5 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.16.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.16.0
Thursday, July 31st, 2025
Following [Proposal 541](https://injhub.com/proposal/541/) This indicates that the upgrade procedure should be performed on block number **127250000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Thursday, July 31st, 2025, 16:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **127250000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.16.0](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.0-1753404855) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.15.0-1744722790](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.15.0-1744722790) and continue this earlier chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
1. Verify you are currently running the correct version (`v1.15.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.15.0 (013606f41)
Compiled at 20250528-1843 using Go go1.24.0 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `v1.16.0`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.16.0-1753404855/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`v1.16.0`) of `injectived` after downloading the`v1.16.0` release:
```bash theme={null}
Version v1.16.0 (95706035d)
Compiled at 20250725-0055 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`v1.16.0-peggofix`) of `peggo` after downloading the `v1.16.0` release:
```bash theme={null}
$ peggo version
Version v1.16.0-peggofix (3b346ece0)
Compiled at 20250730-2027 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.16.1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.16.1
Monday, August 4th, 2025
Following [Proposal 544](https://injhub.com/proposal/544/) This indicates that the upgrade procedure should be performed on block number **127714000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Monday, August 4th, 2025, 12:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **127714000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.16.1](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.1-1754161770) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.16.0](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.0-1753404855) and continue this earlier chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
1. Verify you are currently running the correct version (`v1.16.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.16.0 (95706035d)
Compiled at 20250725-0055 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `v1.16.1`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.16.1-1754161770/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`v1.16.1`) of `injectived` after downloading the`v1.16.1` release:
```bash theme={null}
Version v1.16.1 (8be67e82d)
Compiled at 20250802-1910 using Go go1.23.9
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`v1.16.1`) of `peggo` after downloading the `v1.16.1` release:
```bash theme={null}
$ peggo version
Version v1.16.1 (8be67e82d)
Compiled at 20250802-1913 using Go go1.23.9
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.16.2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.16.2
Tuesday, August 19th, 2025
Following [Proposal 550](https://injhub.com/proposal/550/) This indicates that the upgrade procedure should be performed on block number **129772500**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Tuesday, August 19th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **129772500**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.16.2](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.2-1755212690) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.16.1](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.1-1754161770) and continue this earlier chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
1. Verify you are currently running the correct version (`v1.16.1`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.16.1 (8be67e82d)
Compiled at 20250802-1913 using Go go1.23.9
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `v1.16.2`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.16.2-1755212690/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`v1.16.2`) of `injectived` after downloading the`v1.16.2` release:
```bash theme={null}
Version v1.16.2 (437674d)
Compiled at 20250814-2305 using Go go1.23.9
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`v1.16.2`) of `peggo` after downloading the `v1.16.2` release:
```bash theme={null}
$ peggo version
Version v1.16.2 (437674d)
Compiled at 20250814-2307 using Go go1.23.9
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.16.4
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.16.4
Thursday, 25th September, 2025
Following [IIP 560](https://injhub.com/proposal/560/) This indicates that the upgrade procedure should be performed on block number **134805000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Thursday, 25th September, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **134805000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.16.4](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.4-1758323548) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.16.2](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.16.2-1755212690) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`v1.16.2`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.16.2 (437674d)
Compiled at 20250814-2305 using Go go1.23.9
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `v1.16.4`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.16.4-1758323548/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`v1.16.4`) of `injectived` after downloading the `v1.16.4` release:
```bash theme={null}
$ injectived version
Version v1.16.4 (470633e)
Compiled at 20250915-0554 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`v1.16.4`) of `peggo` after downloading the `v1.16.4` release:
```bash theme={null}
$ peggo version
Version v1.16.4 (470633e)
Compiled at 20250915-0555 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.17.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.17.0
Tuesday, November 11th, 2025
Following [IIP 583](https://injhub.com/proposal/583/) This indicates that the upgrade procedure should be performed on block number **141150000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Tuesday, November 11th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **141150000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [1.17.0](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.17.0-1762751536) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [1.16.4](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/1.16.4-1758323548) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`1.16.4`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.16.4 (470633e)
Compiled at 20250915-0554 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `1.17.0`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/1.17.0-1762751536/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`1.17.0`) of `injectived` after downloading the `1.17.0` release:
```bash theme={null}
$ injectived version
Version v1.17.0 (33ff202)
Compiled at 20251110-0512 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`1.17.0`) of `peggo` after downloading the `1.17.0` release:
```bash theme={null}
$ peggo version
Version v1.17.0 (33ff202)
Compiled at 20251110-0512 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.17.1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.17.1
Wednesday, December 3rd, 2025
Following [Proposal 601](https://injhub.com/proposal/601/) This indicates that the upgrade procedure should be performed on block number **144210000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Wednesday, December 3rd, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **144210000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [1.17.1](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.17.1-1764720946) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [1.17.0](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/1.17.0-1762751536) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`1.17.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.17.0 (33ff202)
Compiled at 20251110-0512 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `1.17.1`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.17.1-1764720946/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`1.17.1`) of `injectived` after downloading the `1.17.1` release:
```bash theme={null}
$ injectived version
Version v1.17.1 (99ae041)
Compiled at 20251202-2355 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`1.17.1`) of `peggo` after downloading the `1.17.1` release:
```bash theme={null}
$ peggo version
Version v1.17.1 (99ae041)
Compiled at 20251202-2355 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.17.2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-1.17.2
Thursday, December 18th, 2025
Following [Proposal 603](https://injhub.com/proposal/603/) This indicates that the upgrade procedure should be performed on block number **146295000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Thursday, December 18th, 2025, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **146295000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [1.17.2](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.17.2-1765930431) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [1.17.1](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.17.1-1764720946) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`1.17.1`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.17.1 (99ae041)
Compiled at 20251202-2355 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `1.17.2`:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.17.2-1765930431/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`1.17.2`) of `injectived` after downloading the `1.17.2` release:
```bash theme={null}
$ injectived version
Version v1.17.2 (b2809ae)
Compiled at 20251217-0013 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`1.17.2`) of `peggo` after downloading the `1.17.2` release:
```bash theme={null}
$ peggo version
Version v1.17.2 (b2809ae)
Compiled at 20251217-0013 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to 10002-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10002-rc1
# Upgrade to 10002-rc1
Nov. 8th, 2021
The upgrade will take place via an on-chain software upgrade proposal passed by Injective governance. If passed, this proposal would commit the Injective Mainnet to halting the Canary Chain `injectived` application binary at approximately **14:00 UTC on Nov. 8th, 2021,** and starting the application binary for the Injective Canonical Chain.
In case of a failed migration via the upgrade module, the Injective Labs team will post an official `injective-canonical-chain` genesis file, but it is recommended that validators should do try to export the genesis on their own node to verify the resulting genesis file.
Following [proposal #65](https://injhub.com/proposals/65) This indicates that the upgrade procedure should be performed on block number **4,352,000**.
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
## Summary
The Injective Canary Chain will undergo a scheduled upgrade to Injective Canonical Chain on **Monday, November 8th 2021**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **4,352,000**.
2. Backing up configs, data, and keys used for running the Injective Canary Chain.
3. Install the [Injective 10002-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.0-1636178708)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully migrate from the Canary chain to Canonical chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble migrating from the Canary chain to Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the Canary chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted, and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting the Canary chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10001-rc7 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.0.1-1635956190) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`b174465c`) of `injectived`:
```bash theme={null}
$ injectived version
commit: b174465c
go: go1.16.8
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Canary Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10002-rc1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.1.0-1636178708/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
```
4. Verify you are currently running the correct version (`eb018590`) of `injectived` after downloading the 10002-rc1 release:
```bash theme={null}
$ injectived version
commit: eb018590
go: go1.17.2
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
# Upgrade to 10002-rc2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10002-rc2
November 15th, 2021
Following [proposal #70](https://injhub.com/proposals/70) This indicates that the upgrade procedure should be performed on block number **4, 594, 100**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
## Summary
The Injective Canonical Chain will undergo a scheduled upgrade to Injective Canonical Chain on **Monday, November 15th, 2021**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **4, 594, 100**.
2. Backing up configs, data, and keys used for running the Injective Canary Chain.
3. Install the [Injective Chain 10002-rc2 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.1-1636733798)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrade chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting the chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10002-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.0-1636178708) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`eb018590`) of `injectived`:
```bash theme={null}
$ injectived version
commit: eb018590
Compiled at 20211106-0605 using Go go1.17.2 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10002-rc2 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.1.1-1636733798/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
```
4. Verify you are currently running the correct version (`096cbe5`) of `injectived` after downloading the 10002-rc2 release:
```bash theme={null}
$ injectived version
Version dev (096cbe5)
Compiled at 20211112-1620 using Go go1.17.2 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
# Upgrade to 10003-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10003-rc1
Thursday, December 30th, 2021
Following [proposal #93](https://injhub.com/proposals/93) This indicates that the upgrade procedure should be performed on block number **6159200**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Notes for DEX Relayer Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, December 30th, 2021 (approximately 14:00 UTC)**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **6159200**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-10003-rc1-1640627705](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.1-1640627705)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10002-rc2 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.1-1636733798) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`096cbe5`) of `injectived`:
```bash theme={null}
$ injectived version
commit: 096cbe5
Compiled at 20211106-0605 using Go go1.17.2 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10003-rc1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.1.1-1640627705/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
```
4. Verify you are currently running the correct version (`260e526`) of `injectived` after downloading the 10003-rc1 release:
```bash theme={null}
$ injectived version
Version dev (260e526)
Compiled at 20211227-1757 using Go go1.17.2 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10004-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10004-rc1
Tuesday, January 25th, 2022
Following [proposal #106](https://injhub.com/proposals/106) This indicates that the upgrade procedure should be performed on block number **7067700**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Service Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Tuesday, January 25th, 2022 (approximately at 14:00 UTC)**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **7067700**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-10004-rc1-v1.4.0-1642928125](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.4.0-1642928125)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10003-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.1.1-1640627705) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`260e526`) of `injectived`:
```bash theme={null}
$ injectived version
commit: 260e526
Compiled at 20211227-1757 using Go go1.17.2 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10004-rc1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.4.0-1642928125/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
```
4. Verify you are currently running the correct version (`94583db`) of `injectived` after downloading the 10004-rc1 release:
```bash theme={null}
$ injectived version
Version dev (94583db)
Compiled at 20220123-0855 using Go go1.17.6 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10004-rc1-patch
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10004-rc1-patch
Sunday, February 20th, 2022
Upgrade Injective from 10004-rc1 to 10004-rc1 [patch version 10004-rc1-1645352045](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.4.0-1645352045) created due to an incident that happened on Sunday, February 20th, 2022, at 3:55 AM UTC-05:00, when [Network halted at block 7941974](https://explorer.injective.network/block/7941974).
This indicates that the upgrade procedure should be performed on block number **7941974**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for DEX Relayer Providers](#notes-for-dex-relayer-providers)
## Summary
* At 3:55 AM UTC-05:00 Network halted, [block height 7941974](https://explorer.injective.network/block/7941974)
* At 5:05 AM UTC-05:00 [New release created with rolled back fix, Version 10004-rc1-1645352045](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.4.0-1645352045)
* At 06:08 UTC-05:00 [Network is back up](https://explorer.injective.network/block/7941975)
The following is a short summary of the upgrade steps:
1. Backing up configs, data, and keys used for running the Injective Canonical Chain.
2. Install the [Mainnet-10004-rc1-v1.4.0-1642928125](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.4.0-1645352045)
3. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. Happy path\
Validators successfully migrate from the 10004-rc1 Injective Chain to 10004-rc1 patch without purging the blockchain history, and all validators are up within 1-2 hours.
2. Not-so-happy path\
Validators have trouble upgrading the chain. This could be some consensus-breaking changes not covered in the upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. Abort path\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.
## Recovery
Prior to exporting Injective state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally, this can be done by backing up the `.injectived` directory. We would suggest using `aws s3 sync` with the delete flag, this will shorten the snapshotting time as only the file diffs are uploaded to the S3 bucket.
It is critically important to back up the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
## Upgrade Procedure
1. Verify you are currently running the correct (pre-upgrade) version (`94583db`) of `injectived`:
```bash theme={null}
$ injectived version
Version dev (94583db)
Compiled at 20220123-0855 using Go go1.17.6 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the existing Canonical Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install Injective 10004-rc1 release
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.4.0-1645352045/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived /usr/bin
```
4. Verify you are currently running the correct new version (`30b3dbf`) of `injectived` after downloading the 10004-rc1 release:
```bash theme={null}
$ injectived version
Version dev (30b3dbf)
Compiled at 20220220-1014 using Go go1.17.6 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10005-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10005-rc1
Monday, April 11th, 2022
Following [proposal #133](https://injhub.com/proposals/133) This indicates that the upgrade procedure should be performed on block number **9614200**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for DEX relayer Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Monday, April 11th, 2022 (approximately at 14:00 UTC)**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **9614200**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-v1.5.0-1649280277](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.5.0-1649280277)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10004-rc1-patch release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.4.0-1645352045) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`30b3dbf`) of `injectived`:
```bash theme={null}
$ injectived version
Version dev (30b3dbf)
Compiled at 20220220-1014 using Go go1.17.6 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10005-rc1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.5.0-1649280277/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
```
4. Verify you are currently running the correct version (`568ce23`) of `injectived` after downloading the 10005-rc1 release:
```bash theme={null}
$ injectived version
Version dev (568ce23)
Compiled at 20220406-2125 using Go go1.18 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10006-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10006-rc1
Tuesday, July 5th, 2022
Following [proposal 159](https://injhub.com/proposals/159/) This indicates that the upgrade procedure should be performed on block number **12569420**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for DEX Relayer Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Tuesday, July 5th, 2022 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **12569420**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-v1.6.0-1656650662](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.6.0-1656650662)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10005-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.5.0-1649280277/linux-amd64.zip) and continue the chain until next upgrade announcement.
### Upgrade Procedure
1. Verify you are currently running the correct version (`568ce23`) of `injectived`:
```bash theme={null}
$ injectived version
Version dev (568ce23)
Compiled at 20220406-2125 using Go go1.18 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10006-rc1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.6.0-1656650662/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`ef7f6f7a`) of `injectived` after downloading the 10006-rc1 release:
```bash theme={null}
$ injectived version
Version dev (ef7f6f7a)
Compiled at 20220701-0444 using Go go1.18.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. As the above release didn't include the updates to peggo correctly, we created a new release for peggo changes. Download and install the new Injective Chain 10006-rc1 release for peggo binary:
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.6.0-1657048292/linux-amd64.zip
unzip linux-amd64.zip
sudo mv peggo /usr/bin
```
7. Verify you are currently running the correct version (`ade8906`) of `peggo` after downloading the 10006-rc1 release:
```bash theme={null}
$ peggo version
Version dev (ade8906)
Compiled at 20220701-0444 using Go go1.18.3 (amd64)
```
8. Start peggo
```
peggo start
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10007-rc1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10007-rc1
Thursday, September 1st, 2022
Following [proposal 170)](https://injhub.com/proposals/170/) This indicates that the upgrade procedure should be performed on block number **14731000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
* [Notes for DEX Relayer Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, September 1st, 2022, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **14730999**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-v1.7.0-1665417543](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.7.0-1665417543)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10006-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.6.0-1656650662/linux-amd64.zip) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `1s`.
1. Verify you are currently running the correct version (`ef7f6f7a`) of `injectived`:
```bash theme={null}
injectived version
Version dev (ef7f6f7a)
Compiled at 20220705-1911 using Go go1.18.3 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10007 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.7.0-1665417543/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`178f6dbb`) of `injectived` after downloading the 10007 release:
```bash theme={null}
injectived version
Version dev (178f6dbb)
Compiled at 20220903-1641 using Go go1.18.5 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`ade8906`) of `peggo` after downloading the 10007 release:
```bash theme={null}
peggo version
Version dev (ade8906)
Compiled at 20220830-1738 using Go go1.18.5 (amd64)
```
8. Start peggo
```bash theme={null}
peggo start
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10008 - Camelot
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10008
Thursday, November 21st, 2022
Following [proposal 182](https://injhub.com/proposals/182/) This indicates that the upgrade procedure should be performed on block number **19761600**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validator-operators)
* [Notes for Service Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, November 21st, 2022, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **19761599**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-10008-1668679102](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.8.0-1668679102)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10007-rc1 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.7.0-1665417543/linux-amd64.zip) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `300ms`.
1. Verify you are currently running the correct version (`ca447cf8`) of `injectived`:
```bash theme={null}
injectived version
Version dev (ca447cf8)
Compiled at 20221024-1031 using Go go1.18.5 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10008 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.8.0-1668679102/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`64c9081`) of `injectived` after downloading the 10008 release:
```bash theme={null}
injectived version
Version dev (64c9081)
Compiled at 20221117-0959 using Go go1.18.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`ade8906`) of `peggo` after downloading the 10008 release:
```bash theme={null}
peggo version
Version dev (ade8906)
Compiled at 20220830-1738 using Go go1.18.5 (amd64)
```
8. Start peggo
```bash theme={null}
peggo start
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to 10009
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-10009
Tuesday, January 18th, 2022
Following [proposal 198](https://injhub.com/proposals/198/) This indicates that the upgrade procedure should be performed on block number **24204000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validator-operators)
* [Notes for Service Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Tuesday, January 18th, 2022, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **24204000**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-10009-1673640888](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.9.0-1673640888)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10008 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.8.0-1668679102) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `300ms`.
1. Verify you are currently running the correct version (`64c9081`) of `injectived`:
```bash theme={null}
injectived version
Version dev (64c9081)
Compiled at 20221024-1031 using Go go1.18.5 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10009 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.9.0-1673640888/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`3c87354f5`) of `injectived` after downloading the 10009 release:
```bash theme={null}
injectived version
Version dev (3c87354f5)
Compiled at 20230113-2015 using Go go1.18.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`ade8906`) of `peggo` after downloading the 10009 release:
```bash theme={null}
peggo version
Version dev (ade8906)
Compiled at 20220830-1738 using Go go1.18.5 (amd64)
```
8. Start peggo
```bash theme={null}
peggo start
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to v1.10
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.10.0
Friday, March 17th, 2023
Following [proposal 207](https://injhub.com/proposals/207/) This indicates that the upgrade procedure should be performed on block number **28864000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validator-operators)
* [Notes for Service Providers](#notes-for-dex-relayer-providers)
### Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **March 17th 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **28864000**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [Mainnet-v1.10-1678709842](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.10-1678709842)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
### Risks
As a validator performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
### Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10009 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.9.0-1673970775) and continue the chain until next upgrade announcement.
#### Upgrade Procedure
### Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `300ms`.
1. Verify you are currently running the correct version (`3c87354f5`) of `injectived`:
```bash theme={null}
injectived version
Version v1.9.0 (3c87354f5)
Compiled at 20230118-1421 using Go go1.18.3 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See Recovery for details on how to proceed.
3. Download and install the injective-chain `10010 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.10-1678709842
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`e218afcf7`) of `injectived` after downloading the 10009 release:
```bash theme={null}
injectived version
Version dev (e218afcf7) │
Compiled at 20230313-1224 using Go go1.18.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`bede2b6`) of `peggo` after downloading the 10009 release:
```bash theme={null}
peggo version
Version dev (bede2b6) │
Compiled at 20230313-1224 using Go go1.18.3 (amd64)
```
8. Start peggo
```bash theme={null}
peggo start
```
### Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded as it relies on several other components that work with injectived.
# Upgrade to v1.11
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.11.0
Thursday, June 1st, 2023
Following [proposal 231](https://injhub.com/proposals/231/) This indicates that the upgrade procedure should be performed on block number **34775000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
* [Notes for Service Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, June 1st, 2023, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **34775000**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [v1.11-1685225746](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.11-1685225746)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#mainnet-validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10010 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.10.1-1685036881) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `300ms`. You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`b578d018c8`) of `injectived`:
```bash theme={null}
injectived version
Version dev (b578d018c8)
Compiled at 20230118-1421 using Go go1.18.3 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `10011 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.11-1685225746/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`f77eb5cfd7`) of `injectived` after downloading the 10009 release:
```bash theme={null}
injectived version
Version v1.11 (f77eb5cfd7)
Compiled at 20230527-2215 using Go go1.19.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`5868c94`) of `peggo` after downloading the 10009 release:
```bash theme={null}
peggo version
Version dev (5868c94)
Compiled at 20230527-2216 using Go go1.19.3 (amd64)
```
8. Start peggo
```bash theme={null}
peggo start
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to v1.12.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.12.0
Thursday, January 11th, 2024
Following [proposal 314](https://injhub.com/proposals/314/) This indicates that the upgrade procedure should be performed on block number **57076000**
* [Summary](#summary)
* [Risks](#risks)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
* [Notes for DEX Relayer Providers](#notes-for-dex-relayer-providers)
## Summary
The Injective Canonical Chain will undergo a scheduled enhancement upgrade on **Thursday, January 11th, 2024, 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **57076000**.
2. Backing up configs, data, and keys used for running the Injective Canonical Chain.
3. Install the [v1.12.0-1704530206](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.12.0-1704530206)
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history, and all validators are up within 1-2 hours of the scheduled upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to the latest Canonical chain. This could be some consensus breaking changes not covered in upgrade handler, or compatibility issue of the migrated state with new injectived binary, but validators can at least export the genesis.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes. A new governance proposal for the upgrade will need to be issued and voted on by the community for the next upgrade.
## Risks
As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing.
The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from an Injective developer before resetting your validator.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain 10011 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.11.6-1688984159) and continue the chain until next upgrade announcement.
### Upgrade Procedure
## Notes for Validators
Validator operators should configure the **timeout\_commit** in **config.toml** to `300ms`.
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`69fb0c5`) of `injectived`:
```bash theme={null}
injectived version
Version dev (69fb0c5)
Compiled at 20230710-1016 using Go go1.19.3 (amd64)
```
2. After the chain has halted, make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
\
**NOTE**: It is recommended for validators and operators to take a full data snapshot at the export height before proceeding in case the upgrade does not go as planned or if not enough voting power comes online in a sufficient and agreed upon amount of time. In such a case, the chain will fallback to continue operating the Chain. See [Recovery](#recovery) for details on how to proceed.
3. Download and install the injective-chain `v1.12.0 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.12.0-1704530206/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`b92723b13`) of `injectived` after downloading the v1.12.0 release:
```bash theme={null}
injectived version
Version dev (b92723b13)
Compiled at 20240106-0837 using Go go1.19.3 (amd64)
```
5. Coordinate to restart your injectived with other validators
```bash theme={null}
injectived start
```
The binary will perform the upgrade automatically and continue the next consensus round if everything goes well.
6. Verify you are currently running the correct version (`9e702f1`) of `peggo` after downloading the v1.12.0 release:
```bash theme={null}
peggo version
Version dev (9e702f1)
Compiled at 20240106-0837 using Go go1.19.3 (amd64)
```
8. Start peggo
```bash theme={null}
peggo orchestrator
```
## Notes for DEX relayer providers
Relayer upgrade will be available after the chain is successfully upgraded, as it relies on several other components that work with injectived.
# Upgrade to v1.12.1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.12.1
Monday, January 22nd, 2024
This is a **non-consensus breaking release**, node operators can upgrade in their earliest convenience.
**Network downtime will not occur during this upgrade.**
All nodes, validator and non-consensus nodes must be upgraded as soon as possible to this version.
Validators should also upgrade the peggo version.
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validator Operators](#notes-for-validators)
### Summary
The Injective Canonical Chain will undergo a improvement upgrade on **Jan 22th**.
The following is a short summary of the upgrade steps:
1. Backing up configs, data, and keys used for running the Injective Canonical Chain.
2. Install the [v1.12.1-1705909076](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.12.1-1705909076)
3. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**\
Validators successfully upgrade chain without purging the blockchain history and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**\
Validators have trouble upgrading to latest Canonical chain.
3. **Abort path**\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the #mainnet-validators channel of [Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
### Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to [Injective Chain v1.12.0 release](https://github.com/InjectiveLabs/injective-chain-releases/releases/v1.12.0-1704530206) and continue the chain until next upgrade announcement.
#### Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version (rm -rf .injectived/wasm/wasm/cache/).
1. Verify you are currently running the correct version (`b92723b13`) of `injectived`:
```bash theme={null}
injectived version
Version dev (b92723b13)
Compiled at 20240106-0837 using Go go1.19.3 (amd64)
```
2. Make a backup of your `.injectived` directory
```bash theme={null}
cp ~/.injectived ./injectived-backup
```
3. Download and install the injective-chain `v1.12.1 release`
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.12.1-1705909076/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
3. Verify you are currently running the correct version (`c1a64b7ed`) of `injectived` after downloading the v1.12.1 release:
```bash theme={null}
injectived version
Version dev (c1a64b7ed)
Compiled at 20240122-0743 using Go go1.19.3 (amd64)
```
4. Start injectived
```bash theme={null}
injectived start
```
5. Verify you are currently running the correct version (`e8089a7`) of `peggo` after downloading the v1.12.0 release:
```bash theme={null}
peggo version
Version dev (e8089a7)
Compiled at 20240122-0743 using Go go1.19.3 (amd64)
```
6. Start peggo
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.18.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.18.0
Wednesday, February 19th, 2026
Following [Proposal 619](https://injhub.com/proposal/619/) This indicates that the upgrade procedure should be performed on block number **154983000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Wednesday, February 19th, 2026, \~15:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **154983000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [1.18.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.0-1770939123) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [1.17.2](https://github.com/InjectiveLabs/injective-chain-releases/releases/tag/v1.17.2-1765930431) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`1.17.2`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.17.2 (b2809ae)
Compiled at 20251217-0013 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for `1.18.0`:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.18.0-1770939123/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (`1.18.0`) of `injectived` after downloading the `1.18.0` release:
```bash theme={null}
$ injectived version
Version v1.18.0 (6632cef)
Compiled at 20260212-2333 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (`1.18.0`) of `peggo` after downloading the `1.18.0` release:
```bash theme={null}
$ peggo version
Version v1.18.0 (6632cef)
Compiled at 20260212-2333 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.18.2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.18.2
Monday, March 9th, 2026
Following [IIP-624](https://injhub.com/proposal/624/) This indicates that the upgrade procedure should be performed on block number **157455000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Monday, March 9th, 2026, 10:00 AM ET / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **157455000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.18.2](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.2-1772587486) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [1.18.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.0-1770939123) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`1.18.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.18.0 (6632cef)
Compiled at 20260212-2333 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.18.2:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.18.2-1772587486/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.18.2) of `injectived` after downloading the v1.18.2 release:
```bash theme={null}
$ injectived version
Version v1.18.2 (71954e5)
Compiled at 20260304-0125 using Go go1.23.9 (amd64)
```
5. Disable JSON-RPC in your `app.toml` configuration:
```toml theme={null}
[json-rpc]
enable = false
```
6. Start `injectived`:
```bash theme={null}
injectived start
```
7. Verify you are currently running the correct version (v1.18.2) of `peggo` after downloading the v1.18.2 release:
```bash theme={null}
$ peggo version
Version v1.18.2 (71954e5)
Compiled at 20260304-0132 using Go go1.23.9 (amd64)
```
8. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.18.3
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.18.3
Monday, April 7th, 2026
Following [IIP-628](https://injhub.com/proposal/628/) This indicates that the upgrade procedure should be performed on block number **161472000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Monday, April 7th, 2026, \~10:00 AM EDT / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **161472000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.18.3](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.3-1774990852) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.18.2](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.2-1772587486) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`v1.18.2`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.18.2 (71954e5)
Compiled at 20260304-0125 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.18.3:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.18.3-1774990852/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.18.3) of `injectived` after downloading the v1.18.3 release:
```bash theme={null}
$ injectived version
Version v1.18.3 (b18483b)
Compiled at 20260331-2102 using Go go1.23.9 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (v1.18.3) of `peggo` after downloading the v1.18.3 release:
```bash theme={null}
$ peggo version
Version v1.18.3 (b18483b)
Compiled at 20260331-2102 using Go go1.23.9 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.19.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.19.0
Tuesday, April 28th, 2026
Following [IIP-632](https://injhub.com/proposal/632/) This indicates that the upgrade procedure should be performed on block number **164394000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Tuesday, April 28th, 2026, 10:00 AM ET / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **164394000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.19.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.19.0) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.18.3](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.18.3-1774990852) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
**Source builds require Go v1.26.2**
The v1.19.0 source has already been released and raises the minimum Go version to `go1.26.2`. Validators building `injectived` or `peggo` from source should upgrade their Go environment before the chain halt to avoid build-time surprises. Teams deploying the published binaries or Docker image do not need to make any additional environment changes.
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`v1.18.3`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.18.3 (b18483b)
Compiled at 20260331-2102 using Go go1.23.9 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.19.0:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.19.0/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.19.0) of `injectived` after downloading the v1.19.0 release:
```bash theme={null}
$ injectived version
Version v1.19.0 (750b3fb)
Compiled at 20260424-1643 using Go go1.26.2 (amd64)
```
5. Remove any deprecated `statsd` flags from your `injectived start` command or systemd unit file:
The following flags are no longer supported as of v1.19.0 and will cause `injectived` to fail on startup:
`statsdAgent`, `statsdEnabled`, `statsd-prefix`, `statsd-address`, `statsd-stuck-func`, `statsd-tracing-enabled`, `statsd-profiling-enabled`, `trace-recorder-threshold`
These flags were supported in earlier versions but have been removed. An external validator on testnet encountered a startup failure due to these flags still being present. Check your start script, systemd service file, or any wrapper scripts and remove them **before** proceeding to the next step.
6. Start `injectived`:
```bash theme={null}
injectived start
```
7. Verify you are currently running the correct version (v1.19.0) of `peggo` after downloading the v1.19.0 release:
```bash theme={null}
$ peggo version
Version v1.19.0 (750b3fb)
Compiled at 20260424-1644 using Go go1.26.2 (amd64)
```
8. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.20.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.20.0
Thursday, June 4th, 2026
Following [IIP-650](https://injhub.com/proposal/650/) This indicates that the upgrade procedure should be performed on block number **169334500**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
* [Breaking Changes MUST REVIEW](#breaking-changes-must-review)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade on **Thursday, June 4th, 2026, 10:00 AM ET / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **169334500**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.20.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.0) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.19.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.19.0) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
## Breaking Changes MUST REVIEW
Review the [v1.20.0 injectived startup flag changes](./canonical-chain-upgrade-v1.20.0-flag-changes/) before restarting `injectived`. Deprecated flags must be removed, renamed flags should be updated, and changed defaults should be pinned explicitly if your node depends on the previous behavior.
### Steps
1. Verify you are currently running the correct version (`v1.19.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.19.0 (750b3fb)
Compiled at 20260424-1643 using Go go1.26.2 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.20.0:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.20.0-1780128924/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.20.0) of `injectived` after downloading the v1.20.0 release:
```bash theme={null}
$ injectived version
Version v1.20.0 (3ade14d)
Compiled at 20260530-0816 using Go go1.26.2 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (v1.20.0) of `peggo` after downloading the v1.20.0 release:
```bash theme={null}
$ peggo version
Version v1.20.0 (3ade14d)
Compiled at 20260530-0823 using Go go1.26.2 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Injectived Startup Flag Changes for v1.20.0
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.20.0-flag-changes
This note summarizes `injectived start` flag changes between `v1.19.0` and `v1.20.0`. All node runners should review their startup scripts, Helm charts, systemd units, or Kubernetes manifests before upgrading.
## Recommended Upgrade Checklist
1. Remove flags that no longer exist.
2. Rename metrics and ChainStream flags if you use them.
3. Pin any changed defaults if your node depends on the old behavior.
## Long-Term Configuration Recommendation
Prefer setting persistent node configuration in the appropriate config file:
* Use `app.toml` for application-level settings such as API, gRPC, JSON-RPC, pruning, metrics, ChainStream, EVM, and Injective websocket configuration.
* Use `config.toml` for CometBFT settings such as P2P, RPC, consensus, and mempool configuration.
Startup flags should be treated as temporary overrides for testing, emergency operations, or short-lived rollout changes. They should not be used as the permanent replacement for values that belong in `app.toml` or `config.toml`. Keeping long-lived settings in config files makes validator behavior easier to audit, reduces upgrade risk when flags are renamed or removed, and keeps startup commands stable across releases. The template `app.toml` and `config.toml` files for the `v1.20.0` tag can be found at the bottom of this file.
## Renamed or Replaced Flags
Update any startup configuration that still uses the old flag names.
| v1.19.0 flag | v1.20.0 flag | Notes |
| ----------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------- |
| `--metrics-enable-metrics=false` | `--metrics.metrics-enabled=false` | Same default. |
| `--metrics-enable-tracing=false` | `--metrics.tracing-enabled=false` | Same default. |
| `--metrics-endpoint=localhost:4317` | `--metrics.endpoint=localhost:4317` | Same default. |
| `--metrics-insecure=false` | `--metrics.insecure-endpoint=false` | Same default. |
| `--metrics-export-interval=10s` | `--metrics.export-interval=10s` | Now parsed as a duration and used by metrics export. |
| `--metrics-stuck-func=0m` | `--metrics.stuck-func-timeout=0s` | Same effective disabled default. |
| `--trace-flight-recorder-threshold=0` | `--metrics.flight-recorder-threshold=0s` | Same effective disabled default. |
| `--log-color=true` | `--log-no-color=false` | Behavior is inverted. |
| `--chainstream-server=""` | `--chainstream.server=""` | Same default. |
| `--chainstream-buffer-cap=100` | `--chainstream.buffer-cap=100` | Same default. |
| `--chainstream-publisher-buffer-cap=100` | `--chainstream.publisher-buffer-cap=100` | Same default. |
| `--chainstream-enforce-keepalive=false` | `--chainstream.enforce-keepalive=false` | Same default. |
| `--chainstream-min-client-ping-interval=30` | `--chainstream.min-client-ping-interval=30` | Same default. |
| `--chainstream-max-connection-idle=180` | `--chainstream.max-connection-idle=180` | Same default. |
| `--chainstream-server-ping-interval=60` | `--chainstream.server-ping-interval=60` | Same default. |
| `--chainstream-server-ping-response-timeout=40` | `--chainstream.server-ping-response-timeout=40` | Same default. |
## Added Flags (Informational. No change needed as these values will be picked from the config files as usual)
These flags did not exist as `injectived start` flags in `v1.19.0`.
| v1.20.0 flag | Default | Notes |
| ---------------------------------------------- | ------------- | ------------------------------------------------------------------------- |
| `--app-db-backend` | `""` | Empty value falls back to the CometBFT DB backend. |
| `--index-events` | `[]` | Controls event indexing for CometBFT. Empty means all events are indexed. |
| `--chain-id` | `injective-1` | New on `start` |
| `--injective-websocket.address` | `""` | Empty value disables the Injective websocket server. |
| `--injective-websocket.max-open-connections` | `0` | Unlimited. |
| `--injective-websocket.read-timeout` | `10s` | HTTP read timeout. |
| `--injective-websocket.write-timeout` | `10s` | HTTP write timeout. |
| `--injective-websocket.max-body-bytes` | `1000000` | Maximum allowed request body size. |
| `--injective-websocket.max-header-bytes` | `1048576` | Maximum allowed header size. |
| `--injective-websocket.max-request-batch-size` | `10` | Maximum JSON-RPC requests per batch. |
## Removed Flags
Remove these from startup scripts before running `v1.20.0`.
| Removed flag | Previous default | Replacement |
| -------------------------- | ------------------------ | --------------------------------------------------------------------------------------- |
| `--with-comet` | `true` | None. In-process CometBFT startup is now the supported path. |
| `--with-tendermint` | Alias for `--with-comet` | None. |
| `--address` | `tcp://0.0.0.0:26658` | None for normal validator startup. |
| `--transport` | `socket` | None for normal validator startup. |
| `--pruning-keep-every` | `0` | None. Use `--pruning`, `--pruning-keep-recent`, and `--pruning-interval`. |
| `--multistore-commit-sync` | `false` | None. This flag was registered in `v1.19.0` but not consumed by Injective startup code. |
## Same Name, Changed Default or Type
If your deployment relied on the old default, set the old value explicitly.
| Flag | v1.19.0 | v1.20.0 | Suggested validator action |
| --------------------------------------- | ---------------------- | ----------------------- | ------------------------------------------------------------------ |
| `--minimum-gas-prices` | `""` | `160000000inj` | Pin explicitly if you require a different minimum gas price. |
| `--pruning` | `default` | `nothing` | Pin `--pruning=default` if you do not want archive-style behavior. |
| `--pruning-keep-recent` | `uint64(0)` | string `"0"` | Usually no action; type changed for config parsing. |
| `--pruning-interval` | `uint64(0)` | string `"0"` | Usually no action; type changed for config parsing. |
| `--iavl-cache-size` | `int(500000)` | `uint64(781250)` | Pin old value if memory/cache sizing depends on it. |
| `--api.enable` | `false` flag default | `true` flag default | Pin `--api.enable=false` if the REST API must remain disabled. |
| `--api.swagger` | `false` flag default | `true` flag default | Pin `--api.swagger=false` if Swagger must remain disabled. |
| `--api.address` | `tcp://localhost:1317` | `tcp://0.0.0.0:10337` | Pin old address if exposing the API is not intended. |
| `--api.enabled-unsafe-cors` | `false` flag default | `true` flag default | Pin `false` if CORS must remain disabled. |
| `--json-rpc.api` | unset / nil | `[eth, net, web3, inj]` | Pin explicitly if you require a different namespace set. |
| `--json-rpc-debug.api` | `[debug]` | `[eth, debug, inj]` | Pin explicitly if debug RPC is enabled and should stay debug-only. |
| `--json-rpc.max-open-connections` | `uint(0)` | `int(0)` | Usually no action; value remains unlimited. |
| `--json-rpc.return-data-limit` | `uint(512000)` | `int64(512000)` | Usually no action; value is unchanged. |
| `--json-rpc-debug.max-open-connections` | `uint(0)` | `int(0)` | Usually no action; value remains unlimited. |
| `--json-rpc-debug.return-data-limit` | `uint(10485760)` | `int64(10485760)` | Usually no action; value is unchanged. |
| `--log-level` | `info` | `""` | Pin explicitly if you require a specific log level. |
| `--log-format` | `plain` | `""` | Pin explicitly if you require `plain` or `json`. |
For `--api.*`, note that generated Injective `app.toml` defaults were already set to enabled in `v1.19.0`; the notable change is the effective CLI flag default used by `injectived start`.
## Template Config Files
### app.toml
```toml theme={null}
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
###############################################################################
### Base Configuration ###
###############################################################################
# The minimum gas prices a validator is willing to accept for processing a
# transaction. A transaction's fees must meet the minimum of any denomination
# specified in this config (e.g. 0.25token1,0.0001token2).
minimum-gas-prices = "160000000inj"
# The maximum gas a query coming over rest/grpc may consume.
# If this is set to zero, the query can consume an unbounded amount of gas.
query-gas-limit = "0"
# default: the last 362880 states are kept, pruning at 10 block intervals
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
# everything: 2 latest states will be kept; pruning at 10 block intervals.
# custom: allow pruning options to be manually specified through 'pruning-keep-recent', and 'pruning-interval'
pruning = "nothing"
# These are applied if and only if the pruning strategy is custom.
pruning-keep-recent = "0"
pruning-interval = "0"
# HaltHeight contains a non-zero block height at which a node will gracefully
# halt and shutdown that can be used to assist upgrades and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-height = 0
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
# a node will gracefully halt and shutdown that can be used to assist upgrades
# and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-time = 0
# MinRetainBlocks defines the minimum block height offset from the current
# block being committed, such that all blocks past this offset are pruned
# from CometBFT. It is used as part of the process of determining the
# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates
# that no blocks should be pruned.
#
# This configuration value is only responsible for pruning CometBFT blocks.
# It has no bearing on application state pruning which is determined by the
# "pruning-*" configurations.
#
# Note: CometBFT block pruning is dependant on this parameter in conjunction
# with the unbonding (safety threshold) period, state pruning and state sync
# snapshot parameters to determine the correct minimum value of
# ResponseCommit.RetainHeight.
min-retain-blocks = 0
# InterBlockCache enables inter-block caching.
inter-block-cache = true
# IndexEvents defines the set of events in the form {eventType}.{attributeKey},
# which informs CometBFT what to index. If empty, all events will be indexed.
#
# Example:
# ["message.sender", "message.recipient"]
index-events = []
# IavlCacheSize set the size of the iavl tree cache (in number of nodes).
iavl-cache-size = 781250
# IAVLDisableFastNode enables or disables the fast node feature of IAVL.
# Default is false.
iavl-disable-fastnode = true
# AppDBBackend defines the database backend type to use for the application and snapshots DBs.
# An empty string indicates that a fallback will be used.
# The fallback is the db_backend value set in CometBFT's config.toml.
app-db-backend = ""
###############################################################################
### API Configuration ###
###############################################################################
[api]
# Enable defines if the API server should be enabled.
enable = true
# Swagger defines if swagger documentation should automatically be registered.
swagger = true
# Address defines the API server to listen on.
address = "tcp://0.0.0.0:10337"
# MaxOpenConnections defines the number of maximum open connections.
max-open-connections = 1000
# RPCReadTimeout defines the CometBFT RPC read timeout (in seconds).
rpc-read-timeout = 10
# RPCWriteTimeout defines the CometBFT RPC write timeout (in seconds).
rpc-write-timeout = 0
# RPCMaxBodyBytes defines the CometBFT maximum request body (in bytes).
rpc-max-body-bytes = 1000000
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
enabled-unsafe-cors = true
###############################################################################
### gRPC Configuration ###
###############################################################################
[grpc]
# Enable defines if the gRPC server should be enabled.
enable = true
# Address defines the gRPC server address to bind to.
address = "0.0.0.0:9900"
# MaxRecvMsgSize defines the max message size in bytes the server can receive.
# The default value is 10MB.
max-recv-msg-size = "10485760"
# MaxSendMsgSize defines the max message size in bytes the server can send.
# The default value is math.MaxInt32.
max-send-msg-size = "2147483647"
###############################################################################
### gRPC Web Configuration ###
###############################################################################
[grpc-web]
# GRPCWebEnable defines if the gRPC-web should be enabled.
# NOTE: gRPC must also be enabled, otherwise, this configuration is a no-op.
# NOTE: gRPC-Web uses the same address as the API server.
enable = true
###############################################################################
### EVM Configuration ###
###############################################################################
[evm]
# Tracer defines the 'vm.Tracer' type that the EVM will use when the node is run in
# debug mode. To enable tracing use the '--evm.tracer' flag when starting your node.
# Valid types are: json|struct|access_list|markdown
tracer = ""
# MaxTxGasWanted defines the gas wanted for each eth tx returned in ante handler in check tx mode.
max-tx-gas-wanted = 0
# Enabled or disable TraceTx/TraceBlock/TraceCall gRPC endpoints
enable-grpc-tracing = false
###############################################################################
### JSON RPC Configuration ###
###############################################################################
[json-rpc]
# Enable defines if the gRPC server should be enabled.
enable = true
# Address defines the EVM RPC HTTP server address to bind to.
address = "127.0.0.1:8545"
# Address defines the EVM WebSocket server address to bind to.
ws-address = "127.0.0.1:8546"
# API defines a list of JSON-RPC namespaces that should be enabled
# Example: "eth,txpool,personal,net,debug,web3"
api = "eth,net,web3,inj"
# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite).
gas-cap = 25000000
# EVMTimeout is the global timeout for eth_call. Default: 5s.
evm-timeout = "5s"
# TxFeeCap is the global tx-fee cap for send transaction. Default: 1eth.
txfee-cap = 1
# FilterCap sets the global cap for total number of filters that can be created
filter-cap = 200
# FeeHistoryCap sets the global cap for total number of blocks that can be fetched
feehistory-cap = 100
# LogsCap defines the max number of results can be returned from single 'eth_getLogs' query.
logs-cap = 10000
# BlockRangeCap defines the max block range allowed for 'eth_getLogs' query.
block-range-cap = 10000
# HTTPTimeout is the read/write timeout of http json-rpc server.
http-timeout = "30s"
# HTTPIdleTimeout is the idle timeout of http json-rpc server.
http-idle-timeout = "2m0s"
# AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via
# the node's RPC when the global parameter is disabled.
allow-unprotected-txs = false
# MaxOpenConnections sets the maximum number of simultaneous connections
# for the server listener.
max-open-connections = 0
# EnableIndexer enables the custom transaction indexer for the EVM (ethereum transactions).
enable-indexer = true
# AllowIndexerGap allow block gap for the custom transaction indexer for the EVM (ethereum transactions).
allow-indexer-gap = true
# Define if JSON-RPC metrics server should be enabled.
metrics = true
# MetricsAddress defines the EVM Metrics server address to bind to.
# Prometheus metrics path: /debug/metrics/prometheus
metrics-address = "127.0.0.1:6065"
# Maximum number of bytes returned from eth_call or similar invocations.
return-data-limit = 512000
###############################################################################
### Debug JSON RPC Configuration ###
###############################################################################
[json-rpc-debug]
# Enable defines if the dedicated debug JSON-RPC server should be enabled.
enable = false
# Address defines the debug JSON-RPC HTTP server address to bind to.
address = "127.0.0.1:8547"
# API defines a list of JSON-RPC namespaces that should be enabled
# Example: "eth,debug"
api = "eth,debug,inj"
# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite).
gas-cap = 250000000
# EVMTimeout is the global timeout for eth_call. Default: 5s.
evm-timeout = "20s"
# TxFeeCap is the global tx-fee cap for send transaction. Default: 10eth.
txfee-cap = 10
# FilterCap sets the global cap for total number of filters that can be created
filter-cap = 2000
# FeeHistoryCap sets the global cap for total number of blocks that can be fetched
feehistory-cap = 1000
# LogsCap defines the max number of results can be returned from single 'eth_getLogs' query.
logs-cap = 100000
# BlockRangeCap defines the max block range allowed for 'eth_getLogs' query.
block-range-cap = 50000
# HTTPTimeout is the read/write timeout of http json-rpc server.
http-timeout = "30s"
# HTTPIdleTimeout is the idle timeout of http json-rpc server.
http-idle-timeout = "2m0s"
# MaxOpenConnections sets the maximum number of simultaneous connections
# for the server listener.
max-open-connections = 0
# Maximum number of bytes returned from eth_call or similar invocations.
return-data-limit = 10485760
###############################################################################
### Injective Websocket Configuration ###
###############################################################################
[injective-websocket]
# Address defines the websocket server address to bind to.
address = ""
# MaxOpenConnections sets the maximum number of simultaneous connections.
max-open-connections = 0
# ReadTimeout defines the HTTP read timeout.
read-timeout = "10s"
# WriteTimeout defines the HTTP write timeout.
write-timeout = "10s"
# MaxBodyBytes defines the maximum allowed HTTP body size (in bytes).
max-body-bytes = 1000000
# MaxHeaderBytes defines the maximum allowed HTTP header size (in bytes).
max-header-bytes = 1048576
# MaxRequestBatchSize defines the maximum number of RPC calls per batch request.
max-request-batch-size = 10
###############################################################################
### State Sync Configuration ###
###############################################################################
# State sync snapshots allow other nodes to rapidly join the network without replaying historical
# blocks, instead downloading and applying a snapshot of the application state at a given height.
[state-sync]
# snapshot-interval specifies the block interval at which local state sync snapshots are
# taken (0 to disable).
snapshot-interval = 0
# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).
snapshot-keep-recent = 2
###############################################################################
### State Streaming ###
###############################################################################
# Streaming allows nodes to stream state to external systems.
[streaming]
# streaming.abci specifies the configuration for the ABCI Listener streaming service.
[streaming.abci]
# List of kv store keys to stream out via gRPC.
# The store key names MUST match the module's StoreKey name.
#
# Example:
# ["acc", "bank", "gov", "staking", "mint"[,...]]
# ["*"] to expose all keys.
keys = []
# The plugin name used for streaming via gRPC.
# Streaming is only enabled if this is set.
# Supported plugins: abci
plugin = ""
# stop-node-on-err specifies whether to stop the node on message delivery error.
stop-node-on-err = true
###############################################################################
### Mempool ###
###############################################################################
[mempool]
# Setting max-txs to 0 will allow for a unbounded amount of transactions in the mempool.
# Setting max_txs to negative 1 (-1) will disable transactions from being inserted into the mempool (no-op mempool).
# Setting max_txs to a positive number (> 0) will limit the number of transactions in the mempool, by the specified amount.
#
# Note, this configuration only applies to SDK built-in app-side mempool
# implementations.
max-txs = -1
###############################################################################
### ChainStream Server ###
###############################################################################
[chainstream]
# ChainStream server address to bind to. Empty = disabled.
server = ""
# Configure ChainStream server buffer capacity for each connected client
buffer-cap = "100"
# Configure ChainStream publisher buffer capacity
publisher-buffer-cap = "100"
# Define if Keepalive configuration params should be applied to chainstream gRPC server
enforce-keepalive = "false"
# Amount of time (in seconds) a client should wait before sending a keepalive ping
min-client-ping-interval = "30"
# Amount of time in seconds a connection is allowed to stay idle before forcing the disconnection
max-connection-idle = "180"
# Amount of time in seconds after which the server will send a keepalive ping to the client on an idle connection
server-ping-interval = "60"
# Amount of time in seconds the server waits for the client to respond to a ping message before forcing a disconnection
server-ping-response-timeout = "40"
###############################################################################
### Metrics Configuration ###
###############################################################################
[metrics]
# whether metrics collections should be enabled, default: false
metrics-enabled = false
# whether tracing should be enabled, default: false
tracing-enabled = false
# gRPC OTEL receiver endpoint, default: localhost:4317
endpoint = "localhost:4317"
# whether to use TLS during Endpoint connection
insecure-endpoint = false
# time interval between metric exports, default: 10s
export-interval = "10s"
# timeout before FuncTiming marks the function as stuck and ends the span.
# Set to 0 (default) to disable timeouts completely.
stuck-func-timeout = "0s"
# time after which block is considered slow and flight recorder should dump the profile to disk.
# 0 - disables flight recorder.
flight-recorder-threshold = "0s"
```
### config.toml
```toml theme={null}
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
# NOTE: Any path below can be absolute (e.g. "/var/myawesomeapp/data") or
# relative to the home directory (e.g. "data"). The home directory is
# "$HOME/.cometbft" by default, but could be changed via $CMTHOME env variable
# or --home cmd flag.
# The version of the CometBFT binary that created or
# last modified the config file. Do not modify this.
version = "1.0.1"
#######################################################################
### Main Base Config Options ###
#######################################################################
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the CometBFT binary
proxy_app = "tcp://0.0.0.0:26658"
# A custom human readable name for this node
moniker = "validatornode"
# Database backend: badgerdb | goleveldb | pebbledb | rocksdb | cleveldb | boltdb
# * badgerdb (uses github.com/dgraph-io/badger)
# - stable
# - pure go
# - use badgerdb build tag (go build -tags badgerdb)
# * goleveldb (github.com/syndtr/goleveldb)
# - UNMAINTAINED
# - stable
# - pure go
# * pebbledb (uses github.com/cockroachdb/pebble)
# - stable
# - pure go
# * rocksdb (uses github.com/linxGnu/grocksdb)
# - requires gcc
# - use rocksdb build tag (go build -tags rocksdb)
# * cleveldb (uses levigo wrapper)
# - DEPRECATED
# - requires gcc
# - use cleveldb build tag (go build -tags cleveldb)
# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt)
# - DEPRECATED
# - stable
# - use boltdb build tag (go build -tags boltdb)
db_backend = "goleveldb"
# Database directory
db_dir = "data"
# Output level for logging, including package level options
log_level = "info"
# Output format: 'plain' (colored text) or 'json'
log_format = "plain"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_key_file = "config/priv_validator_key.json"
# Path to the JSON file containing the last sign state of a validator
priv_validator_state_file = "data/priv_validator_state.json"
# TCP or UNIX socket address for CometBFT to listen on for
# connections from an external PrivValidator process
priv_validator_laddr = "tcp://0.0.0.0:26658"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
#######################################################################
### Advanced Configuration Options ###
#######################################################################
#######################################################
### RPC Server Configuration Options ###
#######################################################
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:26657"
# A list of origins a cross-domain request can be executed from
# Default value '[]' disables cors support
# Use '["*"]' to allow any origin
cors_allowed_origins = []
# A list of methods the client is allowed to use with cross-domain requests
cors_allowed_methods = ["HEAD", "GET", "POST", ]
# A list of non simple headers the client is allowed to use with cross-domain requests
cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ]
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
# Maximum number of simultaneous connections (including WebSocket).
# If you want to accept a larger number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
# 1024 - 40 - 10 - 50 = 924 = ~900
max_open_connections = 900
# Maximum number of unique clientIDs that can /subscribe.
# If you're using /broadcast_tx_commit, set to the estimated maximum number
# of broadcast_tx_commit calls per block.
max_subscription_clients = 100
# Maximum number of unique queries a given client can /subscribe to.
# If you're using /broadcast_tx_commit, set to the estimated maximum number
# of broadcast_tx_commit calls per block.
max_subscriptions_per_client = 5
# Experimental parameter to specify the maximum number of events a node will
# buffer, per subscription, before returning an error and closing the
# subscription. Must be set to at least 100, but higher values will accommodate
# higher event throughput rates (and will use more memory).
experimental_subscription_buffer_size = 200
# Experimental parameter to specify the maximum number of RPC responses that
# can be buffered per WebSocket client. If clients cannot read from the
# WebSocket endpoint fast enough, they will be disconnected, so increasing this
# parameter may reduce the chances of them being disconnected (but will cause
# the node to use more memory).
#
# Must be at least the same as "experimental_subscription_buffer_size",
# otherwise connections could be dropped unnecessarily. This value should
# ideally be somewhat higher than "experimental_subscription_buffer_size" to
# accommodate non-subscription-related RPC responses.
experimental_websocket_write_buffer_size = 200
# If a WebSocket client cannot read fast enough, at present we may
# silently drop events instead of generating an error or disconnecting the
# client.
#
# Enabling this experimental parameter will cause the WebSocket connection to
# be closed instead if it cannot read fast enough, allowing for greater
# predictability in subscription behavior.
experimental_close_on_slow_client = false
# How long to wait for a tx to be committed during /broadcast_tx_commit.
# WARNING: Using a value larger than 10s will result in increasing the
# global HTTP write timeout, which applies to all connections and endpoints.
# See https://github.com/tendermint/tendermint/issues/3435
timeout_broadcast_tx_commit = "10s"
# Maximum number of requests that can be sent in a batch
# If the value is set to '0' (zero-value), then no maximum batch size will be
# enforced for a JSON-RPC batch request.
max_request_batch_size = 10
# Maximum size of request body, in bytes
max_body_bytes = 1000000
# Maximum size of request header, in bytes
max_header_bytes = 1048576
# The path to a file containing certificate that is used to create the HTTPS server.
# Might be either absolute path or path related to CometBFT's config directory.
# If the certificate is signed by a certificate authority,
# the certFile should be the concatenation of the server's certificate, any intermediates,
# and the CA's certificate.
# NOTE: both tls_cert_file and tls_key_file must be present for CometBFT to create HTTPS server.
# Otherwise, HTTP server is run.
tls_cert_file = ""
# The path to a file containing matching private key that is used to create the HTTPS server.
# Might be either absolute path or path related to CometBFT's config directory.
# NOTE: both tls_cert_file and tls_key_file must be present for CometBFT to create HTTPS server.
# Otherwise, HTTP server is run.
tls_key_file = ""
# pprof listen address (https://golang.org/pkg/net/http/pprof)
pprof_laddr = "localhost:6060"
#######################################################
### gRPC Server Configuration Options ###
#######################################################
#
# Note that the gRPC server is exposed unauthenticated. It is critical that
# this server not be exposed directly to the public internet. If this service
# must be accessed via the public internet, please ensure that appropriate
# precautions are taken (e.g. fronting with a reverse proxy like nginx with TLS
# termination and authentication, using DDoS protection services like
# CloudFlare, etc.).
#
[grpc]
# TCP or UNIX socket address for the RPC server to listen on. If not specified,
# the gRPC server will be disabled.
laddr = ""
#
# Each gRPC service can be turned on/off, and in some cases configured,
# individually. If the gRPC server is not enabled, all individual services'
# configurations are ignored.
#
# The gRPC version service provides version information about the node and the
# protocols it uses.
[grpc.version_service]
enabled = true
# The gRPC block service returns block information
[grpc.block_service]
enabled = true
# The gRPC block results service returns block results for a given height. If no height
# is given, it will return the block results from the latest height.
[grpc.block_results_service]
enabled = true
#
# Configuration for privileged gRPC endpoints, which should **never** be exposed
# to the public internet.
#
[grpc.privileged]
# The host/port on which to expose privileged gRPC endpoints.
laddr = ""
#
# Configuration specifically for the gRPC pruning service, which is considered a
# privileged service.
#
[grpc.privileged.pruning_service]
# Only controls whether the pruning service is accessible via the gRPC API - not
# whether a previously set pruning service retain height is honored by the
# node. See the [storage.pruning] section for control over pruning.
#
# Disabled by default.
enabled = false
#######################################################
### P2P Configuration Options ###
#######################################################
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:26656"
# Address to advertise to peers for them to dial. If empty, will use the same
# port as the laddr, and will introspect on the listener to figure out the
# address. IP and port are required. Example: 159.89.10.97:26656
external_address = ""
# Comma separated list of seed nodes to connect to
seeds = "38c18461209694e1f667ff2c8636ba827cc01c86@176.9.143.252:11751,4f9025feca44211eddc26cd983372114947b2e85@176.9.140.49:11751,c98bb1b889ddb58b46e4ad3726c1382d37cd5609@65.109.51.80:11751,23d0eea9bb42316ff5ea2f8b4cd8475ef3f35209@65.109.36.70:11751,f9ae40fb4a37b63bea573cc0509b4a63baa1a37a@15.235.114.80:11751,7f3473ddab10322b63789acb4ac58647929111ba@15.235.13.116:11751,ade4d8bc8cbe014af6ebdf3cb7b1e9ad36f412c0@seeds.polkachu.com:14356,ebc272824924ea1a27ea3183dd0b9ba713494f83@injective.mainnet.seed.autostake.net:26726,1846e76e14913124a07e231586d487a0636c0296@tenderseed.ccvalidators.com:26007"
# Comma separated list of nodes to keep persistent connections to
persistent_peers = ""
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
# Set false for private or local networks
addr_book_strict = true
# Maximum number of inbound peers
max_num_inbound_peers = 40
# Maximum number of outbound peers to connect to, excluding persistent peers
max_num_outbound_peers = 40
# List of node IDs, to which a connection will be (re)established ignoring any existing limits
unconditional_peer_ids = ""
# Maximum pause when redialing a persistent peer (if zero, exponential backoff is used)
persistent_peers_max_dial_period = "0s"
# Time to wait before flushing messages out on the connection
flush_throttle_timeout = "10ms"
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 5120000
# Rate at which packets can be received, in bytes/second
recv_rate = 5120000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
# Toggle to disable guard against peers connecting from the same ip.
allow_duplicate_ip = false
# Peer connection configuration.
handshake_timeout = "20s"
dial_timeout = "3s"
#######################################################
### Mempool Configuration Options ###
#######################################################
[mempool]
# The type of mempool for this node to use.
#
# Possible types:
# - "flood" : concurrent linked list mempool with flooding gossip protocol
# (default)
# - "nop" : nop-mempool (short for no operation; the ABCI app is responsible
# for storing, disseminating and proposing txs). "create_empty_blocks=false" is
# not supported.
type = "flood"
# recheck (default: true) defines whether CometBFT should recheck the
# validity for all remaining transaction in the mempool after a block.
# Since a block affects the application state, some transactions in the
# mempool may become invalid. If this does not apply to your application,
# you can disable rechecking.
recheck = true
# recheck_timeout is the time the application has during the rechecking process
# to return CheckTx responses, once all requests have been sent. Responses that
# arrive after the timeout expires are discarded. It only applies to
# non-local ABCI clients and when recheck is enabled.
recheck_timeout = "1s"
# broadcast (default: true) defines whether the mempool should relay
# transactions to other peers. Setting this to false will stop the mempool
# from relaying transactions to other peers until they are included in a
# block. In other words, if Broadcast is disabled, only the peer you send
# the tx to will see it until it is included in a block.
broadcast = true
# wal_dir (default: "") configures the location of the Write Ahead Log
# (WAL) for the mempool. The WAL is disabled by default. To enable, set
# wal_dir to where you want the WAL to be written (e.g.
# "data/mempool.wal").
wal_dir = ""
# Maximum number of transactions in the mempool
size = 200
# Maximum size in bytes of a single transaction accepted into the mempool.
max_tx_bytes = 1048576
# The maximum size in bytes of all transactions stored in the mempool.
# This is the raw, total transaction size. For example, given 1MB
# transactions and a 5MB maximum mempool byte size, the mempool will
# only accept five transactions.
max_txs_bytes = 67108864
# Size of the cache (used to filter transactions we saw earlier) in transactions
cache_size = 10000
# Do not remove invalid transactions from the cache (default: false)
# Set to true if it's not possible for any invalid transaction to become valid
# again in the future.
keep-invalid-txs-in-cache = false
# Experimental parameters to limit gossiping txs to up to the specified number of peers.
# We use two independent upper values for persistent and non-persistent peers.
# Unconditional peers are not affected by this feature.
# If we are connected to more than the specified number of persistent peers, only send txs to
# ExperimentalMaxGossipConnectionsToPersistentPeers of them. If one of those
# persistent peers disconnects, activate another persistent peer.
# Similarly for non-persistent peers, with an upper limit of
# ExperimentalMaxGossipConnectionsToNonPersistentPeers.
# If set to 0, the feature is disabled for the corresponding group of peers, that is, the
# number of active connections to that group of peers is not bounded.
# For non-persistent peers, if enabled, a value of 10 is recommended based on experimental
# performance results using the default P2P configuration.
experimental_max_gossip_connections_to_persistent_peers = 0
experimental_max_gossip_connections_to_non_persistent_peers = 0
# When using the Flood mempool type, enable the DOG gossip protocol to
# reduce network bandwidth on transaction dissemination (for details, see
# specs/mempool/gossip/).
dog_protocol_enabled = true
# Used by the DOG protocol to set the desired transaction redundancy level
# for the node. For example, redundancy of 0.5 means that, for every two first-time
# transactions received, the node will receive one duplicate transaction.
dog_target_redundancy = 1
# Used by the DOG protocol to set how often it will attempt to adjust the
# redundancy level. The higher the value, the longer it will take the node
# to reduce bandwidth and converge to a stable redundancy level.
dog_adjust_interval = "1s"
#######################################################
### State Sync Configuration Options ###
#######################################################
[statesync]
# State sync rapidly bootstraps a new node by discovering, fetching, and restoring a state machine
# snapshot from peers instead of fetching and replaying historical blocks. Requires some peers in
# the network to take and serve state machine snapshots. State sync is not attempted if the node
# has any local state (LastBlockHeight > 0). The node will have a truncated block history,
# starting from the height of the snapshot.
enable = false
# RPC servers (comma-separated) for light client verification of the synced state machine and
# retrieval of state data for node bootstrapping. Also needs a trusted height and corresponding
# header hash obtained from a trusted source, and a period during which validators can be trusted.
#
# For Cosmos SDK-based chains, trust_period should usually be about 2/3 of the unbonding time (~2
# weeks) during which they can be financially punished (slashed) for misbehavior.
rpc_servers = ""
trust_height = 0
trust_hash = ""
trust_period = "168h0m0s"
# Time to spend discovering snapshots before initiating a restore.
discovery_time = "15s"
# Temporary directory for state sync snapshot chunks, defaults to the OS tempdir (typically /tmp).
# Will create a new, randomly named directory within, and remove it when done.
temp_dir = ""
# The timeout duration before re-requesting a chunk, possibly from a different
# peer (default: 1 minute).
chunk_request_timeout = "10s"
# The number of concurrent chunk fetchers to run (default: 1).
chunk_fetchers = "4"
#######################################################
### Block Sync Configuration Options ###
#######################################################
[blocksync]
# Block Sync version to use:
#
# In v0.37, v1 and v2 of the block sync protocols were deprecated.
# Please use v0 instead.
#
# 1) "v0" - the default block sync implementation
version = "v0"
#######################################################
### Consensus Configuration Options ###
#######################################################
[consensus]
wal_file = "data/cs.wal/wal"
# How long we wait for a proposal block before prevoting nil
timeout_propose = "1s"
# How much timeout_propose increases with each round
timeout_propose_delta = "100ms"
# How long we wait after receiving +2/3 prevotes for “anything” (ie. not a single block or nil)
timeout_prevote = "250ms"
# How much the timeout_prevote increases with each round
timeout_prevote_delta = "100ms"
# How long we wait after receiving +2/3 precommits for “anything” (ie. not a single block or nil)
timeout_precommit = "250ms"
# How much the timeout_precommit increases with each round
timeout_precommit_delta = "100ms"
# How long we wait after committing a block, before starting on the new
# height (this gives us a chance to receive some more precommits, even
# though we already have +2/3).
# Set to 0 if you want to make progress as soon as the node has all the precommits.
timeout_commit = "500ms"
# Deprecated: set `timeout_commit` to 0 instead.
skip_timeout_commit = false
# How many blocks to look back to check existence of the node's consensus votes before joining consensus
# When non-zero, the node will panic upon restart
# if the same consensus key was used to sign {double_sign_check_height} last blocks.
# So, validators should stop the state machine, wait for some blocks, and then restart the state machine to avoid panic.
double_sign_check_height = 0
# EmptyBlocks mode and possible interval between empty blocks
create_empty_blocks = true
create_empty_blocks_interval = "0s"
# Reactor sleep duration parameters
peer_gossip_sleep_duration = "10ms"
peer_gossip_intraloop_sleep_duration = "0s"
peer_query_maj23_sleep_duration = "2s"
#######################################################
### Storage Configuration Options ###
#######################################################
[storage]
# Set to true to discard ABCI responses from the state store, which can save a
# considerable amount of disk space. Set to false to ensure ABCI responses are
# persisted. ABCI responses are required for /block_results RPC queries, and to
# reindex events in the command-line tool.
discard_abci_responses = false
# The representation of keys in the database.
# The current representation of keys in Comet's stores is considered to be v1
# Users can experiment with a different layout by setting this field to v2.
# Note that this is an experimental feature and switching back from v2 to v1
# is not supported by CometBFT.
# If the database was initially created with v1, it is necessary to migrate the DB
# before switching to v2. The migration is not done automatically.
# v1 - the legacy layout existing in Comet prior to v1.
# v2 - Order preserving representation ordering entries by height.
experimental_db_key_layout = "v1"
# If set to true, CometBFT will force compaction to happen for databases that support this feature.
# and save on storage space. Setting this to true is most benefits when used in combination
# with pruning as it will physically delete the entries marked for deletion.
# false by default (forcing compaction is disabled).
compact = false
# To avoid forcing compaction every time, this parameter instructs CometBFT to wait
# the given amount of blocks to be pruned before triggering compaction.
# It should be tuned depending on the number of items. If your retain height is 1 block,
# it is too much of an overhead to try compaction every block. But it should also not be a very
# large multiple of your retain height as it might occur bigger overheads.
compaction_interval = "1000"
[storage.pruning]
# The time period between automated background pruning operations.
interval = "10s"
#
# Storage pruning configuration relating only to the data companion.
#
[storage.pruning.data_companion]
# Whether automatic pruning respects values set by the data companion. Disabled
# by default. All other parameters in this section are ignored when this is
# disabled.
#
# If disabled, only the application retain height will influence block pruning
# (but not block results pruning). Only enabling this at a later stage will
# potentially mean that blocks below the application-set retain height at the
# time will not be available to the data companion.
enabled = false
# The initial value for the data companion block retain height if the data
# companion has not yet explicitly set one. If the data companion has already
# set a block retain height, this is ignored.
initial_block_retain_height = 0
# The initial value for the data companion block results retain height if the
# data companion has not yet explicitly set one. If the data companion has
# already set a block results retain height, this is ignored.
initial_block_results_retain_height = 0
#######################################################
### Transaction Indexer Configuration Options ###
#######################################################
[tx_index]
# What indexer to use for transactions
#
# The application will set which txs to index. In some cases a node operator will be able
# to decide which txs to index based on configuration set in the application.
#
# Options:
# 1) "null"
# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
# - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed.
# 3) "psql" - the indexer services backed by PostgreSQL.
# When "kv" or "psql" is chosen "tx.height" and "tx.hash" will always be indexed.
indexer = "kv"
# The PostgreSQL connection configuration, the connection format:
# postgresql://:@:/?
psql-conn = ""
#######################################################
### Instrumentation Configuration Options ###
#######################################################
[instrumentation]
# When true, Prometheus metrics are served under /metrics on
# PrometheusListenAddr.
# Check out the documentation for the list of available metrics.
prometheus = true
# Address to listen for Prometheus collector(s) connections
prometheus_listen_addr = "127.0.0.1:26660"
# Maximum number of simultaneous connections.
# If you want to accept a larger number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
max_open_connections = 3
# Instrumentation namespace
namespace = "cometbft"
```
# Upgrade to v1.20.1
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.20.1
How to upgrade Injective Mainnet Validator node to v1.20.1
Thursday, July 2nd, 2026
Following [IIP-665](https://injhub.com/proposal/665/) This indicates that the upgrade procedure should be performed on block number **172502000**
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade at approximately **Thursday, July 2nd, 2026, 10:00 AM ET / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **172502000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.20.1](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.1-1782532109) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in a consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.20.0](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.0) and continue this earlier chain until next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Breaking changes
Verify that you make the following changes as part of the upgrade process.
The Go build environment has changed.
In the previous release, the Go version was 1.26.2 (`go1.26.2`).
Update your Go version to 1.26.4 (`go1.26.4`).
### Steps
1. Verify you are currently running the correct version (`v1.20.0`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.20.0 (3ade14d)
Compiled at 20260530-0816 using Go go1.26.2 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.20.1:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.20.1-1782532109/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.20.1) of `injectived` after downloading the v1.20.1 release:
```bash theme={null}
$ injectived version
Version v1.20.1 (eef179e)
Compiled at 20260627-0349 using Go go1.26.4 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (v1.20.1) of `peggo` after downloading the v1.20.1 release:
```bash theme={null}
$ peggo version
Version v1.20.1 (eef179e)
Compiled at 20260627-0356 using Go go1.26.4 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.20.3
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.20.3
How to upgrade Injective Mainnet Validator node to v1.20.3
Wednesday, July 29th, 2026
Following [IIP-677](https://injhub.com/proposal/677/), the upgrade procedure should be performed at block height **176273000**.
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade at approximately **Wednesday, July 29th, 2026, 10:00 AM ET / 14:00 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **176273000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.20.3](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.3-1784976496) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in consensus rounds. It is a critical file needed to prevent double-signing in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.20.1](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.1-1782532109) and continue this earlier chain until the next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`v1.20.1`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.20.1 (eef179e)
Compiled at 20260627-0349 using Go go1.26.4 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.20.3:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.20.3-1784976496/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.20.3) of `injectived` after downloading the v1.20.3 release:
```bash theme={null}
$ injectived version
Version v1.20.3 (5c3143e)
Compiled at 20260725-1049 using Go go1.26.4 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (v1.20.3) of `peggo` after downloading the v1.20.3 release:
```bash theme={null}
$ peggo version
Version v1.20.3 (5c3143e)
Compiled at 20260725-1056 using Go go1.26.4 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Upgrade to v1.20.3-safeharbor.2
Source: https://docs.injective.network/infra/validator-mainnet/canonical-chain-upgrade-v1.20.3-safeharbor.2
How to upgrade Injective Mainnet Validator node to v1.20.3-safeharbor.2
Wednesday, September 2nd, 2026
Following [IIP-690](https://injhub.com/proposal/690/), the upgrade procedure should be performed at block height **181295000**.
* [Summary](#summary)
* [Recovery](#recovery)
* [Upgrade Procedure](#upgrade-procedure)
* [Notes for Validators](#notes-for-validators)
## Summary
The Injective Chain will undergo a scheduled enhancement upgrade at approximately **Wednesday, September 2nd, 2026, 1:30 PM EDT / 17:30 UTC**.
The following is a short summary of the upgrade steps:
1. Vote and wait till the node panics at block height **181295000**.
2. Backing up configs, data, and keys used for running the Injective Chain.
3. Install the [v1.20.3-safeharbor.2](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.3-safeharbor.2-1788352700) binaries.
4. Start your node with the new injectived binary to fulfill the upgrade.
Upgrade coordination and support for validators will be available on the `#validators` private channel of the [Injective Discord](https://discord.gg/injective).
The network upgrade can take the following potential pathways:
1. **Happy path**:\
Validators successfully upgrade the chain without purging the blockchain history, and all validators are up within 5-10 minutes of the upgrade.
2. **Not-so-happy path**:\
Validators have trouble upgrading to the latest Canonical chain.
3. **Abort path**:\
In the rare event that the team becomes aware of unnoticed critical issues, the Injective team will attempt to patch all the breaking states and provide another official binary within 36 hours.\
If the chain is not successfully resumed within 36 hours, the upgrade will be announced as aborted on the `#validators` channel in [Injective's Discord](https://discord.gg/injective), and validators will need to resume running the chain without any updates or changes.
## Recovery
Prior to exporting chain state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snapshotting depends heavily on infrastructure, but generally this can be done by backing up the `.injectived` directory.
It is critically important to backup the `.injectived/data/priv_validator_state.json` file after stopping your injectived process. This file is updated every block as your validator participates in consensus rounds. It is a critical file needed to prevent double-signing in case the upgrade fails and the previous chain needs to be restarted.
In the event that the upgrade does not succeed, validators and operators must restore the snapshot and downgrade back to Injective Chain release [v1.20.3](https://github.com/InjectiveFoundation/injective-core/releases/tag/v1.20.3-1784976496) and continue this earlier chain until the next upgrade announcement.
## Upgrade Procedure
### Notes for Validators
You must remove the wasm cache before upgrading to the new version:
```shell theme={null}
rm -rf .injectived/wasm/wasm/cache/
```
### Steps
1. Verify you are currently running the correct version (`v1.20.3`) of `injectived`:
```bash theme={null}
$ injectived version
Version v1.20.3 (5c3143e)
Compiled at 20260725-1049 using Go go1.26.4 (amd64)
```
2. Make a backup of your `.injectived` directory:
```bash theme={null}
cp -r ~/.injectived ./injectived-backup
```
3. Download and install the `injective-chain` release for v1.20.3-safeharbor.2:
```bash theme={null}
wget https://github.com/InjectiveFoundation/injective-core/releases/download/v1.20.3-safeharbor.2-1788352700/linux-amd64.zip
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
4. Verify you are currently running the correct version (v1.20.3-safeharbor.2) of `injectived` after downloading the v1.20.3-safeharbor.2 release:
```bash theme={null}
$ injectived version
Version v1.20.3-safeharbor.2 (de8a811)
Compiled at 20260902-1239 using Go go1.26.4 (amd64)
```
5. Start `injectived`:
```bash theme={null}
injectived start
```
6. Verify you are currently running the correct version (v1.20.3-safeharbor.2) of `peggo` after downloading the v1.20.3-safeharbor.2 release:
```bash theme={null}
$ peggo version
Version v1.20.3-safeharbor.2 (de8a811)
Compiled at 20260902-1247 using Go go1.26.4 (amd64)
```
7. Start peggo:
```bash theme={null}
peggo orchestrator
```
# Mainnet Validators
Source: https://docs.injective.network/infra/validator-mainnet/index
Step-by-step guide to setting up and running a validator node on Injective Mainnet. Includes hardware requirements, key management, and commission configuration.
Before setting up a validator, ensure your server meets the [Hardware Requirements](/infra/hardware).
### Step 1: Create a Validator Account
First, run the keygen command with your desired validator key name.
```bash theme={null}
export VALIDATOR_KEY_NAME=[my-validator-key]
injectived keys add $VALIDATOR_KEY_NAME
```
This will derive a new private key and encrypt it to disk. Make sure to remember the password you used.
```bash theme={null}
# EXAMPLE OUTPUT
- name: myvalidatorkey
type: local
address: inj1queq795wx8gzqc8706uz80whp07mcgg5nmpj6h
pubkey: injpub1r0mckeepqwzmrzt5af00hgc7fhve05rr0q3q6wvx4xn6k46zguzykdszg6cnu0zca4q
mnemonic: ""
threshold: 0
pubkeys: []
**Important** write this mnemonic phrase in a safe place.
It is the only way to recover your account if you ever forget your password.
```
**The output will contain a mnemonic phrase that represents your key in plain text. Make sure to save this phrase as a backup of your key, since without a key you will not be able to control your validator. The phrase is better be backed up on physical paper, storing it in cloud storage may compromise your validator later.**
Remember the address starting from `inj`, this is going to be your Injective Validator Account address.
### Step 2: Obtain Mainnet INJ
To proceed with the next step, you will need to obtain some real INJ on Mainnet Ethereum (ERC-20 token address [`0xe28b3b32b6c345a34ff64674606124dd5aceca30`](https://etherscan.io/token/0xe28b3b32b6c345a34ff64674606124dd5aceca30)).
### Step 3: "Transfer" INJ to your validator account on Injective
Deposit your Mainnet INJ tokens into your validator's account on Injective by using the staking dashboard. You will have to [connect your wallet](https://medium.com/injective-labs/injective-hub-guide-9a14f09f6a7d) on our [Hub](https://injhub.com/bridge) and then deposit INJ from Ethereum Mainnet network. This will trigger an automated bridge that maps tokens from Ethereum network to Injective.
After a few minutes, you should be able to verify that your deposit was successful on the UI. Alternatively, you can query your account balance using the `injectived` CLI with the following command:
```bash theme={null}
injectived q bank balances
```
### Step 4: Create your validator account
Obtain your node's Tendermint validator Bech32 encoded PubKey consensus address.
```bash theme={null}
VALIDATOR_PUBKEY=$(injectived tendermint show-validator)
echo $VALIDATOR_PUBKEY
# Example: {"@type": "/cosmos.crypto.ed25519.PubKey", "key": "GWEJv/KSFhUUcKBWuf9TTT3Ful+3xV/1lFhchyW1TZ8="}
```
Then create your new validator initialized with a self-delegation with your INJ tokens. Most critically, you will need to decide on the values of your validator's staking parameters.
* `--moniker` - Your validator's name
* `--amount` - Your validator's initial amount of INJ to bond
* `--commission-max-change-rate` - Your validator's maximum commission change rate percentage (per day)
* `--commission-max-rate` - Your validator's maximum commission rate percentage
* `--commission-rate` - Your validator's initial commission rate percentage
* `--min-self-delegation` - Your validator's minimum required self delegation
Once you decide on your desired values, set them as follows.
```bash theme={null}
MONIKER=
AMOUNT=100000000000000000000inj # to delegate 100 INJ, as INJ is represented with 18 decimals.
COMMISSION_MAX_CHANGE_RATE=0.1 # e.g. for a 10% maximum change rate percentage per day
COMMISSION_MAX_RATE=0.1 # e.g. for a 10% maximum commission rate percentage
COMMISSION_RATE=0.1 # e.g. for a 10% initial commission rate percentage
MIN_SELF_DELEGATION_AMOUNT=50000000000000000000 # e.g. for a minimum 50 INJ self delegation required on the validator
```
Then run the following command to create your validator.
```bash theme={null}
injectived tx staking create-validator \
--moniker=$MONIKER \
--amount=$AMOUNT \
--gas-prices=500000000inj \
--pubkey=$VALIDATOR_PUBKEY \
--from=$VALIDATOR_KEY_NAME \
--keyring-backend=file \
--yes \
--node=tcp://localhost:26657 \
--chain-id=injective-1
--commission-max-change-rate=$COMMISSION_MAX_CHANGE_RATE \
--commission-max-rate=$COMMISSION_MAX_RATE \
--commission-rate=$COMMISSION_RATE \
--min-self-delegation=$MIN_SELF_DELEGATION_AMOUNT
```
Extra `create-validator` options to consider:
```
--identity= The optional identity signature (ex. UPort or Keybase)
--pubkey= The Bech32 encoded PubKey of the validator
--security-contact= The validator's (optional) security contact email
--website= The validator's (optional) website
```
You can check that your validator was successfully created by checking the [Injective Hub staking dashboard](https://injhub.com/stake) or by entering the following CLI command.
```bash theme={null}
injectived q staking validators
```
If you see your validator in the list of validators, then congratulations, you've officially joined as an Injective Mainnet validator! 🎉
### Step 5: (Optional) Delegate Additional INJ to your Validator
To gain a deeper empirical understanding of user experience that your future delegators will experience, you can try delegation through [Staking Guide](https://medium.com/injective-labs/injective-hub-guide-9a14f09f6a7d).
These steps will allow you to experience the delegation flow using MetaMask Transactions. 🦊
Alternatively, you can always use the Injective CLI to send a delegation transaction.
```bash theme={null}
injectived tx staking delegate [validator-addr] [amount] --from $VALIDATOR_KEY_NAME --keyring-backend=file --yes --node=tcp://localhost:26657
```
### Step 6: (Recommended) Connecting Your Validator Identity with Keybase
By adding your Keybase pubkey to your validator identity information in Injective, you can automatically pull in your Keybase public profile information in client applications like the Injective Hub and Explorer. Here's how to connect your validator identity with your Keybase pubkey:
1. Create a validator profile on Keybase at [https://keybase.io/](https://keybase.io/) and make sure it's complete.
2. Add your validator identity pubkey to Injective:
* Send a `MsgEditValidator` to update your `Identity` validator identity with your Keybase pubkey. You can also use this message to change your website, contact email, and other details.
That's it! Once you've connected your validator identity with Keybase, the Injective Explorer and Hub can automatically pull in your brand identity, and other public profile information.
#### Next Steps
Next, proceed to set up your [Ethereum Bridge Relayer](/infra/validator-mainnet/peggo/). This is a necessary step to prevent your validator from being slashed. You should do this immediately after setting up your validator.
# Peggo Bridge
Source: https://docs.injective.network/infra/validator-mainnet/peggo
Peggo is the Peggy bridge orchestrator that runs alongside `injectived` on validator nodes. It handles four responsibilities:
* **Oracle** - monitors Ethereum for deposit and bridge events, then reports them to the Injective chain
* **Signer** - signs validator set updates and transaction batches
* **Batch Creator** - groups outbound token transfers into batches for efficient bridging
* **Relayer** - submits signed validator sets and transaction batches to the Ethereum Peggy contract
Validators who do not run peggo will be **jailed** for missing batch confirmations or validator set confirmations within the required signing windows.
### Configuration
Example of `.env` for peggo:
```bash theme={null}
PEGGO_ENV="local" # environment name for metrics (dev/test/staging/prod/local)
PEGGO_LOG_LEVEL="debug" # log level depth
PEGGO_COSMOS_CHAIN_ID="injective-1" # chain ID of the Injective network
PEGGO_COSMOS_GRPC="tcp://localhost:9090" # gRPC of your injectived process
PEGGO_TENDERMINT_RPC="http://localhost:26657" # Tendermint RPC of your injectived process
# Note: omitting PEGGO_COSMOS_GRPC and PEGGO_TENDERMINT_RPC enables stand-alone peggo mode. In this mode,
# peggo is connected to load balanced endpoints provided by the Injective network. This decouples peggo's connection from your injectived process.
# Injective config
PEGGO_COSMOS_FEE_DENOM="inj" # token used to pay fees on Injective
PEGGO_COSMOS_GAS_PRICES="160000000inj" # default --gas-prices flag value for sending messages to Injective
PEGGO_COSMOS_KEYRING="file" # keyring backends ("os", "file", "kwallet", "memory", "pass", "test")
PEGGO_COSMOS_KEYRING_DIR= # path to your keyring dir
PEGGO_COSMOS_KEYRING_APP="peggo" # arbitrary name for your keyring app
PEGGO_COSMOS_FROM= # account address of your Validator (or your Delegated Orchestrator)
PEGGO_COSMOS_FROM_PASSPHRASE= # keyring passphrase
PEGGO_COSMOS_PK= # private key of your Validator (or your Delegated Orchestrator)
PEGGO_COSMOS_USE_LEDGER=false
# Ethereum config
PEGGO_ETH_KEYSTORE_DIR= # path to your Ethereum keystore
PEGGO_ETH_FROM= # your Ethereum address (must be Delegated Ethereum address if you're a Validator)
PEGGO_ETH_PASSPHRASE= # passphrase of your Ethereum keystore
PEGGO_ETH_PK= # private key of your Ethereum address
PEGGO_ETH_GAS_PRICE_ADJUSTMENT=1.3 # suggested Ethereum gas price will be adjusted by this factor (Relayer)
PEGGO_ETH_MAX_GAS_PRICE="500gwei" # max gas price allowed for sending Eth transactions (Relayer)
PEGGO_ETH_CHAIN_ID=1 # chain ID of Ethereum network
PEGGO_ETH_RPC="http://localhost:8545" # RPC of your Ethereum node
PEGGO_ETH_ALCHEMY_WS="" # optional websocket endpoint for listening pending transactions on Peggy.sol
PEGGO_ETH_USE_LEDGER=false
# Price feed provider for token assets (Batch Creator)
PEGGO_COINGECKO_API="https://api.coingecko.com/api/v3"
# Relayer config
PEGGO_RELAY_VALSETS=true # set to `true` to relay Validator Sets
PEGGO_RELAY_VALSET_OFFSET_DUR="5m" # duration which needs to expire before a Valset is eligible for relaying
PEGGO_RELAY_BATCHES=true # set to `true` to relay Token Batches
PEGGO_RELAY_BATCH_OFFSET_DUR="5m" # duration which needs to expire before a Token Batch is eligible for relaying
PEGGO_RELAY_PENDING_TX_WAIT_DURATION="20m" # time to wait until a pending tx is processed
# Batch Creator config
PEGGO_MIN_BATCH_FEE_USD=23.2 # minimum amount of fee a Token Batch must satisfy to be created
# Metrics config
PEGGO_STATSD_PREFIX="peggo."
PEGGO_STATSD_ADDR="localhost:8125"
PEGGO_STATSD_STUCK_DUR="5m"
PEGGO_STATSD_MOCKING=false
PEGGO_STATSD_DISABLED=true
```
**IMPORTANT NOTE:** if you're running your own `injectived` (Injective node) and `geth` (Ethereum node) processes, ensure that they are in sync with the latest state. Outdated nodes can skew the business logic of `peggo` to display "false alarm" logs sometimes.
## Step 1: Configuring .env
```bash theme={null}
# official Injective mainnet .env config
mkdir ~/.peggo
cp mainnet-config/10001/peggo-config.env ~/.peggo/.env
cd ~/.peggo
```
Ethereum config
First, update the `PEGGO_ETH_RPC` in the `.env` file with a valid Ethereum EVM RPC Endpoint.
To set up your own Ethereum full node, follow the instructions [here](https://ethereum.org/en/developers/docs/nodes-and-clients/run-a-node/). It's possible to use an external Ethereum RPC provider such as Alchemy or Infura, but keep in mind that the Peggo bridge relayer makes a heavy use of `eth_getLogs` calls which may increase your cost burden, depending on your provider.
## **Managing Ethereum keys for `peggo`**
Peggo supports two options to provide signing key credentials - using the Geth keystore (recommended) or by providing a plaintext Ethereum private key.
#### **Option 1. Geth Keystore**
You can find instructions for securely creating a new Ethereum account using a keystore in the Geth Documentation [here](https://geth.ethereum.org/docs/interface/managing-your-accounts).
For convenience, an example is provided below.
```bash theme={null}
geth account new --datadir=/home/ec2-user/.peggo/data/
INFO [03-23|18:18:36.407] Maximum peer count ETH=50 LES=0 total=50
Your new account is locked with a password. Please give a password. Do not forget this password.
Password:
Repeat password:
Your new key was generated
Public address of the key: 0x9782dc957DaE6aDc394294954B27e2118D05176C
Path of the secret key file: /home/ec2-user/.peggo/data/keystore/UTC--2021-03-23T15-18-44.284118000Z--9782dc957dae6adc394294954b27e2118d05176c
- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
```
Make sure you heed the warnings that geth provides, particularly in backing up your key file so that you don't lose your keys by mistake. We also recommend not using any quote or backtick characters in your passphrase for peggo compatibility purposes.
You should now set the following env variables:
```bash theme={null}
# example values, replace with your own
PEGGO_ETH_KEYSTORE_DIR=/home/ec2-user/.peggo/data/keystore
PEGGO_ETH_FROM=0x9782dc957DaE6aDc394294954B27e2118D05176C
PEGGO_ETH_PASSPHRASE=12345678
```
Then ensure that your Ethereum address has enough ETH.
#### **Option 2. Ethereum Private Key (Unsafe)**
Simply update the `PEGGO_ETH_PK` with a new Ethereum Private Key from a new account.
Then ensure that your Ethereum address has enough ETH.
## Injective config
### **Creating your delegated Cosmos Key for sending Injective transactions**
Your peggo orchestrator can either:
* Use an explicitly delegated account key specific for sending validator specific Peggy transactions (i.e., `ValsetConfirm`, `BatchConfirm`, and `SendToCosmos` transactions) or
* Simply use your validator's account key ("your Validator is your Orchestrator")
For isolation purposes, we recommend creating a delegated Cosmos key to send Injective transactions instead of using your validator account key.
To create a new key, run
```bash theme={null}
injectived keys add $ORCHESTRATOR_KEY_NAME
```
Then ensure that your orchestrator inj address has INJ balance in it, so peggo orchestrator can send messages to Injective.
To obtain your orchestrator's inj address, run
```bash theme={null}
injectived keys list $ORCHESTRATOR_KEY_NAME
```
You can transfer INJ from your validator account to orchestrator address using this command
```bash theme={null}
injectived tx bank send $VALIDATOR_KEY_NAME $ORCHESTRATOR_INJ_ADDRESS --chain-id=injective-1 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
Example
```bash theme={null}
injectived tx bank send genesis inj1u3eyz8nkvym0p42h79aqgf37gckf7szreacy9e 20000000000000000000inj --chain-id=injective-1 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
You can then verify that your orchestrator account has INJ balances by running
```bash theme={null}
injectived q bank balances $ORCHESTRATOR_INJ_ADDRESS
```
### **Managing Cosmos account keys for `peggo`**
Peggo supports two options to provide Cosmos signing key credentials - using the Cosmos keyring (recommended) or by providing a plaintext private key.
#### **Option 1. Cosmos Keyring**
In the `.env` file, first specify the `PEGGO_COSMOS_FROM` and `PEGGO_COSMOS_FROM_PASSPHRASE` corresponding to your peggo account signing key.
If you are using a delegated account key configuration as recommended above, this will be your `$ORCHESTRATOR_KEY_NAME` and passphrase respectively. Otherwise, this should be your `$VALIDATOR_KEY_NAME` and associated validator passphrase.
Please note that the default keyring backend is `file` and that as such peggo will try to locate keys on disk by default.
To use the default injectived key configuration, you should set the keyring path to the home directory of your injectived node, e.g., `~/.injectived`.
You can also read more about the Cosmos Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html).
#### **Option 2. Cosmos Private Key (Unsafe)**
In the `.env` file, specify the `PEGGO_COSMOS_PK` corresponding to your peggo account signing key.
If you are using a delegated account key configuration as recommended above, this will be your orchestrator account's private key. Otherwise, this should be your validator's account private key.
To obtain your orchestrator's Cosmos private key (if applicable), run
```bash theme={null}
injectived keys unsafe-export-eth-key $ORCHESTRATOR_KEY_NAME
```
To obtain your validator's Cosmos private key (if applicable), run
```bash theme={null}
injectived keys unsafe-export-eth-key $VALIDATOR_KEY_NAME
```
Again, this method is less secure and is not recommended.
### Step 2: Register Your Orchestrator and Ethereum Address
You can register orchestrator and ethereum address only once. It **CANNOT** be updated later. So Check twice before running below command.
```bash theme={null}
injectived tx peggy set-orchestrator-address $VALIDATOR_INJ_ADDRESS $ORCHESTRATOR_INJ_ADDRESS $ETHEREUM_ADDRESS --from $VALIDATOR_KEY_NAME --chain-id=injective-1 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
* To obtain your validator's inj address, run, `injectived keys list $VALIDATOR_KEY_NAME`
* To obtain your orchestrators's inj address, `injectived keys list $ORCHESTRATOR_KEY_NAME`
Example:
```bash theme={null}
injectived tx peggy set-orchestrator-address inj10m247khat0esnl0x66vu9mhlanfftnvww67j9n inj1x7kvxlz2epqx3hpq6v8j8w859t29pgca4z92l2 0xf79D16a79130a07e77eE36e8067AeA783aBdA3b6 --from validator-key-name --chain-id=injective-1 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
You can verify successful registration by checking for your Validator's mapped Ethereum address on [https://lcd.injective.network/peggy/v1/valset/current](https://lcd.injective.network/peggy/v1/valset/current).
**NOTE:** Once you've registered your Orchestrator with the `set-orchestrator-address` message, you **CANNOT** register again. Once this step is complete, your `Validator` is bound to the provided Ethereum address (as well the Delegated address you may have provided). In other words, your peggo must always run with the addresses you provided for registration.
### Step 3: Start the Relayer
```bash theme={null}
cd ~/.peggo
peggo orchestrator
```
This starts the Peggo bridge (relayer / orchestrator).
### Step 4: Create a Peggo systemd service
Add `peggo.service` file with below content under `/etc/systemd/system/peggo.service`
```ini theme={null}
[Unit]
Description=peggo
[Service]
WorkingDirectory=/home/ec2-user/.peggo
ExecStart=/bin/bash -c 'peggo orchestrator '
Type=simple
Restart=always
RestartSec=1
User=ec2-user
[Install]
WantedBy=multi-user.target
```
Then use the following commands to configure Environment variables, start and stop the peggo relayer.
```bash theme={null}
sudo systemctl start peggo
sudo systemctl stop peggo
sudo systemctl restart peggo
sudo systemctl status peggo
# enable start on system boot
sudo systemctl enable peggo
# To check Logs
journalctl -f -u peggo
```
### Step 5: (Optional) Protect Cosmos Keyring from unauthorized access
This is an advanced DevOps topic, consult with your sysadmin.
Learn more about Cosmos Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html). Once you've launched your node, the default keyring will have the validator operator key stored on disk in the encrypted form. Usually the keyring is located within node's homedir, i.e. `~/.injectived/keyring-file`.
Some sections of the Injective Staking documentation will guide you through using this key for governance purposes, i.e. submitting transactions and setting up an Ethereum bridge. In order to protect the keys from unauthorized access, even when the keyring passphrase is leaked via configs, you can set OS permissions to allow disk access to `injectived` / `peggo` processes only.
In Linux systems like Debian, Ubuntu and RHEL, this can be achieved using POSIX Access Control Lists (ACLs). Before beginning to work with ACLs, the file system must be mounted with ACLs turned on. There are some official guides for each distro:
* [Ubuntu](https://help.ubuntu.com/community/FilePermissionsACLs)
* [Debian](https://wiki.debian.org/Permissions)
* [Amazon Linux (RHEL)](https://www.redhat.com/sysadmin/linux-access-control-lists)
### Contribute
If you'd like to inspect the Peggo orchestrator source code and contribute, you can do so at [https://github.com/InjectiveLabs/peggo](https://github.com/InjectiveLabs/peggo).
# Testnet Validators
Source: https://docs.injective.network/infra/validator-testnet/index
Step-by-step guide to setting up and running a validator node on Injective testnet. Includes hardware requirements, key management, and commission configuration.
Node operators should deploy bare metal servers to achieve optimal performance. Additionally, validator nodes must meet the recommended hardware specifications and particularly the CPU requirements, to ensure high uptime.
#### Hardware Requirements
| *Minimum* | *Recommendation* |
| :-------------------: | :-------------------: |
| RAM Memory 128GB | RAM Memory 128GB |
| CPU 12 cores | CPU 16 cores |
| CPU base clock 3.7GHz | CPU base clock 4.2GHz |
| Storage 2TB NVMe | Storage 2TB NVMe |
| Network 1Gbps+ | Network 1Gbps+ |
### Step 1: Create a Validator Account
First, run the keygen command with your desired validator key name.
```bash theme={null}
export VALIDATOR_KEY_NAME=[my-validator-key]
injectived keys add $VALIDATOR_KEY_NAME
```
This will derive a new private key and encrypt it to disk. Make sure to remember the password you used.
```bash theme={null}
# EXAMPLE OUTPUT
- name: myvalidatorkey
type: local
address: inj1queq795wx8gzqc8706uz80whp07mcgg5nmpj6h
pubkey: injpub1r0mckeepqwzmrzt5af00hgc7fhve05rr0q3q6wvx4xn6k46zguzykdszg6cnu0zca4q
mnemonic: ""
threshold: 0
pubkeys: []
**Important** write this mnemonic phrase in a safe place.
It is the only way to recover your account if you ever forget your password.
```
**The output will contain a mnemonic phrase that represents your key in plain text. Make sure to save this phrase as a backup of your key, since without a key you will not be able to control your validator. The phrase is better be backed up on physical paper, storing it in cloud storage may compromise your validator later.**
Remember the address starting from `inj`, this is going to be your Injective Chain Validator Account address.
### Step 2: Obtain INJ
In order to proceed with the next step, you will have to obtain some INJ on Injective.
You can request funds from the [Testnet Faucet](https://faucet.injective.network/).
After a few minutes, you should be able to verify that your deposit was successful on the UI. Alternatively, you can query your account balance using the `injectived` CLI with the following command:
```bash theme={null}
injectived q bank balances
```
### Step 3: Create your validator account
Obtain your node's Tendermint validator Bech32 encoded PubKey consensus address.
```bash theme={null}
VALIDATOR_PUBKEY=$(injectived tendermint show-validator)
echo $VALIDATOR_PUBKEY
# Example: {"@type": "/cosmos.crypto.ed25519.PubKey", "key": "GWEJv/KSFhUUcKBWuf9TTT3Ful+3xV/1lFhchyW1TZ8="}
```
Then create your new validator initialized with a self-delegation with your INJ tokens. Most critically, you will need to decide on the values of your validator's staking parameters.
* `--moniker` - Your validator's name
* `--amount` - Your validator's initial amount of INJ to bond
* `--commission-max-change-rate` - Your validator's maximum commission change rate percentage (per day)
* `--commission-max-rate` - Your validator's maximum commission rate percentage
* `--commission-rate` - Your validator's initial commission rate percentage
* `--min-self-delegation` - Your validator's minimum required self delegation
Once you decide on your desired values, set them as follows.
```bash theme={null}
MONIKER=
AMOUNT=100000000000000000000inj # to delegate 100 INJ, as INJ is represented with 18 decimals.
COMMISSION_MAX_CHANGE_RATE=0.1 # e.g. for a 10% maximum change rate percentage per day
COMMISSION_MAX_RATE=0.1 # e.g. for a 10% maximum commission rate percentage
COMMISSION_RATE=0.1 # e.g. for a 10% initial commission rate percentage
MIN_SELF_DELEGATION_AMOUNT=50000000000000000000 # e.g. for a minimum 50 INJ self delegation required on the validator
```
Then run the following command to create your validator.
```bash theme={null}
injectived tx staking create-validator \
--moniker=$MONIKER \
--amount=$AMOUNT \
--gas-prices=500000000inj \
--pubkey=$VALIDATOR_PUBKEY \
--from=$VALIDATOR_KEY_NAME \
--keyring-backend=file \
--yes \
--node=tcp://localhost:26657 \
--chain-id=injective-888
--commission-max-change-rate=$COMMISSION_MAX_CHANGE_RATE \
--commission-max-rate=$COMMISSION_MAX_RATE \
--commission-rate=$COMMISSION_RATE \
--min-self-delegation=$MIN_SELF_DELEGATION_AMOUNT
```
Extra `create-validator` options to consider:
```
--identity= The optional identity signature (ex. UPort or Keybase)
--pubkey= The Bech32 encoded PubKey of the validator
--security-contact= The validator's (optional) security contact email
--website= The validator's (optional) website
```
You can check that your validator was successfully created by checking the
[staking dashboard](https://injhub.com/stake/),
and scrolling down to the "Validators" section.
It looks like this:
Alternatively, enter the following CLI command:
```bash theme={null}
injectived q staking validators
```
If you see your validator in the list of validators, then congratulations, you have officially joined as an Equinox Staking validator! 🎉
### Step 4: (Optional) Delegate Additional INJ to your Validator
To gain a deeper empirical understanding of the user experience that your future delegators will experience, you can complete the remaining steps in the [Staking Guide](https://medium.com/injective-labs/injective-hub-guide-9a14f09f6a7d).
These steps will allow you to experience the delegation flow using MetaMask Transactions. 🦊
Alternatively, you can always use the Injective CLI to send a delegation transaction.
```bash theme={null}
injectived tx staking delegate [validator-addr] [amount] --from $VALIDATOR_KEY_NAME --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657
```
### Next Steps
Next, proceed to set up your Ethereum Bridge Relayer. This is a necessary step in order to prevent your validator from being slashed. You should do this immediately after setting up your validator.
# Testnet Peggo
Source: https://docs.injective.network/infra/validator-testnet/peggo
## Equinox Testnet
## Step 1: Configure your Peggo relayer
```bash theme={null}
mkdir ~/.peggo
cp testnet-config/staking/40014/peggo-config.env ~/.peggo/.env
cd ~/.peggo
```
First, update the `PEGGO_ETH_RPC` in the `.env` file with a valid Sepolia EVM RPC Endpoint.
To set up your own Sepolia full node, follow the instructions [here](https://ethereum.org/en/developers/docs/nodes-and-clients/run-a-node/). It's possible to use an Alchemy or Infura RPC, but keep in mind that the Peggo bridge is still under development, and the request amount it makes to the RPC is not optimized. Ensure it does not incur high costs on your account.
Peggo also requires access to your validator's Cosmos and Ethereum credentials to sign transactions for the corresponding networks.
## **Cosmos Keys**
There are two ways to provide the credential access - a keyring with encrypted keys, or just private key in plaintext.
### **1. Cosmos Keyring**
Update the `PEGGO_COSMOS_FROM` to your validator key name (or account address) and `PEGGO_COSMOS_FROM_PASSPHRASE` to your Cosmos Keyring passphrase. Please note that the default keyring backend is `file` and it will try to locate keys on disk.
Keyring path must be pointing to homedir of your injectived node, if you want reuse the keys from there.
Learn more about Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html).
### **2. Cosmos Private Key (Unsafe)**
Simply update the `PEGGO_COSMOS_PK` with your Validator's Account private key.
To obtain your validator's Cosmos private key, run `injectived keys unsafe-export-eth-key $VALIDATOR_KEY_NAME`.
This method is insecure and is not recommended.
## **Ethereum Keys**
There are two ways to provide the credential access - a Geth keystore with encrypted keys, or just private key in plaintext.
### **1. Geth Keystore**
Simply create a new private key store and update the following env variables:
* `PEGGO_ETH_KEYSTORE_DIR`
* `PEGGO_ETH_FROM`
* `PEGGO_ETH_PASSPHRASE`
You can find instructions for securely creating a new Ethereum account using a keystore in the Geth Documentation [here](https://geth.ethereum.org/docs/interface/managing-your-accounts).
Example is provided below.
```bash theme={null}
geth account new --datadir=/home/ec2-user/.peggo/data/
INFO [03-23|18:18:36.407] Maximum peer count ETH=50 LES=0 total=50
Your new account is locked with a password. Please give a password. Do not forget this password.
Password:
Repeat password:
Your new key was generated
Public address of the key: 0x9782dc957DaE6aDc394294954B27e2118D05176C
Path of the secret key file: /home/ec2-user/.peggo/data/keystore/UTC--2021-03-23T15-18-44.284118000Z--9782dc957dae6adc394294954b27e2118d05176c
- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
```
Now, you can set env variables like this:
```ini theme={null}
PEGGO_ETH_KEYSTORE_DIR=/home/ec2-user/.peggo/data/keystore
PEGGO_ETH_FROM=0x9782dc957DaE6aDc394294954B27e2118D05176C
PEGGO_ETH_PASSPHRASE=12345678
```
Next, ensure that your Ethereum addresss has Sepolia ETH. You can request Sepolia ETH from the public faucet [here](https://www.alchemy.com/faucets/ethereum-sepolia).
### **2. Ethereum Private Key (Unsafe)**
Simply update the `PEGGO_ETH_PK` with a new Ethereum Private Key from a new account.
Next, ensure that your Ethereum addresss has Sepolia ETH. You can request Sepolia ETH from the public faucet [here](https://www.alchemy.com/faucets/ethereum-sepolia).
### Step 2: Register Your Orchestrator and Ethereum Address
You can register orchestrator and ethereum address only once. It **CANNOT** be updated later. So Check twice before running below command.
```bash theme={null}
injectived tx peggy set-orchestrator-address $VALIDATOR_INJ_ADDRESS $ORCHESTRATOR_INJ_ADDRESS $ETHEREUM_ADDRESS --from $VALIDATOR_KEY_NAME --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=160000000inj
```
* To obtain your validator's inj address, run, `injectived keys list $VALIDATOR_KEY_NAME`
* To obtain your orchestrators's inj address, `injectived keys list $ORCHESTRATOR_KEY_NAME`
Example:
```bash theme={null}
injectived tx peggy set-orchestrator-address inj10m247khat0esnl0x66vu9mhlanfftnvww67j9n inj1x7kvxlz2epqx3hpq6v8j8w859t29pgca4z92l2 0xf79D16a79130a07e77eE36e8067AeA783aBdA3b6 --from validator-key-name --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=160000000inj
```
You can verify successful registration by checking for your Validator's mapped Ethereum address on [https://testnet.sentry.lcd.injective.network/peggy/v1/valset/current](https://testnet.sentry.lcd.injective.network/peggy/v1/valset/current).
**NOTE:** Once you've registered your Orchestrator with the `set-orchestrator-address` message, you **CANNOT** register again. Once this step is complete, your `Validator` is bound to the provided Ethereum address (as well the Delegated address you may have provided). In other words, your peggo must always run with the addresses you provided for registration.
## Step 3: Start the Relayer
```bash theme={null}
peggo orchestrator
```
This starts the Peggo bridge (relayer / orchestrator).
## Step 4: Create a Peggo systemd service
Add `peggo.service` file with below content under `/etc/systemd/system/peggo.service`
```ini theme={null}
[Unit]
Description=peggo
[Service]
WorkingDirectory=/home/ec2-user/.peggo
ExecStart=/bin/bash -c 'peggo orchestrator '
Type=simple
Restart=always
RestartSec=1
User=ec2-user
[Install]
WantedBy=multi-user.target
```
Then, run the following commands to configure Environment variables, start and stop the peggo relayer.
```bash theme={null}
sudo systemctl start peggo
sudo systemctl stop peggo
sudo systemctl restart peggo
sudo systemctl status peggo
# enable start on system boot
sudo systemctl enable peggo
# To check Logs
journalctl -f -u peggo
```
## Step 5: (Optional) Protect Cosmos Keyring from unauthorized access
This is an advanced DevOps topic, consult with your sysadmin.
Learn more about Cosmos Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html). Once you've launched your node, the default keyring will have the validator operator key stored on disk in the encrypted form. Usually, the keyring is located within node's homedir, i.e. `~/.injectived/keyring-file`.
Some sections of the Injective Staking documentation will guide you through using this key for governance purposes, i.e., submitting transactions and setting up an Ethereum bridge. In order to protect the keys from unauthorized access, even when the keyring passphrase is leaked via configs, you can set OS permissions to allow disk access to `injectived` / `peggo` processes only.
In Linux systems like Debian, Ubuntu and RHEL, this can be achieved using POSIX Access Control Lists (ACLs). Before beginning to work with ACLs, the file system must be mounted with ACLs turned on. There are some official guides for each distro:
* [Ubuntu](https://help.ubuntu.com/community/FilePermissionsACLs)
* [Debian](https://wiki.debian.org/Permissions)
* [Amazon Linux (RHEL)](https://www.redhat.com/sysadmin/linux-access-control-lists)
## Testnet
## Step 1: Configure your Peggo relayer
```bash theme={null}
mkdir ~/.peggo
cp testnet-config/40014/peggo-config.env ~/.peggo/.env
cd ~/.peggo
```
First, update the `PEGGO_ETH_RPC` in the `.env` file with a valid Ethereum EVM RPC Endpoint.
To create your own Ethereum full node, you can follow our instructions [here](https://ethereum.org/en/developers/docs/nodes-and-clients/run-a-node/). It's possible to use an external Ethereum RPC provider such as Alchemy or Infura, but keep in mind that the Peggo bridge relayer uses heavy use of `eth_getLogs` calls which may increase your cost burden depending on your provider.
Peggo also requires access to your validator's delegated Injective Chain account and Ethereum key credentials to sign transactions for the corresponding networks.
### **Creating your delegated Cosmos Key for sending Injective transactions**
Your peggo relayer can either
* Use an explicitly delegated account key specific for sending validator specific Peggy transactions (i.e. `ValsetConfirm`, `BatchConfirm`, and `SendToCosmos` transactions), or
* Simply use your validator's account key.
For isolation purposes, we recommend creating a delegated Cosmos key to send Injective transactions instead of using your validator account key.
To create a new key, run:
```bash theme={null}
injectived keys add $ORCHESTRATOR_KEY_NAME
```
Then, ensure that your orchestrator inj address has INJ balance.
To obtain your orchestrators's inj address, run:
```bash theme={null}
injectived keys list $ORCHESTRATOR_KEY_NAME
```
You can transfer INJ from your validator account to orchestrator address using this command:
```bash theme={null}
injectived tx bank send $VALIDATOR_KEY_NAME $ORCHESTRATOR_INJ_ADDRESS --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
Example:
```bash theme={null}
injectived tx bank send genesis inj1u3eyz8nkvym0p42h79aqgf37gckf7szreacy9e 20000000000000000000inj --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
You can then verify that your orchestrator account has INJ balances by running:
```bash theme={null}
injectived q bank balances $ORCHESTRATOR_INJ_ADDRESS
```
### **Managing Cosmos account keys for `peggo`**
Peggo supports two options to provide Cosmos signing key credentials - using the Cosmos keyring (recommended), or by providing a plaintext private key.
#### **Option 1. Cosmos Keyring**
In the `.env` file, first specify the `PEGGO_COSMOS_FROM` and `PEGGO_COSMOS_FROM_PASSPHRASE` corresponding to your peggo account signing key.
If you are using a delegated account key configuration as recommended above, this will be your `$ORCHESTRATOR_KEY_NAME` and passphrase, respectively. Otherwise, this should be your `$VALIDATOR_KEY_NAME` and associated validator passphrase.
Please note that the default keyring backend is `file` and that, as such, peggo will try to locate keys on disk by default.
To use the default injectived key configuration, you should set the keyring path to the home directory of your injectived node, e.g. `~/.injectived`.
You can also read more about the Cosmos Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html).
#### **Option 2. Cosmos Private Key (Unsafe)**
In the `.env` file, specify the `PEGGO_COSMOS_PK` corresponding to your peggo account signing key.
If you are using a delegated account key configuration as recommended above, this will be your orchestrator account's private key. Otherwise, this should be your validator's account private key.
To obtain your orchestrator's Cosmos private key (if applicable), run:
```bash theme={null}
injectived keys unsafe-export-eth-key $ORCHESTRATOR_KEY_NAME
```
To obtain your validator's Cosmos private key (if applicable), run:
```bash theme={null}
injectived keys unsafe-export-eth-key $VALIDATOR_KEY_NAME
```
Again, this method is less secure and is not recommended.
### **Managing Ethereum keys for `peggo`**
Peggo supports two options to provide signing key credentials - using the Geth keystore (recommended), or by providing a plaintext Ethereum private key.
#### **Option 1. Geth Keystore**
Simply create a new private key store and update the following env variables:
* `PEGGO_ETH_KEYSTORE_DIR`
* `PEGGO_ETH_FROM`
* `PEGGO_ETH_PASSPHRASE`
You can find instructions for securely creating a new Ethereum account using a keystore in the Geth Documentation [here](https://geth.ethereum.org/docs/interface/managing-your-accounts).
For convience, an example is provided below.
```bash theme={null}
geth account new --datadir=/home/ec2-user/.peggo/data/
INFO [03-23|18:18:36.407] Maximum peer count ETH=50 LES=0 total=50
Your new account is locked with a password. Please give a password. Do not forget this password.
Password:
Repeat password:
Your new key was generated
Public address of the key: 0x9782dc957DaE6aDc394294954B27e2118D05176C
Path of the secret key file: /home/ec2-user/.peggo/data/keystore/UTC--2021-03-23T15-18-44.284118000Z--9782dc957dae6adc394294954b27e2118d05176c
- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
```
Make sure you heed the warnings that geth provides, particularly in backing up your key file so that you don't lose your keys by mistake. We also recommend not using any quote or backtick characters in your passphrase for peggo compatibility purposes.
You should now set the following env variables:
```bash theme={null}
# example values, replace with your own
PEGGO_ETH_KEYSTORE_DIR=/home/ec2-user/.peggo/data/keystore
PEGGO_ETH_FROM=0x9782dc957DaE6aDc394294954B27e2118D05176C
PEGGO_ETH_PASSPHRASE=12345678
```
Then, ensure that your Ethereum address has enough ETH.
#### **Option 2. Ethereum Private Key (Unsafe)**
Simply update the `PEGGO_ETH_PK` with a new Ethereum Private Key from a new account.
Then, ensure that your Ethereum address has ETH.
## Step 2: Register Your Orchestrator and Ethereum Address
You can register orchestrator and ethereum address only once. It **CANNOT** be updated later. So Check twice before running below command.
```bash theme={null}
injectived tx peggy set-orchestrator-address $VALIDATOR_INJ_ADDRESS $ORCHESTRATOR_INJ_ADDRESS $ETHEREUM_ADDRESS --from $VALIDATOR_KEY_NAME --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
* To obtain your validator's inj address, run, `injectived keys list $VALIDATOR_KEY_NAME`
* To obtain your orchestrators's inj address, `injectived keys list $ORCHESTRATOR_KEY_NAME`
Example:
```bash theme={null}
injectived tx peggy set-orchestrator-address inj10m247khat0esnl0x66vu9mhlanfftnvww67j9n inj1x7kvxlz2epqx3hpq6v8j8w859t29pgca4z92l2 0xf79D16a79130a07e77eE36e8067AeA783aBdA3b6 --from validator-key-name --chain-id=injective-888 --keyring-backend=file --yes --node=tcp://localhost:26657 --gas-prices=500000000inj
```
You can verify successful registration by checking for your Validator's mapped Ethereum address on [https://testnet.lcd.injective.dev/peggy/v1/valset/current](https://testnet.lcd.injective.dev/peggy/v1/valset/current).
## Step 3: Start the Relayer
```bash theme={null}
cd ~/.peggo
peggo orchestrator
```
This starts the Peggo bridge (relayer / orchestrator).
## Step 4: Create a Peggo systemd service
Add `peggo.service` file with below content under `/etc/systemd/system/peggo.service`
```ini theme={null}
[Unit]
Description=peggo
[Service]
WorkingDirectory=/home/ec2-user/.peggo
ExecStart=/bin/bash -c 'peggo orchestrator '
Type=simple
Restart=always
RestartSec=1
User=ec2-user
[Install]
WantedBy=multi-user.target
```
Then, run the following commands to configure Environment variables, start and stop the peggo relayer:
```bash theme={null}
sudo systemctl start peggo
sudo systemctl stop peggo
sudo systemctl restart peggo
sudo systemctl status peggo
# enable start on system boot
sudo systemctl enable peggo
# To check Logs
journalctl -f -u peggo
```
## Step 5: (Optional) Protect Cosmos Keyring from unauthorized access
This is an advanced DevOps topic, consult with your sysadmin.
Learn more about Cosmos Keyring setup [here](https://docs.cosmos.network/v0.46/run-node/keyring.html). Once you've launched your node, the default keyring will have the validator operator key stored on disk in the encrypted form. Usually, the keyring is located within node's homedir, i.e. `~/.injectived/keyring-file`.
Some sections of the Injective Staking documentation will guide you through using this key for governance purposes, i.e., submitting transactions and setting up an Ethereum bridge. In order to protect the keys from unauthorized access, even when the keyring passphrase is leaked via configs, you can set OS permissions to allow disk access to `injectived` / `peggo` processes only.
In Linux systems like Debian, Ubuntu and RHEL, this can be achieved using POSIX Access Control Lists (ACLs). Before beginning to work with ACLs, the file system must be mounted with ACLs turned on. There are some official guides for each distro:
* [Ubuntu](https://help.ubuntu.com/community/FilePermissionsACLs)
* [Debian](https://wiki.debian.org/Permissions)
* [Amazon Linux (RHEL)](https://www.redhat.com/sysadmin/linux-access-control-lists)
## Contribute
If you'd like to inspect the Peggo orchestrator source code and contribute, you can do so at [https://github.com/InjectiveLabs/peggo](https://github.com/InjectiveLabs/peggo)
# Indexer API Reference
Source: https://docs.injective.network/redirects/https_api_injective_network_swagger
# Using IBC
Source: https://docs.injective.network/redirects/https_injective_com_blog_how_to_bridge_from_cosmos_to_injective_using_keplr
# From Ethereum
Source: https://docs.injective.network/redirects/https_injective_com_blog_how_to_bridge_from_ethereum_to_injective_using_metamask
# From Solana
Source: https://docs.injective.network/redirects/https_injective_com_blog_how_to_bridge_from_solana_to_injective_using_phantom
# Using Wormhole
Source: https://docs.injective.network/redirects/https_injective_com_blog_how_to_bridge_to_injective_using_wormhole
# Indexer Service Setup Guide
Source: https://docs.injective.network/redirects/https_injective_notion_site_indexer_service_setup_guide
# Overview
Source: https://docs.injective.network/build-index
Build on Injective using EVM, Wasm VM, native modules, or AI tooling
Injective supports multiple execution environments and developer paths. Choose your starting point below.
Deploy Solidity smart contracts, use Hardhat or Foundry, and integrate with MetaMask and existing EVM tooling
Build with the native Go execution layer, custom modules, transactions, and direct chain queries
Write smart contracts in Rust, deploy to the Wasm VM, and interact with Injective's native modules
Build frontend applications with Nuxt or React using Injective's TypeScript SDK
Build AI-powered applications with iAgent, MCP integration, and on-chain agent tooling
Network information, address conversion, injectived CLI, gRPC, and SDK utilities
# About These Docs
Source: https://docs.injective.network/contributing
The Injective developer documentation is written and maintained by Injective Labs' Developer Relations team.
[Injective Labs](https://injective.com) is the core team behind the Injective blockchain.
## Contributing
The documentation is open source.
If you find an error or want to improve a page, open a pull request at
[`github.com/InjectiveLabs/injective-docs`](https://github.com/InjectiveLabs/injective-docs).
Community contributions are encouraged!
## Last update
Last major update was made on March 2026.
# Eligibility
Source: https://docs.injective.network/defi/open-liquidity-program/eligibility
How to Qualify and Remain Qualified for OLP Rewards
## Clean Slate Qualification
An Injective address can qualify for OLP by meeting the following criteria:
* The **address must be opted out of the Trade & Earn (T\&E) program** prior to the start of the qualification process. The address will not earn T\&E rewards during the qualification process. See examples in [Python](https://github.com/InjectiveLabs/sdk-python/blob/master/examples/chain_client/24_MsgRewardsOptOut.py), [Go](https://github.com/InjectiveLabs/sdk-go/blob/master/examples/chain/24_MsgRegisterAsDMM/example.go), and [TS](https://github.com/InjectiveLabs/injective-ts/wiki/04CoreModulesExchange#msgrewardsoptout) for opting out programatically.
* Note: Eligibility for the qualification process begins at 00:00 UTC the day after the opt out is complete. To check if an address has been successfully opted out of T\&E, [this list can be cross referenced](https://lcd.injective.network/injective/exchange/v1beta1/opted_out_of_rewards_accounts).
* The address's maker volume must account for **at least 0.25% of the total daily exchange maker volume of** [**eligible markets**](./eligible-markets/) **for 3 days in a row** in the same epoch. Self trading is strictly prohibited.
Assuming both of these requirements have been met, the address will qualify for OLP rewards on the 4th day at 00:00 UTC. Once qualified, an address will remain eligible for rewards through the rest of the epoch unless special circumstances (e.g. abusing the system, wash trading, etc.) compel the removal of the address. Note that activity prior to qualification will not count towards rewards.
It may be prudent to consolidate trading strategies into a single address to increase maker volume. Otherwise, addresses with less maker volume than the required threshold will not qualify for rewards even if volume on an aggregate level between multiple addresses exceeds the threshold.
See [`documentation on the Injective authz module`](https://docs.injective.network/develop/modules/Core/authz/) for a method of executing multiple strategies from a single address while retaining trading [fee discounts](https://helixapp.com/fee-discounts).
## Maintaining Next Epoch Eligibility/Pre-Qualification
To automatically qualify for the next epoch after qualifying for the current epoch, an **address must account for at least 0.25% of total exchange maker volume** of [eligible markets](./eligible-markets/) from the date of qualification to the last day of the epoch.
* Example: Address `inj1a` enters epoch 21 ineligible for OLP rewards. `inj1a` accounts for 1%, 0.1%, and 0.2% of total daily exchange maker volume of [eligible markets](./eligible-markets/) on days 1, 2, and 3 of epoch 21, respectively. On days 4, 5, and 6, `inj1a` accounts for 0.5% of the applicable volume each day. `inj1a` qualifies on day 7 of the epoch. To maintain eligibility/qualification for epoch 22, `inj1a` must account for at least 0.25% of the cumulative applicable maker volume from day 7 through day 28 of epoch 21. If the cumulative maker volume of [eligible markets](./eligible-markets/) for this period (days 7 through 28) was \$100M, then `inj1a` must account for \$250,000 of cumulative maker volume in those markets within the same period.
If the address was eligible for the entire epoch through a previous epoch's pre-qualification, that address must account for at least 0.25% of the maker volume of [eligible markets](./eligible-markets/) in the entire epoch.
* Example: Address `inj1a` enters epoch 22 prequalified from maintaining eligibility in epoch 21. Suppose the cumulative maker volume of [eligible markets](./eligible-markets.md) in epoch 22 totals \$200M. Then `inj1a` must contribute at least \$500,000 of the \$200M in [eligible markets](./eligible-markets.md) by the end of epoch 22 to maintain automatic eligibility for epoch 23.
## Disqualification
**Any address that fails to account for at least 0.25% of applicable maker volume in an epoch will be disqualified from OLP at the start of the next epoch**. If the address wishes to rejoin the program, the address must go through the [clean slate qualification process](./eligibility/#clean-slate-qualification) again (though the address does not have to opt out of T\&E another time). Note that any liquidity contributed on days that the address is not eligible will not be rewarded retroactively once the address requalifies.
Disqualification occurs at the end of each epoch, meaning addresses continue to accrue rewards within the epoch regardless of next epoch eligibility.
## Tracking Eligibility
Use the [OLP dashboard](https://trading.injective.network/program/liquidity/eligibility).
# Epochs
Source: https://docs.injective.network/defi/open-liquidity-program/epochs
OLP Epoch Duration and Schedule
An epoch is the period of time in which market makers are evaluated based on their relative performance. Several metrics are cumulatively tracked during each epoch and reset upon the commencement of a new epoch. Rewards are disbursed after the completion of each epoch (see [Rewards Disbursement](/defi/open-liquidity-program/reward-disbursements)). Currently, **each epoch is 28 days long**.
The **first public OLP epoch (epoch 21) began on June 13, 2023 at 00:00 UTC**.
Start and end dates for OLP epochs are shown in the table below. This table is updated occasionally (typically a few months in advance) to include future epochs.
Epoch
Start Date (UTC)
End Date (UTC)
Rewards (INJ)
56
Feb 17, 2026 00:00:00
Mar 17, 2026 00:00:00
30,000
57
Mar 17, 2026 00:00:00
Apr 14, 2026 00:00:00
30,000
58
Apr 14, 2026 00:00:00
May 12, 2026 00:00:00
30,000
59
May 12, 2026 00:00:00
Jun 9, 2026 00:00:00
30,000
60
Jun 9, 2026 00:00:00
Jul 7, 2026 00:00:00
30,000
61
Jul 7, 2026 00:00:00
Aug 4, 2026 00:00:00
25,000
62
Aug 4, 2026 00:00:00
Sep 1, 2026 00:00:00
20,000
63
Sep 1, 2026 00:00:00
Sep 29, 2026 00:00:00
20,000
64
Sep 29, 2026 00:00:00
Oct 27, 2026 00:00:00
15,000
# Fee Tiers
Source: https://docs.injective.network/defi/open-liquidity-program/fee-tiers
Traders who meet threshold criteria for minimum staked INJ and minimum trading volume on a 28-day rolling basis are eligible for [discounts on Injective trading fees](https://www.helixapp.com/fee-discounts).
# Flexible Reward Allocations
Source: https://docs.injective.network/defi/open-liquidity-program/flexible-reward-allocations
OLP Reward Allocations to Markets and Institutional Liquidity Providers
Part of the changes implemented for epoch 43 include a fully discretionary dynamic reward to bootstrap liquidity for certain markets at specific times. Following the exact same calculations detailed in [reward-allocations](./reward-allocations/), and the classic formula/scoring methodology to determine a liquidity provider's share of the total reward for a market, this will be accomplished through the concept of Mini Epochs, which incur a "boosted reward" that comes out of the total reward for the main "parent" epoch.
Mini Epochs are flexible in that they are not constrained by the starting/ending point for a pair to exist in a Primary Epoch. Mini Epochs can begin or end on any date (at 00:00 UTC) within a Primary Epoch, though they cannot span multiple epochs.
Rewards for Mini Epochs will be clearly defined. All eligible OLP participants can accrue pair-specific rewards for Mini Epochs, even if they are not otherwise earning rewards for that pair. Of course, a liquidity provider's participation in the Mini Epoch implies participation in the Primary Epoch, even if just opportunistically for that specific time period. Rewards for Mini Epochs are visible on the OLP dashboard side-by-side with the overall rewards for the Primary Epoch.
For example, if there is a 500 INJ Mini Epoch for a pair, providing liquidity for that pair during the Mini Epoch entitles you to the normal allocation for that pair plus a pro-rata allocation of the 500 INJ Mini Epoch, which comes out of the parent epoch's rewards. In this example, if a parent epoch has a reward of 30,000 INJ, this means that 29,500 INJ will be distributed based on scores for the parent epoch, and 500 INJ will be distributed based on scores for the Mini Epoch.
# Formula Parameters
Source: https://docs.injective.network/defi/open-liquidity-program/formula-parameters
Values for OLP Formula Parameters
# Introduction
Source: https://docs.injective.network/defi/open-liquidity-program/introduction
Injective's market-maker incentive program, the Open Liquidity Program (OLP): How epochs, scoring, fee tiers, and reward disbursements work.
Injective uniquely provides the world's first truly onchain orderbook environment to bring shared liquidity across all DeFi applications. By relentlessly pursuing deeper liquidity, Injective empowers users and protocols with seamless access to capital-efficient markets not available elsewhere.
Driven by an unwavering commitment to innovation, Injective has created the [**Open Liquidity Program (OLP)**](https://trading.injective.network/program/liquidity) which caters to institutions and everyday traders alike. This milestone signifies a new era in which market makers and participants can earn rewards for providing orderbook liquidity with minimal barriers to entry. This guide offers a comprehensive walkthrough of the Open Liquidity Program and provides details on how you can become an OLP member.
Rewards for OLP are disbursed in the form of INJ, Injective's native utility and governance token. Current and upcoming epoch reward amounts are listed in [Epochs](./epochs).
These amounts may be reassessed in advance for future epochs, or retroactively reduced for a current epoch as a result of one or more volatility response modifications (VRMs). For more information on OLP epochs, see [Epochs](./epochs).
A list of trading pairs eligible for rewards can be found on the [OLP Dashboard](https://trading.injective.network/program/liquidity).
All rewards are subject to governance approval by the Injective community.
More information on OLP rewards can be found on the [OLP Dashboard](https://trading.injective.network/program/liquidity) under the [Injective Trading Portal](https://trading.injective.network/).
# Performance Tracking
Source: https://docs.injective.network/defi/open-liquidity-program/performance-tracking
OLP Dashboard
Current and historical information on market allocations, expected/earned rewards, scores per market, and eligibility can be found on the [OLP Dashboard](https://trading.injective.network/program/liquidity) in the [Injective Trading Portal](https://trading.injective.network/).
Snapshot data can be found under the [Scores tab](https://trading.injective.network/program/liquidity/scores). CSV files can also be downloaded in the [Scores tab](https://trading.injective.network/program/liquidity/scores) to view scores for all addresses and all markets at the same time—this information may be helpful for market participants that wish to view data on a broad level.
OLP data for current and previous epochs can also be queried programmatically:
> Epochs and Markets:
```
curl -s -X POST -H "Content-Type: application/json" -d '{}' https://glp.rest.injective.network/api/olp/v1/epochs
```
> Rewards per Address:
```
curl -s -X POST -H "Content-Type: application/json" -d '{"epoch_id":"epoch_231128_231225"}' https://glp.rest.injective.network/api/olp/v1/epoch-scores
```
> Rewards in a Market:
```
curl -X POST -H "Content-Type: application/json" -d '{"epoch_id": "epoch_240123_240219", "market_id":"0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce", "page": {"per_page": 200}}' https://glp.rest.injective.network/api/olp/v1/total-scores
```
> Snapshots for Address:
```
curl -X POST -H "Content-Type: application/json" -d '{"epoch_id": "epoch_240123_240219", "account_address": ""}' https://glp.rest.injective.network/api/olp/v1/epoch-scores/history
```
# Reward Allocations
Source: https://docs.injective.network/defi/open-liquidity-program/reward-allocations
OLP Reward Allocations
## Market Reward Allocations
Rewards are allocated to [eligible markets](/defi/open-liquidity-program/eligible-markets) in three different methods :
1. Static allocations
2. Minimum allocation with a dynamic component
3. Flexible reward allocations
### Static Market Reward Allocations (Preallocations)
13.33% of INJ rewards will be preallocated to each of the BTC/USDC PERP market, ETH/USDC PERP market, and INJ/USDC PERP market, and 5% will be preallocated to the INJ/USDC spot market. The remaining INJ for each epoch will be allocated to each remaining eligible market with a minimum allocation of 100 INJ.
Static allocations may change over time as more markets are added to the eligible list
### Dynamic Market Reward Allocations
The remaining rewards are allocated to eligible markets (excluding BTC/ETH/INJ Perps) based on the following schematic.
First, each epoch starts fresh, such that every pair has an equal chance of earning the maximum total available reward for that epoch, regardless of trading volume and liquidity from the prior epoch. Each pair starts day 1 of the epoch with a range of possibility, from a minimum of 100 INJ for the epoch.
Prior to this change, minimum rewards were 400 INJ, maximum rewards were around 900 INJ, and there was insufficient variation in reward accrual between pairs with low volume and pairs with substantially more volume. With this change, liquidity providers are rewarded for volume in popular markets.
The range for each market's rewards will progress throughout the epoch, converging on the final day of the epoch to the true reward for that market. The range $[Rewards_{min};Rewards_{max}]$ will be defined per market as follows :
$$
MinVolume=Min(Market\ traded\ volume\ since\ beginning\ of\ epoch) \\
MaxVolume=Max(Market\ traded\ volume\ since\ beginning\ of\ epoch)
$$
where
$$
Rewards_{min_{market\ i}}=100+\frac{Volume_{market_{i}}-MinVolume}{MaxVolume-MinVolume}(Rewards_{max}-100)
$$
and $Rewards_{max}$ is still calculated as on the bottom of this page. Ergo, the highest traded volume market will receive $Rewards_{max}$ and the lowest traded market by volume will have a minimum reward of 100 INJ.
It must be noted that $Rewards_{min}$ is just the floor for the rewards range, it will never be equivalent to the reward except in the case of the highest traded volume market where range will be trivial $[Rewards_{max};Rewards_{max}]$, in which case rewards will be equal to $Rewards_{max}$. This is a linear function that goes from 100 INJ to $Rewards_{max}$.
With this range defined, the steps to calculate the reward for a market are :
1. Start with $Rewards_{Market_{i}}=Rewards_{min_{market\ i}}$
2. Distribute the remaining rewards, *RR*, with $RR=TAR-\sum_{i}Rewards_{min_{market\ i}}$using the formulas above.
3. For any calculated rewards that exceed $Rewards_{max}$, redistribute this across all markets again following the formula above.
4. Iterate until there are no remaining rewards.
**Markets Added Partway Through an Epoch**
For markets added to the eligible list midway through an epoch, the preallocation will be prorated. For example, if ARB/USDT is added on the 15th day of the epoch, then the market will receive half of the rewards for the epoch (as there are 14 full days remaining out of 28).
### Market Allocation Cap
For each market that has dynamic reward allocations, a hard cap will be applied according to the following formula, where $n$ is the number of eligible markets excluding BTC, ETH, and INJ perps:
$$
Rewards_{max} = TAR\ *\ \frac{1 - TPR}{n}*2
$$
where *TPR* is equal to the percentage (expressed as a decimal) of total preallocated rewards (currently 0.375) and *n* is the number of non-preallocated pairs.
Any reward allocations that exceed the cap will be redistributed amongst the other eligible markets according to the [dynamic allocation formula](/defi/open-liquidity-program/reward-allocations#dynamic-market-reward-allocations).
## Reward Allocations
Rewards to individual institutional liquidity providers will be allocated based on the following equation:
$$
Rewards_{MM_i} = \sum_{Market}\left(Rewards_{Market} * \frac {TS_{MM_i, \ Market}} {\sum_{MM} TS_{MM,\ Market}} \right)
$$
**Each institutional liquidity provider will receive rewards based on their proportional [Total Score](/defi/open-liquidity-program/scoring#total-score) within the market, subject to governance approval.**
Rewards for addresses totaling \< 1 INJ at the end of each epoch will be disregarded to reduce the overhead of the disbursement process.
# Reward Disbursements
Source: https://docs.injective.network/defi/open-liquidity-program/reward-disbursements
How OLP Rewards Are Disbursed
OLP Rewards are sent directly to the participants' addresses after the end of each epoch, pending a governance proposal that must be voted on by the community over a four-day period. **Half of the earned INJ rewards are paid out in this immediate proposal, while the other half is subject to a three epoch vesting period**. If an eligible address becomes inactive (which is defined by either not meeting the daily volume criteria at the start of the epoch, *or* earning a reward for a given epoch of less than 60% of the prior epoch's reward) for any epoch during the vesting period, the vested rewards will be subject to forfeiture.
All rewards are subject to governance approval, and all addresses eligible for more than 500 INJ per epoch are subject to KYC/KYB verification before their reward disbursement is included in a governance proposal submitted by Injective. While any address can permissionlessly join OLP, reward eligibility is dependent on KYC/KYB.
Disbursements usually occur a few days after the conclusion of each epoch due to the governance process required.
Reward disbursements can be tracked on the Community Spend Pool page on the [Injective Explorer](https://explorer.injective.network/).
# Scoring Formula/Methodology
Source: https://docs.injective.network/defi/open-liquidity-program/scoring
Scoring a Market Maker's Epoch Performance in a Single Market
To promote deep, lasting liquidity across the Injective on-chain orderbook, the following metrics are prioritized by OLP:
* **Dual-sided liquidity** (both bid and ask liquidity)
* **Liquidity depth**
* **Bid-ask spread**
* **Market maker uptime**
* **Volume** (maker and taker)
* **Participation in multiple markets**
## Total Score
For any given market, a liquidity provider's $TS$ (Total Score) in an epoch is calculated as:
$$
TS_{Market} = (LS_{Epoch})^a \cdot (Uptime_{Epoch})^b \cdot (Volume_{epoch})^c
$$
where $LS_{epoch}$ is the liquidity provider's [Liquidity Score](/defi/open-liquidity-program/scoring#liquidity-score) in the market in the epoch, $Uptime_{Epoch}$is the liquidity provider's [Uptime score](/defi/open-liquidity-program/scoring#uptime-score) in the market in the epoch, and $Volume_{epoch}$ is the liquidity provider's total volume (maker and taker) in the market in the epoch.
$a$, $b$, and $c$ are exponent [parameters](/defi/open-liquidity-program/formula-parameters) that weight the different components of the formula.
## Liquidity Score
$$
LS_{Epoch} = \sum \limits_{N=1}^{40,320} \min(LS_{N_{Bid}}, LS_{N_{Ask}})
$$
The liquidity provider’s Liquidity Score for a market in an epoch, $LS_{Epoch}$, is the sum of the minimum between the Bid and Ask Liquidity Scores (see below) across all order book snapshots in the epoch for the relevant market, multiplied by a bespoke volatility parameter for each market (represented by Θ). This promotes dual-sided liquidity since single-sided liquidity will earn a Liquidity Score of 0 under the $\min()$ function.
A snapshot of the order book is taken randomly every 10-100 blocks. This is approximately every minute on average, which means there are approximately 40,320 snapshots in an epoch $(60 \cdot 24 \cdot 28 = 40,320).$ In practice, the upper bound of the summation will vary depending on the actual number of snapshots in the epoch. For the purposes of this guide, we will assume that there were exactly 40,320 snapshots in the epoch.
$$
LS_{N_{Bid}} = \frac{BidDepth_1}{Spread_1} \cdot \Theta_{vol} + \frac{BidDepth_2}{Spread_2} \cdot \Theta_{vol} + \ldots \\
\forall \ BidDepth_i \geq MinDepth \text{ and } Spread_i \leq MaxSpread
$$
$$
LS_{N_{Ask}} = \frac{AskDepth_1}{Spread_1} \cdot \Theta_{vol} + \frac{AskDepth_2}{Spread_2} \cdot \Theta_{vol} + \ldots \\
\forall \ AskDepth_i \geq MinDepth \text{ and } Spread_i \leq MaxSpread
$$
$LS_{N_{Bid}}$ is the sum of all bid order depths divided by the spread of the order, multiplied by the volatility parameter for that snapshot, for all limit orders placed by the liquidity provider in snapshot $N$ that exceed $MinDepth$ in size and are within the $MaxSpread.$
$LS_{N_{Ask}}$ follows the same logic as $LS_{N_{Bid}}$, but on the ask side of the order book.
The volatility parameter is calculated as follows:
$$
\Theta_{\text{vol}}(S_b)\;=\;
\min\!\bigl(\,\Theta_{\max},\;
\max\!\{\,1,\;
e^{\alpha\,\sigma_b\,|\frac{S_b-\mu_b}{S_b}|}\}\bigr)
$$
where $\mu_b$ is the oracle price moving average over $N$ blocks (1000 blocks, or roughly 10 minutes), $S_b$ represents the oracle price of the current block, and $\sigma_b$ represents the realized volatility over $N$ blocks. This function has a clamp and scales well if the current oracle price deviates from the moving average, or if there is a spike in volatility over the last $N$ blocks. The range of $\Theta_{\text{vol}} \in [1, \Theta_{\text{max}}]$ - so we bound it within a finite field. We introduce two new parameters $(\alpha, \Theta_{\text{max}})$ which monitor the sensitivity to volatility and a clamp. Because $\Theta_{\text{max}}$ should trend towards the cap of 10 as described above for a 3% price move within a 10-minute span, $\alpha$ is currently set at 2,500. A higher value $\alpha$ means $\Theta$ trends towards $\Theta_{\text{max}}$ faster, but as $\Theta_{\text{max}}$ is currently set to 10 per market (and can be modified on a per-market basis), a higher $\alpha$ would not bypass that maximum.
$Spread$ is calculated from the mid-price (distance from mid-price divided by mid-price).
For the current values of $MinDepth$ and $MaxSpread$, see the [Formula Parameters page](/defi/open-liquidity-program/formula-parameters).
## Uptime Score
$$
Uptime_{Epoch} = \sum \limits_{N=1}^{40,320} \begin{cases}1&\text{if } \min(LS_{N_{Bid}}, LS_{N_{Ask}}) > 0\\ 0&\text{otherwise}\end{cases}
$$
$Uptime_{Epoch}$ is the number of order book snapshots throughout the epoch in which the liquidity provider had a [***positive Bid Liquidity Score and a positive Ask Liquidity Score***](/defi/open-liquidity-program/scoring#liquidity-score) in the market of interest. This means the liquidity provider quoted on both sides of the order book with order sizes greater than or equal to $MinDepth$ with spreads less than or equal to $MaxSpread$ in the snapshot.
For liquidity providers who qualify for OLP rewards (for the first time ever) partway through an epoch, $Uptime_{Epoch}$ is scaled based on the total number of snapshots from the moment of qualification to the end of the epoch.
For example, suppose there are exactly 40,320 snapshots in an epoch and a liquidity provider qualifies for the first time with exactly 20,000 snapshots remaining. Also suppose the liquidity provider had an $Uptime_{Epoch}$ of 18,0000 as defined by the scoring formula above during the remainder of the epoch. In this case, $Uptime_{Epoch}$ would be scaled to $\frac{18000}{20000}*40320 = 36288$.
For addresses that qualify partway through an epoch but have qualified in the past (the address failed to maintain eligibility at some point), $Uptime_{Epoch}$ will not be scaled. This is to disincentivize addresses from losing their eligibility from epoch to epoch.
## Volume
$Volume$ is a liquidity provider's cumulative eligible maker and taker volume in the market during the epoch.
## Fully Expanded Formula
The fully expanded formula is:
$TS_{Market} =$
$$
\left(\sum \limits_{N=1}^{40,320} \min(LS_{N_{Bid}}, LS_{N_{Ask}})\right)^a \cdot \left(\sum \limits_{N=1}^{40,320} \begin{cases}1&\text{if } \min(LS_{N_{Bid}}, LS_{N_{Ask}}) > 0\\ 0&\text{otherwise}\end{cases} \right)^b \cdot Volume^c
$$
where
$$
LS_{N_{Bid}} = \frac{BidDepth_1}{Spread_1} \cdot \Theta_{vol} + \frac{BidDepth_2}{Spread_2} \cdot \Theta_{vol} + \ldots \\
\forall \ BidDepth_i \geq MinDepth \text{ and } Spread_i \leq MaxSpread
$$
$$
LS_{N_{Ask}} = \frac{AskDepth_1}{Spread_1} \cdot \Theta_{vol} + \frac{AskDepth_2}{Spread_2} \cdot \Theta_{vol} + \ldots \\
\forall \ AskDepth_i \geq MinDepth \text{ and } Spread_i \leq MaxSpread
$$
For information on individual reward calculations each epoch, see the [Reward Allocations page](/defi/open-liquidity-program/reward-allocations).
[^1]: Market Maker
[^2]: Market Maker
[^3]: Average between best bid price and best ask price in the order book
[^4]: Market Maker
[^5]: Market Maker
[^6]: Market Makers
[^7]: Market Maker
[^8]: Market Maker
[^9]: Market Maker
[^10]: Market Maker
# Volatility Response Modifications (VRMs)
Source: https://docs.injective.network/defi/open-liquidity-program/volatility-response-modifications
Volatility Response Modifications (VRMs) are part of our dynamic rewards system designed to optimise liquidity efficiency across the protocol. These modifications allow us to reallocate rewards when market conditions require adjustment, ensuring that incentives align with the protocol's needs during periods of high volatility.
Our system continuously monitors market conditions using specific threshold criteria :
**Triggering Conditions**
A Volatility Response Modification may be triggered when :
1. The overall market experiences a change of more than 5% in a 24-hour period, **AND**
2. Available liquidity fails to meet at least 50% of the established 30-day threshold, which is typically as follows for major trading pairs :
* BTC/USDT PERP : \$750,000 within 50 bps on each side
* ETH/USDT PERP : \$500K within 50 bps on each side
* INJ/USDT PERP : \$200K within 50 bps on each side
When these conditions are detected, our system automatically initiates the VRM process. Unlike subjective assessments, these clear thresholds ensure transparency and predictability for all liquidity providers.
#### Communication Process
When triggering conditions are met, we follow a clear, time-sensitive communication process :
1. **Immediate Alert** : All subscribed OLP participants will receive an immediate notification through Notifi alerting them that a VRM is now in effect. The 60-minute response window begins at this point.
2. **Response Window** : LPs have a 60-minute window to react to the VRM and restore liquidity to the required thresholds.
3. **Confirmation Notice** : At the conclusion of the 60-minute window, another notification will be sent announcing the outcome of the VRM, as detailed below. Note that a maximum of one VRM can be implemented in a 24-hour period.
4. **Transparency Report** : A summary of the market conditions and liquidity levels that triggered the modification will be included in the Commonwealth thread accompanying the OLP rewards governance proposal at the end of the epoch.
#### Epoch Reward Adjustments
When a VRM event occurs, the following adjustments apply to the total INJ rewards for the current epoch :
* **Liquidity Restoration** : If liquidity is sized up to meet the required thresholds within the 60-minute response window, the overall epoch rewards will be **boosted by 2,500 INJ** (e.g. 42,500 INJ for the epoch instead of 40,000).
* **Liquidity Shortfall** : If liquidity thresholds are not met after the response window :
* First VRM in an epoch: 2,500 INJ reduction in overall epoch rewards
* Second or subsequent VRMs in the same epoch: 5,000 INJ reduction per occurrence
In the event there is a liquidity shortfall after the 60-minute response window, these adjusted rewards are redirected to support more efficient liquidity provision strategies, ensuring that the protocol remains resilient during volatile market conditions.
Our goal is to create a fair system that rewards participants who help maintain market quality, particularly when it matters most. These modifications help us build a more resilient protocol that better serves all stakeholders in our ecosystem.
# Gas and Fees
Source: https://docs.injective.network/defi/transaction-fees
## Gas and Fees
Learn about the differences between `Gas` and `Fees` on Injective.
Helpful foundational reading: [Gas Metering](https://docs.cosmos.network/sdk/latest/learn/concepts/context-gas-events#gas-metering), [Auth Module Gas & Fees](https://docs.cosmos.network/sdk/latest/modules/auth/auth#gas-&-fees)
**Exchange module gas heuristics:** For detailed gas costs per message type on the exchange module (order placement, cancellation, market operations), see the [Gas Heuristics reference](/developers-native/injective/exchange/12_gas_heuristics).
Gas represents the amount of computational effort required to execute specific operations on the state machine.
Injective utilizes the concept of gas to track the resource usage of operations during execution. Operations on Injective are represented as read or writes done to the chain's store.
A fee is calculated and charged to the user during a message execution. This fee is calculated from the sum of all gas consumed in a message execution:
```
fee = gas * gas price
```
Gas is used to make sure that operations do not require an excess amount of computational power to complete, and to deter bad-acting users from spamming the network.
**Minimum gas price:** The minimum gas price set by validators is currently `160,000,000inj`. To find the amount paid in `inj`, multiply the gas price by the gas amount and divide by 1e18 (INJ has 18 decimals).
**For example:** if `gasWanted` is 104,519, then `gasFees` = 160,000,000 \* 104,519 / 1e18 = 0.000016723`inj`
### Cosmos SDK `Gas`
In the Cosmos SDK, gas is tracked in the main `GasMeter` and the `BlockGasMeter`:
* `GasMeter`: keeps track of the gas consumed during executions that lead to state transitions. It is reset on every transaction execution.
* `BlockGasMeter`: keeps track of the gas consumed in a block and enforces that the gas does not go over a predefined limit. This limit is defined in the Tendermint consensus parameters and can be changed via governance parameter change proposals.
More information regarding gas in Cosmos SDK can be found [here](https://docs.cosmos.network/main/learn/beginner/gas-fees).
In Cosmos, there are types of operations that are not triggered by transactions that can also result in state transitions. Concrete examples are the `BeginBlock` and `EndBlock` operations and the `AnteHandler` checks, which might also read and write to the store before running the state transition from a transaction.
#### `BeginBlock` and `EndBlock`
These operations are defined by the Tendermint Core's Application Blockchain Interface (ABCI) and are defined by each Cosmos SDK module. As their name suggest, they are executed at the beginning and at the end of each block processing respectively (i.e., pre- and post-transaction execution).
#### `AnteHandler`
The Cosmos SDK [`AnteHandler`](https://docs.cosmos.network/v0.45/modules/auth/03_antehandlers.html) performs basic checks prior to transaction execution. These checks are usually signature verification, transaction field validation, transaction fees, etc.
# Add Injective to Your dApp
Source: https://docs.injective.network/developers-evm/add-injective-to-your-dapp
# Add Injective to Your dApp
Enable your users to connect to the Injective network with a single click.
Use the code snippet below to add an “Add Injective Network” button to your dApp, making it easy for users to add Injective to MetaMask or any EVM-compatible wallet.
1. Copy and paste the snippet into your frontend codebase.
2. Connect the `addInjectiveNetwork` function to your preferred UI button.
3. That’s it—your users can now add Injective to their wallet in seconds
```tsx theme={null}
// Network configuration
const INJECTIVE_MAINNET_CONFIG = {
chainId: '0x6f0', // 1776 in decimal
chainName: 'Injective',
rpcUrls: ['https://evm-rpc.injective.network'],
nativeCurrency: {
name: 'Injective',
symbol: 'INJ',
decimals: 18
},
blockExplorerUrls: ['https://explorer.injective.network']
};
async function addInjectiveNetwork() {
// Check if MetaMask or another Web3 wallet is installed
if (!window.ethereum) {
alert('Please install MetaMask or another Web3 wallet!');
return;
}
try {
// First, try to switch to the Injective network
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: INJECTIVE_MAINNET_CONFIG.chainId }],
});
console.log('Switched to Injective network successfully!');
} catch (switchError) {
// Error code 4902 means the network hasn't been added yet
if (switchError.code === 4902) {
try {
// Add the Injective network
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [INJECTIVE_MAINNET_CONFIG],
});
console.log('Injective network added successfully!');
} catch (addError) {
console.error('Failed to add Injective network:', addError);
alert('Failed to add Injective network. Please try again.');
}
} else {
console.error('Failed to switch network:', switchError);
alert('Failed to switch to Injective network.');
}
}
}
```
# Alchemy RPC Setup
Source: https://docs.injective.network/developers-evm/alchemy-rpc-setup
Connect to Injective EVM mainnet and testnet via Alchemy JSON-RPC endpoints.
[Alchemy](https://www.alchemy.com/) provides reliable JSON-RPC endpoints for the Injective EVM. Use this guide to create an Alchemy app and start making requests.
[*Injective + Alchemy quick start (1 min) - YouTube*](https://youtube.com/watch?v=Ma3CIvDRSjk)
## Prerequisites
* An Alchemy account ([sign up free](https://www.alchemy.com/))
* Basic familiarity with EVM RPC endpoints
## Steps
From the [Alchemy dashboard](https://dashboard.alchemy.com/), create a new app and select **Injective** as the chain. Choose the network:
* **Injective EVM Mainnet**: Chain ID `1776`
* **Injective EVM Testnet**: Chain ID `1439`
Open your app and copy the HTTPS and WebSocket endpoints:
```bash Mainnet theme={null}
https://inj-mainnet.g.alchemy.com/v2/
wss://inj-mainnet.g.alchemy.com/v2/
```
```bash Testnet theme={null}
https://inj-testnet.g.alchemy.com/v2/
wss://inj-testnet.g.alchemy.com/v2/
```
Keep your API key private, treat it like a password.
Use the endpoint with your preferred library:
```ts ethers.js (v6) theme={null}
import { JsonRpcProvider } from "ethers";
const provider = new JsonRpcProvider(
"https://inj-mainnet.g.alchemy.com/v2/"
);
const blockNumber = await provider.getBlockNumber();
console.log("Latest block:", blockNumber);
```
```ts viem theme={null}
import { createPublicClient, http } from "viem";
import { defineChain } from "viem";
const injectiveEVM = defineChain({
id: 1776,
name: "Injective EVM",
nativeCurrency: { name: "Injective", symbol: "INJ", decimals: 18 },
rpcUrls: {
default: { http: ["https://inj-mainnet.g.alchemy.com/v2/"] },
},
});
const client = createPublicClient({
chain: injectiveEVM,
transport: http(),
});
const blockNumber = await client.getBlockNumber();
console.log("Latest block:", blockNumber);
```
```bash curl theme={null}
curl https://inj-mainnet.g.alchemy.com/v2/ \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```
## Next steps
* [EVM network information](/developers-evm/network-information) - chain IDs, explorers, and public RPC endpoints
* [Deploy your first EVM smart contract](/developers-evm/smart-contracts)
* [Connect your dApp to Injective](/developers-evm/add-injective-to-your-dapp)
# Bank Precompile
Source: https://docs.injective.network/developers-evm/bank-precompile
# Bank Precompile
The Bank Precompile is a system smart contract residing at the fixed address `0x0000000000000000000000000000000000000064`.
It offers EVM developers a gas-efficient and native pathway to interact directly with the Injective's **bank module** (`x/bank`). This effectively bringing ERC-20 tokens on-chain. Any ERC-20 contract using the Bank precompile will be represented as `erc20:0x...` denom on-chain. Technically, this means that tokens reside only on-chain, with the EVM providing a view to the chain state rather than maintaining a separate copy. Unlike traditional bridging, where two token versions require user actions to switch, the Bank precompile offers real-time, dual-environment reflection for any transfer using either the on-chain bank denom or the ERC-20 `transfer()` method.
A range of ERC-20 implementations backed by the Bank precompile, alongside precompile interfaces and abstract contracts, are available at [Injective’s Solidity Contracts Repository](https://github.com/InjectiveLabs/solidity-contracts). Key contracts include:
* **Bank.sol** – precompile interface
* **BankERC20.sol** – abstract ERC20 implementation backed by the Bank precompile
* **FixedSupplyBankERC20.sol** – decentralized ERC20 with fixed supply (no owner, no minting or burning)
* **MintBurnBankERC20.sol** – ERC20 with an owner authorized to mint and burn tokens
These implementations are based on OpenZeppelin’s ERC20 contracts. Developers can freely create custom ERC20 contracts utilizing the Bank precompile.
## ERC20 Contract Deployment
**ℹ️ Note:**
To prevent denom spam, deploying an ERC20 contract via the ERC20 module is a **payable operation** and requires a deployment fee of **1 INJ**. Make sure your ERC20 contract deployment transaction includes this amount, or the operation will be rejected.
## Bank Precompile Interface
```solidity theme={null}
interface IBankModule {
function mint(address,uint256) external payable returns (bool);
function balanceOf(address,address) external view returns (uint256);
function burn(address,uint256) external payable returns (bool);
function transfer(address,address,uint256) external payable returns (bool);
function totalSupply(address) external view returns (uint256);
function metadata(address) external view returns (string memory,string memory,uint8);
function setMetadata(string memory,string memory,uint8) external payable returns (bool);
}
```
## Example
[Wrapped INJ (wINJ)](/developers-evm/wrapped-inj#is-winj-the-same-as-weth "Is wINJ the same as wETH?")
makes use of the Bank EVM precompile in order to implement the
[MultiVM Token Standard (MTS)](/developers-evm/multivm-token-standard).
## Start building
We've prepared a handful of demos that show how to build contracts using the Bank, Exchange, and Staking precompiles. These examples also demonstrate how to interact with the Injective EVM using the most common Ethereum development framework—**Foundry**.
See the bank precompile demo [here](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/erc20) and follow the corresponding README.
# Connect with MetaMask
Source: https://docs.injective.network/developers-evm/dapps/connect-with-metamask
## Connect MetaMask to Injective EVM Testnet
MetaMask is a browser wallet extension that lets you connect to any EVM-compatible network, including **Injective EVM**.
### How to Install MetaMask
Install the official MetaMask extension from the [MetaMask download page](https://metamask.io/download).
### Add Injective EVM Testnet to MetaMask
1. Click the **MetaMask icon** in your browser and unlock your wallet.
2. Click the **network selector** at the top (the default is *"Ethereum Mainnet"*).
3. Select **“Add Network”** or **“Add a network manually”** to open the custom network form.
#### Injective EVM Testnet Parameters
Fill in the following details:
```json theme={null}
Network Name: Injective EVM Testnet
Chain ID: 1439
RPC URL: https://k8s.testnet.json-rpc.injective.network/
Currency Symbol: INJ
Block Explorer URL: https://testnet.blockscout.injective.network/blocks
```
> *Note: Block Explorer URL is optional, powered by BlockScout.*
### Switch to Injective EVM Testnet
Once the network is added, use the network selector to switch to **Injective EVM Testnet**.
### Fund Your Wallet (Optional)
Need Testnet INJ? Visit the [Injective Testnet faucet](https://testnet.faucet.injective.network).
Funds will appear once included in a Testnet block.
***
### You're All Set!
MetaMask is now connected to the **Injective EVM Testnet**. You can:
* Deploy smart contracts using tools like **Foundry**, **Hardhat**, or **Remix**.
* Interact with Testnet dApps and contracts.
* Inspect transactions via the Blockscout explorer.
> **Tip:** Always double-check RPC URLs and Chain IDs - accuracy is crucial to avoid misconfiguration.
***
### Connect MetaMask via `ethers.js`
You can also connect MetaMask programmatically using [`ethers`](https://docs.ethers.org/).
#### Sample Code
```ts theme={null}
import { ethers } from 'ethers';
export const INJECTIVE_EVM_PARAMS = {
chainId: '0x59f', // 1439 in hexadecimal
chainName: 'Injective EVM',
rpcUrls: ['https://k8s.testnet.json-rpc.injective.network/'],
nativeCurrency: {
name: 'Injective',
symbol: 'INJ',
decimals: 18,
},
blockExplorerUrls: ['https://testnet.blockscout.injective.network/blocks'],
};
export async function connectMetaMask() {
if (typeof window.ethereum === 'undefined') {
alert('MetaMask not installed!');
return;
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [INJECTIVE_EVM_PARAMS],
});
await provider.send('eth_requestAccounts', []);
const signer = provider.getSigner();
const address = await signer.getAddress();
console.log('Connected address:', address);
return { provider, signer, address };
} catch (err) {
console.error('MetaMask connection failed:', err);
}
}
```
### Using `ethers.js` to interact with your smart contract
Sample code for counter contract ABI:
```tsx theme={null}
// abi/counterAbi.ts
[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "reason",
"type": "string"
}
],
"name": "UserRevert",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newValue",
"type": "uint256"
}
],
"name": "ValueSet",
"type": "event"
},
{
"inputs": [],
"name": "increment",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "number",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newNumber",
"type": "uint256"
}
],
"name": "setNumber",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "reason",
"type": "string"
}
],
"name": "userRevert",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
```
```javascript theme={null}
import { ethers } from 'ethers'
import { counterAbi } from './abi/counterAbi'
import { INJECTIVE_EVM_PARAMS } from './config' // From separate file
// Replace with your deployed contract address
const contractAddress = '0xYourContractAddressHere'
async function connectAndInteract() {
if (!window.ethereum) {
alert('MetaMask is not installed!')
return
}
// Request Injective EVM Network be added to MetaMask
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: INJECTIVE_EVM_PARAMS.chainHex,
chainName: INJECTIVE_EVM_PARAMS.chainName,
rpcUrls: [INJECTIVE_EVM_PARAMS.rpcUrl],
nativeCurrency: INJECTIVE_EVM_PARAMS.nativeCurrency,
blockExplorerUrls: [INJECTIVE_EVM_PARAMS.blockExplorer],
},
],
})
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send('eth_requestAccounts', [])
const signer = provider.getSigner()
const userAddress = await signer.getAddress()
console.log('Connected as:', userAddress)
// Contract instance
const contract = new ethers.Contract(contractAddress, counterAbi, signer)
// Send transaction to increment
const tx = await contract.increment()
console.log('Transaction sent:', tx.hash)
const receipt = await tx.wait()
console.log('Transaction mined in block:', receipt.blockNumber)
}
connectAndInteract().catch(console.error)
```
### Using `viem` to interact with your smart contract
Sample code
```javascript theme={null}
import { counterAbi } from './abi/counterAbi'
import { INJECTIVE_EVM_PARAMS } from './config'
import { createPublicClient, http } from 'viem'
import { createWalletClient, custom, defineChain, formatEther } from 'viem'
// Replace with your deployed contract address
const contractAddress = '0xYourContractAddressHere'
async function connectAndInteract() {
if (typeof window === 'undefined' || typeof window.ethereum === 'undefined') {
alert('MetaMask is not installed!')
return
}
const client = createWalletClient({
chain: INJECTIVE_EVM_PARAMS,
transport: custom(window.ethereum),
})
// Create a PublicClient for reading contract state
const publicClient = createPublicClient({
chain: injectiveEvm,
transport: http(),
})
const [account] = await client.requestAddresses()
console.log('Connected account:', account)
// Send transaction to increment using wallet client
const hash = await client.writeContract({
address: contractAddress,
abi: counterAbi,
functionName: 'increment',
account,
})
console.log('Transaction sent with hash:', hash)
}
connectAndInteract().catch(console.error)
```
# Connect with WalletConnect
Source: https://docs.injective.network/developers-evm/dapps/connect-with-walletconnect
WalletConnect is an open-source, chain-agnostic protocol that securely links wallets and Web3 applications. It uses a bridge server to relay encrypted messages, allowing users to connect by scanning a QR code or via deep-linking, without exposing private keys.
### Integration Steps for WalletConnect
#### Prerequisites
Register at [WalletConnect Cloud](https://cloud.walletconnect.com) and obtain the **project ID**.
***
#### Install Dependency
```bash theme={null}
npm install ethers wagmi viem @walletconnect/ethereum-provider
```
Set up Injective EVM network configuration
```javascript theme={null}
// lib/injectiveChain.ts
import { defineChain } from 'viem'
export const injectiveEvm = defineChain({
id: 1439,
name: 'Injective EVM',
nativeCurrency: {
name: 'INJ',
symbol: 'INJ',
decimals: 18,
},
rpcUrls: {
default: { http: ['https://k8s.testnet.json-rpc.injective.network'] },
},
blockExplorers: {
default: { name: 'InjectiveScan', url: 'https://testnet.blockscout.injective.network/blocks' },
},
})
```
Set up Wagmi + WalletConnect
```javascript theme={null}
// lib/wagmi.ts
import { walletConnect } from '@wagmi/connectors'
import { createConfig, http } from '@wagmi/core'
import { injectiveEvm } from './injectiveChain'
export const wagmiConfig = createConfig({
chains: [injectiveEvm],
connectors: [
walletConnect({
projectId: 'your-walletconnect-project-id', // From WalletConnect Cloud
showQrModal: true,
}),
],
transports: {
[injectiveEvm.id]: http(injectiveEvm.rpcUrls.default.http[0]),
},
})
```
Integrate into your project
```javascript theme={null}
'use client'
import Image from 'next/image'
import { wagmiConfig } from './providers'
import { useConnect, useAccount, WagmiProvider } from 'wagmi'
import { QueryClientProvider, QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient()
function WalletConnector() {
const { connectors, connect, isPending } = useConnect()
const { address, isConnected } = useAccount()
const wcConnector = connectors.find(c => c.id === 'walletConnect')
return (
{isConnected ? (
Connected to {address}
) : (
)}
)
}
export default function Home() {
return (
)
}
```
***
More Info
* WalletConnect docs: [https://docs.walletconnect.com](https://docs.walletconnect.com)
* WalletConnect official examples: [https://github.com/WalletConnect/web-examples](https://github.com/WalletConnect/web-examples)
# Your First EVM dApp
Source: https://docs.injective.network/developers-evm/dapps/index
Step-by-step guide to creating dApps for EVM smart contracts on Injective using Metamask, WalletConnect, ethers.js, and viem.
Decentralized applications (dApps) are front end applications that interact with a blockchain,
typically with smart contracts.
Do check out [your first EVM smart contract](/developers-evm/smart-contracts/) first!
These guides show you how to connect using different EVM wallets and libraries.
## Guides
* [Connect with MetaMask](/developers-evm/dapps/connect-with-metamask/)
* [Connect with WalletConnect](/developers-evm/dapps/connect-with-walletconnect/)
# ERC20 Module
Source: https://docs.injective.network/developers-evm/erc20-module
### ERC20 Module
The ERC20 module enables **existing** bank denoms (e.g., IBC-bridged tokens, USDC, tokenfactory, and Peggy) to integrate with the Injective EVM. It maintains a mapping between token pairs within its storage, creating an association between ERC20 tokens and their corresponding bank denoms. When a new token pair is generated for an existing bank denom, the module deploys an ERC20 contract that interacts with the Bank precompile, which then references the storage mapping to align the ERC20 address with the respective bank denom. This module serves several essential purposes:
1. **Storage**: Maps between bank denom ↔ ERC20 address
2. **New Message Type**: Enables users to establish new token pair mappings by issuing a chain message
#### Creating a New Token Pair
Currently, three types of bank denoms can have associated token pairs, each with specific rules:
* **Tokenfactory (`factory/...`)**\
Only the denom admin or governance can create a token pair. The sender can specify an existing ERC20 contract address as a custom implementation. If omitted, a new instance of `MintBurnBankERC20.sol` is deployed, with `msg.sender` as the owner, allowing minting and burning through the contract.
* **IBC (`ibc/...`)**\
IBC denoms can be integrated into the EVM by any user through token pair creation, though without the option for custom ERC20 addresses. These will always deploy a new, ownerless instance of `FixedSupplyBankERC20.sol`.
* **Peggy (`peggy0x...`)**\
Peggy denoms can be integrated into the EVM by any user through token pair creation, though without the option for custom ERC20 addresses. These will always deploy a new, ownerless instance of `FixedSupplyBankERC20.sol`.
# EVM Equivalence
Source: https://docs.injective.network/developers-evm/evm-equivalence
Understanding EVM equivalence on Injective
## Injective EVM vs. Ethereum Mainnet
Injective's native EVM us a fully embedded execution environment that has been integrated into the core architecture of the chain. It is designed to be a 1:1 equivalent to Ethereum in terms of development experience.
Native EVM on Injective supports the latest version of `geth`, ensuring that developers have access to the latest features, tooling, security patches, and improvements. In addition, Injective’s EVM enhances performance and expands capabilities, granting access to Injective's sophisticated financial infrastructure that extends beyond what’s available on Ethereum.
## Gas Fee Estimates for Transactions
| Chain | Gas Price Range | Token Price | Create ERC-4337 Account | Simple Transfer | ERC-20 Transfer |
| ---------------- | ---------------- | ----------- | ----------------------- | ----------------- | ----------------- |
| Ethereum¹ | 30.5 ± 10.6 gwei | \$3000 | $35.25 ± $12.25 | $1.9215 ± $0.6678 | $5.9475 ± $2.067 |
| Polygon² | 224 ± 108 gwei | \$0.4 | $0.0345 ± $0.0166 | $0.0018 ± $0.0009 | $0.0058 ± $0.0028 |
| Optimism³ | 0.30 ± 0.15 gwei | \$3000 | $0.3467 ± $0.1733 | $0.0189 ± $0.0094 | $0.0585 ± $0.0292 |
| Avalanche⁴ | 36.4 ± 4.5 nAVAX | \$28 | $0.3926 ± $0.0485 | $0.0214 ± $0.0026 | $0.0662 ± $0.0081 |
| BnB Smart Chain⁵ | 7.05 ± 0.53 gwei | \$600 | $1.6296 ± $0.1225 | $0.0888 ± $0.0066 | $0.2749 ± $0.0206 |
| Sei⁶ | 0.02 usei | \$0.40 | \$0.0030 | \$0.00017 | \$0.0005 |
| Injective⁷ | 0.16 nINJ | \$23 | \$0.0014 | \$0.00008 | \$0.0002 |
### Note: Gas per Action
* Create Account ERC-4337: `385266`
* Simple Transfer: `21000`
* ERC-20 Token Transfer: `65000`
### Gas Price Sources
1. [Ethereum Gas Price Source](https://etherscan.io/chart/gasprice) ↩︎
2. [Polygon Gas Price Source](https://polygonscan.com/chart/gasprice) ↩︎
3. [Optimism Gas Price Source](https://optimistic.etherscan.io/chart/gasprice) ↩︎
4. [Avalanche Gas Price Source](https://snowtrace.io/insight/leaderboard/gas-tracker) ↩︎
5. [BnB Smart Chain Gas Price Source](https://bscscan.com/chart/gasprice) ↩︎
6. [Sei Gas Prices Config](https://github.com/sei-protocol/chain-registry/blob/main/gas.json) ↩︎
7. [Injective Launches Gas Compression](https://injective.com/blog/injective-unveils-fee-reductions-with-gas-compression/) ↩︎
## EIP-1559 Configuration
Coming soon.
# EVM Gateway
Source: https://docs.injective.network/developers-evm/evm-gateway
Run Injective's standalone EVM JSON-RPC gateway for Ethereum-compatible RPC access to Injective EVM data.
## About
`evm-gateway` is a standalone Injective EVM JSON-RPC server.
It reads block data from CometBFT RPC, uses Injective gRPC for module queries, indexes EVM-relevant data into a local KV store, and serves Ethereum-style RPC from that local indexed state where possible.
Operationally, it is meant to be run beside an Injective node, not inside it. It replaces the internal JSON-RPC server that was not intended to be run in production environments.
**Properties:**
* cache-first for indexed EVM blocks, receipts, transactions, and logs
* historical gap fill and forward sync
* no keyring-backed `personal_*` API
* HTTP and WebSocket JSON-RPC
## Installing
Source and releases: [github.com/InjectiveLabs/evm-gateway/releases](https://github.com/InjectiveLabs/evm-gateway/releases)
Requirements to run `evm-gateway`:
* Go toolchain and `make`, when building from source
* Access to a running Injective node:
* CometBFT RPC endpoint
* gRPC endpoint
* archival node access for historical backfill
### Using the repository
```bash theme={null}
git clone https://github.com/InjectiveLabs/evm-gateway.git
cd evm-gateway
make install
```
### Pre-built Docker image
```bash theme={null}
docker run -it --rm injectivelabs/evm-gateway:latest --help
```
## Configuration
Configuration is environment-driven with the `WEB3INJ_` prefix. The repository's `.env.example` is the reference.
Minimum practical configuration:
```bash theme={null}
export WEB3INJ_CHAIN_ID=injective-1
export WEB3INJ_COMET_RPC=http://127.0.0.1:26657
export WEB3INJ_GRPC_ADDR=127.0.0.1:9090
export WEB3INJ_EARLIEST_BLOCK=127250000
export WEB3INJ_DATA_DIR=evm-gateway-data
export WEB3INJ_JSONRPC_API=eth,net,web3,debug
```
If you run it next to `injectived`, do not bind on the same ports. A typical sidecar setup is:
```bash theme={null}
export WEB3INJ_JSONRPC_ADDRESS=0.0.0.0:8645
export WEB3INJ_JSONRPC_WS_ADDRESS=0.0.0.0:8646
```
On the first run, the service syncs historical data from `WEB3INJ_EARLIEST_BLOCK` to the current chain tip. Use an archival node for the first backfill and size rate limits accordingly.
## Virtualized Cosmos Bank Transfers
By default, `evm-gateway` exposes the EVM view of Injective blocks. Native Cosmos `x/bank` transfers are not EVM transactions, so an EVM-only JSON-RPC index would otherwise miss native bank activity such as standard Cosmos transfers, minting, burning, and module-level balance movements.
Set `WEB3INJ_VIRTUALIZE_COSMOS_EVENTS=true` to enable the optional virtualization layer:
```bash theme={null}
export WEB3INJ_VIRTUALIZE_COSMOS_EVENTS=true
```
When enabled, `evm-gateway` parses supported Cosmos `x/bank` events and projects them into Ethereum-style JSON-RPC transactions and logs. This makes native bank transfer activity discoverable through familiar methods such as `eth_getBlockByNumber`, `eth_getTransactionByHash`, `eth_getTransactionReceipt`, `eth_getLogs`, and filter APIs.
The gateway currently virtualizes these Cosmos bank event types:
* `transfer`
* `coin_spent`
* `coin_received`
* `coinbase`
* `burn`
Virtualized bank logs are emitted from the reserved pseudo-contract address:
```text theme={null}
0x0000000000000000000000000000000000000800
```
The event-only ABI is:
```solidity theme={null}
interface IInjectiveNativeBankTransfers {
event NativeBankTransfer(bytes32 indexed sender, bytes32 indexed recipient, string denom, uint256 amount);
event NativeBankCoinSpent(bytes32 indexed spender, string denom, uint256 amount);
event NativeBankCoinReceived(bytes32 indexed receiver, string denom, uint256 amount);
event NativeBankCoinbase(bytes32 indexed minter, string denom, uint256 amount);
event NativeBankBurn(bytes32 indexed burner, string denom, uint256 amount);
}
```
Address-like Cosmos fields are encoded as right-aligned `bytes32` values. This allows both 20-byte EVM addresses and longer Cosmos addresses to fit the same ABI.
### Virtual transaction behavior
Virtualized Cosmos events are represented as synthetic Ethereum-style transactions:
* non-EVM Cosmos transaction events use a virtual hash derived from `keccak256(cosmos_tx_hash)`
* begin-block and end-block events use deterministic hashes derived from the block height
* virtual transactions use empty `input`, zero gas and value defaults, and `to = 0x0000000000000000000000000000000000000800`
* virtual transactions and logs include `virtual: true` metadata
* when the source Cosmos transaction hash is available, virtual transaction and log results include `cosmos_hash`
* begin-block and end-block virtual transactions do not include `cosmos_hash`
The gateway preserves block ordering in the virtualized view:
1. begin-block virtual transaction, when applicable
2. normal EVM transactions and virtualized Cosmos transactions in block order
3. end-block virtual transaction, when applicable
If a Cosmos bank event is emitted as a side effect of an EVM transaction, the virtual logs are appended after that transaction's real EVM logs.
### Querying virtualized transfers
You can query the reserved pseudo-contract address with `eth_getLogs`:
```bash theme={null}
curl -s localhost:8645 \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getLogs","params":[{"address":"0x0000000000000000000000000000000000000800","fromBlock":"latest","toBlock":"latest"}]}'
```
Then use the returned transaction hash with `eth_getTransactionByHash` or `eth_getTransactionReceipt` to inspect the virtual transaction and its logs.
For a complete indexed history, enable `WEB3INJ_VIRTUALIZE_COSMOS_EVENTS=true` before the initial sync. If you already synced an existing data directory with virtualization disabled, rebuild or reindex the data directory before relying on historical virtualized bank logs.
## Quick checking functionality with curl
Version:
```bash theme={null}
curl -s localhost:8645 -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"web3_clientVersion","params":[]}'
```
Chain ID:
```bash theme={null}
curl -s localhost:8645 -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'
```
Head:
```bash theme={null}
curl -s localhost:8645 -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
```
Sync status:
```bash theme={null}
curl -s localhost:8645/status/sync
```
Simple block read:
```bash theme={null}
curl -s localhost:8645 -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["latest",false]}'
```
If `debug` is enabled, transaction tracing:
```bash theme={null}
curl -s localhost:8645 -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction","params":["0x",{"tracer":"callTracer"}]}'
```
## Configuring state persistence
State persistence is controlled primarily by:
* `WEB3INJ_DATA_DIR`
* `WEB3INJ_DB_BACKEND`
Example:
```bash theme={null}
export WEB3INJ_DATA_DIR=/var/lib/evm-gateway
export WEB3INJ_DB_BACKEND=goleveldb
```
## Glossary of implemented methods by namespace
Default namespaces:
* `eth`
* `net`
* `web3`
Optional namespaces:
* `debug`
* `inj`
Not implemented:
* `personal`
* `txpool`
* `miner`
### `eth`
`eth_blockNumber`, `eth_getBlockByNumber`, `eth_getBlockByHash`, `eth_getBlockTransactionCountByHash`, `eth_getBlockTransactionCountByNumber`, `eth_getTransactionByHash`, `eth_getTransactionCount`, `eth_getTransactionReceipt`, `eth_getTransactionByBlockHashAndIndex`, `eth_getTransactionByBlockNumberAndIndex`, `eth_sendRawTransaction`, `eth_getBalance`, `eth_getStorageAt`, `eth_getCode`, `eth_getProof`, `eth_call`, `eth_protocolVersion`, `eth_gasPrice`, `eth_estimateGas`, `eth_feeHistory`, `eth_maxPriorityFeePerGas`, `eth_chainId`, `eth_getUncleByBlockHashAndIndex`, `eth_getUncleByBlockNumberAndIndex`, `eth_getUncleCountByBlockHash`, `eth_getUncleCountByBlockNumber`, `eth_hashrate`, `eth_mining`, `eth_syncing`, `eth_coinbase`, `eth_getTransactionLogs`, `eth_fillTransaction`, `eth_getPendingTransactions`
Filter methods under the same namespace:
`eth_newPendingTransactionFilter`, `eth_newBlockFilter`, `eth_newFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, `eth_getLogs`, `eth_uninstallFilter`
### `net`
`net_version`, `net_listening`, `net_peerCount`
### `web3`
`web3_clientVersion`, `web3_sha3`
### `debug`
Tracing and block inspection:
`debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, `debug_traceCall`, `debug_getHeaderRlp`, `debug_getBlockRlp`, `debug_printBlock`, `debug_intermediateRoots`
Runtime and profiling:
`debug_blockProfile`, `debug_cPUProfile`, `debug_gcStats`, `debug_goTrace`, `debug_memStats`, `debug_setBlockProfileRate`, `debug_stacks`, `debug_startCPUProfile`, `debug_stopCPUProfile`, `debug_writeBlockProfile`, `debug_writeMemProfile`, `debug_mutexProfile`, `debug_setMutexProfileFraction`, `debug_writeMutexProfile`, `debug_freeOSMemory`, `debug_setGCPercent`, `debug_startGoTrace`, `debug_stopGoTrace`
### `inj`
`inj_getTxHashByEthHash`
# EVM Integrations Cheat Sheet
Source: https://docs.injective.network/developers-evm/evm-integrations-cheat-sheet
Looking to build a production application on Injective's EVM Mainnet? Handy references to get your integrations up to speed. Suitable for both greenfield deployments and cross-chain deployments.
# Network Config
* Chain ID: `1776`
Reference: [EVM Network Information | Injective | Docs](/developers-evm/network-information#injective-evm-mainnet)
Do **not** use inEVM, as that has been **deprecated**.
# Explorers
* [https://blockscout.injective.network/](https://blockscout.injective.network/) → Can only see EVM transactions
* [https://injscan.com/](https://injscan.com/) → Can see both EVM and Cosmos transactions
Reference: [EVM Network Information | Injective | Docs](/developers-evm/network-information#injective-evm-mainnet)
# RPC Endpoints
* Recommended for integrations partners
* Customizable for needs of applications
* Customizable rate limits, and no cut off for historical data queries
* Freemium/ paid options
* How to connect:
* QuickNode: [Injective RPC Node Endpoints, APIs & Tools | QuickNode](https://www.quicknode.com/chains/inj)
* Thirdweb: [Injective EVM](https://thirdweb.com/injective)
* Not recommended for integrations partners
* Not customizable for needs of applications
* Heavy rate limiting, and cut off for historical data queries
* Free option
* How to connect:
* JSON-RPC Endpoint: `https://sentry.evm-rpc.injective.network/`
* WS Endpoint: `wss://sentry.evm-ws.injective.network`
Reference: [EVM Network Information | Injective | Docs](/developers-evm/network-information#injective-evm-mainnet)
Do **not** use inEVM, as that has been **deprecated**.
# Contract Addresses
| | | |
| --------- | ----------- | -------------------------------------------- |
| USDT | MTS USDT | `0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13` |
| wETH | wrapped ETH | `0x83A15000b753AC0EeE06D2Cb41a69e76D0D5c7F7` |
| wINJ | wrapped INJ | `0x0000000088827d2d103ee2d9A6b781773AE03FfB` |
| USDC | | `0x2a25fbD67b3aE485e461fe55d9DbeF302B7D3989` |
| MultiCall | | `0xcA11bde05977b3631167028862bE2a173976CA11` |
Please use the reference page below as the canonical source.
Reference: [EVM Network Information | Injective | Docs](/developers-evm/network-information#injective-evm-mainnet)
# Bridges
[Injective Bridge](https://bridge.injective.network)
Guides:
* [How To Bridge From Ethereum To Injective Using Metamask](https://injective.com/blog/how-to-bridge-from-ethereum-to-injective-using-metamask/)
* [How To Bridge From Solana To Injective Using Phantom](https://injective.com/blog/how-to-bridge-from-solana-to-injective-using-phantom/)
* [How to Bridge To Injective Using Wormhole](https://injective.com/blog/how-to-bridge-to-injective-using-wormhole/)
* [How To Bridge From Cosmos To Injective Using Keplr](https://injective.com/blog/how-to-bridge-from-cosmos-to-injective-using-keplr/)
# Data
## Explorers
* Explorer: [`blockscout.injective.network`](https://blockscout.injective.network/)
* Explorer API: [`blockscout-api.injective.network/api`](https://blockscout-api.injective.network/api)
## Oracles
* API3:
* Price feeds: [Api3 Market | Injective EVM](https://market.api3.org/injective)
* Pyth:
* Documentation: [Pyth EVM Real Time Data Pull Integration](https://docs.pyth.network/price-feeds/core/use-real-time-data/pull-integration/evm)
* Reference documentation: [Pyth EVM Smart Contract Addresses](https://docs.pyth.network/price-feeds/core/contract-addresses/evm)
* Chainlink:
* Documentation: [Chainlink Data Streams](https://docs.chain.link/data-streams)
# Wallets
## `injectived`
Suitable for programmatic control, e.g. application/ dApp operated accounts.
Installation quickstart:
```shell theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.17.2-1765930431/linux-amd64.zip # replace with latest version of injective
unzip linux-amd64.zip
sudo mv injectived peggo /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
In the command above, the URL for the `wget` download should be replaced with one for the latest release.
You can find the exact commands (including the URL) under the [Mainnet Validator](/infra/validator-mainnet) section.
More options, including installation via Docker, or compiling from source,
available in the reference page below.
Reference: [injectived | Injective | Docs](/developers/injectived)
## MetaMask
Suitable for retail users who need to interact with EVM DApps.
Installation:
* Get browser extension from [https://metamask.io/en-GB](https://metamask.io/en-GB) for your specific browser.
* Visit [https://blockscout.injective.network/](https://blockscout.injective.network/), scroll to the bottom of the page
* Click on the “Add Injective” button
* Follow the prompts in MetaMask’s user interface
Reference: [How to Create an Injective Wallet (MetaMask)](https://injective.com/blog/how-to-create-an-injective-wallet-2/#to-connect-to-injective-with-metamask)
## Keplr
* Keplr : [https://www.keplr.app](https://www.keplr.app)
* Keplr API Docs for Integration: [https://docs.keplr.app/api/intro](https://docs.keplr.app/api/intro)
Reference: [How to Create an Injective Wallet (Keplr)](https://injective.com/blog/how-to-create-an-injective-wallet-2#to-connect-to-injective-with-keplr)
# EVM Integrations FAQ
Source: https://docs.injective.network/developers-evm/evm-integrations-faq
Frequently asked questions about integrating with Injective's EVM.
## Does Injective EVM Support ...?
**Does Injective EVM Support (EVM feature)?**
A: Generally, the answer is "yes".
See below for more specific questions.
**Does Injective EVM Support these specific opcodes?**
A: Yes. We have parity with opcode implementation.
More precisely: We have not added any custom opcodes,
neither have we modified the behaviour of any opcodes.
**Does Injective EVM Support ERC20, ERC721, ERC1155?**
A: Yes, any Solidity based smart contract standards should work,
as these *do not* require any special features beyond the standard EVM.
Corollary: When implementing fungible tokens,
ERC20 and any others that are extensions of it,
we strongly recommend that you implement them as a MultiVM Token Standard (MTS) token.
This allows the same token to act as a fungible token not only on Injective's EVM,
but also on Injective native (specifically as Cosmos Denoms).
Should Injective add other VMs in the future, MTS tokens will also work on those.
Read more about MTS:
[General article](https://injective.com/blog/multivm-token-standard-wrapped-inj), and
[technical reference](/developers-evm/multivm-token-standard).
**Does Injective EVM Support EIP-1559?**
A: Yes.
## Can I use ... on Injective EVM?
A: Generally, the answer is "yes".
See below for more specific questions.
**Can I use (EVM tool/ library) on Injective EVM?**
A:
* viem - yes
* ethers.js - yes
* hardhat - yes
* foundry - yes
**Can I use Foundry on Injective EVM with Injective's EVM precompiles?**
A: Injective's [EVM precompiles](/developers-evm/precompiles)
access Injective native functions that are not available in generic EVMs.
This means that if you use, for example Foundry and connect it to:
* Injective EVM Testnet, it will work
* `localhost` simulated EVM network, it will not work
To address the latter issue, we've forked the Foundry tools to support
Injective precompiles (starting with Bank precompile needed for `BankERC20.sol` and MTS),
if you are using Solidity tests or deployment scripts as a Foundry project,
you will need this so `forge`/ `cast` can do local simulations.
Latest releases, with pre-built binaries for x86\_64 Linux and macOS ARM64,
can be found at [github.com/InjectiveLabs/foundry/releases](https://github.com/InjectiveLabs/foundry/releases).
In order to enable support for Injective precompiles on this forked version of Foundry, you must set `injective = true` inside config file `foundry.toml` or use an environment variable `FOUNDRY_INJECTIVE=true`.
## Which EVM development infrastructure is available on Injective?
Refer to the [EVM Integrations Cheat Sheet](/developers-evm/evm-integrations-cheat-sheet)
for the answers.
## How can I resolve JSON-RPC dropped responses?
A: If you are using the public endpoint for RPCs,
this is likely to happen to you due to rate limiting.
These endpoints are designed for very light usage.
If you have deployed a complex application,
you should consider using a commercial grade RPC endpoint instead.
You may find recommended RPC providers in the
[EVM Integrations Cheat Sheet](/developers-evm/evm-integrations-cheat-sheet).
## Are `inj...` addresses and `0x...` addresses comaptible?
A: Yes. In fact they are the same, simply rendered in different formats.
Refer to [Convert addresses](https://docs.injective.network/developers/convert-addresses)
for example code to convert between these two formats.
## Which EVM hardfork does Injective support?
A: Here's the live configuration data for the EVM:
[sentry.lcd.injective.network/injective/evm/v1/params](https://sentry.lcd.injective.network/injective/evm/v1/params)
Under `chain_config`, there are keys that correspond to EVM hard fork names (with suffixes).
These indicate which ones are enabled.
The value for these keys are the block numbers at which they are enabled.
As of the Mainnet launch, all of them are set to `0`,
i.e. they are enabled from the "genesis" EVM block.
For future EVM hardforks, new keys will be added that will have a non-zero block number
at which they will be (or were) enabled.
Do you have more questions about EVM integrations?
Join the [Injective discord](https://discord.com/invite/injective)
community and ask them there!
# Exchange Precompile
Source: https://docs.injective.network/developers-evm/exchange-precompile
The Exchange Precompile is a system smart contract residing at the fixed address `0x0000000000000000000000000000000000000065`. It offers Solidity developers a gas-efficient and native pathway to interact directly with the Injective chain's exchange module. By leveraging this precompile, your smart contracts can seamlessly perform a variety of exchange-related actions, including:
* Depositing and withdrawing funds to/from subaccounts.
* Placing or cancelling spot and derivative orders.
* Querying subaccount balances and open positions.
* Managing authorization grants for other accounts or contracts.
#### Calling the Precompile: Direct vs. Proxy Access
Interacting with the Exchange Precompile can be approached in two primary ways:
**1. Direct Access (Self-Calling Contracts)**
In this mode, your smart contract interacts with the precompile on its own behalf. The contract itself is the actor performing operations on the exchange module, using its own funds and managing its own positions.
*Example:*
```
exchange.deposit(address(this), subaccountID, denom, amount);
```
This method is straightforward and **requires no explicit authorization grant**, as the contract is inherently permissioned to manage its own resources.
**2. Proxy Access (Calling on Behalf of Another User)**
Smart contracts can also be designed to act as intermediaries, performing exchange operations on behalf of external user accounts. In this scenario, the contract calls the precompile, specifying a third-party's address as the sender or the account to be acted upon.
*Example:*
```
exchange.deposit(userAddress, subaccountID, denom, amount);
```
For this to succeed, the smart contract (`grantee`) **must be explicitly authorized** by the user (`userAddress`, the `granter`) to perform the specified action. This authorization is managed using the `approve` and `revoke` methods provided by the precompile. **It's crucial to handle these authorizations with care to ensure user funds are secure.**
To authorize a contract to perform specific actions on your behalf:
```
exchange.approve(grantee, msgTypes, spendLimit, duration);
```
* `grantee`: The address of the contract being authorized.
* `msgTypes`: An array of message types (e.g., `MsgCreateDerivativeLimitOrder`, `MsgDeposit`) the `grantee` is authorized to execute. Refer to `ExchangeTypes.sol` or the Injective Protocol protobuf definitions for a complete list.
* `spendLimit`: An array of `Cosmos.Coin` structs defining the maximum amount of specified tokens the `grantee` can utilize per message type or overall for the grant.
* `duration`: The time period, in seconds, for which the authorization remains valid.
To revoke a previously granted authorization:
```
exchange.revoke(grantee, msgTypes);
```
To check if an authorization currently exists:
```
exchange.allowance(grantee, granter, msgType);
```
#### Example: Direct Method
The `ExchangeDemo` contract below illustrates how a smart contract can use the direct access method. It performs basic exchange actions like depositing funds, withdrawing funds, creating a derivative limit order, and querying subaccount positions, all using its own subaccount and funds.
The `Exchange.sol` and `ExchangeTypes.sol` files contain the necessary interface definitions and data structures for interacting with the precompile. These are typically available in the official Injective Solidity contracts repository or can be included as dependencies in your project.
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../src/Exchange.sol"; // Contains IExchangeModule interface
import "../src/ExchangeTypes.sol"; // Contains necessary structs like DerivativeOrder
contract ExchangeDemo {
address constant exchangeContract = 0x0000000000000000000000000000000000000065;
IExchangeModule exchange = IExchangeModule(exchangeContract);
/***************************************************************************
* Calling the precompile directly (contract acts on its own behalf)
****************************************************************************/
/**
* @notice Deposits funds from the contract's balance into one of its exchange subaccounts.
* @param subaccountID The target subaccount ID (derived from the contract's address).
* @param denom The denomination of the asset to deposit (e.g., "inj").
* @param amount The quantity of the asset to deposit.
* @return success Boolean indicating if the deposit was successful.
*/
function deposit(
string calldata subaccountID,
string calldata denom,
uint256 amount
) external returns (bool) {
try exchange.deposit(address(this), subaccountID, denom, amount) returns (bool success) {
return success;
} catch Error(string memory reason) {
revert(string(abi.encodePacked("Deposit error: ", reason)));
} catch {
revert("Unknown error during deposit");
}
}
/**
* @notice Withdraws funds from one of the contract's exchange subaccounts to its main balance.
* @param subaccountID The source subaccount ID.
* @param denom The denomination of the asset to withdraw.
* @param amount The quantity of the asset to withdraw.
* @return success Boolean indicating if the withdrawal was successful.
*/
function withdraw(
string calldata subaccountID,
string calldata denom,
uint256 amount
) external returns (bool) {
try exchange.withdraw(address(this), subaccountID, denom, amount) returns (bool success) {
return success;
} catch Error(string memory reason) {
revert(string(abi.encodePacked("Withdraw error: ", reason)));
} catch {
revert("Unknown error during withdraw");
}
}
/**
* @notice Queries the derivative positions for a given subaccount of this contract.
* @param subaccountID The subaccount ID to query.
* @return positions An array of DerivativePosition structs.
*/
function subaccountPositions(
string calldata subaccountID
) external view returns (IExchangeModule.DerivativePosition[] memory positions) {
// Note: View functions calling precompiles might behave differently based on node configuration
// For on-chain state, this is fine. For off-chain queries, direct gRPC/API queries are often preferred.
return exchange.subaccountPositions(subaccountID);
}
/**
* @notice Creates a new derivative limit order from the contract's subaccount.
* @param order The DerivativeOrder struct containing order details.
* @return response The response struct containing details like order hash.
*/
function createDerivativeLimitOrder(
IExchangeModule.DerivativeOrder calldata order
) external returns (IExchangeModule.CreateDerivativeLimitOrderResponse memory response) {
try exchange.createDerivativeLimitOrder(address(this), order) returns (IExchangeModule.CreateDerivativeLimitOrderResponse memory resp) {
return resp;
} catch Error(string memory reason) {
revert(string(abi.encodePacked("CreateDerivativeLimitOrder error: ", reason)));
} catch {
revert("Unknown error during createDerivativeLimitOrder");
}
}
}
```
#### Start building
For detailed instructions on how to build, deploy, and interact with this `ExchangeDemo` smart contract, including setting up subaccounts and funding, please refer to the comprehensive demo available in our [solidity-contracts](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/exchange) repository.
#### Conclusion
The Exchange Precompile is a powerful tool, enabling sophisticated, protocol-integrated trading logic to be embedded directly within your smart contracts on Injective. Whether your contract is managing its own portfolio or acting as a versatile trading interface for other users (via the proxy pattern with `approve` and `revoke`), this precompile offers a clean, secure, and efficient method to interact with the core exchange module using Solidity.
Remember to prioritize direct calls for self-contained contract logic and to carefully implement the proxy pattern with robust authorization when building reusable contract interfaces for the broader Injective ecosystem.
\\
# Overview
Source: https://docs.injective.network/developers-evm/index
How to build on Injective using Solidity and standard Ethereum tooling MetaMask, Hardhat, Foundry, viem, ethers.js, and Remix are all compatible.
This section is for EVM developers who want to build on Injective using familiar EVM tooling and workflows.
Whether you're deploying Solidity smart contracts, integrating WalletCOnnect/MetaMask, or building full-stack dApps,
you'll find comprehensive guides for using Hardhat, Foundry,and other standard Ethereum development tools.
All while gaining access to Injective's unique native modules like the on-chain orderbook and staking.
Injective brings the best of both worlds to EVM developers:
Complete compatibility with Ethereum tooling combined with access to powerful native Cosmos modules.
Unlike typical EVM-compatible chains that operate in isolation,
Injective's precompiles act as bridges between Solidity and the native Injective modules.
This means your smart contracts can directly interact with the on-chain orderbook, stake tokens, participate in governance,
and leverage the MultiVM Token Standard for unified liquidity,
all while using MetaMask, Hardhat, Foundry, or any standard Ethereum tool.
With gas costs a fraction of Ethereum's and sub-second block times,
Injective offers a superior developer experience without sacrificing the familiar workflows Ethereum developers know.
## Key Concepts
| Term | Definition |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **EVM Equivalence** | Injective's native EVM maintains 1:1 compatibility with Ethereum, supporting the latest `geth` version while offering significantly lower gas costs. |
| **Precompiles** | Special Go-based contracts embedded at fixed EVM addresses (e.g., `0x64` for Bank) that bridge Solidity to Injective's native modules (Cosmos SDK), enabling access to exchange, staking, and bank functionality. |
| **MultiVM Token Standard (MTS)** | A unified token model where tokens share one canonical balance across EVM and Cosmos environments, eliminating the need for bridging or wrapping between VMs. |
| **Bank Precompile** | The EVM interface at `0x64` that proxies ERC20 operations (mint, burn, transfer) to the native bank module, enabling MTS-compliant tokens with unified balances. |
| **Bank Module** | A native module (Cosmos SDK) that maps native bank denoms (INJ, IBC, Peggy assets) to MTS-compliant ERC20 contracts within the EVM. |
## Navigation
Set up your connection to Injective's EVM
Write **Solidity**. Then compile, test, deploy, verify, and interact with **smart contracts**.
EVM interface that allows smart contracts to interact with native functions of the Injective chain
INJ Faucet
# Infrastructure & Tooling
Source: https://docs.injective.network/developers-evm/infrastructure-and-tooling
Build on Injective with World-Class Infrastructure
BlockScoutInjective is supported by a rich ecosystem of industry-leading infrastructure providers. Access reliable RPCs, powerful data indexers, and all the tools you need to build and scale your dApp on the fastest L1 for finance.
### RPC & Node Providers
Tenderly is a platform that offers advanced transaction simulation, enabling users to preview and analyze Ethereum and EVM-compatible transactions without broadcasting them on-chain. It also provides powerful debugging tools, including step-by-step execution tracing and error reporting, to help developers identify and fix issues in smart contracts efficiently.
Envio is the data layer for blockchain apps. It gives Injective developers a flexible way to access real-time and historical onchain data, from a single GraphQL API to raw high-speed access, with managed hosting on Envio Cloud. Injective EVM is natively supported by Envio's HyperSync, so HyperIndex can index it out of the box.
The Graph’s state-of-the-art Substreams and Substreams-powered subgraph solution, developed by StreamingFast for Injective, provides developers seamless access to on-chain data.
EVM standard Block explorer (Hosted by BlockScout)
# MultiVM Token Standard
Source: https://docs.injective.network/developers-evm/multivm-token-standard
Understanding token representation in Injective's multi-VM architecture
## What is MultiVM Token Standard (MTS)?
MTS (MultiVM Token Standard) ensures that every token on Injective -
whether deployed using Cosmos modules or via the Ethereum Virtual Machine (EVM) —
has one canonical balance and identity.
This unified approach prevents fragmentation and eliminates the need for bridging or wrapping tokens,
thereby enabling seamless interoperability and unified liquidity for decentralized finance (DeFi) and dApp interactions.
## Why is MTS Important?
* **Seamless Interoperability:** Tokens remain consistent across Cosmos and EVM environments.
* **Unified Liquidity:** A single source of truth avoids liquidity fragmentation.
* **Enhanced Developer Experience:** Standard tools like Hardhat, Foundry, and MetaMask work out of the box.
* **Security & Efficiency:** All token state is maintained centrally in the bank module, ensuring robust security.
## Architecture
The system comprises two main components:
* [**Bank Precompile**](/developers-evm/bank-precompile/):
* Developed in Go, this precompile is embedded directly in the Injective EVM.
* It provides a Solidity interface that proxies ERC20 operations—such as mint, burn, and transfer—to the bank module.
* [**ERC20 Module**](/developers-evm/erc20-module/):
* This module is used for representing existing on-chain denoms to EVM. Creating MTS-based ERC20 tokens should be thought the `Bank` precompile.
* This module maps native bank denoms (e.g., INJ, IBC tokens, Peggy assets) to an ERC20 contract within the EVM.
* It deploys MTS-compliant ERC20 contracts that always reflect the canonical token balance as maintained by the bank module.
Single Token Representation Architecture
### Creating an MTS-Compliant Token
#### New ERC20 tokens ("erc20:..." denoms)
1. [**Using Our Prebuilt Templates**](https://github.com/InjectiveLabs/solidity-contracts/tree/master/src):
* Start with the provided Solidity templates, such as `BankERC20.sol`, `MintBurnBankERC20.sol`, or `FixedSupplyBankERC20.sol`.
2. [**Deploying the Contract**](/developers-evm/smart-contracts/):
* Deploy your MTS token contract on the Injective EVM network.
* The contract automatically interacts with the Bank Precompile to update the canonical state.
#### Existing denoms (TokenFactory, IBC, Peggy)
You have two options:
1. Create a token pair inside erc20 module by submitting a `MsgCreateTokenPair` message.
2. Use custom ERC20 implementation for Smart Contract during pair creation (tokenfactory denoms only):
* Upload your custom ERC20 Smart Contract on chain first.
* Provide the address of the contract while submitting the `MsgCreateTokenPair` message.
### Interoperability and Cross-Chain Integration
#### Native Interoperability\*
Injective’s EVM is integrated directly into the Cosmos-based chain.
* EVM smart contracts, when using MTS, perform operations that reflect immediately
on native modules (such as the exchange, staking, and governance modules).
* [JSON-RPC endpoints](/developers-evm/network-information/)
provided within the Injective binary are compatible with Ethereum,
ensuring smooth developer integration.
#### Cross-Chain Operations
* **IBC Compatibility:** Existing native tokens
(e.g., those created via a
[Token Factory](/developers-native/injective/tokenfactory/) or pegged via Peggy)
are accessible from the EVM once an MTS pairing is established.
* **Bridging Alternatives:** While many blockchains require separate bridge operations (lock, mint, unlock),
MTS avoids these steps by natively synchronizing states.
#### Allowances & Extended ERC20 Functions
* MTS contracts maintain standard ERC20 functionalities such as allowances
(approve/transferFrom).
* Note that while the allowance mechanism is maintained within the EVM contract for convenience,
the ultimate balance is managed by the bank module, preserving integrity.
### Performance, Gas, and Security Considerations
#### Gas Costs and Efficiency
* Gas fees are paid in INJ.
While MTS operations via the EVM introduce an abstraction layer that may slightly increase gas usage compared to native transactions,
the overall cost remains lower than comparable operations on Ethereum.
* The gas model is designed to reflect a balance between EVM-style opcode costs and native module interactions.
#### Security
* The [bank module](/developers-native/core/), as the single source of truth,
underpins MTS’s security by ensuring that token balances are consistent and verifiable.
* The use of [precompiles](/developers-evm/precompiles/) prevents common pitfalls
like state desynchronization, ensuring that all operations -
no matter where initiated—update the same canonical ledger.
* Advanced security guidelines and best practices for smart contract development
are provided in our security section and external resources.
**Note**:
To prevent denom spam, deploying an ERC20 contract via the ERC20 module
is a **payable operation** and requires a deployment fee of **1 INJ**.
Make sure your ERC20 contract deployment transaction includes this amount,
or the operation will be rejected.
If the supply is wiped - specifically total supply goes from non-zero to zero -
the underlying denom is removed from the blockchain state.
Should you add supply again - specifically total supply goes from zero to non-zero -
the underlying denom must be recreated, and thus the deployment fee is charged again.
# EVM Network Information
Source: https://docs.injective.network/developers-evm/network-information
Essential information about the Injective EVM network. Chain IDs, RPC endpoints, block explorer URLs, and network configuration for Injective's EVM layer.
## Network Info
* Chain ID: `1776`
* JSON-RPC Endpoint: `https://sentry.evm-rpc.injective.network/`
* EVM Gateway JSON-RPC Endpoint: `https://evm.archival.chain.virtual.json-rpc.injective.network/`
* WS Endpoint: `wss://sentry.evm-ws.injective.network`
* Faucet: N/A, to obtain Mainnet INJ see [`injective.com/getinj`](https://injective.com/getinj/)
* Explorer: [`blockscout.injective.network`](https://blockscout.injective.network/)
* Explorer API: `https://blockscout-api.injective.network/api`
Note that the Injective Chain ID is natively `injective-1`.
However, EVM uses a numeric chain ID of `1776`.
While these are different, they map to the **same** network.
See [Injective Mainnet network information](/developers/network-information/#injective-mainnet) for more details.
## Contracts
* **wINJ** wrapped INJ (MTS) -
[`0x0000000088827d2d103ee2d9A6b781773AE03FfB`](https://blockscout.injective.network/address/0x0000000088827d2d103ee2d9A6b781773AE03FfB)
* **wETH** wrapped ETH (MTS) -
[`0x83A15000b753AC0EeE06D2Cb41a69e76D0D5c7F7`](https://blockscout.injective.network/address/0x83A15000b753AC0EeE06D2Cb41a69e76D0D5c7F7)
* **USDC** USD Coin (MTS) -
See [USDC on Injective](/developers-defi/usdc-stablecoin) for the addresses of USDC and USDC CCTP smart contracts.
* **USDT** USDT (MTS) -
[`0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13`](https://blockscout.injective.network/address/0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13)
* **MultiCall** -
[`0xcA11bde05977b3631167028862bE2a173976CA11`](https://blockscout.injective.network/address/0xcA11bde05977b3631167028862bE2a173976CA11)
Note that tokens that are **MTS** follow the
[MultiVM Token Standard](https://docs.injective.network/developers-evm/multivm-token-standard).
This means the same token can be used in all Injective modules (EVM, Cosmos) without using a bridge.
## More Providers
* Explorers
* Blockscout mirror: [`injective.cloud.blockscout.com`](https://injective.cloud.blockscout.com)
* JSON-RPC Providers
* Alchemy: [`alchemy.com`](https://www.alchemy.com/)
* Note that you will need to create an account on Alchemy to obtain an endpoint URL
* [Alchemy RPC Setup guide](/developers-evm/alchemy-rpc-setup)
* Quicknode [`quicknode.com/chains/inj`](https://www.quicknode.com/chains/inj)
* Note that you will need to create an account on quicknode to obtain an endpoint URL
* [Quicknode JSON-RPC documentation](https://www.quicknode.com/docs/injective/evm/eth_blockNumber)
* ThirdWeb [`thirdweb.com/injective`](https://thirdweb.com/injective)
* Note that you will need to create an account on thirdweb to obtain an endpoint URL
* [ThirdWeb Playground](https://playground.thirdweb.com/)
## Network Info
* Chain ID: `1439`
* JSON-RPC Endpoint: `https://k8s.testnet.json-rpc.injective.network/`
* EVM Gateway JSON-RPC Endpoint: `https://testnet.evm.archival.chain.virtual.json-rpc.injective.network/`
* WS Endpoint: `wss://k8s.testnet.ws.injective.network/`
* Faucet (Injective): [`testnet.faucet.injective.network/`](https://testnet.faucet.injective.network/)
* Faucet (Google): [`cloud.google.com/application/web3/faucet/injective/testnet`](https://cloud.google.com/application/web3/faucet/injective/testnet)
* Faucet (Circle): [`faucet.circle.com/`](https://faucet.circle.com/)
* Explorer: [`testnet.blockscout.injective.network/`](https://testnet.blockscout.injective.network/)
* Explorer API: `https://testnet.blockscout-api.injective.network/api`
Note that the Injective Chain ID is natively `injective-888`.
However, EVM uses a numeric chain ID of `1439`.
While these are different, they map to the **same** network.
See [Injective Testnet network information](/developers/network-information/#injective-testnet) for more details.
## Contracts
* **wINJ** wrapped INJ (MTS) -
[`0x0000000088827d2d103ee2d9A6b781773AE03FfB`](https://testnet.blockscout.injective.network/address/0x0000000088827d2d103ee2d9A6b781773AE03FfB)
* **USDC** USD Coin (MTS) -
See [USDC on Injective](/developers-defi/usdc-stablecoin) for the addresses of USDC and USDC CCTP smart contracts.
* **USDT** USDT (MTS) -
[`0xaDC7bcB5d8fe053Ef19b4E0C861c262Af6e0db60`](https://testnet.blockscout.injective.network/address/0xaDC7bcB5d8fe053Ef19b4E0C861c262Af6e0db60)
Note that tokens that are **MTS** follow the
[MultiVM Token Standard](https://docs.injective.network/developers-evm/multivm-token-standard).
This means the same token can be used in all Injective modules (EVM, Cosmos) without using a bridge.
## More Providers
* Explorers
* Blockscout mirror: [`testnet-injective.cloud.blockscout.com/`](https://testnet-injective.cloud.blockscout.com/)
* JSON-RPC Providers
* Alchemy: [`alchemy.com`](https://www.alchemy.com/)
* Note that you will need to create an account on Alchemy to obtain an endpoint URL
* [Alchemy RPC Setup guide](/developers-evm/alchemy-rpc-setup)
* Quicknode: [`quicknode.com/chains/inj`](https://www.quicknode.com/chains/inj)
* Note that you will need to create an account on Quicknode to obtain an endpoint URL
* [Quicknode JSON-RPC documentation](https://www.quicknode.com/docs/injective/evm/eth_blockNumber)
* ThirdWeb: [`thirdweb.com/injective-evm-testnet`](https://thirdweb.com/injective-evm-testnet)
* Note that you will need to create an account on ThirdWeb to obtain an endpoint URL
* [ThirdWeb Playground](https://playground.thirdweb.com/)
## More Info
For more information about Injective EVM Testnet see the following pages:
* Basics:
* [Start Building on EVM](/developers-evm)
* [Your first EVM smart contract](/developers-evm/smart-contracts)
* [Your first EVM dApp](/developers-evm/dapps)
* Advanced:
* [EVM Equivalence](/developers-evm/evm-equivalence)
* [MultiVM Token Standard](/developers-evm/multivm-token-standard)
* [Precompiles](/developers-evm/precompiles)
# Oracle Precompile
Source: https://docs.injective.network/developers-evm/oracle-precompile
Approaches for querying price data on Injective, including native oracle queries, off-chain Pyth, and on-chain Pyth via EVM.
The Oracle Precompile is not yet available.
In the meantime, you can query prices using one of the approaches described below.
Injective offers multiple ways to access oracle price data depending on your architecture and requirements.
The following approaches range from integrating with Pyth off-chain,
to reading Pyth prices on-chain through Injective's EVM.
## Approach 1: Off-chain price feeds with Pyth
If your application fetches prices off-chain (for example, in a backend service or bot),
you can query Pyth's HTTP API directly without any on-chain interaction.
Refer to the Pyth documentation on [fetching price updates](https://docs.pyth.network/price-feeds/core/fetch-price-updates)
for details on available endpoints, request formats, and response schemas.
This approach is ideal for off-chain trading systems, analytics dashboards,
or any service that requires price data without submitting transactions.
## Approach 2: On-chain price feeds with Pyth (EVM)
For smart contracts that need to read prices on-chain, you can interact with the Pyth contract deployed on Injective's EVM.
This uses Pyth's pull-based oracle model, where price updates are fetched off-chain and submitted on-chain before reading.
### Contract addresses
To obtain the Pyth contract address for Injective EVM, refer to the Pyth contract addresses page and locate the **Injective EVM** entry:
* [Mainnet contract addresses](https://docs.pyth.network/price-feeds/core/contract-addresses/evm#mainnets)
* [Testnet contract addresses](https://docs.pyth.network/price-feeds/core/contract-addresses/evm#testnets)
Currently, Pyth is deployed at `0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320` on Injective's EVM mainnet.
Always consult the official documentation to obtain up-to-date addresses.
### Price feed IDs
Each asset pair has a unique price feed ID. You can look up feed IDs on
the [Pyth price feed IDs](https://docs.pyth.network/price-feeds/core/price-feeds/price-feed-ids) page.
For example, search for `INJ/USD`.
### Supported feeds
| Asset | Price Feed ID |
| -------- | -------------------------------------------------------------------- |
| INJ/USD | `0x7a5bc1d2b56ad029048cd63964b3ad2776eadf812edc1a43a31406cb54bff592` |
| BTC/USD | `0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43` |
| ETH/USD | `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace` |
| XRP/USD | `0xec5d399846a9209f3fe5881d70aae9268c94339ff9817e8d18ff19fa05eea1c8` |
| USDT/USD | `0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b` |
| USDC/USD | `0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a` |
Always consult the official documentation to obtain up-to-date addresses.
### Integration guide
For a complete walkthrough of integrating Pyth price feeds into your Solidity contracts using the pull model,
follow the [Pyth EVM pull integration tutorial](https://docs.pyth.network/price-feeds/core/use-real-time-data/pull-integration/evm).
### Example
The following example demonstrates how to read the INJ/USD price from the Pyth contract on Injective's EVM:
```typescript theme={null}
import { ethers } from "ethers";
import { toHumanReadable } from "@injectivelabs/utils";
const provider = new ethers.JsonRpcProvider(
"https://sentry.evm-rpc.injective.network",
);
const IPythABI = [
{
inputs: [{ internalType: "bytes[]", name: "updateData", type: "bytes[]" }],
name: "getUpdateFee",
outputs: [{ internalType: "uint256", name: "feeAmount", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "bytes[]", name: "updateData", type: "bytes[]" }],
name: "updatePriceFeeds",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{ internalType: "bytes32", name: "id", type: "bytes32" },
{ internalType: "uint256", name: "age", type: "uint256" },
],
name: "getPriceNoOlderThan",
outputs: [
{
components: [
{ internalType: "int64", name: "price", type: "int64" },
{ internalType: "uint64", name: "conf", type: "uint64" },
{ internalType: "int32", name: "expo", type: "int32" },
{ internalType: "uint256", name: "publishTime", type: "uint256" },
],
internalType: "struct PythStructs.Price",
name: "price",
type: "tuple",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "bytes32", name: "id", type: "bytes32" }],
name: "getPriceUnsafe",
outputs: [
{
components: [
{ internalType: "int64", name: "price", type: "int64" },
{ internalType: "uint64", name: "conf", type: "uint64" },
{ internalType: "int32", name: "expo", type: "int32" },
{ internalType: "uint256", name: "publishTime", type: "uint256" },
],
internalType: "struct PythStructs.Price",
name: "price",
type: "tuple",
},
],
stateMutability: "view",
type: "function",
},
] as const;
const pyth = new ethers.Contract(
"0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320",
IPythABI,
provider,
);
async function main() {
try {
const priceFeedId =
"0x7a5bc1d2b56ad029048cd63964b3ad2776eadf812edc1a43a31406cb54bff592"; // INJ/USD
// const price = await pyth.getPriceUnsafe(priceFeedId);
const maxAge = 60; // 60s
const price = await pyth.getPriceNoOlderThan(priceFeedId, maxAge);
console.log(
"Human readable price:",
price.expo > 0
? toHumanReadable(price.price.toString(), price.expo.toString()).toFixed(2)
: toHumanReadable(price.price.toString(), (-price.expo).toString()).toFixed(4),
);
} catch (error) {
console.error(error);
} finally {
await provider.destroy();
}
}
main().catch(console.error);
```
`getPriceUnsafe` returns the last pushed price without a staleness check. For
production use, prefer `getPriceNoOlderThan(priceFeedId, maxAge)` — but note
this requires the price to have been pushed on-chain recently, otherwise the
call will revert.
See the [Pyth documentation](https://docs.pyth.network/price-feeds/use-real-time-data/evm)
for more details on the pull model.
# Permissioned MTS Tokens
Source: https://docs.injective.network/developers-evm/permissioned-multivm-token
Leverage Injective's permissions module in MultiVM Token Standard (MTS) tokens
## Injective's Permissions Module
The [`permissions` module](https://docs.injective.network/developers-native/injective/permissions)
is native to Injective, and allows custom management (e.g. roles) for Denoms.
This capability is extended to MultiVM Token Standard (MTS) tokens,
where you can implement those custom management rules within your EVM smart contract code.
## Why Use Permissions on MTS Tokens?
If you are tokenizing real-world assets (RWAs) using MTS on Injective,
and that underlying asset inherently requires permissions,
that is a great use case for tapping into Injective's `permissions` module.
The EVM smart contract of your MTS token simply needs to implement
an additional Solidity interface to leverage the power of the `permissions` module.
## Smart Contract Implementation
In your smart contract, import `IPermissionsHook` from `PermissionsHook.sol` and extend it.
```solidity theme={null}
interface IPermissionsHook
```
This will involve implementing the `isTransferRestricted` function,
with the following signature:
```solidity theme={null}
function isTransferRestricted(
address from,
address to,
Cosmos.Coin calldata amount
)
```
You may find the full file on Github:
[`PermissionsHook.sol`](https://github.com/InjectiveLabs/solidity-contracts/blob/master/src/PermissionsHook.sol)
### Smart Contract Example
Create a smart contract that extends `PermissionsHook`:
```solidity theme={null}
import { Cosmos } from "../src/CosmosTypes.sol";
import { PermissionsHook } from "../src/PermissionsHook.sol";
contract RestrictedAddressTransferHook is PermissionsHook {
/*
...
*/
}
```
Add a custom implementation of the `isTransferRestricted` function.
For example, this function will allow all transfers,
except for ones involving a specific address:
```solidity theme={null}
function isTransferRestricted(
address from,
address to,
Cosmos.Coin calldata amount
) external pure override returns (bool) {
address restrictedAddress = "0x...";
if (from == restrictedAddress || to == restrictedAddress) {
// this particular address is not allowed to transfer
return true;
}
// All other transfers are allowed
return false;
}
```
You may find a more detailed example of this on Github:
[`PermissionsHookExamples.sol`](https://github.com/InjectiveLabs/solidity-contracts/blob/master/examples/PermissionsHookExamples.sol)
## Registering Hook
To register the hook for the permissions,
you will need the following:
* Control of the same account that deployed the MTS token.
* The deployed address of the MTS token
* The deployed address of the Permissions Hook
With the above, you can create
[a JSON file similar to this one](https://github.com/InjectiveLabs/stablecoin-evm/blob/fiattoken-inj/scripts/demo/namespace.json).
Then run `injectived` for the registration,
using the same account that deployed the MTS token.
```shell theme={null}
injectived tx permissions create-namespace ...
```
Note that the MTS token and the Permissions Hook can have the same address.
That is an architectural decision that is up to you.
### Registering Hook Example
Create a file named `register-hooks.json`, with the following content:
```jsonc theme={null}
{
"denom": "erc20:0x...", // <-- EVM address of the MTS token
"evm_hook": "0x...", // <-- EVM address of the permissions hook
"role_permissions": [
{
"name": "EVERYONE",
"role_id": 0,
"permissions": 10
}
],
"actor_roles": [
]
}
```
Be sure to replace the value of the
`denom` and `evm_hook` fields with the appropriate values.
Delete all comments as well, so that the file is valid JSON.
Then run the following command:
```shell theme={null}
injectived tx permissions create-namespace register-hooks.json [flags]
```
Note that this is merely one specific way to define permissions hooks on an MTS token.
There are multiple variations.
For additional details on this step, including other variations, refer to
[How to Launch Permissioned Assets](https://docs.injective.network/developers-native/injective/permissions/04_launch_permissioned_asset).
## Reference Implementation
A more complete example that demonstrates the use of permissioned MTS tokens
for a stable coin is also available.
This example involves a permissions hook which prevents transfers
when the token is paused, and also maintains a blacklist of addresses.
```solidity theme={null}
function isTransferRestricted(
address _from,
address _to,
Cosmos.Coin calldata /* _amount */
) external view returns (bool) {
if (fiatToken.paused()) {
return true;
} else if (fiatToken.isBlacklisted(_from) || fiatToken.isBlacklisted(_to)) {
return true;
}
return false;
}
```
See [`PermissionsHook_Inj.sol`](https://github.com/InjectiveLabs/stablecoin-evm/blob/fiattoken-inj/contracts/v2/PermissionsHook_Inj.sol).
# Precompiles
Source: https://docs.injective.network/developers-evm/precompiles
Precompiles that expose Injective-specific modules (exchange, staking, governance) to the Injective EVM, which allows Solidity smart contracts to have direct access to Injective's powerful native modules.
## What are Precompiles on Injective?
On Injective, precompiles are special, highly-optimized smart contracts embedded directly into our
EVM (Ethereum Virtual Machine) layer at the protocol level.
Unlike standard Solidity smart contracts that are deployed by users, precompiles are part of the chain's core logic.
They are written in Go instead of Solidity and are exposed to the EVM with fixed addresses,
making them callable from your Solidity smart contracts just like any other smart contract.
Think of them as native functions of the Injective chain that have been given an Ethereum-style interface.
## Why are they necessary?
Short answer: They bridge EVM & Native Modules
The Injective EVM doesn't operate in a silo. It's deeply integrated with Injective's powerful native Cosmos SDK modules,
such as the Bank module (for token management), the Exchange module (for the on-chain order book), the Staking module, and more.
Precompiles serve as the crucial **bridge** between the EVM world (where your Solidity contracts live)
and these native Injective functionalities. Without precompiles, your EVM smart contracts would be isolated,
unable to tap into the rich features and liquidity of the broader Injective ecosystem.
For example, our [MultiVM Token Standard (MTS)](/developers-evm/multivm-token-standard) model,
which ensures unified token balances across native and EVM environments, is heavily reliant on the **Bank Precompile**.
### Benefits for Developers
* **Access to Native Features:** Directly interact with Injective's unique modules like the on-chain order book,
native staking, governance, and the bank module for MTS.
* **Enhanced Performance:** Operations executed via precompiles can be significantly faster and
more gas-efficient than trying to replicate complex native logic purely in Solidity,
as they run as optimized native code.
* **Seamless Interoperability:** Build truly integrated applications that leverage the strengths of both
the EVM and Injective's Cosmos-native capabilities.
* **Simplified Development:** Interact with complex native functionalities through familiar Solidity interfaces,
abstracting away much of the underlying Cosmos complexity.
A range of ERC-20 implementations backed by the Bank precompile, alongside precompile interfaces and abstract contracts,
are available at [Injective’s Solidity Contracts Repository](https://github.com/InjectiveLabs/solidity-contracts).
Key contracts include:
* [**Bank.sol**](https://github.com/InjectiveLabs/solidity-contracts/blob/master/src/Bank.sol) –
precompile interface
* [**BankERC20.sol**](https://github.com/InjectiveLabs/solidity-contracts/blob/master/src/BankERC20.sol) –
abstract ERC20 implementation backed by the Bank precompile
* [**FixedSupplyBankERC20.sol**](https://github.com/InjectiveLabs/solidity-contracts/blob/master/src/FixedSupplyBankERC20.sol) –
decentralized ERC20 with fixed supply (no owner, no minting or burning)
* [**MintBurnBankERC20.sol**](https://github.com/InjectiveLabs/solidity-contracts/blob/master/src/MintBurnBankERC20.sol) –
ERC20 with an owner authorized to mint and burn tokens
These implementations are based on OpenZeppelin’s ERC20 contracts.
Developers can freely create custom ERC20 contracts utilizing the Bank precompile.
### Demos to get you started
We've prepared a handful of demos that show how to build contracts using the Bank, Exchange, and Staking precompiles.
These examples also demonstrate how to interact with the Injective EVM
using the most common Ethereum development framework — **Foundry**.
By leveraging Foundry's `cast` tool,
you can easily deploy contracts and interact with the Injective chain directly from your terminal.
This enables builders to quickly experiment, test, and deploy powerful applications that tap into Injective's native modules.
Explore the demos below to see:
* How to write Solidity contracts that call precompiles for token management, trading, and staking.
* How to use Foundry scripts and `cast` commands to deploy and interact with these contracts on Injective EVM.
* Best practices for bridging EVM logic with Injective's native features.
Jump start your development by cloning the
[Injective Solidity Contracts Repository](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos) and
following the step-by-step guides in each demo directory.
* [Bank Precompile Demo](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/erc20)
* [Exchange Precompile Demo](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/exchange)
* [Staking Precompile Demo](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/staking)
### Precompile Addresses
| Name | Purpose | EVM address |
| ----------------------------------------------- | ----------------------------- | ----------- |
| [Bank](/developers-evm/bank-precompile) | Token Management | `0x64` |
| [Exchange](/developers-evm/exchange-precompile) | On-chain Order Book | `0x65` |
| Staking | Native staking token on-chain | `0x66` |
## The non-contract address error
When using Foundry, and you "fork" the Injective Mainnet or Injective Testnet locally,
and execute your smart contracts in that environment,
you may see an error similar to the following:
```text theme={null}
[Revert] call to non-contract address 0x0000000000000000000000000000000000000064
```
This occurs because Foundry is *simulating* Injective locally,
rather than actually running on Injective.
It is therefore running a *generic EVM* simulation,
and not one that is specific to Injective.
The difference lies in Injective's native functionality not being present,
and therefore it being unaware of the precompiles.
The fix for this is simple:
Use a version of Foundry that has been patched to include Injective's precompiles:
[github.com/InjectiveLabs/foundry/releases](https://github.com/InjectiveLabs/foundry/releases).
These include pre-built binaries for x86\_64 Linux and macOS ARM64.
# Set up Foundry and compile a smart contract
Source: https://docs.injective.network/developers-evm/smart-contracts/compile-foundry
## Prerequisites
Ensure that you have Foundry installed, by running the following command:
```shell theme={null}
forge --version
```
Note that the version used in this tutorial was `1.2.3-stable`. Be sure to use this version or later when following along.
If you do not have foundry yet, run the following command to install it:
```shell theme={null}
curl -L https://foundry.paradigm.xyz | bash
```
There are other options for how to install Foundry.
See the [the Foundry installation docs](https://getfoundry.sh/introduction/installation).
You will need a wallet, and an account that has been funded with some Testnet INJ.
You can request EVM testnet funds from the [Injective Testnet faucet](https://testnet.faucet.injective.network/).
After creating your account, be sure to copy your private key somewhere accessible, as you will need it to complete this tutorial.
Note that private keys should be handled with caution.
The instructions here should be considered sufficient for local development and Testnet.
However, these are **not** secure enough for private keys used on Mainnet.
Please ensure that you follow best practices for key security on Mainnet, and do not re-use the same keys/ accounts between Mainnet and other networks.
## Set up a new Foundry project
Use git to clone the demo repo, which already has the project completely set up for you.
```shell theme={null}
git clone https://github.com/injective-dev/foundry-inj
cd foundry-inj
```
Install the `forge-std` library, which provides utility functions used in this project.
```shell theme={null}
forge install foundry-rs/forge-std
```
## Orientation
Open the repo in your code editor/ IDE, and take a look at the directory structure.
```text theme={null}
foundry-inj/
src/
Counter.sol → smart contract Solidity code
test/
Counter.t.sol → test cases
foundry.toml → configuration
```
The `foundry.toml` file is already pre-configured to connect to the Injective EVM Testnet.
All you need to do before proceeding is to provide it with a private key of your Injective Testnet account.
Enter the following command to import a private key, and save it against an account named `injTest`:
```shell theme={null}
cast wallet import injTest --interactive
```
This will prompt you for the private key, and also a password that you need to enter each time you wish to use this account.
Use the private key of the account which you have just created and funded earlier (e.g. via the Injective Testnet faucet).
Note that when you type or paste text for the private key and password, nothing is shown in the terminal.
The output should look similar to this:
```text theme={null}
Enter private key:
Enter password:
`injTest` keystore was saved successfully. Address: 0x58f936cb685bd6a7dc9a21fa83e8aaaf8edd5724
```
This saves an encrypted version of the private key in `~/.foundry/keystores`,
and in subsequent commands can be accessed using the `--account` CLI flag.
## Edit the smart contract
The smart contract that is included in this demo is very basic. It:
* Stores one `value` which is a number.
* Exposes a `value()` query method.
* Exposes an `increment(num)` transaction method.
Open the file: `src/Counter.sol`
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
contract Counter {
uint256 public value = 0;
function increment(uint256 num) external {
value += num;
}
}
```
## Compile the smart contract
Run the following command:
```shell theme={null}
forge build
```
Foundry will automatically download and run the version of the Solidity compiler (`solc`) that was configured in the `foundry.toml` file.
## Check the compilation output
After the compiler completes, you should see additional directories in the project directory:
```text theme={null}
foundry-inj/
cache/
...
out/
build-info/
...
Counter.sol/
Counter.json → open this file
```
Open the `Counter.json` file (`out/Counter.sol/Counter.json`).
In it, you should see the compiler outputs, including the `abi` and `bytecode` fields.
These artifacts are used in all later steps (test, deploy, verify, and interact).
## Next steps
Now that you have set up a Foundry project and compiled a smart contract, you are ready to test that smart contract!
Check out the [test a smart contract using Foundry](./test-foundry/) tutorial next.
# Set up Hardhat and compile a smart contract
Source: https://docs.injective.network/developers-evm/smart-contracts/compile-hardhat
## Prerequisites
Ensure that you have a recent version of NodeJs installed.
You can check this using the following command:
```shell theme={null}
node -v
```
This guide was written using the following version:
```text theme={null}
v22.16.0
```
If you do not have NodeJs installed yet, do so using:
* Linux or Mac: [NVM](https://github.com/nvm-sh/nvm)
* Windows: [NVM for Windows](https://github.com/coreybutler/nvm-windows)
You will need a wallet, and an account that has been funded with some Testnet INJ.
You can request EVM testnet funds from the [Injective Testnet faucet](https://testnet.faucet.injective.network/).
After creating your account, be sure to copy your private key somewhere accessible, as you will need it to complete this tutorial.
Note that private keys should be handled with caution.
The instructions here should be considered sufficient for local development and Testnet.
However, these are **not** secure enough for private keys used on Mainnet.
Please ensure that you follow best practices for key security on Mainnet, and do not re-use the same keys/ accounts between Mainnet and other networks.
## Set up a new Hardhat project
Use git to clone the demo repo, which already has the project completely set up for you.
```shell theme={null}
git clone https://github.com/injective-dev/hardhat-inj
```
Install dependencies from npm:
```shell theme={null}
npm install
```
## Orientation
While waiting for npm to download and install, open the repo in your code editor/ IDE, and take a look at the directory structure.
```text theme={null}
hardhat-inj/
contracts/
Counter.sol → smart contract Solidity code
script/
deploy.js → deployment script
test/
Counter.test.js → test cases
hardhat.config.js → configuration
.example.env
```
The `hardhat.config.js` file is already pre-configured to connect to the Injective EVM Testnet.
All you need to do before proceeding is to provide it with a private key of your Injective Testnet account.
```shell theme={null}
cp .example.env .env
```
Edit the `.env` file to add the private key.
Optionally, you may wish to update to any alternative JSON-RPC endpoints.
```shell theme={null}
PRIVATE_KEY=your private key without 0x prefix
INJ_TESTNET_RPC_URL=https://k8s.testnet.json-rpc.injective.network/
```
## Edit the smart contract
The smart contract that is included in this demo is very basic. It:
* Stores one `value` which is a number.
* Exposes a `value()` query method.
* Exposes an `increment(num)` transaction method.
Open the file: `contracts/Counter.sol`
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
contract Counter {
uint256 public value = 0;
function increment(uint256 num) external {
value += num;
}
}
```
## Compile the smart contract
Run the following command:
```shell theme={null}
npx hardhat compile
```
Hardhat will automatically download and run the version of the Solidity compiler (`solc`) that was configured in the `hardhat.config.js` file.
## Check the compilation output
After the compiler completes, you should see additional directories in the project directory:
```text theme={null}
hardhat-inj/
artifacts/
build-info/
...
contracts/
Counter.sol/
Counter.json → open this file
...
cache/
...
```
Open the `Counter.json` file (`artifacts/contracts/Counter.sol/Counter.json`).
In it, you should see the compiler outputs, including the `abi` and `bytecode` fields.
These artifacts are used in all later steps (test, deploy, verify, and interact).
## Next steps
Now that you have set up a Hardhat project and compiled a smart contract, you are ready to test that smart contract!
Check out the [test a smart contract using Hardhat](./test-hardhat/) tutorial next.
# Deploy a smart contract using Foundry
Source: https://docs.injective.network/developers-evm/smart-contracts/deploy-foundry
## Prerequisites
You should already have a Foundry project set up, and have compiled your smart contract successfully.
See the [set up Foundry and compile a smart contract](./compile-foundry/) tutorial for how to do so.
Optionally, but strongly recommended: You should also have tested your smart contract successfully.
See the [test a smart contract using Foundry](./test-foundry/) tutorial for how to do so.
## Run the deployment
Run the following command to deploy the smart contract:
```shell theme={null}
forge create \
src/Counter.sol:Counter \
--rpc-url injectiveEvm \
--legacy \
--account injTest \
--gas-price 160000000 \
--gas-limit 2000000 \
--broadcast
```
Note that we're using the `injTest` account saved to the keystore,
which was previously set up in [set up Foundry and compile a smart contract](./compile-foundry/).
The output should look similar to:
```text theme={null}
Enter keystore password:
Deployer: 0x58f936cb685Bd6a7dC9a21Fa83E8aaaF8EDD5724
Deployed to: 0x213bA803265386C10CE04a2cAa0f31FF3440b9cF
Transaction hash: 0x6aa9022f593083c7779da014a3032efd40f3faa2cf3473f4252a8fbd2a80db6c
```
Copy the deployed address, visit [`https://testnet.blockscout.injective.network`](https://testnet.blockscout.injective.network/), and paste the address in the search field.
You'll visit the smart contract page in the block explorer for the smart contract that you have just deployed.
If you click on the "Contract" tab, you should see the EVM bytecode for that contract, and it will match the EVM bytecode found in your artifacts directory after compilation.
## Next steps
Now that you have deployed your smart contract, you are ready to verify that smart contract!
Check out the [verify a smart contract using Foundry](./verify-foundry/) tutorial next.
# Deploy a smart contract using Hardhat
Source: https://docs.injective.network/developers-evm/smart-contracts/deploy-hardhat
## Prerequisites
You should already have a Hardhat project set up, and have compiled your smart contract successfully.
See the [set up Hardhat and compile a smart contract](./compile-hardhat/) tutorial for how to do so.
Optionally, but strongly recommended: You should also have tested your smart contract successfully.
See the [test a smart contract using Hardhat](./test-hardhat/) tutorial for how to do so.
## Edit the deployment script
In order for the smart contract that you have compiled on your computer to exist on the Injective Testnet, it needs to be deployed onto the network.
To do so, we will make use of a script that uses an `ethers` instance that is pre-configured by Hardhat using the values specified in `hardhat.config.js`.
Open the file: `script/deploy.js`
```js theme={null}
async function main() {
const Counter = await ethers.getContractFactory('Counter');
const counter = await Counter.deploy({
gasPrice: 160e6,
gasLimit: 2e6,
});
await counter.waitForDeployment();
const address = await counter.getAddress();
console.log('Counter smart contract deployed to:', address);
}
```
Recall that after compiling the smart contracts, we looked at `artifacts/contracts/Counter.sol/Counter.json`? In this script, `ethers.getContractFactory('Counter')` retrieves that file, and extracts ABI and EVM bytecode from it.
The following lines use that information to construct a deployment transaction and submit it to the network.
If successful, the address at which your smart contract was deployed will be output, for example:
[`0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b`](https://testnet.blockscout.injective.network/address/0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b)
Note that on other EVM networks, transactions (including deployment transactions), do not need to specify a gas price and a gas limit. Currently, however, this is necessary on Injective.
## Run the deployment script
Run the following command to deploy the smart contract:
```shell theme={null}
npx hardhat run script/deploy.js --network inj_testnet
```
Copy the deployed address, visit [`https://testnet.blockscout.injective.network`](https://testnet.blockscout.injective.network/), and paste the address in the search field.
You'll visit the smart contract page in the block explorer for the smart contract that you have just deployed.
If you click on the "Contract" tab, you should see the EVM bytecode for that contract, and it will match the EVM bytecode found in your artifacts directory after compilation.
## Next steps
Now that you have deployed your smart contract, you are ready to verify that smart contract!
Check out the [verify a smart contract using Hardhat](./verify-hardhat/) tutorial next.
# Your First EVM Smart Contract
Source: https://docs.injective.network/developers-evm/smart-contracts/index
Step-by-step guide to creating a EVM smart contracts on Injective using Hardhat or Foundry development frameworks. Including set up, compilation, testing, deploying, verifying, and interacting with Solidity smart contracts.
Smart contracts are code that runs on a blockchain.
You can compile Solidity smart contracts,
and test, deploy, verify, and interact with them on Injective's Ethereum Virtual Machine (EVM).
You can do so using standard EVM developer tools and frameworks too.
These guides show you how to do so using Hardhat and Foundry.
## Hardhat
This guide will walk you through building an EVM Smart Contract on Injective Testnet using [Hardhat](https://hardhat.org/).
* [Set up Hardhat and compile a smart contract](/developers-evm/smart-contracts/compile-hardhat/)
* [Test a smart contract using Hardhat](/developers-evm/smart-contracts/test-hardhat/)
* [Deploy a smart contract using Hardhat](/developers-evm/smart-contracts/deploy-hardhat/)
* [Verify a smart contract using Hardhat](/developers-evm/smart-contracts/verify-hardhat/)
* [Interact with a smart contract using Hardhat](/developers-evm/smart-contracts/interact-hardhat/)
## Foundry
This guide will walk you through building an EVM Smart Contract on Injective Testnet using [Foundry](https://getfoundry.sh/).
* [Set up Foundry and compile a smart contract](/developers-evm/smart-contracts/compile-foundry/)
* [Test a smart contract using Foundry](/developers-evm/smart-contracts/test-foundry/)
* [Deploy a smart contract using Foundry](/developers-evm/smart-contracts/deploy-foundry/)
* [Verify a smart contract using Foundry](/developers-evm/smart-contracts/verify-foundry/)
* [Interact with a smart contract using Foundry](/developers-evm/smart-contracts/interact-foundry/)
## Next steps
Smart contracts do not provide a user experience for non-technical users.
To cater to them, you will need to build a decentralised application.
To do so, check out the [your first dApp](/developers-evm/dapps/) guides!
# Interact with a smart contract using Foundry
Source: https://docs.injective.network/developers-evm/smart-contracts/interact-foundry
## Prerequisites
You should already have a Foundry project set up, and have deployed your smart contract successfully.
See the [deploy a smart contract using Foundry](./deploy-foundry/) tutorial for how to do so.
Optionally, but strongly recommended: You should also have successfully verified your smart contract.
See the [verify a smart contract using Foundry](./verify-foundry/) tutorial for how to do so.
## Invoke function - query
Queries are read-only operations.
So smart contract state **is not updated**.
As *no state change* is needed, no wallets, signatures, or transaction fees (gas) are required.
Use the following command to query the `value()` function:
```shell theme={null}
cast call \
--rpc-url injectiveEvm \
${SC_ADDRESS} \
"value()"
```
Replace `${SC_ADDRESS}` with the address at which you deployed your smart contract.
For example, if the smart contract address is `0x213ba803265386c10ce04a2caa0f31ff3440b9cf`, the command is:
```shell theme={null}
cast call \
--rpc-url injectiveEvm \
0x213ba803265386c10ce04a2caa0f31ff3440b9cf \
"value()"
```
This should output the following.
```text theme={null}
0x0000000000000000000000000000000000000000000000000000000000000000
```
Note that `0x0000000000000000000000000000000000000000000000000000000000000000` means `0`.
It is the raw representation in hexadecimal for Solidity's `uint256` (the return type of the `value()` function in the smart contract).
## Invoke function - transaction
Transactions are write operations.
So smart contract **state is updated**.
As *state change* can occur, the transaction must be signed by a wallet, and transaction fees (gas) need to be paid.
Use the following command to transact the `increment(num)` function.
```shell theme={null}
cast send \
--legacy \
--rpc-url injectiveEvm \
--gas-price 160000000 \
--gas-limit 2000000 \
--account injTest \
${SC_ADDRESS} \
"increment(uint256)" \
1
```
Note that gas price is stated in *wei*.
1 wei = 10^-18 INJ.
Replace `${SC_ADDRESS}` with the address at which you deployed your smart contract.
For example, if the smart contract address is `0x213ba803265386c10ce04a2caa0f31ff3440b9cf`, the command is:
```shell theme={null}
cast send \
--legacy \
--rpc-url injectiveEvm \
--gas-price 160000000 \
--gas-limit 2000000 \
--account injTest \
0x213ba803265386c10ce04a2caa0f31ff3440b9cf \
"increment(uint256)" \
1
```
If successful, this should produce a result similar to the following:
```text theme={null}
Enter keystore password:
blockHash 0xe4c1f5faafc5365c43678135d6adc87104f0e288cddfcdffeb2f5aa08282ca22
blockNumber 83078201
contractAddress
cumulativeGasUsed 43623
effectiveGasPrice 160000000
from 0x58f936cb685Bd6a7dC9a21Fa83E8aaaF8EDD5724
gasUsed 43623
logs []
logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root
status 1 (success)
transactionHash 0x3c95e15ba24074301323e09d09d5967cc2858e255d1fdfd912758fd8bbd353b4
transactionIndex 0
type 0
blobGasPrice
blobGasUsed
to 0x213bA803265386C10CE04a2cAa0f31FF3440b9cF
```
After updating the state, you can query the new state.
The result will reflect the state change.
```shell theme={null}
cast call \
--rpc-url injectiveEvm \
${SC_ADDRESS} \
"value()"
```
This time the result should be `0x0000000000000000000000000000000000000000000000000000000000000001` because `0 + 1 = 1`.
```js theme={null}
0x0000000000000000000000000000000000000000000000000000000000000001
```
## Next steps
Congratulations, you have completed this entire guide for developing EVM smart contracts on Injective using Foundry!
Smart contracts do not provide a user experience for non-technical users.
To cater to them, you will need to build a decentralised application.
To do so, check out the [your first dApp](../dapps/) guides!
# Interact with a smart contract using Hardhat
Source: https://docs.injective.network/developers-evm/smart-contracts/interact-hardhat
## Prerequisites
You should already have a Hardhat project set up, and have deployed your smart contract successfully.
See the [deploy a smart contract using Hardhat](./deploy-hardhat/) tutorial for how to do so.
Optionally, but strongly recommended: You should also have successfully verified your smart contract.
See the [verify a smart contract using Hardhat](./verify-hardhat/) tutorial for how to do so.
## Start the Hardhat console
Use the following command to start an interactive Javascript REPL.
```shell theme={null}
npx hardhat console --network inj_testnet
```
Now the shell will be a NodeJs REPL instead of your regular shell (bash, zsh, et cetera).
In this REPL, we will create an instance of the `Counter` smart contract.
To do so, use `ethers.getContractFactory(...)` and `contract.attach('0x...');`.
For example, if the smart contract was deployed to `0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b`, the commands should look like this:
```js theme={null}
const Counter = await ethers.getContractFactory('Counter');
const counter = await Counter.attach('0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b');
```
Note that in this REPL, you will see `> ` as the shell prompt.
The results of each prompt are output without this prefix.
The contents of your terminal will therefore look similar to this:
```js theme={null}
> const Counter = await ethers.getContractFactory('Counter');
undefined
> const counter = await Counter.attach('0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b');
undefined
```
Now you can interact with the smart contract using `counter`.
## Invoke function - query
Queries are read-only operations.
So smart contract state **is not updated**.
As *no state change* is needed, no wallets, signatures, or transaction fees (gas) are required.
Use the following command to query the `value()` function.
```js theme={null}
await counter.value();
```
This should output the following.
```js theme={null}
0n
```
Note that `0n` means `0`, the `n` suffix indicates that it is
a [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
and not a [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number).
This is because Solidity's `uint256` (the return type of the `value()` function in the smart contract),
is not possible to be represented with `Number`,
as the largest possible integer value for that is `2^53 - 1`.
Thus `BigInt` needs to be used instead.
## Invoke function - transaction
Transactions are write operations.
So smart contract **state is updated**.
As *state change* can occur, the transaction must be signed by a wallet, and transaction fees (gas) need to be paid.
Use the following command to transact the `increment(num)` function.
```js theme={null}
await counter.increment(1, { gasPrice: 160e6, gasLimit: 2e6 });
```
Note that gas price is stated in *wei*.
1 wei = 10^-18 INJ.
If successful, this should produce a result similar to the following:
```js theme={null}
ContractTransactionResponse { ...
```
After updating the state, you can query the new state.
The result will reflect the state change.
```js theme={null}
await counter.value();
```
This time the result should be `1n` because `0 + 1 = 1`.
```js theme={null}
1n
```
## Stop the Hardhat console
Press `Ctrl+C` twice in a row, or enter the `.exit` command.
## Next steps
Congratulations, you have completed this entire guide for developing EVM smart contracts on Injective using Hardhat!
Smart contracts do not provide a user experience for non-technical users.
To cater to them, you will need to build a decentralised application.
To do so, check out the [your first dApp](../dapps/) guides!
# Test a smart contract using Foundry
Source: https://docs.injective.network/developers-evm/smart-contracts/test-foundry
## Prerequisites
You should already have a Foundry project set up, and have compiled your smart contract successfully.
See the [set up Foundry and compile a smart contract](./compile-foundry/) tutorial for how to do so.
## Edit the test specifications
As the smart contract we are testing is minimal, so are the test cases that it needs.
Before testing, we need to deploy the smart contract.
This happens in the `setUp` block.
This is because smart contracts cannot execute in isolation, they must be within the EVM to execute.
In Foundry, by default, the tests will execute in an emulated in-memory EVM instance, which is transient, so the deployment is perfunctory.
Open the file: `test/Counter.t.sol`
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { Test } from "forge-std/Test.sol";
import { Counter } from "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
}
function testInitialValue() public view {
assertEq(counter.value(), 0);
}
function testIncrementValueFromZero() public {
counter.increment(100);
assertEq(counter.value(), 100);
}
function testIncrementValueFromNonZero() public {
counter.increment(100);
counter.increment(23);
assertEq(counter.value(), 123);
}
}
```
We see that there are 3 test cases:
* Check the initial `value()`.
* Invoke `increment(num)` and then check that the `value()` has updated.
* Invoke `increment(num)` again, and then check that the `value()` has updated again.
## Execute tests against the smart contract
The following command runs the tests we just looked at.
```shell theme={null}
forge test
```
## Check the test output
If all the tests work as planned, you should see some output similar to the following:
```text theme={null}
Ran 3 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrementValueFromNonZero() (gas: 32298)
[PASS] testIncrementValueFromZero() (gas: 31329)
[PASS] testInitialValue() (gas: 10392)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.35ms (3.16ms CPU time)
Ran 1 test suite in 171.04ms (5.35ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)
```
## Next steps
Now that you have tested your smart contract, you are ready to deploy that smart contract!
Check out the [deploy a smart contract using Foundry](./deploy-foundry/) tutorial next.
# Test a smart contract using Hardhat
Source: https://docs.injective.network/developers-evm/smart-contracts/test-hardhat
## Prerequisites
You should already have a Hardhat project set up, and have compiled your smart contract successfully.
See the [set up Hardhat and compile a smart contract](./compile-hardhat/) tutorial for how to do so.
## Edit the test specifications
As the smart contract we are testing is minimal, so are the test cases that it needs.
Before testing, we need to deploy the smart contract.
This happens in the `before` block.
This is because smart contracts cannot execute in isolation, they must be within the EVM to execute.
In Hardhat, by default, the tests will execute in an emulated in-memory EVM instance, which is transient, so the deployment is perfunctory.
Open the file: `test/Counter.test.js`
```js theme={null}
const { expect } = require('chai');
describe('Counter', function () {
let counter;
before(async function () {
Counter = await ethers.getContractFactory('Counter');
counter = await Counter.deploy();
await counter.waitForDeployment();
});
it('should start with a count of 0', async function () {
expect(await counter.value()).to.equal(0);
});
it('should increment the count starting from zero', async function () {
await counter.increment(100);
expect(await counter.value()).to.equal(100);
});
it('should increment the count starting from non-zero', async function () {
await counter.increment(23);
expect(await counter.value()).to.equal(123);
});
});
```
We see that there are 3 test cases:
* Check the initial `value()`.
* Invoke `increment(num)` and then check that the `value()` has updated.
* Invoke `increment(num)` again, and then check that the `value()` has updated again.
## Execute tests against the smart contract
The following command runs the tests we just looked at.
```shell theme={null}
npx hardhat test
```
The following command runs the test, but **not** within the emulated EVM instance.
Instead, the smart contract is deployed to the Injective Testnet (a public network), and then tests are run against it.
This is **not recommended** in most cases, and is only needed in select/ advanced use cases.
```shell theme={null}
npx hardhat test --network inj_testnet
```
## Check the test output
If all the tests work as planned, you should see some output similar to the following:
```text theme={null}
Counter
✔ should start with a count of 0
✔ should increment the count starting from zero
✔ should increment the count starting from non-zero
3 passing (41ms)
```
This is followed by a table which includes additional reporting on gas, which is a measure of the complexity and transaction costs.
## Next steps
Now that you have tested your smart contract, you are ready to deploy that smart contract!
Check out the [deploy a smart contract using Hardhat](./deploy-hardhat/) tutorial next.
# Verify a smart contract using Foundry
Source: https://docs.injective.network/developers-evm/smart-contracts/verify-foundry
## Prerequisites
You should already have a Foundry project set up, and have deployed your smart contract successfully.
See the [deploy a smart contract using Foundry](./deploy-foundry/) tutorial for how to do so.
## What is smart contract verification?
The process of verification does not have any effect on the smart contract itself, or any other state of the network.
Instead, it is a standardised process through which network explorers are provided with the original source code of the smart contract deployed at a particular address. The network explorer **independently compiles** that source code, and verifies that the resultant bytecode is indeed a **match** with the bytecode present from the smart contract's deployment transaction.
If verification passes (there is a match), the block explorer "unlocks" an enhanced mode within for that particular smart contract's page.
More smart contract details are now displayed, including:
* Full source code (Solidity)
* ABI (JSON)
* Transactions and events are shown with higher detail (parsed using ABI)
Additionally, if the user connects their wallet, they can invoke functions within the network explorer itself to query the smart contract, and even send transactions to update its state.
## Run the verification command
Enter the following command:
```shell theme={null}
forge verify-contract \
--rpc-url injectiveEvm \
--verifier blockscout \
--verifier-url 'https://testnet.blockscout-api.injective.network/api/' \
${SC_ADDRESS} \
src/Counter.sol:Counter
```
Replace `${SC_ADDRESS}` with the address at which you deployed your smart contract.
For example, if the smart contract address is `0x213bA803265386C10CE04a2cAa0f31FF3440b9cF`, the command is:
```shell theme={null}
forge verify-contract \
--rpc-url injectiveEvm \
--verifier blockscout \
--verifier-url 'https://testnet.blockscout-api.injective.network/api/' \
0x213bA803265386C10CE04a2cAa0f31FF3440b9cF \
src/Counter.sol:Counter
```
## Check the verification outcome
You should see output similar to this in the terminal:
```text theme={null}
Start verifying contract `0x213bA803265386C10CE04a2cAa0f31FF3440b9cF` deployed on 1439
Submitting verification for [src/Counter.sol:Counter] 0x213bA803265386C10CE04a2cAa0f31FF3440b9cF.
Submitted contract for verification:
Response: `OK`
GUID: `213ba803265386c10ce04a2caa0f31ff3440b9cf686b778c`
URL: https://testnet.blockscout-api.injective.network/address/0x213ba803265386c10ce04a2caa0f31ff3440b9cf
```
The more interesting outcome is visiting the network explorer.
Visit the network explorer URL from the verification output.
Then select the "Contract" tab.
Then select the "Code" sub-tab.
Previously, there was only "ByteCode" available,
and now "Code", "Compiler", and "ABI" are also available.
Still within the "Contract" tab,
select the "Read/Write contract" sub-tab.
Previously, this did not exist,
but now you can interact with every smart contract function directly from the block explorer.
## Next steps
Now that you have deployed and verified your smart contract, you are ready to interact with that smart contract!
Check out the [interact with a smart contract using Foundry](./interact-foundry/) tutorial next.
# Verify a smart contract using Hardhat
Source: https://docs.injective.network/developers-evm/smart-contracts/verify-hardhat
## Prerequisites
You should already have a Hardhat project set up, and have deployed your smart contract successfully.
See the [deploy a smart contract using Hardhat](./deploy-hardhat/) tutorial for how to do so.
## What is smart contract verification?
The process of verification does not have any effect on the smart contract itself, or any other state of the network.
Instead, it is a standardised process through which network explorers are provided with the original source code of the smart contract deployed at a particular address. The network explorer **independently compiles** that source code, and verifies that the resultant bytecode is indeed a **match** with the bytecode present from the smart contract's deployment transaction.
If verification passes (there is a match), the block explorer "unlocks" an enhanced mode within for that particular smart contract's page.
More smart contract details are now displayed, including:
* Full source code (Solidity)
* ABI (JSON)
* Transactions and events are shown with higher detail (parsed using ABI)
Additionally, if the user connects their wallet, they can invoke functions within the network explorer itself to query the smart contract, and even send transactions to update its state.
## Edit smart contract verification configuration
Open `hardhat.config.js`, and look at the `etherscan` and `sourcify` elements.
```js theme={null}
etherscan: {
apiKey: {
inj_testnet: 'nil',
},
customChains: [
{
network: 'inj_testnet',
chainId: 1439,
urls: {
apiURL: 'https://testnet.blockscout-api.injective.network/api',
browserURL: 'https://testnet.blockscout.injective.network/',
},
},
],
},
sourcify: {
enabled: false,
},
```
Sourcify and Etherscan are two popular block explorers, each with a different API for verification.
Injective uses Blockscout, which is compatible with the Etherscan API.
Hence, Sourcify is disabled in the configuration.
Within the Etherscan configuration, the `apiKey` value is not needed, so any non-empty value is OK.
The `inj_testnet` network within `customChains` is already configured with the appropriate values for Injective Testnet.
## Run the verification command
Enter the following command:
```shell theme={null}
npx hardhat verify --network inj_testnet ${SC_ADDRESS}
```
Replace `${SC_ADDRESS}` with the address at which you deployed your smart contract.
For example, if the smart contract address is `0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b`, the command is:
```shell theme={null}
npx hardhat verify --network inj_testnet 0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b
```
## Check the verification outcome
You should see output similar to this in the terminal:
```text theme={null}
Successfully submitted source code for contract
contracts/Counter.sol:Counter at 0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b
for verification on the block explorer. Waiting for verification result...
Successfully verified contract Counter on the block explorer.
https://testnet.blockscout.injective.network/address/0x98798cc92651B1876e9Cc91EcBcfe64cac720a1b#code
```
The more interesting outcome is visiting the network explorer.
Visit the network explorer URL from the verification output.
Then select the "Contract" tab.
Then select the "Code" sub-tab.
Previously, there was only "ByteCode" available, and now "Code", "Compiler", and "ABI" are also available.
Still within the "Contract" tab,
select the "Read/Write contract" sub-tab.
Previously, this did not exist,
but now you can interact with every smart contract function directly from the block explorer.
## Next steps
Now that you have deployed and verified your smart contract, you are ready to interact with that smart contract!
Check out the [interact with a smart contract using Hardhat](./interact-hardhat/) tutorial next.
# Wrapped INJ (wINJ)
Source: https://docs.injective.network/developers-evm/wrapped-inj
# Wrapped INJ (wINJ)
## What is a wrapped cryptocurrency?
On Injective, INJ is the cryptocurrency, which is what is used to pay transaction fees on the network.
However, some dApps (including DEXes), only accept ERC20 tokens in their interfaces, and thus INJ does **not** work with them.
The solution is to create an ERC20 token which wraps INJ, called "wrapped INJ".
Its token symbol is **wINJ**.
Thus any dApp that accepts ERC20 tokens accepts wINJ.
The mechanism with which the wINJ token works is straightforward:
* Mint: Increase the total supply whenever depositing INJ into it.
* Burn: Decrease the total supply whenever withdrawing INJ from it.
You may think of wINJ as an ERC20 token that is 1-to-1 collateralised with INJ, and therefore be treated as equal value but with a different technical interface.
## Is wINJ the same as wETH?
For those of you familiar with Ethereum,
you may be thinking that this sounds the same as wrapper Ether (wETH).
You are right, so far wINJ behaves in the same way as wETH.
However, note that the Injective network is designed with a MultiVM technical architecture.
This means that if wINJ were to be implemented using a *standard* ERC20 implementation,
as wETH does, wINJ would **not** be accessible when interacting with
the the non-EVM parts of the Injective network (e.g. Cosmos transactions).
This is precisely the type of limitation that Injective's
[MultiVM Token Standard (MTS)](/developers-evm/multivm-token-standard)
was designed for.
Specifically, note
[this line](https://github.com/InjectiveLabs/solidity-contracts/blob/b152129a/src/WINJ9.sol#L9C10-L9C15):
```solidity theme={null}
contract WINJ9 is BankERC20, IWINJ9 {
```
Instead of storing balances as `uint256` values within the smart contract,
as is typical of ERC20 implementations,
the wINJ smart contract uses the `Bank` precompile.
The magic happens in the
[`_update` function of `BankERC20`](https://github.com/InjectiveLabs/solidity-contracts/blob/b152129a/src/BankERC20.sol#L50-L81),
where `mint`, `burn`, or `transfer` functions in the `Bank` module are invoked,
via its [EVM precompile](/developers-evm/bank-precompile "EVM precompile for Injective's native Bank module").
As those balances are stored/ retrieved from the `Bank` precompile,
they are accessible from elsewhere within Injective's MultiVM architecture.
For example, using the Cosmos SDK you can query the wINJ balances,
even after updating them through EVM transactions;
and vice versa.
We refer to this as "native chain balances".
Check out a [full demo of wINJ](https://github.com/InjectiveLabs/solidity-contracts/tree/master/demos/winj9) in action.
## How to use wINJ programmatically
* Address on Injective Mainnet: `0x0000000088827d2d103ee2d9A6b781773AE03FfB`
* Address on Injective Testnet: `0x0000000088827d2d103ee2d9A6b781773AE03FfB`
To convert INJ to wINJ, invoke the `deposit` function on this smart contract:
* The function signature is: `deposit() public payable`
* Note that you do not need to specify the amount as a parameter,
instead set the `value` on the transactions, and `payable` will pick it up as `msg.value`.
To convert wINJ to INJ, invoke the `withdraw` function on this smart contract:
* The function signature is: `withdraw(uint256 wad) public`
* Set the amount of INJ you intend to received as the `wad` parameter.
All other functions, e.g. transfers, are the same as standard ERC20.
## How to use wINJ via the network explorer
* Explorer URL for Injective Mainnet: [`0x0000000088827d2d103ee2d9A6b781773AE03FfB`](https://blockscout.injective.network/address/0x0000000088827d2d103ee2d9A6b781773AE03FfB?tab=contract)
* Explorer URL for Injective Testnet: [`0x0000000088827d2d103ee2d9A6b781773AE03FfB`](https://testnet.blockscout.injective.network/address/0x0000000088827d2d103ee2d9A6b781773AE03FfB?tab=contract)
To convert INJ to wINJ, invoke the `deposit` function on this smart contract:
* Navigate to the "Contract" tab, then the "Read/Write contract" sub tab in the explorer page for the wINJ token.
* Find the `deposit()` function, and expand it by clicking on the `>` symbol
* Fill in the field "Send native INJ (uint256)" with the amount of INJ you wish to convert
* Note that this number is automatically multiplied by `10^18`, you do not need to perform that conversion manually
* Press the "Write" button
* In your wallet, confirm the transaction to sign and submit it.
* Your wallet should reflect an INJ decrease and a wINJ increase by the amount you selected.
* Note that the INJ decrease will be marginally more, because it is used to pay for transaction fees.
To convert wINJ to INJ, invoke the `withdraw` function on this smart contract:
* Navigate to the "Contract" tab, then the "Read/Write contract" sub tab in the explorer page for the wINJ token.
* Find the `withdraw()` function, and expand it by clicking on the `>` symbol
* Fill in the field "wad (uint256)" with the amount of wINJ you wish to convert
* Note that this number is automatically multiplied by `10^18`, you do not need to perform that conversion manually
* Press the "Write" button
* In your wallet, confirm the transaction to sign and submit it.
* Your wallet should reflect an INJ increase and a wINJ decrease by the amount you selected.
* Note that the INJ increase will be marginally less, because it is used to pay for transaction fees.
# How to use wINJ via Injective Do
* Visit [Injective Do](https://do.injective.network/)
* Press the "Connect" button in the top right corner
* Select your wallet
* In your wallet select "Allow" to allow it to connect ot the Injective Do dApp.
* You should now see your wallet address apear in the top right corner (where the "Connect" button was previously)
* In the nav bar at the top, select "EVM"
* In the drop-down menu select "Wrap/Unwrap"
* To convert INJ to wINJ
* Press the "Wrap" tab at the top
* In the "Amount" field, type your desired amount for conversion
* Press the "Wrap" button at the bottom
* When the transaction has completed, check your INJ and wINJ balances in your wallet
* To convert wINJ to INJ
* Press the "Unwrap" tab at the top
* In the "Amount" field, type your desired amount for conversion
* Press the "Unwrap" button at the bottom
* When the transaction has completed, check your INJ and wINJ balances in your wallet
# Ethereum Bridge
Source: https://docs.injective.network/developers-native/bridges/ethereum
The Injective Ethereum bridge enables the Injective Chain to support a trustless, on-chain bidirectional token bridge. In this system, holders of ERC-20 tokens on Ethereum can instantaneously convert their ERC-20 tokens to Cosmos-native coins on the Injective Chain and vice-versa.
The Injective Peggy bridge consists of three main components:
1. Peggy Contract on Ethereum
2. Peggo Orchestrator
3. Peggy Module on the Injective Chain
## Peggy Contract
The function of the Peggy contract is to facilitate efficient, bidirectional cross-chain transfers of ERC-20 tokens from Ethereum to the Injective Chain. Unlike other token bridge setups, the Injective Peggy bridge is a decentralized, non-custodial bridge operated solely by the validators on Injective. The bridge is secured by the proof of stake security of the Injective Chain, as deposits and withdrawals are processed in accordance with attestations made by at least two-thirds of the validators based on consensus staking power.
## Peggo Orchestrator
The orchestrator is an off-chain relayer that every Injective Chain validator operates which serves the function of transmitting ERC-20 token transfer data from Ethereum to the Injective Chain.
## Peggy Module
On a basic level, the Peggy module mints new tokens on the Injective Chain upon an ERC-20 deposit from Ethereum and burns tokens upon withdrawing a token from the Injective Chain back to Ethereum. The Peggy module also manages the economic incentives to ensure that validators act honestly and efficiently, through a variety of mechanisms including slashing penalties, native token rewards, and withdrawal fees.
## From Ethereum to Injective
To transfer from Ethereum to Injective you have to make a Web3 Transaction and interact with the Peggy contract on Ethereum. There are two steps required to make a transfer:
1. As we are basically locking our ERC20 assets on the Peggy Contract which lives on Ethereum, we need to set an allowance for the assets we are transferring to the Peggy Contract. You can use any web3 provider (such as ethers.js or web3.js) to call the ERC-20 `approve` function with the Peggy contract address as the spender.
2. After the allowance is set, we need to call the `sendToInjective` function on the Peggy Contract with the desired amount and asset that we want to transfer to the Injective Chain, You can use the `PeggyContract` class from `@injectivelabs/contracts` (see example below) or interact with the contract directly using the ABI. Once the transaction is confirmed, it’ll take a couple of minutes for the assets to show on the Injective Chain.
Couple of notes about the examples above:
* The destination address (if you want to build the transaction yourself) is in the following format
```ts theme={null}
"0x000000000000000000000000{ETHEREUM_ADDRESS_HERE_WITHOUT_0X_PREFIX}";
// example
"0x000000000000000000000000e28b3b32b6c345a34ff64674606124dd5aceca30";
```
where the Ethereum address is the corresponding Ethereum address of the destination Injective address.
* `walletStrategy` `walletStrategy` is an abstraction that we’ve built which supports a lot of wallets which can be used to sign and broadcast transactions (both on Ethereum and on the Injective Chain), more details can be found in the documentation of the npm package [@injectivelabs/wallet-strategy](https://github.com/InjectiveLabs/injective-ts/tree/master/packages/wallets/wallet-strategy) and [@injectivelabs/wallet-core](https://github.com/InjectiveLabs/injective-ts/tree/master/packages/wallets/wallet-core). Obviously, this is just an example and you can use the web3 package directly, or any web3 provider to handle the transaction.
```ts theme={null}
import { PeggyContract } from "@injectivelabs/contracts";
const contract = new PeggyContract({
ethereumChainId,
address: peggyContractAddress,
web3: web3 as any,
});
```
* The snippet below instantiates a PeggyContract instance which can easily `estimateGas` and `sendTransaction` using the `web3` we provide to the contract’s constructor. Its implementation can be found [here](https://github.com/InjectiveLabs/injective-ts/blob/master/packages/contracts/src/contracts/Peggy.ts). Obviously, this is just an example and you can use the web3 package directly + the ABI of the contract to instantiate the contract, and then handle the logic of signing and broadcasting the transaction using some web3 provider.
## From Injective to Ethereum
Now that you have the ERC20 version of INJ transferred over to Injective, the native `inj` denom on the Injective Chain is minted and it is the canonical version of the INJ token. To withdraw `inj` from Injective to Ethereum we have to prepare, sign and then broadcast a native Cosmos transaction on the Injective Chain.
If you are not familiar with how Transactions (and Messages) work on Cosmos you can find more information here. The Message we need to pack into a transaction to instruct Injective to withdraw funds from Injective to Ethereum is `MsgSendToEth`.
When `MsgSendToEth` is called on the chain, some of the validators will pick up the transaction, batch multiple `MsgSendToEth` requests into one and: burn the assets being withdrawn on Injective, unlock these funds on the Peggy Smart Contract on Ethereum and send them to the respective address.
There is a bridgeFee included in these transactions to incentivize Validators to pick up and process your withdrawal requests faster. The bridgeFee is in the asset the user wants to withdraw to Ethereum (if you withdraw INJ you have to pay the bridgeFee in INJ as well).
Here is an example implementation that prepares the transaction, uses a privateKey to sign it and finally, broadcasts it to Injective:
```ts theme={null}
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import {
TxClient,
TxRestApi,
createTransaction,
} from "@injectivelabs/sdk-ts/core/tx";
import {
PrivateKey,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
MsgSendToEth,
} from "@injectivelabs/sdk-ts/core/modules";
import {
ChainRestAuthApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { toChainFormat, getDefaultStdFee } from "@injectivelabs/utils";
/** MsgSendToEth Example */
(async () => {
const network = getNetworkInfo(Network.Mainnet); // Gets the rpc/lcd endpoints
const privateKeyHash =
"f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3";
const privateKey = PrivateKey.fromPrivateKey(privateKeyHash);
const injectiveAddress = privateKey.toBech32();
const ethAddress = privateKey.toHex();
const publicKey = privateKey.toPublicKey().toBase64();
/** Account Details **/
const accountDetails = await new ChainRestAuthApi(network.rest).fetchAccount(
injectiveAddress
);
/** Prepare the Message */
const amount = {
amount: toChainFormat(0.01).toFixed(),
denom: "inj",
};
const bridgeFee = {
amount: toChainFormat(0.01).toFixed(),
denom: "inj",
};
const msg = MsgSendToEth.fromJSON({
amount,
bridgeFee,
injectiveAddress,
address: ethAddress,
});
/** Prepare the Transaction **/
const { signBytes, txRaw } = createTransaction({
message: msg,
pubKey: publicKey,
fee: getDefaultStdFee(),
sequence: parseInt(accountDetails.account.base_account.sequence, 10),
accountNumber: parseInt(
accountDetails.account.base_account.account_number,
10
),
chainId: network.chainId,
});
/** Sign transaction */
const signature = await privateKey.sign(Buffer.from(signBytes));
/** Append Signatures */
txRaw.signatures = [signature];
/** Calculate hash of the transaction */
console.log(`Transaction Hash: ${TxClient.hash(txRaw)}`);
const txService = new TxRestApi(network.rest);
/** Simulate transaction */
const simulationResponse = await txService.simulate(txRaw);
console.log(
`Transaction simulation response: ${JSON.stringify(
simulationResponse.gasInfo
)}`
);
/** Broadcast transaction */
const txResponse = await txService.broadcast(txRaw);
if (txResponse.code !== 0) {
console.log(`Transaction failed: ${txResponse.rawLog}`);
} else {
console.log(
`Broadcasted transaction hash: ${JSON.stringify(txResponse.txhash)}`
);
}
})();
```
# Bridges
Source: https://docs.injective.network/developers-native/bridges/index
This section covers bridging assets to and from Injective.
* [Ethereum Bridge](/developers-native/bridges/ethereum/) - Bridge assets between Ethereum and Injective using Peggy
# Auction
Source: https://docs.injective.network/developers-native/examples/auction
The `auction` module is heart of the `buy-back-and-burn` on chain mechanism, where 60% of the weekly trading fees are collected and auctioned off to the highest INJ bidder where the submitted INJ of the highest bidder are burned in the process.
## MsgBid
This message is used to submit a bid on the [auction](https://hub.injective.network/auction/) held weekly to allow members to use INJ to bid for the basket of trading fees (60%) collected by Injective that week.
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
import { toChainFormat } from '@injectivelabs/utils'
import { MsgBid } from '@injectivelabs/sdk-ts/core/modules'
import { MsgBroadcasterWithPk } from '@injectivelabs/sdk-ts/core/tx'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { ChainGrpcAuctionApi } from '@injectivelabs/sdk-ts/client/chain'
const endpointsForNetwork = getNetworkEndpoints(Network.Mainnet)
const auctionApi = new ChainGrpcAuctionApi(endpointsForNetwork.grpc)
const injectiveAddress = 'inj1...'
/* format amount for bid, note that bid amount has to be higher than the current highest bid */
const amount = {
denom: 'inj',
amount: toChainFormat(1).toFixed(),
}
const latestAuctionModuleState = await auctionApi.fetchModuleState()
const latestRound = latestAuctionModuleState.auctionRound
/* create message in proto format */
const msg = MsgBid.fromJSON({
amount,
injectiveAddress,
round: latestRound,
})
const privateKey = '0x...'
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
network: Network.Mainnet,
privateKey,
}).broadcast({
msgs: msg,
})
console.log(txHash)
```
## Burn Auction Deposit via MsgExternalTransfer
If you would like to grow the burn auction's pool size, you can directly send funds to the Auction subaccount.
Notes:
* You will need to send funds to the pool's subaccount `0x1111111111111111111111111111111111111111111111111111111111111111`.
* Be aware that any funds you send will be reflected in the next auction, not the current one.
* You cannot transfer from your default subaccountId since that balance is now associated with your Injective address in the bank module. Therefore, in order for `MsgExternalTransfer` to work, you will need to transfer from a non-default subaccountId.
How to find the subaccountId that you will be transferring from:
* you can query your existing subaccountIds via the [account portfolio api](../query-indexer/portfolio/).
How to use funds that are currently associated with your Injective Address in bank module:
* If you have existing non-default subaccounts, you'll want to do a[ MsgDeposit ](/developers-native/examples/exchange#msgdeposit)to one of your existing non-default subaccountIds and use that subaccountId as the `srcSubaccountId` below.
* If you don't have existing non-default subaccounts, you can do a [MsgDeposit](/developers-native/examples/exchange#msgdeposit) to a new default subaccountId, which would be done via importing `getSubaccountId` from `sdk-ts` and setting the `subaccountId` field in [MsgDeposit](/developers-native/examples/exchange#msgdeposit) to `getSubaccountId(injectiveAddress, 1)`.
For more info, check out the [burn auction pool docs](https://docs.injective.network/developers/modules/injective/auction).
```ts theme={null}
import { Network } from '@injectivelabs/networks'
import { toChainFormat } from '@injectivelabs/utils'
import { MsgBroadcasterWithPk } from '@injectivelabs/sdk-ts/core/tx'
import { MsgExternalTransfer } from '@injectivelabs/sdk-ts/core/modules'
const injectiveAddress = 'inj1...'
const srcSubaccountId = '0x...'
const POOL_SUBACCOUNT_ID = `0x1111111111111111111111111111111111111111111111111111111111111111`
// USDT Peggy token details
const USDT_DENOM = 'peggy0xdAC17F958D2ee523a2206206994597C13D831ec7'
const USDT_DECIMALS = 6
/* format amount to add to the burn auction pool */
const amount = {
denom: USDT_DENOM,
amount: toChainFormat(1, USDT_DECIMALS).toFixed(),
}
/* create message in proto format */
const msg = MsgExternalTransfer.fromJSON({
amount,
srcSubaccountId,
injectiveAddress,
dstSubaccountId: POOL_SUBACCOUNT_ID,
})
const privateKey = '0x...'
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
network: Network.Mainnet,
privateKey,
}).broadcast({
msgs: msg,
})
console.log(txHash)
```
# AuthZ
Source: https://docs.injective.network/developers-native/examples/authz
The `authz` module is an implementation of a Cosmos SDK module, per ADR 30, that allows granting arbitrary privileges from one account (the granter) to another account (the grantee).
## Messages
### MsgGrant
An authorization grant is created using the MsgGrant message. If there is already a grant for the (granter, grantee, Authorization) triple, then the new grant will overwrite the previous one. To update or extend an existing grant, a new grant with the same (granter, grantee, Authorization) triple should be created.
List of useful message types:
```
"/injective.exchange.v1beta1.MsgCreateSpotLimitOrder",
"/injective.exchange.v1beta1.MsgCreateSpotMarketOrder",
"/injective.exchange.v1beta1.MsgCancelSpotOrder",
"/injective.exchange.v1beta1.MsgBatchUpdateOrders",
"/injective.exchange.v1beta1.MsgBatchCancelSpotOrders",
"/injective.exchange.v1beta1.MsgDeposit",
"/injective.exchange.v1beta1.MsgWithdraw",
"/injective.exchange.v1beta1.MsgCreateDerivativeLimitOrder",
"/injective.exchange.v1beta1.MsgCreateDerivativeMarketOrder",
"/injective.exchange.v1beta1.MsgCancelDerivativeOrder",
"/injective.exchange.v1beta1.MsgBatchUpdateOrders",
"/injective.exchange.v1beta1.MsgBatchCancelDerivativeOrders",
"/injective.exchange.v1beta1.MsgDeposit",
"/injective.exchange.v1beta1.MsgWithdraw",
```
Per [cosmos sdk docs](https://docs.cosmos.network/main/modules/authz), "Authorizations must be granted for a particular Msg service method one by one", so the following code snippet must be repeated for each message type that you would like for the `grantee` to have authorization on behalf of a `granter`.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgGrant } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKeyOfGranter = "0x...";
const grantee = "inj...";
const granter = "inj...";
const messageType =
"/injective.exchange.v1beta1.MsgCreateSpotLimitOrder"; /* example message type */
const msg = MsgGrant.fromJSON({
messageType,
grantee,
granter,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey: privateKeyOfGranter,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgExec
When a grantee wants to execute a transaction on behalf of a granter, they must send MsgExec. In this example, we'll do a MsgSend to transfer assets from the granter's account address to another account address.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgExec, MsgSend } from "@injectivelabs/sdk-ts/core/modules";
const privateKeyOfGrantee = "0x...";
const grantee = "inj...";
const granter = "inj...";
const msgs = MsgSend.fromJSON({
amount: {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
},
srcInjectiveAddress: granter,
dstInjectiveAddress: "inj1...",
});
const msg = MsgExec.fromJSON({
msgs,
grantee,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey: privateKeyOfGrantee,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgRevoke
A grant can be removed with the MsgRevoke message.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgRevoke } from "@injectivelabs/sdk-ts/core/modules";
import { getEthereumAddress } from "@injectivelabs/sdk-ts/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKeyOfGranter = "0x...";
const grantee = "inj...";
const granter = "inj...";
const messageType =
"/injective.exchange.v1beta1.MsgCreateSpotLimitOrder"; /* example message type */
const msg = MsgRevoke.fromJSON({
messageType,
grantee,
granter,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey: privateKeyOfGranter,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Bank
Source: https://docs.injective.network/developers-native/examples/bank
The bank module is responsible for handling multi-asset coin transfers between accounts and tracking special-case pseudo-transfers which must work differently with particular kinds of accounts (notably delegating/undelegating for vesting accounts). It exposes several interfaces with varying capabilities for secure interaction with other modules which must alter user balances.
In addition, the bank module tracks and provides query support for the total supply of all assets used in the application.
## Messages
Let's explore (and provide examples) the messages that the Bank module exports and we can use to interact with the Injective chain.
### MsgSend
This message is used to send coins from one address to another. Any TokenFactory token and Peggy token can be used here.
To transfer CW20 tokens, see the `MsgExecuteContract` section in [wasm module examples](/developers-native/examples/wasm.md#msgexecutecontract-transfer).
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const amount = {
denom: "inj",
amount: toChainFormat(1).toFixed(),
};
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgMultiSend
This message is used to send to multiple recipients from multiple senders.
```typescript theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgMultiSend } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const denom = "inj";
const decimals = 18;
const records = [
/** add records here */
] as {
address: string;
amount: string /* in a human readable number */;
}[];
const totalToSend = records.reduce((acc, record) => {
return acc.plus(toChainFormat(record.amount, decimals));
}, toChainFormat(0));
const msg = MsgMultiSend.fromJSON({
inputs: [
{
address: injectiveAddress,
coins: [
{
denom,
amount: totalToSend.toFixed(),
},
],
},
],
outputs: records.map((record) => {
return {
address: record.address,
coins: [
{
amount: toChainFormat(record.amount, decimals).toFixed(),
denom,
},
],
};
}),
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Distribution
Source: https://docs.injective.network/developers-native/examples/distribution
The `distribution` module is extended from the cosmos sdk [distribution module](https://github.com/InjectiveLabs/cosmos-sdk/tree/master/x/distribution), where delegator can withdraw their staking rewards from the validator.
Distribution -> MsgWithdrawValidatorCommission
## MsgWithdrawDelegatorReward
This message is used to withdraw all available delegator staking rewards from the validator.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgWithdrawDelegatorReward } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const validatorAddress = "inj1...";
/* create message in proto format */
const msg = MsgWithdrawDelegatorReward.fromJSON({
validatorAddress,
delegatorAddress: injectiveAddress,
});
const privateKey = "0x...";
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet
}).broadcast({
msgs: msg
});
console.log(txHash);
```
## MsgWithdrawValidatorCommission
This message is used by the validator to withdraw the commission earned.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgWithdrawValidatorCommission } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const validatorAddress = "inj1...";
/* create message in proto format */
const msg = MsgWithdrawValidatorCommission.fromJSON({
validatorAddress,
});
const privateKey = "0x...";
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet
}).broadcast({
msgs: msg
});
console.log(txHash);
```
# Exchange
Source: https://docs.injective.network/developers-native/examples/exchange
The `exchange` module is the heart of the Injective Chain which enables fully decentralized spot and derivative exchange.
It is the sine qua non module of the chain and integrates tightly with the `auction`, `insurance`, `oracle`, and `peggy` modules.
The exchange protocol enables traders to create and trade on arbitrary spot and derivative markets.
The entire process of orderbook management, trade execution, order matching and settlement occurs on chain through the logic codified by the exchange module.
## Messages
Let's explore (and provide examples) the Messages that the Exchange module exports and we can use to interact with the Injective chain.
### MsgDeposit
This Message is used to send coins from the Bank module to a wallet's subaccount
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgDeposit } from "@injectivelabs/sdk-ts/core/modules";
import { getEthereumAddress } from "@injectivelabs/sdk-ts/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const amount = {
denom: "inj",
amount: toChainFormat(1).toFixed(),
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgDeposit.fromJSON({
amount,
subaccountId,
injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgWithdraw
This Message is used to send coins from the wallet's subaccount back to the users Bank funds
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgWithdraw } from "@injectivelabs/sdk-ts/core/modules";
import { getEthereumAddress } from "@injectivelabs/sdk-ts/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const amount = {
denom: "inj",
amount: toChainFormat(1).toFixed(),
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgWithdraw.fromJSON({
amount,
subaccountId,
injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgCreateSpotLimitOrder
This Message is used to create a spot limit order
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgCreateSpotLimitOrder } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import {
getEthereumAddress,
getSpotMarketTensMultiplier,
spotPriceToChainPriceToFixed,
spotQuantityToChainQuantityToFixed,
} from "@injectivelabs/sdk-ts/utils";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const feeRecipient = "inj1...";
const market = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
};
const order = {
price: 1,
quantity: 1,
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgCreateSpotLimitOrder.fromJSON({
subaccountId,
injectiveAddress,
orderType: 1 /* Buy */,
price: spotPriceToChainPriceToFixed({
value: order.price,
tensMultiplier: market.priceTensMultiplier,
baseDecimals: market.baseDecimals,
quoteDecimals: market.quoteDecimals,
}),
quantity: spotQuantityToChainQuantityToFixed({
value: order.quantity,
tensMultiplier: market.quantityTensMultiplier,
baseDecimals: market.baseDecimals,
}),
marketId: market.marketId,
feeRecipient: feeRecipient,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgCreateSpotMarketOrder
This Message is used to create a spot market order
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgCreateSpotMarketOrder } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import {
getEthereumAddress,
getSpotMarketTensMultiplier,
spotPriceToChainPriceToFixed,
spotQuantityToChainQuantityToFixed,
} from "@injectivelabs/sdk-ts/utils";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const feeRecipient = "inj1...";
const market = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
};
const order = {
price: 10,
quantity: 1,
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgCreateSpotMarketOrder.fromJSON({
subaccountId,
injectiveAddress,
orderType: 1 /* Buy */,
price: spotPriceToChainPriceToFixed({
value: order.price,
tensMultiplier: market.priceTensMultiplier,
baseDecimals: market.baseDecimals,
quoteDecimals: market.quoteDecimals,
}),
quantity: spotQuantityToChainQuantityToFixed({
value: order.quantity,
tensMultiplier: market.quantityTensMultiplier,
baseDecimals: market.baseDecimals,
}),
marketId: market.marketId,
feeRecipient: feeRecipient,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgCreateDerivativeLimitOrder
This Message is used to create a derivative limit order
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgCreateDerivativeLimitOrder } from "@injectivelabs/sdk-ts/core/modules";
import {
getEthereumAddress,
getDerivativeMarketTensMultiplier,
derivativePriceToChainPriceToFixed,
derivativeQuantityToChainQuantityToFixed,
derivativeMarginToChainMarginToFixed,
} from "@injectivelabs/sdk-ts/utils";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const feeRecipient = "inj1...";
const market = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
};
const order = {
price: 10,
quantity: 1,
margin: 10,
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgCreateDerivativeLimitOrder.fromJSON({
orderType: 1 /* Buy */,
triggerPrice: "0",
injectiveAddress,
price: derivativePriceToChainPriceToFixed({
value: order.price,
quoteDecimals: market.quoteDecimals,
}),
quantity: derivativeQuantityToChainQuantityToFixed({ value: order.quantity }),
margin: derivativeMarginToChainMarginToFixed({
value: order.margin,
quoteDecimals: market.quoteDecimals,
}),
marketId: market.marketId,
feeRecipient: feeRecipient,
subaccountId: subaccountId,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgCreateDerivativeMarketOrder
This Message is used to create a derivative market order
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgCreateDerivativeMarketOrder } from "@injectivelabs/sdk-ts/core/modules";
import {
getEthereumAddress,
getDerivativeMarketTensMultiplier,
derivativePriceToChainPriceToFixed,
derivativeQuantityToChainQuantityToFixed,
derivativeMarginToChainMarginToFixed,
} from "@injectivelabs/sdk-ts/utils";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const feeRecipient = "inj1...";
const market = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
};
const order = {
price: 10,
quantity: 1,
margin: 10,
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgCreateDerivativeMarketOrder.fromJSON({
orderType: 1 /* Buy */,
triggerPrice: "0",
injectiveAddress,
price: derivativePriceToChainPriceToFixed({
value: order.price,
tensMultiplier: market.priceTensMultiplier,
quoteDecimals: market.quoteDecimals,
}),
quantity: derivativeQuantityToChainQuantityToFixed({
value: order.quantity,
tensMultiplier: market.quantityTensMultiplier,
}),
margin: derivativeMarginToChainMarginToFixed({
value: order.margin,
quoteDecimals: market.quoteDecimals,
tensMultiplier: market.priceTensMultiplier,
}),
marketId: market.marketId,
feeRecipient: feeRecipient,
subaccountId: subaccountId,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgBatchUpdateOrders
This Message is used to batch update orders on the chain
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { OrderSide } from "@injectivelabs/sdk-ts/types";
import { MsgBatchUpdateOrders } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { GrpcOrderType } from "@injectivelabs/sdk-ts/client/chain";
import {
getEthereumAddress,
getDerivativeMarketTensMultiplier,
derivativePriceToChainPriceToFixed,
derivativeQuantityToChainQuantityToFixed,
derivativeMarginToChainMarginToFixed,
spotPriceToChainPriceToFixed,
spotQuantityToChainQuantityToFixed,
} from "@injectivelabs/sdk-ts/utils";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const feeRecipient = "inj1...";
const derivativeMarket = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getDerivativeMarketTensMultiplier */,
};
const derivativeOrder = {
price: 10,
quantity: 1,
margin: 10,
orderType: OrderSide.Buy,
};
const spotMarket = {
marketId: "0x...",
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: "" /* fetched from the chain */,
minQuantityTickSize: "" /* fetched from the chain */,
priceTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
quantityTensMultiplier:
"" /** can be fetched from getSpotMarketTensMultiplier */,
};
const spotOrder = {
price: 10,
quantity: 1,
margin: 10,
orderType: OrderSide.Buy,
};
const ethereumAddress = getEthereumAddress(injectiveAddress);
const subaccountIndex = 0;
const suffix = "0".repeat(23) + subaccountIndex;
const subaccountId = ethereumAddress + suffix;
const msg = MsgBatchUpdateOrders.fromJSON({
injectiveAddress,
subaccountId: subaccountId,
derivativeOrdersToCreate: [
{
orderType: derivativeOrder.orderType as GrpcOrderType,
price: derivativePriceToChainPriceToFixed({
value: derivativeOrder.price,
quoteDecimals: 6 /* USDT has 6 decimals */,
}),
quantity: derivativeQuantityToChainQuantityToFixed({
value: derivativeOrder.quantity,
}),
margin: derivativeMarginToChainMarginToFixed({
value: derivativeOrder.margin,
quoteDecimals: 6 /* USDT has 6 decimals */,
}),
marketId: derivativeMarket.marketId,
feeRecipient: injectiveAddress,
},
],
spotOrdersToCreate: [
{
orderType: spotOrder.orderType as GrpcOrderType,
price: spotPriceToChainPriceToFixed({
value: spotOrder.price,
baseDecimals: 18 /* INJ has 18 decimals */,
quoteDecimals: 6 /* USDT has 6 decimals */,
}),
quantity: spotQuantityToChainQuantityToFixed({
value: spotOrder.quantity,
baseDecimals: 18 /* INJ has 18 decimals */,
}),
marketId: spotMarket.marketId,
feeRecipient: injectiveAddress,
},
],
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgBatchCancelSpotOrders
This Message is used to batch cancel spot orders on the chain
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgBatchCancelSpotOrders } from "@injectivelabs/sdk-ts/core/modules";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const orders = [
{
marketId: "0x...",
subaccountId: "0x...",
orderHash: "0x...",
},
{
marketId: "0x...",
subaccountId: "0x...",
orderHash: "0x...",
},
];
const messages = orders.map((order) =>
MsgBatchCancelSpotOrders.fromJSON({
injectiveAddress,
orders: [
{
marketId: order.marketId,
subaccountId: order.subaccountId,
orderHash: order.orderHash,
},
],
})
);
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: messages,
});
console.log(txHash);
```
This Message is used to batch cancel spot orders on the chain
### MsgBatchCancelDerivativeOrders
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgBatchCancelDerivativeOrders } from "@injectivelabs/sdk-ts/core/modules";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const orders = [
{
marketId: "0x...",
subaccountId: "0x...",
orderHash: "0x...",
},
{
marketId: "0x...",
subaccountId: "0x...",
orderHash: "0x...",
},
];
const messages = orders.map((order) =>
MsgBatchCancelDerivativeOrders.fromJSON({
injectiveAddress,
orders: [
{
marketId: order.marketId,
subaccountId: order.subaccountId,
orderHash: order.orderHash,
},
],
})
);
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: messages,
});
console.log(txHash);
```
### MsgRewardsOptOut
This Message is used to opt out of the Trade & Earn program.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgRewardsOptOut } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj...";
const msg = MsgRewardsOptOut.fromJSON({ sender: injectiveAddress });
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgExternalTransfer
This message is used to transfer balance from one subaccount to another subaccount.
Note:
* You cannot transfer from your default subaccountId since that balance is now associated with your Injective address in the bank module. Therefore, in order for `MsgExternalTransfer` to work, you will need to transfer from a non-default subaccountId.
How to find the subaccountId that you will be transferring from:
* you can query your existing subaccountIds via the [account portfolio api](../query-indexer/portfolio).
How to use funds that are currently associated with your Injective Address in bank module:
* If you have existing non-default subaccounts, you'll want to do a [MsgDeposit](/developers-native/examples/exchange#msgdeposit) to one of your existing non-default subaccountIds and use that subaccountId as the `srcSubaccountId` below.
* If you don't have existing non-default subaccounts, you can do a [MsgDeposit](/developers-native/examples/exchange#msgdeposit) to a new default subaccountId, which would be done via importing `getSubaccountId` from `sdk-ts` and setting the `subaccountId` field in [MsgDeposit](/developers-native/examples/exchange#msgdeposit) to `getSubaccountId(injectiveAddress, 1)`.
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
import { Network } from "@injectivelabs/networks";
import { MsgExternalTransfer } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj...";
const srcSubaccountId = "0x...";
const dstSubaccountId = `0x...`;
// INJ token details
const INJ_DENOM = "inj";
const INJ_DECIMALS = 18;
/* format amount to add to the burn auction pool */
const amount = {
denom: INJ_DENOM,
amount: toChainFormat(1, INJ_DECIMALS).toFixed(),
};
/* create message in proto format */
const msg = MsgExternalTransfer.fromJSON({
amount,
dstSubaccountId,
srcSubaccountId,
injectiveAddress,
});
const privateKey = "0x...";
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
network: Network.Testnet,
privateKey,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Fee Grant
Source: https://docs.injective.network/developers-native/examples/feegrant
The `feegrant` module allows accounts (granters) to grant fee allowances to other accounts (grantees). This allows the grantee to use the granter's funds to pay for transaction fees.
## Messages
### MsgGrantAllowance
A fee allowance grant is created using the `MsgGrantAllowance` message. If there is already a grant for the (granter, grantee) pair, then the new grant will overwrite the previous one.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgGrantAllowance } from "@injectivelabs/sdk-ts/core/modules";
const privateKeyOfGranter = "0x...";
const date = new Date("2023-10-02T00:00:00Z");
const expiration = date.getTime() / 1000;
const granter = "inj...";
const grantee = "inj...";
const allowance = {
spendLimit: [
{
denom: "inj",
amount: "10000",
},
],
expiration,
};
const msg = MsgGrantAllowance.fromJSON({
granter,
grantee,
allowance,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey: privateKeyOfGranter,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgRevokeAllowance
A grant can be removed using the MsgRevokeAllowance message. The grantee will no longer be able to use the granter's funds to pay for transaction fees.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgRevokeAllowance } from "@injectivelabs/sdk-ts/core/modules";
const privateKey = "0x...";
const granteeAddress = "inj...";
const granterAddress = "inj...";
const params = {
grantee: granteeAddress,
granter: granterAddress,
};
const msg = MsgRevokeAllowance.fromJSON(params);
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Governance
Source: https://docs.injective.network/developers-native/examples/governance
Injective is a community-run blockchain and users who have staked INJ are able to participate in governance as it relates to the blockchain. Proposals can be submitted to make revisions to Injective programs, tech upgrades, or any other Injective related changes that impact the entire Injective ecosystem.
For every proposal you create, we require you to deposit at least 1 INJ. This is to ensure that you are an active participant of the Injective community and you are eligible to make proposals and govern the protocol moving forward. For the proposal to pass to the voting stage, it must have 500 INJ deposited. You can deposit the 500 INJ yourself or collaborate with the community to deposit them collectively.
## Messages
Let's explore (and provide examples) the messages that the Governance module exports and we can use to interact with the Injective chain. For example, you can use these messages to propose new spot, perpetual, or futures markets.
### MsgGovDeposit
This message can be used to deposit towards an existing proposal.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgGovDeposit } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const denom = "inj";
const proposalId = 12345;
const privateKey = "0x...";
const injectiveAddress = "inj...";
const amount = toChainFormat(1).toFixed();
const message = MsgGovDeposit.fromJSON({
amount: {
denom,
amount,
},
proposalId,
depositor: injectiveAddress,
});
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgVote
After the proposal is properly funded, voting can commence. You can vote "Yes", "No", "Abstain", or "No with Veto".
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { VoteOption } from "@injectivelabs/sdk-ts/types";
import { MsgVote } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const proposalId = 12345;
const privateKey = "0x...";
const injectiveAddress = "inj...";
const vote = VoteOption.VOTE_OPTION_YES;
const message = MsgVote.fromJSON({
vote,
proposalId,
voter: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgSubmitTextProposal
Propose any action on Injective. TextProposal defines a standard text proposal whose changes need to be manually updated in case of approval.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgSubmitTextProposal } from "@injectivelabs/sdk-ts/core/modules";
const denom = "inj";
const privateKey = "0x...";
const injectiveAddress = "inj...";
const amount = toChainFormat(1).toFixed();
const message = MsgSubmitTextProposal.fromJSON({
title: "Title of Proposal",
description: "Description of Proposal",
proposer: injectiveAddress,
deposit: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgSubmitProposalSpotMarketLaunch
This message allows you to propose a new spot market. Ensure that the ticker is accurate and provide the base asset denom followed by the quote asset denom. Base denom refers to the asset you would like to trade and quote denom refers to the asset by which your base asset is denominated. For instance, in the INJ/USDT market you would buy or sell INJ using USDT.
```ts theme={null}
import { TokenStaticFactory } from "@injectivelabs/sdk-ts/service";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { toChainFormat, toHumanReadable } from "@injectivelabs/utils";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { MsgSubmitProposalSpotMarketLaunch } from "@injectivelabs/sdk-ts/core/modules";
// refer to https://github.com/InjectiveLabs/injective-lists
import { tokens } from "../data/tokens.json";
const tokenStaticFactory = new TokenStaticFactory(tokens as TokenStatic[]);
const denom = "inj";
const privateKey = "0x...";
const injectiveAddress = "inj...";
const amount = toChainFormat(1).toFixed();
const market = {
baseDenom: "inj", // for example
quoteDenom: "peggy0x...",
makerFeeRate: "0.001",
takerFeeRate: "0.002",
title: "INJ/USDT Spot Market Launch",
description:
"This proposal will launch the INJ/USDT Spot Market with maker and taker fees 0.001% and 0.002% respectively",
ticker: "INJ/USDT",
minPriceTickSize: "0.001",
minQuantityTickSize: "0.001",
};
const baseDenom = tokenStaticFactory.toToken(market.baseDenom);
const quoteDenom = tokenStaticFactory.toToken(market.quoteDenom);
const marketWithDecimals: SpotMarketLaunchProposal = {
...market,
baseTokenDecimals: baseDenom ? baseDenom.decimals : 18,
quoteTokenDecimals: quoteDenom ? quoteDenom.decimals : 6,
};
const marketWithTickSizes = {
...market,
minPriceTickSize: toHumanReadable(
marketWithDecimals.minPriceTickSize,
marketWithDecimals.baseTokenDecimals - marketWithDecimals.quoteTokenDecimals
).toFixed(),
minQuantityTickSize: toChainFormat(
marketWithDecimals.minQuantityTickSize,
marketWithDecimals.baseTokenDecimals
).toFixed(),
};
const message = MsgSubmitProposalSpotMarketLaunch.fromJSON({
market: marketWithTickSizes,
proposer: injectiveAddress,
deposit: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgSubmitProposalPerpetualMarketLaunch
This message allows you to propose a new perpetual market. perpetual futures contracts, or perps, are derivative futures contracts that allow users to buy or sell the value of an underlying base asset without actually owning it. This is the message you can use to create a perp market for a specified token pair.
```ts theme={null}
import {
TokenStaticFactory,
MsgBroadcasterWithPk,
MsgSubmitProposalPerpetualMarketLaunch,
} from "@injectivelabs/sdk-ts/core/modules";
import { toChainFormat } from "@injectivelabs/utils";
import { TokenStaticFactory } from "@injectivelabs/sdk-ts/service";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
// refer to https://github.com/InjectiveLabs/injective-lists
import { tokens } from "../data/tokens.json";
const tokenStaticFactory = new TokenStaticFactory(tokens as TokenStatic[]);
const denom = "inj";
const privateKey = "0x...";
const injectiveAddress = "inj...";
const amount = toChainFormat(1).toFixed();
const market = {
title: "INJ/USDT Perpetual Market Launch",
description:
"This proposal will launch the INJ/USDT Spot Market with maker and taker fees 0.001% and 0.002% respectively",
ticker: "INJ/USDT PERP",
quoteDenom: "peggy0x...",
oracleBase: "INJ",
oracleQuote: "USDT",
oracleScaleFactor: 6,
oracleType: 10, // BAND IBC
initialMarginRatio: "0.05",
maintenanceMarginRatio: "0.02",
makerFeeRate: "0.01",
takerFeeRate: "0.02",
minPriceTickSize: "0.01",
minQuantityTickSize: "0.01",
};
const quoteDenom = await tokenStaticFactory.toToken(market.quoteDenom);
const marketWithDecimals = {
...market,
quoteTokenDecimals: quoteDenom ? quoteDenom.decimals : 6,
};
const marketWithTickSizes = {
...market,
minPriceTickSize: toChainFormat(
marketWithDecimals.minPriceTickSize,
marketWithDecimals.quoteTokenDecimals
).toFixed(),
};
const message = MsgSubmitProposalPerpetualMarketLaunch.fromJSON({
market: marketWithTickSizes,
proposer: injectiveAddress,
deposit: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgSubmitProposalExpiryFuturesMarketLaunch
An expiry futures contract is an agreement between two counterparties to buy and sell a specific amount of an underlying base asset at a specific future price, which is set to expire at a specified date in the future. This is the message you can use to create a futures market for a specified token pair.
```ts theme={null}
import {
TokenStaticFactory,
MsgBroadcasterWithPk,
MsgSubmitProposalExpiryFuturesMarketLaunch,
import { toChainFormat } from "@injectivelabs/utils";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
// refer to https://github.com/InjectiveLabs/injective-lists
import { tokens } from "../data/tokens.json";
const tokenStaticFactory = new TokenStaticFactory(tokens as TokenStatic[]);
const denom = "inj";
const injectiveAddress = "inj...";
const privateKey = "0x...";
const amount = toChainFormat(1).toFixed();
const market = {
title: "INJ/USDT Futures Market Launch",
description:
"This proposal will launch the INJ/USDT Spot Market with maker and taker fees 0.001% and 0.002% respectively",
ticker: "INJ/USDT 24-MAR-2023",
quoteDenom: "peggy0x...",
oracleBase: "INJ",
oracleQuote: "USDT",
expiry: 1000000, // when the market will expire, in ms
oracleScaleFactor: 6,
oracleType: 10, // BAND IBC
initialMarginRatio: "0.05",
maintenanceMarginRatio: "0.02",
makerFeeRate: "0.01",
takerFeeRate: "0.02",
minPriceTickSize: "0.01",
minQuantityTickSize: "0.01",
};
const quoteDenom = await tokenStaticFactory.toToken(market.quoteDenom);
const marketWithDecimals = {
...market,
quoteTokenDecimals: quoteDenom ? quoteDenom.decimals : 6,
};
const marketWithTickSizes = {
...market,
minPriceTickSize: toChainFormat(
marketWithDecimals.minPriceTickSize,
marketWithDecimals.quoteTokenDecimals
).toFixed(),
};
const message = MsgSubmitProposalExpiryFuturesMarketLaunch.fromJSON({
market: marketWithTickSizes,
proposer: injectiveAddress,
deposit: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
### MsgSubmitProposalSpotMarketParamUpdate
This message can be used to update the params of a spot market.
```ts theme={null}
import {
MsgBroadcasterWithPk,
MsgSubmitProposalSpotMarketParamUpdate,
} from "@injectivelabs/sdk-ts/core/modules";
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { GrpcMarketStatusMap } from "@injectivelabs/sdk-ts/client/chain";
const denom = "inj";
const privateKey = "0x...";
const injectiveAddress = "inj...";
const amount = toChainFormat(1).toFixed();
const market = {
title: "INJ/USDT Spot Market Launch",
description:
"This proposal will launch the INJ/USDT Spot Market with maker and taker fees 0.001% and 0.002% respectively",
marketId: "0x...",
makerFeeRate: "0.02",
takerFeeRate: "0.03",
relayerFeeShareRate: "0.4", // 40%, the percent of tsx fees that go to the relayers
minPriceTickSize: "0.002",
minQuantityTickSize: "0.002",
status: GrpcMarketStatusMap.Active,
};
const message = MsgSubmitProposalSpotMarketParamUpdate.fromJSON({
market,
proposer: injectiveAddress,
deposit: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: message,
});
```
# IBC
Source: https://docs.injective.network/developers-native/examples/ibc
## Messages
## MsgTransfer
This message is used to send coins from the sender's Bank module on Injective to the receiver's Bank module on another Cosmos chain through IBC, which is Cosmos's Inter-Blockchain Communication Protocol.
Note that Injective only supports mainnet transfers across IBC for most networks.
Application to application communication in IBC is conducted over channels, which route between an application module on one chain, and the corresponding application module on another one. More info on IBC channels can be found at [https://tutorials.cosmos.network/academy/3-ibc/3-channels.html](https://tutorials.cosmos.network/academy/3-ibc/3-channels.html).
A list of canonical channel Ids for mainnet transfers to and from Injective can be found in the [Injective Lists repository](https://github.com/InjectiveLabs/injective-lists).
Also noteworthy is that the application module on each chain has a portId to designate the type of module on each end.
For example, `transfer` is the portId designating the transfer of ICS-20 tokens between bank modules.
In this example, we will transfer ATOM from Injective to CosmosHub
For token metadata (symbols, decimals, channel mappings), please use
[Injective Lists](https://github.com/InjectiveLabs/injective-lists) which
provides up-to-date token information in JSON format.
```ts theme={null}
import { toChainFormat, toBigNumber } from "@injectivelabs/utils";
import { ChainId, CosmosChainId } from "@injectivelabs/ts-types";
import { MsgTransfer } from "@injectivelabs/sdk-ts/core/modules";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { makeTimeoutTimestampInNs } from "@injectivelabs/sdk-ts/utils";
import { ChainRestTendermintApi } from "@injectivelabs/sdk-ts/client/chain";
const injectiveChainId = CosmosChainId["Injective"];
const destinationChainId = CosmosChainId["Cosmoshub"];
const endpointsForNetwork = getNetworkEndpoints(Network.Mainnet);
/**
* For IBC transfers, you need:
* 1. The IBC denom hash for the token on Injective
* 2. The channel ID for the destination chain
*
* You can find this information in the Injective Lists repository:
* https://github.com/InjectiveLabs/injective-lists
*/
const injectiveToCosmosHubChannelId = "channel-1";
/**
* The IBC denom for ATOM on Injective
* Format: ibc/{hash} where hash is derived from the channel and base denom
*/
const atomIbcDenom =
"ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9";
/* format amount for transfer (0.001 ATOM with 6 decimals) */
const amount = {
denom: atomIbcDenom,
amount: toChainFormat(0.001, 6).toFixed(),
};
const injectiveAddress = "inj...";
const destinationAddress = "cosmos...";
const port = "transfer";
const timeoutTimestamp = makeTimeoutTimestampInNs();
/* get the latestBlock from the origin chain */
const tendermintRestApi = new ChainRestTendermintApi(endpointsForNetwork.rest);
/* Block details from the origin chain */
const latestBlock = await tendermintRestApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
30 // default block timeout height
);
/* create message in proto format */
const msg = MsgTransfer.fromJSON({
port,
memo: `IBC transfer from ${injectiveChainId} to ${destinationChainId}`,
sender: injectiveAddress,
receiver: destinationAddress,
channelId: injectiveToCosmosHubChannelId,
timeout: timeoutTimestamp,
height: {
revisionHeight: timeoutHeight.toNumber(),
revisionNumber: parseInt(latestBlock.header.version.block, 10),
},
amount,
});
const privateKey = "0x...";
/* broadcast transaction */
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Transaction Examples
Source: https://docs.injective.network/developers-native/examples/index
This section provides code examples for common transaction types on Injective.
* [Auction](/developers-native/examples/auction/) - Auction module transactions
* [Authz](/developers-native/examples/authz/) - Authorization module transactions
* [Bank](/developers-native/examples/bank/) - Bank module transactions
* [Distribution](/developers-native/examples/distribution/) - Distribution module transactions
* [Exchange](/developers-native/examples/exchange/) - Exchange module transactions
* [Feegrant](/developers-native/examples/feegrant/) - Fee grant module transactions
* [Governance](/developers-native/examples/governance/) - Governance module transactions
* [IBC](/developers-native/examples/ibc/) - IBC module transactions
* [Insurance](/developers-native/examples/insurance/) - Insurance module transactions
* [Peggy](/developers-native/examples/peggy/) - Peggy bridge transactions
* [Permissions](/developers-native/examples/permissions/) - Permissions module transactions
* [Staking](/developers-native/examples/staking/) - Staking module transactions
* [Token Factory](/developers-native/examples/token-factory/) - Token factory module transactions
* [Wasm](/developers-native/examples/wasm/) - CosmWasm transactions
# Insurance
Source: https://docs.injective.network/developers-native/examples/insurance
This module provides insurance funds for derivative markets in the exchange module of the Injective Chain to use in order to support higher leverage trading. On a high level, insurance funds for each derivative market are funded by a permissionless group of underwriters who each own a proportional claim (represented through insurance fund share tokens) over the underlying assets in the insurance fund.
## Messages
Let's explore (and provide examples) the Messages that the Insurance module exports and we can use to interact with the Injective chain.
### MsgCreateInsuranceFund
This Message is used to create an Insurance Fund
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgCreateInsuranceFund } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const amount = 5;
const fund = {
ticker: "BTC/USDT",
quoteDenom: "peggy0x...",
oracleBase: "BTC",
oracleQuote: "USDT",
oracleType: 10, // BANDIBC
};
const msg = MsgCreateInsuranceFund.fromJSON({
fund,
injectiveAddress,
deposit: {
denom: fund.quoteDenom,
amount: toChainFormat(amount, 6 /* 6 because USDT has 6 decimals */).toFixed(),
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgRequestRedemption
This Message is used to request redemption.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgRequestRedemption } from "@injectivelabs/sdk-ts/core/modules";
const marketId = "0x....";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const denom = "share1"; // the insurance fund denom (share{id})
const amount = toChainFormat(5).toFixed();
const msg = MsgRequestRedemption.fromJSON({
marketId,
injectiveAddress,
amount: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgUnderwrite
This Message is used to underwrite to an insurance fund.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgUnderwrite } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const usdtDecimals = 6;
const marketId = "0x...";
const privateKey = "0x...";
const denom = "peggy0x...";
const injectiveAddress = "inj1...";
const amount = toChainFormat(5, usdtDecimals).toFixed();
const msg = MsgUnderwrite.fromJSON({
marketId,
injectiveAddress,
amount: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Peggy
Source: https://docs.injective.network/developers-native/examples/peggy
The `peggy` module is the heart of the injective ↔ ethereum bridge, where deposited funds will be locked on the ethereum [peggy contract](https://etherscan.io/address/0xF955C57f9EA9Dc8781965FEaE0b6A2acE2BAD6f3#code) and minted on the Injective chain. Similarly withdrawal funds will be burned on the injective chain and unlocked on the ethereum peggy contract.
## Messages
### MsgSendToEth
This message is used to withdraw funds from the Injective Chain via the [peggy contract](https://etherscan.io/address/0xF955C57f9EA9Dc8781965FEaE0b6A2acE2BAD6f3#code), in the process funds will be burned on the injective chain and distributed to the ethereum address from the peggy contract.
Note that a \$10 USD bridge fee will be charged for this transaction to cover for the ethereum gas fee on top of the standard INJ transaction fee.
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
import { toBigNumber, toChainFormat } from '@injectivelabs/utils'
import { MsgSendToEth } from '@injectivelabs/sdk-ts/core/modules'
import { MsgBroadcasterWithPk } from '@injectivelabs/sdk-ts/core/tx'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TokenPrice, TokenStaticFactory } from '@injectivelabs/sdk-ts/service'
// refer to https://github.com/InjectiveLabs/injective-lists
import { tokens } from '../data/tokens.json'
export const tokenStaticFactory = new TokenStaticFactory(tokens as TokenStatic[])
const tokenPriceMap = new TokenPrice(Network.Mainnet)
const tokenService = new TokenService({
chainId: ChainId.Mainnet,
network: Network.Mainnet,
})
const ETH_BRIDGE_FEE_IN_USD = 10
const endpointsForNetwork = getNetworkEndpoints(Network.Mainnet)
const tokenSymbol = 'INJ'
const tokenMeta = tokenStaticFactory.toToken(tokenSymbol)
const amount = 1
const injectiveAddress = 'inj1...'
const destinationAddress = '0x...' // ethereum address
const tokenDenom = `peggy${tokenMeta.erc20.address}`
if (!tokenMeta) {
return
}
const tokenUsdPrice = tokenPriceMap[tokenMeta.coinGeckoId]
const amountToFixed = toChainFormat(amount, tokenMeta.decimals).toFixed()
const bridgeFeeInToken = toBigNumber(ETH_BRIDGE_FEE_IN_USD).dividedBy(tokenUsdPrice).toFixed()
const msg = MsgSendToEth.fromJSON({
injectiveAddress,
address: destinationAddress,
amount: {
denom: tokenDenom,
amount: amountToFixed,
},
bridgeFee: {
denom: tokenDenom,
amount: bridgeFeeInToken,
},
})
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet,
}).broadcast({
msgs: msg,
})
```
# Permissions
Source: https://docs.injective.network/developers-native/examples/permissions
The Permissions Module facilitates the management of namespaces, roles, and permissions within the Injective ecosystem. This documentation outlines the key message types and their usage for interacting with permissions-related data.
## Messages
Let's explore (and provide examples) the Messages that the Permissions module exports and we can use to interact with the Injective chain.
### `MsgClaimVoucher`
This message is used to claim a voucher tied to a specific address within a namespace.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgClaimVoucher } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const denom = "inj";
const msg = MsgClaimVoucher.fromJSON({
injectiveAddress,
denom,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### `MsgCreateNamespace`
This message is used to creates a new namespace with permissions and roles.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgCreateNamespace } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const secondAddress = "inj2.....";
const privateKey = "0x...";
const denom = "inj";
const wasmHook = "inj3....";
const mintsPausedValue = false;
const sendsPausedValue = false;
const burnsPausedValue = false;
const role1 = "Everyone";
const permissions1 = 1;
const msg = MsgCreateNamespace.fromJSON({
injectiveAddress,
namespace: {
denom,
wasmHook,
mintsPausedValue,
sendsPausedValue,
burnsPausedValue,
rolePermissions: {
role: role1,
permissions: permissions1,
},
addressRoles: {
address: injectiveAddress,
roles: [role1],
},
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### `MsgDeleteNamespace`
This message is used to delete an existing namespace.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgDeleteNamespace } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const denom = "inj";
const msg = MsgDeleteNamespace.fromJSON({
injectiveAddress,
denom,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### `MsgRevokeNamespaceRoles`
This message is used to revoke roles from specified addresses in a namespace.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgRevokeNamespaceRoles } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const denom = "inj";
const roles = ["role1", "role2"];
const msg = MsgRevokeNamespaceRoles.fromJSON({
injectiveAddress,
denom,
addressRolesToRevoke: {
injectiveAddress,
roles: roles,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### `MsgUpdateNamespace`
This message is used to update namespace properties like mints, sends, and burns.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgUpdateNamespace } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1..."
const privateKey = "0x...";
const denom = "inj";
const wasmHookValue = "inj2...";
const mintsPausedValue = false;
const sendsPausedValue = false;
const burnsPausedValue = true;
const msg = await new MsgUpdateNamespace.fromJSON({
injectiveAddress,
denom,
wasmHook: {
newValue: wasmHookValue
},
mintsPaused: {
newValue: mintsPausedValue;
},
sendsPaused: {
newValue: sendsPausedValue;
},
burnsPaused: {
newValue: burnsPausedValue;
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet
}).broadcast({
msgs: msg
});
console.log(txHash);
```
### `MsgUpdateNamespaceRoles`
This message is used to modify the roles and permissions for addresses in a namespace.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgUpdateNamespaceRoles } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const denom = "inj";
const role = "role1";
const permissions = 4;
const msg = MsgUpdateNamespaceRoles.fromJSON({
injectiveAddress,
denom,
rolePermissions: {
role,
permissions: permissions,
},
addressRoles: {
injectiveAddress,
roles: [role],
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Staking
Source: https://docs.injective.network/developers-native/examples/staking
The module enables Cosmos SDK-based blockchain to support an advanced Proof-of-Stake (PoS) system. In this system, holders of the native staking token of the chain can become validators and can delegate tokens to validators, ultimately determining the effective validator set for the system.
## Messages
Let's explore (and provide examples) the Messages that the Staking module exports and we can use to interact with the Injective chain.
### MsgBeginRedelegate
This Message is used to Redelegate staked INJ from one validator to another.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgBeginRedelegate } from "@injectivelabs/sdk-ts/core/modules";
const denom = "inj";
const privateKey = "0x...";
const amount = toChainFormat(5);
const injectiveAddress = "inj1...";
const sourceValidatorAddress = "inj1...";
const destinationValidatorAddress = "inj1...";
const msg = MsgBeginRedelegate.fromJSON({
injectiveAddress,
dstValidatorAddress: destinationValidatorAddress,
srcValidatorAddress: sourceValidatorAddress,
amount: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgDelegate
This Message is used to Delegate INJ to a validator.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgDelegate } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const denom = "inj";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const validatorAddress = "inj1...";
const amount = toChainFormat(5).toFixed();
const msg = MsgDelegate.fromJSON({
injectiveAddress,
validatorAddress,
amount: {
denom,
amount,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgCancelUnbondingDelegation
This message is used to cancel unbonding from a validator, reset the bonding period, and delegate back to the previous validator.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgCancelUnbondingDelegation } from "@injectivelabs/sdk-ts/core/modules";
const denom = "inj";
const delegatorAddress = "inj1...";
const privateKey = "0x...";
const amount = toChainFormat(5).toFixed();
const validatorAddress = "inj1...";
const creationHeight = "123456"; // the height at which the unbonding was initiated
const msg = MsgCancelUnbondingDelegation.fromJSON({
delegatorAddress,
validatorAddress,
amount: {
denom,
amount,
},
creationHeight,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Token Factory
Source: https://docs.injective.network/developers-native/examples/token-factory
This `tokenfactory` module allows any account to create a new token with the name `factory/{creator address}/{subdenom}`. Because tokens are namespaced by creator address, this allows token minting to be permissionless, due to not needing to resolve name collisions.
*Note: If you want your denom to be visible on products like Helix, Hub, Explorer, etc, it's important to add token metadata information using the `MsgSetDenomMetadata` message as explained below.*
*Note #2: It's recommended to change your admin to the zero address for safety and preventing supply manipulation.*
## Messages
Let's explore (and provide examples) the Messages that the TokenFactory module exports and we can use to interact with the Injective chain.
### MsgCreateDenom
Creates a denom of `factory/{creator address}/{subdenom}` given the denom creator address and the subdenom. Subdenoms can contain \[a-zA-Z0-9./]. Keep in mind that there is a `creation fee` which you need to cover when creating a new token.
Keep in mind that that the `admin` of the token can change the supply (mint or burn new tokens). Its recommended that the `admin` is unset using the `MsgChangeAdmin`, as explained below.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgCreateDenom } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = "inj-test";
const msg = MsgCreateDenom.fromJSON({
subdenom,
symbol: "InjTest",
name: "Inj Testing",
sender: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgMint
Minting of a specific denom is only allowed for the current admin. Note, the current admin is defaulted to the creator of the denom.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgMint } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = "inj-test";
const amountToMint = 1_000_000_000;
const msg = MsgMint.fromJSON({
sender: injectiveAddress,
amount: {
denom: `factory/${injectiveAddress}/${subdenom}`,
amount: amountToMint,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgBurn
The admin can burn the supply of the token factory. Everyone else can use this message to burn their funds only.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBurn } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = "inj-test";
const amountToBurn = 1_000_000_000;
const msg = MsgBurn.fromJSON({
sender: injectiveAddress,
amount: {
denom: `factory/${injectiveAddress}/${subdenom}`,
amount: amountToBurn,
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgSetDenomMetadata
Setting of metadata for a specific denom is only allowed for the admin of the denom. It allows the overwriting of the denom metadata in the bank module.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgSetDenomMetadata } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = 'inj-test'
const denom = `factory/${injectiveAddress}/${subdenom}`;
const denomUnitsIfTokenHas0Decimals = [
{
denom: denom,
exponent: 0,
aliases: [subdenom]
},
]
const denomUnitsIfTokenHas6Decimals = [
{
denom: denom, /** we use the whole denom here */
exponent: 0,
aliases: [subdenom]
},
{
denom: subdenom,
exponent: 6, /** we use the subdenom only here (if you want your token to have 6 decimals) */
aliases: []
},
]
const msg = MsgSetDenomMetadata.fromJSON({
sender: injectiveAddress,
metadata: {
base: denom, /** the base denom */
description: '', /** description of your token */
display: subdenom, /** the display alias of your token on UIs (it's the denom of the unit with highest decimals) */
name: '', /** the name of your token */
symbol: '', /** the symbol of your token */
uri: '' /** the logo of your token, should be hosted on IPFS and should be a small webp image */
denomUnits: denomUnitsIfTokenHas6Decimals /** choose if you want to have 6 or 0 decimals for the token */,
decimals: 6 /** choose if you want to have 6 or 0 decimals for the token */
}
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet
}).broadcast({
msgs: msg
});
console.log(txHash);
```
### MsgChangeAdmin
The admin of the denom can mint new supply or burn existing one. It's recommended to change the admin to the zero address as to not allow changing the token's supply.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgChangeAdmin } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = "inj-test";
const denom = `factory/${injectiveAddress}/${subdenom}`;
const msg = MsgChangeAdmin.fromJSON({
denom,
sender: injectiveAddress,
newAdmin:
"inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49" /** SET TO ZERO ADDRESS */,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
## Full Example
Here is a full example on how to create a new token, mint new tokens and set token metadata on Injective.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgSetDenomMetadata, MsgCreateDenom, MsgMint, MsgChangeAdmin } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const privateKey = "0x...";
const subdenom = 'inj-test'
const denom = `factory/${injectiveAddress}/${subdenom}`;
const amount = 1_000_000_000
const msgCreateDenom = MsgCreateDenom.fromJSON({
subdenom,
sender: injectiveAddress,
});
const msgMint = MsgMint.fromJSON({
sender: injectiveAddress,
amount: {
denom: `factory/${injectiveAddress}/${subdenom}`,
amount: amount
}
});
const msgChangeAdmin = MsgChangeAdmin.fromJSON({
denom: `factory/${injectiveAddress}/${subdenom}`,
sender: injectiveAddress,
newAdmin: 'inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49' /** SET TO ZERO ADDRESS */
});
const msgSetDenomMetadata = MsgSetDenomMetadata.fromJSON({
sender: injectiveAddress,
metadata: {
base: denom, /** the base denom */
description: '', /** description of your token */
display: '', /** the displayed name of your token on UIs */,
name: '', /** the name of your token */,
symbol: '' /** the symbol of your token */,
uri: '' /** the logo of your token, should be hosted on IPFS and should be a small webp image */
denomUnits: [
{
denom: denom,
exponent: 0,
aliases: [subdenom]
},
{
denom: subdenom,
exponent: 6, /** if you want your token to have 6 decimals */
aliases: []
},
]
}
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet
}).broadcast({
msgs: [msgCreateDenom, msgMint, msgSetDenomMetadata, msgChangeAdmin]
});
console.log(txHash);
```
# Wasm
Source: https://docs.injective.network/developers-native/examples/wasm
The `wasm` module is the heart of interacting with the wasm smart contracts deployed on the injective chain, here you can find a list of [smart contracts](https://injscan.com/smart-contracts/) that are deployed on the Injective chain.
`MsgUpdateCode` and `MsgStoreCode` are not supported by Ethereum (ex:
Metamask) wallets.
## Messages
### MsgExecuteContract (Transfer)
This message is used to execute contract function, below we will use the [CW20 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md) transfer message as an example.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgExecuteContract } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const recipientAddress = "inj2...";
const contractAddress = "cw...";
const msg = MsgExecuteContract.fromJSON({
contractAddress,
sender: injectiveAddress,
exec: {
action: "transfer",
msg: {
recipient: recipientAddress,
amount: 100000,
},
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgExecuteContract (funds example)
In some scenarios, depending on the smart contract's function we have to transfer tokens to the smart contract, following cosmwasm convention, we use the funds field to transfer tokens to the smart contract from the user's bank module.
Below is an example of how we can send the `MsgExecuteContract` using an `test` contract function.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgExecuteContract } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const contractAddress = "cw...";
const msg = MsgExecuteContract.fromJSON({
contractAddress,
sender: injectiveAddress,
exec: {
action: "test",
funds: [
{
denom: "inj",
amount: toChainFormat(1).toFixed(),
},
],
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
### MsgExecuteContractCompat
There are some compatibility issues parsing the `funds` array and `msgs` object in the previous example with EIP712. Since `MsgExecuteContract` can't be properly converted to EIP712 and then signed by Ethereum wallets, we introduced `MsgExecuteContractCompat` which is fully compatible with EIP712.
***Note:*** *`MsgExecuteContract` and `MsgExecuteContractCompat` underlying messages are the same. `MsgExecuteContractCompat` is just EIP712 compatible.*
Below is an example of how we can send the `MsgExecuteContractCompact` using an `test` contract function.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgExecuteContractCompat } from "@injectivelabs/sdk-ts/core/modules";
const injectiveAddress = "inj1...";
const contractAddress = "cw...";
const msg = MsgExecuteContractCompat.fromJSON({
contractAddress,
sender: injectiveAddress,
exec: {
action: "test",
funds: [
{
denom: "inj",
amount: toChainFormat(1).toFixed(),
},
],
},
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Mainnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Overview
Source: https://docs.injective.network/developers-native/index
This section is for developers building on Injective using native Cosmos SDK modules and Go-based tooling.
Whether you're developing custom modules, querying chain state, constructing transactions,
or integrating with Injective's core financial primitives,
you'll find comprehensive technical documentation for the native development path.
Injective extends the Cosmos SDK with purpose-built modules for decentralized finance,
creating a modular architecture where each component handles specific responsibilities while inter-operating seamlessly.
The `exchange` module, Injective's centerpiece, enables fully on-chain orderbook trading that rivals centralized exchanges,
while modules like `tokenfactory` allow permissionless asset creation and `peggy` facilitates trustless Ethereum bridging.
This modular design allows developers to compose powerful applications by leveraging existing infrastructure: staking for consensus,
governance for protocol upgrades, and the native bank module for unified token balances across EVM and Cosmos environments.
For developers, this means access to institutional-grade financial primitives through clean Go APIs, gRPC endpoints,
and comprehensive message types that can be composed into sophisticated trading strategies and DeFi applications.
## Key Concepts
| Term | Definition |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cosmos SDK Module** | Self-contained units of blockchain logic (e.g., `exchange`, `bank`, `staking`) that provide specific functionalities and communicate through inter-module messaging. |
| **Exchange Module** | Injective's core module enabling fully decentralized spot and derivative trading with on-chain orderbook management, order matching, and settlement. |
| **Keeper** | Core component of a module that manages state and exposes business logic through defined interfaces, handling read/write operations and state transitions. |
| **Message (Msg)** | Transaction types that trigger state changes in modules, such as `MsgSend` for transfers or `MsgCreateSpotLimitOrder` for trading. |
| **Query** | Read-only operations that retrieve chain state without modifying it, exposed through gRPC, REST, and CLI interfaces. |
## Explore Modules
`injective` module. Injective-specific modules including Exchange (on-chain orderbook), TokenFactory (permissionless token creation), Peggy (Ethereum bridge), Oracle (price feeds), Insurance Funds, Auction, and WASMx (CosmWasm integration).
`core` module. Standard Cosmos SDK modules including Bank (token transfers), Staking (validator delegation), Governance (on-chain proposals), Auth (accounts), Distribution (rewards), and IBC (cross-chain interoperability).
## Additional Resources
* **[Transaction Examples](/developers-native/examples/)** —
Code samples for common operations across all modules including bank transfers, exchange trading, staking, governance,
and IBC transfers.
* **[Query Chain](/developers-native/query-chain/)** —
Reference for querying chain state directly via gRPC and RPC endpoints for all modules.
* **[Query Indexer](/developers-native/query-indexer/)** —
Guide to using the Injective Indexer for efficient historical data queries including trades, orders, and portfolio data.
* **[Transactions](/developers-native/transactions/)** —
Documentation for constructing and broadcasting transactions using various signer types (Ethereum, Cosmos, private keys).
# Auction
Source: https://docs.injective.network/developers-native/query-chain/auction
Example code snippets to query the auction module on the chain.
## Using gRPC
### Fetch module params such as the auction period
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcAuctionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcAuctionApi = new ChainGrpcAuctionApi(endpoints.grpc);
const moduleParams = await chainGrpcAuctionApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch the state of the current auction, such as the latest round
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcAuctionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcAuctionApi = new ChainGrpcAuctionApi(endpoints.grpc);
const latestAuctionModuleState = await chainGrpcAuctionApi.fetchModuleState();
console.log(latestAuctionModuleState);
```
### Fetch the current auction basket and get info such as the highest bidder and amount
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcAuctionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcAuctionApi = new ChainGrpcAuctionApi(endpoints.grpc);
const currentBasket = await chainGrpcAuctionApi.fetchCurrentBasket();
console.log(currentBasket);
```
# Querying the Chain
Source: https://docs.injective.network/developers-native/query-chain/index
This section covers how to query data directly from the Injective blockchain.
* [Auction](/developers-native/query-chain/auction/) - Query auction module data
* [Auth](/developers-native/query-chain/auth/) - Query auth module data
* [Bank](/developers-native/query-chain/bank/) - Query bank module data
* [Distribution](/developers-native/query-chain/distribution/) - Query distribution module data
* [Exchange](/developers-native/query-chain/exchange/) - Query exchange module data
* [Governance](/developers-native/query-chain/governance/) - Query governance module data
* [IBC](/developers-native/query-chain/ibc/) - Query IBC module data
* [Insurance Funds](/developers-native/query-chain/insurance-funds/) - Query insurance funds data
* [Mint](/developers-native/query-chain/mint/) - Query mint module data
* [Oracle](/developers-native/query-chain/oracle/) - Query oracle module data
* [Peggy](/developers-native/query-chain/peggy/) - Query peggy module data
* [Permissions](/developers-native/query-chain/permissions/) - Query permissions module data
* [Staking](/developers-native/query-chain/staking/) - Query staking module data
* [Tendermint](/developers-native/query-chain/tendermint/) - Query tendermint data
* [Token Factory](/developers-native/query-chain/token-factory/) - Query token factory data
* [Wasm](/developers-native/query-chain/wasm/) - Query wasm module data
* [WasmX](/developers-native/query-chain/wasmx/) - Query wasmx module data
# Cosmos Transactions
Source: https://docs.injective.network/developers-native/transactions/cosmos
Every transaction on Injective follows the same flow. The flow consists of three steps: preparing, signing and broadcasting the transaction. Let's dive into each step separately and explain the process in-depth (including examples) so we can understand the whole transaction flow.
## Preparing a transaction
First of, we need to prepare the transaction for signing.
At this point you **can't** use some online abstractions that provide a quick way to prepare the transaction for you based on the provided Message and the signer (ex. using the `@cosmjs/stargate` package). The reason why is that these packages don't support Injective's publicKey typeUrl, so we have to do the preparation of the address on the client side.
To resolve this, we have provided functions which can prepare the `txRaw` transaction within out `@injectivelabs/sdk-ts` package. `txRaw` is the transaction interface used in Cosmos that contains details about the transaction and the signer itself.
Getting a private key from cosmos wallets is usually done by taking the current key for the chainId and accessing the pubKey from there (ex: `const key = await window.keplr.getKey(chainId)` => `const pubKey = key.publicKey`).
```ts theme={null}
import {
MsgSend,
} from "@injectivelabs/sdk-ts/core/modules";
import {
BaseAccount,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
createTransaction,
} from "@injectivelabs/sdk-ts/core/tx";
import { toBigNumber, toChainFormat } from "@injectivelabs/utils";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { getStdFee, DEFAULT_BLOCK_TIMEOUT_HEIGHT } from "@injectivelabs/utils";
(async () => {
const injectiveAddress = "inj1";
const chainId = "injective-1"; /* ChainId.Mainnet */
const restEndpoint =
"https://sentry.lcd.injective.network"; /* getNetworkEndpoints(Network.MainnetSentry).rest */
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(restEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(restEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
/** Preparing the transaction */
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** Get the PubKey of the Signer from the Wallet/Private Key */
const pubKey = await getPubKey();
/** Prepare the Transaction **/
const { txRaw, signDoc } = createTransaction({
pubKey,
chainId,
fee: getStdFee({}),
message: msg,
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
});
})();
```
## Signing a transaction
Once we have prepared the transaction, we proceed to signing. Once you get the `txRaw` transaction from the previous step use any Cosmos native wallet to sign (ex: Keplr),
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
import { SignDoc } from '@keplr-wallet/types'
const getKeplr = async (chainId: string) => {
await window.keplr.enable(chainId);
const offlineSigner = window.keplr.getOfflineSigner(chainId);
const accounts = await offlineSigner.getAccounts();
const key = await window.keplr.getKey(chainId);
return { offlineSigner, accounts, key }
}
const { offlineSigner } = await getKeplr(ChainId.Mainnet)
/* Sign the Transaction */
const address = 'inj1...'
const signDoc = /* From the previous step */
const directSignResponse = await offlineSigner.signDirect(address, signDoc as SignDoc)
```
You can also use our `@injectivelabs/wallet-strategy` package to get out-of-the-box wallet provides that will give you abstracted methods that you can use to sign transactions. Refer to the documentation of the package, its straightforward to setup and use. **This is the recommended way as you have access to more than one wallet to use in your dApp. The `WalletStrategy` provides more than just signing transaction abstractions.**
## Broadcasting a transaction
Once we have the signature ready, we need to broadcast the transaction to the Injective chain itself. After getting the signature from the second step, we need to include it in the signed transaction and broadcast it to the chain.
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
import {
TxRestApi,
CosmosTxV1Beta1Tx,
BroadcastModeKeplr,
getTxRawFromTxRawOrDirectSignResponse,
TxRaw,
} from '@injectivelabs/sdk-ts/core/tx'
import { TransactionException } from '@injectivelabs/exceptions'
/**
* IMPORTANT NOTE:
* If we use Keplr/Leap wallets
* after signing the transaction we get a `directSignResponse`,
* and instead of adding the signature to the `txRaw` we create
* using the `createTransaction` function we need to append the
* signature from the `directSignResponse` to the transaction that
* got actually signed (i.e `directSignResponse.signed`) and
* the reason why is that the user can make some changes on the original
* transaction (i.e change gas limit or gas prices) and the transaction
* that get's signed and the one that gets broadcasted are not the same.
*/
const directSignResponse = /* From the second step above */;
const txRaw = getTxRawFromTxRawOrDirectSignResponse(directSignResponse)
const broadcastTx = async (chainId: String, txRaw: TxRaw) => {
const getKeplr = async (chainId: string) => {
await window.keplr.enable(chainId);
return window.keplr
}
const keplr = await getKeplr(ChainId.Mainnet)
const result = await keplr.sendTx(
chainId,
CosmosTxV1Beta1Tx.TxRaw.encode(txRaw).finish(),
BroadcastModeKeplr.Sync,
)
if (!result || result.length === 0) {
throw new TransactionException(
new Error('Transaction failed to be broadcasted'),
{ contextModule: 'Keplr' },
)
}
return Buffer.from(result).toString('hex')
}
const txHash = await broadcastTx(ChainId.Mainnet, txRaw)
/**
* Once we get the txHash, because we use the Sync mode we
* are not sure that the transaction is included in the block,
* it can happen that it's still in the mempool so we need to query
* the chain to see when the transaction will be included
*/
const restEndpoint = 'https://sentry.lcd.injective.network' /* getNetworkEndpoints(Network.MainnetSentry).rest */
const txRestApi = new TxRestApi(restEndpoint)
/** This will poll querying the transaction and await for it's inclusion in the block */
const response = await txRestApi.fetchTxPoll(txHash)
```
## Example (Prepare + Sign + Broadcast)
Let's have a look at the whole flow (using Keplr as a signing wallet)
```ts theme={null}
import {
MsgSend,
} from "@injectivelabs/sdk-ts/core/modules";
import {
BaseAccount,
} from "@injectivelabs/sdk-ts/core/accounts";
import { ChainId } from "@injectivelabs/ts-types";
import { SignDoc } from "@keplr-wallet/types";
import { toBigNumber, toChainFormat } from "@injectivelabs/utils";
import { TransactionException } from "@injectivelabs/exceptions";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { getStdFee, DEFAULT_BLOCK_TIMEOUT_HEIGHT } from "@injectivelabs/utils";
import {
TxRaw,
TxRestApi,
createTransaction,
CosmosTxV1Beta1Tx,
BroadcastModeKeplr,
getTxRawFromTxRawOrDirectSignResponse,
} from "@injectivelabs/sdk-ts/core/tx";
const getKeplr = async (chainId: string) => {
await window.keplr.enable(chainId);
const offlineSigner = window.keplr.getOfflineSigner(chainId);
const accounts = await offlineSigner.getAccounts();
const key = await window.keplr.getKey(chainId);
return { offlineSigner, accounts, key };
};
const broadcastTx = async (chainId: string, txRaw: TxRaw) => {
const keplr = await getKeplr(ChainId.Mainnet);
const result = await keplr.sendTx(
chainId,
CosmosTxV1Beta1Tx.TxRaw.encode(txRaw).finish(),
BroadcastModeKeplr.Sync
);
if (!result || result.length === 0) {
throw new TransactionException(
new Error("Transaction failed to be broadcasted"),
{ contextModule: "Keplr" }
);
}
return Buffer.from(result).toString("hex");
};
(async () => {
const chainId = "injective-1"; /* ChainId.Mainnet */
const { key, offlineSigner } = await getKeplr(chainId);
const pubKey = Buffer.from(key.pubKey).toString("base64");
const injectiveAddress = key.bech32Address;
const restEndpoint =
"https://sentry.lcd.injective.network"; /* getNetworkEndpoints(Network.MainnetSentry).rest */
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(restEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(restEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
/** Preparing the transaction */
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** Prepare the Transaction **/
const { signDoc } = createTransaction({
pubKey,
chainId,
fee: getStdFee({}),
message: msg,
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
});
const directSignResponse = await offlineSigner.signDirect(
injectiveAddress,
signDoc as SignDoc
);
const txRaw = getTxRawFromTxRawOrDirectSignResponse(directSignResponse);
const txHash = await broadcastTx(ChainId.Mainnet, txRaw);
const response = await new TxRestApi(restEndpoint).fetchTxPoll(txHash);
console.log(response);
})();
```
## Example with WalletStrategy (Prepare + Sign + Broadcast)
Example can be found in the [wallet-core package](https://github.com/InjectiveLabs/injective-ts/tree/master/packages/wallets/wallet-core).
# Ledger through Keplr Wallet Transaction
Source: https://docs.injective.network/developers-native/transactions/cosmos-ledger-keplr
On this page, we are going to have a look at the implementation for Injective when your users are using a Ledger device through the Keplr wallet.
As explained before, Injective uses a different derivation curve from the rest of the Cosmos chains which means that the users have to use the Ethereum app (for now) to interact with Injective.
The easiest way all of the edge cases covered and a full out-of-the-box solution for all of the supported wallets on Injective I suggest you have a look at the [MsgBroadcaster + WalletStrategy ](./msgbroadcaster/#msgbroadcaster-+-wallet-strategy)abstraction. If you want to do your own implementation, let's go through the code example together.
## Overview
Keplr exposes a `experimentalSignEIP712CosmosTx_v0` method which can be utilized to sign EIP712 typed data (automatically generated on the Keplr side by passing a Cosmos StdSignDoc to the method above) and allow EVM-compatible chains to get proper signatures when we have Ledger devices connected through Keplr.
Here is the function's signature:
```typescript theme={null}
/**
* Sign the sign doc with ethermint's EIP-712 format.
* The difference from signEthereum(..., EthSignType.EIP712) is that this api returns a new sign doc changed by the user's fee setting and the signature for that sign doc.
* Encoding tx to EIP-712 format should be done on the side using this api.
* Not compatible with cosmjs.
* The returned signature is (r | s | v) format which used in ethereum.
* v should be 27 or 28 which is used in the ethereum mainnet regardless of chain.
* @param chainId
* @param signer
* @param eip712
* @param signDoc
* @param signOptions
*/
experimentalSignEIP712CosmosTx_v0(chainId: string, signer: string, eip712: {
types: Record;
domain: Record;
primaryType: string;
}, signDoc: StdSignDoc, signOptions?: KeplrSignOptions): Promise;
```
What we need to do now is generate the `eip712` and the `signDoc`, pass them to this function and Keplr will ask the user to sign the transaction using the Ethereum app on their Ledger device.
## Example Implementation
Based on the overview above, let's now showcase a full example of how to implement signing transactions on Injective using Ledger + Keplr. Keep in mind that the example below takes into consideration that you are using the [Msgs](https://github.com/InjectiveLabs/injective-ts/blob/master/packages/sdk-ts/src/core/modules/msgs.ts#L60) interface exported from the `@injectivelabs/sdk-ts` package.
```typescript theme={null}
import {
TxGrpcApi,
SIGN_AMINO,
createTransaction,
createTxRawEIP712,
getEip712TypedData
createWeb3Extension,
getGasPriceBasedOnMessage,
} from '@injectivelabs/sdk-ts/core/tx'
import {
BaseAccount,
} from '@injectivelabs/sdk-ts/core/accounts'
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from '@injectivelabs/sdk-ts/client/chain'
import { EvmChainId, ChainId } from '@injectivelabs/ts-types'
import { getNetworkEndpoints, NetworkEndpoints, Network } from '@injectivelabs/networks'
import { GeneralException, TransactionException } from '@injectivelabs/exceptions'
import { toBigNumber, getStdFee } from '@injectivelabs/utils'
export interface Options {
evmChainId: EvmChainId /* Evm chain id */
chainId: ChainId; /* Injective chain id */
endpoints: NetworkEndpoints /* can be fetched from @injectivelabs/networks based on the Network */
}
export interface Transaction {
memo?: string
injectiveAddress?: string
msgs: Msgs | Msgs[]
// In case we manually want to set gas options
gas?: {
gasPrice?: string
gas?: number /** gas limit */
feePayer?: string
granter?: string
}
}
/** Converting EIP712 tx details to Cosmos Std Sign Doc */
export const createEip712StdSignDoc = ({
memo,
chainId,
accountNumber,
timeoutHeight,
sequence,
gas,
msgs,
}: {
memo?: string
chainId: ChainId
timeoutHeight?: string
accountNumber: number
sequence: number
gas?: string
msgs: Msgs[]
}) => ({
chain_id: chainId,
timeout_height: timeoutHeight || '',
account_number: accountNumber.toString(),
sequence: sequence.toString(),
fee: getStdFee({ gas }),
msgs: msgs.map((m) => m.toEip712()),
memo: memo || '',
})
/**
* We use this method only when we want to broadcast a transaction using Ledger on Keplr for Injective
*
* Note: Gas estimation not available
* @param tx the transaction that needs to be broadcasted
*/
export const experimentalBroadcastKeplrWithLedger = async (
tx: Transaction,
options: Options
) => {
const { endpoints, chainId, evmChainId } = options
const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs]
const DEFAULT_BLOCK_TIMEOUT_HEIGHT = 60
/**
* You choose to perform a check if
* the user is indeed connected with Ledger + Keplr
*/
if (/* your condition here */) {
throw new GeneralException(
new Error(
'This method can only be used when Keplr is connected with Ledger',
),
)
}
/** Account Details * */
const chainRestAuthApi = new ChainRestAuthApi(endpoints.rest)
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
tx.injectiveAddress,
)
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse)
const accountDetails = baseAccount.toAccountDetails()
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(endpoints.rest)
const latestBlock = await chainRestTendermintApi.fetchLatestBlock()
const latestHeight = latestBlock.header.height
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
)
const key = await window.keplr.getKey(chainId)
const pubKey = Buffer.from(key.pubKey).toString('base64')
const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString()
/** EIP712 for signing on Ethereum wallets */
const eip712TypedData = getEip712TypedData({
msgs,
fee: getStdFee({ ...tx.gas, gas }),
tx: {
memo: tx.memo,
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toFixed(),
chainId,
},
evmChainId,
})
const aminoSignResponse = await window.keplr.experimentalSignEIP712CosmosTx_v0(
chainId,
tx.injectiveAddress,
eip712TypedData,
createEip712StdSignDoc({
...tx,
...baseAccount,
msgs,
chainId,
gas: gas || tx.gas?.gas?.toString(),
timeoutHeight: timeoutHeight.toFixed(),
})
)
/**
* Create TxRaw from the signed tx that we
* get as a response in case the user changed the fee/memo
* on the Keplr popup
*/
const { txRaw } = createTransaction({
pubKey,
message: msgs,
memo: aminoSignResponse.signed.memo,
signMode: SIGN_AMINO,
fee: aminoSignResponse.signed.fee,
sequence: parseInt(aminoSignResponse.signed.sequence, 10),
timeoutHeight: parseInt(
(aminoSignResponse.signed as any).timeout_height,
10,
),
accountNumber: parseInt(aminoSignResponse.signed.account_number, 10),
chainId,
})
/** Preparing the transaction for client broadcasting */
const web3Extension = createWeb3Extension({
evmChainId,
})
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension)
/** Append Signatures */
const signatureBuff = Buffer.from(
aminoSignResponse.signature.signature,
'base64',
)
txRawEip712.signatures = [signatureBuff]
/** Broadcast the transaction */
const response = await new TxGrpcApi(endpoints.grpc).broadcast(txRawEip712)
if (response.code !== 0) {
throw new TransactionException(new Error(response.rawLog), {
code: UnspecifiedErrorCode,
contextCode: response.code,
contextModule: response.codespace,
})
}
return response
}
```
# Ethereum Transaction
Source: https://docs.injective.network/developers-native/transactions/ethereum
Every transaction on Injective follows the same flow. The flow consists of three steps: preparing, signing and broadcasting the transaction. Let's dive into each step separately and explain the process in-depth (including examples) so we can understand the whole transaction flow.
## Preparing a transaction
First of, we need to prepare the transaction for signing. To use Ethereum native wallets, we have to convert the transaction to EIP712 typed data and use the wallet to sign this typed data.
Using our custom abstraction for the Messages which allows the developer to get EIP712 TypedData straight from the proto file of the particular message.
```ts theme={null}
import {
MsgSend,
} from "@injectivelabs/sdk-ts/core/modules";
import {
BaseAccount,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import {
getEip712TypedDataV2,
} from "@injectivelabs/sdk-ts/core/tx";
import {
toBigNumber,
toChainFormat,
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from "@injectivelabs/utils";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
const injectiveAddress = "inj1";
const chainId = ChainId.Mainnet;
const evmChainId = EvmChainId.Mainnet;
const restEndpoint =
"https://lcd.injective.network"; /* getNetworkEndpoints(Network.Mainnet).rest */
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(restEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
const accountDetails = baseAccount.toAccountDetails();
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(restEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
/** Preparing the transaction */
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** EIP712 for signing on Ethereum wallets */
const eip712TypedData = getEip712TypedDataV2({
msgs: [msg],
tx: {
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toFixed(),
chainId: chainId,
},
evmChainId,
});
```
## Signing a transaction
Once we have prepared the EIP712 typed data, we proceed to signing.
```ts theme={null}
/** Use your preferred approach to sign EIP712 TypedData, example with Metamask */
const signature = await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [
ethereumAddress,
JSON.stringify(eip712TypedData /* from previous step */),
],
});
/** Get Public Key of the signer */
const publicKeyHex = recoverTypedSignaturePubKey(eip712TypedData, signature);
const publicKeyBase64 = hexToBase64(publicKeyHex);
```
You can also use our `@injectivelabs/wallet-strategy` package to get out-of-the-box wallet provides that will give you abstracted methods which you can use to sign transaction. Refer to the documentation of the package, its really simple to setup and use. **This is the recommended way as you have access to more than one wallet to use in your dApp. The `WalletStrategy` provides more than just signing transaction abstractions.**
## Broadcasting a transaction
Once we have the signature ready, we need to broadcast the transaction to the Injective chain itself. After getting the signature from the second step, we need to include that signature in the signed transaction and broadcast it to the chain.
```ts theme={null}
import {
Network,
SIGN_AMINO,
getNetworkEndpoints,
} from "@injectivelabs/networks";
import { getDefaultStdFee } from "@injectivelabs/utils";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { createTransaction, TxRestApi } from "@injectivelabs/sdk-ts/core/tx";
const evmChainId = EvmChainId.Mainnet;
const { txRaw } = createTransaction({
message: msgs,
memo: memo,
signMode: SIGN_AMINO,
fee: getDefaultStdFee(),
pubKey: publicKeyBase64 /* From previous step */,
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
chainId: chainId,
});
const web3Extension = createWeb3Extension({
evmChainId,
});
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
/** Append Signatures */
txRawEip712.signatures = [signatureBuff /* From previous step */];
/** Broadcast the Transaction */
const restEndpoint =
"https://lcd.injective.network"; /* getNetworkEndpoints(Network.Mainnet).rest */
const txRestApi = new TxRestApi(restEndpoint);
const txHash = await txRestApi.broadcast(txRawEip712);
/**
* Once we get the txHash, because we use the Sync mode we
* are not sure that the transaction is included in the block,
* it can happen that it's still in the mempool so we need to query
* the chain to see when the transaction will be included
*/
/** This will poll querying the transaction and await for it's inclusion in the block */
const response = await txRestApi.fetchTxPoll(txHash);
```
## Example without WalletStrategy (Prepare + Sign + Broadcast)
Let's have a look at the whole flow (using Metamask as a signing wallet)
```ts theme={null}
import {
MsgSend,
} from "@injectivelabs/sdk-ts/core/modules";
import {
BaseAccount,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
TxRestApi,
SIGN_AMINO,
hexToBase64,
createTransaction,
createTxRawEIP712,
getEip712TypedData,
createWeb3Extension,
recoverTypedSignaturePubKey,
} from "@injectivelabs/sdk-ts/core/tx";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import {
getEthereumAddress,
} from "@injectivelabs/sdk-ts/utils";
import {
toBigNumber,
toChainFormat,
getDefaultStdFee,
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from "@injectivelabs/utils";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
const injectiveAddress = "inj1";
const chainId = ChainId.Mainnet;
const evmChainId = EvmChainId.Mainnet;
const ethereumAddress = getEthereumAddress(injectiveAddress);
const restEndpoint = getNetworkEndpoints(Network.MainnetSentry).rest;
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(restEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
const accountDetails = baseAccount.toAccountDetails();
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(restEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
/** Preparing the transaction */
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** EIP712 for signing on Ethereum wallets */
const eip712TypedData = getEip712TypedData({
msgs: [msg],
tx: {
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toFixed(),
chainId,
},
evmChainId,
});
/** Use your preferred approach to sign EIP712 TypedData, example with Metamask */
const signature = await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [ethereumAddress, JSON.stringify(eip712TypedData)],
});
/** Get Public Key of the signer */
const publicKeyHex = recoverTypedSignaturePubKey(eip712TypedData, signature);
const publicKeyBase64 = hexToBase64(publicKeyHex);
const signatureBuff = Buffer.from(signature.replace("0x", ""), "hex");
const { txRaw } = createTransaction({
message: [msg],
memo: "",
signMode: SIGN_AMINO,
fee: getDefaultStdFee(),
pubKey: publicKeyBase64,
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
chainId: chainId,
});
const web3Extension = createWeb3Extension({
evmChainId,
});
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
/** Append Signatures */
txRawEip712.signatures = [signatureBuff];
/** Broadcast the Transaction */
const txRestApi = new TxRestApi(restEndpoint);
const txResponse = await txRestApi.broadcast(txRawEip712);
const response = await txRestApi.fetchTxPoll(txResponse.txHash);
```
## Example with WalletStrategy (Prepare + Sign + Broadcast)
Example can be found in the [wallet-core package](https://github.com/InjectiveLabs/injective-ts/tree/master/packages/wallets/wallet-core).
# Ethereum Ledger Transaction
Source: https://docs.injective.network/developers-native/transactions/ethereum-ledger
## Signing Transactions on Injective using Ledger
The goal of this document is to explain how to use Ledger to sign transactions on Injective and broadcast them to the chain. The implementation differs from the default approach that Cosmos SDK native chains have because Injective defines its custom Account type that uses Ethereum's ECDSA secp256k1 curve for keys.
## Implementation
To understand how we should do the implementation, let’s go through some concepts so it's easier to understand the approach we are going to take.
### Background
A derivation path is a piece of data that tells a Hierarchical Deterministic (HD) wallet how to derive a specific key within a tree of keys. Derivation paths are used as a standard and were introduced with HD wallets as a part of BIP32. A Hierarchical Deterministic Wallet is a term used to describe a wallet that uses a seed to derive many public and private keys.
This is what a derivation path looks like
`m/purpose'/coin_type'/account'/change/address_index`
Each of the parts in the sequence plays a part and each changes what the private key, public key, and address would be. We are not going to deep dive into the exact details about what every part of the HD path means, instead, we are just going to briefly explain the `coin_type`. Each blockchain has a number that represents it i.e the `coin_type`. Bitcoin is `0`, Ethereum is `60`, Cosmos is `118`.
### Injective specific context
Injective uses the same `coin_type` as Ethereum, i.e `60`. This means for Ledger to be used to sign transactions on Injective, **we have to use the Ethereum app on Ledger**.
Ledger is limited to having one installed application for one `coin_type`. As we have to use the Ethereum app to sign transactions on Injective, we have to explore available options to us to get a valid signature. One of the available options is the `EIP712` procedure for hashing and signing typed structured data. Ledger exposes the `signEIP712HashedMessage` which we are going to use.
Once we sign the `EIP712` typed data, we are going to pack the transaction using the normal Cosmos-SDK approach of packing and broadcasting the transaction. There are some minor differences, one of them being using the `SIGN_MODE_LEGACY_AMINO_JSON` mode and appending a `Web3Exension` to the Cosmos transaction and we are going to explain them in this document.
### EIP712 Typed Data
EIP 712 is a standard for hashing and signing of typed structured data. For every EIP712 typed data, each of the values the user passes (that need to be signed) has a type representative which explains the exact type of that particular value. In addition to the value the user wants to sign and its type (the `PrimaryType` of the EIP712 typedData), every EIP712 typed data should contain an `EIP712Domain` which provides context about the source of the transaction.
## Transaction Flow
The implementation itself consists of a few steps, namely:
1. Preparing the transaction to be signed using the Ethereum app on Ledger,
2. Preparing and signing the transaction on Ledger,
3. Preparing the transaction to be broadcasted,
4. Broadcast the transaction.
We are going deep dive into each step and elaborate on the actions we need to take to get the transaction signed and broadcasted to the chain.
### Preparing the transaction (for signing)
As we’ve said above, the transaction needs to be signed using the Ethereum app on Ledger. This means that the user has to be prompted to switch (or open) the Ethereum app on Ledger once they reach the signing stage.
We know that each Cosmos transaction consists of messages which signify the instructions the user wants to execute on the chain. If we want to send funds from one address to another, we are going to pack the `MsgSend` message into a transaction and broadcast it to the chain.
Knowing this, the Injective team made [abstraction](https://github.com/InjectiveLabs/injective-ts/blob/master/packages/sdk-ts/src/core/modules/MsgBase.ts) of these Messages to simplify the way they are packed into a transaction. Each of these Messages accepts a specific set of parameters that are needed to instantiate the message. Once this is done, the abstraction exposes a couple of convenient methods which we can use based on the signing/broadcasting method we chose to use. As an example, the Message exposes the `toDirectSign` method which returns the type and the proto representation of the message which can be then used to pack the transaction using the default Cosmos approach, sign it using a privateKey and broadcast it to the chain.
What is of importance for us for this particular implementation are the `toEip712Types` and `toEip712` methods. Calling the first one on an instance of the Message gives out the types of the Message for the EIP712 typed data and the second one gives the values of the Message for the EIP712 data. When we combine these two methods we can generate valid EIP712 typed data which can be passed down to the signing process.
So, let’s see a quick code snippet of the usage of these methods and how we can generate EIP712 typedData from a message:
```ts theme={null}
import {
MsgSend,
} from "@injectivelabs/sdk-ts/core/modules";
import {
getEip712TypedDataV2,
type Eip712ConvertTxArgs,
type Eip712ConvertFeeArgs,
} from "@injectivelabs/sdk-ts/core/tx";
import { EvmChainId } from "@injectivelabs/ts-types";
import { toChainFormat, getDefaultStdFee } from "@injectivelabs/utils";
/** More details on these two interfaces later on */
const txArgs: Eip712ConvertTxArgs = {
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toFixed(),
chainId: chainId,
};
const txFeeArgs: Eip712ConvertFeeArgs = getDefaultStdFee();
const injectiveAddress = "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku";
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
const evmChainId = EvmChainId.Mainnet;
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** The EIP712 TypedData that can be used for signing **/
const eip712TypedData = getEip712TypedDataV2({
msgs: msg,
tx: txArgs,
evmChainId,
fee: txFeeArgs,
});
return eip712TypedData;
```
### Preparing the signing process on Ledger
Now that we have the `eip712TypedData` we need to sign it using Ledger. First, we need to get the Ledger’s transport depending on the support that the user has on the browser and use the `@ledgerhq/hw-app-eth` to make a Ledger instance with the transport that’ll use the Ethereum app on the Ledger device for executing the user’s actions (confirming transactions). After we get the `eip712TypedData` from Step 1, we can use the `signEIP712HashedMessage` on the `EthereumApp` to sign this typedData and return the signature.
```ts theme={null}
import { TypedDataUtils } from 'eth-sig-util'
import { bufferToHex, addHexPrefix } from 'ethereumjs-util'
import EthereumApp from '@ledgerhq/hw-app-eth'
const domainHash = (message: any) =>
TypedDataUtils.hashStruct('EIP712Domain', message.domain, message.types, true)
const messageHash = (message: any) =>
TypedDataUtils.hashStruct(
message.primaryType,
message.message,
message.types,
true,
)
const transport = /* Get the transport from Ledger */
const ledger = new EthereumApp(transport)
const derivationPath = /* Get the derivation path for the address */
/* eip712TypedData from Step 1 */
const object = JSON.parse(eip712TypedData)
const result = await ledger.signEIP712HashedMessage(
derivationPath,
bufferToHex(domainHash(object)),
bufferToHex(messageHash(object)),
)
const combined = `${result.r}${result.s}${result.v.toString(16)}`
const signature = combined.startsWith('0x') ? combined : `0x${combined}`
return signature;
```
### Preparing the transaction to be broadcasted
Now that we have the signature, we can prepare the transaction using the default cosmos approach.
```ts theme={null}
import {
SIGN_AMINO,
createTransaction,
createTxRawEIP712,
createWeb3Extension,
} from "@injectivelabs/sdk-ts/core/tx";
import {
BaseAccount,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import {
toBigNumber,
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from "@injectivelabs/utils";
const msg: MsgSend; /* from Step 1 */
const chainId = ChainId.Mainnet;
const evmChainId = EvmChainId.Mainnet;
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(lcdEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
const accountDetails = baseAccount.toAccountDetails();
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(lcdEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
const { txRaw } = createTransaction({
message: msgs,
memo: "",
signMode: SIGN_AMINO,
fee: getDefaultStdFee(),
pubKey: publicKeyBase64,
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
chainId,
});
const web3Extension = createWeb3Extension({
evmChainId,
});
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
/** Append Signatures */
const signatureBuff = Buffer.from(signature.replace("0x", ""), "hex");
txRawEip712.signatures = [signatureBuff];
return txRawEip712;
```
### Broadcasting the transaction
Now that we have the transaction packed into `TxRaw` we can broadcast it to the node using the default cosmos approach.
## Codebase
Let’s see an example codebase containing all of the steps above
```ts theme={null}
import {
TxRestApi,
SIGN_AMINO,
createTransaction,
createTxRawEIP712,
createWeb3Extension,
getEip712TypedDataV2,
type Eip712ConvertTxArgs,
type Eip712ConvertFeeArgs
} from '@injectivelabs/sdk-ts/core/tx'
import {
MsgSend,
} from '@injectivelabs/sdk-ts/core/modules'
import {
BaseAccount,
} from '@injectivelabs/sdk-ts/core/accounts'
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from '@injectivelabs/sdk-ts/client/chain'
import { TypedDataUtils } from 'eth-sig-util'
import EthereumApp from '@ledgerhq/hw-app-eth'
import { bufferToHex, addHexPrefix } from 'ethereumjs-util'
import { EvmChainId, ChainId } from '@injectivelabs/ts-types'
import { toChainFormat, DEFAULT_BLOCK_TIMEOUT_HEIGHT, getDefaultStdFee } from '@injectivelabs/utils'
const domainHash = (message: any) =>
TypedDataUtils.hashStruct('EIP712Domain', message.domain, message.types, true)
const messageHash = (message: any) =>
TypedDataUtils.hashStruct(
message.primaryType,
message.message,
message.types,
true,
)
const signTransaction = async (eip712TypedData: any) => {
const transport = /* Get the transport from Ledger */
const ledger = new EthereumApp(transport)
const derivationPath = /* Get the derivation path for the address */
/* eip712TypedData from Step 1 */
const result = await ledger.signEIP712HashedMessage(
derivationPath,
bufferToHex(domainHash(eip712TypedData)),
bufferToHex(messageHash(eip712TypedData)),
)
const combined = `${result.r}${result.s}${result.v.toString(16)}`
const signature = combined.startsWith('0x') ? combined : `0x${combined}`
return signature;
}
const getAccountDetails = (address: string): BaseAccount => {
const chainRestAuthApi = new ChainRestAuthApi(
lcdEndpoint,
)
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
address,
)
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse)
const accountDetails = baseAccount.toAccountDetails()
return accountDetails
}
const getTimeoutHeight = () => {
const chainRestTendermintApi = new ChainRestTendermintApi(
lcdEndpoint,
)
const latestBlock = await chainRestTendermintApi.fetchLatestBlock()
const latestHeight = latestBlock.header.height
const timeoutHeight = latestHeight + DEFAULT_BLOCK_TIMEOUT_HEIGHT
return timeoutHeight
}
const address = 'inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku'
const chainId = ChainId.Mainnet
const evmChainId = EvmChainId.Mainnet
const accountDetails = getAccountDetails()
const timeoutHeight = getTimeoutHeight
const txArgs: Eip712ConvertTxArgs = {
accountNumber: accountDetails.accountNumber.toString(),
sequence: accountDetails.sequence.toString(),
timeoutHeight: timeoutHeight.toString(),
chainId: chainId,
}
const txFeeArgs: Eip712ConvertFeeArgs = getDefaultStdFee()
const injectiveAddress = 'inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku'
const amount = {
amount: toChainFormat(0.01).toFixed(),
denom: "inj",
};
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** The EIP712 TypedData that can be used for signing **/
const eip712TypedData = getEip712TypedDataV2({
msgs: msg,
tx: txArgs,
evmChainId,
fee: txFeeArgs
})
/** Signing on Ethereum */
const signature = await signTransaction(eip712TypedData)
/** Preparing the transaction for client broadcasting */
const { txRaw } = createTransaction({
message: msg,
memo: '',
signMode: SIGN_AMINO,
fee: getDefaultStdFee(),
pubKey: publicKeyBase64,
sequence: accountDetails.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: accountDetails.accountNumber,
chainId: chainId,
})
const web3Extension = createWeb3Extension({
evmChainId,
})
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension)
/** Append Signatures */
const signatureBuff = Buffer.from(signature.replace('0x', ''), 'hex')
txRawEip712.signatures = [signatureBuff]
/** Broadcast the transaction **/
const txRestApi = new TxRestApi(lcdEndpoint)
const response = await txRestApi.broadcast(txRawEip712)
if (response.code !== 0) {
throw new Error(`Transaction failed: ${response.rawLog}`)
}
return response.txhash
```
# Transactions
Source: https://docs.injective.network/developers-native/transactions/index
This section covers how to create, sign, and broadcast transactions on Injective.
* [Cosmos Transactions](/developers-native/transactions/cosmos/) - Sign transactions using Cosmos wallets
* [Cosmos Ledger + Keplr](/developers-native/transactions/cosmos-ledger-keplr/) - Sign transactions using Ledger with Keplr
* [Ethereum Transactions](/developers-native/transactions/ethereum/) - Sign transactions using Ethereum wallets
* [Ethereum Ledger](/developers-native/transactions/ethereum-ledger/) - Sign transactions using Ledger with Ethereum wallets
* [MsgBroadcaster](/developers-native/transactions/msgbroadcaster/) - Use the MsgBroadcaster utility class
* [Private Key](/developers-native/transactions/private-key/) - Sign transactions using a private key
* [Web3 Gateway](/developers-native/transactions/web3-gateway/) - Use the Web3 Gateway for transaction broadcasting
# MsgBroadcaster Transaction
Source: https://docs.injective.network/developers-native/transactions/msgbroadcaster
The `MsgBroadcaster` abstraction class is a way to broadcast transactions on Injective with ease. With it, you can pass a Message that you want to be packed in a transaction and the signer's address and the transaction will be prepared, signed, and broadcasted.
An example of usage can be found on our [Helix demo repo](https://github.com/InjectiveLabs/injective-helix-demo). As for the messages that you can pass to the `broadcast` methods, you can find examples in the [Core Modules](../examples/) section of the docs.
## MsgBroadcaster + Wallet Strategy
This MsgBroadcaster is used alongside the Wallet Strategy class for building decentralized applications.
To instantiate (and use) the `MsgBroadcaster` class, you can use the following code snippet
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcaster } from "@injectivelabs/wallet-core";
import { WalletStrategy } from "@injectivelabs/wallet-strategy";
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
export const evmRpcEndpoint = "";
export const walletStrategy = new WalletStrategy({
chainId: ChainId.Mainnet,
evmOptions: {
rpcUrl: evmRpcEndpoint,
evmChainId: EvmChainId.Mainnet,
},
strategies: {},
});
export const msgBroadcaster = new MsgBroadcaster({
walletStrategy,
simulateTx: true,
network: Network.Mainnet,
endpoints: getNetworkEndpoints(Network.Mainnet),
gasBufferCoefficient: 1.1,
});
// Usage Example
const signer = "inj1...";
const msg = MsgSend.fromJSON({
amount: {
denom: "inj",
amount: toChainFormat(0.01, 18).toFixed(),
},
srcInjectiveAddress: signer,
dstInjectiveAddress: "inj1...",
});
// Prepare + Sign + Broadcast the transaction using the Wallet Strategy
await msgBroadcaster.broadcast({
injectiveAddress: signer,
msgs: msg,
});
```
### Constructor/Broadcast Options
We allow to override some of the options passed to the constructor of `MsgBroadcaster` as well as when broadcasting the transaction. Here is the interface and the meaning of each field
```typescript theme={null}
import { Msgs } from '@injectivelabs/sdk-ts/core/modules'
import { ChainId, EvmChainId } from '@injectivelabs/ts-types'
import { Network, NetworkEndpoints } from '@injectivelabs/networks'
import type { WalletStrategy } from '../strategies'
export interface MsgBroadcasterOptions {
network: Network /** network configuration (chainId, fees, etc) - Network.MainnetSentry for mainnet or Network.TestnetSentry for testnet */
endpoints?: NetworkEndpoints /** optional - overriding the endpoints taken from the `network` param **/
feePayerPubKey?: string /** optional - if you are using the fee delegation service, you can set the fee payer so you don't do an extra query to the Web3Gateway */
simulateTx?: boolean /** simulate the transaction before broadcasting + get gas fees needed for the transaction */
txTimeout?: number /** optional - blocks to wait for tx to be included in a block **/
walletStrategy: WalletStrategy
gasBufferCoefficient?: number /** optional - as gas buffer to add to the simulated/hardcoded gas to ensure the transaction is included in a block */
}
export interface MsgBroadcasterTxOptions {
memo?: string /** MEMO added to the transaction **/
injectiveAddress: string /** the signer of the transaction **/
msgs: Msgs | Msgs[] /** the messages to pack into a transaction **/
/*
*** overriding the hardcoded gas/simulation -
*** depending on the simulateTx parameter in
*** the MsgBroadcaster constructor
*/
gas?: {
gasPrice?: string
gas?: number /** gas limit */
feePayer?: string
granter?: string
}
}
```
To override the `endpoints` and use your infrastructure (which is something we
recommend), please read more on the
[Networks](../../developers/concepts/networks/) page on the endpoints you need
to provide and how to set them up.
## MsgBroadcaster with Private Key
This MsgBroadcaster is used with a private key (mostly used for CLI environments). Constructor/broadcast options are quite similar as for the `MsgBroadcaster`.
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
export const msgBroadcasterWithPk = new MsgBroadcasterWithPk({
privateKey: `0x...` /** private key hash or PrivateKey class from sdk-ts */,
network: NETWORK,
});
// Usage Example
const signer = "inj1...";
const msg = MsgSend.fromJSON({
amount: {
denom: "inj",
amount: toChainFormat(0.01, 18).toFixed(),
},
srcInjectiveAddress: signer,
dstInjectiveAddress: "inj1...",
});
// Prepare + Sign + Broadcast the transaction using the Private Key
await msgBroadcasterWithPk.broadcast({
injectiveAddress: signer,
msgs: msg,
});
```
# Private Key Transaction
Source: https://docs.injective.network/developers-native/transactions/private-key
In this document, we are going to show you how to use a PrivateKey to sign transactions on Injective.
Every transaction on Injective follows the same flow. The flow consists of three steps: preparing, signing and broadcasting the transaction. Let's dive into each step separately and explain the process in-depth (including examples) so we can understand the whole transaction flow.
## Preparing a transaction
First of, we need to prepare the transaction for signing.
```ts theme={null}
import {
toBigNumber,
toChainFormat,
getDefaultStdFee,
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from "@injectivelabs/utils";
import {
ChainRestAuthApi,
ChainRestTendermintApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { ChainId } from "@injectivelabs/ts-types";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { createTransaction } from "@injectivelabs/sdk-ts/core/tx";
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
import { PrivateKey, BaseAccount } from "@injectivelabs/sdk-ts/core/accounts";
const privateKeyHash = "";
const privateKey = PrivateKey.fromHex(privateKeyHash);
const injectiveAddress = privateKey.toBech32();
const address = privateKey.toAddress();
const pubKey = privateKey.toPublicKey().toBase64();
const chainId = "injective-1"; /* ChainId.Mainnet */
const restEndpoint =
"https://lcd.injective.network"; /* getNetworkEndpoints(Network.Mainnet).rest */
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
/** Account Details **/
const chainRestAuthApi = new ChainRestAuthApi(restEndpoint);
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse);
const accountDetails = baseAccount.toAccountDetails();
/** Block Details */
const chainRestTendermintApi = new ChainRestTendermintApi(restEndpoint);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
const latestHeight = latestBlock.header.height;
const timeoutHeight = toBigNumber(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT
);
/** Preparing the transaction */
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** Prepare the Transaction **/
const { txRaw, signBytes } = createTransaction({
pubKey,
chainId,
message: msg,
fee: getDefaultStdFee(),
sequence: baseAccount.sequence,
timeoutHeight: timeoutHeight.toNumber(),
accountNumber: baseAccount.accountNumber,
});
```
## Signing a transaction
Once we have prepared the transaction, we proceed to signing. Once you get the `txRaw` transaction from the previous step use any Cosmos native wallet to sign (ex: Keplr),
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
/* Sign the Transaction */
const privateKeyHash = ''
const privateKey = PrivateKey.fromHex(privateKeyHash);
const signBytes = /* From the previous step */
/** Sign transaction */
const signature = await privateKey.sign(Buffer.from(signBytes));
```
## Broadcasting a transaction
Once we have the signature ready, we need to broadcast the transaction to the Injective chain itself. After getting the signature from the second step, we need to include that signature in the signed transaction and broadcast it to the chain.
```ts theme={null}
import { ChainId } from '@injectivelabs/ts-types'
import { TxClient } from '@injectivelabs/sdk-ts/core/tx'
import { TxGrpcApi } from '@injectivelabs/sdk-ts/client/chain'
import { Network, getNetworkInfo } from '@injectivelabs/networks'
/** Append Signatures */
const network = getNetworkInfo(Network.Testnet);
const txRaw = /* from the first step */
const signature = /* from the second step */
txRaw.signatures = [signature];
/** Calculate hash of the transaction */
console.log(`Transaction Hash: ${TxClient.hash(txRaw)}`);
const txService = new TxGrpcApi(network.grpc);
/** Simulate transaction */
const simulationResponse = await txService.simulate(txRaw);
console.log(
`Transaction simulation response: ${JSON.stringify(
simulationResponse.gasInfo
)}`
);
/** Broadcast transaction */
const txResponse = await txService.broadcast(txRaw);
console.log(txResponse);
if (txResponse.code !== 0) {
console.log(`Transaction failed: ${txResponse.rawLog}`);
} else {
console.log(
`Broadcasted transaction hash: ${JSON.stringify(txResponse.txHash)}`
);
}
```
## Example (Prepare + Sign + Broadcast)
Let's have a look at the whole flow (using Keplr as a signing wallet)
```ts theme={null}
import {
TxClient,
TxGrpcApi,
createTransaction,
} from "@injectivelabs/sdk-ts/core/tx";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { PrivateKey } from "@injectivelabs/sdk-ts/core/accounts";
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import { ChainRestAuthApi } from "@injectivelabs/sdk-ts/client/chain";
import { toChainFormat, getDefaultStdFee } from "@injectivelabs/utils";
/** MsgSend Example */
(async () => {
const network = getNetworkInfo(Network.Testnet);
const privateKeyHash =
"f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3";
const privateKey = PrivateKey.fromHex(privateKeyHash);
const injectiveAddress = privateKey.toBech32();
const publicKey = privateKey.toPublicKey().toBase64();
/** Account Details **/
const accountDetails = await new ChainRestAuthApi(network.rest).fetchAccount(
injectiveAddress
);
/** Prepare Message */
const amount = {
denom: "inj",
amount: toChainFormat(0.01).toFixed(),
};
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
/** Prepare the Transaction **/
const { signBytes, txRaw } = createTransaction({
message: msg,
memo: "",
pubKey: publicKey,
fee: getDefaultStdFee(),
sequence: parseInt(accountDetails.account.base_account.sequence, 10),
accountNumber: parseInt(
accountDetails.account.base_account.account_number,
10
),
chainId: network.chainId,
});
/** Sign transaction */
const signature = await privateKey.sign(Buffer.from(signBytes));
/** Append Signatures */
txRaw.signatures = [signature];
/** Calculate hash of the transaction */
console.log(`Transaction Hash: ${TxClient.hash(txRaw)}`);
const txService = new TxGrpcApi(network.grpc);
/** Simulate transaction */
const simulationResponse = await txService.simulate(txRaw);
console.log(
`Transaction simulation response: ${JSON.stringify(
simulationResponse.gasInfo
)}`
);
/** Broadcast transaction */
const txResponse = await txService.broadcast(txRaw);
if (txResponse.code !== 0) {
console.log(`Transaction failed: ${txResponse.rawLog}`);
} else {
console.log(
`Broadcasted transaction hash: ${JSON.stringify(txResponse.txHash)}`
);
}
})();
```
## Example with MsgBroadcasterWithPk
You can use the `MsgBroadcasterWithPk` class from the `@injectivelabs/sdk-ts` package which abstracts away most of the logic written above into a single class.
**This abstraction allows you to sign transactions in a Node/CLI environment.**
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { toChainFormat } from "@injectivelabs/utils";
import { MsgSend } from "@injectivelabs/sdk-ts/core/modules";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const amount = {
denom: "inj",
amount: toChainFormat(1).toFixed(),
};
const msg = MsgSend.fromJSON({
amount,
srcInjectiveAddress: injectiveAddress,
dstInjectiveAddress: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.Testnet,
}).broadcast({
msgs: msg,
});
console.log(txHash);
```
# Web3 Gateway Transaction
Source: https://docs.injective.network/developers-native/transactions/web3-gateway
*Pre-requisite reading #1:* [Transaction Lifecycle](https://docs.cosmos.network/main/basics/tx-lifecycle)
*Pre-requisite reading #2:* Transactions on Injective
The Web3Gateway microservice exposes an API to the end user with the main purpose of providing fee delegation for transactions that happen on Injective. This allows users to enjoy a gasless environment while interacting with Injective as the gas is paid for by the runner of the Web3Gateway service.
Alongside fee delegation support, Web3Gateway allows developers to convert Messages to EIP712 typed data. After converting the Message the EIP712 data, it can be signed by any Ethereum native wallet and then broadcasted to Injective.
## Fee Delegation
As said before, fee delegation allows users to interact with Injective (submit transactions) without having to pay for gas. As a part of the *Transaction Lifecycle* of every Cosmos-SDK powered chain, we have `AnteHandler`'s, which, among other things perform a signature verification, gas calculation, and fee deduction.
There are a couple of things that we need to know:
* Transactions can have multiple signers (i.e we can include multiple signatures within a transaction),
* Gas Fee for the transaction is deducted from the `authInfo.fee.feePayer` value and the signature that gets verified against the `feePayer` is the first signature within the signatures list of the Transaction ([reference](https://github.com/cosmos/cosmos-sdk/blob/e2d6cbdeb55555893ffde3f2ae0ed6db7179fd0d/x/auth/ante/fee.go#L15-L24)),
* The rest of the signatures are being verified against the actual sender of the transaction.
Knowing this, to achieve fee delegation, we have to sign the transaction using the private key of the Web3Gateway microservice, including the address of that `privateKey` as a `feePayer`, sign this transaction using the privateKey that we want to interact with Injective from, and broadcast that transaction.
## Web3Gateway API
Everyone can run the Web3Gateway microservice and provide fee delegation services to their users. An example usage can be developers who build exchange dApps on top of Injective run this microservice to offer a gasless trading environment to their traders.
This microservice exposes an API containing two core methods:
* `PrepareTx`(and `PrepareCosmosTx`)
* `BroadcastTx` (and `BroadcastCosmosTx`)
## PrepareTx
The `PrepareTx` method accepts a Message(s) including context for the transaction the user wants to execute (`chainId`, `signerAddress`, `timeoutHeight`, etc), and returns an EIP712 typed data of the particular message, including its signature within the EIP712 typed data. We can use this EIP712 typed data to sign it using any Ethereum native wallet and get the signature for users who want to interact with Injective.
The EIP712 typed data is generated from the proto definition of the Message we pass to the `PrepareTx` method.
## BroadcastTx
The `BroadcastTx` method is responsible for broadcasting the transaction to the node. Alongside the full response of the `PrepareTx` API call, we pass in the signature of the EIP712 typed data. Then, the `BroadcastTx` packs the Message into a native Cosmos transaction, prepares the transaction (including its context) and broadcasts it to Injective. As a result, the transaction hash is being returned to the user.
## Prepare/BroadcastCosmosTx
The above methods are used when we use **Ethereum Native wallets** to sign and broadcast transactions as we sign an EIP712 transaction representation.
If we want support fee delegation on Cosmos native wallets using the Web3Gateway, we can omit the PrepareCosmosTx call (or call it if we need the Web3Gateway signer's `publicKey`), prepare the transaction on the client side, sign it using a Cosmos wallet, and broadcast it using the `BroadcastCosmosTx` method.
The way this works is we add the `publicKey` of the `Web3Gateway`'s signer to the `authInfo` object in the `TxRaw` and then sign the transaction using the `privateKey` on the API side when we broadcast
The difference with the previous EIP712 approach is that we need to sign the transaction using the `Web3Gateway`'s signer in advance i.e when we generate the EIP712 → meaning that we need to use `PrepareTx` and can't generate the transaction on the client side.).
# Accounts
Source: https://docs.injective.network/developers-native/wallets/accounts
Injective defines its own custom Account type that uses Ethereum's ECDSA secp256k1 curve for keys. This satisfies the [EIP84](https://github.com/ethereum/EIPs/issues/84) for full [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) paths. The root HD path for Injective-based accounts is `m/44'/60'/0'/0.`
### Address conversion
You can easily convert between an Injective address and Ethereum address by using our utility functions in the `@injectivelabs/sdk-ts` package:
```ts theme={null}
import {
getInjectiveAddress,
getEthereumAddress,
} from "@injectivelabs/sdk-ts/utils";
const injectiveAddress = "inj1...";
const ethereumAddress = "0x..";
console.log(
"Injective address from Ethereum address => ",
getInjectiveAddress(ethereumAddress)
);
console.log(
"Ethereum address from Injective address => ",
getEthereumAddress(injectiveAddress)
);
```
### Deriving wallets
**Using Injective utility classes**
* Example code snippet on how to derive Injective Account from a private key and/or a mnemonic phrase:
```ts theme={null}
import { PrivateKey } from "@injectivelabs/sdk-ts/core/accounts";
const mnemonic =
"indoor dish desk flag debris potato excuse depart ticket judge file exit";
const privateKey =
"afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890";
const privateKeyFromMnemonic = PrivateKey.fromMnemonic(mnemonic);
const privateKeyFromHex = PrivateKey.fromPrivateKey(privateKey);
const address =
privateKeyFromMnemonic.toAddress(); /* or privateKeyFromHex.toAddress() */
console.log({
injectiveAddress: address.toBech32(),
ethereumAddress: address.toHex(),
});
```
* Example code snippet on how to derive a public address from a public key:
```ts theme={null}
import { PublicKey } from "@injectivelabs/sdk-ts/core/accounts";
const pubKey = "AuY3ASbyRHfgKNkg7rumWCXzSGCvvgtpR6KKWlpuuQ9Y";
const publicKey = PublicKey.fromBase64(pubKey);
console.log(publicKey.toAddress().toBech32());
```
* Example code snippet on how to derive an address from a private key:
```ts theme={null}
import { PublicKey } from "@injectivelabs/sdk-ts/core/accounts";
const privateKey =
"afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890";
const publicKey = PublicKey.fromPrivateKeyHex(privateKey);
const type = "/injective.crypto.v1beta1.ethsecp256k1.PubKey";
console.log(publicKey.toBase64());
```
**Without using Injective utility classes**
* Example code snippet on how to derive Injective Account from a private key and/or a mnemonic phrase:
```ts theme={null}
import { Wallet } from "ethers";
import { Address as EthereumUtilsAddress } from "ethereumjs-util";
const mnemonic =
"indoor dish desk flag debris potato excuse depart ticket judge file exit";
const privateKey =
"afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890";
const defaultDerivationPath = "m/44'/60'/0'/0/0";
const defaultBech32Prefix = "inj";
const isPrivateKey: boolean = true; /* just for the example */
const wallet = isPrivateKey
? Wallet.fromMnemonic(mnemonic, defaultDerivationPath)
: new Wallet(privateKey);
const ethereumAddress = wallet.address;
const addressBuffer = EthereumUtilsAddress.fromString(
ethereumAddress.toString()
).toBuffer();
const injectiveAddress = bech32.encode(
defaultBech32Prefix,
bech32.toWords(addressBuffer)
);
```
* Example code snippet on how to derive a public key from a private key:
```ts theme={null}
import secp256k1 from "secp256k1";
const privateKey =
"afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890";
const privateKeyHex = Buffer.from(privateKey.toString(), "hex");
const publicKeyByte = secp256k1.publicKeyCreate(privateKeyHex);
const buf1 = Buffer.from([10]);
const buf2 = Buffer.from([publicKeyByte.length]);
const buf3 = Buffer.from(publicKeyByte);
const publicKey = Buffer.concat([buf1, buf2, buf3]).toString("base64");
const type = "/injective.crypto.v1beta1.ethsecp256k1.PubKey";
```
#### Convert Cosmos address to Injective Address
As Injective has a different derivation path than the default Cosmos one, you need the `publicKey` of the account to convert a Cosmos `publicAddress` to Injective one.
Here is an example of how to do it
```typescript theme={null}
import { config } from "dotenv";
import { PublicKey } from "@injectivelabs/sdk-ts/core/accounts";
import { ChainRestAuthApi } from "@injectivelabs/sdk-ts/client/chain";
config();
(async () => {
const chainApi = new ChainRestAuthApi(
"https://rest.cosmos.directory/cosmoshub"
);
const cosmosAddress = "cosmos1..";
const account = await chainApi.fetchCosmosAccount(cosmosAddress);
if (!account.pub_key?.key) {
console.log("No public key found");
return;
}
console.log(
"injectiveAddress",
PublicKey.fromBase64(account.pub_key.key || "")
.toAddress()
.toBech32()
);
})();
```
# Wallet Connections
Source: https://docs.injective.network/developers-native/wallets/connections
Injective supports both Ethereum and Cosmos native wallets. You can use popular wallets like Metamask, Ledger, Keplr, Leap, etc. to sign transactions on Injective.
### Wallet Strategy
The recommended way to have support for all of these wallets out of the box is to use the [WalletStrategy](./strategy/) abstraction we've built. This approach will enable your dApp users to connect and interact with different wallets.
Combining it with the [MsgBroadcaster](../transactions/msgbroadcaster/) abstraction allows you to sign transactions using one function call. This is what's being used on all products like Helix, Hub, Explorer, etc., and we strongly recommend using this approach in your dApp.
In case you still want to use some wallet natively (without the WalletStrategy class), we are going to provide examples of how to connect to a dApp built on Injective via Metamask and Keplr in this doc.
### Metamask
Metamask is an Ethereum native wallet and can be used to connect and interact with your dApp built on Injective.
* **Get Injective addresses from Metamask**
```ts lines highlight={4-6,11-13} theme={null}
import { getInjectiveAddress } from "@injectivelabs/sdk-ts/utils";
const getEthereum = () => {
if (!window.ethereum) {
throw new Error("Metamask extension not installed");
}
return window.ethereum;
};
const ethereum = getEthereum();
const addresses = await ethereum.request({
method: "eth_requestAccounts",
}); /** these are evm addresses */
const injectiveAddresses = addresses.map(getInjectiveAddress);
console.log(injectiveAddresses);
```
* **Sign transactions using Metamask**
An example of how to prepare + sign + broadcast a transaction on Injective using Metamask can be found [here](../transactions/ethereum/).
### Keplr
Keplr is a Cosmos native wallet and can be used to connect and interact with your dApp built on Injective.
* **Get Injective addresses from Keplr**
```ts lines theme={null}
import { ChainId } from "@injectivelabs/ts-types";
const getKeplr = () => {
if (!window.keplr) {
throw new Error("Keplr extension not installed");
}
return window.keplr;
};
(async () => {
const keplr = getKeplr();
const chainId = ChainId.Mainnet;
await keplr.enable(chainId);
const injectiveAddresses = await keplr
.getOfflineSigner(chainId)
.getAccounts();
console.log(injectiveAddresses);
})();
```
* **Sign transactions using Keplr**
An example of how to prepare + sign + broadcast a transaction on Injective using Keplr can be found in [Cosmos Transactions](../transactions/cosmos/).
# Wallets
Source: https://docs.injective.network/developers-native/wallets/index
This section covers wallet integration and management for Injective.
* [Accounts](/developers-native/wallets/accounts/) - Learn about Injective accounts and address derivation
* [Connections](/developers-native/wallets/connections/) - Connect to different wallet providers
* [Strategy](/developers-native/wallets/strategy/) - Wallet strategy patterns for dApps
* [Offchain Data](/developers-native/wallets/offchain-data/) - Working with offchain data signing
# Offchain (Arbitrary) Data
Source: https://docs.injective.network/developers-native/wallets/offchain-data
On this page, we'll provide an example of how to sign and verify arbitrary data as per the [ADR-036](https://docs.cosmos.network/main/build/architecture/adr-036-arbitrary-signature) specification on Cosmos.
You can use the `generateArbitrarySignDoc` function from `@injectivelabs/sdk-ts` to generate ADR-36 compatible `signDoc`. You can then use it to sign/verify using a browser wallet or in a CLI environment. Make sure you are using the latest package versions.
#### Sign and verify using a browser wallet like Keplr
```typescript theme={null}
(async () => {
const message = "Offline Sign Message Example";
const signer = 'inj1...'
const chainId = 'injective-1'
// Sign Arbitrary Data
const signature = await window.keplr.signArbitrary(chainId, signer, message)
// Verify Arbitrary Data
const result = await window.keplr.verifyArbitrary(chainId, signer, message, signature)
if (result) {
console.log("Signature is valid");
}
})();
```
#### Sign and verify using PrivateKey in a CLI environment
```typescript theme={null}
import { config } from "dotenv";
import { PrivateKey } from "@injectivelabs/sdk-ts/core/accounts";
import { generateArbitrarySignDoc } from "@injectivelabs/sdk-ts/core/tx";
config();
(async () => {
const { privateKey } = PrivateKey.generate();
const injectiveAddress = privateKey.toBech32();
const publicKey = privateKey.toPublicKey();
const message = "Offline Sign Message Example";
const { signDocBuff } = generateArbitrarySignDoc(message, injectiveAddress);
const signature = await privateKey.sign(signDocBuff);
const signatureInHex = Buffer.from(signature).toString("hex");
if (
PrivateKey.verifyArbitrarySignature({
signature: signatureInHex,
signDoc: signDocBuff,
publicKey: publicKey.toHex(),
})
) {
console.log("Signature is valid");
}
})();
```
# Wallet Strategy
Source: https://docs.injective.network/developers-native/wallets/strategy
The main purpose of the `@injectivelabs/wallet-strategy` is to offer developers a way to have different wallet implementations on Injective. All of these wallets implementations are exposing the same `ConcreteStrategy` interface which means that users can just use these methods without the need to know the underlying implementation for specific wallets as they are abstracted away.
To start, you have to make an instance of the `WalletStrategy` class which gives you the ability to use different wallets out of the box. You can switch the current wallet that is used by using the `setWallet` method on the `walletStrategy` instance (note: `setWallet` is async and requires `await`).
Let's have a look at the methods that `WalletStrategy` strategy exposes and what they mean:
**Both Ethereum and Cosmos native wallets:**
* `getAddresses` gets the addresses from the connected wallet strategy. This method returns the Ethereum addresses for Ethereum native wallets (strategies) and Injective addresses for Cosmos native wallets (strategies).
* `signTransaction` signs a transaction using the corresponding wallet type method (`signCosmosTransaction` for Cosmos native wallets, `signEip712TypedData` for Ethereum native wallets)
* `sendTransaction` signs a transaction using the corresponding wallet type method (needs a `sentryEndpoint` passed to the options if we wanna use it on Ethereum native wallets - explanation can be found below)
* `getWalletDeviceType` returns the wallet connection type (mobile, browser, hardware),
**Cosmos native wallets:**
* `signCosmosTransaction` signs an Injective transaction using the connected wallet strategy,
* `getPublicKey` get the public key for the Cosmos native wallet strategies,
**Ethereum native wallets:**
* `getEthereumChainId` get the chain id for the Ethereum native wallet strategies,
* `signEip712TypedData` signs an EIP712 typed data using the connected wallet strategy,
* `sendEvmTransaction` sends an Ethereum Web3 transaction using the connected wallet strategy,
* `signEvmTransaction` signs an Ethereum Web3 transaction using the connected wallet strategy,
* `getEvmTransactionReceipt` get the transaction receipt for Ethereum native transactions for the wallet strategy,
### Arguments
The arguments passed to the WalletStrategy have the following interface:
```ts theme={null}
export interface WalletStrategyEvmOptions {
rpcUrl: string; // rpc url needed **ONLY** the Ethereum native methods on the strategies
evmChainId: EvmChainId; // needed if you are signing EIP712 typed data using the Wallet Strategies
}
export interface EthereumWalletStrategyArgs {
chainId: ChainId; // the Injective chain id
evmOptions?: WalletStrategyEvmOptions; // optional, needed only if you are using Ethereum native wallets
disabledWallets?: Wallet[]; // optional, needed if you wanna disable some wallets for being instantiated
wallet?: Wallet; // optional, the initial wallet selected (defaults to Metamask if `evmOptions` are passed and Keplr if they are not)
}
```
*Note:* When we wanna use the `sendTransaction` on Ethereum native wallets alongside the other options (chainId and address) we also need to pass a gRPC endpoint to a sentry to broadcast the transaction. This is needed because from Ethereum native wallets, we don't have access to a `broadcastTx` method as we have on Keplr or Leap to broadcast the transaction using the wallet's abstraction so we have to broadcast it on the client side directly to the chain.
### Example usage
```ts theme={null}
import { TxRaw } from '@injectivelabs/sdk-ts/types'
import { Web3Exception } from '@injectivelabs/exceptions'
import { ChainId, EvmChainId } from '@injectivelabs/ts-types'
import { WalletStrategy } from '@injectivelabs/wallet-strategy'
const chainId = ChainId.Testnet // The Injective Testnet Chain ID
const evmChainId = EvmChainId.TestnetEvm // The Injective Evm Testnet Chain ID
export const evmRpcEndpoint = `https://eth-sepolia.g.alchemy.com/v2/${process.env.APP_EVM_RPC_KEY}`
export const walletStrategy = new WalletStrategy({
chainId,
evmOptions: {
evmChainId,
rpcUrl: evmRpcEndpoint,
},
})
// Get wallet's addresses
export const getAddresses = async (): Promise => {
const addresses = await walletStrategy.getAddresses()
if (addresses.length === 0) {
throw new Web3Exception(new Error('There are no addresses linked in this wallet.'))
}
return addresses
}
// Sign an Injective transaction
export const signTransaction = async (tx: TxRaw): Promise => {
const response = await walletStrategy.signCosmosTransaction(
/*transaction:*/ { txRaw: tx, accountNumber: /* */, chainId: 'injective-1' },
/*address: */ 'inj1...',
)
return response
}
// Send an Injective transaction
export const sendTransaction = async (tx: TxRaw): Promise => {
const response = await walletStrategy.sendTransaction(
tx,
// `sentryEndpoint` needed if Ethereum wallets are used
{address: 'inj1...', chainId: 'injective-1', sentryEndpoint: 'https://grpc.injective.network' }
)
return response
}
```
# gRPC & Protobuf
Source: https://docs.injective.network/developers/concepts/grpc-protobuf
gRPC is a modern open-source high-performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking, and authentication. It is also applicable in the last mile of distributed computing to connect devices, mobile applications, and browsers to backend services.
Protobuf is the most commonly used IDL (Interface Definition Language) for gRPC. It's where you basically store your data and function contracts in the form of a proto file.
```proto theme={null}
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
}
```
# Networks
Source: https://docs.injective.network/developers/concepts/networks
Up-to-date public Endpoints can be found [here](https://docs.injective.network/develop/public-endpoints/#mainnet). We **do not recommend** using them in production for applications having high usage/traffic. There are thousands of developers using the public infrastructure and we cannot promise 100% uptime and reliability. \
\
If you still opt to use the **public** networks, you can use the `Network.{Mainnet|Testnet}Sentry` from the `@injectivelabs/networks` package.
Building dApps on Injective requires tapping into different environments and networks where you can easily test your dApp. As part of the `injective-ts` monorepo, we have the `@injectivelabs/networks` package allows developers to easily access pre-defined environments to connect to different pieces of Injective.
There are two key functions exported from this package:
* `export function getNetworkEndpoints(network: Network): NetworkEndpoints`
* `export function getEndpointsForNetwork(network: Network): OldNetworkEndpoints`
* `export function getNetworkInfo(network: Network): ChainInfo`
The first one, `getNetworkEndpoints` returns a pre-defined set of endpoints that can be used by the developers - depending on their needs. Here is the interface that gets returned from this function:
```ts theme={null}
export type NetworkEndpoints = {
indexer: string // the grpc-web port of the indexer API service
grpc: string // the grpc-web port of the sentry node
rest: string // the REST endpoint of the sentry node
rpc?: string // the REST endpoint of the Tendermint RPC
}
/** @deprecated */
export type OldNetworkEndpoints = {
exchangeApi: string // @deprecated - the grpc-web port of the exchange API service
indexerApi: string // the grpc-web port of the indexer API service
sentryGrpcApi: string // the grpc-web port of the sentry node
sentryHttpApi: string // the REST endpoint of the sentry node
tendermintApi?: string // the REST endpoint of the Tendermint RPC
chronosApi?: string // the REST endpoint of the chronos API service
exchangeWeb3GatewayApi?: string // the grpc-web port of the web3-gateway service API
}
```
Let's explain these endpoints, and what they mean:
* `indexer` is the [**grpc-web**](https://github.com/grpc/grpc-web) endpoint that we can use to connect to the `exchange/indexer` service which listens for events from the chain, processes the events, and stores the data into a MongoDB so it's easier and much more performant to serve that data than querying it straight from the chain itself,
* `grpc` is the [**grpc-web**](https://github.com/grpc/grpc-web) endpoint that we can use to connect to a sentry node. A Sentry node is a read (and light) only version of the chain that we can use to query data directly from the chain.
* `rest` is the REST endpoint that we can use to connect to a sentry node.
* `rpc` is the REST endpoint that we can use to connect to the Tendermint RPC,
The `getNetworkInfo` exports these endpoints plus the `chainId` and the default `fee` for the `Network` we want.
Using the TypeScript SDK with your infrastructure (endpoints) means you have to set up a `grpc-web` proxy in your server. To learn more about it, please reference [this documentation](https://github.com/grpc/grpc-web?tab=readme-ov-file#2-run-the-server-and-proxy).
# Convert addresses
Source: https://docs.injective.network/developers/convert-addresses
Within this document, we'll outline some examples on how to convert addresses between different formats and derivation paths.
### Convert Hex ↔ Bech32 address
As we've mentioned in the [wallet](../defi/wallet) section, Injective addresses are compatible with Ethereum addresses. You can convert between the two formats easily.
### Using TypeScript
You can easily convert between an Injective address and Ethereum address by using our utility functions in the `@injectivelabs/sdk-ts` package:
```typescript theme={null}
import {
getEthereumAddress,
getInjectiveAddress,
} from "@injectivelabs/sdk-ts/utils";
const injectiveAddress = "inj1...";
const ethereumAddress = "0x..";
console.log(
"Injective address from Ethereum address => ",
getInjectiveAddress(ethereumAddress)
);
console.log(
"Ethereum address from Injective address => ",
getEthereumAddress(injectiveAddress)
);
```
### **Convert Cosmos address to Injective Address**
As Injective has a different derivation path than the default Cosmos one, you need the `publicKey` of the account to convert a Cosmos `publicAddress` to Injective one.
### Using TypeScript
```typescript theme={null}
import { config } from "dotenv";
import { PublicKey } from "@injectivelabs/sdk-ts/core/accounts";
import { ChainRestAuthApi } from "@injectivelabs/sdk-ts/client/chain";
config();
(async () => {
const chainApi = new ChainRestAuthApi(
"https://rest.cosmos.directory/cosmoshub"
);
const cosmosAddress = "cosmos1..";
const account = await chainApi.fetchCosmosAccount(cosmosAddress);
if (!account.pub_key?.key) {
console.log("No public key found");
return;
}
console.log(
"injectiveAddress",
PublicKey.fromBase64(account.pub_key.key || "")
.toAddress()
.toBech32()
);
})();
```
More examples can be found in [wallet accounts](../defi/wallet/accounts/).
# Configuring Nuxt
Source: https://docs.injective.network/developers/dapps/configure-nuxt
## Nuxt3 - The Intuitive Web Framework
The preferred choice of UI framework to build decentralized applications on top of Injective at @InjectiveLabs is Nuxt3. We are going to help you configure Nuxt3 + the Vite builder with the `@injectivelabs` packages and some polyfills since you'll need them to interact with Crypto wallets.
### 1. Installing Nuxt 3
Follow the Getting Started guide at [Nuxt3 Docs](https://nuxt.com/docs/getting-started/installation) and setup your application.
### 2. Installing @injectivelabs packages
You can install the @injectivelabs packages using yarn.
```bash theme={null}
$ yarn add @injectivelabs/sdk-ts @injectivelabs/networks @injectivelabs/ts-types @injectivelabs/utils
## If you need Wallet Connection
$ yarn add @injectivelabs/wallet-strategy
```
These are the most commonly used packages from the `injective-ts` monorepo.
### 3. Configuring Nuxt and adding polyfills
First, add the needed polyfill packages
```bash theme={null}
$ yarn add @bangjelkoski/node-stdlib-browser
$ yarn add -D @bangjelkoski/vite-plugin-node-polyfills
```
Make sure you are using the `vue-tsc@1.8.8` , `nuxt@^3.8.1`, `typescript@^5.0.4` versions.
**Buffer**
One of the main dependencies for any crypto-related decentralized application is Buffer. To make sure we add Buffer to our project, we can install it as a dependency and then make a Nuxt plugin to import it to the global/window object:
```bash theme={null}
$ yarn add buffer
```
```ts theme={null}
// filename - plugins/buffer.client.ts
export default defineNuxtPlugin(() => {
import('buffer/').then((Buffer) => {
window.Buffer = window.Buffer || Buffer.default.Buffer
globalThis.Buffer = window.Buffer || Buffer.default.Buffer
})
})
```
### 4. Using a state management
If you are going to use `pinia` as state management, add it to your packages:
```bash theme={null}
$ yarn add @pinia/nuxt@^0.4.9
```
### 5. Using `vueuse`
We recommend adding `@vueuse/nuxt` as a dependency as it offers a lot of utility functions out of the box.
Then, we need to configure the `tsconfig.json` if you are using TypeScript (recommended). You can reference the following `tsconfig.json` as a base.
```json theme={null}
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strict": true,
"module": "NodeNext",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["@vueuse/nuxt", "@pinia/nuxt"]
},
"exclude": ["node_modules", "dist", ".output"]
}
```
### 6. nuxt.config.ts / packages.json
Before we boot our application, we need to set everything up in the `nuxt.config.ts`, the main configuration point for every Nuxt 3 application. Let's see a reference `nuxt.config.ts` and explain every line using comments so it's easier for developers to understand.
```ts theme={null}
// filename - nuxt.config.ts
import tsconfigPaths from 'vite-tsconfig-paths'
import { nodePolyfills } from '@bangjelkoski/vite-plugin-node-polyfills'
export default defineNuxtConfig({
ssr: false, // whether to pre-render your application
modules: [
// nuxtjs modules
'@pinia/nuxt',
'@vueuse/nuxt',
],
typescript: {
typeCheck: 'build', // we recommend build so you do typescript checks only on build type
},
imports: {
// automatic imports of store definitions (if you use pinia)
dirs: ['store/**'],
},
pinia: {
// import pinia definitions
autoImports: ['defineStore'],
},
plugins: [
{
// import the buffer plugin we've made
src: './plugins/buffer.client.ts',
ssr: false,
},
],
// We generate only sitemaps for the client side as we don't need a server
// Note: there is a problem with sitemaps for Vite + Nuxt3
// as usual is that it takes too much time/memory to generate
// sitemaps and the build process can fail
// on Github Actions/Netlify/Vercel/etc so we have to use another
// strategy like generating them locally and pushing them to services like
// busgnag
sourcemap: {
server: false,
client: true,
},
// Vite related config
vite: {
plugins: [
// setting up node + crypto polyfils + vite TS path resolution
tsconfigPaths(),
nodePolyfills({ protocolImports: false }),
],
build: {
sourcemap: false, // we don't generate
// default rollup options
rollupOptions: {
cache: false,
output: {
manualChunks: (id: string) => {
//
},
},
},
},
// needed for some Vite related issue for the
// @bangjelkoski/vite-plugin-node-polyfills plugin
optimizeDeps: {
exclude: ['fsevents'],
},
},
})
```
There is one optimization that you can to decrease the bundle size - add these resolutions in the `packages.json`
```
"resolutions": {
"@ethereumjs/tx": "^4.1.1",
"**/libsodium": "npm:@bangjelkoski/noop",
"**/libsodium-wrappers": "npm:@bangjelkoski/noop"
}
```
### 7. Booting our app
Finally, you can start your app locally using `yarn dev` or generate static pages using `yarn generate` which you can deploy to any static page hosting like Netlify, Vercel, etc.
# Configuring React
Source: https://docs.injective.network/developers/dapps/configure-react
## React - Library for building user interfaces
React is currently one of the most popular UI Frameworks. We are going to help you configure React + the Vite builder with the `@injectivelabs` packages and some polyfills since you'll need them to interact with Crypto wallets.
### 1. Installing React
Follow the Getting Started guide at [Vite Docs](https://vitejs.dev/guide/) and setup your application.
```bash theme={null}
$ npm create vite@latest
```
### 2. Installing @injectivelabs packages
You can install the @injectivelabs packages using yarn.
```bash theme={null}
$ yarn add @injectivelabs/sdk-ts @injectivelabs/networks @injectivelabs/ts-types @injectivelabs/utils
## If you need Wallet Connection
$ yarn add @injectivelabs/wallet-strategy
```
These are the most commonly used packages from the `injective-ts` monorepo.
### 3. Configuring Vite and adding polyfills
First, add the needed polyfill packages and buffer
One of the main dependencies for any crypto-related decentralized application is `Buffer`. To make sure we add `Buffer` To our project, we can install it as a dependency and then make a import it to the global/window object.
Example `vite.config.ts` is shared below.
```bash theme={null}
$ yarn add @bangjelkoski/node-stdlib-browser
$ yarn add -D @bangjelkoski/vite-plugin-node-polyfills
$ yarn add buffer
```
Finally, make sure to import the `buffer` in your `main.tsx` on top of the file
```typescript theme={null}
import { Buffer } from "buffer";
if (!window.Buffer) {
window.Buffer = Buffer; // Optional, for packages expecting Buffer to be global
}
```
### 4. Using a state management
React has a lot of different state managers, pick the one you are going to use and install it. You can use the build in `Context API` for state management without the need to install a third-party solution. The preferred third-party state managers are `Redux` and `Zustand`.
```bash theme={null}
$ yarn add zustand
```
### 5. vite.config.ts
The last step is to configure Vite to use the `node-polyfills` that we installed earlier.
Open up `vite.config.ts` and add `node-polyfills` inside the `plugins` array.
Your config should look like this:
```ts theme={null}
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { nodePolyfills } from "@bangjelkoski/vite-plugin-node-polyfills";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), nodePolyfills({ protocolImports: true })],
define: {
global: "globalThis",
},
resolve: {
alias: {
// others
buffer: "buffer/",
},
},
optimizeDeps: {
include: ["buffer"],
},
});
```
### 8. Booting our app
Finally, you can start your app locally using `yarn dev` or build for production using `yarn build` which you can deploy to any static page hosting like Netlify, Vercel, etc.
# DEX
Source: https://docs.injective.network/developers/dapps/example-dex
Within these short series we are going to showcase how easy it is to build a DEX on top of Injective. There is an open-sourced [DEX](https://github.com/InjectiveLabs/injective-dex) which everyone can reference and use to build on top of Injective. For those who want to start from scratch, this is the right place to start.
The series will include:
* Setting up the API clients and environment,
* Connecting to the Chain and the Indexer API,
* Connect to a user wallet and get their address,
* Fetching Spot and Derivative markets and their orderbooks,
* Placing market orders on both spot and a derivative market,
* View all positions for an Injective address.
## Setup
First, configure your desired UI framework. You can find more details on the configuration here.
To get started with the dex, we need to setup the API clients and the environment. To build our DEX we are going to query data from both the Injective Chain and the Indexer API. In this example, we are going to use the existing **Testnet** environment.
Let's first setup some of the classes we need to query the data.
```ts theme={null}
// filename: Services.ts
import {
ChainGrpcBankApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import {
IndexerGrpcSpotApi,
IndexerGrpcDerivativesApi,
} from "@injectivelabs/sdk-ts/client/indexer";
import {
IndexerGrpcSpotStreamV2,
IndexerGrpcDerivativesStreamV2
} from "@injectivelabs/sdk-ts/client/indexer";
// Getting the pre-defined endpoints for the Testnet environment
// (using TestnetK8s here because we want to use the Kubernetes infra)
export const NETWORK = Network.Testnet;
export const ENDPOINTS = getNetworkEndpoints(NETWORK);
export const chainBankApi = new ChainGrpcBankApi(ENDPOINTS.grpc);
export const indexerSpotApi = new IndexerGrpcSpotApi(ENDPOINTS.indexer);
export const indexerDerivativesApi = new IndexerGrpcDerivativesApi(
ENDPOINTS.indexer
);
export const indexerSpotStream = new IndexerGrpcSpotStreamV2(
ENDPOINTS.indexer
);
export const indexerDerivativeStream = new IndexerGrpcDerivativesStreamV2(
ENDPOINTS.indexer
);
```
Then, we also need to setup a wallet connection to allow the user to connect to our DEX and start signing transactions. To make this happen we are going to use our `@injectivelabs/wallet-strategy` package which allows users to connect with a various of different wallet providers and use them to sign transactions on Injective.
```ts theme={null}
// filename: Wallet.ts
import { Wallet } from "@injectivelabs/wallet-base";
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { WalletStrategy } from "@injectivelabs/wallet-strategy";
const chainId = ChainId.Testnet; // The Injective Chain chainId
const evmChainId = EvmChainId.Sepolia; // The EVM Chain ID
export const evmRpcEndpoint = `https://eth-sepolia.g.alchemy.com/v2/${process.env.APP_EVM_RPC_KEY}`;
export const walletStrategy = new WalletStrategy({
chainId,
evmOptions: {
rpcUrl: evmRpcEndpoint,
evmChainId,
},
});
```
If we don't want to use Ethereum native wallets, just omit the `evmOptions` within the `WalletStrategy` constructor.
Finally, to do the whole transaction flow (prepare + sign + broadcast) on Injective we are going to use the MsgBroadcaster class.
```ts theme={null}
// filename: MsgBroadcaster.ts
import { Wallet } from "@injectivelabs/wallet-base";
import { MetamaskStrategy } from "@injectivelabs/wallet-evm";
import { BaseWalletStrategy, MsgBroadcaster } from "@injectivelabs/wallet-core";
const strategyArgs: WalletStrategyArguments = {}; /** define the args */
const strategyEthArgs: ConcreteEthereumWalletStrategyArgs =
{}; /** if the wallet is an Ethereum wallet */
const strategies = {
[Wallet.Metamask]: new MetamaskStrategy(strategyEthArgs),
};
export const walletStrategy = new BaseWalletStrategy({
...strategyArgs,
strategies,
});
const broadcasterArgs: MsgBroadcasterOptions =
{}; /** define the broadcaster args */
export const msgBroadcaster = new MsgBroadcaster({
...broadcasterArgs,
walletStrategy,
});
```
## Connect to the user's wallet
Since we are using the `WalletStrategy` to handle the connection with the user's wallet, we can use its methods to handle some use cases like getting the user's addresses, sign/broadcast a transaction, etc. To find out more about the wallet strategy, you can explore the documentation interface and the method the `WalletStrategy` offers.
Note: We can switch between the "active" wallet within the `WalletStrategy` using the `setWallet` method (which is async and requires `await`).
```ts theme={null}
// filename: WalletConnection.ts
import {
WalletException,
UnspecifiedErrorCode,
ErrorType,
} from "@injectivelabs/exceptions";
import { Wallet } from "@injectivelabs/wallet-base";
import { walletStrategy } from "./Wallet.ts";
export const getAddresses = async (wallet: Wallet): Promise => {
await walletStrategy.setWallet(wallet);
const addresses = await walletStrategy.getAddresses();
if (addresses.length === 0) {
throw new WalletException(
new Error("There are no addresses linked in this wallet."),
{
code: UnspecifiedErrorCode,
type: ErrorType.WalletError,
}
);
}
if (!addresses.every((address) => !!address)) {
throw new WalletException(
new Error("There are no addresses linked in this wallet."),
{
code: UnspecifiedErrorCode,
type: ErrorType.WalletError,
}
);
}
// If we are using Ethereum native wallets the 'addresses' are the hex addresses
// If we are using Cosmos native wallets the 'addresses' are bech32 injective addresses,
return addresses;
};
```
## Querying
After the initial setup is done, let's see how to query (and stream) markets from the IndexerAPI, as well as user's balances from the chain directly.
```ts theme={null}
// filename: Query.ts
import { getDefaultSubaccountId, OrderbookWithSequence } from '@injectivelabs/sdk-ts/utils'
import { StreamManagerV2 } from '@injectivelabs/sdk-ts/client/indexer'
import {
chainBankApi,
indexerSpotApi,
indexerSpotStream,
indexerDerivativesApi,
indexerDerivativeStream,
} from './Services.ts'
export const fetchDerivativeMarkets = async () => {
return await indexerDerivativesApi.fetchMarkets()
}
export const fetchPositions = async (injectiveAddress: string) => {
const subaccountId = getDefaultSubaccountId(injectiveAddress)
return await indexerDerivativesApi.fetchPositions({ subaccountId })
}
export const fetchSpotMarkets = async () => {
return await indexerSpotApi.fetchMarkets()
}
export const fetchBankBalances = async (injectiveAddress: string) => {
return await chainBankApi.fetchBalances(injectiveAddress)
}
export const streamDerivativeMarketOrderbook = (marketId: string) => {
const streamManager = new StreamManagerV2({
id: 'derivative-orderbook',
streamFactory: () => indexerDerivativeStream.streamOrderbookV2({
marketIds: [marketId],
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderbookUpdate) => {
console.log(orderbookUpdate)
},
retryConfig: { enabled: true }
})
streamManager.start()
return streamManager
}
export const streamSpotMarketOrderbook = (marketId: string) => {
const streamManager = new StreamManagerV2({
id: 'spot-orderbook',
streamFactory: () => indexerSpotStream.streamOrderbookV2({
marketIds: [marketId],
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderbookUpdate) => {
console.log(orderbookUpdate)
},
retryConfig: { enabled: true }
})
streamManager.start()
return streamManager
}
```
Once we have these functions we can call them anywhere in our application (usually the centralized state management services like Pinia in Nuxt, or Context providers in React, etc).
## Transactions
Finally, let's make some transactions. For this example, we are going to:
1. Send assets from one address to another,
2. Make a spot limit order,
3. Make a derivative market order.
```ts theme={null}
// filename: Transactions.ts
import { toChainFormat } from '@injectivelabs/utils'
import {
MsgSend,
MsgCreateSpotLimitOrder,
MsgCreateDerivativeMarketOrder,
} from '@injectivelabs/sdk-ts/core/modules'
import {
spotPriceToChainPriceToFixed,
spotQuantityToChainQuantityToFixed,
derivativePriceToChainPriceToFixed,
derivativeQuantityToChainQuantityToFixed,
derivativeMarginToChainMarginToFixed,
getDefaultSubaccountId
} from '@injectivelabs/sdk-ts/utils'
// used to send assets from one address to another
export const makeMsgSend = ({
sender,
recipient,
amount,
denom
}: {
sender: string,
recipient: string,
amount: string, // human readable amount
denom: string
}) => {
const amount = {
denom,
amount: toChainFormat(amount, /** denom's decimals */).toFixed()
}
return MsgSend.fromJSON({
amount,
srcInjectiveAddress: sender,
dstInjectiveAddress: recipient,
})
}
// used to create a spot limit order
export const makeMsgCreateSpotLimitOrder = ({
price, // human readable number
quantity, // human readable number
orderType, // OrderType enum
injectiveAddress,
}) => {
const subaccountId = getDefaultSubaccountId(injectiveAddress)
const market = {
marketId: '0x...',
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: '', /* fetched from the chain */
minQuantityTickSize: '', /* fetched from the chain */
priceTensMultiplier: '', /** can be fetched from getSpotMarketTensMultiplier */
quantityTensMultiplier: '', /** can be fetched from getSpotMarketTensMultiplier */
}
return MsgCreateSpotLimitOrder.fromJSON({
subaccountId,
injectiveAddress,
orderType: orderType,
price: spotPriceToChainPriceToFixed({
value: price,
tensMultiplier: market.priceTensMultiplier,
baseDecimals: market.baseDecimals,
quoteDecimals: market.quoteDecimals
}),
quantity: spotQuantityToChainQuantityToFixed({
value: quantity,
tensMultiplier: market.quantityTensMultiplier,
baseDecimals: market.baseDecimals
}),
marketId: market.marketId,
feeRecipient: injectiveAddress,
})
}
// used to create a derivative market order
export const makeMsgCreateDerivativeMarketOrder = ({
price, // human readable number
margin, // human readable number
quantity, // human readable number
orderType, // OrderType enum
injectiveAddress,
}) => {
const subaccountId = getDefaultSubaccountId(injectiveAddress)
const market = {
marketId: '0x...',
baseDecimals: 18,
quoteDecimals: 6,
minPriceTickSize: '', /* fetched from the chain */
minQuantityTickSize: '', /* fetched from the chain */
priceTensMultiplier: '', /** can be fetched from getDerivativeMarketTensMultiplier */
quantityTensMultiplier: '', /** can be fetched from getDerivativeMarketTensMultiplier */
}
return MsgCreateDerivativeMarketOrder.fromJSON({
orderType: orderType,
triggerPrice: '0',
injectiveAddress,
price: derivativePriceToChainPriceToFixed({
value: price,
tensMultiplier: market.priceTensMultiplier,
quoteDecimals: market.quoteDecimals
}),
quantity: derivativeQuantityToChainQuantityToFixed({
value: quantity,
tensMultiplier: market.quantityTensMultiplier,
}),
margin: derivativeMarginToChainMarginToFixed({
value: margin,
quoteDecimals: market.quoteDecimals,
tensMultiplier: market.priceTensMultiplier,
}),
marketId: market.marketId,
feeRecipient: injectiveAddress,
subaccountId: subaccountId
})
}
```
After we have the Messages, you can use the `msgBroadcaster` client to broadcast these transactions:
```ts theme={null}
const response = await msgBroadcaster.broadcast({
msgs: /** the message here */,
injectiveAddress: signersInjectiveAddress,
})
console.log(response)
```
## Final Thoughts
What's left for you is to build a nice UI around the business logic explained above :)
# Smart Contract
Source: https://docs.injective.network/developers/dapps/example-smart-contract
Within these short series we are going to showcase how easy it is to build a dApp on top of Injective. There is an open-sourced [dApp](https://github.com/InjectiveLabs/injective-simple-sc-counter-ui) which everyone can reference and use to build on top of Injective. There are examples for Next, Nuxt and Vanilla Js. For those who want to start from scratch, this is the right place to start.
In this example we will implement the connection and interact with an example Smart Contract deployed on the Injective Chain using the injective-ts module.
The series will include:
* Setting up the API clients and environment,
* Connecting to the Chain and the Indexer API,
* Connect to a user wallet and get their address,
* Querying the smart contract ( in this case fetching the current count of the smart contract ),
* Modifying the state of the contract ( in this case incrementing the count by 1, or setting it to a specific value),
## Setup
First, configure your desired UI framework. You can find more details on the configuration here.
To get started with the dex, we need to setup the API clients and the environment. To build our DEX we are going to query data from both the Injective Chain and the Indexer API. In this example, we are going to use the existing **Testnet** environment.
Let's first setup some of the classes we need to query the data.
For interacting with the smart contract, we are going to use `ChainGrpcWasmApi` from `@injectivelabs/sdk-ts`. Also we will need the Network Endpoints we are going to use (Mainnet or Testnet), which we can find in `@injectivelabs/networks`
Example:
```js theme={null}
//filename: services.ts
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/wasm";
export const NETWORK = Network.Testnet;
export const ENDPOINTS = getNetworkEndpoints(NETWORK);
export const chainGrpcWasmApi = new ChainGrpcWasmApi(ENDPOINTS.grpc);
```
Then, we also need to setup a wallet connection to allow the user to connect to our DEX and start signing transactions. To make this happen we are going to use our `@injectivelabs/wallet-strategy` package which allows users to connect with a various of different wallet providers and use them to sign transactions on Injective.
The main purpose of the `@injectivelabs/wallet-strategy` is to offer developers a way to have different wallet implementations on Injective. All of these wallets implementations are exposing the same `ConcreteStrategy` interface which means that users can just use these methods without the need to know the underlying implementation for specific wallets as they are abstracted away.
To start, you have to make an instance of the WalletStrategy class which gives you the ability to use different wallets out of the box. You can switch the current wallet that is used by using the `setWallet` method on the walletStrategy instance (note: `setWallet` is async and requires `await`). The default is `Metamask`.
```ts theme={null}
// filename: wallet.ts
import { ChainId, EvmChainId } from "@injectivelabs/ts-types";
import { WalletStrategy } from "@injectivelabs/wallet-strategy";
const chainId = ChainId.Testnet; // The Injective Testnet Chain ID
const evmChainId = EvmChainId.TestnetEvm; // The Injective Evm Testnet Chain ID
export const evmRpcEndpoint = `https://eth-sepolia.g.alchemy.com/v2/${process.env.APP_EVM_RPC_KEY}`;
export const walletStrategy = new WalletStrategy({
chainId,
evmOptions: {
evmChainId,
rpcUrl: evmRpcEndpoint,
},
});
```
If we don't want to use Ethereum native wallets, just omit the `evmOptions` within the `WalletStrategy` constructor.
Finally, to do the whole transaction flow (prepare + sign + broadcast) on Injective we are going to use the MsgBroadcaster class.
```js theme={null}
import { Network } from "@injectivelabs/networks";
export const NETWORK = Network.Testnet;
export const msgBroadcastClient = new MsgBroadcaster({
walletStrategy,
network: NETWORK,
});
```
## Connect to the user's wallet
Since we are using the `WalletStrategy` to handle the connection with the user's wallet, we can use its methods to handle some use cases like getting the user's addresses, sign/broadcast a transaction, etc. To find out more about the wallet strategy, you can explore the documentation interface and the method the `WalletStrategy` offers.
Note: We can switch between the "active" wallet within the `WalletStrategy` using the `setWallet` method (which is async and requires `await`).
```ts theme={null}
// filename: WalletConnection.ts
import {
WalletException,
UnspecifiedErrorCode,
ErrorType,
} from "@injectivelabs/exceptions";
import { Wallet } from "@injectivelabs/wallet-base";
import { walletStrategy } from "./Wallet.ts";
export const getAddresses = async (wallet: Wallet): Promise => {
await walletStrategy.setWallet(wallet);
const addresses = await walletStrategy.getAddresses();
if (addresses.length === 0) {
throw new WalletException(
new Error("There are no addresses linked in this wallet."),
{
code: UnspecifiedErrorCode,
type: ErrorType.WalletError,
}
);
}
if (!addresses.every((address) => !!address)) {
throw new WalletException(
new Error("There are no addresses linked in this wallet."),
{
code: UnspecifiedErrorCode,
type: ErrorType.WalletError,
}
);
}
// If we are using Ethereum native wallets the 'addresses' are the hex addresses
// If we are using Cosmos native wallets the 'addresses' are bech32 injective addresses,
return addresses;
};
```
## Querying
After the initial setup is done, let's see how to query the smart contract to get the current count using the chainGrpcWasmApi service we created earlier, and calling get\_count on the Smart Contract.
```ts theme={null}
function getCount() {
const response = (await chainGrpcWasmApi.fetchSmartContractState(
COUNTER_CONTRACT_ADDRESS, // The address of the contract
toBase64({ get_count: {} }) // We need to convert our query to Base64
)) as { data: string };
const { count } = fromBase64(response.data) as { count: number }; // we need to convert the response from Base64
return count; // return the current counter value.
}
```
Once we have these functions (`getCount` or others we create) we can call them anywhere in our application (usually the centralized state management services like Pinia in Nuxt, or Context providers in React, etc).
## Modifying the State
Next we will modify the `count` state. We can do that by sending messages to the chain using the `Broadcast Client` we created earlier and `MsgExecuteContractCompat` from `@injectivelabs/sdk-ts`
The Smart Contract we use for this example has 2 methods for altering the state:
* `increment`
* `reset`
`increment` increment the count by 1, and `reset` sets the count to a given value. Note that `reset` can only be called if you are the creator of the smart contract.
When we call these functions, our wallet opens up to sign the message/transaction and broadcasts it.
Lets first see how to increment the count.
```js theme={null}
// Preparing the message
const msg = MsgExecuteContractCompat.fromJSON({
contractAddress: COUNTER_CONTRACT_ADDRESS,
sender: injectiveAddress,
msg: {
increment: {}, // we pass an empty object if the method doesn't have parameters
},
});
// Signing and broadcasting the message
const response = await msgBroadcastClient.broadcast({
msgs: msg, // we can pass multiple messages here using an array. ex: [msg1,msg2]
injectiveAddress: injectiveAddress,
});
console.log(response);
```
Now, lets see an example of how to set the counter to a specific value. Note that in this Smart Contract the count can be set to specific value only by the creator of the Smart Contract.
```js theme={null}
// Preparing the message
const msg = MsgExecuteContractCompat.fromJSON({
contractAddress: COUNTER_CONTRACT_ADDRESS,
sender: injectiveAddress,
msg: {
reset: {
count: parseInt(number, 10), // we are parsing the number variable here because usually it comes from an input which always gives a string, and we need to pass a number instead.
},
},
});
// Signing and broadcasting the message
const response = await msgBroadcastClient.broadcast({
msgs: msg,
injectiveAddress: injectiveAddress,
});
console.log(response);
```
### Full example
Now lets see a full example of this in Vanilla JS (You can find examples for specific frameworks like Nuxt And Next [HERE](https://github.com/InjectiveLabs/injective-simple-sc-counter-ui))
```js theme={null}
import { Web3Exception } from "@injectivelabs/exceptions"
import { WalletStrategy } from "@injectivelabs/wallet-strategy"
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/wasm"
import { Network, getNetworkEndpoints } from "@injectivelabs/networks"
import { getInjectiveAddress } from "@injectivelabs/sdk-ts/utils"
const chainId = ChainId.Testnet // The Injective Testnet Chain ID
const evmChainId = EvmChainId.TestnetEvm // The Injective Evm Testnet Chain ID
export const evmRpcEndpoint = `https://eth-sepolia.g.alchemy.com/v2/${process.env.APP_EVM_RPC_KEY}`
const NETWORK = Network.Testnet
const ENDPOINTS = getNetworkEndpoints(NETWORK)
const chainGrpcWasmApi = new ChainGrpcWasmApi(ENDPOINTS.grpc)
export const walletStrategy = new WalletStrategy({
chainId,
evmOptions: {
evmChainId,
rpcUrl: evmRpcEndpoint,
},
})
export const getAddresses = async (): Promise => {
const addresses = await walletStrategy.getAddresses()
if (addresses.length === 0) {
throw new Web3Exception(
new Error("There are no addresses linked in this wallet.")
)
}
return addresses
}
const msgBroadcastClient = new MsgBroadcaster({
walletStrategy,
network: NETWORK,
})
const [address] = await getAddresses()
const injectiveAddress = getInjectiveAddress(getInjectiveAddress)
async function fetchCount() {
const response = (await chainGrpcWasmApi.fetchSmartContractState(
COUNTER_CONTRACT_ADDRESS, // The address of the contract
toBase64({ get_count: {} }) // We need to convert our query to Base64
)) as { data: string }
const { count } = fromBase64(response.data) as { count: number } // we need to convert the response from Base64
console.log(count)
}
async function increment(){
const msg = MsgExecuteContractCompat.fromJSON({
contractAddress: COUNTER_CONTRACT_ADDRESS,
sender: injectiveAddress,
msg: {
increment: {},
},
})
// Signing and broadcasting the message
await msgBroadcastClient.broadcast({
msgs: msg,
injectiveAddress: injectiveAddress,
})
}
async function main() {
await fetchCount() // this will log: {count: 5}
await increment() // this opens up your wallet to sign the transaction and broadcast it
await fetchCount() // the count now is 6. log: {count: 6}
}
main()
```
## Final Thoughts
What's left for you is to build a nice UI around the business logic explained above :)
# Simple HTML example with Webpack
Source: https://docs.injective.network/developers/dapps/example-webpack
The [example](https://github.com/InjectiveLabs/injective-ts-webpack-example) is based on the [Cosmos transaction handling section](../../developers-native/transactions/cosmos/).
## Running the example
Clone the project repo:
```
git clone https://github.com/InjectiveLabs/injective-ts-webpack-example.git
```
Ensure you have npm installed and install dependencies:
```
cd injective-ts-webpack-example && npm install
```
Run the example:
```
npm start
....
[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8080/, http://[::1]:8080/
....
```
Go to the [http://localhost:8080/](http://localhost:8080/) in your browser. If you have a Kelr wallet set up and connected to the Injective testnet, you should see "Confirm Transaction" pop up window.
## How does it work?
Transaction logic is in the `src/sendTx.tx`, which is loaded by `src/index.html` Webpack is used to put eveything together and serve on the local server endpoint.
The `webpack.config.js` file configures Webpack to bundle a TypeScript application starting from `./src/sendTx.ts`, using `ts-loader` to transpile TypeScript files, and includes rules to handle `.js` and `.json` files appropriately. It resolves browser-compatible versions of Node.js core modules using the `fallback` option, enabling modules like `buffer`, `crypto`, and `stream` in the browser environment. The configuration utilizes `HtmlWebpackPlugin` to generate an HTML file based on `src/index.html`, and `ProvidePlugin` to automatically load `Buffer` and `process` variables globally. The bundled output is named `bundle.js` and placed in the `dist` directory, and the `devServer` is set up to serve content from `./dist` for development purposes.
# Building dApps
Source: https://docs.injective.network/developers/dapps/index
Injective is a Layer-1 blockchain built for finance. Injective offers developers out-of-the-box primitives for building decentralized financial applications in addition to an open and permissionless smart contracts layer providing advanced capabilities in building robust Web3 applications.
Injective is natively interoperable with several well-known blockchain networks, including Ethereum, Solana, and all IBC-enabled cosmos chains like CosmosHub, Osmosis, etc. The interoperability not only allows Injective to enable users to bridge assets from multiple chains but also allows for transferring arbitrary data - like oracle prices, etc.
Within this section we are going to explore configuring different UI frameworks to work with the `@injectivelabs` packages so you can start building decentralized applications on top of Injective. We are also going to showcase example (simple) dApps built on top of Injective.
For security reasons, we recommend using the stable package versions of NPM packages.
**Stable Package Version**
    
**Latest Package Versions:**
    
The latest versions are published using the `next` tag. For stable versions
use the `latest` tag or check npm registry for the latest stable version.
If you are looking for how to build a dApp on Injective EVM, you should check
out the guides in [your first EVM dApp](/developers-evm/dapps/).
### Create Injective dApp CLI tool
The simplest way to start your journey on Injective is using our CLI tool. To do this, simply write this command and follow the instructions in your terminal!
```bash theme={null}
$ npx @injectivelabs/create-injective-app
```
### Configuration
| Topic | Description |
| ------------------------------------------------------- | --------------------------- |
| [Configuring Nuxt](/developers/dapps/configure-nuxt/) | Configuring Nuxt 3.x + Vite |
| [Configuring React](/developers/dapps/configure-react/) | Configuring React 18 + Vite |
### dApps
| Topic | Description |
| ------------------------------------------------------------------ | -------------------------------------------------------- |
| [DEX](/developers/dapps/example-dex/) | Building a decentralized exchange on top of Injective |
| [Simple Smart Contract](/developers/dapps/example-smart-contract/) | Building a simple smart contract app on top of Injective |
| [Webpack](/developers/dapps/example-webpack/) | Simple HTML example with Webpack and Injective |
# Running examples
Source: https://docs.injective.network/developers/dapps/run-examples
Each of these examples can be run in a simple TypeScript environment.
You can clone this open-sourced repository [https://github.com/InjectiveLabs/injective-ts-examples](https://github.com/InjectiveLabs/injective-ts-examples) and follow the steps in **📚 Getting Started** to get started with your examples!
You can check the examples in the repository to make everything work out of the box in a Node environment, querying and sending a transaction.
# Overview
Source: https://docs.injective.network/developers/index
The goal of this section is to help developers build their projects on Injective
Injective is the only blockchain specifically designed for cross-chain trading, derivatives, DeFi, and Web3 applications.
Positioned to become the premier global destination for DeFi ecosystem builders,
Injective offers a multitude of advantages for developers, empowering them to build more powerful applications in less time.
This section provides comprehensive guides, tools, and resources for developers building on Injective.
Whether you're creating smart contracts in Solidity or CosmWasm, developing decentralized exchanges,
integrating with native modules, or building full-stack dApps,
this documentation will help you get started quickly and leverage Injective's unique DeFi-focused infrastructure.
Injective combines the best of both Ethereum and Cosmos ecosystems by offering dual smart contract environments:
EVM for Solidity developers and CosmWasm for Rust developers—while providing native DeFi modules
that eliminate the need to rebuild core financial infrastructure from scratch.
With ultra-fast 650ms block times, \$0.0003 transaction costs, MEV-resistant infrastructure,
and seamless interoperability with 23+ networks including Ethereum and Solana,
developers can build sophisticated cross-chain financial applications without compromising on performance,
security, or user experience.
## Key Concepts
| Term | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **injectived** | The command-line interface and node daemon that connects to and interacts with the Injective blockchain. |
| **Denom** | The native asset representation format on Injective (e.g., `factory/{creator}/{subdenom}` or `peggy{ethereumAddress}`). |
| **Trading Account** | An isolated subaccount that decouples funds from the Bank module for executing trades. One address can have unlimited trading accounts. |
| **Token Factory** | A native module allowing users and contracts to create new native tokens and swap between native tokens and CW20 tokens. |
| **Indexer API** | A high-performance data indexing service that provides fast queries, streaming updates, and historical data from the chain. |
## Why Build on Injective?
⇾ Specialized modules
⇾ Low-latency infrastructure
⇾ Quicker development
⇾ Greater capabilities
⇾ Unlock new potential
⇾ 25,000+ TPS
⇾ 650ms block times
⇾ Instant finality
⇾ MEV-resistant
⇾ \$0.0003 per transaction
⇾ Native interoperability, 23+ networks, including Ethereum & Solana
⇾ IBC-Enabled, 110+ chains
⇾ Natively integrated execution layer
⇾ WASM + EVM
⇾ Powered by Rust, Golang, and Solidity
## Developer Paths
Deploy Solidity smart contracts and build dApps using familiar Ethereum tooling on Injective's EVM-compatible execution layer.
Build secure, efficient smart contracts using Rust and the CosmWasm framework with enhanced performance and safety guarantees.
Leverage Injective's native financial primitives to build DEXs, derivatives platforms, lending protocols, and other DeFi applications.
Access low-level chain functionality including the exchange, oracle, auction, and token factory modules for advanced use cases.
## Additional Resources
Step-by-step guides for configuring UI frameworks (React, Nuxt, Webpack) and building example dApps on Injective.
Deep dives into core concepts like trading accounts, token standards, gRPC, and the Indexer API architecture.
Install, configure, and use the Injective node daemon for chain interaction, validator operations, and advanced commands.
# Commands
Source: https://docs.injective.network/developers/injectived/advanced
This section describes the commands available from `injectived`, the command line interface that connects a running `injectived` process (node).
Several `injectived` commands require subcommands, arguments, or flags to operate. To view this information, run the `injectived` command with the `--help` or `-h` flag. See `query` or `tx` for usage examples of the help flag.
For the `chain-id` argument, `injective-1` should be used for mainnet, and `injective-888` should be used for testnet.
### `add-genesis-account`
Adds a genesis account to `genesis.json`. For more information on `genesis.json`, see the Join Testnet or Join Mainnet guide.
**Syntax**
```bash theme={null}
injectived add-genesis-account
```
**Example**
```bash theme={null}
injectived add-genesis-account acc1 100000000000inj
```
### `collect-gentxs`
Collects genesis transactions and outputs them to `genesis.json`. For more information on `genesis.json`, see the Join Testnet or Join Mainnet guide [here](../../infra/join-a-network/).
**Syntax**
```bash theme={null}
injectived collect-gentxs
```
### `debug`
Helps debug the application. For a list of syntax and subcommands, run the `debug` command with the `--help` or `-h` flag:
```bash theme={null}
injectived debug -h
```
**Subcommands**:
```bash theme={null}
injectived debug [subcommand]
```
* **`addr`**: Convert an address between hex and bech32
* **`pubkey`**: Decode a pubkey from proto JSON
* **`raw-bytes`**: Convert raw bytes output (e.g., \[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]) to hex
### `export`
Exports the state to JSON.
**Syntax**
```bash theme={null}
injectived export
```
### `gentx`
Adds a genesis transaction to `genesis.json`. For more information on `genesis.json`, see the Join Testnet or Join Mainnet guide.
**Note:** The `gentx` command has many flags available. Run the `gentx` command with `--help` or `-h` to view all flags.
**Syntax**
```bash theme={null}
injectived gentx
```
**Example**
```bash theme={null}
injectived gentx myKey 100000000000inj --home=/path/to/home/dir --keyring-backend=os --chain-id=injective-1 \
--moniker="myValidator" \
--commission-max-change-rate=0.01 \
--commission-max-rate=1.0 \
--commission-rate=0.07 \
--details="..." \
--security-contact="..." \
--website="..."
```
### `help`
Shows an overview of available commands.
**Syntax**
```bash theme={null}
injectived help
```
### `init`
Initializes the configuration files for a node.
**Syntax**
```bash theme={null}
injectived init
```
**Example**
```bash theme={null}
injectived init myNode
```
### `keys`
Manages Keyring commands. These keys may be in any format supported by the Tendermint crypto library and can be used by light-clients, full nodes, or any other application that needs to sign with a private key.
For a list of syntax and subcommands, run the `keys` command with the `--help` or `-h` flag:
```bash theme={null}
injectived keys -h
```
**Subcommands**:
```bash theme={null}
injectived keys [subcommand]
```
* **`add`**: Add an encrypted private key (either newly generated or recovered), encrypt it, and save to the provided file name
* **`delete`**: Delete the given keys
* **`export`**: Export private keys
* **`import`**: Import private keys into the local keybase
* **`list`**: List all keys
* **`migrate`**: Migrate keys from the legacy (db-based) Keybase
* **`mnemonic`**: Compute the bip39 mnemonic for some input entropy
* **`parse`**: Parse address from hex to bech32 and vice versa
* **`show`**: Retrieve key information by name or address
* **`unsafe-export-eth-key`**: Export an Ethereum private key in plain text
* **`unsafe-import-eth-key`**: Import Ethereum private keys into the local keybase
\
`migrate`
Migrates the source genesis into the target version and prints to STDOUT. For more information on `genesis.json`, see the Join Testnet or Join Mainnet guide.
**Syntax**
```bash theme={null}
injectived migrate
```
**Example**
```bash theme={null}
injectived migrate v1.9.0 /path/to/genesis.json --chain-id=injective-888 --genesis-time=2023-03-07T17:00:00Z
```
### `query`
Manages queries. For a list of syntax and subcommands, run the `query` subcommand with the `--help` or `-h` flag:
```bash theme={null}
injectived query -h
```
**Subcommands**:
```bash theme={null}
injectived query [subcommand]
```
* **`account`**: Query for account by address
* **`auction`**: Querying commands for the `auction` module
* **`auth`**: Querying commands for the `auth` module
* **`authz`**: Querying commands for the `authz` module
* **`bank`**: Querying commands for the `bank` module
* **`block`**: Get verified data for a block at the given height
* **`chainlink`**: Querying commands for the `oracle` module
* **`distribution`**: Querying commands for the `distribution` module
* **`evidence`**: Query for evidence by hash or for all (paginated) submitted evidence
* **`exchange`**: Querying commands for the `exchange` module
* **`feegrant`**: Querying commands for the `feegrant` module
* **`gov`**: Querying commands for the `governance` module
* **`ibc`**: Querying commands for the `ibc` module
* **`ibc-fee`**: IBC relayer incentivization query subcommands
* **`ibc-transfer`**: IBC fungible token transfer query subcommands
* **`insurance`**: Querying commands for the `insurance` module
* **`interchain-accounts`**: Interchain accounts subcommands
* **`mint`**: Querying commands for the minting module
* **`oracle`**: Querying commands for the `oracle` module
* **`params`**: Querying commands for the `params` module
* **`peggy`**: Querying commands for the `peggy` module
* **`slashing`**: Querying commands for the `slashing` module
* **`staking`**: Querying commands for the `staking` module
* **`tendermint-validator-set`**: Get the full Tendermint validator set at given height
* **`tokenfactory`**: Querying commands for the `tokenfactory` module
* **`tx`**: Query for a transaction by hash, account sequence, or combination or comma-separated signatures in a committed block
* **`txs`**: Query for paginated transactions that match a set of events
* **`upgrade`**: Querying commands for the `upgrade` module
* **`wasm`**: Querying commands for the `wasm` module
* **`xwasm`**: Querying commands for the `wasmx` module
### `rollback`
A state rollback is performed to recover from an incorrect application state transition, when Tendermint has persisted an incorrect app hash and is thus unable to make progress. Rollback overwrites a state at height *n* with the state at height *n - 1*. The application also rolls back to height *n - 1*. No blocks are removed, so upon restarting Tendermint the transactions in block *n* will be re-executed against the application.
**Syntax**
```bash theme={null}
injectived rollback
```
### `rosetta`
Creates a Rosetta server.
**Syntax**
```bash theme={null}
injectived rosetta [flags]
```
### `start`
Runs the full node application with Tendermint in or out of process. By default, the application runs with Tendermint in process.
The `start` command has many flags available. Run the `start` command with `--help` or `-h` to view all flags.
**Syntax**
```bash theme={null}
injectived start [flags]
```
### `status`
Displays the status of a remote node. Use the `--node` or `-n` flag to specify a node endpoint.
**Syntax**
```bash theme={null}
injectived status
```
### `tendermint`
Manages the Tendermint protocol. For a list of syntax and subcommands, run the `query` subcommand with the `--help` or `-h` flag:
```bash theme={null}
injectived tendermint -h
```
**Subcommands**:
```bash theme={null}
injectived tendermint [subcommand]
```
* **`reset-state`**: Remove all the data and WAL
* **`show-address`**: Shows this node's Tendermint validator consensus address
* **`show-node-id`**: Show this node's ID
* **`show-validator`**: Show this node's Tendermint validator info
* **`unsafe-reset-all`**: Remove all the data and WAL, reset this node's validator to genesis state
* **`version`** Show Tendermint library versions
### `testnet`
Creates a testnet with the specified number of directories and populates each directory with the necessary files.
The `testnet` command has many flags available. Run the `testnet` command with `--help` or `-h` to view all flags.
**Syntax**
```bash theme={null}
injectived testnet [flags]
```
**Example**
```bash theme={null}
injectived testnet --v 4 --keyring-backend test --output-dir ./output --ip-addresses 192.168.10.2
```
### `tx`
Manages generation, signing, and broadcasting of transactions. See Using Injectived for examples.
For more information on syntax and available subcommands and, run the `tx` command with the `--help` or `-h` flag:
```bash theme={null}
injectived tx -h
```
**Subcommands**:
```bash theme={null}
injectived tx [subcommand]
```
* **`auction`**: Auction transactions subcommands
* **`authz`**: Authorization transactions subcommands
* **`bank`**: Bank transactions subcommands
* **`broadcast`**: Broadcast transactions generated offline
* **`chainlink`**: Off-Chain Reporting (OCR) subcommands
* **`crisis`**: Crisis transactions subcommands
* **`decode`**: Decode a binary encoded transaction string
* **`distribution`**: Distribution transactions subcommands
* **`encode`**: Encode transactions generated offline
* **`evidence`**: Evidence transactions subcommands
* **`exchange`**: Exchange transactions subcommands
* **`feegrant`**: Feegrant transactions subcommands
* **`gov`**: Governance transactions subcommands
* **`ibc`**: IBC transactions subcommands
* **`ibc-fee`**: IBC relayer incentivization transactions subcommands
* **`ibc-transfer`**: IBC fungible token transfer transactions subcommands
* **`insurance`**: Insurance transactions subcommands
* **`multisign`**: Generate multisig signatures for transactions generated offline
* **`oracle`**: Oracle transactions subcommands
* **`peggy`**: Peggy transactions subcommands
* **`sign`**: Sign a transaction generated offline
* **`sign-batch`**: Sign transaction batch files
* **`slashing`**: Slashing transactions subcommands
* **`staking`**: Staking transactions subcommands
* **`tokenfactory`**: Tokenfactory transactions subcommands
* **`validate-signatures`**: Validate transaction signatures
* **`vesting`**: Vesting transactions subcommands
* **`wasm`**: Wasm transactions subcommands
* **`xwasm`**: Wasmx transactions subcommands
### `validate-genesis`
Validates the genesis file at the default location or at the location specified. For more information on the genesis file, see the Join Testnet or Join Mainnet guide.
**Syntax**
```bash theme={null}
injectived validate-genesis
```
### `version`
Returns the version of Injective you’re running.
**Syntax**
```bash theme={null}
injectived version
```
# injectived
Source: https://docs.injective.network/developers/injectived/index
`injectived` is the command-line interface and node daemon that connects to Injective. Injective core is the official Golang reference implementation of the Injective node software.
## Getting Started
Learn how to install injectived
Learn how to use injectived
Learn advanced commands for injectived
# Install injectived
Source: https://docs.injective.network/developers/injectived/install
## Platform compatibiltiy guide
Check out this table to see which platform is supported to run `injectived` CLI:
| Platform | Pre-Built Binaries | Docker | From Source |
| ----------------- | ------------------ | ------ | ----------- |
| macOS (M1/ARM) | ❌ | ✅ | ✅ |
| macOS (Intel) | ❌ | ✅ | ✅ |
| Windows (x86\_64) | ❌ | ✅ | ❌ |
| Windows (ARM) | ❌ | ✅ | ❌ |
| Linux (x86\_64) | ✅ | ✅ | ✅ |
| Linux (ARM) | ❌ | ✅ | ✅ |
## Getting started with pre-built binaries
At the moment, the only supported platform to run a pre-built `injectived` CLI is Linux x86\_64. The pre-built binaries are available on the [Injective GitHub Releases page](https://github.com/InjectiveFoundation/injective-core/releases).
```bash theme={null}
# Replace the URL with the latest release from https://github.com/InjectiveFoundation/injective-core/releases
wget https://github.com/InjectiveFoundation/injective-core/releases/latest/download/linux-amd64.zip
unzip linux-amd64.zip
```
This zip file will contain these files:
* **`injectived`** - Injective daemon also CLI
* **`peggo`** - Injective Ethereum's bridge relayer daemon
* **`libwasmvm.x86_64.so`** - the WASM virtual machine support file
Note: you do not need `peggo` for deploying and instantiating smart contracts, this is for validators.
```bash theme={null}
sudo mv injectived /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib
```
Confirm your version matches the output below (your output may be slightly different if a newer version is available):
```bash theme={null}
injectived version
Version v1.14.1 (0fe59376dc)
Compiled at 20250302-2204 using Go go1.23.1 (amd64)
```
Continue to [Using injectived](/developers/injectived/use/) to learn how to use `injectived` CLI for interacting with the Injective blockchain.
## Getting started with Docker
The following command will start a container with `injectived` CLI:
```bash theme={null}
docker run -it --rm injectivelabs/injective-core:v1.14.1 injectived version
Version v1.14.1 (0fe59376d)
Compiled at 20250302-2220 using Go go1.22.11 (amd64)
```
This is compatible with most platforms and arm64 / x86\_64 architectures.
Continue to [Using injectived](/developers/injectived/use/) to learn how to use `injectived` CLI for interacting with the Injective blockchain.
## Getting started with source code
The following command will build `injectived` CLI from source code:
```bash theme={null}
git clone https://github.com/InjectiveFoundation/injective-core.git
cd injective-core && git checkout v1.14.1
make install
```
This will install `injectived` CLI to your go path.
```bash theme={null}
injectived version
Version v1.14.1 (dd7622f)
Compiled at 20250302-2230 using Go go1.24.0 (amd64)
```
(the commit hash may be different, as the open-source repository is published separately from the pre-built versions).
Continue to [Using injectived](/developers/injectived/use/) to learn how to use `injectived` CLI for interacting with the Injective blockchain.
# Using injectived
Source: https://docs.injective.network/developers/injectived/use
The following page explains what one can do via `injectived`, the command-line interface that connects to Injective. You can use `injectived` to interact with the Injective blockchain by uploading smart contracts, querying data, managing staking activities, working with governance proposals, and more.
## Prerequisites
### Ensuring injectived is installed
See [Install injectived](/developers/injectived/install/) for more information. If you have installed `injectived` successfully, you should be able to run the following command:
```bash theme={null}
injectived version
```
Please adjust your command to use the home dir properly.
```bash theme={null}
injectived keys list --home ~/.injective
```
### Using Dockerized CLI
In case when running from Docker, you have to mount the home dir to the container.
```bash theme={null}
docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys list --home /root/.injective
```
Adding a key using Dockerized CLI is straightforward.
```bash theme={null}
docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys add my_key --home /root/.injective
```
There's a breakdown of that command:
* docker runs the image `injectivelabs/injective-core:v1.14.1`
* `injectived` is the command to run the CLI from within the container
* `keys add` is the command to add a key
* `my_key` is the name of the key
* `--home /root/.injective` is the home directory for CLI inside the container
* `-v ~/.injective:/root/.injective` simply mounts the host `~/.injective` dir to the container's `/root/.injective` dir.
It will create a key pair and save it to the container's `/root/.injective/keyring-file` dir, which is the same as your host `~/.injective/keyring-file` dir.
You can list all the keys by running:
```bash theme={null}
docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys list --home /root/.injective
```
### Using the RPC endpoint
Before you can access the Injective blockchain, you need to have a node running. You can either run your own full node or connect to someone else’s.
To query the state and send transactions, you must connect to a node, which is the access point to the entire network of peer connections. You can either run your own full node or connect to someone else’s.
[Running own node](/infra/join-a-network/) is for advanced users only. For most users, it is recommended to connect to a public node.
To set the RPC endpoint, you can use the following command:
```bash theme={null}
injectived config set client node https://sentry.tm.injective.network:443
injectived config set client chain-id injective-1
```
For testnet only, you can use: `https://k8s.testnet.tm.injective.network:443` (chain-id `injective-888`)
Now try to query the state:
```bash theme={null}
injectived q bank balances inj1yu75ch9u6twffwp94gdtf4sa7hqm6n7egsu09s
balances:
- amount: "28748617927330656"
denom: inj
```
### General help
For more general information about `injectived`, run:
```bash theme={null}
injectived --help
```
For more information about a specific `injectived` command, append the `-h` or `--help` flag after the command. For example:
```bash theme={null}
injectived query --help.
```
### Configuring `injectived` client
To configure more options of `injectived`, edit the `config.toml` file in the `~/.injective/config/` directory. Keyring file is located in `~/.injective/keyring-file` directory when keyring-backend is set to `file`. It's possible to set keyring-backend to `test` or `os` as well. In case for the test, it will be also stored as file `~/.injective/keyring-test` but not password-protected.
All options in the file can be set using the CLI: `injectived config set client