# Bridge
Source: https://docs.injective.network/defi/bridge
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
## 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
This section helps traders to learn more about trading instruments and start trading on Injective
Here you can find a comprehensive overview of Injective's **exchange module**, as well as tutorials, guides, reward programs, leaderboard competitions, and general resources for developers and API traders.
* [Wallet](/defi/wallet/)
* [Trading](/defi/trading/)
* [Token Standards](/defi/tokens/)
* [Open Liquidity Program (OLP)](/defi/open-liquidity-program/)
# 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/) (not including KAVA reward 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)
50 Sep 2, 2025 00:00:00 Sep 30, 2025 00:00:00
51 Sep 30, 2025 00:00:00 Oct 28, 2025 00:00:00
52 Oct 28, 2025 00:00:00 Nov 25, 2025 00:00:00
53 Nov 25, 2025 00:00:00 Dec 23, 2025 00:00:00
54 Dec 23, 2025 00:00:00 Jan 20, 2026 00:00:00
55 Jan 20, 2026 00:00:00 Feb 17, 2026 00:00:00
56 Feb 17, 2026 00:00:00 Mar 17, 2026 00:00:00
57 Mar 17, 2026 00:00:00 Apr 14, 2026 00:00:00
58 Apr 14, 2026 00:00:00 May 12, 2026 00:00:00
59 May 12, 2026 00:00:00 Jun 9, 2026 00:00:00
60 Jun 9, 2026 00:00:00 Jul 7, 2026 00:00:00
# 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 distrubuted 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
Parameter
Definition
Value (Subject to Change)
a Liquidity Score exponent0.4
b Uptime Score exponent3
c Volume exponent0.8
MinDepth Minimum notional order size needed to generate points for Total Score 20k (BTC / INJ / ETH perp markets), 4k (other markets
MaxSpread Maximum allowable spread against mid-price in an order to generate points for Total Score 50 bps (BTC & ETH perp markets), 100 bps (other markets)
# Introduction
Source: https://docs.injective.network/defi/open-liquidity-program/introduction
An Introduction to the Open Liquidity Program (OLP)
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. Currently, **30,000 INJ** can be earned by eligible participants during each epoch (28 day period).
This amount 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 (Epoch 43 Onwards)
## 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)
12.5% of INJ rewards will be preallocated to each of the BTC/USDT PERP market, ETH/USDT PERP market, and INJ/USDT PERP market. The remaining INJ for the epoch will be allocated to each remaining eligible market with a minimum allocation of 100 INJ.
Market
Allocation
BTC/USDT Perp 13.33%
ETH/USDT Perp 13.33%
INJ/USDT Perp 13.33%
INJ/USDT Spot 5%
Other Eligible Markets Formula-based allocation
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**[ ](/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
\newline \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 \newline \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
$$
$$
\text {where}
$$
$$
LS_{N_{Bid}} = \frac{BidDepth_1}{Spread_1} \cdot \Theta_{vol} + \frac{BidDepth_2}{Spread_2} \cdot \Theta_{vol} + \ldots
\newline \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 \newline \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.
# Staking
Source: https://docs.injective.network/defi/staking
### 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
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
TokenFactory tokens are tokens that are natively integrated into the bank module of the Cosmos SDK. 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.
To learn more about creating your token on Injective, see [token launch](../../developers-defi/token-launch/).
To read more about the TokenFactory standard, see [token factory module](../../developers-native/injective/tokenfactory/).
# 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:
* NVDA
* 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)
# 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) | Every weekday from 9.30AM ET to 4PM ET | 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.
* Outside market hours, price feeds are held constant. Because the mark price does not update, it's virtually impossible to get liquidated outside of these times.
* 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-setled, 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 encure 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, an mark price is still needed prior to this time to inform liquidation prices.
To solve this, Pre-Launch Futures will initially use an 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
* [Order Types](/defi/trading/order-types/)
* [Trading Fees and Rebates](/defi/trading/fees/)
* [Margin Trading](/defi/trading/margin/)
* [Derivatives](/defi/trading/derivatives/)
# 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: $\mathrm{FundingRate = cap(TWAP + HourlyInterestRate)}$
*where* $\mathrm{TWAP = \frac{Cumulative\ Price}{(LastTimestamp + 3600 - NextFundingTimestamp)*24}}$
and *Cumulative Price* is the sum of the deltas of the last traded price and the mark price, as described herein. The system constantly calculates a *Premium*, which represents how far the contract is trading from the mark price. This is calculated as a percentage difference:
$\text{Premium} = \frac{\text{Trade Price} - \text{Mark Price}}{\text{Mark Price}}$
where *Trade Price* is the volume-weighted average price (VWAP) of trades in a particular block.
Using this figure, a *Cumulative Price* is accumulated throughout the hour. As each trade occurs, the current *Premium* is multiplied by the time passed (in seconds) since the last update:
$\text{New Cumulative Price} = \text{Old Cumulative Price} + (\text{Premium} \times \Delta t)$
**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, market makers can effectively utilize the `MsgLiquidatePosition` function to participate in Injective's liquidation mechanism and capture potential profit opportunities.
# 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 stablish 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 stablish 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.
# Gas and Fees
Source: https://docs.injective.network/defi/transaction-fees
## Gas and Fees
Learn about the differences between `Gas` and `Fees` on Injective.
Pre-requisite Readings → [Cosmos SDK Gas](https://docs.cosmos.network/main/build/modules/auth#gas--fees)
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.
# 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)
# MCP Server
Source: https://docs.injective.network/developers/ai/mcp
Connect AI assistants to Injective documentation using the Model Context Protocol
The Injective documentation provides a Model Context Protocol (MCP) server that allows AI assistants like
Claude, Cursor, and other MCP-compatible tools to **search and retrieve information** from the Injective docs.
This is powerful not only for documentation search, but also useful when **used in combination** with other MCP servers and with AI agents.
For example, you could add vibe code Injective applications by connecting your agentic development tools, such as Cursor,
using this MCP server to **add Injective-specific knowledge** to the LLM's general-purpose software writing abilities.
Details on how to configure your developer tools are included below.
## What is MCP?
[MCP](https://modelcontextprotocol.io/introduction) is an **open standard** that enables AI assistants to connect to external data sources and tools.
Instead of relying solely on training data, AI assistants can use MCP to access real-time, authoritative information directly from documentation.
## Why use the Injective MCP server?
When you connect an AI assistant to the Injective MCP server, it can:
* Search the complete Injective documentation
* Find code examples, API references, and guides
* Answer questions with up-to-date information
* Provide direct links to relevant documentation pages
This is particularly useful when building on Injective, as the AI can help you find the right documentation for your specific use case.
## MCP server details
| Property | Value |
| -------------- | ------------------------------------ |
| Endpoint | `https://docs.injective.network/mcp` |
| Transport | HTTP (Streamable HTTP) |
| Available tool | `SearchInjectiveDocs` |
## Connecting to the MCP server
For MCP-compatible clients, use the endpoint URL directly:
```
https://docs.injective.network/mcp
```
The server uses Streamable HTTP transport, which is supported by most modern MCP clients.
Add the MCP server in Cursor's settings:
1. Open Cursor Settings
2. Navigate to **Features** → **MCP Servers**
3. Click **Add new MCP server**
4. Enter the following:
* **Name**: `injective-docs`
* **Type**: `command`
* **Command**: `npx mcp-remote https://docs.injective.network/mcp`
Add the following to your Claude Desktop configuration file:
Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json theme={null}
{
"mcpServers": {
"injective-docs": {
"command": "npx",
"args": [
"mcp-remote",
"https://docs.injective.network/mcp"
]
}
}
}
```
Edit `%APPDATA%\Claude\claude_desktop_config.json`:
```json theme={null}
{
"mcpServers": {
"injective-docs": {
"command": "npx",
"args": [
"mcp-remote",
"https://docs.injective.network/mcp"
]
}
}
}
```
After saving the configuration, restart Claude Desktop. The Injective Docs tool will appear in Claude's available tools.
## Using the MCP server
Once connected, you can ask your AI assistant questions about Injective.
The assistant will automatically use the `SearchInjectiveDocs` tool to find relevant information.
**Example prompts:**
* "How do I create a wallet on Injective?"
* "Show me how to deploy a CosmWasm smart contract"
* "What are the network endpoints for Injective mainnet?"
* "How do I use the Token Factory module?"
* "Explain how margin trading works on Injective"
The AI will search the documentation and provide answers with links to the relevant pages.
## MCP Tool reference
### `SearchInjectiveDocs`
Searches across the Injective documentation to find relevant information.
**Parameters:**
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------------------------------------------- |
| `query` | string | Yes | The search query to find relevant documentation |
**Returns:**
Contextual content with titles and direct links to documentation pages.
The tool performs a semantic search across the entire Injective documentation corpus, matching your query against page content, headings, and code examples.
Results are ranked by relevance and returned with extracted snippets that best answer your query, along with source URLs for further reading.
**Example:**
Request:
```json theme={null}
{
"tool": "SearchInjectiveDocs",
"arguments": {
"query": "how to create a token using token factory"
}
}
```
Response:
```json theme={null}
{
"content": [
{
"type": "text",
"text": "# Token Factory\n\nThe Token Factory module allows you to create and manage custom tokens on Injective...\n\n## Creating a Token\n\nTo create a new token, use the `tokenfactory/create-denom` message...\n\nSource: https://docs.injective.network/developers/concepts/token-factory"
}
]
}
```
# 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
# 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.
# Market min price tick size calculation
Source: https://docs.injective.network/developers/concepts/calculation-min-price-tick-size
# Market min price tick size
The min market price tick size for an order price - if a market has an minPriceTickSick of `0.001` and order submission with the price of `0.0011` will be rejected.
Note that calculating the formula for calculating a spot and quote market price tick size are different.
### Spot market
1. UI human readable to chain format:
Using INJ/USDT market which has 18 base decimals and 6 quote decimals as an example, here's how we convert the value to the chain format:
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
const value = toChainFormat(value, quoteDecimals - baseDecimals).toFixed();
```
2. Chain format to UI human readable format:
Using INJ/USDT market which has 18 base decimals and 6 quote decimals as an example, here's how we convert the value to the UI human readable format:
```ts theme={null}
import { toHumanReadable } from "@injectivelabs/utils";
const value = toHumanReadable(value, quoteDecimals - baseDecimals).toFixed();
```
### Derivative market
1. UI human readable to chain format:
Using INJ/USDT perp market which has 6 quote decimals as an example, here's how we convert the value to the chain format:
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
const value = toChainFormat(value, -quoteDecimals).toFixed();
```
2. Chain format to UI human readable format:
Using INJ/USDT perp market which has 6 quote decimals as an example, here's how we convert the value to the UI human readable format:
```ts theme={null}
import { toHumanReadable } from "@injectivelabs/utils";
const value = toHumanReadable(value, quoteDecimals).toFixed();
```
# Market min quantity tick size calculation
Source: https://docs.injective.network/developers/concepts/calculation-min-quantity-tick-size
The min market quantity tick size for an order price - if a market has an minQuantityTickSize of `0.001` and order submission with the quantity of `0.0011` will be rejected.
Note that derivate markets shares the same format for minQuantityTickSize between UI and the chain, so no formatting is required.
## Spot market
1. UI human readable to chain format:
Using on a INJ/USDT market which has 18 base decimals and 6 quote decimals as an example, here's how we convert the value to the chain format:
```js theme={null}
import { toChainFormat } from "@injectivelabs/utils";
const chainFormat = toChainFormat(value, baseDecimals).toFixed();
```
2. Chain format to UI human readable format:
Using INJ/USDT market which has 18 base decimals and 6 quote decimals as an example, here's how we convert the value to the UI human readable format:
```js theme={null}
import { toHumanReadable } from "@injectivelabs/utils";
const humanReadableFormat = toHumanReadable(
minQuantityTickSize,
baseDecimals
).toFixed();
```
# CosmJs Support
Source: https://docs.injective.network/developers/concepts/cosmjs-support
Injective is not natively supported on the `@cosmjs` packages. It's highly recommended to use our `@injectivelabs` packages to interact with Injective.
If you are familiar with the `@cosmjs` packages we are exporting similar interfaces/classes that work the same as the classes on `@cosmjs` but have support for Injective as well.
Again, keep in mind that the recommended approach is to use the Injective's standard approach, which you can learn more about in [Cosmos transactions](../../developers-native/transactions/cosmos/).
## Usage using Keplr
Here is an example on how to use the `@injectivelabs` alternatives from the `@cosmjs` packages with Keplr:
```ts theme={null}
import {
PrivateKey,
InjectiveStargate,
} from "@injectivelabs/sdk-ts/cosmjs";
import { OfflineDirectSigner } from "@cosmjs/proto-signing";
import { assertIsBroadcastTxSuccess } from '@cosmjs/stargate'
(async () => {
// Enable Keplr
await window.keplr.enable(chainId);
// Get the offline signer
const offlineSigner = window.getOfflineSigner(chainId);
const [account] = await offlineSigner.getAccounts();
// Initialize the stargate client
const client =
await InjectiveStargate.InjectiveSigningStargateClient.connectWithSigner(
"https://lcd-cosmoshub.keplr.app/rest",
offlineSigner,
);
})
const amount = {
denom: "inj",
amount: amount.toString(),
};
const fee = {
amount: [
{
denom: "inj",
amount: "5000000000000000",
},
],
gas: "200000",
};
const result = await client.sendTokens(
account.address,
recipient,
[amount],
fee,
""
);
assertIsBroadcastTxSuccess(result);
if (result.code !== undefined && result.code !== 0) {
alert("Failed to send tx: " + result.log || result.rawLog);
} else {
alert("Succeed to send tx:" + result.transactionHash);
}
})()
```
## Usage in a CLI/Node environment
Here is an example on how to use the `@injectivelabs` alternatives from the `@cosmjs` packages in a node or CLI environment.
Again, keep in mind that the recommended approach is to use the [MsgBroadcasterWithPk](../../developers-native/transactions/private-key/#example-with-msgbroadcasterwithpk) abstraction to follow the Injective's standard approach.
```ts theme={null}
import {
PrivateKey,
InjectiveStargate,
InjectiveDirectEthSecp256k1Wallet,
} from "@injectivelabs/sdk-ts/cosmjs";
import { getStdFee } from "@injectivelabs/utils";
import { OfflineDirectSigner } from "@cosmjs/proto-signing";
import { Network, getNetworkInfo } from "@injectivelabs/networks";
(async () => {
const network = getNetworkInfo(Network.Testnet);
const privateKeyHash = process.env.PRIVATE_KEY as string;
const privateKey = PrivateKey.fromHex(privateKeyHash);
const injectiveAddress = privateKey.toBech32();
const wallet = (await InjectiveDirectEthSecp256k1Wallet.fromKey(
Buffer.from(privateKeyHash, "hex")
)) as OfflineDirectSigner;
const [account] = await wallet.getAccounts();
const client =
await InjectiveStargate.InjectiveSigningStargateClient.connectWithSigner(
network.rpc as string,
wallet
);
const recipient = injectiveAddress;
const amount = {
denom: "inj",
amount: "1000000000",
};
const txResponse = await client.sendTokens(
account.address,
recipient,
[amount],
getStdFee(),
"Have fun with your star coins"
);
if (txResponse.code !== 0) {
console.log(`Transaction failed: ${txResponse.rawLog}`);
} else {
console.log(
`Broadcasted transaction hash: ${JSON.stringify(
txResponse.transactionHash
)}`
);
}
})();
```
# 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;
}
```
# Technical Concepts
Source: https://docs.injective.network/developers/concepts/index
Learning these concepts can help you be a more efficient dApps developer on top of Injective.
We'll keep the explanations brief so only the necessary context is shared with the reader.
We encourage developers to explore these concepts more thoroughly at their own convenience.
# 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).
# 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).
# Sentry Node
Source: https://docs.injective.network/developers/concepts/sentry-node
Sentry node is a read-only full node running the Injective chain. A full node is a server running a chain's binary (its software) that fully validates transactions and blocks of a blockchain and keeps a full record of all historic activity. A full node is distinct from a pruned node that processes only block headers and a small subset of transactions. Running a full node requires more resources than a pruned node. Validators can decide to run either a full node or a pruned node, but they need to make sure they retain enough blocks to be able to validate new blocks.
We query the sentry node to get on-chain data served on our decentralized application.
# Token Factory
Source: https://docs.injective.network/developers/concepts/token-factory
The Token Factory module on Injective which allows users and contracts to create new native tokens and swap native tokens with CW20 tokens using the Mint + Burn model. This is an important feature to have on chain because representing assets from different sources to a native bank denom is crucial to allow users to access the rest of the on-chain modules like exchange, auction, insurance funds, etc. The token factory denoms are in the following format `factory/{creator address}/{subdenom}`.
Combined with the `CW20AdapterContract` which acts as a creator, we allow CW20 assets to be natively represented on Injective as Token Factory denoms. The way it works is that CW20 assets are held by the `CW20AdapterContract` and minted as a factory denom for the injective address and when we want to redeem them back to CW20, they are burned from the bank module and unlocked from the `CW20AdapterContract` back to the owner address.
## Example on how to 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
```
## Example on how to 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
```
# 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.
# 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.
## 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
## What Are You Interested In?
Build smart contracts and dApps on Injective's Ethereum Virtual Machine
Build smart contracts and dApps on Injective's CosmWasm module
Build decentralized finance applications, for example a DEX like Helix
Build using Injective's native modules. (low-level)
# 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/InjectiveLabs/injective-chain-releases/releases).
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.14.1-1740773301/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 `.
## Generate, Sign, and Broadcast a Transaction
Running the following command sends INJ tokens from the sender's account to the recipient's account. `1000inj` is the amount of INJ tokens to send, where `1 INJ = 10^18 inj`, so `1000inj` is a really small amount.
```bash theme={null}
injectived tx bank send MY_WALLET RECEIVER_WALLET 1000inj --from MY_WALLET
```
The following steps are performed:
* Generates a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console.
* Ask the user for confirmation to send the transaction from the `$MY_WALLET` account.
* Fetch `$MY_WALLET` from the keyring. This is possible because we have set up the CLI's keyring in a previous step.
* Sign the generated transaction with the keyring's account.
* Broadcast the signed transaction to the network. This is possible because the CLI connects to the public Injective node's RPC endpoint.
The CLI bundles all the necessary steps into a simple-to-use user experience. However, it is possible to run all the steps individually as well.
### (Only) Generating a Transaction
Generating a transaction can simply be done by appending the `--generate-only` flag on any `tx` command, e.g.,
```bash theme={null}
injectived tx bank send MY_WALLET RECEIVER_WALLET 1000inj --from MY_WALLET --generate-only
```
This will output the unsigned transaction as JSON in the console. We can also save the unsigned transaction to a file (to be passed around between signers more easily) by appending `> unsigned_tx.json` to the above command.
### Signing a pre-generated Transaction
Signing a transaction using the CLI requires the unsigned transaction to be saved in a file. Let's assume the unsigned transaction is in a file called `unsigned_tx.json` in the current directory (see previous paragraph on how to do that). Then, simply run the following command:
```bash theme={null}
injectived tx sign unsigned_tx.json --from=MY_WALLET
```
This command will decode the unsigned transaction and sign it with `SIGN_MODE_DIRECT` with `MY_WALLET`'s key, which we already set up in the keyring. The signed transaction will be output as JSON to the console, and, as above, we can save it to a file by appending `> signed_tx.json` to the commandline.
```bash theme={null}
injectived tx sign unsigned_tx.json --from=MY_WALLET > signed_tx.json
```
Some useful flags to consider in the `tx sign` command:
* `--sign-mode`: you may use `amino-json` to sign the transaction using `SIGN_MODE_LEGACY_AMINO_JSON`,
* `--offline`: sign in offline mode. This means that the `tx sign` command doesn't connect to the node to retrieve the signer's account number and sequence, both needed for signing. In this case, you must manually supply the `--account-number` and `--sequence` flags. This is useful for offline signing, i.e., signing in a secure environment which doesn't have access to the internet.
### Signing with multiple signers (Multi Sig)
Signing with multiple signers is done with the `tx multi-sign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multi-sign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method.
For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run:
```bash theme={null}
# Let signer1 sign the unsigned tx.
injectived tx multi-sign unsigned_tx.json signer_key_1 > partial_tx_1.json
# Now signer1 will send the partial_tx_1.json to the signer2.
# Signer2 appends their signature:
injectived tx multi-sign partial_tx_1.json signer_key_2 > partial_tx_2.json
# Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature:
injectived tx multi-sign partial_tx_2.json signer_key_3 > partial_tx_3.json
```
### Broadcasting a Transaction
Broadcasting a transaction is done using the following command:
```bash theme={null}
injectived tx broadcast tx_signed.json
```
You may optionally pass the `--broadcast-mode` flag to specify which response to receive from the node:
* `block`: the CLI waits for the tx to be included in a block.
* `sync`: the CLI waits for a CheckTx execution response only, query transaction result manually to ensure it was included.
* `async`: the CLI returns immediately (transaction might fail) - DO NOT USE.
To query the transaction result, you can use the following command:
```bash theme={null}
injectived tx query TX_HASH
```
## Additional Troubleshooting
Sometimes the config is not set correctly. You can force the correct node RPC endpoint by adding the following to the commandline. When sharing commands with others, it is recommended to have all the flags explicitly set in the commandline. (chain-id, node, keyring-backend, etc.)
```bash theme={null}
injectived --node https://sentry.tm.injective.network:443
```
# Network Information
Source: https://docs.injective.network/developers/network-information
Essential information about the Injective network
* Chain ID: `injective-1`
* Node: `https://sentry.tm.injective.network:443`
Note that the Injective Chain ID for EVM is `1776`.
However, it natively uses a chain ID of `injective-1`.
While the chain IDs are different, they map to the **same** network.
See [Injective EVM Mainnet network information](/developers-evm/network-information#injective-evm-mainnet) for more details.
* Chain ID: `injective-888`
* Node: `https://testnet.sentry.tm.injective.network:443`
Note that the Injective Chain ID for EVM is `1439`.
However, it natively uses a chain ID of `injective-888`.
While the chain IDs are different, they map to the **same** network.
See [Injective EVM Testnet network information](/developers-evm/network-information#injective-evm-testnet) for more details.
* Chain ID: `injective-777`
* Node: `https://devnet.sentry.tm.injective.network:443`
Note that the Injective EVM does not have an equivalent of Devnet
See [EVM network information](/developers-evm/network-information/) for more details.
# Testnet Proposals
Source: https://docs.injective.network/developers/testnet-proposals
Let's say that you want to submit a proposal on `testnet`. Because there is a short period of voting time for the proposals, we recommend lowering the deposit on the proposal to not make the proposal go into voting stage directly. Basically, it should be slightly less than `min_deposit` value.
Once you submit the proposal, you should reach out to the team:
1. Join the [Injective Discord server](https://discord.gg/injective) and find the relevant channel.
2. Join the [Injective Developer Telegram channel](https://t.me/+8Y_0HOFLhnRlZDU9).
Here is an example for the `GrantProviderPrivilegeProposal`
```bash theme={null}
injectived tx oracle grant-provider-privilege-proposal YOUR_PROVIDER \
YOUR_ADDRESS_HERE \
--title="TITLE OF THE PROPOSAL" \
--description="Registering PROVIDER as an oracle provider" \
--chain-id=injective-888 \
--from=local_key \
--node=https://testnet.sentry.tm.injective.network:443 \
--gas-prices=160000000inj \
--gas=20000000 \
--deposit="40000000000000000000inj" <-- use this amount
```
# About Injective
Source: https://docs.injective.network/index
Injective is a high-performance, interoperable layer-one blockchain for building premier Web3 financial applications.
## What is Injective?
Injective is the blockchain built for finance.
It 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.
***
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
# Getting Started
Welcome to your journey of exploring Injective! Before asking questions, please try using the search functionality in the docs. Our goal is to make the documentation self-sufficient, ensuring that onboarding is smooth and everyone can easily learn more about Injective.
## Quickstart Guide to Injective
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?
Should you have some questions or feedback, you can reach out to 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).
# 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
```
***
# Overview
Source: https://docs.injective.network/infra/index
This section helps node operators and validators to run, upgrade and maintain their sentry/validator nodes.
* [Mainnet Validator](/infra/validator-mainnet/)
* [Testnet Validator](/infra/validator-testnet/)
# 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
This guide will walk you through the process of setting up a standalone network locally, as well as running a node on Mainnet or Testnet.
You can also find the hardware requirements for each network in the respective tabs.
To easily set up a local node, download and run the `setup.sh` script. This will initialize your local Injective network.
```bash theme={null}
wget https://raw.githubusercontent.com/InjectiveLabs/injective-chain-releases/master/scripts/setup.sh
chmod +x ./setup.sh # Make the script executable
./setup.sh
```
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—please 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.
#### 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).
#### 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 chain releases repo](https://github.com/InjectiveLabs/injective-chain-releases/releases/) for the most recent releases. Non-validator node operators do not need to install `peggo`.
```bash theme={null}
wget https://github.com/InjectiveLabs/injective-chain-releases/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 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 Mainnet has a chain-id of "injective-1"
injectived init $MONIKER --chain-id injective-1
```
Running the `init` command will create `injectived` default configuration files at `~/.injectived`.
#### Prepare Configuration to Join Mainnet
You should now update the default configuration with the Mainnet'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/mainnet-config
# copy genesis file to config directory
cp mainnet-config/10001/genesis.json ~/.injectived/config/genesis.json
# copy config file to config directory
cp mainnet-config/10001/app.toml ~/.injectived/config/app.toml
```
You can also run verify the checksum of the genesis checksum - 573b89727e42b41d43156cd6605c0c8ad4a1ce16d9aad1e1604b02864015d528
```bash theme={null}
sha256sum ~/.injectived/config/genesis.json
```
Then update the `seeds` field in `~/.injectived/config/config.toml` with the contents of `mainnet-config/10001/seeds.txt` and update the `timeout_commit` to `300ms`.
```bash theme={null}
cat mainnet-config/10001/seeds.txt
nano ~/.injectived/config/config.toml
```
#### 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
```
The service should be stopped before and started after the 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
```
#### Sync with the network
**Option 1. State-Sync**
*To be added soon*
**Option 2. Snapshots**
**Pruned**
1. [Polkachu](https://polkachu.com/tendermint_snapshots/injective).
2. [HighStakes](https://tools.highstakes.ch/files/injective.tar.gz).
3. [Imperator](https://www.imperator.co/services/chain-services/mainnets/injective).
4. [Bware Labs](https://bwarelabs.com/snapshots).
5. [AutoStake](https://autostake.com/networks/injective/#validator).
Should the Injective `mainnet-config seeds.txt` list not work (the node fails to sync blocks), ChainLayer, Polkachu, and Autostake maintain peer lists (can be used in the `persistent_peers` field in `config.toml`) or addressbooks (for faster peer discovery).
**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)
# Running a node
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.
# Upgrade your node
Source: https://docs.injective.network/infra/upgrade-node
### 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.
See [InjectiveLabs/injective-chain-releases](https://github.com/InjectiveLabs/injective-chain-releases/releases) for the most recent and prior chain releases.
### 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.
### 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 links to snapshots as well as directions for applying the snapshot/syncing the node, see [Join Mainnet](./join-a-network/).
# Mainnet
Source: https://docs.injective.network/infra/validator-mainnet/index
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 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
Source: https://docs.injective.network/infra/validator-mainnet/peggo
If you're on this page then you've probably become a Validator on Injective. Congratulations! Configuring `peggo` is the final step of your setup.
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).
# Exchange API Reference
Source: https://docs.injective.network/redirects/https_api_injective_exchange
# The Graph
Source: https://docs.injective.network/redirects/https_docs_substreams_dev_documentation_how_to_guides_injective
# Injective TypeScript SDK
Source: https://docs.injective.network/redirects/https_docs_ts_injective_network
# Injective Explorer
Source: https://docs.injective.network/redirects/https_explorer_injective_network
# Injective CosmWasm SDK
Source: https://docs.injective.network/redirects/https_github_com_InjectiveLabs_cw_injective
# Injective Rust SDK
Source: https://docs.injective.network/redirects/https_github_com_InjectiveLabs_injective_rust
# Injective Go SDK
Source: https://docs.injective.network/redirects/https_github_com_InjectiveLabs_sdk_go
# Injective Python SDK
Source: https://docs.injective.network/redirects/https_github_com_InjectiveLabs_sdk_python
# Injective Hub
Source: https://docs.injective.network/redirects/https_hub_injective_network
# 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
# Chain API Reference
Source: https://docs.injective.network/redirects/https_sentry_lcd_injective_network_swagger
# EVM Testnet Faucet
Source: https://docs.injective.network/redirects/https_testnet_faucet_injective_network
# Using Injective Modules and Queries in CosmWasm
Source: https://docs.injective.network/developers-cosmwasm/cosmwasm-any
This guide provides a comprehensive overview of how to interact with Injective's modules and queries in CosmWasm using `Any` messages and queries. The older [injective-cosmwasm](https://github.com/InjectiveLabs/cw-injective/tree/dev/packages/injective-cosmwasm) package, which relied on JSON-encoded messages, is no longer maintained and may become outdated. This guide focuses on the recommended approach using protobuf-encoded `Any` messages and queries, which is more efficient and aligned with modern CosmWasm standards.
## What are `Any` Messages in CosmWasm?
In CosmWasm, `Any` messages are part of the `CosmosMsg` enum, allowing you to send messages wrapped in a protobuf `Any` type supported by the chain. They replace the deprecated `Stargate` messages (still available under the `stargate` feature flag) with improved naming and syntax. `Any` messages are feature-gated with `cosmwasm_2_0`, meaning they require a chain running CosmWasm 2.0 which is supported by Injective. Here’s a snippet of the `CosmosMsg` definition:
```rust theme={null}
pub enum CosmosMsg {
// ...
#[cfg(feature = "cosmwasm_2_0")]
Any(AnyMsg),
// ...
}
pub struct AnyMsg {
pub type_url: String,
pub value: Binary,
}
```
The `type_url` specifies the protobuf message type, and `value` contains the serialized message data.
## Why Use This Method?
The `injective-cosmwasm` package used JSON-based messages, which are less efficient and may not remain compatible with future updates. The new `Any` message approach uses protobuf encoding, offering better performance, type safety, and compatibility with CosmWasm 2.0+. This is now the recommended method for interacting with Injective's modules and queries.
## Sending Messages
To send messages, you create a protobuf message, encode it, and wrap it in an `Any` message. Below is an example of creating a spot market order on Injective's exchange module.
### Example: Creating a Spot Market Order
```rust theme={null}
use cosmwasm_std::{AnyMsg, CosmosMsg, StdResult};
use injective_cosmwasm::{InjectiveMsgWrapper, OrderType, SpotMarket};
use injective_math::{round_to_min_tick, round_to_nearest_tick, FPDecimal};
use injective_std::types::injective::exchange::v1beta1 as Exchange;
use prost::Message;
pub fn create_spot_market_order_message(
price: FPDecimal,
quantity: FPDecimal,
order_type: OrderType,
sender: &str,
subaccount_id: &str,
fee_recipient: &str,
market: &SpotMarket,
) -> StdResult> {
let msg = create_spot_market_order(price, quantity, order_type, sender, subaccount_id, fee_recipient, market);
let mut order_bytes = vec![];
Exchange::MsgCreateSpotMarketOrder::encode(&msg, &mut order_bytes).unwrap();
Ok(CosmosMsg::Any(AnyMsg {
type_url: Exchange::MsgCreateSpotMarketOrder::TYPE_URL.to_string(),
value: order_bytes.into(),
}))
}
fn create_spot_market_order(
price: FPDecimal,
quantity: FPDecimal,
order_type: OrderType,
sender: &str,
subaccount_id: &str,
fee_recipient: &str,
market: &SpotMarket,
) -> Exchange::MsgCreateSpotMarketOrder {
let rounded_quantity = round_to_min_tick(quantity, market.min_quantity_tick_size);
let rounded_price = round_to_nearest_tick(price, market.min_price_tick_size);
Exchange::MsgCreateSpotMarketOrder {
sender: sender.to_string(),
order: Some(Exchange::SpotOrder {
market_id: market.market_id.as_str().into(),
order_info: Some(Exchange::OrderInfo {
subaccount_id: subaccount_id.to_string(),
fee_recipient: fee_recipient.to_string(),
price: rounded_price.to_string(),
quantity: rounded_quantity.to_string(),
cid: "".to_string(),
}),
order_type: order_type as i32,
trigger_price: "".to_string(),
}),
}
}
```
**Steps:**
1. Construct the `MsgCreateSpotMarketOrder` protobuf message with order details.
2. Encode it into bytes using `prost::Message::encode`.
3. Wrap it in an `AnyMsg` with the correct `type_url`.
4. Return it as a `CosmosMsg::Any`.
This approach can be adapted for other modules (e.g., auction, tokenfactory) by using the appropriate protobuf message and `type_url`.
## Performing Queries
Queries are performed using `QuerierWrapper` with `InjectiveQueryWrapper`. You can use pre-built queriers from `injective_std` or send raw queries. Below are examples covering different modules.
### Example: Querying a Spot Market (Exchange Module)
```rust theme={null}
use cosmwasm_std::{to_json_binary, Binary, Deps, StdResult};
use injective_cosmwasm::InjectiveQueryWrapper;
use injective_std::types::injective::exchange::v1beta1::ExchangeQuerier;
pub fn handle_query_spot_market(deps: Deps, market_id: &str) -> StdResult {
let querier = ExchangeQuerier::new(&deps.querier);
to_json_binary(&querier.spot_market(market_id.to_string())?)
}
```
**Steps:**
1. Create an `ExchangeQuerier` from `deps.querier`.
2. Call `spot_market` with the `market_id`.
3. Serialize the response to JSON and return it as a `Binary`.
### Example: Querying Bank Parameters (Bank Module)
```rust theme={null}
use cosmwasm_std::{to_json_binary, Binary, Deps, StdResult};
use injective_cosmwasm::InjectiveQueryWrapper;
use injective_std::types::cosmos::bank::v1beta1::BankQuerier;
pub fn handle_query_bank_params(deps: Deps) -> StdResult {
let querier = BankQuerier::new(&deps.querier);
to_json_binary(&querier.params()?)
}
```
**Steps:**
1. Create a `BankQuerier` from `deps.querier`.
2. Call `params` to fetch bank module parameters.
3. Serialize and return the result.
## Working with Other Modules
The same principles apply to other Injective modules like auction, insurance, oracle, permissions, and tokenfactory, as well as the Cosmos native modules. For example:
* **Auction Module**: Use `AuctionQuerier` for queries or encode `MsgBid` as an `Any` message.
* **Tokenfactory Module**: Encode `MsgCreateDenom` or use `TokenFactoryQuerier`.
Refer to [injective\_std](https://github.com/InjectiveLabs/injective-rust/tree/dev/packages/injective-std) for specific message types and queriers.
# Creating UIs
Source: https://docs.injective.network/developers-cosmwasm/create-uis-guide
More comprehensive docs about creating UIs as well as bootstrapping options
can be found on the [dApps docs](/developers/dapps/).
We've interacted with our contract through the Injective CLI, but this is not ideal for most dApp users. A web UI can provide a much better experience! Rather than sending transaction messages through `injectived`, we can abstract away the complexity and provide the user with two buttons—one to increment the count, and one to reset the count.

For example, see the [counter website](https://injective-simple-cosmwasm-sc.netlify.app/). A high level guide on developing the frontend using Vue and the [Injective TS SDK](https://github.com/InjectiveLabs/injective-ts/tree/master/packages/sdk-ts) can be found in the [website repo here](https://github.com/InjectiveLabs/injective-simple-sc-counter-ui/tree/master/nuxt). For a React implementation, see [here](https://github.com/InjectiveLabs/injective-simple-sc-counter-ui/tree/master/next).
Now, interacting with the contract is as simple as clicking a button and signing with MetaMask (make sure the account is set to Ethereum Sepolia Testnet or you will receive a chain ID mismatch error).

You may notice that you get an "Unauthorized" error message when attempting to
reset the count. This is expected behavior! Recall from the [contract logic
for
reset](/developers-cosmwasm/smart-contracts/your-first-smart-contract/#reset)
that only the contract owner is permitted to reset the count. Since you did
not instantiate the exact contract that the frontend is interacting with, you
don't have the required permissions to reset the count.
# Create your Swap Contract
Source: https://docs.injective.network/developers-cosmwasm/create-your-swap-contract-guide
The [swap contract](https://github.com/InjectiveLabs/swap-contract) allows an instant swap between two different tokens. Under the hood, it uses atomic orders to place market orders in one or more spot markets.
### Getting started
Anyone can instantiate an instance of the swap contract. There is a version of this contract uploaded on Injective mainnet already, and can be found [here](https://injscan.com/code/67/).
Before instantiating the contract, as the contract owner, you have three questions to answer:
#### 1. Which address should be the fee recipient?
Since orders placed by the swap contract are orders in the Injective Exchange Module, this means each order can have a fee recipient which can receive 40% of the trading fee. Typically, Exchange dApps will set the fee recipient as their own addresses.
#### 2. What tokens should this contract support?
Every token available in the contract must have a route defined. Route refers to which markets `token A` will go through in order to get `token B`. For example, if you would like to support swapping between ATOM and INJ, then you would have to set route by providing the contract the market IDs of ATOM/USDT and INJ/USDT, so that it knows the route of swapping between ATOM and INJ would be ATOM ⇔ USDT ⇔ INJ.
At this moment, the contract can only support markets quoted in USDT.
#### 3. How much buffer should be provided to this contract?
As the contract owner, you also have to provide funds to the contract which will be used when the swap happens. The buffer is used by the contract when it places orders. If the user wants to swap a big amount or swap in an illiquid market, then more buffer is required. An error will occur when the contract buffer cannot satisfy the user's input amount.
At this moment, the buffer should only be USDT.
### Messages
#### Instantiate
Initializes the contract state with the contract version and configuration details. The config includes an administrator address and a fee recipient address.
```rust theme={null}
pub fn instantiate(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result, ContractError>
```
#### Execute
Handles different types of transactions and admin functions:
* SwapMinOutput: Swap with the minimum output quantity.
* SwapExactOutput: Swap with an exact output quantity.
* SetRoute: Set a swap route.
* DeleteRoute: Delete a swap route.
* UpdateConfig: Update the contract configuration.
* WithdrawSupportFunds: Withdraw the support funds from the contract.
```rust theme={null}
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result, ContractError>
```
#### Reply
Handles the replies from other contracts or transactions.
```rust theme={null}
pub fn reply(
deps: DepsMut,
env: Env,
msg: Reply,
) -> Result, ContractError>
```
#### Query
Handles various queries to the contract:
* GetRoute: Get a specific swap route.
* GetOutputQuantity: Get the output quantity for a given input quantity.
* GetInputQuantity: Get the input quantity for a given output quantity.
* GetAllRoutes: Get all available swap routes.
```rust theme={null}
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult
```
### Repo
The complete GitHub repository for the swap contract can be found [here](https://github.com/InjectiveLabs/swap-contract).
# CW20 Adapter
Source: https://docs.injective.network/developers-cosmwasm/cw20-adapter
In this document, we'll explain the CW20 Adapter Contract that allows exchanging CW-20 tokens for Injective issued native tokens (using the TokenFactory module) and vice versa. For the CW-20 Adapter GitHub repository, see [here](https://github.com/InjectiveLabs/cw20-adapter/tree/master/contracts/cw20-adapter).
## Background
CW-20 is a specification for fungible tokens in CosmWasm, loosely based on ERC-20 specification. It allows creating and handling of arbitrary fungible tokens within CosmWasm, specifying methods for creating, minting and burning and transferring those tokens between accounts. The adapter contract will ensure that only authorized source CW-20 contracts can mint tokens (to avoid creating “counterfeit” tokens).
While the CW-20 standard is relatively mature and complete, the tokens exist purely within the CosmWasm context and are entirely managed by the issuing contract (including keeping track of account balances). That means that they cannot interact directly with Injective's native modules (for example, it’s not possible to trade them via the Injective exchange module, or to transfer without involving issuing contracts).
Considering the above, it’s necessary to provide a solution that would work as a bridge between CW20 and the Injective bank module.
The workflow of the contract is
* Register a new CW-20 token
* Exchange amount of X CW-20 tokens for Y TokenFactory tokens (original CW-20 tokens will be held by the contract)
* Exchange Y TF tokens back for X CW-20 tokens (CW-20 tokens are released and TokenFactory tokens are burned)
### Messages
#### `RegisterCw20Contract { addr: Addr }`
Registers a new CW-20 contract (`addr`) that will be handled by the adapter and creates a new TokenFactory token in format `factory/{adapter_contract}/{cw20_contract}`.
```rust theme={null}
ExecuteMsg::RegisterCw20Contract { addr } => execute_register::handle_register_msg(deps, env, info, addr)
pub fn handle_register_msg(
deps: DepsMut,
env: Env,
info: MessageInfo,
addr: Addr,
) -> Result, ContractError> {
if is_contract_registered(&deps, &addr) {
return Err(ContractError::ContractAlreadyRegistered);
}
let required_funds = query_denom_creation_fee(&deps.querier)?;
if info.funds.len() > required_funds.len() {
return Err(ContractError::SuperfluousFundsProvided);
}
let mut provided_funds = info.funds.iter();
for required_coin in &required_funds {
let pf = provided_funds
.find(|c| -> bool { c.denom == required_coin.denom })
.ok_or(ContractError::NotEnoughBalanceToPayDenomCreationFee)?;
match pf.amount.cmp(&required_coin.amount) {
Ordering::Greater => return Err(ContractError::SuperfluousFundsProvided),
Ordering::Less => return Err(ContractError::NotEnoughBalanceToPayDenomCreationFee),
Ordering::Equal => {}
}
}
let create_denom_msg = register_contract_and_get_message(deps, &env, &addr)?;
Ok(Response::new().add_message(create_denom_msg))
}
```
#### `Receive { sender: String, amount: Uint128, msg: Binary }`
Implementation of the Receiver CW-20 interface.
Must be called by the CW-20 contract only.
```rust theme={null}
ExecuteMsg::Receive { sender, amount, msg: _ } => execute_receive::handle_on_received_cw20_funds_msg(deps, env, info, sender, amount)
pub fn handle_on_received_cw20_funds_msg(
deps: DepsMut,
env: Env,
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result, ContractError> {
if!info.funds.is_empty() {
return Err(ContractError::SuperfluousFundsProvided);
}
let mut response = Response::new();
let token_contract = info.sender;
if!is_contract_registered(&deps, &token_contract) {
ensure_sufficient_create_denom_balance(&deps, &env)?;
response = response.add_message(register_contract_and_get_message(deps, &env, &token_contract)?);
}
let master = env.contract.address;
let denom = get_denom(&master, &token_contract);
let coins_to_mint = Coin::new(amount.u128(), denom);
let mint_tf_tokens_message = create_mint_tokens_msg(master, coins_to_mint, recipient);
Ok(response.add_message(mint_tf_tokens_message))
}
```
#### `RedeemAndTransfer \{ recipient: Option\ \}`
Redeem attached TokenFactory tokens and transfer CW-20 tokens to the recipient. If the recipient is not provided, they will be sent to the message sender.
#### `RedeemAndSend { recipient: String, submessage: Binary }`
Redeem attached TokenFactory tokens and send CW-20 tokens to the recipient contract. Caller may provide optional submessage.
```rust theme={null}
ExecuteMsg::RedeemAndTransfer { recipient } => execute_redeem::handle_redeem_msg(deps, env, info, recipient, None)
ExecuteMsg::RedeemAndSend { recipient, submsg } => execute_redeem::handle_redeem_msg(deps, env, info, Some(recipient), Some(submsg))
pub fn handle_redeem_msg(
deps: DepsMut,
env: Env,
info: MessageInfo,
recipient: Option,
submessage: Option,
) -> Result, ContractError> {
let recipient = recipient.unwrap_or_else(|| info.sender.to_string());
if info.funds.len() > 1 {
return Err(ContractError::SuperfluousFundsProvided);
}
let tokens_to_exchange = info
.funds
.iter()
.find_map(|c| -> Option {
match AdapterDenom::new(&c.denom) {
Ok(denom) => Some(AdapterCoin { amount: c.amount, denom }),
Err(_) => None,
}
})
.ok_or(ContractError::NoRegisteredTokensProvided)?;
let cw20_addr = tokens_to_exchange.denom.cw20_addr.clone();
let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, &tokens_to_exchange.denom.cw20_addr);
if!is_contract_registered {
return Err(ContractError::NoRegisteredTokensProvided);
}
let burn_tf_tokens_message = create_burn_tokens_msg(env.contract.address, tokens_to_exchange.as_coin());
let cw20_message: WasmMsg = match submessage {
None => WasmMsg::Execute {
contract_addr: cw20_addr,
msg: to_binary(&Cw20ExecuteMsg::Transfer {
recipient,
amount: tokens_to_exchange.amount,
})?,
funds: vec![],
},
Some(msg) => WasmMsg::Execute {
contract_addr: cw20_addr,
msg: to_binary(&Cw20ExecuteMsg::Send {
contract: recipient,
amount: tokens_to_exchange.amount,
msg,
})?,
funds: vec![],
},
};
Ok(Response::new().add_message(cw20_message).add_message(burn_tf_tokens_message))
}
```
#### `UpdateMetadata { addr : Addr}`
Will query the CW-20 address (if registered) for metadata and will call setMetadata in the bank module (using TokenFactory access method).
```rust theme={null}
ExecuteMsg::UpdateMetadata { addr } => execute_metadata::handle_update_metadata(deps, env, addr)
pub fn handle_update_metadata(
deps: DepsMut,
env: Env,
cw20_addr: Addr,
) -> Result, ContractError> {
let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, cw20_addr.as_str());
if!is_contract_registered {
return Err(ContractError::ContractNotRegistered);
}
let token_metadata = fetch_cw20_metadata(&deps, cw20_addr.as_str())?;
let denom = get_denom(&env.contract.address, &cw20_addr);
let set_metadata_message = create_set_token_metadata_msg(denom, token_metadata.name, token_metadata.symbol, token_metadata.decimals);
Ok(Response::new().add_message(set_metadata_message))
}
Queries
RegisteredContracts {}
Return a list of registered CW-20 contracts.
QueryMsg::RegisteredContracts {} => to_binary(&query::registered_contracts(deps)?)
pub fn registered_contracts(deps: Deps) -> StdResult> {}
NewDenomFee {}
Returns a fee required to register a new tokenFactory denom.
QueryMsg::NewDenomFee {} => to_binary(&query::new_denom_fee(deps)?)
pub fn new_denom_fee(deps: Deps) -> StdResult {}
```
# Overview
Source: https://docs.injective.network/developers-cosmwasm/index
CosmWasm is a novel smart contracting platform built for the Cosmos ecosystem. You can learn more about CosmWasm from [the CosmWasm docs](https://cosmwasm.cosmos.network/), or see the [CosmWasm Book](https://book.cosmwasm.com/index.html) for a guide on creating CosmWasm smart contracts.
**Start your builder journey on Injective using Cosmwasm here**: [Your First CosmWasm Smart Contracts](/developers-cosmwasm/smart-contracts/your-first-smart-contract)
# Injective Test Tube
Source: https://docs.injective.network/developers-cosmwasm/injective-test-tube
`injective-test-tube` is a CosmWasm x Injective integration testing library that, unlike `cw-multi-test`, allows you to test your CosmWasm contract against the chain's actual logic instead of mocks.
The `dev` branch depends on currently private repos, but you can use published versions instead. Please refer to [`CHANGELOG`](https://github.com/InjectiveLabs/test-tube/blob/dev/packages/injective-test-tube/CHANGELOG.md) for features and update information.
### Getting Started
To demonstrate how `injective-test-tube` works, let us use a simple example contract: [cw-whitelist](https://github.com/CosmWasm/cw-plus/tree/main/contracts/cw1-whitelist) from `cw-plus`.
Here is how to set up the test:
```rust theme={null}
use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;
// create new injective appchain instance.
let app = InjectiveTestApp::new();
// create new account with initial funds
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000, "usdt"),
Coin::new(1_000_000_000_000, "inj"),
],
2,
)
.unwrap();
let admin = &accs[0];
let new_admin = &accs[1];
```
Now we have the appchain instance and accounts that have some initial balances and can interact with the appchain. This does not run Docker instance or spawning external process, it just loads the appchain's code as a library to create an in memory instance.
Note that `init_accounts` is a convenience function that creates multiple accounts with the same initial balance. If you want to create just one account, you can use `init_account` instead.
```rust theme={null}
use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;
let app = InjectiveTestApp::new();
let account = app.init_account(&[
Coin::new(1_000_000_000_000, "usdt"),
Coin::new(1_000_000_000_000, "inj"),
]);
```
Now if we want to test a cosmwasm contract, we need to
* build the wasm file
* store code
* instantiate
Then we can start interacting with our contract. Let's do just that.
```rust theme={null}
use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg}; // for instantiating cw1_whitelist contract
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};
let app = InjectiveTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000, "usdt"),
Coin::new(1_000_000_000_000, "inj"),
],
2,
)
.unwrap();
let admin = &accs[0];
let new_admin = &accs[1];
// ============= NEW CODE ================
// `Wasm` is the module we use to interact with cosmwasm related logic on the appchain
// it implements `Module` trait which you will see more later.
let wasm = Wasm::new(&app);
// Load compiled wasm bytecode
let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
.store_code(&wasm_byte_code, None, admin)
.unwrap()
.data
.code_id;
```
Not that in this example, it loads wasm bytecode from [cw-plus release](https://github.com/CosmWasm/cw-plus/releases) for simple demonstration purposes. You might want to run `cargo wasm` and find your wasm file in `target/wasm32-unknown-unknown/release/.wasm`.
```rust theme={null}
use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, AdminListResponse};
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};
let app = InjectiveTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000, "usdt"),
Coin::new(1_000_000_000_000, "inj"),
],
2,
)
.unwrap();
let admin = &accs[0];
let new_admin = &accs[1];
let wasm = Wasm::new(&app);
let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
.store_code(&wasm_byte_code, None, admin)
.unwrap()
.data
.code_id;
// ============= NEW CODE ================
// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
.instantiate(
code_id,
&InstantiateMsg {
admins: init_admins.clone(),
mutable: true,
},
None, // contract admin used for migration, not the same as cw1_whitelist admin
Some("Test label"), // contract label
&[], // funds
admin, // signer
)
.unwrap()
.data
.address;
// query contract state to check if contract instantiation works properly
let admin_list = wasm
.query::(&contract_addr, &QueryMsg::AdminList {})
.unwrap();
assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);
```
Now let's execute the contract and verify that the contract's state is updated properly.
```rust theme={null}
use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, ExecuteMsg, AdminListResponse};
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};
let app = InjectiveTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000, "usdt"),
Coin::new(1_000_000_000_000, "inj"),
],
2,
)
.unwrap();
let admin = &accs[0];
let new_admin = &accs[1];
let wasm = Wasm::new(&app);
let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
.store_code(&wasm_byte_code, None, admin)
.unwrap()
.data
.code_id;
// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
.instantiate(
code_id,
&InstantiateMsg {
admins: init_admins.clone(),
mutable: true,
},
None, // contract admin used for migration, not the same as cw1_whitelist admin
Some("Test label"), // contract label
&[], // funds
admin, // signer
)
.unwrap()
.data
.address;
let admin_list = wasm
.query::(&contract_addr, &QueryMsg::AdminList {})
.unwrap();
assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);
// ============= NEW CODE ================
// update admin list and rechec the state
let new_admins = vec![new_admin.address()];
wasm.execute::(
&contract_addr,
&ExecuteMsg::UpdateAdmins {
admins: new_admins.clone(),
},
&[],
admin,
)
.unwrap();
let admin_list = wasm
.query::(&contract_addr, &QueryMsg::AdminList {})
.unwrap();
assert_eq!(admin_list.admins, new_admins);
assert!(admin_list.mutable);
```
### Debugging
In your contract code, if you want to debug, you can use [`deps.api.debug(..)`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/trait.Api.html#tymethod.debug) which will print the debug message to stdout. `wasmd` disabled this by default but `InjectiveTestApp` allows stdout emission so that you can debug your smart contract while running tests.
### Using Module Wrapper
In some cases, you might want to interact directly with appchain logic to set up the environment or query appchain's state. Module wrappers provide convenient functions to interact with the appchain's module.
Let's try to interact with `Exchange` module:
```rust theme={null}
use cosmwasm_std::{Addr, Coin};
use injective_std::types::injective::exchange::v1beta1::{
MarketStatus, MsgInstantSpotMarketLaunch,
QuerySpotMarketsRequest, QuerySpotMarketsResponse, SpotMarket,
};
use injective_test_tube::{Account, Exchange, InjectiveTestApp};
use test_tube_inj::Module;
let app = InjectiveTestApp::new();
let signer = app
.init_account(&[
Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
Coin::new(100_000_000_000_000_000_000u128, "usdt"),
])
.unwrap();
let trader = app
.init_account(&[
Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
Coin::new(100_000_000_000_000_000_000u128, "usdt"),
])
.unwrap();
let exchange = Exchange::new(&app);
exchange
.instant_spot_market_launch(
MsgInstantSpotMarketLaunch {
sender: signer.address(),
ticker: "INJ/USDT".to_owned(),
base_denom: "inj".to_owned(),
quote_denom: "usdt".to_owned(),
min_price_tick_size: "10000".to_owned(),
min_quantity_tick_size: "100000".to_owned(),
},
&signer,
)
.unwrap();
exchange
.instant_spot_market_launch(
MsgInstantSpotMarketLaunch {
sender: signer.address(),
ticker: "INJ/USDT".to_owned(),
base_denom: "inj".to_owned(),
quote_denom: "usdt".to_owned(),
min_price_tick_size: "10000".to_owned(),
min_quantity_tick_size: "100000".to_owned(),
},
&signer,
)
.unwrap_err();
app.increase_time(1u64);
let spot_markets = exchange
.query_spot_markets(&QuerySpotMarketsRequest {
status: "Active".to_owned(),
market_ids: vec![],
})
.unwrap();
let expected_response = QuerySpotMarketsResponse {
markets: vec![SpotMarket {
ticker: "INJ/USDT".to_string(),
base_denom: "inj".to_string(),
quote_denom: "usdt".to_string(),
maker_fee_rate: "-100000000000000".to_string(),
taker_fee_rate: "1000000000000000".to_string(),
relayer_fee_share_rate: "400000000000000000".to_string(),
market_id: "0xd5a22be807011d5e42d5b77da3f417e22676efae494109cd01c242ad46630115"
.to_string(),
status: MarketStatus::Active.into(),
min_price_tick_size: "10000".to_string(),
min_quantity_tick_size: "100000".to_string(),
}],
};
assert_eq!(spot_markets, expected_response);
```
Additional examples can be found in the [modules](https://github.com/InjectiveLabs/test-tube/tree/dev/packages/injective-test-tube/src/module) directory.
### Versioning
The version of injective-test-tube is determined by the versions of its dependencies, injective and test-tube, as well as its own changes. The version is represented in the format A.B.C, where:
* A is the major version of injective,
* B is the minor version of test-tube,
* C is the patch number of injective-test-tube itself.
When a new version of injective is released and contains breaking changes, we will also release breaking changes from test-tube if any and increment the major version of injective-test-tube. This way, it's clear that the new version of injective-test-tube is not backwards-compatible with previous versions.
When adding a new feature to injective-test-tube that is backward-compatible, the minor version number will be incremented.
When fixing bugs or making other changes that are `injective-test-tube` specific and backward-compatible, the patch number will be incremented.
Please review the upgrade guide for upgrading the package, in case of breaking changes
It is important to note that we track the version of the package independent of the version of dependencies.
# Local Development
Source: https://docs.injective.network/developers-cosmwasm/local-development-guide
This guide will get you started deploying `cw20` smart contracts on a local Injective network running on your computer.
We'll use the `cw20-base` contract from [CosmWasm's collection of specifications and contracts](https://github.com/CosmWasm/cw-plus) designed for production use on real networks. `cw20-base` is a basic implementation of a `cw20` compatible contract that can be imported in any custom contract you want to build on. It contains a straightforward but complete implementation of the cw20 spec along with all extensions. `cw20-base` can be deployed as-is or imported by other contracts.
### Prerequisites
Install Go, Rust, and other Cosmwasm dependencies by following the instructions:
1. [Go](https://docs.cosmwasm.com/docs/getting-started/installation#go)
2. [Rust](https://docs.cosmwasm.com/docs/getting-started/installation#rust)
Before starting, make sure you have [`rustup`](https://rustup.rs/) along with recent versions of `rustc` and `cargo` installed. Currently, we are testing on Rust v1.58.1+.
You also need to have the `wasm32-unknown-unknown` target installed as well as the `cargo-generate` Rust crate.
You can check versions via the following commands:
```bash theme={null}
rustc --version
cargo --version
rustup target list --installed
# if wasm32 is not listed above, run this
rustup target add wasm32-unknown-unknown
# to install cargo-generate, run this
cargo install cargo-generate
```
### injectived
Make sure you have `injectived` installed locally. You can follow the [install injectived](/developers/injectived/install/) guide to get `injectived` and other prerequisites running locally.
Once you have `injectived` installed, you should also [start a local chain instance.](/developers/injectived/install/#start-injectived)
### Compile CosmWasm Contracts
In this step, we will get all CW production template contracts and compile them using the [CosmWasm Rust Optimizer](https://github.com/CosmWasm/rust-optimizer) Docker image for compiling multiple contracts (called `workspace-optimizer`)—see [here](https://hub.docker.com/r/cosmwasm/workspace-optimizer/tags) (x86) or [here](https://hub.docker.com/r/cosmwasm/workspace-optimizer-arm64/tags) (ARM) for latest versions. This process may take a bit of time and CPU power.
```bash theme={null}
git clone https://github.com/CosmWasm/cw-plus
cd cw-plus
```
Non ARM (Non-Apple silicon) devices:
```bash theme={null}
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/workspace-optimizer:0.12.12
```
Alternatively for Apple Silicon devices (M1, M2, etc.) please use:
```bash theme={null}
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/workspace-optimizer-arm64:0.12.12
```
The docker script builds and optimizes all the CW contracts in the repo, with the compiled contracts located under the `artifacts` directory. Now we can deploy the `cw20_base.wasm` contract (`cw20_base-aarch64.wasm` if compiled on an ARM device).
### Upload the CosmWasm Contract to the Chain
```bash theme={null}
# inside the CosmWasm/cw-plus repo
yes 12345678 | injectived tx wasm store artifacts/cw20_base.wasm --from=genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000
```
**Output:**
```
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
```
Then query the transaction by the `txhash` to verify the contract was indeed deployed.
```bash theme={null}
injectived query tx 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
```
**Output:**
```bash theme={null}
code: 0
codespace: ""
data: 0A460A1E2F636F736D7761736D2E7761736D2E76312E4D736753746F7265436F64651224080112205F8201CF5E2D7E6C15DB11ADF03D62DDDDC92B8D4FAE98C4F3C1C37F378E15D9
events:
- attributes:
- index: true
key: YWNjX3NlcQ==
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204LzE=
type: tx
- attributes:
- index: true
key: c2lnbmF0dXJl
value: R29McmoxaDBtelNWN085SUNScStLbDdCVmdocDB6aU5EQ0Jwc1dFS1I1TlNXZkR2V1ZJejF0TEpGb0ZwSzlhNkFIQVdSVkZRNjExYitwSHdpY04wN1FFPQ==
type: tx
- attributes:
- index: true
key: c3BlbmRlcg==
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
- index: true
key: YW1vdW50
value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
type: coin_spent
- attributes:
- index: true
key: cmVjZWl2ZXI=
value: aW5qMTd4cGZ2YWttMmFtZzk2MnlsczZmODR6M2tlbGw4YzVsNnM1eWU5
- index: true
key: YW1vdW50
value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
type: coin_received
- attributes:
- index: true
key: cmVjaXBpZW50
value: aW5qMTd4cGZ2YWttMmFtZzk2MnlsczZmODR6M2tlbGw4YzVsNnM1eWU5
- index: true
key: c2VuZGVy
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
- index: true
key: YW1vdW50
value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
type: transfer
- attributes:
- index: true
key: c2VuZGVy
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
type: message
- attributes:
- index: true
key: ZmVl
value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
- index: true
key: ZmVlX3BheWVy
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
type: tx
- attributes:
- index: true
key: YWN0aW9u
value: L2Nvc213YXNtLndhc20udjEuTXNnU3RvcmVDb2Rl
type: message
- attributes:
- index: true
key: bW9kdWxl
value: d2FzbQ==
- index: true
key: c2VuZGVy
value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
type: message
- attributes:
- index: true
key: Y29kZV9jaGVja3N1bQ==
value: NWY4MjAxY2Y1ZTJkN2U2YzE1ZGIxMWFkZjAzZDYyZGRkZGM5MmI4ZDRmYWU5OGM0ZjNjMWMzN2YzNzhlMTVkOQ==
- index: true
key: Y29kZV9pZA==
value: MQ==
type: store_code
- attributes:
- index: true
key: YWNjZXNzX2NvbmZpZw==
value: eyJwZXJtaXNzaW9uIjoiRXZlcnlib2R5IiwiYWRkcmVzcyI6IiIsImFkZHJlc3NlcyI6W119
- index: true
key: Y2hlY2tzdW0=
value: Ilg0SUJ6MTR0Zm13VjJ4R3Q4RDFpM2QzSks0MVBycGpFODhIRGZ6ZU9GZGs9Ig==
- index: true
key: Y29kZV9pZA==
value: IjEi
- index: true
key: Y3JlYXRvcg==
value: ImluajEwcmRzeGRncjhsOHMwZ3Z1OHJ5bmh1MjJubnhrZnl0ZzU4Y3dtOCI=
type: cosmwasm.wasm.v1.EventCodeStored
gas_used: "2158920"
gas_wanted: "20000000"
height: "47"
info: ""
logs:
- events:
- attributes:
- key: access_config
value: '{"permission":"Everybody","address":"","addresses":[]}'
- key: checksum
value: '"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk="'
- key: code_id
value: '"1"'
- key: creator
value: '"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"'
type: cosmwasm.wasm.v1.EventCodeStored
- attributes:
- key: action
value: /cosmwasm.wasm.v1.MsgStoreCode
- key: module
value: wasm
- key: sender
value: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
type: message
- attributes:
- key: code_checksum
value: 5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9
- key: code_id
value: "1"
type: store_code
log: ""
msg_index: 0
raw_log: '[{"events":[{"type":"cosmwasm.wasm.v1.EventCodeStored","attributes":[{"key":"access_config","value":"{\"permission\":\"Everybody\",\"address\":\"\",\"addresses\":[]}"},{"key":"checksum","value":"\"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk=\""},{"key":"code_id","value":"\"1\""},{"key":"creator","value":"\"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8\""}]},{"type":"message","attributes":[{"key":"action","value":"/cosmwasm.wasm.v1.MsgStoreCode"},{"key":"module","value":"wasm"},{"key":"sender","value":"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"}]},{"type":"store_code","attributes":[{"key":"code_checksum","value":"5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9"},{"key":"code_id","value":"1"}]}]}]'
timestamp: "2023-03-06T15:48:30Z"
tx:
'@type': /cosmos.tx.v1beta1.Tx
auth_info:
fee:
amount:
- amount: "10000000000000000"
denom: inj
gas_limit: "20000000"
granter: ""
payer: ""
signer_infos:
- mode_info:
single:
mode: SIGN_MODE_DIRECT
public_key:
'@type': /injective.crypto.v1beta1.ethsecp256k1.PubKey
key: Ay+cc/lvd4Mn4pbgFkN87vWDaCXuXjVJYJGsdhrD09vk
sequence: "1"
body:
extension_options: []
memo: ""
messages:
- '@type': /cosmwasm.wasm.v1.MsgStoreCode
instantiate_permission: null
sender: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
wasm_byte_code: YOUR_WASM_BYTE_HERE
non_critical_extension_options: []
timeout_height: "0"
signatures:
- GoLrj1h0mzSV7O9ICRq+Kl7BVghp0ziNDCBpsWEKR5NSWfDvWVIz1tLJFoFpK9a6AHAWRVFQ611b+pHwicN07QE=
txhash: 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
```
Inspecting the output more closely, we can see the `code_id` of 1 for the contract
```bash theme={null}
logs:
- events:
- attributes:
- key: access_config
value: '{"permission":"Everybody","address":"","addresses":[]}'
- key: checksum
value: '"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk="'
- key: code_id
value: '"1"'
- key: creator
value: '"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"'
type: cosmwasm.wasm.v1.EventCodeStored
- attributes:
- key: action
value: /cosmwasm.wasm.v1.MsgStoreCode
- key: module
value: wasm
- key: sender
value: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
type: message
- attributes:
- key: code_checksum
value: 5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9
- key: code_id
value: "1"
type: store_code
log: ""
msg_index: 0
```
We’ve uploaded the contract code, but we still need to instantiate the contract.
### Instantiate the Contract
Before instantiating the contract, let's take a look at the CW-20 contract function signature for `instantiate`.
```rust theme={null}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
mut deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> Result {
```
Notably, it contains the `InstantiateMsg` parameter which contains the token name, symbol, decimals, and other details.
```rust theme={null}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
pub struct InstantiateMsg {
pub name: String,
pub symbol: String,
pub decimals: u8,
pub initial_balances: Vec,
pub mint: Option,
pub marketing: Option,
}
```
The first step of instantiating the contract is to select an address to supply with our initial CW20 token allocation. In our case, we can just use the genesis address since we have the keys already set up, but feel free to generate new addresses and keys.
Make sure you have the private keys for the address you choose—you won't be able to test token transfers from the address otherwise. In addition, the chosen address must be a valid address on the chain (the address must have received funds at some point in the past) and must have balances to pay for gas when executing the contract.
To find the genesis address, run:
```bash theme={null}
yes 12345678 | injectived keys show genesis
```
**Output:**
```bash theme={null}
- name: genesis
type: local
address: inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3
pubkey: '{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"ArtVkg9feLXjD4p6XRtWxVpvJUDhrcqk/5XYLsQI4slb"}'
mnemonic: ""
```
Run the CLI command with `code_id` `1` along with the JSON encoded initialization arguments (with your selected address) and a label (a human-readable name for this contract in lists) to instantiate the contract:
```bash theme={null}
CODE_ID=1
INIT='{"name":"Albcoin","symbol":"ALB","decimals":6,"initial_balances":[{"address":"inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3","amount":"69420"}],"mint":{"minter":"inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"},"marketing":{}}'
yes 12345678 | injectived tx wasm instantiate $CODE_ID $INIT --label="Albcoin Token" --from=genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000 --no-admin
```
Now the address of the instantiated contract can be obtained on `http://localhost:10337/swagger/#/Query/ContractsByCode`
And the contract info metadata can be obtained on `http://localhost:10337/swagger/#/Query/ContractInfo` or by CLI query
```bash theme={null}
CONTRACT=$(injectived query wasm list-contract-by-code $CODE_ID --output json | jq -r '.contracts[-1]')
injectived query wasm contract $CONTRACT
```
**Output:**
```bash theme={null}
injectived query wasm contract $CONTRACT
address: inj14hj2tavq8fpesdwxxcu44rty3hh90vhujaxlnz
contract_info:
admin: ""
code_id: "1"
created:
block_height: "95"
tx_index: "0"
creator: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
extension: null
ibc_port_id: ""
label: Albcoin Token
```
### Querying the contract
The entire contract state can be queried with:
```bash theme={null}
injectived query wasm contract-state all $CONTRACT
```
**Output:**
```bash theme={null}
models:
- key: 000762616C616E6365696E6A31306366793565367174327A793535713277327578327675713836327A63796634666D66706A33
value: IjY5NDIwIg==
- key: 636F6E74726163745F696E666F
value: eyJjb250cmFjdCI6ImNyYXRlcy5pbzpjdzIwLWJhc2UiLCJ2ZXJzaW9uIjoiMS4wLjEifQ==
- key: 6D61726B6574696E675F696E666F
value: eyJwcm9qZWN0IjpudWxsLCJkZXNjcmlwdGlvbiI6bnVsbCwibG9nbyI6bnVsbCwibWFya2V0aW5nIjpudWxsfQ==
- key: 746F6B656E5F696E666F
value: eyJuYW1lIjoiQWxiY29pbiIsInN5bWJvbCI6IkFMQiIsImRlY2ltYWxzIjo2LCJ0b3RhbF9zdXBwbHkiOiI2OTQyMCIsIm1pbnQiOnsibWludGVyIjoiaW5qMTBjZnk1ZTZxdDJ6eTU1cTJ3MnV4MnZ1cTg2MnpjeWY0Zm1mcGozIiwiY2FwIjpudWxsfX0=
pagination:
next_key: null
total: "0"
```
The individual user’s token balance can also be queried with:
```bash theme={null}
BALANCE_QUERY='{"balance": {"address": "inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
```
**Output:**
```bash theme={null}
{"data":{"balance":"69420"}}
```
### Transferring Tokens
```bash theme={null}
TRANSFER='{"transfer":{"recipient":"inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz","amount":"420"}}'
yes 12345678 | injectived tx wasm execute $CONTRACT "$TRANSFER" --from genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000
```
Then confirm the balance transfer occurred successfully with:
```bash theme={null}
# first address balance query
BALANCE_QUERY='{"balance": {"address": "inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
```
**Output:**
```bash theme={null}
{"data":{"balance":"69000"}}
```
And confirm the recipient received the funds:
```bash theme={null}
# recipient's address balance query
BALANCE_QUERY='{"balance": {"address": "inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
```
**Output:**
```bash theme={null}
{"data":{"balance":"420"}}
```
## Testnet Development
Here are the main differences between a `local` and `testnet` development/deployment
* You can use our [Injective Testnet Faucet](https://testnet.faucet.injective.network) to get testnet funds to your address,
* You can use the [Injective Testnet Explorer](https://testnet.explorer.injective.network/smart-contracts/code/) to query your transactions and get more details,
* When you are using `injectived` you have to specify the `testnet` rpc using the `node` flag `--node=https://testnet.sentry.tm.injective.network:443`
* Instead of using `injective-1` as a `chainId` you should use `injective-888` i.e the `chain-id` flag should now be `--chain-id="injective-888"`
* You can use the [Injective Testnet Explorer](https://testnet.explorer.injective.network/smart-contracts/code/) to find information about the `codeId` of the uploaded smart contracts OR find your instantiated smart contract
You can read more on the `injectived` and how to use it to query/send transactions against `testnet` in the [using injectived](/developers/injectived/use/) guide.
# Mainnet Deployment
Source: https://docs.injective.network/developers-cosmwasm/mainnet-deployment-guide
This guide will get you started with the governance process of deploying and instantiating CosmWasm smart contracts on Injective Mainnet.
### Submit a Code Upload Proposal
In this section, you will learn how to submit a smart contract code proposal and vote for it.
Injective network participants can propose smart contracts deployments and vote in governance to enable them. The `wasmd` authorization settings are by on-chain governance, which means deployment of a contract is completely determined by governance. Because of this, a governance proposal is the first step to uploading contracts to Injective mainnet.
Sample usage of `injectived` to start a governance proposal to upload code to the chain:
```bash theme={null}
injectived tx wasm submit-proposal wasm-store artifacts/cw_controller.wasm
--title="Proposal Title" \
--summary="Proposal Summary" \
--instantiate-everybody true \
--broadcast-mode=sync \
--chain-id=injective-1 \
--node=https://sentry.tm.injective.network:443 \
--deposit=100000000000000000000inj \
--gas=20000000 \
--gas-prices=160000000inj \
--from [YOUR_KEY] \
--yes \
--output json
```
The command `injectived tx gov submit-proposal wasm-store` submits a wasm binary proposal. The code will be deployed if the proposal is approved by governance.
Let’s go through two key flags `instantiate-everybody` and `instantiate-only-address`, which set instantiation permissions of the uploaded code. By default, everyone can instantiate the contract.
```bash theme={null}
--instantiate-everybody boolean # Everybody can instantiate a contract from the code, optional
--instantiate-only-address string # Only this address can instantiate a contract instance from the code
```
### Contract Instantiation (No Governance)
In most cases, you don’t need to push another governance proposal to instantiate. Simply instantiate with `injectived tx wasm instantiate`. You only need a governance proposal to *upload* a contract. You don’t need to go through governance to instantiate unless if the contract has the `--instantiate-everybody` flag to set to `false`, and `--instantiate-only-address` flag set to the governance module. The default value for `--instantiate-everybody` is `true`, and in this case you can permissionlessly instantiate via `injectived tx wasm instantiate`.
```bash theme={null}
injectived tx wasm instantiate [code_id_int64] [json_encoded_init_args] --label [text] --admin [address,optional] --amount [coins,optional] [flags]
```
```bash theme={null}
Flags:
-a, --account-number uint The account number of the signing account (offline mode only)
--admin string Address or key name of an admin
--amount string Coins to send to the contract during instantiation
--aux Generate aux signer data instead of sending a tx
-b, --broadcast-mode string Transaction broadcasting mode (sync|async) (default "sync")
--chain-id string The network chain ID
--dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)
--fee-granter string Fee granter grants fees for the transaction
--fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer
--fees string Fees to pay along with transaction; eg: 10uatom
--from string Name or address of private key with which to sign
--gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000)
--gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1)
--gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
--generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)
-h, --help help for instantiate
--keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--label string A human-readable name for this contract in lists
--ledger Use a connected Ledger device
--no-admin You must set this explicitly if you don't want an admin
--node string : to tendermint rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality)
-o, --output string Output format (text|json) (default "json")
-s, --sequence uint The sequence number of the signing account (offline mode only)
--sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature
--timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height
--tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator
-y, --yes Skip tx broadcasting prompt confirmation
```
An example `injectived tx wasm instantiate` can look something like this:
```bash theme={null}
injectived tx wasm instantiate \
150 \
'{"bank": "inj1egl894wme0d4d029hlv3kuqs0mc9atep2s89h8"}' \
--label="LABEL" \
--from=inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c \
--chain-id=injective-1 \
--yes \
--gas-prices 160000000inj \
--gas=10000000 \
--no-admin \
--node=https://sentry.tm.injective.network:443 \
```
### Contract Instantiation (Governance)
As mentioned above, contract instantiation permissions on mainnet depend on the flags used when uploading the code. By default, it is set to permissionless, as we can verify on the genesis `wasmd` Injective setup:
```json theme={null}
"wasm": {
"codes": [],
"contracts": [],
"gen_msgs": [],
"params": {
"code_upload_access": {
"address": "",
"permission": "Everybody"
},
"instantiate_default_permission": "Everybody"
},
"sequences": []
}
```
However, if the `--instantiate-everybody` flag is set to `false`, then the contract instantiation must go through governance.
The Injective testnet is permissionless by default in order to allow developers to easily deploy contracts.
#### Contract Instantiation Proposal
```bash theme={null}
injectived tx gov submit-proposal instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional] [flags]
```
```bash theme={null}
Flags:
-a, --account-number uint The account number of the signing account (offline mode only)
--admin string Address of an admin
--amount string Coins to send to the contract during instantiation
-b, --broadcast-mode string Transaction broadcasting mode (sync|async|block) (default "sync")
--deposit string Deposit of proposal
--description string Description of proposal
--dry-run ignore the --gas flag and perform a simulation of a transaction, but dont broadcast it (when enabled, the local Keybase is not accessible)
--fee-account string Fee account pays fees for the transaction instead of deducting from the signer
--fees string Fees to pay along with transaction; eg: 10uatom
--from string Name or address of private key with which to sign
--gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default 200000)
--gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1)
--gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
--generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)
-h, --help help for instantiate-contract
--keyring-backend string Select keyrings backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--label string A human-readable name for this contract in lists
--ledger Use a connected Ledger device
--no-admin You must set this explicitly if you dont want an admin
--node string : to tendermint rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality
-o, --output string Output format (text|json) (default "json")
--proposal string Proposal file path (if this path is given, other proposal flags are ignored)
--run-as string The address that pays the init funds. It is the creator of the contract and passed to the contract as sender on proposal execution
-s, --sequence uint The sequence number of the signing account (offline mode only)
--sign-mode string Choose sign mode (direct|amino-json), this is an advanced feature
--timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height
--title string Title of proposal
--type string Permission of proposal, types: store-code/instantiate/migrate/update-admin/clear-admin/text/parameter_change/software_upgrade
-y, --yes Skip tx broadcasting prompt confirmation
```
### Contract Migration
Migration is the process through which a given smart contract's code can be swapped out or 'upgraded'.
When instantiating a contract, there is an optional admin field that you can set. If it is left empty, the contract is immutable. If the admin is set (to an external account or governance contract), that account can trigger a migration. The admin can also reassign the admin role, or even make the contract fully immutable if desired. However, keep in mind that when migrating from an old contract to a new contract, the new contract needs to be aware of how the state was previously encoded.
A more detailed description of the technical aspects of migration can be found in the [CosmWasm migration documentation](https://docs.cosmwasm.com/docs/smart-contracts/migration).
# CW20 to Bank & Market Order in One Transaction
Source: https://docs.injective.network/developers-cosmwasm/smart-contracts/cw20-convert-market-order
This example helps you create messages to convert CW20 tokens to bank tokens on the Injective blockchain. This is particularly useful when you have CW20 tokens and need to convert them to their bank equivalents to perform operations like placing market orders. Note that this flow only works for cw20 tokens and their corresponding [factory tokens](../../developers/concepts/).
This guide will walk you through:
* Obtaining the user's CW20 token balance.
* Creating a message to convert CW20 tokens to bank tokens using ConvertCw20ToBankService
* Executing a market order using the converted bank balance and existing bank balance
## Get User's CW20 Balance
You can perform this using [explorer indexer queries](../../developers-native/query-indexer/explorer/#fetch-cw20-balances).
* Find the cw20 address and balance from the result set that you want to convert to a bank factory token
## Create CW20 to Bank Conversion Message
* create the `convertMsg` using the steps detailed [here](../../developers/concepts/token-factory/#example-on-how-to-convert-cw20-to-a-factory-denom) in order to convert your CW20 token to a bank factory token. No need to submit the tsx yet.
## Create a `MsgCreateSpotMarketOrder` message
* Create the `msg` using the steps detailed in [MsgCreateSpotMarketOrder](../../developers-native/examples/exchange/#msgcreatespotmarketorder). No need to submit the tsx yet.
* Note that the buy order you create will have access to your converted cw20 balance + existing bank balance. Example:
```ts theme={null}
const order = {
price: 1,
quantity: 10,
}
```
* If you had 5 Cw20 tokens and 5 bank tokens at a price of \$1 each, then the order above will go through because we will convert the cw20 to bank before the chain executes this market order. This will be more clear in the next step.
## Place a Market Order Using Converted CW20 Balance and your existing bank balance
Now that you have both messages formatted, you can convert your cw20 tokens to bank factory tokens and then place a market order using the combined balance, all in one transaction
```ts theme={null}
import { Network } from '@injectivelabs/networks'
import { MsgBroadcasterWithPk } from '@injectivelabs/sdk-ts/core/tx'
const privateKey = '0x...'
const injectiveAddress = 'inj1...'
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.MainnetSentry,
}).broadcast({
msgs: [convertMsg, msg], // the convert to bank message executes first, Then, you will have that additional balance to complete your market order in the following msg
})
console.log(txHash)
```
# CosmWasm Smart Contracts
Source: https://docs.injective.network/developers-cosmwasm/smart-contracts/index
## What is CosmWasm?
CosmWasm is a novel smart contracting platform built for the Cosmos ecosystem. You can learn more about CosmWasm from [the CosmWasm docs](https://cosmwasm.cosmos.network/), or see the [CosmWasm Book](https://book.cosmwasm.com/index.html) for a guide on creating CosmWasm smart contracts.
## Developing CosmWasm Smart Contracts
New to CosmWasm?
We suggest that you follow this guide: [Your First CosmWasm Smart Contract](/developers-cosmwasm/smart-contracts/your-first-smart-contract/)
## Notable Cosmwasm Smart Contracts
| Topic | Description |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------- |
| [Injective Name Service](/developers-cosmwasm/smart-contracts/injective-name-service/) | Injective Name Service |
| [Neptune Service](/developers-cosmwasm/smart-contracts/neptune-service/) | Neptune Service |
| [CW20 to Bank & Market Order in One Transaction](/developers-cosmwasm/smart-contracts/cw20-convert-market-order/) | Convert Cw20 Example |
# Injective Name Service
Source: https://docs.injective.network/developers-cosmwasm/smart-contracts/injective-name-service
Within this section, we will look at how to query the Injective Name Service contracts to resolve .inj domain names and perform reverse lookups.
## InjNameService Class Implementation
The recommended approach is to create your own `InjNameService` class that encapsulates the name service functionality. This provides a clean, reusable interface for domain resolution and reverse lookups.
### Creating the InjNameService Class
Here's a complete implementation you can use in your application:
```ts theme={null}
import {
Network,
getNetworkEndpoints,
getInjNameRegistryContractForNetwork,
getInjNameReverseResolverContractForNetwork,
} from "@injectivelabs/networks";
import {
QueryInjName,
ChainGrpcWasmApi,
QueryResolverAddress,
QueryInjectiveAddress,
InjNameServiceQueryTransformer,
} from "@injectivelabs/sdk-ts/client/wasm";
import { nameToNode, normalizeName } from "@injectivelabs/sdk-ts/utils";
export class InjNameService {
private reverseResolverAddress: string;
private registryAddress: string;
private chainGrpcWasmApi: ChainGrpcWasmApi;
constructor(network: Network = Network.Mainnet) {
const endpoints = getNetworkEndpoints(network);
this.chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
this.registryAddress = getInjNameRegistryContractForNetwork(network);
this.reverseResolverAddress =
getInjNameReverseResolverContractForNetwork(network);
}
/**
* Fetch the .inj name for a given Injective address (reverse resolution)
* @param address - The Injective address to resolve
* @returns The .inj domain name
*/
async fetchInjName(address: string): Promise {
const query = new QueryInjName({ address }).toPayload();
const response = await this.chainGrpcWasmApi.fetchSmartContractState(
this.reverseResolverAddress,
query
);
const name =
InjNameServiceQueryTransformer.injectiveNameResponseToInjectiveName(
response
);
if (!name) {
throw new Error(`.inj name not found for ${address}`);
}
// Verify the reverse resolution by checking if the name resolves back to the same address
const addressFromName = await this.fetchInjAddress(name);
if (addressFromName.toLowerCase() !== address.toLowerCase()) {
throw new Error(`.inj name not found for ${address}`);
}
return name;
}
/**
* Fetch the Injective address for a given .inj domain name (forward resolution)
* @param name - The .inj domain name to resolve
* @returns The Injective address
*/
async fetchInjAddress(name: string): Promise {
const normalizedName = normalizeName(name);
const node = nameToNode(normalizedName);
if (!node || node.length === 0) {
throw new Error(`The ${name} can't be normalized`);
}
const resolverAddress = await this.fetchResolverAddress(node);
if (!resolverAddress) {
throw new Error(`Resolver address not found for ${name}`);
}
const query = new QueryInjectiveAddress({ node }).toPayload();
const response = await this.chainGrpcWasmApi.fetchSmartContractState(
resolverAddress,
query
);
const address =
InjNameServiceQueryTransformer.injectiveAddressResponseToInjectiveAddress(
response
);
if (!address) {
throw new Error(`Address not found for ${name}`);
}
return address;
}
/**
* Fetch the resolver address for a given node
* @private
*/
private async fetchResolverAddress(node: number[]): Promise {
const query = new QueryResolverAddress({ node }).toPayload();
const response = await this.chainGrpcWasmApi.fetchSmartContractState(
this.registryAddress,
query
);
return InjNameServiceQueryTransformer.resolverAddressResponseToResolverAddress(
response
);
}
}
```
### Using the InjNameService Class
```ts theme={null}
import { Network } from "@injectivelabs/networks";
// Initialize the service
const injNameService = new InjNameService(Network.Mainnet);
// Resolve a .inj domain to an address
const address = await injNameService.fetchInjAddress("ninja.inj");
console.log("Address:", address);
// Reverse resolve an address to a .inj domain
const name = await injNameService.fetchInjName("inj1...");
console.log("Name:", name);
```
## Raw Smart Contract Querying
If you need more control or want to query the contracts directly without the abstraction layer, here are the individual methods:
### Domain Resolution (Forward Lookup)
Resolve a .inj domain name to an Injective address.
#### Step 1: Get the Resolver Address
```ts theme={null}
import {
Network,
getNetworkEndpoints,
getInjNameRegistryContractForNetwork,
} from "@injectivelabs/networks";
import { nameToNode, normalizeName } from "@injectivelabs/sdk-ts/utils";
import {
ChainGrpcWasmApi,
QueryResolverAddress,
InjNameServiceQueryTransformer,
} from "@injectivelabs/sdk-ts/client/wasm";
const endpoints = getNetworkEndpoints(Network.Mainnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const registryContractAddress = getInjNameRegistryContractForNetwork(
Network.Mainnet
);
const name = "ninja.inj";
const normalizedName = normalizeName(name);
const node = nameToNode(normalizedName);
const query = new QueryResolverAddress({ node }).toPayload();
const response = await chainGrpcWasmApi.fetchSmartContractState(
registryContractAddress,
query
);
const resolverAddress =
InjNameServiceQueryTransformer.resolverAddressResponseToResolverAddress(
response
);
console.log("Resolver Address:", resolverAddress);
```
#### Step 2: Get the Address for the Domain
```ts theme={null}
import { Network, getNetworkEndpoints } from "@injectivelabs/networks";
import { nameToNode, normalizeName } from "@injectivelabs/sdk-ts/utils";
import {
ChainGrpcWasmApi,
QueryInjectiveAddress,
InjNameServiceQueryTransformer,
} from "@injectivelabs/sdk-ts/client/wasm";
const endpoints = getNetworkEndpoints(Network.Mainnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const name = "ninja.inj";
const normalizedName = normalizeName(name);
const node = nameToNode(normalizedName);
// Use the resolver address from Step 1
const resolverAddress = "...";
const query = new QueryInjectiveAddress({ node }).toPayload();
const response = await chainGrpcWasmApi.fetchSmartContractState(
resolverAddress,
query
);
const injectiveAddress =
InjNameServiceQueryTransformer.injectiveAddressResponseToInjectiveAddress(
response
);
if (!injectiveAddress) {
throw new Error(`Address not found for ${name}`);
}
console.log("Injective Address:", injectiveAddress);
```
### Reverse Resolution (Address to Domain)
Resolve an Injective address to its primary .inj domain name.
```ts theme={null}
import {
Network,
getNetworkEndpoints,
getInjNameReverseResolverContractForNetwork,
} from "@injectivelabs/networks";
import {
QueryInjName,
ChainGrpcWasmApi,
InjNameServiceQueryTransformer,
} from "@injectivelabs/sdk-ts/client/wasm";
const endpoints = getNetworkEndpoints(Network.Mainnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const reverseResolverContractAddress =
getInjNameReverseResolverContractForNetwork(Network.Mainnet);
const injectiveAddress = "inj1...";
const query = new QueryInjName({ address: injectiveAddress }).toPayload();
const response = await chainGrpcWasmApi.fetchSmartContractState(
reverseResolverContractAddress,
query
);
const name =
InjNameServiceQueryTransformer.injectiveNameResponseToInjectiveName(response);
if (!name) {
throw new Error(`.inj name not found for ${injectiveAddress}`);
}
console.log("INS Name:", name);
```
**Important:** Always verify reverse resolution by checking that the name resolves back to the original address. Here's how to perform the verification using forward resolution:
```ts theme={null}
import { Network, getNetworkEndpoints, getInjNameRegistryContractForNetwork } from "@injectivelabs/networks";
import { nameToNode, normalizeName } from "@injectivelabs/sdk-ts/utils";
import {
ChainGrpcWasmApi,
QueryResolverAddress,
QueryInjectiveAddress,
InjNameServiceQueryTransformer,
} from "@injectivelabs/sdk-ts/client/wasm";
// Initialize the gRPC client
const endpoints = getNetworkEndpoints(Network.Mainnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
// After getting the name from reverse resolution, verify it resolves back
const normalizedName = normalizeName(name);
const node = nameToNode(normalizedName);
// Get resolver address
const registryContractAddress = getInjNameRegistryContractForNetwork(
Network.Mainnet
);
const resolverQuery = new QueryResolverAddress({ node }).toPayload();
const resolverResponse = await chainGrpcWasmApi.fetchSmartContractState(
registryContractAddress,
resolverQuery
);
const resolverAddress =
InjNameServiceQueryTransformer.resolverAddressResponseToResolverAddress(
resolverResponse
);
// Get address from name
const addressQuery = new QueryInjectiveAddress({ node }).toPayload();
const addressResponse = await chainGrpcWasmApi.fetchSmartContractState(
resolverAddress,
addressQuery
);
const addressFromName =
InjNameServiceQueryTransformer.injectiveAddressResponseToInjectiveAddress(
addressResponse
);
// Verify the name resolves back to the original address
if (addressFromName.toLowerCase() !== injectiveAddress.toLowerCase()) {
throw new Error(
`Verification failed: ${name} does not resolve back to ${injectiveAddress}`
);
}
```
> **Note:** If you're using the `InjNameService` class shown earlier, you can simplify this verification to: `await injNameService.fetchInjAddress(name)`
## Best Practices
1. **Always normalize domain names** before processing them to ensure consistency
2. **Verify reverse resolutions** by performing a forward lookup to ensure the name actually points back to the address
3. **Handle errors gracefully** - not all addresses have .inj names, and not all names are registered
4. **Use proper network configuration** - ensure you're querying the correct network (Mainnet vs Testnet)
5. **Cache results** when appropriate to reduce unnecessary contract queries
## Dependencies
To use the Injective Name Service, you'll need the following packages:
```json theme={null}
{
"dependencies": {
"@injectivelabs/sdk-ts": "latest",
"@injectivelabs/networks": "latest"
}
}
```
# NeptuneService
Source: https://docs.injective.network/developers-cosmwasm/smart-contracts/neptune-service
`NeptuneService` is a straightforward tool that interacts with the Neptune CosmWasm smart contracts on Injective. It allows you to fetch asset prices, calculate exchange ratios, create deposit and withdraw messages, and retrieve lending rates.
Below are examples of how to use each method in the `NeptuneService` class.
## Initialize NeptuneService
Before using the service, create an instance of `NeptuneService`.
```ts theme={null}
import { Network } from "@injectivelabs/networks";
import { NeptuneService } from "@injectivelabs/sdk-ts/client/wasm";
// Create a NeptuneService instance using the mainnet
const neptuneService = new NeptuneService(Network.MainnetSentry);
```
## Fetch Prices
* Get the prices of specific assets from the Neptune Price Oracle contract. Use native\_token for bank denoms and token with contract\_addr for CW20 tokens.
```ts theme={null}
const assets = [
{
native_token: {
denom: "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7", // peggy USDT bank denom
},
},
{
token: {
contract_addr: "inj1cy9hes20vww2yr6crvs75gxy5hpycya2hmjg9s", // nUSDT contract address
},
},
];
const prices = await neptuneService.fetchPrices(assets);
console.log(prices);
```
## Fetch Redemption Ratio
* Calculate the redemption ratio between nUSDT (CW20 token) and USDT (bank token).
```ts theme={null}
const cw20Asset = {
token: {
contract_addr: "inj1cy9hes20vww2yr6crvs75gxy5hpycya2hmjg9s", // nUSDT
},
};
const nativeAsset = {
native_token: {
denom: "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT
},
};
const redemptionRatio = await neptuneService.fetchRedemptionRatio({
cw20Asset,
nativeAsset,
});
console.log(`Redemption Ratio: ${redemptionRatio}`);
```
## Convert CW20 nUSDT to Bank USDT
* Calculate the amount in bank USDT from a given amount of CW20 nUSDT using the redemption ratio.
```ts theme={null}
const amountCW20 = 1000; // Amount in nUSDT
const redemptionRatio = 0.95; // Obtained from fetchRedemptionRatio
const bankAmount = neptuneService.calculateBankAmount(
amountCW20,
redemptionRatio
);
console.log(`Bank USDT Amount: ${bankAmount}`);
```
## Convert Bank USDT to CW20 nUSDT
* Calculate the amount in CW20 nUSDT from a given amount of bank USDT using the redemption ratio.
```ts theme={null}
const amountBank = 950; // Amount in USDT
const redemptionRatio = 0.95; // Obtained from fetchRedemptionRatio
const cw20Amount = neptuneService.calculateCw20Amount(
amountBank,
redemptionRatio
);
console.log(`CW20 nUSDT Amount: ${cw20Amount}`);
```
## Fetch Lending Rates
* Retrieve lending rates for the different lending markets in neptune's lending market smart contract
```ts theme={null}
const lendingRates = await neptuneService.getLendingRates({
limit: 10, // Optional: number of rates to fetch
});
console.log(lendingRates);
```
## Fetch Lending Rate by Denomination
* Get the lending rate for USDT for example
```ts theme={null}
const denom = "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7"; // USDT denom
const lendingRate = await neptuneService.getLendingRateByDenom({ denom });
if (lendingRate) {
console.log(`Lending Rate for USDT: ${lendingRate}`);
} else {
console.log("Lending Rate for USDT not found");
}
```
## Calculate Annual Percentage Yield (APY)
* Convert the annual percentage rate (APR) to the continuously compounded annual percentage yield (APY). Make sure to use the lending rate retried from neptuneService.getLendingRateByDenom to use as the apr.
```ts theme={null}
const apr = 0.1; // 10% APR
const apy = neptuneService.calculateAPY(apr);
console.log(`APY (continuously compounded): ${(apy * 100).toFixed(2)}%`);
```
## Create and Broadcast a Deposit Message
* Create a message to deposit USDT into the Neptune USDT lending market and broadcast it to the network.
```ts theme={null}
import { toChainFormat } from "@injectivelabs/utils";
import { MsgBroadcasterWithPk } from "@injectivelabs/sdk-ts/core/tx";
import { MsgExecuteContractCompat } from "@injectivelabs/sdk-ts/core/modules";
const privateKey = "0x...";
const injectiveAddress = "inj1...";
const denom = "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7"; // USDT denom
const amountInUsdt = "100";
// Convert the amount to the smallest unit (USDT has 6 decimals)
const amount = toChainFormat(amountInUsdt, 6).toFixed();
const depositMsg = neptuneService.createDepositMsg({
denom,
amount,
sender: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.MainnetSentry,
}).broadcast({
msgs: depositMsg,
});
console.log(txHash);
```
## Create and Broadcast a Withdraw Message
* Create a message to withdraw USDT from the Neptune USDT lending market and broadcast it to the network
```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 privateKey = "0x..."; // Your private key
const injectiveAddress = "inj1..."; // Your Injective address
// Define the amount to withdraw (e.g., 100 nUSDT)
const amountInNusdt = "100";
// Convert the amount to the smallest unit (nUSDT has 6 decimals)
const amount = toChainFormat(amountInNusdt, 6).toFixed();
const withdrawMsg = neptuneService.createWithdrawMsg({
amount,
sender: injectiveAddress,
});
const txHash = await new MsgBroadcasterWithPk({
privateKey,
network: Network.MainnetSentry,
}).broadcast({
msgs: withdrawMsg,
});
console.log(`Transaction Hash: ${txHash}`);
```
# Your First CosmWasm Smart Contract
Source: https://docs.injective.network/developers-cosmwasm/smart-contracts/your-first-smart-contract
Within this section, we'll explain how to setup your environment for CosmWasm Smart Contracts Development.
## Prerequisites
Before starting, make sure you have [`rustup`](https://rustup.rs/) along with recent versions of `rustc` and `cargo` installed. Currently, we are testing on Rust v1.58.1+.
You also need to have the `wasm32-unknown-unknown` target installed as well as the `cargo-generate` Rust crate.
You can check versions via the following commands:
```bash theme={null}
rustc --version
cargo --version
rustup target list --installed
# if wasm32 is not listed above, run this
rustup target add wasm32-unknown-unknown
# to install cargo-generate, run this
cargo install cargo-generate
```
## Objectives
* Create and interact with a smart contract that increases and resets a counter to a given value
* Understand the basics of a CosmWasm smart contract, learn how to deploy it on Injective, and interact with it using Injective tools
## CosmWasm Contract Basics
A smart contract can be considered an instance of a [singleton object](https://en.wikipedia.org/wiki/Singleton_pattern) whose internal state is persisted on the blockchain. Users can trigger state changes by sending the smart contract JSON messages, and users can also query its state by sending a request formatted as a JSON message. These JSON messages are different than Injective blockchain messages such as `MsgSend` and `MsgExecuteContract`.
As a smart contract writer, your job is to define 3 functions that compose your smart contract's interface:
* `instantiate()`: a constructor which is called during contract instantiation to provide initial state
* `execute()`: gets called when a user wants to invoke a method on the smart contract
* `query()`: gets called when a user wants to get data out of a smart contract
In our [sample counter contract](https://github.com/InjectiveLabs/cw-counter/blob/59b9fed82864103eb704a58d20ddb4bf94c69787/src/msg.rs), we will implement one `instantiate`, one `query`, and two `execute` methods.
## Start with a Template
In your working directory, quickly launch your smart contract with the recommended folder structure and build options by running the following commands:
```bash theme={null}
cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 1.0 --name my-first-contract
cd my-first-contract
```
This helps get you started by providing the basic boilerplate and structure for a smart contract. In the [`src/contract.rs`](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/contract.rs) file you will find that the standard CosmWasm entrypoints [`instantiate()`](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/contract.rs#L15), [`execute()`](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/contract.rs#L35), and [`query()`](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/contract.rs#L72) are properly exposed and hooked up.
## State
You can learn more about CosmWasm State on their [documentation](https://book.cosmwasm.com/basics/state.html).
`State` handles the state of the database where smart contract data is stored and accessed.
The [starting template](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/state.rs) has the following basic state, a singleton struct `State` containing:
* `count`, a 32-bit integer with which `execute()` messages will interact by increasing or resetting it.
* `owner`, the sender `address` of the `MsgInstantiateContract`, which will determine if certain execution messages are permitted.
```c theme={null}
// src/state.rs
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::Addr;
use cw_storage_plus::Item;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
pub count: i32,
pub owner: Addr,
}
pub const STATE: Item = Item::new("state");
```
Injective smart contracts have the ability to keep persistent state through Injective's native LevelDB, a bytes-based key-value store. As such, any data you wish to persist should be assigned a unique key, which may be used to index and retrieve the data.
Data can only be persisted as raw bytes, so any notion of structure or data type must be expressed as a pair of serializing and deserializing functions. For instance, objects must be stored as bytes, so you must supply both the function that encodes the object into bytes to save it on the blockchain, as well as the function that decodes the bytes back into data types that your contract logic can understand. The choice of byte representation is up to you, so long as it provides a clean, bi-directional mapping.
Fortunately, CosmWasm provides utility crates, such as [`cosmwasm-storage`](https://crates.io/crates/cosmwasm-storage), which provides convenient high-level abstractions for data containers such as a "singleton" and "bucket", which automatically provide serialization and deserialization for commonly-used types, such as structs and Rust numbers. Additionally, the[`cw-storage-plus`](https://cosmwasm.cosmos.network/smart-contracts/state/cw-plus/) crate can be used for a more efficient storage mechanism.
Notice how the `State` struct holds both `count` and `owner`. In addition, the `derive` attribute is applied to auto-implement some useful traits:
* `Serialize`: provides serialization
* `Deserialize`: provides deserialization
* `Clone`: makes the struct copyable
* `Debug`: enables the struct to be printed to string
* `PartialEq`: provides equality comparison
* `JsonSchema`: auto-generates a JSON schema
`Addr` refers to a human-readable Injective address prefixed with `inj`, e.g. `inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt`.
## InstantiateMsg
You can learn more about CosmWasm [InstantiateMsg on their documentation](https://github.com/CosmWasm/docs/blob/archive/dev-academy/develop-smart-contract/01-intro.md#instantiatemsg)
The `InstantiateMsg` is provided to the contract when a user instantiates a contract on the blockchain through a `MsgInstantiateContract`. This provides the contract with its configuration as well as its initial state.
On the Injective blockchain, the uploading of a contract's code and the instantiation of a contract are regarded as separate events, unlike on Ethereum. This is to allow a small set of vetted contract archetypes to exist as multiple instances sharing the same base code, but be configured with different parameters (imagine one canonical ERC20, and multiple tokens that use its code).
### Example
For your contract, the contract creator is expected to supply the initial state in a JSON message. We can see in the message definition below that the message holds one parameter `count`, which represents the initial count.
```json theme={null}
{
"count": 100
}
```
### Message Definition
```c theme={null}
// src/msg.rs
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
pub count: i32,
}
```
### Contract Logic
In `contract.rs`, you will define your first entry-point, `instantiate()`, or where the contract is instantiated and passed its `InstantiateMsg`. Extract the count from the message and set up your initial state where:
* `count` is assigned the count from the message
* `owner` is assigned to the sender of the `MsgInstantiateContract`
```c theme={null}
// src/contract.rs
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result {
let state = State {
count: msg.count,
owner: info.sender.clone(),
};
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
STATE.save(deps.storage, &state)?;
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("owner", info.sender)
.add_attribute("count", msg.count.to_string()))
}
```
## ExecuteMsg
You can learn more about CosmWasm ExecuteMsg in their [documentation](https://book.cosmwasm.com/basics/execute.html).
The `ExecuteMsg` is a JSON message passed to the `execute()` function through a `MsgExecuteContract`. Unlike the `InstantiateMsg`, the `ExecuteMsg` can exist as several different types of messages to account for the different types of functions that a smart contract can expose to a user. The [`execute()` function](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/src/contract.rs#L35) demultiplexes these different types of messages to its appropriate message handler logic.
We have [two ExecuteMsg](https://github.com/InjectiveLabs/cw-counter/blob/59b9fed82864103eb704a58d20ddb4bf94c69787/src/msg.rs#L9): `Increment` and `Reset`.
* `Increment` has no input parameter and increases the value of count by 1.
* `Reset` takes a 32-bit integer as a parameter and resets the value of `count` to the input parameter.
### Example
**Increment**
Any user can increment the current count by 1.
```json theme={null}
{
"increment": {}
}
```
#### Reset
Only the owner can reset the count to a specific number. See Logic below for the implementation details.
```json theme={null}
{
"reset": {
"count": 5
}
}
```
### Message Definition
For `ExecuteMsg`, an `enum` can be used to multiplex over the different types of messages that your contract can understand. The `serde` attribute rewrites your attribute keys in snake case and lower case, so you'll have `increment` and `reset` instead of `Increment` and `Reset` when serializing and deserializing across JSON.
```c theme={null}
// src/msg.rs
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
Increment {},
Reset { count: i32 },
}
```
### Logic
```c theme={null}
// src/contract.rs
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result {
match msg {
ExecuteMsg::Increment {} => try_increment(deps),
ExecuteMsg::Reset { count } => try_reset(deps, info, count),
}
}
```
This is your `execute()` method, which uses Rust's pattern matching to route the received `ExecuteMsg` to the appropriate handling logic, either dispatching a `try_increment()` or a `try_reset()` call depending on the message received.
```c theme={null}
pub fn try_increment(deps: DepsMut) -> Result {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
state.count += 1;
Ok(state)
})?;
Ok(Response::new().add_attribute("method", "try_increment"))
}
```
First, it acquires a mutable reference to the storage to update the item located at key `state`. It then updates the state's count by returning an `Ok` result with the new state. Finally, it terminates the contract's execution with an acknowledgement of success by returning an `Ok` result with the `Response`.
```c theme={null}
// src/contract.rs
pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
if info.sender != state.owner {
return Err(ContractError::Unauthorized {});
}
state.count = count;
Ok(state)
})?;
Ok(Response::new().add_attribute("method", "reset"))
}
```
The logic for reset is very similar to increment—except this time, it first checks that the message sender is permitted to invoke the reset function (in this case, it must be the contract owner).
## QueryMsg
You can learn more about CosmWasm [QueryMsg in their documentation](https://docs.cosmwasm.com/docs/smart-contracts/query)
The `GetCount` [query message](https://github.com/InjectiveLabs/cw-counter/blob/59b9fed82864103eb704a58d20ddb4bf94c69787/src/msg.rs#L16) has no parameters and returns the `count` value.
See the implementation details in Logic below.
### Example
The template contract only supports one type of `QueryMsg`:
**GetCount**
The request:
```json theme={null}
{
"get_count": {}
}
```
Which should return:
```json theme={null}
{
"count": 5
}
```
### Message Definition
To support data queries in the contract, you'll have to define both a `QueryMsg` format (which represents requests), as well as provide the structure of the query's output—`CountResponse` in this case. You must do this because `query()` will send information back to the user through structured JSON, so you must make the shape of your response known. See Generating JSON Schema for more info.
Add the following to your `src/msg.rs`:
```c theme={null}
// src/msg.rs
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
// GetCount returns the current count as a json-encoded number
GetCount {},
}
// Define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
pub count: i32,
}
```
### Logic
The logic for `query()` is similar to that of `execute()`; however, since `query()` is called without the end-user making a transaction, the `env` argument is omitted as no information is necessary.
```c theme={null}
// src/contract.rs
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult {
match msg {
QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
}
}
fn query_count(deps: Deps) -> StdResult {
let state = STATE.load(deps.storage)?;
Ok(CountResponse { count: state.count })
}
```
## Unit test
Unit tests should be run as the first line of assurance before deploying the code on chain. They are quick to execute and can provide helpful backtraces on failures with the `RUST_BACKTRACE=1` flag:
```c theme={null}
cargo unit-test // run this with RUST_BACKTRACE=1 for helpful backtraces
```
You can find the [unit test implementation](https://github.com/InjectiveLabs/cw-counter/blob/59b9fed82864103eb704a58d20ddb4bf94c69787/src/contract.rs#L88) at `src/contract.rs`
## Building the Contract
Now that we understand and have tested the contract, we can run the following command to build the contract. This will check for any preliminary errors before we optimize the contract in the next step.
```bash theme={null}
cargo wasm
```
Next, we must optimize the contract in order to ready the code for upload to the chain.
Read more details on [preparing the Wasm bytecode for production](https://github.com/InjectiveLabs/cw-counter/blob/59b9fed82864103eb704a58d20ddb4bf94c69787/Developing.md#preparing-the-wasm-bytecode-for-production)
CosmWasm has [rust-optimizer](https://github.com/CosmWasm/rust-optimizer), an optimizing compiler that can produce a small and consistent build output. The easiest method to use the tool is to use a published Docker image—check [here](https://hub.docker.com/r/cosmwasm/rust-optimizer/tags) for the latest x86 version, or [here](https://hub.docker.com/r/cosmwasm/rust-optimizer-arm64/tags) for the latest ARM version. With Docker running, run the following command to mount the contract code to `/code` and optimize the output (you can use an absolute path instead of `$(pwd)` if you don't want to `cd` to the directory first):
```bash theme={null}
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer:0.12.12
```
If you're on an ARM64 machine, you should use a docker image built for ARM64:
```bash theme={null}
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer-arm64:0.12.12
```
CosmWasm does not recommend using the ARM64 version of the compiler because it produces different Wasm artifacts from the Intel/AMD version. For release/production, only contracts built with Intel/AMD optimizers are recommended for use. See [here](https://github.com/CosmWasm/rust-optimizer#notice) for the note from CosmWasm.
You may receive an `` Unable to update registry `crates-io` `` error while running the command. Try adding the following lines to the `Cargo.toml` file located within the contract directory and running the command again:
```toml theme={null}
[net]
git-fetch-with-cli = true
```
See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli) for more information.
This produces an `artifacts` directory with a `PROJECT_NAME.wasm`, as well as `checksums.txt`, containing the Sha256 hash of the Wasm file. The Wasm file is compiled deterministically (anyone else running the same docker on the same git commit should get the identical file with the same Sha256 hash).
## Install `injectived`
`injectived` is the command-line interface and daemon that connects to Injective and enables you to interact with the Injective blockchain.
If you want to interact with your Smart Contract locally using CLI, you have to have `injectived` installed. To do so, you can follow the installation guidelines in the [injectived installation guide](/developers/injectived/install).
Alternatively, a Docker image has been prepared to make this tutorial easier.
If you install `injectived` from the binary, ignore the docker commands.
In the [public endpoints section](/infra/public-endpoints) you can find the right --node info to interact with Mainnet and Testnet.
Executing this command will make the docker container execute indefinitely.
```bash theme={null}
docker run --name="injective-core-staging" \
-v=/artifacts:/var/artifacts \
--entrypoint=sh public.ecr.aws/l9h3g6c6/injective-core:staging \
-c "tail -F anything"
```
Note: `directory_to_which_you_cloned_cw-template` must be an absolute path. The absolute path can easily be found by running the `pwd` command from inside the CosmWasm/cw-counter directory.
Open a new terminal and go into the Docker container to initialize the chain:
```bash theme={null}
docker exec -it injective-core-staging sh
```
Let’s start by adding `jq` dependency, which will be needed later on:
```bash theme={null}
# inside the "injective-core-staging" container
apk add jq
```
Now we can proceed to local chain initialization and add a test user called `testuser` (when prompted use 12345678 as password). We will only use the test user to generate a new private key that will later on be used to sign messages on the testnet:
```sh theme={null}
# inside the "injective-core-staging" container
injectived keys add testuser
```
**OUTPUT**
```
- name: testuser
type: local
address: inj1exjcp8pkvzqzsnwkzte87fmzhfftr99kd36jat
pubkey: '{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"Aqi010PsKkFe9KwA45ajvrr53vfPy+5vgc3aHWWGdW6X"}'
mnemonic: ""
**Important** write this mnemonic phrase in a safe place.
It is the only way to recover your account if you ever forget your password.
wash wise evil buffalo fiction quantum planet dial grape slam title salt dry and some more words that should be here
```
Take a moment to write down the address or export it as an env variable, as you will need it to proceed:
```bash theme={null}
# inside the "injective-core-staging" container
export INJ_ADDRESS=
```
You can request testnet funds for your recently generated test address using the [Injective test faucet](https://faucet.injective.network/).
Now you have successfully created `testuser` an Injective Testnet. It should also hold some funds after requesting `testnet` funds from the faucet.
To confirm, search for your address on the [Injective Testnet Explorer](https://testnet.explorer.injective.network/) to check your balance.
Alternatively, you can verify it by [querying the bank balance](https://sentry.testnet.lcd.injective.network/swagger/#/Query/AllBalances) or with curl:
```bash theme={null}
curl -X GET "https://sentry.testnet.lcd.injective.network/cosmos/bank/v1beta1/balances/" -H "accept: application/json"
```
## Upload the Wasm Contract
Now it's time to upload the `.wasm` file that you compiled in the previous steps to the Injective Testnet. Please note that the procedure for mainnet is different and [requires a governance proposal.](../mainnet-deployment-guide/)
```bash theme={null}
# inside the "injective-core-staging" container, or from the contract directory if running injectived locally
yes 12345678 | injectived tx wasm store artifacts/my_first_contract.wasm \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443
```
**Output:**
```bash theme={null}
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C
```
Check your address on the [Injective Testnet Explorer](https://testnet.explorer.injective.network), and look for a transaction with the `txhash` returned from storing the code on chain. The transaction type should be `MsgStoreCode`.
You can see all stored codes on Injective Testnet under [Code](https://testnet.explorer.injective.network/smart-contracts/code/).
There are different ways to find the code that you just stored:
* Look for the TxHash on the Injective Explorer [codes list](https://testnet.explorer.injective.network/smart-contracts/code/); it is most likely the most recent.
* Use `injectived` to query transaction info.
To query the transaction use the `txhash` and verify the contract was deployed.
```sh theme={null}
injectived query tx 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C --node=https://testnet.sentry.tm.injective.network:443
```
Inspecting the output more closely, we can see the `code_id` of `290` for the uploaded contract:
```bash theme={null}
- events:
- attributes:
- key: access_config
value: '{"permission":"Everybody","address":""}'
- key: checksum
value: '"+OdoniOsDJ1T9EqP2YxobCCwFAqNdtYA4sVGv7undY0="'
- key: code_id
value: '"290"'
- key: creator
value: '"inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty"'
type: cosmwasm.wasm.v1.EventCodeStored
- attributes:
- key: action
value: /cosmwasm.wasm.v1.MsgStoreCode
- key: module
value: wasm
- key: sender
value: inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty
type: message
- attributes:
- key: code_id
value: "290"
type: store_code
```
Let's export your `code_id` as an ENV variable—we'll need it to instantiate the contract. You can skip this step and manually add it later, but keep note of the ID.
```bash theme={null}
export CODE_ID=
```
## Generating JSON Schema
While the Wasm calls `instantiate`, `execute`, and `query` accept JSON, this is not enough information to use them. We need to expose the schema for the expected messages to the clients.
In order to make use of JSON-schema auto-generation, you should register each of the data structures that you need schemas for.
```c theme={null}
// examples/schema.rs
use std::env::current_dir;
use std::fs::create_dir_all;
use cosmwasm_schema::{export_schema, remove_schemas, schema_for};
use my_first_contract::msg::{CountResponse, HandleMsg, InitMsg, QueryMsg};
use my_first_contract::state::State;
fn main() {
let mut out_dir = current_dir().unwrap();
out_dir.push("schema");
create_dir_all(&out_dir).unwrap();
remove_schemas(&out_dir).unwrap();
export_schema(&schema_for!(InstantiateMsg), &out_dir);
export_schema(&schema_for!(ExecuteMsg), &out_dir);
export_schema(&schema_for!(QueryMsg), &out_dir);
export_schema(&schema_for!(State), &out_dir);
export_schema(&schema_for!(CountResponse), &out_dir);
}
```
The schemas can then be generated with
```bash theme={null}
cargo schema
```
which will output 5 files in `./schema`, corresponding to the 3 message types the contract accepts, the query response message, and the internal `State`.
These files are in standard JSON Schema format, which should be usable by various client side tools, either to auto-generate codecs, or just to validate incoming JSON with respect to the defined schema.
Take a minute to generate the schema ([take a look at it here](https://github.com/InjectiveLabs/cw-counter/blob/master/schema/cw-counter.json)) and get familiar with it, as you will need to it for the next steps.
## Instantiate the Contract
Now that we have the code on Injective, it is time to instantiate the contract to interact with it.
Reminder On CosmWasm, the upload of a contract's code and the instantiation of a contract are regarded as separate events
To instantiate the contract, run the following CLI command with the code\_id you got in the previous step, along with the [JSON encoded initialization arguments](https://github.com/InjectiveLabs/cw-counter/blob/ea3b781447a87f052e4b8308d5c73a30481ed61f/schema/cw-counter.json#L7) and a label (a human-readable name for this contract in lists).
```bash theme={null}
INIT='{"count":99}'
yes 12345678 | injectived tx wasm instantiate $CODE_ID $INIT \
--label="CounterTestInstance" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj \
--gas=2000000 \
--no-admin \
--node=https://testnet.sentry.tm.injective.network:443
```
**Output:**
```bash theme={null}
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 01804F525FE336A5502E3C84C7AE00269C7E0B3DC9AA1AB0DDE3BA62CF93BE1D
```
You can find the contract address and metadata by:
* Looking on the [Testnet Explorer](https://www.injscan.com/smart-contracts/)
* Querying the [ContractsByCode](https://k8s.testnet.lcd.injective.network/swagger/#/Query/ContractsByCode) and [ContractInfo](https://k8s.testnet.lcd.injective.network/swagger/#/Query/ContractInfo) APIs
* Querying through the CLI
```bash theme={null}
injectived query wasm contract inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 --node=https://testnet.sentry.tm.injective.network:443
```
## Querying the Contract
As we know from earlier, the only QueryMsg we have is `get_count`.
```bash theme={null}
GET_COUNT_QUERY='{"get_count":{}}'
injectived query wasm contract-state smart inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$GET_COUNT_QUERY" \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
```
**Output:**
```bash theme={null}
{"data":{"count":99}}
```
We see that `count` is 99, as set when we instantiated the contract.
If you query the same contract, you may receive a different response as others may have interacted with the contract and incremented or reset the count.
## Execute the Contract
Let's now interact with the contract by incrementing the counter.
```bash theme={null}
INCREMENT='{"increment":{}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$INCREMENT" --from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
```
If we query the contract for the count, we see:
```bash theme={null}
{"data":{"count":100}}
```
**yes 12345678 |** automatically pipes (passes) the passphrase to the input of **injectived tx wasm execute** so you do not need to enter it manually.
To reset the counter:
```bash theme={null}
RESET='{"reset":{"count":999}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$RESET" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
```
Now, if we query the contract again, we see the count has been reset to the provided value:
```bash theme={null}
{"data":{"count":999}}
```
## Cosmos Messages
In addition to defining custom smart contract logic, CosmWasm also allows contracts to interact with the underlying Cosmos SDK functionalities. One common use case is to send tokens from the contract to a specified address using the Cosmos SDK's bank module.
### Example: Bank Send
The `BankMsg::Send` message allows the contract to transfer tokens to another address. This can be useful in various scenarios, such as distributing rewards or returning funds to users.
**Note:** If you want to send funds and execute a function on another contract at the same time, don't use BankMsg::Send. Instead, use WasmMsg::Execute and set the respective funds field.
### Constructing the Message
You can construct a `BankMsg::Send` message within your contract's `execute` function. This message requires specifying the recipient address and the amount to send. Here's an example of how to construct this message:
```rust theme={null}
use cosmwasm_std::{BankMsg, Coin, Response, MessageInfo};
pub fn try_send(
info: MessageInfo,
recipient_address: String,
amount: Vec,
) -> Result {
let send_message = BankMsg::Send {
to_address: recipient_address,
amount,
};
let response = Response::new().add_message(send_message);
Ok(response)
}
```
### Usage in a Smart Contract
In your contract, you can add a new variant to your ExecuteMsg enum to handle this bank send functionality. For example:
```rust theme={null}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
// ... other messages ...
SendTokens { recipient: String, amount: Vec },
}
```
Then, in the `execute` function, you can add a case to handle this message:
```rust theme={null}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result {
match msg {
// ... other message handling ...
ExecuteMsg::SendTokens { recipient, amount } => try_send(info, recipient, amount),
}
}
```
## Testing
As with other smart contract functions, you should add unit tests to ensure your bank send functionality works as expected. This includes testing different scenarios, such as sending various token amounts and handling errors correctly.
You may use [test-tube](https://github.com/InjectiveLabs/test-tube) for running integration tests that include a local Injective chain.
Congratulations! You've created and interacted with your first Injective smart contract and now know how to get started with CosmWasm development on Injective. Continue to Creating a Frontend for Your Contract for a guide on creating a web UI.
# Whitelisting deployment address
Source: https://docs.injective.network/developers-cosmwasm/whitelisting-deployment-address-guide
Contract upload on Injective mainnet requires governance approval. You can read through the guide to do so in the [mainnet-deployment.md](/developers-cosmwasm/mainnet-deployment-guide/ "mention") guide.
This structure has been put in place for multiple reasons:
1. Historically, the community has discovered vulnerabilities in CosmWasm whereby certain contracts could cause a chain halt. Thus, requiring approval will be inherently more secure until the language matures and stabilizes over time.
2. Governance prevents unaudited and bogus contracts from being deployed into the network, ensuring that hacks and scams will occur at a lesser degree.
3. This results in a more curated experience for ecosystem users, since the blockchain does not fill up with unwanted transactions and contracts.
This governance process is time-consuming for validators and the community. Injective builders additionally face a 4-day wait to roll out features, affecting developer experience and user growth.
As of the Altaris chain upgrade, it is now possible to request whitelisting your address for contract uploads.
## Submission Guidelines
To be considered for a whitelist approval, it is strongly recommended to include all the following information in the governance proposal. Validators are recommended to contact the Foundation in Discord to verify the information submitted for each proposal before voting.
1. Are the identities of the team members known to the community?
2. Has the team completed KYC/KYB with the Injective Foundation?
3. How long has the protocol been live on Injective mainnet?
4. Has the team developed applications in other ecosystems? (provide the details)
## Voting Guidelines
Users that meet these criteria will have a greater chance at receiving whitelist permissions:
1. The project has completed Injective Foundation KYC/KYB and the team members' identities are known to the community. *Exception: Anonymous developers with proven credibility and successful apps in other ecosystems.*
2. The project has been live on mainnet for at least 1 month and has achieved significant TVL/usage.
## Operational Guidelines
It is strongly recommended to use a multisig or Ledger wallet for the whitelisted uploads.
# Min Price Tick Size
Source: https://docs.injective.network/developers-defi/calculate-min-price-tick-size
## Minimum Market Price Tick Size
The minimum market price tick size dictates the smallest increment by which an order price can increase or decrease. For instance, if a market has a minimum price tick size of **0.001**, an order submitted with a price of **0.0011** would be rejected because it does not align with the allowed increments.
**Note:** The formulas for calculating the price tick size differ between spot and derivative markets.
### Spot Market
#### Conversion from Human-Readable Format to Chain Format
Using the INJ/USDT market as an example, which has **18 base decimals** and **6 quote decimals**, the conversion to chain format is as follows:
$\text{chainFormat} = \text{value} \times 10^{(\text{quoteDecimals} - \text{baseDecimals})}$
#### Conversion from Chain Format to Human-Readable Format
To convert back to a human-readable format:
$\text{humanReadableFormat} = \text{value} \times 10^{(\text{baseDecimals} - \text{quoteDecimals})}$
### Derivative Market
#### Conversion from Human-Readable Format to Chain Format
Using the INJ/USDT perpetual market with **6 quote decimals** as an example, the conversion to chain format is:
$\text{chainFormat} = \text{value} \times 10^{-\text{quoteDecimals}}$
#### Conversion from Chain Format to Human-Readable Format
To convert back to a human-readable format:
$\text{humanReadableFormat} = \text{value} \times 10^{-\text{quoteDecimals}}$
Also, be sure to check out our [concepts](../developers/concepts/calculation-min-price-tick-size/).
# Denom Metadata
Source: https://docs.injective.network/developers-defi/denom-and-bank
A `denom` is how tokens 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.
One of the biggest pain points for developers and traders is getting the metadata of these `denoms`. This metadata includes `decimals`, `symbol`, `name`, etc.
This guide shows how to fetch `denom` metadata directly from the `injective-lists` repository and map it to your `denom`. You can also use this approach to map `denoms`'s metadata for Spot and Derivative Markets.
## Injective Lists
`injective-lists` is a public repository that holds metadata information for all tokens on Injective. It's the most up-to-date and reliable source of this particular information. You can submit your token information by creating a PR for this repo. Be sure to correctly specify the fields. In particular, `"denom"` field (read about [token standards](/defi/tokens/)) should have respective `ibc`, `peggy` and `factory` prefixes depending on the token standard.
The metadata is fetched automatically for new `denoms` on chain every 30 minutes and the `json` files are regenerated.
You can head to the [https://github.com/InjectiveLabs/injective-lists/tree/master/json/tokens](https://github.com/InjectiveLabs/injective-lists/tree/master/json/tokens) folder and download the metadata based on the environment:
1. [Mainnet Raw JSON](https://raw.githubusercontent.com/InjectiveLabs/injective-lists/refs/heads/master/json/tokens/mainnet.json)
2. [Testnet Raw JSON](https://github.com/InjectiveLabs/injective-lists/blob/master/json/tokens/testnet.json)
Once you have the JSON, you have to map the metadata with the particular `denom`.
The interface that this metadata information has is
```typescript theme={null}
export interface Token {
name: string;
logo: string;
symbol: string;
decimals: number;
coinGeckoId: string;
denom: string;
address: string;
tokenType: string;
tokenVerification: string;
}
```
### Bank Balance
Let's say you fetch the bank balance of a particular address (as shown on the example below using TypeScript), you can easily map it to the metadata information from the JSON files above
```typescript theme={null}
import { config } from "dotenv";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { IndexerGrpcPortfolioApi } from "@injectivelabs/sdk-ts/client/indexer";
config();
/** Querying Example */
(async () => {
const endpoints = getNetworkEndpoints(Network.MainnetSentry);
const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc);
const injectiveAddress = "inj...";
const { balances } = chainGrpcBankApi.fetchBalances(injectiveAddress);
console.log(bankBalances);
const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
denom: string;
address: string;
decimals: string;
logo: string;
name: string;
tokenType: string;
coinGeckoId: string
}[];
const balances = bankBalances.map((balance) => {
const meta = metadata.find((m) => m.denom === balance.denom);
return {
...balance,
...meta,
};
}
console.log(balances)
})();
```
Now, your bank balance has all of the metadata information that you need (including `decimals`, `symbol`, `name`, `logo`, etc).
### Spot Market
Similar to the Bank balances, you can use the same approach to map the `denoms` within a Spot market with it's metadata.
```typescript theme={null}
import { config } from "dotenv";
import { readFile } from "fs/promises";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts/client/indexer";
config();
/** Querying Example */
(async () => {
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);
const markets = await indexerGrpcSpotApi.fetchMarkets();
console.log(markets);
const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
denom: string;
address: string;
decimals: string;
logo: string;
name: string;
tokenType: string;
coinGeckoId: string;
}[];
const marketsWithMetadata = markets.map((market) => {
const baseTokenMetadata = metadata.find(
(m) => m.denom === market.baseDenom
);
const quoteTokenMetadata = metadata.find(
(m) => m.denom === market.quoteDenom
);
return {
...market,
baseTokenMetadata,
quoteTokenMetadata,
};
});
console.log(marketsWithMetadata);
})();
```
### Derivative Market
Similar to the Bank balances, you can use the same approach to map the `denom` within a Derivative market with it's metadata.
```typescript theme={null}
import { config } from "dotenv";
import { readFile } from "fs/promises";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
config();
/** Querying Example */
(async () => {
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativeApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const markets = await indexerGrpcDerivativeApi.fetchMarkets();
console.log(markets);
const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
denom: string;
address: string;
decimals: string;
logo: string;
name: string;
tokenType: string;
coinGeckoId: string;
}[];
const marketsWithMetadata = markets.map((market) => {
const baseTokenMetadata = metadata.find(
(m) => m.denom === market.baseDenom
);
return {
...market,
quoteTokenMetadata,
};
});
console.log(marketsWithMetadata);
})();
```
# Overview
Source: https://docs.injective.network/developers-defi/index
Injective marks the forefront of exchange-focused layer-1 blockchains offering decentralized perpetual swaps, futures, and spot trading. It fully taps into the possibilities of decentralized derivatives and borderless DeFi. Every component is designed to embody complete trustlessness, resistance to censorship, public verifiability, and resilience against front-running.
Injective enables traders to create and trade arbitrary spot and derivatives markets. Injective also enables on-chain limit orderbook management, on-chain trade execution, on-chain order matching, on-chain transaction settlement, and on-chain trading incentive distribution through the logic codified by the Injective Chain's [Exchange Module](/developers-native/injective/exchange).
## Build an Orderbook DEX on Injective
As an incentive mechanism to encourage exchanges to build on Injective and source trading activity, exchanges that originate orders into the shared orderbook of Injective's exchange protocol ([read more](/developers-native/injective/exchange)) are rewarded with $β=40%β=40%$ of the trading fee from orders that they source. The exchange protocol implements a global minimum trading fee of $rm=0.1%rm=0.1%$ for makers and $rt=0.2%rt=0.2%$ for takers.
The goal of Injective's incentive mechanism is to allow exchanges competing amongst each other to provide a better user experience and to better serve users, broadening access to DeFi for users all around the world.
An exchange can easily set up a client (such as a UI on web or mobile) and an API provider.
Head to our [DEX tutorial](/developers/dapps/example-dex) to bootstrap your DEX with a few clicks!
# Injective Trader
Source: https://docs.injective.network/developers-defi/injective-trader/index
# Injective Trader
Injective Trader is a professional-grade framework that provides a high-performance, reliable foundation for developing and deploying algorithmic trading strategies on the Injective blockchain. It bridges the gap between algorithmic trading strategies and blockchain execution, eliminating technical barriers. The framework takes care of the heavy lifting - real-time data streaming, order execution, reconnection/recovery, transaction batching, and analytics This frees up traders to focus solely on strategy development rather than blockchain complexities.
You can use it to import existing strategies or create new ones that:
* **Place orders automatically** based on your logic
* **Monitor markets 24/7** and react to price changes
* **Manage risk** with built-in limits and safety features
* **Handle multiple markets** simultaneously
* **Provide detailed logs** of all trading activity
### Core Capabilities
**Simplified Strategy Development**
* No SDK expertise required - focus purely on trading logic
* Rapid strategy deployment with minimal technical overhead
* Event-driven architecture enabling intuitive strategy implementation
**Built-in Reliability**
* Automated reconnection and recovery mechanisms
* Transaction validation before execution
* Comprehensive error handling and retry logic
**Performance Optimization**
* Intelligent transaction batching for cost reduction
* Automatic fee management and optimization
* Multi-account support for scale
**Enterprise-Ready Features**
* Complete position and PnL tracking
* Risk management capabilities
* Detailed performance analytics
Injective trader transacts assets with real value, as such security is paramount. Be sure to use the following as a security baseline, and also take further measures to protect your assets.
* **Never share private keys** or commit them to Git.
* Store secrets in a local **`.env` file** and load via environment variables.
* For extra safety, consider using
[**AuthZ**](https://github.com/InjectiveLabs/injective-trader/tree/master?tab=readme-ov-file#authorization-trading-authz)
to grant trading rights without exposing your main account.
## Quick Start (5 minutes)
### 1. Get Your Injective Account Ready (and Funded)
1. **Create an account** on Injective using Keplr or `injectived`.
2. If using Keplr, **export your private key** for the `.env` file.
* *Tip: With AuthZ, you can grant limited permissions to a trading account for better security.*
3. **Fund your account** with USDT by sending from another Injective address, or via [bridge.injective.network](http://bridge.injective.network/).
* *EVM tip: You can derive your `inj` address with the TS SDK and bridge USDT from Ethereum to Injective without even setting up an Injective account.*
### 2. Download and Setup
```bash theme={null}
git clone https://github.com/InjectiveLabs/injective-trader.git
cd injective-trader
# Create a virtual environment (recommended)
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements.txt
pip install "injective-py==1.9"
```
*Note: `injective-trader` is not yet compatible with `injective-py` v1.11.*
### 3. Configure Your Strategy
Edit the preexisting `config.yaml`:
```yaml theme={null}
Exchange: Helix
ConsoleLevel: INFO
FileLevel: DEBUG
Components:
Initializer:
Network: mainnet
BotName: MyBot
MarketTickers:
- INJ/USDT PERP
- BTC/USDT PERP
- ETH/USDT PERP
Strategies:
MyMarketMaker:
Name: "MyMarketMaker"
Class: "SimpleStrategy"
MarketIds:
- "0x17ef48032..." # INJ/USDT PERP
- "0x4ca0f92f..." # BTC/USDT PERP
- "0x9b998016..." # ETH/USDT PERP
AccountAddresses:
- "inj1your_account_address_here"
TradingAccount: "inj1your_account_address_here"
CIDPrefix: "my_mm"
Parameters:
OrderSize: 0.1
MaxPosition: 1.0
SpreadThreshold: 0.005
```
### 4. Set Your Private Key
Instead of a single `INJECTIVE_PRIVATE_KEY`, use **bot-scoped environment variables** in `.env` (matches the framework defaults):
```
# For bot named "MyBot"
MyBot_GRANTER_INJECTIVE_PRIVATE_KEY=your_granter_private_key_here
MyBot_GRANTEE_0_INJECTIVE_PRIVATE_KEY=your_first_grantee_private_key_here
```
Load them into your session:
```bash theme={null}
export $(grep -v '^#' .env | xargs)
```
### 5. Run Your Strategy
```bash theme={null}
python main.py MyBot config.yaml --log_path logs/my_bot.log --network mainnet
```
That's it - your bot is now live!
## IDE set up
If you are using VS code or compatible IDEs (such as Cursor), consider adding the following configuration for easy debugging.
`.vscode/launch.json`:
```json theme={null}
{
"version": "0.2.0",
"configurations": [
{
"name": "Run MyBot (mainnet)",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/main.py",
"console": "integratedTerminal",
"args": ["MyBot", "config.yaml", "--log_path", "logs/strategy.log", "--network", "mainnet"],
"envFile": "${workspaceFolder}/.env"
},
{
"name": "Run MyBot (testnet, debug)",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/main.py",
"console": "integratedTerminal",
"args": ["MyBot", "config.yaml", "--log_path", "logs/debug.log", "--network", "testnet", "--debug"],
"envFile": "${workspaceFolder}/.env"
}
]
}
```
You should now be able to do **Run → Start Debugging** in your IDE.
## Architecture
### System Architecture Diagram
Injective Trader Network Architecture Diagram
### Core Design Patterns
* **Mediator Pattern**: Centralizes communication between components, enabling a decoupled architecture where components interact without direct dependencies.
* **Component Pattern**: Standardizes lifecycle management (initialize, run, terminate) for all system components, ensuring consistent behavior.
* **State Pattern**: Manages component lifecycle through well-defined states (Idle, Running, Terminated), providing predictable transitions and error handling.
* **Task Management Pattern**: Coordinates asynchronous tasks with automated monitoring and recovery, ensuring reliable execution in an event-driven environment.
* **Observer Pattern**: Enables strategies to react to specific update events through specialized event handlers, creating a flexible strategy development approach.
### Key Components
**Exchange-Specific Agents**
* **Initializer**: Sets up exchange connections, accounts, and markets
* **ChainListener**: Streams real-time blockchain data with automatic reconnection
* **MessageBroadcaster**: Handles transaction creation and broadcasting with retry logic
* **Liquidator**: Monitors and executes liquidations for undercollateralized positions
**Managers**
* **MarketManager**: Processes market data and maintains orderbook integrity
* **AccountManager**: Tracks balances, positions, and order state
* **StrategyManager**: Routes market events to appropriate strategy implementations
* **RiskManager**: Enforces position limits and risk controls
* **TaskManager**: Orchestrates and monitors asynchronous task execution
**Data-Level Domains**
* **Market**: Represents trading pairs with orderbooks and metadata
* **Account**: Manages account balances, deposits, and subaccounts
* **Positions**: Tracks derivative positions with P\&L calculations
* **Order**: Order state tracking with execution history
* **Oracle Prices**: Real-time price feeds with timestamp tracking
**Strategy-Level Plugins**
* **Strategy Base**: Template for implementing custom strategies
* **Update Handlers**: Event-specific processors for market data events
* **Performance Metrics**: Statistics and P\&L tracking
* **Risk Models**: Customizable risk management rules
## Next
Learn more about the [simple strategy](/developers-defi/injective-trader/simple-strategy) that ships with Injective Trader, to get yourself comfortable with the codebase before diving in.
# Getting Started
Source: https://docs.injective.network/developers-defi/injective-trader/simple-strategy
# Getting Started
Injective Trader comes built in with a "Simple Strategy" to aid with rapid prototyping and familiarizing yourself with the codebase.
**What it does:**
* Monitors orderbooks for INJ, BTC, ETH
* Places buy orders slightly below market price
* Places sell orders slightly above market price
* Maintains a spread for profitability
* Respects position limits for risk control
**Best for:** predictable and steady trading while familiarizing yourself with Injective Trader. **Not recommended for production use.**
**Example Logs:**
```
[INFO] Placing BUY order: 0.1 INJ at $3.45 (spread: 0.5%)
[INFO] Placing SELL order: 0.1 INJ at $3.47 (spread: 0.5%)
[INFO] Order filled: BUY 0.1 INJ at $3.45
```
## Customizing Your Strategy
### Order size
```yaml theme={null}
OrderSize: 0.5
MaxPosition: 2.0
```
### More markets
```yaml theme={null}
MarketTickers:
- INJ/USDT PERP
- BTC/USDT PERP
- ETH/USDT PERP
- APT/USDT PERP
- AVAX/USDT PERP
- SOL/USDT PERP
```
### Spreads
```yaml theme={null}
SpreadThreshold: 0.01 # conservative
SpreadThreshold: 0.002 # aggressive
```
## Common Configurations
### Conservative Maker
```yaml theme={null}
OrderSize: 0.05
MaxPosition: 0.5
SpreadThreshold: 0.01
```
### Aggressive Maker
```yaml theme={null}
OrderSize: 0.5
MaxPosition: 5.0
SpreadThreshold: 0.002
```
### Multi-Market Strategy
```yaml theme={null}
MarketTickers:
- INJ/USDT PERP
- BTC/USDT PERP
- ETH/USDT PERP
- APT/USDT PERP
- AVAX/USDT PERP
- SOL/USDT PERP
- TON/USDT PERP
- ATOM/USDT PERP
```
## Monitoring Your Bot
```bash theme={null}
tail -f logs/my_bot.log
grep "Order filled" logs/my_bot.log
grep "ERROR" logs/my_bot.log
```
### Key Messages
* ✅ Order placed successfully
* 💰 Order filled
* ⚠️ Position limit reached
* ❌ Insufficient balance
### Performance Metrics
* Total PnL
* Win rate
* Fill rate
* Average spread
## Risk Management
* Set position limits (`MaxPosition`)
* Monitor positions and stop out manually if needed
* Maintain enough USDT for margin, fees, and buffer
## Troubleshooting
**No private keys found**
```bash theme={null}
echo $MyBot_GRANTER_INJECTIVE_PRIVATE_KEY
```
* **Insufficient balance** → Add USDT / reduce `OrderSize`
* **Market not found** → Double-check tickers/IDs
* **Bot stops working**
```bash theme={null}
grep "ERROR" logs/my_bot.log | tail -10
python main.py MyBot config.yaml --log_path logs/my_bot.log --network mainnet
```
## Advanced Features
* **Multiple accounts** via `AccountAddresses`
* **Custom order types** (limit, market, reduce-only)
* **External signals** with Redis/Valkey
## Next
Learn how to develop your own [custom strategy](/developers-defi/injective-trader/strategy-development-guide) for Injective Trader.
# Strategy Development Guide
Source: https://docs.injective.network/developers-defi/injective-trader/strategy-development-guide
# Strategy Development Guide
## Configuration Guide
The Injective Trader uses a YAML configuration file to define behavior, components, and strategy parameters.
The most important configuration sections to focus on are:
* `LogLevel`
* `Network` and `MarketTickers` in the `Initializer` under the `Components` section
* `Strategies` section
Here's a detailed breakdown of the configuration structure:
### Top-Level Parameters
```yaml theme={null}
Exchange: Helix # Trading exchange to use
LogLevel: INFO # Logging level (DEBUG, INFO, WARNING, ERROR)
```
### Components Section
The `Components` section configures framework components:
```yaml theme={null}
Components:
# Chain initialization and market setup
Initializer:
Network: mainnet # Network to connect to (mainnet or testnet)
MarketTickers: # Market tickers to track (will be converted to IDs)
- INJ/USDT PERP
- ETH/USDT
BotName: MyBot
# Chain listening configuration
ChainListener:
ReconnectionDelay: 5 # Seconds to wait before reconnection attempts
LargeGapThreshold: 50 # Sequence gap threshold for orderbook snapshot requests
# Transaction broadcasting configuration
MessageBroadcaster:
ErrorCodesJson: config/error_codes.json # Error code lookup for tx validation
GranteePool: # For authz transaction mode
MaxPendingTxs: 5 # Maximum pending transactions per grantee
ErrorThreshold: 3 # Consecutive errors before blocking a grantee
BlockDuration: 300 # Seconds to block a grantee after errors
RotationInterval: 1 # Seconds between grantee rotations
RefreshInterval: 300 # Seconds between grant refresh checks
Batch: # Transaction batching settings
MaxBatchSize: 15 # Maximum messages per batch
MinBatchSize: 3 # Minimum messages to trigger immediate send
MaxGasLimit: 5000000 # Maximum gas per batch
MaxBatchDelay: 0.5 # Maximum seconds to wait for batch completion
```
**Note**: Most users only need to take care of `Network` and include all the markets that they want to listen to in `MarketTickers`.
They won't need to modify these advanced component settings. The default values work well for most use cases.
### Strategies Section
The `Strategies` section defines each trading strategy:
```yaml theme={null}
Strategies:
SimpleStrategy: # Strategy identifier (your choice)
# Required parameters
Name: "SimpleStrategy" # Strategy name (used in logs)
Class: "SimpleStrategy" # [REQUIRED] Python class name to instantiate
MarketIds: # [REQUIRED] Markets to trade on
- "0x9b9980167ecc3645ff1a5517886652d94a0825e54a77d2057cbbe3ebee015963" # INJ/USDT PERP
AccountAddresses: # [REQUIRED] Accounts to use
- "inj1youractualaccount..." # (Must match private key in env)
TradingAccount: "inj1youractualaccount..." # [REQUIRED] Account for placing orders (Must match private key in env)
# Optional parameters
FeeRecipient: "inj1feerecipient..." # Address to receive trading fees (if applicable)
CIDPrefix: "simple_strat" # Prefix for client order IDs
SubaccountIds: ["0x123..."] # Specific subaccounts to use (otherwise all available)
# Risk management configuration [Optional]
# You don't have to include them if you don't have your specific risk model
Risk: "BasicRiskModel" # Risk model to apply (if using risk management)
RiskConfig: # Risk thresholds
DrawdownWarning: 0.1 # 10% drawdown warning threshold
DrawdownCritical: 0.2 # 20% drawdown critical threshold
MarginWarning: 0.7 # 70% margin usage warning
MarginCritical: 0.8 # 80% margin usage critical
```
**Required Strategy Parameters:**
* `Class`: Must exactly match your Python class name
* `MarketIds`: List of market IDs to trade on in this strategy (use hex format)
* `AccountAddresses`: List of accounts to use for trading in this strategy
* `TradingAccount`: Account used for order execution (must be in `AccountAddresses`) \[See more details on [Trading Mode Configuration](https://www.notion.so/Trading-Mode-Configuration-1bb7a004ab758056affdefc2c99aca08?pvs=21) ]
**Recommended Parameters:**
* `CIDPrefix`: Prefix for client order IDs (helps identify your orders)
* `Name`: Human-readable name for logs and monitoring
**Custom Parameters:**
* You can add any custom parameters your strategy needs
* All parameters under your strategy name will be available in `self.config`
* Group related parameters under the `Parameters` section for clarity
### Trading Mode Configuration
The framework supports two trading modes:
#### Direct Execution Mode
```yaml theme={null}
Strategies:
SimpleStrategy:
# Other parameters...
TradingAccount: "inj1youraccount..." # Account that will sign and broadcast transactions
```
#### Authorization (Authz) Mode
```yaml theme={null}
Strategies:
SimpleStrategy:
# Other parameters...
Granter: "inj1granteraccount..." # Account granting permission to execute trades
Grantees: # Accounts that can execute trades on behalf of granter
- "inj1grantee1..."
- "inj1grantee2..."
```
**Note**: You must specify either `TradingAccount` for direct execution OR `Granter` and `Grantees` for authorization mode.
The framework enforces this requirement during initialization.
### RetryConfig Section
The `RetryConfig` section controls retry behavior for network operations:
```yaml theme={null}
RetryConfig:
# Global retry settings
DefaultRetry:
max_attempts: 3 # Maximum retry attempts
base_delay: 1.0 # Base delay between retries (seconds)
max_delay: 32.0 # Maximum delay cap (seconds)
jitter: true # Add randomness to delay
timeout: 30.0 # Operation timeout (seconds)
error_threshold: 10 # Errors before circuit breaking
error_window: 60 # Error counting window (seconds)
# Component-specific retry settings (override defaults)
ChainListener:
max_attempts: 5 # More retries for chain listener
base_delay: 2.0
max_delay: 45.0
MessageBroadcaster:
max_attempts: 3
base_delay: 1.0
max_delay: 30.0
```
**Note**: RetryConfig has sensible defaults and typically doesn't need customization unless you're experiencing specific connectivity issues.
***
Now that we understand the overall structure, we are ready to develop custom ones!
## Strategy Development Guide
Strategies in the Injective Trader follow a consistent structure based on the `Strategy` base class. This section explains how to build effective strategies.
### Strategy Class Structure
Your strategy class inherits from the base `Strategy` class:
```python theme={null}
from src.core.strategy import Strategy, StrategyResult
from src.utils.enums import UpdateType, Side
class SimpleStrategy(Strategy):
def __init__(self, logger, config):
"""
Initialize strategy with logger and configuration.
Args:
logger: Logger instance for strategy logging
config: Strategy configuration dictionary
Required keys:
- MarketIds: List of market IDs to trade
- AccountAddresses: List of account addresses to use
Optional keys:
- Name: Strategy name
- Parameters: Strategy-specific parameters
- RiskConfig: Risk management parameters
"""
super().__init__(logger, config)
def on_initialize(self, accounts, markets):
"""
Initialize strategy-specific state and parameters.
Called once before strategy starts processing updates.
Args:
accounts: Dictionary of account_address -> Account
markets: Dictionary of market_id -> Market
"""
pass
async def _execute_strategy(self, update_type, processed_data):
"""
Strategy-specific execution logic.
Args:
update_type: Type of update being processed
processed_data: Update-specific data dictionary after handler processing
Common fields:
- market_id: Market identifier
- account_address: Account address (for account updates)
- subaccount_id: Subaccount identifier (for position/trade updates)
Returns:
StrategyResult:
- orders
- cancellations
- margin updates
"""
pass
```
#### Strategy Constructor (`__init__`)
Your strategy class can include a constructor that calls the parent class constructor:
```python theme={null}
def __init__(self, logger, config):
super().__init__(logger, config)
# Custom initialization that doesn't require markets or accounts
self.custom_parameter = 42
# Optional: Custom handler overrides
self.handlers[UpdateType.OnOrderbook] = MyCustomOrderbookHandler(
self.logger, self.config, self.metrics
)
# Optional: Custom performance metrics overrides
self.my_metrics = MyCustomPerformanceMetrics(self.logger)
```
The base class constructor handles:
1. Parameter validation and extraction
2. Setting up standard metrics and handlers (See \[block link] for more information on writing your own handlers)
3. Initializing state tracking containers
4. Setting up trading mode (direct or authz)
**Important**: The `__init__` method cannot access market data or account information.
Use `on_initialize` for operations requiring those resources.
Available properties provided by base `__init__` are:
| **Property** | **Description** | **Source** | **Required** |
| ------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------- | -------------- |
| `self.name` | Strategy name used in log | From config `Name` | YES |
| `self.logger` | Logger instance | For strategy-specific logging | YES |
| `self.config` | Complete strategy configuration | Corresponding strategy subsection under `Strategies` section in config | YES |
| `self.market_ids` | List of market IDs interested in this strategy | From config `MarketIds` | YES |
| `self.account_addresses` | List of account addresses interested in this strategy | From config `AccountAddresses` | YES |
| `self.subaccount_ids` | List of subaccount IDs | From config `SubaccountIds` | NO |
| `self.markets` | Dictionary of market\_id → `Market` objects | Populated during initialization | NO |
| `self.accounts` | Dictionary of account\_address → `Account` objects | Populated during initialization | NO |
| `self.trading_mode` | "direct" or "authz” | Based on config | YES |
| `self.fee_recipient` | Fee recipient address | From config `FeeRecipient` | NO |
| `self.cid_prefix` | Client order ID prefix | From config `CIDPrefix` | NO |
| `self.metrics` | Performance tracking | For recording metrics and alerts | YES \[DEFAULT] |
| `self.handlers` | Event handlers dictionary | UpdateType → Handler objects | YES \[DEFAULT] |
#### Initialization Method (`on_initialize`)
The `on_initialize` method is called once during framework startup, after markets and accounts are loaded.
**Purpose**: Initialize strategy state and parameters **Parameters**:
* `accounts`: Dictionary of account\_address → `Account` objects
* `markets`: Dictionary of market\_id → `Market` objects
**Returns**: \[Optional] `StrategyResult` with initial orders (if any)
```python theme={null}
def on_initialize(self, accounts, markets):
# Now you have access to all market and account data
# Example: Access market metadata
for market_id in self.market_ids:
market = markets[market_id]
self.logger.info(f"Market {market_id} tick sizes: "
f"price={market.min_price_tick}, "
f"quantity={market.min_quantity_tick}")
# Example: Initialize parameters that need market info
self.avg_prices = {
market_id: markets[market_id].orderbook.tob()[0]
for market_id in self.market_ids
if markets[market_id].orderbook.tob()[0]
}
# Example: Place initial orders
if self.config.get("PlaceInitialOrders", False):
result = StrategyResult()
# Add initial orders...
return result
return None # No initial orders
```
This method is part of the strategy initialization sequence:
1. Framework loads markets and accounts required by this strategy
2. Your `on_initialize` method is called with loaded data
3. Any returned orders are immediately submitted
4. The strategy moves to running state
**Tip**: Use `on_initialize` for parameter initialization that requires market or account data, and to place any initial orders needed for your strategy.
For data structure information on `Account` and `Market`, see below.
#### Strategy Logic (`_execute_strategy` ) Method
The `_execute_strategy` method is a part of “Strategy Execution (`execute`) Method”. The base class `execute` method handles the complete execution flow:
1. **Initialization check**: Initializes the strategy if needed
2. **State update**: Updates the strategy's account and market references
3. **Data processing**: Processes raw update data through the appropriate handler
4. **Strategy execution**: Calls your `_execute_strategy` method with processed data
5. **Order enrichment**: Adds default values to orders (fee recipient, client ID)
You rarely need to override this method. Instead, focus on implementing `_execute_strategy` where your custom trading logic goes:
**Purpose**: Analyze market data and generate trading signals **Parameters**:
* `update_type`: Type of update being processed \[See [Update Types and Corresponding Data Fields](https://www.notion.so/Update-Types-and-Corresponding-Data-Fields-1bb7a004ab7580c1a32ed133b770937a?pvs=21) for more information]
* `processed_data`: Handler-processed data dictionary with relevant fields
**Returns**: `StrategyResult` with orders/cancellations or `None`
```python theme={null}
async def _execute_strategy(self, update_type, processed_data):
# Only respond to orderbook updates
if update_type != UpdateType.OnOrderbook:
return None
# Get market data
market_id = processed_data["market_id"]
market = self.markets[market_id]
# Get current prices
bid, ask = market.orderbook.tob()
if not bid or not ask:
self.logger.warning(f"Incomplete orderbook for {market_id}")
return None
# Implement your strategy logic
spread = (ask - bid) / bid
if spread < self.min_spread_threshold:
self.logger.info(f"Spread too narrow: {spread:.2%}")
return None
# Get subaccount for orders
subaccount_id = self.config.get("SubaccountIds", [""])[0]
if not subaccount_id:
return None
# Check current position to avoid exceeding limits
position = self.get_position(subaccount_id, market_id)
current_size = Decimal("0")
if position:
current_size = position.get("quantity", Decimal("0"))
# Determine order parameters
result = StrategyResult()
# Create a new order
if current_size < self.max_position:
buy_order = Order(
market_id=market_id,
subaccount_id=subaccount_id,
order_side=Side.BUY,
price=bid,
quantity=self.order_size,
market_type=market.market_type # Required field as of v0.5.1
)
result.orders.append(buy_order)
self.logger.info(f"Creating BUY order at {bid}: {self.order_size}")
# Cancel existing orders if needed
for order_hash in self.active_orders:
result.cancellations.append({
"market_id": market_id,
"subaccount_id": subaccount_id,
"order_hash": order_hash
})
return result
```
In `_execute_strategy` you can:
* Filter by update type to handle specific events
* Access current market data and account state
* Check existing positions before placing orders
* Implement custom trading logic based on market conditions
* Create new orders and cancel existing ones
* Update position margins for derivative markets
* Log strategy decisions for monitoring and debugging
The framework will handle the execution details like transaction creation, simulation, and broadcasting based on your returned `StrategyResult`.
#### Best Practices
1. **Initialize all parameters in `on_initialize`**
* Get parameters from `self.config`
* Set default values for missing parameters
* Initialize internal state variables
2. **Filter update types**
* Only process update types your strategy cares about
* Always check for required fields in processed\_data
3. **Validate market data**
* Check if bid/ask exists before using
* Verify position exists before making decisions based on it
4. **Respect market constraints**
* Round prices and quantities to market tick sizes
* Check minimum order size and notional requirements
5. **Handle trading account properly**
* Ensure trading account is in AccountAddresses
* Specify correct subaccount\_id for orders
6. **Implement proper logging**
* Log strategy decisions and important events
* Use appropriate log levels (info, warning, error)
7. **Set custom parameters**
* Use the `Parameters` section for strategy-specific values
* Document expected parameters
This guide should give you a solid foundation for creating and configuring effective trading strategies with the framework.
### Custom Handlers
The framework processes updates through specialized handlers before passing the data to your strategy. You can create custom handlers for more control over data processing.
#### Handler Base Class
All handlers inherit from the `UpdateHandler` base class:
```python theme={null}
class UpdateHandler(ABC):
def __init__(self, logger, config, metrics):
self.logger = logger
self.config = config
self.metrics = metrics
async def process(self, **update_data) -> Dict:
"""Process update data and return processed result"""
try:
return await self._process_update(**update_data)
except Exception as e:
self.logger.error(f"Error processing update: {e}")
return None
@abstractmethod
async def _process_update(self, **kwargs) -> Dict:
"""Process specific update type. To be implemented by subclasses."""
pass
```
#### Creating a Custom Handler
To create a custom handler:
1. Inherit from the appropriate handler base class
2. Override the `_process_update` method
3. Register your handler in your strategy's constructor
```python theme={null}
from src.core.handlers import OrderbookHandler
class MyCustomOrderbookHandler(OrderbookHandler):
async def _process_update(self, **update_data):
# Get basic processed data
processed_data = await super()._process_update(**update_data)
if not processed_data:
return None
# Add custom metrics
market = update_data.get("market")
bid, ask = market.orderbook.tob()
if bid and ask:
# Calculate custom metrics
spread = ask - bid
spread_pct = (ask - bid) / bid
# Add to processed data
processed_data["spread"] = spread
processed_data["spread_pct"] = spread_pct
# Record in metrics system
self.metrics.add_custom_metric(f"{market.market_id}_spread", spread_pct)
return processed_data
```
#### Registering Custom Handlers
Register your custom handlers in your strategy constructor:
```python theme={null}
def __init__(self, logger, config):
super().__init__(logger, config)
# Replace default handlers with custom implementations
self.handlers[UpdateType.OnOrderbook] = MyCustomOrderbookHandler(
self.logger, self.config, self.metrics
)
self.handlers[UpdateType.OnPosition] = MyCustomPositionHandler(
self.logger, self.config, self.metrics
)
```
#### Available Handler Types
The framework provides these handler types that you can extend:
| **Handler Class** | **Update Type** | **Purpose** |
| ------------------ | -------------------------- | ---------------------------- |
| `OrderbookHandler` | `UpdateType.OnOrderbook` | Process orderbook updates |
| `OracleHandler` | `UpdateType.OnOraclePrice` | Process oracle price updates |
| `PositionHandler` | `UpdateType.OnPosition` | Process position updates |
| `BalanceHandler` | `UpdateType.OnBankBalance` | Process balance updates |
| `DepositHandler` | `UpdateType.OnDeposit` | Process deposit updates |
| `TradeHandler` | `UpdateType.OnSpotTrade` | Process trade execution |
| `OrderHandler` | `UpdateType.OnSpotOrder` | Process order updates |
## Key Data Structure
### Update Types and Corresponding Data Fields
The framework processes these main event types that your strategy can react to:
| **Update Type** | **Description** | **Key Data Fields** |
| ------------------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UpdateType.OnOrderbook` | Orderbook updates | - market\_id: str - market: Market object with updated orderbook - sequence: int - block\_time: Optional\[str]
|
| `UpdateType.OnOraclePrice` | Oracle price updates | - market\_id: str - symbol: str - price: Decimal - timestamp: int
|
| `UpdateType.OnBankBalance` | Account balance updates | - account\_address: str - account: Account object - balance: BankBalance object
|
| `UpdateType.OnDeposit` | Subaccount deposit updates | - account\_address: str - subaccount\_id: str - account: Account object - deposit: Deposit object
|
| `UpdateType.OnPosition` | Position changes | - market\_id: str - account\_address: str - subaccount\_id: str - account: Account object - position: Position object
|
| `UpdateType.OnSpotTrade` | Spot trade execution | - market\_id: str - subaccount\_id: str - account: Account object - trade: Order object representing the trade - order: Original Order object that was filled
|
| `UpdateType.OnDerivativeTrade` | Derivative trade execution | - market\_id: str - subaccount\_id: str - account: Account object - trade: Order object representing the trade - order: Original Order object that was filled
|
| `UpdateType.OnSpotOrder` | Spot order updates | - market\_id: str - subaccount\_id: str - account: Account object - order: Order object
|
| `UpdateType.OnDerivativeOrder` | Derivative order updates | - market\_id: str - subaccount\_id: str - account: Account object - order: Order object
|
### Strategy Result
When your strategy decides to take action, return a `StrategyResult` object with:
| **Field** | **Description** | **Content** |
| ---------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `orders` | List of new orders to create | `[Order(...), Order(...)]` |
| `cancellations` | List of orders to cancel | `[{"market_id": "0x123...", "subaccount_id": "0x456...", "order_hash": "0x789..."}]` |
| `margin_updates` | List of margin adjustments | `[{"action": "increase", "market_id": "0x123...", "source_subaccount_id": "0x456...", "destination_subaccount_id": "0x456...", "amount": Decimal("50.0")}]` |
| `liquidations` | List of positions to be liquidated due to insufficient margin | `[{"market_id": "0x123...", "subaccount_id": "0x456...", "executor_subaccount_id": "0x789...", "price": price, "quantity": quantity, "margin": margin}]` |
### Market
| **Property** | **Type** | **Description** | **Note** |
| -------------------- | ------------------------------------------------------ | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `market_id` | `str` | Unique market identifier | |
| `market_type` | `MarketType` object | Market type (SPOT, DERIVATIVE or BINARY) | Required as of v0.5.1 |
| `market` | `BinaryOptionMarket \| DerivativeMarket \| SpotMarket` | Market object from `pyinjective` | SpotMarket: [https://api.injective.exchange/#chain-exchange-for-spot-spotmarkets](https://api.injective.exchange/#chain-exchange-for-spot-spotmarkets) DerivativeMarket: [https://api.injective.exchange/#chain-exchange-for-derivatives-derivativemarkets](https://api.injective.exchange/#chain-exchange-for-derivatives-derivativemarkets) BinaryOptionMarket: [https://api.injective.exchange/#chain-exchange-for-binary-options-binaryoptionsmarkets](https://api.injective.exchange/#chain-exchange-for-binary-options-binaryoptionsmarkets)
|
| `orderbook` | `Orderbook` object | Current market orderbook | |
| `oracle_price` | `Decimal` | Current market oracle price | |
| `min_price_tick` | `Decimal` | Minimum price increment | |
| `min_quantity_tick` | `Decimal` | Minimum quantity increment | |
| `base_oracle_price` | `Decimal` | Base token oracle price | Optional |
| `quote_oracle_price` | `Decimal` | Quote token oracle price | Optional |
| `oracle_timestamp` | `int` | Oracle price timestamp | Optional |
| `mark_price` | `Decimal` | Mark price for derivative | Optional |
### Orderbook
| **Property** | **Type** | **Description** | **Note** |
| ------------ | -------------------- | ------------------------------------------ | --------------------------------------- |
| `sequence` | `str` | Orderbook sequence number | |
| `bids` | `List[L2PriceLevel]` | List of bid price levels | `L2PriceLevel` : `price` and `quantity` |
| `asks` | `List[L2PriceLevel]` | List of ask price levels | `L2PriceLevel` : `price` and `quantity` |
| `tob` | `Tuple` | Top of book (bid, ask) prices | |
| `is_health` | `bool` | Indicates if orderbook is in healthy state | `False` when sequence is 0 |
### Account
| **Property** | **Type** | **Description** | **Note** |
| ----------------- | ------------------------ | ---------------------------------- | --------------------------------------- |
| `private_key` | `PrivateKey` | Injective private key | Not directly accessible |
| `public_key` | `PublicKey` | Corresponding public key | |
| `address` | `Address` | Account address object | Contains sequence information |
| `account_address` | `str` | Bech32 address string | Format: "inj1..." |
| `bank_balances` | `Dict[str, BankBalance]` | Token balances in account | Key: denom |
| `balances` | `Dict[str, Balance]` | Alternative balance representation | Key: denom |
| `subaccounts` | `Dict[str, SubAccount]` | Subaccounts owned by this account | Key: subaccount\_id |
| `sequence` | `int` | Transaction sequence number | Property that accesses address.sequence |
### BankBalance
| **Property** | **Type** | **Description** | **Note** |
| ------------ | --------- | ------------------ | ------------------------- |
| `denom` | `str` | Token denomination | e.g., "inj", "peggy0x..." |
| `amount` | `Decimal` | Token amount | Human-readable format |
### Balance
| **Property** | **Type** | **Description** | **Note** |
| ------------ | --------- | ------------------ | -------------------------- |
| `denom` | `str` | Token denomination | e.g., "inj", "peggy0x..." |
| `total` | `Decimal` | Total balance | |
| `available` | `Decimal` | Available balance | Total minus locked amounts |
### SubAccount
| **Property** | **Type** | **Description** | **Note** |
| ------------------- | ----------------------------- | ---------------------------- | ---------------------------------- |
| `subaccount_id` | `str` | Unique subaccount identifier | Format: "0x..." |
| `portfolio` | `Dict[str, Deposit]` | Token deposits in subaccount | Key: denom |
| `positions` | `Dict[str, Position]` | Trading positions | Key: market\_id |
| `open_bid_orders` | `Dict[str, Dict[str, Order]]` | Open buy orders | `market_id -> {order_hash: Order}` |
| `open_ask_orders` | `Dict[str, Dict[str, Order]]` | Open sell orders | `market_id -> {order_hash: Order}` |
| `traded_bid_orders` | `Dict[str, Dict[str, Order]]` | Filled buy orders | `market_id -> {order_hash: Order}` |
| `traded_ask_orders` | `Dict[str, Dict[str, Order]]` | Filled sell orders | `market_id -> {order_hash: Order}` |
### Deposit
| **Property** | **Type** | **Description** | **Note** |
| ------------------- | --------- | ------------------------ | ------------------------------ |
| `denom` | `str` | Token denomination | e.g., "inj", "peggy0x..." |
| `total_balance` | `Decimal` | Total deposit amount | |
| `available_balance` | `Decimal` | Available deposit amount | Total minus margins and locked |
### Position
| **Property** | **Type** | **Description** | **Note** |
| -------------------------- | --------- | ---------------------------- | ---------- |
| `subaccount_id` | `str` | Owner subaccount ID | |
| `market_id` | `str` | Market identifier | |
| `quantity` | `Decimal` | Position size | |
| `entry_price` | `Decimal` | Average entry price | |
| `margin` | `Decimal` | Total margin amount | |
| `cumulative_funding_entry` | `Decimal` | Cumulative funding at entry | |
| `is_long` | `bool` | Long (true) or short (false) | |
| `unrealized_pnl` | `Decimal` | Unrealized profit/loss | Default: 0 |
| `total_volume` | `Decimal` | Total trading volume | Default: 0 |
| `trades_count` | `int` | Number of trades | Default: 0 |
| `mark_price` | `Decimal` | Current market price | Optional |
| `liquidation_price` | `Decimal` | Liquidation threshold | Optional |
| `margin_ratio` | `Decimal` | Current margin ratio | Optional |
### Order
| **Property** | **Type** | **Description** | **Note** |
| ---------------- | ------------- | --------------------------- | ----------------------------- |
| `market_id` | `str` | Market identifier | |
| `subaccount_id` | `str` | Subaccount identifier | |
| `order_side` | `Side` | Buy or sell | Side.BUY or Side.SELL |
| `price` | `Decimal` | Order price | |
| `quantity` | `Decimal` | Order quantity | |
| `order_hash` | `str` | Unique order identifier | Optional, set by chain |
| `fillable` | `Decimal` | Remaining unfilled quantity | Optional |
| `filled` | `Decimal` | Filled quantity | Optional |
| `status` | `OrderStatus` | Order status | BOOKED, PARTIAL\_FILLED, etc. |
| `order_type` | `str` | Order type | Default: "LIMIT" |
| `margin` | `Decimal` | Margin amount (derivatives) | Optional |
| `leverage` | `Decimal` | Leverage multiple | Optional |
| `trigger_price` | `Decimal` | For conditional orders | Optional |
| `market_type` | `MarketType` | SPOT, DERIVATIVE, BINARY | Optional |
| `fee_recipient` | `str` | Fee recipient address | Default: "" |
| `cid` | `str` | Client order ID | Optional |
| `created_at` | `datetime` | Creation timestamp | Optional |
| `updated_at` | `datetime` | Last update timestamp | Optional |
| `position_delta` | `Dict` | Position change data | Optional for derivatives |
| `payout` | `Decimal` | Expected payout | Optional for derivatives |
| `tx_hash` | `str` | Transaction hash | Optional |
| `error_code` | `str` | Error code if failed | Optional |
| `error_message` | `str` | Error details | Optional |
### OrderStatus (Enum)
| **Value** | **Description** |
| ---------------- | ------------------------- |
| `BOOKED` | Order accepted and active |
| `PARTIAL_FILLED` | Partially filled |
| `FILLED` | Completely filled |
| `CANCELLED` | Cancelled by user |
| `EXPIRED` | Expired (time-based) |
### Side (Enum)
| **Value** | **Description** |
| --------- | --------------- |
| `BUY` | Buy order |
| `SELL` | Sell order |
### MarketType (Enum)
| **Value** | **Description** |
| ------------ | --------------------- |
| `SPOT` | Spot market |
| `DERIVATIVE` | Derivatives market |
| `BINARY` | Binary options market |
# Launch a Market
Source: https://docs.injective.network/developers-defi/market-launch
The prerequisite for launching a market is to [launch a token](/developers-defi/token-launch/).
Launching a trading pair on Injective is quick, easy, and best of all, permissionless!
The following tutorial assumes the pair is listed with an ERC-20 token bridged from Ethereum as the base asset, paired with INJ as the quote asset.
For an Injective-native token, skip the bridging portion and head straight to step 6.
1. Navigate to the [Injective Bridge](http://bridge.injective.network/) to begin the process of bridging your chosen ERC-20 token from Ethereum to Injective using the Peggy bridge.
2. Click the dropdown, scroll to the bottom, and click "add" next to the advanced tool to add a custom ERC-20 token using the token address, which you may want to verify on a trusted source like CoinGecko.
3. Copy and paste the correct contract address, and click "add."
4. Now enter the desired amount of the ERC-20 token you wish to bridge, click "approve," confirm the transaction, then click "review," confirm the transaction, and wait.
   
5. Once the approve spend and deposit transactions are confirmed on the Ethereum blockchain, you will see the progress of the bridging transaction. Once the transaction is confirmed on Injective, your bridged ERC-20 token will be available in your Injective wallet. (Note, if you used MetaMask with the source chain, by default your bridged tokens will be sent to the inj address linked to your MetaMask. This can be changed by clicking the lock icon next to the recipient address at the beginning of step 4.)
6. After the bridging transaction is complete, you're able to list the token permissionlessly on Injective by navigating to the [Injective Hub](https://injhub.com/proposal/create/).
7. Choose "instant spot market launch" from the first dropdown, and specify a ticker. In this example, let's use PEPE/INJ. Now pick the base token from the dropdown. However, beware, several tokens might exist under the same ticker. Always match the correct token address. In this case, as the token was bridged using the Peggy bridge, the address will be peggy followed by the ERC-20 contract address.
8. Now select the correct quote denom, in this case, inj. (Note, if you wish to pair the token with USDT, make sure to select the "correct" USDT address, which is peggy followed by the ERC-20 contract address for USDT.) Finally, specify a minimum price tick size and minimum quantity tick size. Because PEPE/INJ would trade at a fraction of a penny, the minimum ticks are set accordingly.
# Min Quantity Tick Size
Source: https://docs.injective.network/developers-defi/min-quantity-tick-size
## Minimum Market Quantity Tick Size
The minimum market quantity tick size dictates the smallest increment by which an order quantity can increase or decrease. For instance, if a market has a minimum quantity tick size of **0.001**, an order submitted with a quantity of **0.0011** would be rejected because it does not align with the allowed increments.
**Note:** Derivative markets share the same format for `minQuantityTickSize` between the user interface and the chain, so no formatting is required.
### Spot Market
#### Conversion from Human-Readable Format to Chain Format
Using the INJ/USDT market as an example, which has **18 base decimals** and **6 quote decimals**, the conversion to chain format is as follows:
$\text{chainFormat} = \text{value} \times 10^{\text{baseDecimals}}$
#### Conversion from Chain Format to Human-Readable Format
To convert back to a human-readable format:
$\text{humanReadableFormat} = \text{value} \times 10^{-\text{baseDecimals}}$
Also, be sure to check out our [concepts](../developers/concepts/calculation-min-price-tick-size/).
# Provider Oracle
Source: https://docs.injective.network/developers-defi/provider-oracle
Prerequisite reading [Injective Oracle Module](/developers-native/injective/oracle/)
The goal of this section is to provide users a guide on how to launch and maintain an oracle provider on Injective. These oracles can be used for various purposes, like Perpetual Markets, Expiry Futures Markets, [Binary Options markets](/developers-native/injective/exchange/02_binary_options_markets/), etc.
First, what is an oracle provider? It's an oracle **TYPE** that allows external parties to relay price feeds to the Injective chain. These external parties are called providers. A provider identifies each external party, and all the price feeds provided on the chain are stored under that particular provider. This allows custom price feeds to be created on Injective, which can power creative and advanced markets like [Binary Options markets](/developers-native/injective/exchange/02_binary_options_markets/) being launched on Injective.
The first thing developers need to do is register their provider under the Oracle Provider type. You can do that by submitting a `GrantProviderPrivilegeProposal` governance proposal. Once the proposal passes, your provider will be registered, and you can relay price feeds. You can do it in a CLI environment using `injectived` (`grant-provider-privilege-proposal [providerName] [relayers] --title [title] --description [desc] [flags]`) or using any of our SDKs to create the message and broadcast it to the chain.
You can see an example on how to submit this proposal in the Oracle Module Proposals Section
*Note: the `relayers` of the `GrantProviderPrivilegeProposal` are addresses that will be whitelisted to submit the price feeds to Injective.*
Once the proposal passes, the `relayers` can use the `MsgRelayProviderPrices` to submit prices for a base/quote pair within their provider namespace of the Oracle Provider Type oracle on Injective. You can do it in a CLI environment using `injectived` (`relay-provider-prices [providerName] [symbol:prices] [flags]`) or using any of our SDKs to create the message and broadcast it to the chain.
Finally, you can use these price feeds to create your Derivative Markets.
# Testnet Faucet Integration
Source: https://docs.injective.network/developers-defi/testnet-faucet-integration
If you want to have a testnet faucet integration within your dApp, the only thing you need to do is do a `POST` request to `` `https://jsbqfdd4yk.execute-api.us-east-1.amazonaws.com/v1/faucet` `` and pass an `{ address: inj1...}` as the body of the `POST` request. The address is then stored within the queue ,which is processed every 5 to 10 minutes.
Here is an example code snippet:\\
```typescript theme={null}
import { HttpClient } from "@injectivelabs/utils"
const LAMBDA_API = "https://jsbqfdd4yk.execute-api.us-east-1.amazonaws.com/v1"
const client = new HttpClient(LAMBDA_API);
client
.post("faucet", { address: "inj1...." })
.then((response: any) => {
alert("success, your address is in the queue");
})
.catch((e: any) => {
alert("Something happened - " + e.message);
})
.finally(() => {
//
});
```
# Launch a Token
Source: https://docs.injective.network/developers-defi/token-launch
Within this document, we'll explain how to launch a token on Injective.
There are two options for launching a token on Injective: bridging an existing token or creating a new token.
## Bridging
The easiest way to launch a token on Injective is by bridging your existing assets from one of the supported networks that Injective is interoperable with. There are guides in the [bridge](/defi/bridge/ "mention") sections that you can reference to bridge assets from other networks to Injective.
Once the bridging process is completed, a token will be created on Injective, which you can then use to [launch a market](/developers-defi/market-launch/ "mention").
## Creating a New Token
You can also create a new token on Injective using the `TokenFactory` module. There are multiple ways on how to achieve this.
### Using Injective Hub
The [Injective Hub](https://injhub.com/token-factory/) web app provides you the ability to create and manage tokens seamlessly, creating a market on Injective's [native orderbook](/developers-native/injective/exchange), etc.
### Using TokenStation[](/developers-defi/token-launch)
The [TokenStation](https://www.tokenstation.app/) web app provides you the ability to create and manage tokens seamlessly, creating a market on Injective's [native orderbook](/developers-native/injective/exchange/), launching an airdrop, and much more.
### Using DojoSwap[](/developers-defi/token-launch/#4-via-dojoswap)
Similar to above, you can utilize [DojoSwap's Market Creation module](https://docs.dojo.trading/introduction/market-creation) to create, manage, and list your token, along with several other useful features.
### Programmatically
#### Using TypeScript
Learn more about [launching a token](/developers/assets/token-create).
#### Using Injective CLI
You have to have `injectived` installed locally before proceeding with this tutorial. You can learn more about it on the [injectived](/developers/injectived/ "mention")page.
Once you have `injectived` installed and a key added, you can use the CLI to launch your token:
1. **Create a `TokenFactory` denom**
The fee for creating a factory denom is `0.1 INJ`.
```bash theme={null}
injectived tx tokenfactory create-denom [subdenom] [name] [symbol] [decimals] --from=YOUR_KEY --chain-id=injective-888 --node=https://testnet.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
```
Tokens are namespaced by the creator address to be permissionless and avoid name collision. In the example above, the subdenom is `ak` but the denom naming will be `factory/{creator address}/{subdenom}`.
2. **Submit token metadata**
To get your token visible on Injective dApps, you have to submit its metadata.
```bash theme={null}
injectived tx tokenfactory set-denom-metadata "My Token Description" 'factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak' AKK AKCoin AK '' '' '[
{"denom":"factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak","exponent":0,"aliases":[]},
{"denom":"AKK","exponent":6,"aliases":[]}
]' 6 --from=YOUR_KEY --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
```
This command expects the following arguments:
```bash theme={null}
injectived tx tokenfactory set-denom-metadata [description] [base] [display] [name] [symbol] [uri] [uri-hash] [denom-unit (json)] [decimals]
```
3. **Mint tokens**
Once you have created your token and submitted the token metadata, it's time to mint your tokens.
```bash theme={null}
injectived tx tokenfactory mint 1000000factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
```
This command will mint 1 token, assuming your token has 6 decimals.
4. **Burn tokens**
The admin of the token, can also burn the tokens.
```bash theme={null}
injectived tx tokenfactory burn 1000000factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
```
5. **Change admin**
It's recommended once you have minted the initial supply to change admin to the `null` address to make sure that the supply of the token cannot be manipulated. Once again, the admin of the token can mint and burn supply anytime. The `NEW_ADDRESS`, as explained above in most of the cases should be set to `inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49`.
```bash theme={null}
injectived tx tokenfactory change-admin factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak NEW_ADDRESS --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
```
The examples above are for testnet. If you want to run them on mainnet, do the following changes:
`injective-888` > `injective-1`
`https://testnet.sentry.tm.injective.network:443` > `http://sentry.tm.injective.network:443`
#### Using Cosmwasm
To create and manage a bank token programmatically via a smart contract, one can use the following messages found in the [`injective-cosmwasm`](https://github.com/InjectiveLabs/cw-injective/blob/6b2d549ff99912b9b16dbf91a06c83db99b5dace/packages/injective-cosmwasm/src/msg.rs#L399-L434) package:
\
`create_new_denom_msg`
```rust theme={null}
pub fn create_new_denom_msg(sender: String, subdenom: String) -> CosmosMsg {
InjectiveMsgWrapper {
route: InjectiveRoute::Tokenfactory,
msg_data: InjectiveMsg::CreateDenom { sender, subdenom },
}
.into()
}
```
Purpose: Creates a message to create a new token denomination using the tokenfactory module.
Parameters:
* `sender`: The address of the account initiating the creation.
* `subdenom`: The sub-denomination identifier for the new token.
Returns: A `CosmosMsg` wrapped in an `InjectiveMsgWrapper`, ready to be sent to the Injective blockchain.
Example:
```rust theme={null}
let new_denom_message = create_new_denom_msg(
env.contract.address, // Sender's address
"mytoken".to_string(), // Sub-denomination identifier
);
```
#### `create_set_token_metadata_msg`
```rust theme={null}
pub fn create_set_token_metadata_msg(denom: String, name: String, symbol: String, decimals: u8) -> CosmosMsg {
InjectiveMsgWrapper {
route: InjectiveRoute::Tokenfactory,
msg_data: InjectiveMsg::SetTokenMetadata {
denom,
name,
symbol,
decimals,
},
}
.into()
}
```
Purpose: Creates a message to set or update metadata for a token.
Parameters:
* `denom`: The denomination identifier of the token.
* `name`: The full name of the token.
* `symbol`: The symbol of the token.
* `decimals`: The number of decimal places the token uses.
Returns: A `CosmosMsg` wrapped in an `InjectiveMsgWrapper`, ready to be sent to the Injective blockchain.
Example:
```rust theme={null}
let metadata_message = create_set_token_metadata_msg(
"mytoken".to_string(), // Denomination identifier
"My Custom Token".to_string(), // Full name
"MYT".to_string(), // Symbol
18, // Number of decimals
);
```
#### `create_mint_tokens_msg`
```rust theme={null}
pub fn create_mint_tokens_msg(sender: Addr, amount: Coin, mint_to: String) -> CosmosMsg {
InjectiveMsgWrapper {
route: InjectiveRoute::Tokenfactory,
msg_data: InjectiveMsg::Mint { sender, amount, mint_to },
}
.into()
}
```
Purpose: Creates a message to mint new tokens. The token must be a tokenfactory token and the sender must be the token admin.
Parameters:
* `sender`: The address of the account initiating the mint operation.
* `amount`: The amount of tokens to mint.
* `mint_to`: The recipient address where the newly minted tokens should be sent.
Returns: A `CosmosMsg` wrapped in an `InjectiveMsgWrapper`, ready to be sent to the Injective blockchain.
Example:
```rust theme={null}
let mint_message = create_mint_tokens_msg(
env.contract.address, // Sender's address
Coin::new(1000, "factory//mytoken"), // Amount to mint
"inj1...".to_string(), // Recipient's address
);
```
#### `create_burn_tokens_msg`
```rust theme={null}
pub fn create_burn_tokens_msg(sender: Addr, amount: Coin) -> CosmosMsg {
InjectiveMsgWrapper {
route: InjectiveRoute::Tokenfactory,
msg_data: InjectiveMsg::Burn { sender, amount },
}
.into()
}
```
Purpose: Creates a message to burn tokens. The token must be a tokenfactory token and the sender must be the token admin.
Parameters:
* `sender`: The address of the account initiating the burn operation.
* `amount`: The amount of tokens to burn.
Returns: A `CosmosMsg` wrapped in an `InjectiveMsgWrapper`, ready to be sent to the Injective blockchain.
Example:
```rust theme={null}
let burn_message = create_burn_tokens_msg(
env.contract.address, // Sender's address
Coin::new(500, "factory//mytoken"), // Amount to burn
);
```
# 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.');
}
}
}
```
# 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}
) : (
wcConnector && connect({ connector: wcConnector })}
disabled={isPending || !wcConnector}
style={{ padding: '12px 24px', fontSize: '16px' }}
>
Connect Wallet (WalletConnect)
)}
)
}
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
Connect your wallet with MetaMask or WalletConnect
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 uisng 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 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
Welcome, EVM developers! Native EVM support on Injective lets you deploy Solidity smart contracts. It also opens up possibilities such as interacting with the exchange module, building dApps, and much more. You can now build in Injective with familiar tools, libraries, and workflows!
## Start Building on EVM
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 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. QuickNode Build on Injective with the fastest RPC Nodes, a comprehensive library of web3 APIs, and the most performant blockchain ETL tools.
### **Data Indexing & Querying**
The Graph
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.
### Block Explorers
# 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 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**
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.
### **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.
# EVM Network Information
Source: https://docs.injective.network/developers-evm/network-information
Essential information about the Injective EVM network
## Network Info
* Chain ID: `1776`
* JSON-RPC Endpoint: `https://sentry.evm-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
* **USDT** USDT (MTS) - [`0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13`](https://blockscout.injective.network/address/0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13)
* **wETH** wrapped ETH (MTS) - [`0x83A15000b753AC0EeE06D2Cb41a69e76D0D5c7F7`](https://blockscout.injective.network/address/0x83A15000b753AC0EeE06D2Cb41a69e76D0D5c7F7)
* **wINJ** wrapped INJ (MTS) - [`0x0000000088827d2d103ee2d9A6b781773AE03FfB`](https://blockscout.injective.network/address/0x0000000088827d2d103ee2d9A6b781773AE03FfB)
* **USDC** USDC (MTS) - [`0x2a25fbD67b3aE485e461fe55d9DbeF302B7D3989`](https://blockscout.injective.network/address/0x2a25fbD67b3aE485e461fe55d9DbeF302B7D3989)
* **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
* 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/`
* WS Endpoint: `https://k8s.testnet.ws.injective.network/`
* Faucet: [`testnet.faucet.injective.network/`](https://testnet.faucet.injective.network/)
* 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)
* **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
* 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)
# Precompiles
Source: https://docs.injective.network/developers-evm/precompiles
### 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? (Bridging 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.
Jumpstart 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 becuase 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
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
# Overview
Source: https://docs.injective.network/developers-native/index
## What are Modules?
Modules are foundational components in Injective’s blockchain architecture, each built to provide specific functionalities. A module is essentially a self-contained unit with well-defined logic and services, allowing Injective to efficiently manage diverse operations across the network.
Modules work like building blocks that can be combined to expand the blockchain’s capabilities. This approach reduces the friction and technical complexity of implementing core features, making development easier. Each module operates independently but communicates with others through inter-module messaging, making Injective’s architecture modular and adaptable. This allows Injective to continuously evolve, delivering targeted upgrades and functionality without overhauling the entire system.
## Explore Modules
`injective` module
`core` module
# 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
}
```
# 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
# 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
}
```
# Archival setup
Source: https://docs.injective.network/infra/archival-setup
This guide will walk you through the process of creating a fleet of nodes that serve archival data and how to stitch them together using a gateway
## Architecture
To make serving archival data more accessible we split the data into smaller segments. These segments are stored in `s3://injective-snapshots/mainnet/subnode`
| 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 |
| `/119143` | 119M – 143M | v1.17.0 | 16 TiB |
| `/138150` | 138M – 150M | v1.17.2 | 5.8 TiB |
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}
aws s3 cp --recursive s3://injective-snapshots/mainnet/subnode/ $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
#### 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
```
# Premium Endpoints
Source: https://docs.injective.network/infra/premium-endpoints
As a developer, you may be interested in having a dedicated node or indexing solutions powering your dApp.
Here is a list of external providers offering private Injective infrastructure services. For more information, click on their websites below:
* [QuickNode](https://www.quicknode.com/chains/inj)
* [CryptoCrew](https://forms.gle/Aa2XYBB4sLxzHNzc6)
* [Imperator](https://www.imperator.co/products/dedicated-and-rpc-nodes#get-started)
* [AllNodes](https://injective.publicnode.com/)
* [NowNodes](https://nownodes.io/injective-inj)
* [RockX](https://access.rockx.com/product/injective-blockchain-api-for-web3-builders)
| Provider | Chain | Indexer |
| ---------- | ----- | ------- |
| QuickNode | \[x] | \[ ] |
| CryptoCrew | \[x] | \[x] |
| Imperator | \[x] | \[x] |
| AllNodes | \[x] | \[x] |
| NowNodes | \[x] | \[ ] |
| RockX | \[x] | \[ ] |
# Public Endpoints
Source: https://docs.injective.network/infra/public-endpoints
We **do not recommend** using these 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.
## Mainnet
| Service | Address |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Chain Swagger | [https://sentry.lcd.injective.network/swagger/#/](https://sentry.lcd.injective.network/swagger/#/) |
| Chain LCD | [https://sentry.lcd.injective.network:443](https://sentry.lcd.injective.network:443) |
| Chain RPC | [https://sentry.tm.injective.network:443](https://sentry.tm.injective.network:443) |
| Chain WebSocket | wss\://sentry.tm.injective.network:443/websocket |
| Chain Streamer | sentry.chain.stream.injective.network:443 |
| Chain gRPC | sentry.chain.grpc.injective.network:443 |
| Chain gRPC-Web | [https://sentry.chain.grpc-web.injective.network:443](https://sentry.chain.grpc-web.injective.network:443) |
| Indexer Swagger | [https://sentry.exchange.grpc-web.injective.network/swagger/#/](https://sentry.exchange.grpc-web.injective.network/swagger/#/) |
| Indexer gRPC | sentry.exchange.grpc.injective.network:443 |
| Indexer gRPC-Web | [https://sentry.exchange.grpc-web.injective.network:443](https://sentry.exchange.grpc-web.injective.network:443) |
| Indexer EventProvider | k8s.global.mainnet.events.grpc.injective.network:443 |
| Hub | [https://injhub.com/](https://injhub.com/) |
| Explorer | [https://explorer.injective.network/](https://explorer.injective.network/) |
| Faucet | [https://inj.supply/](https://inj.supply/) |
| Status | [https://uptime.com/statuspage/status.injective.network](https://uptime.com/statuspage/status.injective.network) |
## Testnet
| Service | Address |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Chain Swagger | [https://testnet.sentry.lcd.injective.network/swagger/](https://testnet.sentry.lcd.injective.network/swagger/) |
| Chain RPC | [https://testnet.sentry.tm.injective.network:443](https://testnet.sentry.tm.injective.network:443) |
| Chain LCD | [https://testnet.sentry.lcd.injective.network:443](https://testnet.sentry.lcd.injective.network:443) |
| Chain WebSocket | wss\://testnet.sentry.tm.injective.network:443/websocket |
| Chain Streamer | testnet.sentry.chain.stream.injective.network:443 |
| Chain gRPC | testnet.sentry.chain.grpc.injective.network:443 |
| Chain gRPC-Web | [https://testnet.sentry.chain.grpc-web.injective.network:443](https://testnet.sentry.chain.grpc-web.injective.network:443) |
| Indexer Swagger | [https://testnet.sentry.exchange.grpc-web.injective.network/swagger/#/](https://testnet.sentry.exchange.grpc-web.injective.network/swagger/#/) |
| Indexer gRPC | testnet.sentry.exchange.grpc.injective.network:443 |
| Indexer gRPC-Web | [https://testnet.sentry.exchange.grpc-web.injective.network:443](https://testnet.sentry.exchange.grpc-web.injective.network:443) |
| Indexer EventProvider | k8s.global.testnet.events.grpc.injective.network:443 |
| Hub | [https://testnet.hub.injective.network/](https://testnet.hub.injective.network/) |
| Explorer | [https://testnet.explorer.injective.network/](https://testnet.explorer.injective.network/) |
| Faucet | [https://testnet.faucet.injective.network/](https://testnet.faucet.injective.network/) |
| Status | [https://uptime.com/statuspage/testnet.status.injective.network](https://uptime.com/statuspage/testnet.status.injective.network) |
# Canonical Chain Upgrades
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.
# 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
```
# Testnet
Source: https://docs.injective.network/infra/validator-testnet/index
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
# Indexer Service Setup Guide
Source: https://docs.injective.network/redirects/https_injective_notion_site_indexer_service_setup_guide
# EVM Testnet Explorer
Source: https://docs.injective.network/redirects/https_testnet_blockscout_injective_network_blocks
# EVM Testnet Faucet
Source: https://docs.injective.network/redirects/https_testnet_faucet_injective_network
# 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);
```
# State
Source: https://docs.injective.network/developers-native/injective/auction/01_state
# State
## Params
Params is a module-wide configuration structure that stores system parameters and defines overall functioning of the auction module.
* Params: `Paramsspace("auction") -> legacy_amino(params)`
```go theme={null}
type Params struct {
// auction_period_duration defines the auction period duration
AuctionPeriod int64
// min_next_bid_increment_rate defines the minimum increment rate for new bids
MinNextBidIncrementRate math.LegacyDec
}
```
### **LastBid**
Keeps track of the current highest bid
* LastBid: `0x01 -> ProtocolBuffer(Bid)`
```go theme={null}
type Bid struct {
Bidder string
Amount sdk.Coin
}
```
### **AuctionRound**
The current auction round.
* AuctionRound: `0x03 -> BigEndian(AuctionRound)`
### **EndingTimeStamp**
This value is compared against current block time to decide an auction round settlement. When the exported chain is imported again, the EndingTimeStamp will be updated to the next value in future.
* `EndingTimeStamp`: `0x04 -> BigEndian(EndingTimestamp)`
### **LastAuctionResult**
Keeps track of the last auction result.
* LastAuctionResult: `0x05 -> ProtocolBuffer(LastAuctionResult)`
```go theme={null}
type LastAuctionResult struct {
Winner string
Amount sdk.Coin
Round uint64
}
```
# Messages
Source: https://docs.injective.network/developers-native/injective/auction/02_messages
# Messages
In this section we describe the processing of the auction messages and the corresponding updates to the state.
## Msg/Bid
An auction basket from a given round is bid upon by using the `Msg/Bid` service message.
```protobuf theme={null}
// Bid defines a SDK message for placing a bid for an auction
message MsgBid {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
// amount of the bid in INJ tokens
cosmos.base.v1beta1.Coin bid_amount = 2 [(gogoproto.nullable) = false];
// the current auction round being bid on
uint64 round = 3;
}
```
This service message is expected to fail if:
* `Round` does not equal the current auction round
* `BidAmount` does not exceed the previous highest bid amount by at least `min_next_increment_rate` percent.
This service message transfers the `BidAmount` of INJ from the `Sender` to the auction module, stores the bid, and refunds the last bidder's bid amount.
# End-Block
Source: https://docs.injective.network/developers-native/injective/auction/03_end_block
# End-Block
### Auction Settlement
The settlement of a given auction round occurs when `blockTime ≥ EndingTimeStamp.` If a non-zero INJ bid was placed during this period (i.e. there exists a `LastBid`), the following procedure will take place:
* The winning INJ bid amount is burned.
* The basket of coins held by the auction module is transferred to the winning bidder.
* `LastAuctionResult` is written to state and `EventAuctionResult` is emitted.
* The `LastBid` is cleared.
* The AuctionRound is incremented by 1 and the EndingTimestamp is incremented by `AuctionPeriod`.
* The accumulated exchange fees are transferred from the `exchange` module to the `auction` module for the new upcoming auction.
If the round closed without any successful bids, the existing coin basket will be rolled over into the next auction and combined with the new accumulated fee basket.
# Events
Source: https://docs.injective.network/developers-native/injective/auction/04_events
# Events
The auction module emits the following events:
## Handlers
### Msg/Bid
| Type | Attribute Key | Attribute Value |
| -------- | ------------- | --------------- |
| EventBid | Bidder | |
| EventBid | Amount | |
| EventBid | Round | |
## EndBlocker
| Type | Attribute Key | Attribute Value |
| ------------------ | ------------- | --------------- |
| EventAuctionResult | Winner | |
| EventAuctionResult | Amount | |
| EventAuctionResult | Round | |
# Parameters
Source: https://docs.injective.network/developers-native/injective/auction/05_params
# Parameters
The auction module contains the following parameters:
| Key | Type | Example |
| ----------------------- | -------------- | -------- |
| AuctionPeriod | int64 | 604800 |
| MinNextBidIncrementRate | math.LegacyDec | "0.0025" |
# Error Codes
Source: https://docs.injective.network/developers-native/injective/auction/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------- | ---------- | ----------------- |
| auction | 1 | invalid bid denom |
| auction | 2 | invalid bid round |
# Auction
Source: https://docs.injective.network/developers-native/injective/auction/index
## Abstract
The `auction` module periodically obtains a basket of tokens accumulated from trading fees from the `exchange` module and auctions the basket to the highest bidder in an open English auction for INJ. The winner of this auction receives the basket of tokens and the winning INJ bid amount from this auction is burned.
## Contents
1. [State](/developers-native/injective/auction/01_state)
2. [Messages](/developers-native/injective/auction/02_messages)
3. [End Block](/developers-native/injective/auction/03_end_block)
4. [Events](/developers-native/injective/auction/04_events)
5. [Params](/developers-native/injective/auction/05_params)
# Concepts
Source: https://docs.injective.network/developers-native/injective/erc20/01_concepts
# Concepts
The `ERC20` module is designed to introduce existing bank denoms (such as IBC-bridged tokens, USDC, tokenfactory, and peggy) into the Injective EVM. It achieves this by maintaining an association between token pairs in its storage. When creating a new token pair for an existing bank denom, the module uploads a fresh ERC20 contract that utilizes our bank precompile. This precompile then accesses the module's storage to locate the mapping between the newly created ERC20 address and the existing bank denom. We require this separate module for several reasons:
1. storage: store mapping between bank denom ↔ erc20 address
2. new Msg type: allow users to create new token pairs in the mappings, which is done by issuing a chain Msg
# State
Source: https://docs.injective.network/developers-native/injective/erc20/02_state
# State
The `erc20` module keeps state of the following primary objects:
## ERC20 Tokens by Bank denoms
* 0x02 + bank\_denom ⇒ erc20\_address
## Bank Denoms by ERC20 Token
* 0x03 + erc20\_address ⇒ bank\_denom
# null
Source: https://docs.injective.network/developers-native/injective/erc20/03_messages
# Messages
In this section we describe the processing of the `erc20` module messages and the corresponding updates to the state.
### Create Token Pair: `MsgCreateTokenPair`
Creates an association between existing bank denom and new or existing ERC20 smart contract.
If ERC20 address is empty, new ERC20 smart contract will be instantiated. Not all bank denoms are supported.
Validation rules:
* for tokenfactory denoms, only denom admin can create token pair
* for peggy and IBC denoms, anyone can create token pair (only with erc20 address being empty)
```go theme={null}
type MsgCreateTokenPair struct {
Sender string
TokenPair TokenPair
}
type TokenPair struct {
BankDenom string
Erc20Address string
}
```
**State Modifications:**
* Validation checks:
* Sender has permissions to create token pair for this denom (for tokenfactory denom it must be a denom admin)
* Provided bank denom exists and has non-zero supply
* If ERC20 address is provided:
* check that contract exists and is, in fact, an ERC-2o smart contract (by invoking `symbol()` method on it)
* check that existing contract does not have associated bank denom already with circulating supply
* Create the association depending on the bank denom type:
* tokenfactory denom:
* if no ERC-20 address is provided, instantiate new `MintBurnBankERC20` smart contract, otherwise use provided address
* store the association
* IBC and peggy denoms:
* instantiate new `FixedSupplyBankERC20` smart contract
* store the association
### Delete Token Pair: `MsgDeleteTokenPair`
Only authority can remove token pairs for now, by providing bank denom of the pair.
```go theme={null}
type MsgDeleteTokenPair struct {
Sender string
BankDenom string
}
```
# Events
Source: https://docs.injective.network/developers-native/injective/erc20/04_events
# Events
The erc20 module emits the following events:
```protobuf theme={null}
message EventCreateTokenPair {
string bank_denom = 1;
string erc20_address = 2;
}
message EventDeleteTokenPair {
string bank_denom = 1;
}
```
# ERC20
Source: https://docs.injective.network/developers-native/injective/erc20/index
## Abstract
The erc20 module allows for the associations of existing bank denoms with ERC-20 tokens.
## Contents
1. **[Concepts](/developers-native/injective/erc20/01_concepts)**
2. **[State](/developers-native/injective/erc20/02_state)**
3. **[Messages](/developers-native/injective/erc20/03_messages)**
4. **[Events](/developers-native/injective/erc20/04_events)**
# Concepts
Source: https://docs.injective.network/developers-native/injective/evm/01_concepts
## EVM
The Ethereum Virtual Machine (EVM) is a computation engine which can be thought of as one single entity maintained by thousands of connected computers (nodes) running an Ethereum client. As a virtual machine ([VM](https://en.wikipedia.org/wiki/Virtual_machine)), the EVM is responisble for computing changes to the state deterministically regardless of its environment (hardware and OS). This means that every node has to get the exact same result given an identical starting state and transaction (tx).
The EVM is considered to be the part of the Ethereum protocol that handles the deployment and execution of [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/). To make a clear distinction:
* The Ethereum protocol describes a blockchain, in which all Ethereum accounts and smart contracts live. It has only one canonical state (a data structure, which keeps all accounts) at any given block in the chain.
* The EVM, however, is the [state machine](https://en.wikipedia.org/wiki/Finite-state_machine) that defines the rules for computing a new valid state from block to block. It is an isolated runtime, which means that code running inside the EVM has no access to network, filesystem, or other processes (not external APIs).
The `x/evm` module implements the EVM as a Cosmos SDK module. It allows users to interact with the EVM by submitting Ethereum txs and executing their containing messages on the given state to evoke a state transition.
### State
The Ethereum state is a data structure, implemented as a [Merkle Patricia Trie](https://en.wikipedia.org/wiki/Merkle_tree), that keeps all accounts on the chain. The EVM makes changes to this data structure resulting in a new state with a different State Root. Ethereum can therefore be seen as a state chain that transitions from one state to another by executing transations in a block using the EVM. A new block of txs can be described through its Block header (parent hash, block number, time stamp, nonce, receipts,...).
### Accounts
There are two types of accounts that can be stored in state at a given address:
* **Externally Owned Account (EOA)**: Has nonce (tx counter) and balance
* **Smart Contract**: Has nonce, balance, (immutable) code hash, storage root (another Merkle Patricia Trie)
Smart contracts are just like regular accounts on the blockchain, which additionally store executable code in an Ethereum-specific binary format, known as **EVM bytecode**. They are typically written in an Ethereum high level language such as Solidity which is compiled down to EVM bytecode and deployed on the blockchain, by submitting a tx using an Ethereum client.
### Architecture
The EVM operates as a stack-based machine. It's main architecture components consist of:
* Virtual ROM: contract code is pulled into this read only memory when processing txs
* Machine state (volatile): changes as the EVM runs and is wiped clean after processing each tx
* Program counter (PC)
* Gas: keeps track of how much gas is used
* Stack and Memory: compute state changes
* Access to account storage (persistent)
### State Transitions with Smart Contracts
Typically smart contracts expose a public ABI, which is a list of supported ways a user can interact with a contract. To interact with a contract and invoke a state transition, a user will submit a tx carrying any amount of gas and a data payload formatted according to the ABI, specifying the type of interaction and any additional parameters. When the tx is received, the EVM executes the smart contracts's EVM bytecode using the tx payload.
### Executing EVM bytecode
A contract's EVM bytecode consists of basic operations (add, multiply, store, etc...), called **Opcodes**. Each Opcode execution requires gas that needs to be payed with the tx. The EVM is therefore considered quasi-turing complete, as it allows any arbitrary computation, but the amount of computations during a contract execution is limited to the amount of gas provided in the tx. Each Opcode's [**gas cost**](https://www.evm.codes/) reflects the cost of running these operations on actual computer hardware (e.g. `ADD = 3gas` and `SSTORE = 100gas`). To calculate the gas consumption of a tx, the gas cost is multiplied by the **gas price**, which can change depending on the demand of the network at the time. If the network is under heavy load, you might have to pay a highter gas price to get your tx executed. If the gas limit is hit (out of gas execption) no changes to the Ethereum state are applied, except that the sender's nonce increments and their balance goes down to pay for wasting the EVM's time.
Smart contracts can also call other smart contracts. Each call to a new contract creates a new instance of the EVM (including a new stack and memory). Each call passes the sandbox state to the next EVM. If the gas runs out, all state changes are discareded. Otherwise they are kept.
For further reading, please refer to:
* [EVM](https://eth.wiki/concepts/evm/evm)
* [EVM Architecture](https://cypherpunks-core.github.io/ethereumbook/13evm.html#evm_architecture)
* [What is Ethereum](https://ethdocs.org/en/latest/introduction/what-is-ethereum.html#what-is-ethereum)
* [Opcodes](https://www.ethervm.io/)
## Ethermint as Geth implementation
Ethermint is an implementation of the [Etherum protocal in Golang](https://geth.ethereum.org/docs/getting-started) (Geth) as a Cosmos SDK module. Geth includes an implementation of the EVM to compute state transitions. Have a look at the [go-etheruem source code](https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go) to see how the EVM opcodes are implemented. Just as Geth can be run as an Ethereum node, Ethermint can be run as a node to compute state transitions with the EVM. Ethermint supports Geth's standard [Ethereum JSON-RPC APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/) in order to be Web3 and EVM compatible.
### JSON-RPC
JSON-RPC is a stateless, lightweight remote procedure call (RPC) protocol. Primarily this specification defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over HTTP, or in many various message passing environments. It uses JSON (RFC 4627) as a data format.
#### JSON-RPC Example: `eth_call`
The JSON-RPC method [`eth_call`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call) allows you to execute messages against contracts. Usually, you need to send a transaction to a Geth node to include it in the mempool, then nodes gossip between each other and eventually the transaction is included in a block and gets executed. `eth_call` however lets you send data to a contract and see what happens without commiting a transaction.
In the Geth implementation, calling the endpoint roughly goes through the following steps:
1. The `eth_call` request is transformed to call the `func (s *PublicBlockchainAPI) Call()` function using the `eth` namespace
2. [`Call()`](https://github.com/ethereum/go-ethereum/blob/master/internal/ethapi/api.go#L982) is given the transaction arguments, the block to call against and optional overides that modify the state to call against. It then calls `DoCall()`
3. [`DoCall()`](https://github.com/ethereum/go-ethereum/blob/d575a2d3bc76dfbdefdd68b6cffff115542faf75/internal/ethapi/api.go#L891) transforms the arguments into a `ethtypes.message`, instantiates an EVM and applies the message with `core.ApplyMessage`
4. [`ApplyMessage()`](https://github.com/ethereum/go-ethereum/blob/d575a2d3bc76dfbdefdd68b6cffff115542faf75/core/state_transition.go#L180) calls the state transition `TransitionDb()`
5. [`TransitionDb()`](https://github.com/ethereum/go-ethereum/blob/d575a2d3bc76dfbdefdd68b6cffff115542faf75/core/state_transition.go#L275) either `Create()`s a new contract or `Call()`s a contract
6. [`evm.Call()`](https://github.com/ethereum/go-ethereum/blob/d575a2d3bc76dfbdefdd68b6cffff115542faf75/core/vm/evm.go#L168) runs the interpreter `evm.interpreter.Run()` to execute the message. If the execution fails, the state is reverted to a snapshot taken before the execution and gas is consumed.
7. [`Run()`](https://github.com/ethereum/go-ethereum/blob/d575a2d3bc76dfbdefdd68b6cffff115542faf75/core/vm/interpreter.go#L116) performs a loop to execute the opcodes.
The Injective implementation is similar and makes use of the gRPC query client which is included in the Cosmos SDK:
1. `eth_call` request is transformed to call the `func (e *PublicAPI) Call` function using the `eth` namespace
2. [`Call()`](https://github.com/InjectiveLabs/injective-core/injective-chain/blob/main/rpc/namespaces/ethereum/eth/api.go#L639) calls `doCall()`
3. [`doCall()`](https://github.com/InjectiveLabs/injective-core/injective-chain/blob/main/rpc/namespaces/ethereum/eth/api.go#L656) transforms the arguments into a `EthCallRequest` and calls `EthCall()` using the query client of the evm module.
4. [`EthCall()`](https://github.com/InjectiveLabs/injective-core/injective-chain/blob/main/x/evm/keeper/grpc_query.go#L212) transforms the arguments into a `ethtypes.message` and calls \`ApplyMessageWithConfig()
5. [`ApplyMessageWithConfig()`](https://github.com/InjectiveLabs/injective-core/injective-chain/blob/d5598932a7f06158b7a5e3aa031bbc94eaaae32c/x/evm/keeper/state_transition.go#L341) instantiates an EVM and either `Create()`s a new contract or `Call()`s a contract using the Geth implementation.
### StateDB
The `StateDB` interface from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/master/core/vm/interface.go) represents an EVM database for full state querying. EVM state transitions are enabled by this interface, which in the `x/evm` module is implemented by the `Keeper`. The implementation of this interface is what makes Ethermint EVM compatible.
## Consensus Engine
The application using the `x/evm` module interacts with the Tendermint Core Consensus Engine over an Application Blockchain Interface (ABCI). Together, the application and Tendermint Core form the programs that run a complete blockchain and combine business logic with decentralized data storage.
Ethereum transactions that are submitted to the `x/evm` module take part in a this consensus process before being executed and changing the application state. We encourage to understand the basics of the [Tendermint consensus engine](https://docs.tendermint.com/master/introduction/what-is-tendermint.html#intro-to-abci) in order to understand state transitions in detail.
## Transaction Logs
On every `x/evm` transaction, the result contains the Ethereum `Log`s from the state machine execution that are used by the JSON-RPC Web3 server for filter querying and for processing the EVM Hooks.
The tx logs are stored in the transient store during tx execution and then emitted through cosmos events after the transaction has been processed. They can be queried via gRPC and JSON-RPC.
## Block Bloom
Bloom is the bloom filter value in bytes for each block that can be used for filter queries. The block bloom value is stored in the transient store and then emitted through a cosmos event during `EndBlock` processing. They can be queried via gRPC and JSON-RPC.
::: tip
👉 **Note**: Since they are not stored on state, Transaction Logs and Block Blooms are not persisted after upgrades. A user must use an archival node after upgrades in order to obtain legacy chain events.
:::
# State
Source: https://docs.injective.network/developers-native/injective/evm/02_state
This section gives you an overview of the objects stored in the `x/evm` module state, functionalities that are derived from the go-ethereum `StateDB` interface, and its implementation through the Keeper as well as the state implementation at genesis.
## State Objects
The `x/evm` module keeps the following objects in state:
### State
| | Description | Key | Value | Store |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------- | --------- |
| Code | Smart contract bytecode | `[]byte{1} + []byte(address)` | `[]byte{code}` | KV |
| Storage | Smart contract storage | `[]byte{2} + [32]byte{key}` | `[32]byte(value)` | KV |
| Block Bloom | Block bloom filter, used to accumulate the bloom filter of current block, emitted to events at end blocker. | `[]byte{1} + []byte(tx.Hash)` | `protobuf([]Log)` | Transient |
| Tx Index | Index of current transaction in current block. | `[]byte{2}` | `BigEndian(uint64)` | Transient |
| Log Size | Number of the logs emitted so far in current block. Used to decide the log index of following logs. | `[]byte{3}` | `BigEndian(uint64)` | Transient |
| Gas Used | Amount of gas used by ethereum messages of current cosmos-sdk tx, it's necessary when cosmos-sdk tx contains multiple ethereum messages. | `[]byte{4}` | `BigEndian(uint64)` | Transient |
## StateDB
The `StateDB` interface is implemented by the `StateDB` in the `x/evm/statedb` module to represent an EVM database for full state querying of both contracts and accounts. Within the Ethereum protocol, `StateDB`s are used to store anything within the IAVL tree and take care of caching and storing nested states.
```go theme={null}
// github.com/ethereum/go-ethereum/core/vm/interface.go
type StateDB interface {
CreateAccount(common.Address)
SubBalance(common.Address, *big.Int)
AddBalance(common.Address, *big.Int)
GetBalance(common.Address) *big.Int
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
GetCodeHash(common.Address) common.Hash
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
GetCodeSize(common.Address) int
AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
Suicide(common.Address) bool
HasSuicided(common.Address) bool
// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
Exist(common.Address) bool
// Empty returns whether the given account is empty. Empty
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddAddressToAccessList(addr common.Address)
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash)
RevertToSnapshot(int)
Snapshot() int
AddLog(*types.Log)
AddPreimage(common.Hash, []byte)
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
}
```
The `StateDB` in the `x/evm` provides the following functionalities:
### CRUD of Ethereum accounts
You can create `EthAccount` instances from the provided address and set the value to store on the `AccountKeeper`with `createAccount()`. If an account with the given address already exists, this function also resets any preexisting code and storage associated with that address.
An account's coin balance can be is managed through the `BankKeeper` and can be read with `GetBalance()` and updated with `AddBalance()` and `SubBalance()`.
* `GetBalance()` returns the EVM denomination balance of the provided address. The denomination is obtained from the module parameters.
* `AddBalance()` adds the given amount to the address balance coin by minting new coins and transferring them to the address. The coin denomination is obtained from the module parameters.
* `SubBalance()` subtracts the given amount from the address balance by transferring the coins to an escrow account and then burning them. The coin denomination is obtained from the module parameters. This function performs a no-op if the amount is negative or the user doesn't have enough funds for the transfer.
The nonce (or transaction sequence) can be obtained from the Account `Sequence` via the auth module `AccountKeeper`.
* `GetNonce()` retrieves the account with the given address and returns the tx sequence (i.e nonce). The function performs a no-op if the account is not found.
* `SetNonce()` sets the given nonce as the sequence of the address' account. If the account doesn't exist, a new one will be created from the address.
The smart contract bytecode containing arbitrary contract logic is stored on the `EVMKeeper` and it can be queried with `GetCodeHash()` ,`GetCode()` & `GetCodeSize()`and updated with `SetCode()`.
* `GetCodeHash()` fetches the account from the store and returns its code hash. If the account doesn't exist or is not an EthAccount type, it returns the empty code hash value.
* `GetCode()` returns the code byte array associated with the given address. If the code hash from the account is empty, this function returns nil.
* `SetCode()` stores the code byte array to the application KVStore and sets the code hash to the given account. The code is deleted from the store if it is empty.
* `GetCodeSize()` returns the size of the contract code associated with this object, or zero if none.
Gas refunded needs to be tracked and stored in a separate variable in
order to add it subtract/add it from/to the gas used value after the EVM
execution has finalized. The refund value is cleared on every transaction and at the end of every block.
* `AddRefund()` adds the given amount of gas to the in-memory refund value.
* `SubRefund()` subtracts the given amount of gas from the in-memory refund value. This function will panic if gas amount is greater than the current refund.
* `GetRefund()` returns the amount of gas available for return after the tx execution finalizes. This value is reset to 0 on every transaction.
The state is stored on the `EVMKeeper`. It can be queried with `GetCommittedState()`, `GetState()` and updated with `SetState()`.
* `GetCommittedState()` returns the value set in store for the given key hash. If the key is not registered this function returns the empty hash.
* `GetState()` returns the in-memory dirty state for the given key hash, if not exist load the committed value from KVStore.
* `SetState()` sets the given hashes (key, value) to the state. If the value hash is empty, this function deletes the key from the state, the new value is kept in dirty state at first, and will be committed to KVStore in the end.
Accounts can also be set to a suicide state. When a contract commits suicide, the account is marked as suicided, when committing the code, storage and account are deleted (from the next block and forward).
* `Suicide()` marks the given account as suicided and clears the account balance of the EVM tokens.
* `HasSuicided()` queries the in-memory flag to check if the account has been marked as suicided in the current transaction. Accounts that are suicided will be returned as non-nil during queries and "cleared" after the block has been committed.
To check account existence use `Exist()` and `Empty()`.
* `Exist()` returns true if the given account exists in store or if it has been
marked as suicided.
* `Empty()` returns true if the address meets the following conditions:
* nonce is 0
* balance amount for evm denom is 0
* account code hash is empty
### EIP2930 functionality
Supports a transaction type that contains an [access list](https://eips.ethereum.org/EIPS/eip-2930), a list of addresses, and storage keys that the transaction plans to access. The access list state is kept in memory and discarded after the transaction committed.
* `PrepareAccessList()` handles the preparatory steps for executing a state transition with regards to both EIP-2929 and EIP-2930. This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
* Add sender to access list (EIP-2929)
* Add destination to access list (EIP-2929)
* Add precompiles to access list (EIP-2929)
* Add the contents of the optional tx access list (EIP-2930)
* `AddressInAccessList()` returns true if the address is registered.
* `SlotInAccessList()` checks if the address and the slots are registered.
* `AddAddressToAccessList()` adds the given address to the access list. If the address is already in the access list, this function performs a no-op.
* `AddSlotToAccessList()` adds the given (address, slot) to the access list. If the address and slot are already in the access list, this function performs a no-op.
### Snapshot state and Revert functionality
The EVM uses state-reverting exceptions to handle errors. Such an exception will undo all changes made to the state in the current call (and all its sub-calls), and the caller could handle the error and don't propagate. You can use `Snapshot()` to identify the current state with a revision and revert the state to a given revision with `RevertToSnapshot()` to support this feature.
* `Snapshot()` creates a new snapshot and returns the identifier.
* `RevertToSnapshot(rev)` undo all the modifications up to the snapshot identified as `rev`.
Ethermint adapted the [go-ethereum journal implementation](https://github.com/ethereum/go-ethereum/blob/master/core/state/journal.go#L39) to support this, it uses a list of journal logs to record all the state modification operations done so far,
snapshot is consists of a unique id and an index in the log list, and to revert to a snapshot it just undo the journal logs after the snapshot index in reversed order.
### Ethereum Transaction logs
With `AddLog()` you can append the given ethereum `Log` to the list of Logs associated with the transaction hash kept in the current state. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log to store.
## Keeper
The EVM module `Keeper` grants access to the EVM module state and implements `statedb.Keeper` interface to support the `StateDB` implementation. The Keeper contains a store key that allows the DB to write to a concrete subtree of the multistore that is only accessible to the EVM module. Instead of using a trie and database for querying and persistence (the `StateDB` implementation on Ethermint), use the Cosmos `KVStore` (key-value store) and Cosmos SDK `Keeper` to facilitate state transitions.
To support the interface functionality, it imports 4 module Keepers:
* `auth`: CRUD accounts
* `bank`: accounting (supply) and CRUD of balances
* `staking`: query historical headers
* `fee market`: EIP1559 base fee for processing `DynamicFeeTx` after the `London` hard fork has been activated on the `ChainConfig` parameters
```go theme={null}
type Keeper struct {
// Protobuf codec
cdc codec.Codec
// Store key required for the EVM Prefix KVStore. It is required by:
// - storing account's Storage State
// - storing account's Code
// - storing module parameters
storeKey storetypes.StoreKey
// key to access the object store, which is reset on every block during Commit
objectKey storetypes.StoreKey
// the address capable of executing a MsgUpdateParams message. Typically, this should be the x/gov module account.
authority sdk.AccAddress
// access to account state
accountKeeper types.AccountKeeper
// update balance and accounting operations with coins
bankKeeper types.BankKeeper
// access historical headers for EVM state transition execution
stakingKeeper types.StakingKeeper
// fetch EIP1559 base fee and parameters
feeMarketKeeper types.FeeMarketKeeper
// chain ID number obtained from the context's chain id
eip155ChainID *big.Int
// Tracer used to collect execution traces from the EVM transaction execution
tracer string
// EVM Hooks for tx post-processing
hooks types.EvmHooks
customContractFns []CustomContractFn
}
```
## Genesis State
The `x/evm` module `GenesisState` defines the state necessary for initializing the chain from a previous exported height. It contains the `GenesisAccounts` and the module parameters
```go theme={null}
type GenesisState struct {
// accounts is an array containing the ethereum genesis accounts.
Accounts []GenesisAccount `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts"`
// params defines all the parameters of the module.
Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"`
}
```
## Genesis Accounts
The `GenesisAccount` type corresponds to an adaptation of the Ethereum `GenesisAccount` type. It defines an account to be initialized in the genesis state.
Its main difference is that the one on Ethermint uses a custom `Storage` type that uses a slice instead of maps for the evm `State` (due to non-determinism), and that it doesn't contain the private key field.
It is also important to note that since the `auth` module on the Cosmos SDK manages the account state, the `Address` field must correspond to an existing `EthAccount` that is stored in the `auth`'s module `Keeper` (i.e `AccountKeeper`).
```go theme={null}
type GenesisAccount struct {
// address defines an ethereum hex formated address of an account
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
// code defines the hex bytes of the account code.
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
// storage defines the set of state key values for the account.
Storage Storage `protobuf:"bytes,3,rep,name=storage,proto3,castrepeated=Storage" json:"storage"`
}
```
# State Transitions
Source: https://docs.injective.network/developers-native/injective/evm/03_state_transitions
The `x/evm` module allows for users to submit Ethereum transactions (`Tx`) and execute their containing messages to evoke state transitions on the given state.
Users submit transactions client-side to broadcast it to the network. When the transaction is included in a block during consensus, it is executed server-side. We highly recommend to understand the basics of the [Tendermint consensus engine](https://docs.tendermint.com/master/introduction/what-is-tendermint.html#intro-to-abci) to understand the State Transitions in detail.
## Client-Side
::: tip
👉 This is based on the `eth_sendTransaction` JSON-RPC
:::
1. A user submits a transaction via one of the available JSON-RPC endpoints using an Ethereum-compatible client or wallet (eg Metamask, WalletConnect, Ledger, etc):
a. eth (public) namespace:
* `eth_sendTransaction`
* `eth_sendRawTransaction`
b. personal (private) namespace:
* `personal_sendTransaction`
2. An instance of `MsgEthereumTx` is created after populating the RPC transaction using `SetTxDefaults` to fill missing tx arguments with default values
3. The `Tx` fields are validated (stateless) using `ValidateBasic()`
4. The `Tx` is **signed** using the key associated with the sender address and the latest ethereum hard fork (`London`, `Berlin`, etc) from the `ChainConfig`
5. The `Tx` is **built** from the msg fields using the Cosmos Config builder
6. The `Tx` is **broadcasted** in [sync mode](https://docs.cosmos.network/master/run-node/txs.html#broadcasting-a-transaction) to ensure to wait for a [`CheckTx`](https://docs.tendermint.com/master/introduction/what-is-tendermint.html#intro-to-abci) execution response. Transactions are validated by the application using `CheckTx()`, before being added to the mempool of the consensus engine.
7. JSON-RPC user receives a response with the [`RLP`](https://eth.wiki/en/fundamentals/rlp) hash of the transaction fields. This hash is different from the default hash used by SDK Transactions that calculates the `sha256` hash of the transaction bytes.
## Server-Side
Once a block (containing the `Tx`) has been committed during consensus, it is applied to the application in a series of ABCI msgs server-side.
Each `Tx` is handled by the application by calling [`RunTx`](https://docs.cosmos.network/master/core/baseapp.html#runtx). After a stateless validation on each `sdk.Msg` in the `Tx`, the `AnteHandler` confirms whether the `Tx` is an Ethereum or SDK transaction. As an Ethereum transaction it's containing msgs are then handled by the `x/evm` module to update the application's state.
### AnteHandler
The `anteHandler` is run for every transaction. It checks if the `Tx` is an Ethereum transaction and routes it to an internal ante handler. Here, `Tx`s are handled using EthereumTx extension options to process them differently than normal Cosmos SDK transactions. The `antehandler` runs through a series of options and their `AnteHandle` functions for each `Tx`:
* `EthSetUpContextDecorator()` is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption by setting the gas meter to infinite
* `EthValidateBasicDecorator(evmKeeper)` validates the fields of a Ethereum type Cosmos `Tx` msg
* `EthSigVerificationDecorator(evmKeeper)` validates that the registered chain id is the same as the one on the message, and that the signer address matches the one defined on the message. It's not skipped for RecheckTx, because it set `From` address which is critical from other ante handler to work. Failure in RecheckTx will prevent tx to be included into block, especially when CheckTx succeed, in which case user won't see the error message.
* `EthAccountVerificationDecorator(ak, bankKeeper, evmKeeper)` that the sender balance is greater than the total transaction cost. The account will be set to store if it doesn't exist, i.e cannot be found on store. This AnteHandler decorator will fail if:
* any of the msgs is not a MsgEthereumTx
* from address is empty
* account balance is lower than the transaction cost
* `EthNonceVerificationDecorator(ak)` validates that the transaction nonces are valid and equivalent to the sender account’s current nonce.
* `EthGasConsumeDecorator(evmKeeper)` validates that the Ethereum tx message has enough to cover intrinsic gas (during CheckTx only) and that the sender has enough balance to pay for the gas cost. Intrinsic gas for a transaction is the amount of gas that the transaction uses before the transaction is executed. The gas is a constant value plus any cost incurred by additional bytes of data supplied with the transaction. This AnteHandler decorator will fail if:
* the transaction contains more than one message
* the message is not a MsgEthereumTx
* sender account cannot be found
* transaction's gas limit is lower than the intrinsic gas
* user doesn't have enough balance to deduct the transaction fees (gas\_limit \* gas\_price)
* transaction or block gas meter runs out of gas
* `CanTransferDecorator(evmKeeper, feeMarketKeeper)` creates an EVM from the message and calls the BlockContext CanTransfer function to see if the address can execute the transaction.
* `EthIncrementSenderSequenceDecorator(ak)` handles incrementing the sequence of the signer (i.e sender). If the transaction is a contract creation, the nonce will be incremented during the transaction execution and not within this AnteHandler decorator.
The options `authante.NewMempoolFeeDecorator()`, `authante.NewTxTimeoutHeightDecorator()` and `authante.NewValidateMemoDecorator(ak)` are the same as for a Cosmos `Tx`. Click [here](https://docs.cosmos.network/master/basics/gas-fees.html#antehandler) for more on the `anteHandler`.
### EVM module
After authentication through the `antehandler`, each `sdk.Msg` (in this case `MsgEthereumTx`) in the `Tx` is delivered to the Msg Handler in the `x/evm` module and runs through the following the steps:
1. Convert `Msg` to an ethereum `Tx` type
2. Apply `Tx` with `EVMConfig` and attempt to perform a state transition, that will only be persisted (committed) to the underlying KVStore if the transaction does not fail:
1. Confirm that `EVMConfig` is created
2. Create the ethereum signer using chain config value from `EVMConfig`
3. Set the ethereum transaction hash to the (impermanent) transient store so that it's also available on the StateDB functions
4. Generate a new EVM instance
5. Confirm that EVM params for contract creation (`EnableCreate`) and contract execution (`EnableCall`) are enabled
6. Apply message. If `To` address is `nil`, create new contract using code as deployment code. Else call contract at given address with the given input as parameters
7. Calculate gas used by the evm operation
3. If `Tx` applied sucessfully
1. Execute EVM `Tx` postprocessing hooks. If hooks return error, revert the whole `Tx`
2. Refund gas according to Ethereum gas accounting rules
3. Update block bloom filter value using the logs generated from the tx
4. Emit SDK events for the transaction fields and tx logs
# Transactions
Source: https://docs.injective.network/developers-native/injective/evm/04_transactions
This section defines the `sdk.Msg` concrete types that result in the state transitions defined on the previous section.
## `MsgEthereumTx`
An EVM state transition can be achieved by using the `MsgEthereumTx`. This message encapsulates an Ethereum transaction data (`TxData`) as a `sdk.Msg`. It contains the necessary transaction data fields. Note, that the `MsgEthereumTx` implements both the [`sdk.Msg`](https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/types/tx_msg.go#L7-L29) and [`sdk.Tx`](https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/types/tx_msg.go#L33-L41) interfaces. Normally, SDK messages only implement the former, while the latter is a group of messages bundled together.
```go theme={null}
type MsgEthereumTx struct {
// inner transaction data
Data *types.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
// DEPRECATED: encoded storage size of the transaction
Size_ float64 `protobuf:"fixed64,2,opt,name=size,proto3" json:"-"`
// transaction hash in hex format
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty" rlp:"-"`
// ethereum signer address in hex format. This address value is checked
// against the address derived from the signature (V, R, S) using the
// secp256k1 elliptic curve
From string `protobuf:"bytes,4,opt,name=from,proto3" json:"from,omitempty"`
}
```
This message field validation is expected to fail if:
* `From` field is defined and the address is invalid
* `TxData` stateless validation fails
The transaction execution is expected to fail if:
* Any of the custom `AnteHandler` Ethereum decorators checks fail:
* Minimum gas amount requirements for transaction
* Tx sender account doesn't exist or hasn't enough balance for fees
* Account sequence doesn't match the transaction `Data.AccountNonce`
* Message signature verification fails
* EVM contract creation (i.e `evm.Create`) fails, or `evm.Call` fails
### Conversion
The `MsgEthreumTx` can be converted to the go-ethereum `Transaction` and `Message` types in order to create and call evm contracts.
```go theme={null}
// AsTransaction creates an Ethereum Transaction type from the msg fields
func (msg MsgEthereumTx) AsTransaction() *ethtypes.Transaction {
txData, err := UnpackTxData(msg.Data)
if err != nil {
return nil
}
return ethtypes.NewTx(txData.AsEthereumData())
}
// AsMessage returns the transaction as a core.Message.
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
msg := Message{
nonce: tx.Nonce(),
gasLimit: tx.Gas(),
gasPrice: new(big.Int).Set(tx.GasPrice()),
gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
gasTipCap: new(big.Int).Set(tx.GasTipCap()),
to: tx.To(),
amount: tx.Value(),
data: tx.Data(),
accessList: tx.AccessList(),
isFake: false,
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil {
msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
}
var err error
msg.from, err = Sender(s, tx)
return msg, err
}
```
### Signing
In order for the signature verification to be valid, the `TxData` must contain the `v | r | s` values from the `Signer`. Sign calculates a secp256k1 ECDSA signature and signs the transaction. It takes a keyring signer and the chainID to sign an Ethereum transaction according to EIP155 standard. This method mutates the transaction as it populates the V, R, S fields of the Transaction's Signature. The function will fail if the sender address is not defined for the msg or if the sender is not registered on the keyring.
```go theme={null}
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
// takes a keyring signer and the chainID to sign an Ethereum transaction according to
// EIP155 standard.
// This method mutates the transaction as it populates the V, R, S
// fields of the Transaction's Signature.
// The function will fail if the sender address is not defined for the msg or if
// the sender is not registered on the keyring
func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.Signer) error {
from := msg.GetFrom()
if from.Empty() {
return fmt.Errorf("sender address not defined for message")
}
tx := msg.AsTransaction()
txHash := ethSigner.Hash(tx)
sig, _, err := keyringSigner.SignByAddress(from, txHash.Bytes())
if err != nil {
return err
}
tx, err = tx.WithSignature(ethSigner, sig)
if err != nil {
return err
}
msg.FromEthereumTx(tx)
return nil
}
```
## TxData
The `MsgEthereumTx` supports the 3 valid Ethereum transaction data types from go-ethereum: `LegacyTx`, `AccessListTx` and `DynamicFeeTx`. These types are defined as protobuf messages and packed into a `proto.Any` interface type in the `MsgEthereumTx` field.
* `LegacyTx`: [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) transaction type
* `DynamicFeeTx`: [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) transaction type. Enabled by London hard fork block
* `AccessListTx`: [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) transaction type. Enabled by Berlin hard fork block
### `LegacyTx`
The transaction data of regular Ethereum transactions.
```go theme={null}
type LegacyTx struct {
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas price defines the value for each gas unit
GasPrice *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=gas_price,json=gasPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_price,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,3,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,4,opt,name=to,proto3" json:"to,omitempty"`
// value defines the unsigned integer value of the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"`
// v defines the signature value
V []byte `protobuf:"bytes,7,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,8,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,9,opt,name=s,proto3" json:"s,omitempty"`
}
```
This message field validation is expected to fail if:
* `GasPrice` is invalid (`nil` , negaitve or out of int256 bound)
* `Fee` (gasprice \* gaslimit) is invalid
* `Amount` is invalid (negaitve or out of int256 bound)
* `To` address is invalid (non valid ethereum hex address)
### `DynamicFeeTx`
The transaction data of EIP-1559 dynamic fee transactions.
```go theme={null}
type DynamicFeeTx struct {
// destination EVM chain ID
ChainID *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"chainID"`
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas tip cap defines the max value for the gas tip
GasTipCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=gas_tip_cap,json=gasTipCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_tip_cap,omitempty"`
// gas fee cap defines the max value for the gas fee
GasFeeCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=gas_fee_cap,json=gasFeeCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_fee_cap,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,5,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,6,opt,name=to,proto3" json:"to,omitempty"`
// value defines the the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"`
Accesses AccessList `protobuf:"bytes,9,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"`
// v defines the signature value
V []byte `protobuf:"bytes,10,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,11,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,12,opt,name=s,proto3" json:"s,omitempty"`
}
```
This message field validation is expected to fail if:
* `GasTipCap` is invalid (`nil` , negative or overflows int256)
* `GasFeeCap` is invalid (`nil` , negative or overflows int256)
* `GasFeeCap` is less than `GasTipCap`
* `Fee` (gas price \* gas limit) is invalid (overflows int256)
* `Amount` is invalid (negative or overflows int256)
* `To` address is invalid (non-valid ethereum hex address)
* `ChainID` is `nil`
### `AccessListTx`
The transaction data of EIP-2930 access list transactions.
```go theme={null}
type AccessListTx struct {
// destination EVM chain ID
ChainID *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"chainID"`
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas price defines the value for each gas unit
GasPrice *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_price,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,4,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,5,opt,name=to,proto3" json:"to,omitempty"`
// value defines the unsigned integer value of the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
Accesses AccessList `protobuf:"bytes,8,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"`
// v defines the signature value
V []byte `protobuf:"bytes,9,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,10,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,11,opt,name=s,proto3" json:"s,omitempty"`
}
```
This message field validation is expected to fail if:
* `GasPrice` is invalid (`nil` , negative or overflows int256)
* `Fee` (gas price \* gas limit) is invalid (overflows int256)
* `Amount` is invalid (negative or overflows int256)
* `To` address is invalid (non-valid ethereum hex address)
* `ChainID` is `nil`
# Application Blockchain Interface (ABCI)
Source: https://docs.injective.network/developers-native/injective/evm/05_abci
The Application Blockchain Interface (ABCI) allows the application to interact with the Tendermint Consensus engine. The application maintains several separate ABCI connections with Tendermint. The most relevant for the `x/evm` is the [Consensus connection at Commit](https://docs.tendermint.com/v0.35/spec/abci/apps.html#consensus-connection). This connection is responsible for block execution and calls the fuctions `InitChain` (containing `InitGenesis`), `BeginBlock`, `DeliverTx`, `EndBlock`, `Commit` . `InitChain` is only called the first time a new blockchain is started and `DeliverTx` is called for each transaction in the block.
## InitGenesis
`InitGenesis` initializes the EVM module genesis state by setting the `GenesisState` fields to the store. In particular it sets the parameters and genesis accounts (state and code).
## ExportGenesis
The `ExportGenesis` ABCI function exports the genesis state of the EVM module. In particular, it retrieves all the accounts with their bytecode, balance and storage, the transaction logs, and the EVM parameters and chain configuration.
## BeginBlock
The EVM module `BeginBlock` logic is executed prior to handling the state transitions from the transactions. The main objective of this function is to:
* Set the context for the current block so that the block header, store, gas meter, etc are available to the `Keeper` once one of the `StateDB` functions are called during EVM state transitions.
* Set the EIP155 `ChainID` number (obtained from the full chain-id), in case it hasn't been set before during `InitChain`
## EndBlock
The EVM module `EndBlock` logic occurs after executing all the state transitions from the transactions. The main objective of this function is to:
* Emit Block bloom events
* This is due for Web3 compatibility as the Ethereum headers contain this type as a field. The JSON-RPC service uses this event query to construct an Ethereum Header from a Tendermint Header.
* The block Bloom filter value is obtained from the Transient Store and then emitted
# Hooks
Source: https://docs.injective.network/developers-native/injective/evm/06_hooks
The `x/evm` module implements an `EvmHooks` interface that extend and customize the `Tx` processing logic externally.
This supports EVM contracts to call native cosmos modules by
1. defining a log signature and emitting the specific log from the smart contract,
2. recognizing those logs in the native tx processing code, and
3. converting them to native module calls.
To do this, the interface includes a `PostTxProcessing` hook that registers custom `Tx` hooks in the `EvmKeeper`. These `Tx` hooks are processed after the EVM state transition is finalized and doesn't fail. Note that there are no default hooks implemented in the EVM module.
```go theme={null}
type EvmHooks interface {
// Must be called after tx is processed successfully, if return an error, the whole transaction is reverted.
PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error
}
```
## `PostTxProcessing`
`PostTxProcessing` is only called after a EVM transaction finished successfully and delegates the call to underlying hooks. If no hook has been registered, this function returns with a `nil` error.
```go theme={null}
func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
if k.hooks == nil {
return nil
}
return k.hooks.PostTxProcessing(k.Ctx(), msg, receipt)
}
```
It's executed in the same cache context as the EVM transaction, if it returns an error, the whole EVM transaction is reverted, if the hook implementor doesn't want to revert the tx, they can always return `nil` instead.
The error returned by the hooks is translated to a VM error `failed to process native logs`, the detailed error message is stored in the return value. The message is sent to native modules asynchronously, there's no way for the caller to catch and recover the error.
## Use Case: Call Native ERC20 Module on Injective
Here is an example taken from the Injective [erc20 module]() that shows how the `EVMHooks` supports a contract calling a native module to convert ERC-20 Tokens into Cosmos native Coins. Following the steps from above.
You can define and emit a `Transfer` log signature in the smart contract like this:
```solidity theme={null}
event Transfer(address indexed from, address indexed to, uint256 value);
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
```
The application will register a `BankSendHook` to the `EvmKeeper`. It recognizes the ethereum tx `Log` and converts it to a call to the bank module's `SendCoinsFromAccountToAccount` method:
```go theme={null}
const ERC20EventTransfer = "Transfer"
// PostTxProcessing implements EvmHooks.PostTxProcessing
func (k Keeper) PostTxProcessing(
ctx sdk.Context,
msg core.Message,
receipt *ethtypes.Receipt,
) error {
params := h.k.GetParams(ctx)
if !params.EnableErc20 || !params.EnableEVMHook {
// no error is returned to allow for other post processing txs
// to pass
return nil
}
erc20 := contracts.ERC20BurnableContract.ABI
for i, log := range receipt.Logs {
if len(log.Topics) < 3 {
continue
}
eventID := log.Topics[0] // event ID
event, err := erc20.EventByID(eventID)
if err != nil {
// invalid event for ERC20
continue
}
if event.Name != types.ERC20EventTransfer {
h.k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
continue
}
transferEvent, err := erc20.Unpack(event.Name, log.Data)
if err != nil {
h.k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
continue
}
if len(transferEvent) == 0 {
continue
}
tokens, ok := transferEvent[0].(*big.Int)
// safety check and ignore if amount not positive
if !ok || tokens == nil || tokens.Sign() != 1 {
continue
}
// check that the contract is a registered token pair
contractAddr := log.Address
id := h.k.GetERC20Map(ctx, contractAddr)
if len(id) == 0 {
// no token is registered for the caller contract
continue
}
pair, found := h.k.GetTokenPair(ctx, id)
if !found {
continue
}
// check that conversion for the pair is enabled
if !pair.Enabled {
// continue to allow transfers for the ERC20 in case the token pair is disabled
h.k.Logger(ctx).Debug(
"ERC20 token -> Cosmos coin conversion is disabled for pair",
"coin", pair.Denom, "contract", pair.Erc20Address,
)
continue
}
// ignore as the burning always transfers to the zero address
to := common.BytesToAddress(log.Topics[2].Bytes())
if !bytes.Equal(to.Bytes(), types.ModuleAddress.Bytes()) {
continue
}
// check that the event is Burn from the ERC20Burnable interface
// NOTE: assume that if they are burning the token that has been registered as a pair, they want to mint a Cosmos coin
// create the corresponding sdk.Coin that is paired with ERC20
coins := sdk.Coins{{Denom: pair.Denom, Amount: sdk.NewIntFromBigInt(tokens)}}
// Mint the coin only if ERC20 is external
switch pair.ContractOwner {
case types.OWNER_MODULE:
_, err = h.k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, true, "burn", tokens)
case types.OWNER_EXTERNAL:
err = h.k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
default:
err = types.ErrUndefinedOwner
}
if err != nil {
h.k.Logger(ctx).Debug(
"failed to process EVM hook for ER20 -> coin conversion",
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
)
continue
}
// Only need last 20 bytes from log.topics
from := common.BytesToAddress(log.Topics[1].Bytes())
recipient := sdk.AccAddress(from.Bytes())
// transfer the tokens from ModuleAccount to sender address
if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil {
h.k.Logger(ctx).Debug(
"failed to process EVM hook for ER20 -> coin conversion",
"tx-hash", receipt.TxHash.Hex(), "log-idx", i,
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
)
continue
}
}
return nil
```
Lastly, register the hook in `app.go`:
```go theme={null}
app.EvmKeeper = app.EvmKeeper.SetHooks(app.Erc20Keeper)
```
# Events
Source: https://docs.injective.network/developers-native/injective/evm/07_events
The `x/evm` module emits the Cosmos SDK events after a state execution. The EVM module emits events of the relevant transaction fields, as well as the transaction logs (ethereum events).
## MsgEthereumTx
| Type | Attribute Key | Attribute Value |
| ------------ | ------------------ | ----------------------- |
| ethereum\_tx | `"amount"` | `{amount}` |
| ethereum\_tx | `"recipient"` | `{hex_address}` |
| ethereum\_tx | `"contract"` | `{hex_address}` |
| ethereum\_tx | `"txHash"` | `{tendermint_hex_hash}` |
| ethereum\_tx | `"ethereumTxHash"` | `{hex_hash}` |
| ethereum\_tx | `"txIndex"` | `{tx_index}` |
| ethereum\_tx | `"txGasUsed"` | `{gas_used}` |
| tx\_log | `"txLog"` | `{tx_log}` |
| message | `"sender"` | `{eth_address}` |
| message | `"action"` | `"ethereum"` |
| message | `"module"` | `"evm"` |
Additionally, the EVM module emits an event during `EndBlock` for the filter query block bloom.
## ABCI
| Type | Attribute Key | Attribute Value |
| ------------ | ------------- | -------------------- |
| block\_bloom | `"bloom"` | `string(bloomBytes)` |
# Parameters
Source: https://docs.injective.network/developers-native/injective/evm/08_params
The evm module contains the following parameters:
## Params
| Key | Type | Default Value |
| -------------- | ----------- | --------------- |
| `EVMDenom` | string | `"inj"` |
| `EnableCreate` | bool | `true` |
| `EnableCall` | bool | `true` |
| `ExtraEIPs` | \[]int | TBD |
| `ChainConfig` | ChainConfig | See ChainConfig |
## EVM denom
The evm denomination parameter defines the token denomination used on the EVM state transitions and gas consumption for EVM messages.
For example, on Ethereum, the `evm_denom` would be `ETH`. To maintain parity with Ethereum, Injective uses Atto as its base denomination. In essence, 1 (atto) inj equals `1x10⁻¹⁸ INJ`, aligning with Ethereum’s denomination where one wei is equal to 1x10⁻¹⁸ ETH. In terms of precision, the `INJ` and `ETH` share the same value, *i.e* `1 INJ = 10^18 inj` and `1 ETH = 10^18 wei`.
## Enable Create
The enable create parameter toggles state transitions that use the `vm.Create` function. When the parameter is disabled, it will prevent all contract creation functionality.
## Enable Transfer
The enable transfer toggles state transitions that use the `vm.Call` function. When the parameter is disabled, it will prevent transfers between accounts and executing a smart contract call.
## Extra EIPs
The extra EIPs parameter defines the set of activateable Ethereum Improvement Proposals (**[EIPs](https://ethereum.org/en/eips/)**)
on the Ethereum VM `Config` that apply custom jump tables.
::: tip
NOTE: some of these EIPs are already enabled by the chain configuration, depending on the hard fork number.
:::
The supported activateable EIPS are:
* **[EIP 1344](https://eips.ethereum.org/EIPS/eip-1344)**
* **[EIP 1884](https://eips.ethereum.org/EIPS/eip-1884)**
* **[EIP 2200](https://eips.ethereum.org/EIPS/eip-2200)**
* **[EIP 2315](https://eips.ethereum.org/EIPS/eip-2315)**
* **[EIP 2929](https://eips.ethereum.org/EIPS/eip-2929)**
* **[EIP 3198](https://eips.ethereum.org/EIPS/eip-3198)**
* **[EIP 3529](https://eips.ethereum.org/EIPS/eip-3529)**
## Chain Config
The `ChainConfig` is a protobuf wrapper type that contains the same fields as the go-ethereum `ChainConfig` parameters, but using `*sdk.Int` types instead of `*big.Int`.
By default, all block configuration fields but `ConstantinopleBlock`, are enabled at genesis (height 0).
### ChainConfig Defaults
| Name | Default Value |
| ------------------- | -------------------------------------------------------------------- |
| HomesteadBlock | 0 |
| DAOForkBlock | 0 |
| DAOForkSupport | `true` |
| EIP150Block | 0 |
| EIP150Hash | `0x0000000000000000000000000000000000000000000000000000000000000000` |
| EIP155Block | 0 |
| EIP158Block | 0 |
| ByzantiumBlock | 0 |
| ConstantinopleBlock | 0 |
| PetersburgBlock | 0 |
| IstanbulBlock | 0 |
| MuirGlacierBlock | 0 |
| BerlinBlock | 0 |
| LondonBlock | 0 |
| ArrowGlacierBlock | 0 |
| GrayGlacierBlock | 0 |
| MergeNetsplitBlock | 0 |
| ShanghaiTime | 0 |
| CancunTime | 0 |
| PragueTime | 0 |
# Client
Source: https://docs.injective.network/developers-native/injective/evm/09_client
A user can query and interact with the `evm` module using the CLI, JSON-RPC, gRPC or REST.
## CLI
Find below a list of `injectived` commands added with the `x/evm` module. You can obtain the full list by using the `injectived -h` command.
### Queries
The `query` commands allow users to query `evm` state.
**`code`**
Allows users to query the smart contract code at a given address.
```go theme={null}
injectived query evm code ADDRESS [flags]
```
```bash theme={null}
# Example
$ injectived query evm code 0x7bf7b17da59880d9bcca24915679668db75f9397
# Output
code: "0xef616c92f3cfc9e92dc270d6acff9cea213cecc7020a76ee4395af09bdceb4837a1ebdb5735e11e7d3adb6104e0c3ac55180b4ddf5e54d022cc5e8837f6a4f971b"
```
**`storage`**
Allows users to query storage for an account with a given key and height.
```bash theme={null}
injectived query evm storage ADDRESS KEY [flags]
```
```bash theme={null}
# Example
$ injectived query evm storage 0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0 0 --height 0
# Output
value: "0x0000000000000000000000000000000000000000000000000000000000000000"
```
### Transactions
The `tx` commands allow users to interact with the `evm` module.
**`raw`**
Allows users to build cosmos transactions from raw ethereum transaction.
```bash theme={null}
injectived tx evm raw TX_HEX [flags]
```
```bash theme={null}
# Example
$ injectived tx evm raw 0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b84
# Output
value: "0x0000000000000000000000000000000000000000000000000000000000000000"
```
## JSON-RPC
For an overview on the JSON-RPC methods and namespaces supported on Ethermint, please refer to [https://docs.injective.zone/basics/json\_rpc.html](https://docs.injective.zone/basics/json_rpc.html)
## gRPC
### Queries
| Verb | Method | Description |
| ------ | ---------------------------------------------------- | ---------------------------------------------------------------------------- |
| `gRPC` | `injective.evm.v1.Query/Account` | Get an Ethereum account |
| `gRPC` | `injective.evm.v1.Query/CosmosAccount` | Get an Ethereum account's Cosmos Address |
| `gRPC` | `injective.evm.v1.Query/ValidatorAccount` | Get an Ethereum account's from a validator consensus Address |
| `gRPC` | `injective.evm.v1.Query/Balance` | Get the balance of a the EVM denomination for a single EthAccount. |
| `gRPC` | `injective.evm.v1.Query/Storage` | Get the balance of all coins for a single account |
| `gRPC` | `injective.evm.v1.Query/Code` | Get the balance of all coins for a single account |
| `gRPC` | `injective.evm.v1.Query/Params` | Get the parameters of x/evm module |
| `gRPC` | `injective.evm.v1.Query/EthCall` | Implements the eth\_call rpc api |
| `gRPC` | `injective.evm.v1.Query/EstimateGas` | Implements the eth\_estimateGas rpc api |
| `gRPC` | `injective.evm.v1.Query/TraceTx` | Implements the debug\_traceTransaction rpc api |
| `gRPC` | `injective.evm.v1.Query/TraceBlock` | Implements the debug\_traceBlockByNumber and debug\_traceBlockByHash rpc api |
| `GET` | `/injective/evm/v1/account/{address}` | Get an Ethereum account |
| `GET` | `/injective/evm/v1/cosmos_account/{address}` | Get an Ethereum account's Cosmos Address |
| `GET` | `/injective/evm/v1/validator_account/{cons_address}` | Get an Ethereum account's from a validator consensus Address |
| `GET` | `/injective/evm/v1/balances/{address}` | Get the balance of a the EVM denomination for a single EthAccount. |
| `GET` | `/injective/evm/v1/storage/{address}/{key}` | Get the balance of all coins for a single account |
| `GET` | `/injective/evm/v1/codes/{address}` | Get the balance of all coins for a single account |
| `GET` | `/injective/evm/v1/params` | Get the parameters of x/evm module |
| `GET` | `/injective/evm/v1/eth_call` | Implements the eth\_call rpc api |
| `GET` | `/injective/evm/v1/estimate_gas` | Implements the eth\_estimateGas rpc api |
| `GET` | `/injective/evm/v1/trace_tx` | Implements the debug\_traceTransaction rpc api |
| `GET` | `/injective/evm/v1/trace_block` | Implements the debug\_traceBlockByNumber and debug\_traceBlockByHash rpc api |
### Transactions
| Verb | Method | Description |
| ------ | --------------------------------- | ------------------------------- |
| `gRPC` | `injective.evm.v1.Msg/EthereumTx` | Submit an Ethereum transactions |
| `POST` | `/injective/evm/v1/ethereum_tx` | Submit an Ethereum transactions |
# EVM
Source: https://docs.injective.network/developers-native/injective/evm/index
## Abstract
The `x/evm` module enables Injective's Ethereum Virtual Machine (EVM) implementation.
## Contents
1. **[Concepts](/developers-native/injective/evm/01_concepts)**
2. **[State](/developers-native/injective/evm/02_state)**
3. **[State Transitions](/developers-native/injective/evm/03_state_transitions)**
4. **[Transactions](/developers-native/injective/evm/04_transactions)**
5. **[Application Blockchain Interface (ABCI)](/developers-native/injective/evm/05_abci)**
6. **[Hooks](/developers-native/injective/evm/06_hooks)**
7. **[Events](/developers-native/injective/evm/07_events)**
8. **[Parameters](/developers-native/injective/evm/08_params)**
9. **[Client](/developers-native/injective/evm/09_client)**
# Derivative Market Concept
Source: https://docs.injective.network/developers-native/injective/exchange/00_derivative_market_concepts
# Derivative Market Concepts
## Definitions
In a derivative market using linear contracts (as opposed to inverse contracts), a contract with ticker **AAA/BBB**
offers exposure to the underlying AAA using the quote currency BBB for margin and settlement. For each contract, the
quotation unit is the BBB price of one unit of AAA, e.g. the USDT price of one unit of ETH.
**Notional** - the notional value of a position is: `notional = quantity * price`.
**Refunds -** In our clearing system, a refund refers to the action of incrementing the **available balance** of an
account. This liberation of funds occurs as the result of an encumbrance being lifted from the account (e.g. cancelling
a limit order, reducing an order's payable fee to a maker fee, using less margin to fund a market order, etc.).
## Perpetual Market Trading Lifecycle
### Perpetual Market Creation
A market is first created either by the instant launch functionality through `MsgInstantPerpetualMarketLaunch` or `MsgInstantExpiryFuturesMarketLaunch` which creates a market by paying an extra fee which doesn't require governance to approve it. Or it is created in the normal way through governance through `MsgPerpetualMarketLaunchProposal` or `MsgExpiryFuturesMarketLaunchProposal`.
### Balance Management
#### Depositing Funds into Exchange
A trader can deposit funds, e.g., USDT, into the exchange by sending a `MsgDeposit` which transfers coins from the
Cosmos-SDK bank module to the trader's subaccount deposits on the exchange module.
Depositing a given `Amount` of coin will increment both the trader's subaccount deposit `AvailableBalance`
and `TotalBalance` by `Amount`.
#### Withdrawing Funds from Exchange
A trader can withdraw funds from the exchange by sending a `MsgWithdraw` which transfers coins from the trader's subaccount
on the exchange module.
**Withdrawal Requirement:** Withdrawing a given `Amount` of coin will decrement both the trader's subaccount
deposit `AvailableBalance` and `TotalBalance` by `Amount`. Note: `Amount` must be less than or equal
to `AvailableBalance`.
#### Transferring Funds between Subaccounts
A trader can transfer funds between his own subaccounts sending a `MsgSubaccountTransfer` which transfer coins from one of
the trader's subaccount deposits to another subaccount also owned by the trader.
Subaccount transfers have the same Withdrawal Requirement as normal withdrawals.
#### Transferring Funds to another Exchange Account
A trader can transfer funds to an external account by sending a `MsgExternalTransfer` which transfers funds from the
trader's subaccount to another third-party account.
External Funds transfers have the same Withdrawal Requirement as normal withdrawals.
### Order Management
#### Placing Limit Orders
A trader can post a limit buy or sell order by sending a `MsgCreateDerivativeLimitOrder`. Upon submission, the order can
be:
1. Immediately (fully or partially) matched against other opposing resting orders on the orderbook in the Endblocker
batch auction, thus establishing a position for the user.
2. Added to the orderbook.
Note that it is possible for an order to be partially matched and for the remaining unmatched portion to be added to the
orderbook.
#### Placing Market Orders
A trader can post a market buy or sell order by sending a `MsgCreateDerivativeMarketOrder`. Upon submission, the market
order will be executed against other opposing resting orders on the orderbook in the Endblocker batch auction, thus
establishing a position for the user.
#### Cancelling Limit Orders
User cancels a limit buy or sell order by sending a `MsgCancelDerivativeOrder` which removes the user's limit order from
the orderbook.
### Increasing Position Margin
A user can increase the margin of a position by sending a `MsgIncreasePositionMargin`.
### Liquidating Insolvent Positions
A third party can liquidate any user's position if the position's maintenance margin ratio is breached by sending a
`MsgLiquidatePosition`.
**Initial Margin Requirement**
This is the requirement for the ratio of margin to the order's notional as well as the mark price when creating a new position.
The idea behind the additional mark price requirement is to minimize the liquidation risk when traded prices and mark prices
temporally diverge too far from each other. Given the initial margin ratio, an order must fulfill two requirements:
* 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 0.05 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: $\mathrm{MarkPrice}$ ≥ $\mathrm{\frac{Margin - Price * Quantity}{(InitialMarginRatio - 1) * Quantity}}$
* For Sells: $\mathrm{MarkPrice}$ ≤ $\mathrm{\frac{Margin + Price * Quantity}{(InitialMarginRatio + 1) * Quantity}}$
**Maintenance Margin Requirement**
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 * MarkPrice - (MarkPrice - EntryPrice)`
* For Shorts: `Margin >= Quantity * MaintenanceMarginRatio * MarkPrice - (EntryPrice - MarkPrice)`
**Liquidation Payouts**
When your position falls below the maintenance margin ratio, the position can be liquidated by anyone. What happens on-chain is that automatically a reduce-only market order of the same size as the position is created. The market order will have a worst price defined as *Infinity* or *0*, implying it will be matched at whatever prices are available in the order book.
The payout from executing the reduce-only market order will not go towards the position owner. Instead, a part of the remaining funds are transferred to the liquidator bot and the other part is transferred to the insurance fund. The split is defined in the exchange params by `LiquidatorRewardShareRate`. If the payout in the position was negative, i.e., the position's negative PNL was greater than its margin, then the insurance fund will cover the missing funds.
Also note that liquidations are executed immediately in a block before any other order matching occurs.
### Funding Payments
Funding exists only for perpetual markets as a mechanism to align trading prices with the mark price. It refers to the
periodic payments exchanged between the traders that are long or short of a contract at the end of every funding epoch,
e.g. every hour. When the funding rate is positive, longs pay shorts. When it is negative, shorts pay longs.
* `Position Size = Position Quantity * MarkPrice`
* `Funding Payment = Position Size * Hourly Funding Rate (HFR)`
* `HFR = Cap((TWAP((SyntheticVWAPExecutionPrice - MarkPrice)/MarkPrice) + DailyInterestRate) * 1/24)`
* `SyntheticVWAPExecutionPrice = (Price_A*Volume_A +Price_B*Volume_B +Price_C*Volume_C)/(Volume_A + Volume_B + Volume_C)`
* `A` is the market buy batch execution
* `B` is the market sell batch execution
* `C` is the limit matching batch execution
Funding payments are applied to the whole market by modifying the `CumulativeFunding` value. Each position stores the current `CumulativeFunding` as `CumulativeFundingEntry`. Subsequent funding payments are only applied upon position changes and can be calculated as:
* FundingPayment
* For Longs: `FundingPayment ← PositionQuantity * (CumulativeFunding - CumulativeFundingEntry)`
* For Shorts: `FundingPayment ← PositionQuantity * (CumulativeFundingEntry - CumulativeFunding)`
* `Margin' ← Margin + FundingPayment`
* `CumulativeFundingEntry' ← CumulativeFunding`
## Perpetual Market Trading Specification
### Positions
A trader's position records the conditions under which the trader has entered into the derivative contract and is
defined as follows
* Position Definition:
* `Quantity`
* `EntryPrice`
* `Margin`
* `HoldQuantity`
* `CumulativeFundingEntry`
As an example, consider the following position in the ETH/USDT market:
* `Quantity` = -2
* `EntryPrice` = 2200
* `Margin` = 800
* `HoldQuantity` = 1
* `CumulativeFundingEntry` = 4838123
This position represents short exposure for 2 contracts of the ETH/USDT market collateralized with 800 USDT, with an
entry price of 2200. The `HoldQuantity` represents the quantity of the position that the trader has opposing orders for.
`CumulativeFundingEntry` represents the cumulative funding value that the position was last updated at.
Position Netting:
When a new vanilla order is matched for a subaccount with an existing position, the new position will be the result from
netting the existing position with the new vanilla order. A matched vanilla order produces a position delta defined by
`FillQuantity`, `FillMargin` and `ClearingPrice`.
* Applying Position Delta to a position in the same direction:
* `Entry Price' ← (Quantity \* EntryPrice + FillQuantity \* ClearingPrice) / (Quantity + FillQuantity)`
* `Quantity' ← Quantity + FillQuantity`
* `Margin' ← Margin + FillMargin`
* Apply Position Delta to a position in the opposing direction:
* `Entry Price - no change`
* `Quantity' ← Quantity - FillQuantity`
* `Margin' ← Margin \* (Quantity - FillQuantity) / Quantity`
### Limit Buy Order
A limit buy order seeks to purchase a specified Quantity of a derivative contract at a specified Price by providing a
specified amount of margin as collateral.
### Limit Sell Order
A limit sell order seeks to sell a specified Quantity of a derivative contract at a specified Price by providing a
specified amount of margin as collateral.
A matched position will have **subtracted fees** which depend on whether the limit order becomes executed as a
maker order or a taker order.
### Market Buy Order
A market buy order seeks to purchase a specified Quantity of a derivative contract at a specified worst price using
the subaccount's available balance as margin collateral.
Handler and EndBlocker Execution of the market order are conceptually identical to the Limit Buy Order
(Immediately Matched case), since the trader passes the margin which implicitly sets a maximum price limit due to the
initial min margin requirements.
### Market Sell Order
A market sell order seeks to sell a specified Quantity of a derivative contract at a specified worst price using the
subaccount's available balance as margin collateral.
Handler and EndBlocker Execution of the market order are conceptually identical to the Limit Sell Order
(Immediately Matched case), since the trader passes the margin which implicitly sets a minimum price limit due to the
initial min margin requirements.
### Order Types
* BUY (1): A standard buy order to purchase an asset at either the current market price or a set limit price.
* SELL (2): A standard sell order to sell an asset at either the current market price or a set limit price.
* STOP\_BUY (3): A stop-buy order converts into a regular buy order once the oracle price reaches or surpasses a specified trigger price.
* STOP\_SELL (4): A stop-sell order becomes a regular sell order once the oracle price drops to or below a specified trigger price.
* TAKE\_BUY (5): A take-buy order converts into a regular buy order once the oracle price reaches or drops below a specified trigger price.
* TAKE\_SELL (6):A stop-sell order becomes a regular sell order once the oracle price reaches or surpasses 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.
* 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.
### Reduce-Only Orders (Selling Positions)
### Limit Buy Reduce-Only Order
A limit buy reduce-only order seeks to reduce existing long exposure by a specified `Quantity` ETH (**base currency**).
The payout for closing a position will have **subtracted fees**.
### Limit Sell Reduce-Only Order
A limit sell reduce-only order seeks to reduce existing short exposure by a specified `Quantity` ETH (**base currency**).
The payout for closing a position will have **subtracted fees**.
# Spot Market Concepts
Source: https://docs.injective.network/developers-native/injective/exchange/01_spot_market_concepts
# Spot Market Concepts
## Definitions
In a Spot Market with ticker **AAA/BBB, AAA is the base asset, BBB is the quote asset.**
For example, in the ETH/USDT market
* ETH is base asset
* USDT is the quote asset
The spot market's **price** refers to how much USDT (the quote asset) is required for one unit of ETH (the base
asset). For all spot markets, **fees are always paid in the quote asset**, e.g., USDT.
**Debit vs Credit**
* **Debit Amount** refers to the amount of asset that is withdrawn from an account.
* **Credit Amount** refers to the amount of asset that is deposited to an account.
**Refunds**
In our system, a refund refers to the action of incrementing the **available balance** of an account. This liberation of
funds occurs as the result of an encumbrance being lifted from the account (e.g. cancelling a limit order, reducing an
order's payable fee to a maker fee, using less margin to fund a market order, etc.).
### Limit Buy Order
A limit buy order seeks to buy a specified `Quantity` ETH (**base asset**) in exchange for `Quantity * Price` amount of
USDT (**quote asset**) **plus fees** which depend on whether the limit order becomes executed as a maker order or a
taker order.
### Limit Sell Order
A limit sell order seeks to sell a specified `Quantity` ETH (**base asset**) in exchange for `Quantity * Price` amount
of USDT (**quote asset**) **minus fees** which depend on whether the limit order becomes executed as a maker order or a
taker order.
### Market Buy Order
A market buy order seeks to buy a specified `Quantity` ETH (**base asset**) at a specified worst price which is at or near
the current ask using the respective account quote asset balance (USDT) as collateral\*\* (inclusive of fees).
As a result, each market buy order implicitly has a maximum acceptable price associated with it, as filling the market
order beyond that price would simply fail due to a lack of funds.
### Market Sell Order
A market sell order seeks to sell a specified `Quantity` ETH (**base asset**) at a specified worst price which is at or
near the current bid in exchange for any amount of the quote asset (USDT) available in the market.
As a result, each market sell order implicitly has a zero price associated with it.
### Order Types
* BUY (1): A standard buy order to purchase an asset at either the current market price or a set limit price.
* SELL (2): A standard sell order to sell an asset at either the current market price or a set limit price.
* STOP\_BUY (3): This order type is not supported for spot markets.
* STOP\_SELL (4): This order type is not supported for spot markets.
* TAKE\_BUY (5): This order type is not supported for spot markets.
* TAKE\_SELL (6): This order type is not supported for spot markets.
* 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.
* 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.
### Market Data Requirements
Orderbook data aside, so long as our Chain supports the **base capability** to obtain Tick by Tick trading data,
aggregations can be applied to obtain most of the necessary higher order data, including
* OHLCV data
* Account Trading History
* Market Statistics
## Spot Market Lifecycle
### Governance based Spot Market Creation
A market is first created either by the instant launch functionality through `MsgInstantSpotMarketLaunch` which creates a market by paying an extra fee which doesn't require governance to approve it. Or it is created in the normal way through governance through `MsgSpotMarketLaunchProposal`.
### Listing Fee based Spot Market Creation
Allow anyone to create an active spot market of their choice without requiring governance approval by burning a pre-set
SpotMarketInstantListingFee of INJ.
We should still check that the denom is valid though.
### Spot Market Status Update
A Spot Market can exist in four different states:
1. Active
2. Paused
3. Suspended
4. Demolished
#### **Active State**
If a spot market is an active state, it can accept orders and trades.
#### Paused State
If a spot market is a paused state, it will no longer accept orders and trades and will also not allow any users to take
actions on that market (no order cancellations).
#### Suspended State
If a spot market is a suspended state, it will no longer accept orders and trades, and will only allow traders to cancel
their orders.
## Demolished State
When a market becomes demolished, all outstanding orders are cancelled.
#### Market Status State Transitions
There are three state transitions that correspond to the following status changes
* Activate Action - **Paused or Suspended Status → Active Status**
* Pause Action - **Active or Suspended Status → Paused Status**
* Suspend Action - **Active or Paused Status → Suspended Status**
* Demolish Action - **Paused or Suspended Status → Demolished Status**
### Spot Market Parameter Update
The following parameters exist for Spot Markets
* SpotMarketInstantListingFee
* DefaultSpotMakerFeeRate
* DefaultSpotTakerFeeRate
# Binary Options Markets
Source: https://docs.injective.network/developers-native/injective/exchange/02_binary_options_markets
# Binary Options Markets
## Concept
Binary options markets don't have base asset as other markets do, and they are quoted in **USDT** (there is a possibility other quote assets will be added later). Tickers for binary options markets usually follow the scheme of **UFC-KHABIB-TKO-09082022** or similar. Typically, binary options markets are used for betting on sport events, but can also be used for betting on any outcome. All markets have possible price bands between $0.00 and $1.00 with users able to put in orders from $0.01 to $0.99. ($0.00 and $1.00 respectively show an end condition that the outcome did not occur or did). The price submitted in the order is essentially an assumed probability of the given event (market) occurring.
For all binary options markets, **fees are always paid in the quote asset**, e.g., USDT.
There is no leverage in these type of markets, as users trade against each other in a zero-sum market. From this the other requirement is implied: if one side of a bet believes the event will occur (YES side), and current market probability for this exact event is *P* (which means the current market price is *P*), opposing side of the bet should be certain that the event will not happen with *(1-P)* probability. Thus, if the person on YES side buys *Q* number of contracts with the price *P*, he locks *Q\*P* of his balance as his margin, while opposing NO side (seller side) should lock *Q\*(1-P)* of his quote balance as margin.
**Example:**
Alice buys 1 contract at $0.20 (margined with $0.20) against Bob who sells 1 contract at $0.20 (margined with $0.80), creating positions for both of them.
* Alice wins $0.80 if the market settles at $1 and Bob wins $0.2 if the market settles at $0.
## Oracle
Binary options markets are tightly coupled to the Provider Oracle type, which allows a governance-registered provider to relay price feed data for arbitrary new price feeds under the provider's subtype without the need for extra governance for adding successively new price feeds. Each binary options market is comprised of the following oracle parameters:
* Oracle symbol (e.g. UFC-KHABIB-TKO-09082022)
* Oracle provider (e.g. frontrunner)
* Oracle type (required to be provider)
* Oracle scale factor (e.g. 6 if the quote denom is USDT)
The main goal of the oracle is to post the final outcome of the event. This final price settles the market at that exact price. This price is expected to be equal to be 0 or 1 most of the time, reflective of the binary outcome.
Moreover, the market could be settled at any price within the (0, 1) price band. In case the *settlement\_price* posted by oracle is between 0 or 1, all positions will be closed at the *settlement\_price* (e.g. 0.42). If the oracle price exceeds 1, the settlement price will be rounded down to 1.
Oracle can also post the final price of **-1**, which is the flag price than triggers refunding of all positions in the current market and demolishes the market. If there is no oracle update ever prior to settlement, then an oracle price of -1 will be used by default to trigger the refunds of all positions.
Further documentation on the oracle provider type can be found in the Oracle module documentation.
### Registering an oracle provider
To register your oracle provider, you need to submit a `GrantProviderPrivilegeProposal` governance proposal. This proposal will register your provider and will allow your address to relay price feeds.
```go theme={null}
type GrantProviderPrivilegeProposal struct {
Title string
Description string
Provider string // the name of the provider, should be specific to you
Relayers []string // addresses which will be able to relay prices
}
```
Once the proposal passes, your provider will be registered and you'll be able to relay your price feeds (example below).
## Market Lifecycle
### Market Creation
A binary options market can be created through an instant launch (through a `MsgInstantBinaryOptionsMarketLaunch`) or through governance (through a `BinaryOptionsMarketLaunchProposal`).
The market may be optionally configured with a market admin which has the ability to trigger settlement, change the market status as well as modify the expiration and settlement timestamp of the given market. If the market does not specify an admin, then the market parameters can only be modified through governance and that the settlement procedure will be fully based on the associated oracle provider price feed.
### Market State Transitions
Binary options markets can take one of three statuses on Injective: Active, Expired or Demolished. After the market is created, the market has an `Active` status, which signifies that individuals can begin trading.
Pertinently, binary options markets also have a characteristic `ExpirationTimestamp` which specifies the deadline at which trading activity for the market ceases as well as a `SettlementTimestamp` which specifies the deadline at which settlement will occur by (which must be after expiration).
* **Active** = trading is open
* **Expired** = trading is closed, open orders are cancelled, no change to positions.
* **Demolished** = positions are settled / refunded (depending on the settlement), market is demolished
The nature of the status transitions for binary options markets are as follows:
| Status Change | Workflow |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Active → Expired | Expiration is part of the standard workflow for a market. Trading is halted immediately for the market and all open orders are cancelled. The market can now be settled immediately (forcefully) by the admin or oracle or be settled naturally using the latest oracle price when we reach SettlementTimestamp. |
| Expired → Demolished (Settlement) | All positions are settled at either the price set by forceful settlement or natural settlement. The market can never be traded on or reactivated again. For natural settlement, upon the SettlementTimestamp time, the last oracle price is recorded and used for settlement. For ‘force-settle’, Admin should post the MarketUpdate msg with SettlementPrice in it being set in a price band of \[0, 1]. |
| Active/Expired → Demolished (Refund) | All positions get refunded. The market can never be traded on or reactivated again. Admin should post the MarketUpdate msg with SettlementPrice in it being set to -1. |
### Market Settlement
The settlement price options are explained above in the [oracle](#oracle) section.
Settling a market can be achieved using one of these two options:
1. Using the registered provider oracle for the particular market. Once the provider oracle is granted privileges to relay prices (explained above), the address with the privileges can relay prices for a particular price feed using the `MsgRelayProviderPrices` message.
```go theme={null}
// MsgRelayProviderPrices defines a SDK message for setting a price through the provider oracle.
type MsgRelayProviderPrices struct {
Sender string
Provider string
Symbols []string
Prices []cosmossdk_io_math.LegacyDec
}
```
2. Using the `MsgAdminUpdateBinaryOptionsMarket` which allows the market's admin (creator) to submit a settlement price directly to the market.
```go theme={null}
type MsgAdminUpdateBinaryOptionsMarket struct {
// new price at which market will be settled
SettlementPrice *Dec
// expiration timestamp
ExpirationTimestamp int64
// expiration timestamp
SettlementTimestamp int64
// Status of the market
Status MarketStatus
}
// Where Status can be one of these options
enum MarketStatus {
Unspecified = 0;
Active = 1;
Paused = 2;
Demolished = 3;
Expired = 4;
}
```
# Other Concepts
Source: https://docs.injective.network/developers-native/injective/exchange/02_other_concepts
# Other Concepts
## Concurrency-Friendly Market Order Clearing Price Algorithm
We apply the [split-apply-combine](https://stackoverflow.com/tags/split-apply-combine/info) paradigm to leverage
concurrency for efficient data processing.
1. Match all matchable orders (see order matching for details) concurrently in all markets.
* The intermediate result is a clearing price and a list of matched orders with their fill quantities.
* The final result is a temporary cache of all new events and all changes to positions, orders, subaccount deposits,
trading reward points and fees paid.
2. Wait for execution on all markets and persist all data.
Note: beyond just executing settlement, the design must also take into account market data dissemination requirements
for off-chain consumption.
## Atomic Market Order Execution
A common request from new applications built on Cosmwasm is for the ability to be notified upon the execution of an order. In the regular order execution flow, this would not be possible, since the Frequent Batch Auctions (FBA) are executed inside the EndBlocker. To circumvent the FBA, the new type of atomic market orders is introduced. For the privilege of executing such an atomic market order instantly, an additional trading fee is imposed. To calculate the fee of an atomic market order, the market's taker fee is multiplied by the market types's `AtomicMarketOrderFeeMultiplier`.
* `SpotAtomicMarketOrderFeeMultiplier`
* `DerivativeAtomicMarketOrderFeeMultiplier`
* `BinaryOptionsAtomicMarketOrderFeeMultiplier`
These multipliers are defined the global exchange parameters. In addition, the exchange parameters also define the `AtomicMarketOrderAccessLevel` which specifies the minimum access level required to execute an atomic market order.
```golang theme={null}
const (
AtomicMarketOrderAccessLevel_Nobody AtomicMarketOrderAccessLevel = 0
AtomicMarketOrderAccessLevel_BeginBlockerSmartContractsOnly AtomicMarketOrderAccessLevel = 1
AtomicMarketOrderAccessLevel_SmartContractsOnly AtomicMarketOrderAccessLevel = 2
AtomicMarketOrderAccessLevel_Everyone AtomicMarketOrderAccessLevel = 3
)
```
## Trading Rewards
Governance approves a **TradingRewardCampaignLaunchProposal** which specifies:
* The first campaign's starting timestamp
* The **TradingRewardCampaignInfo** which specifies
* The campaign duration in seconds
* The accepted trading fee quote currency denoms
* The optional market-specific **boost** info
* The disqualified marketIDs for markets in which trades will not earn rewards
* The **CampaignRewardPools** which specifies the maximum epoch rewards that constitute the trading rewards pool for each successive campaign
During a given campaign, the exchange will record each trader's cumulative trading reward points obtained from trading volume (with boosts applied, if applicable) from all eligible markets, i.e., markets with a matching quote currency that are not in the disqualified list.
At the end of each campaign, i.e., after the `campaign starting timestamp + campaign duration` has elapsed, each trader will receive a pro-rata percentage of the trading rewards pool based off their trading rewards points from that campaign epoch.
Campaigns will not auto-rollover. If there are no additional campaigns defined inside **CampaignRewardPools**, the trading reward campaigns will finish.
## Fee Discounts
Governance approves a **FeeDiscountProposal** which defines a fee discount **schedule** which specifies fee discount **tiers** which each specify the maker and taker discounts rates a trader will receive if they satisfy the specified minimum INJ staked amount AND have had at least the specified trading volume (based on the specified **quote denoms**) over the specified time period (`bucket count * bucket duration seconds`, which should equal 30 days). The schedule also specifies a list of disqualified marketIDs for markets whose trading volume will not count towards the volume contribution.
* Spot markets where the base and quote are both in the accepted quote currencies list will not be rewarded (e.g. the USDC/USDT spot market).
* Maker fills in markets with negative maker fees will NOT give the trader any fee discounts.
* If the fee discount proposal was passed less than 30 days ago, i.e. `BucketCount * BucketDuration` hasn't passed yet since the creation of the proposal, the fee volume requirement is ignored so we don't unfairly penalize market makers who onboard immediately.
Internally the trading volumes are stored in buckets, typically 30 buckets each lasting 24 hours. When a bucket is older than 30 days, it gets removed. Additionally for performance reasons there is a cache for retrieving the fee discount tier for an account. This cache is updated every 24 hours.
### Stake Delegations/Grants
Staked INJ requirements for fee discount tiers can be met through grants from other addresses that have staked their INJ. The total staked INJ value used for fee discount calculations is `OwnStake + StakeGrantedFromGranter - TotalStakeGrantedToOthers`. Note that although several grants can be made to a single address, **only one grant can be activated** for use at a single time. However, a single address can have multiple grants made to other addresses at the same time. Note that only INJ staked with 25 validators is used to calculate `OwnStake` for stake grant purposes. To ensure all staked INJ can be utilized for grants, stake with 25 or fewer validators. Granted stake cannot be re granted.
# State
Source: https://docs.injective.network/developers-native/injective/exchange/03_state
# State
Genesis state defines the initial state of the module to be used to setup the module.
```go theme={null}
// GenesisState defines the exchange module's genesis state.
type GenesisState struct {
// params defines all the parameters of related to exchange.
Params Params
// accounts is an array containing the genesis trade pairs
SpotMarkets []*SpotMarket
// accounts is an array containing the genesis derivative markets
DerivativeMarkets []*DerivativeMarket
// spot_orderbook defines the spot exchange limit orderbook active at genesis.
SpotOrderbook []SpotOrderBook
// derivative_orderbook defines the derivative exchange limit orderbook active at genesis.
DerivativeOrderbook []DerivativeOrderBook
// balances defines the exchange users balances active at genesis.
Balances []Balance
// positions defines the exchange derivative positions at genesis
Positions []DerivativePosition
// subaccount_trade_nonces defines the subaccount trade nonces for the subaccounts at genesis
SubaccountTradeNonces []SubaccountNonce
// expiry_futures_market_info defines the market info for the expiry futures markets at genesis
ExpiryFuturesMarketInfoState []ExpiryFuturesMarketInfoState
// perpetual_market_info defines the market info for the perpetual derivative markets at genesis
PerpetualMarketInfo []PerpetualMarketInfo
// perpetual_market_funding_state defines the funding state for the perpetual derivative markets at genesis
PerpetualMarketFundingState []PerpetualMarketFundingState
// derivative_market_settlement_scheduled defines the scheduled markets for settlement at genesis
DerivativeMarketSettlementScheduled []DerivativeMarketSettlementInfo
// sets spot markets as enabled
IsSpotExchangeEnabled bool
// sets derivative markets as enabled
IsDerivativesExchangeEnabled bool
// the current trading reward campaign info
TradingRewardCampaignInfo *TradingRewardCampaignInfo
// the current and upcoming trading reward campaign pools
TradingRewardPoolCampaignSchedule []*CampaignRewardPool
// the current and upcoming trading reward account points
TradingRewardCampaignAccountPoints []*TradingRewardCampaignAccountPoints
// the current and upcoming trading reward campaign pending pools
PendingTradingRewardPoolCampaignSchedule []*CampaignRewardPool
// the pending trading reward account points
PendingTradingRewardCampaignAccountPoints []*TradingRewardCampaignAccountPendingPoints
// the fee discount schedule
FeeDiscountSchedule *FeeDiscountSchedule
// the cached fee discount account tiers with TTL
FeeDiscountAccountTierTtl []*FeeDiscountAccountTierTTL
// the fee discount paid by accounts in all buckets
FeeDiscountBucketFeesPaidAccounts []*FeeDiscountBucketFeesPaidAccounts
// sets the first fee cycle as finished
IsFirstFeeCycleFinished bool
}
```
## Params
`Params` is a module-wide configuration that stores system parameters and defines overall functioning of the exchange module.
This configuration is modifiable by governance using params update proposal natively supported by `gov` module.
It defines default fee objects to be used for spot and derivative markets and funding parameters for derivative markets and instant listing fees.
Protobuf interface for the `exchange` module params store.
```go theme={null}
type Params struct {
// spot_market_instant_listing_fee defines the expedited fee in INJ required to create a spot market by bypassing governance
SpotMarketInstantListingFee types.Coin
// derivative_market_instant_listing_fee defines the expedited fee in INJ required to create a derivative market by bypassing governance
DerivativeMarketInstantListingFee types.Coin
// default_spot_maker_fee defines the default exchange trade fee for makers on a spot market
DefaultSpotMakerFeeRate math.LegacyDec
// default_spot_taker_fee_rate defines the default exchange trade fee rate for takers on a new spot market
DefaultSpotTakerFeeRate math.LegacyDec
// default_derivative_maker_fee defines the default exchange trade fee for makers on a new derivative market
DefaultDerivativeMakerFeeRate math.LegacyDec
// default_derivative_taker_fee defines the default exchange trade fee for takers on a new derivative market
DefaultDerivativeTakerFeeRate math.LegacyDec
// default_initial_margin_ratio defines the default initial margin ratio on a new derivative market
DefaultInitialMarginRatio math.LegacyDec
// default_maintenance_margin_ratio defines the default maintenance margin ratio on a new derivative market
DefaultMaintenanceMarginRatio math.LegacyDec
// default_funding_interval defines the default funding interval on a derivative market
DefaultFundingInterval int64
// funding_multiple defines the timestamp multiple that the funding timestamp should be a multiple of
FundingMultiple int64
// relayer_fee_share_rate defines the trade fee share percentage that goes to relayers
RelayerFeeShareRate math.LegacyDec
// default_hourly_funding_rate_cap defines the default maximum absolute value of the hourly funding rate
DefaultHourlyFundingRateCap math.LegacyDec
// hourly_interest_rate defines the hourly interest rate
DefaultHourlyInterestRate math.LegacyDec
// max_derivative_order_side_count defines the maximum number of derivative active orders a subaccount can have for a given orderbook side
MaxDerivativeOrderSideCount uint32
// inj_reward_staked_requirement_threshold defines the threshold on INJ rewards after which one also needs staked INJ to receive more
InjRewardStakedRequirementThreshold github_com_cosmos_cosmos_sdk_types.Int
// the trading_rewards_vesting_duration defines the vesting times for trading rewards
TradingRewardsVestingDuration int64
}
```
## Balance
`Balance` is to manage balances of accounts. The module is storing the whole balance in the module account, while the balance of each account is managed just as a record.
The `Balance` object is stored by `subaccount_id` and `denom`.
```go theme={null}
message Balance {
SubaccountId string
Denom string
Deposits *Deposit
}
// An subaccount's deposit for a given base currency
type Deposit struct {
AvailableBalance math.LegacyDec
TotalBalance math.LegacyDec
}
type SubaccountDeposit {
SubaccountId []byte
Deposit *Deposit
}
```
## SubaccountNonce
`SubaccountNonce` is used to express unique order hashes.
```go theme={null}
type SubaccountNonce struct {
SubaccountId string
SubaccountTradeNonce SubaccountTradeNonce
}
```
## Order
There are a number of structures used to store the orders into the store.
```go theme={null}
type OrderInfo struct {
// bytes32 subaccount ID that created the order
SubaccountId string
// address fee_recipient address that will receive fees for the order
FeeRecipient string
// price of the order
Price math.LegacyDec
// quantity of the order
Quantity math.LegacyDec
}
type SubaccountOrderbookMetadata struct {
VanillaLimitOrderCount uint32
ReduceOnlyLimitOrderCount uint32
// AggregateReduceOnlyQuantity is the aggregate fillable quantity of the subaccount's reduce-only limit orders in the given direction.
AggregateReduceOnlyQuantity math.LegacyDec
// AggregateVanillaQuantity is the aggregate fillable quantity of the subaccount's vanilla limit orders in the given direction.
AggregateVanillaQuantity math.LegacyDec
}
type SubaccountOrder struct {
// price of the order
Price math.LegacyDec
// the amount of the quantity remaining fillable
Quantity math.LegacyDec
IsReduceOnly bool
Cid string
}
type MarketOrderIndicator struct {
// market_id represents the unique ID of the market
MarketId string
IsBuy bool
}
```
## SpotMarket
`SpotMarket` is the structure to store all the required information and state for a spot market.
Spot markets are stored by hash of the market to query the market efficiently.
```go theme={null}
// An object describing trade pair of two assets.
type SpotMarket struct {
// A name of the pair in format AAA/BBB, where AAA is base asset, BBB is quote asset.
Ticker string
// Coin denom used for the base asset
BaseDenom string
// Coin used for the quote asset
QuoteDenom string
// maker_fee_rate defines the fee percentage makers pay when trading
MakerFeeRate math.LegacyDec
// taker_fee_rate defines the fee percentage takers pay when trading
TakerFeeRate math.LegacyDec
// relayer_fee_share_rate defines the percentage of the transaction fee shared with the relayer in a derivative market
RelayerFeeShareRate math.LegacyDec
// Unique market ID.
MarketId string
// Status of the market
Status MarketStatus
// min_price_tick_size defines the minimum tick size that the price required for orders in the market
MinPriceTickSize math.LegacyDec
// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
MinQuantityTickSize math.LegacyDec
}
```
## SpotOrderBook
`SpotOrderBook` is a structure to store spot limit orders for a specific market.
Two objects are created, one for buy orders and one for sell orders.
```go theme={null}
// Spot Exchange Limit Orderbook
type SpotOrderBook struct {
MarketId string
IsBuySide bool
Orders []*SpotLimitOrder
}
type SpotOrder struct {
// market_id represents the unique ID of the market
MarketId string
// order_info contains the information of the order
OrderInfo OrderInfo
// order types
OrderType OrderType
// trigger_price is the trigger price used by stop/take orders
TriggerPrice *math.LegacyDec
}
// A valid Spot limit order with Metadata.
type SpotLimitOrder struct {
// order_info contains the information of the order
OrderInfo OrderInfo
// order types
OrderType OrderType
// the amount of the quantity remaining fillable
Fillable math.LegacyDec
// trigger_price is the trigger price used by stop/take orders
TriggerPrice *math.LegacyDec
OrderHash []byte
}
// A valid Spot market order with Metadata.
type SpotMarketOrder struct {
// order_info contains the information of the order
OrderInfo OrderInfo
BalanceHold math.LegacyDec
OrderHash []byte
}
```
## DerivativeMarket
`DerivativeMarket` is the structure to store all the required information and state for a derivative market.
Derivative markets are stored by hash of the market to query the market efficiently.
```go theme={null}
// An object describing a derivative market in the Injective Futures Protocol.
type DerivativeMarket struct {
// Ticker for the derivative contract.
Ticker string
// Oracle base currency
OracleBase string
// Oracle quote currency
OracleQuote string
// Oracle type
OracleType types1.OracleType
// Scale factor for oracle prices.
OracleScaleFactor uint32
// Address of the quote currency denomination for the derivative contract
QuoteDenom string
// Unique market ID.
MarketId string
// initial_margin_ratio defines the initial margin ratio of a derivative market
InitialMarginRatio math.LegacyDec
// maintenance_margin_ratio defines the maintenance margin ratio of a derivative market
MaintenanceMarginRatio math.LegacyDec
// maker_fee_rate defines the maker fee rate of a derivative market
MakerFeeRate math.LegacyDec
// taker_fee_rate defines the taker fee rate of a derivative market
TakerFeeRate math.LegacyDec
// relayer_fee_share_rate defines the percentage of the transaction fee shared with the relayer in a derivative market
RelayerFeeShareRate math.LegacyDec
// true if the market is a perpetual market. false if the market is an expiry futures market
IsPerpetual bool
// Status of the market
Status MarketStatus
// min_price_tick_size defines the minimum tick size that the price and margin required for orders in the market
MinPriceTickSize math.LegacyDec
// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
MinQuantityTickSize math.LegacyDec
}
```
## DerivativeOrderBook
`DerivativeOrderBook` is a structure to store derivative limit orders for a specific market.
Two objects are created, one for buy orders and one for sell orders.
```go theme={null}
// Spot Exchange Limit Orderbook
type DerivativeOrderBook struct {
MarketId string
IsBuySide bool
Orders []*DerivativeLimitOrder
}
type DerivativeOrder struct {
// market_id represents the unique ID of the market
MarketId string
// order_info contains the information of the order
OrderInfo OrderInfo
// order types
OrderType OrderType
// margin is the margin used by the limit order
Margin math.LegacyDec
// trigger_price is the trigger price used by stop/take orders
TriggerPrice *math.LegacyDec
}
// A valid Derivative limit order with Metadata.
type DerivativeLimitOrder struct {
// order_info contains the information of the order
OrderInfo OrderInfo
// order types
OrderType OrderType
// margin is the margin used by the limit order
Margin math.LegacyDec
// the amount of the quantity remaining fillable
Fillable math.LegacyDec
// trigger_price is the trigger price used by stop/take orders
TriggerPrice *math.LegacyDec
OrderHash []byte
}
// A valid Derivative market order with Metadata.
type DerivativeMarketOrder struct {
// order_info contains the information of the order
OrderInfo OrderInfo
// order types
OrderType OrderType
Margin math.LegacyDec
MarginHold math.LegacyDec
// trigger_price is the trigger price used by stop/take orders
TriggerPrice *math.LegacyDec
OrderHash []byte
}
type DerivativeMarketOrderCancel struct {
MarketOrder *DerivativeMarketOrder
CancelQuantity math.LegacyDec
}
```
## DerivativePosition
`DerivativePosition` is a structure to store derivative positions for a subaccount on a specific market.
**Note:** Derivative orders represent intent while positions represent possession.
```go theme={null}
type Position struct {
IsLong bool
Quantity math.LegacyDec
EntryPrice math.LegacyDec
Margin math.LegacyDec
CumulativeFundingEntry math.LegacyDec
}
type PositionDelta struct {
IsLong bool
ExecutionQuantity math.LegacyDec
ExecutionMargin math.LegacyDec
ExecutionPrice math.LegacyDec
}
type DerivativePosition struct {
SubaccountId string
MarketId string
Position *Position
}
type SubaccountPosition struct {
Position *Position
SubaccountId []byte
}
```
## ExpiryFuturesMarketInfo
`ExpiryFuturesMarketInfo` is a structure to keep the information of expiry futures market.
It is stored by the id of the market.
```go theme={null}
type ExpiryFuturesMarketInfo struct {
// market ID.
MarketId string
// expiration_timestamp defines the expiration time for a time expiry futures market.
ExpirationTimestamp int64
// expiration_twap_start_timestamp defines the start time of the TWAP calculation window
TwapStartTimestamp int64
// expiration_twap_start_price_cumulative defines the cumulative price for the start of the TWAP window
ExpirationTwapStartPriceCumulative math.LegacyDec
// settlement_price defines the settlement price for a time expiry futures market.
SettlementPrice math.LegacyDec
}
```
## PerpetualMarketInfo
`PerpetualMarketInfo` is a structure to keep the information of perpetual market.
```go theme={null}
type PerpetualMarketInfo struct {
// market ID.
MarketId string
// hourly_funding_rate_cap defines the maximum absolute value of the hourly funding rate
HourlyFundingRateCap math.LegacyDec
// hourly_interest_rate defines the hourly interest rate
HourlyInterestRate math.LegacyDec
// next_funding_timestamp defines the next funding timestamp in seconds of a perpetual market
NextFundingTimestamp int64
// funding_interval defines the next funding interval in seconds of a perpetual market.
FundingInterval int64
}
```
## PerpetualMarketFunding
`PerpetualMarketFunding` is a structure to manage perpetual market fundings info.
```go theme={null}
type PerpetualMarketFunding struct {
// cumulative_funding defines the cumulative funding of a perpetual market.
CumulativeFunding math.LegacyDec
// cumulative_price defines the cumulative price for the current hour up to the last timestamp
CumulativePrice math.LegacyDec
LastTimestamp int64
}
```
## Trading Rewards
### CampaignRewardPool
`CampaignRewardPool` is a structure to be used for getting the upcoming trading reward pools.
```go theme={null}
type CampaignRewardPool struct {
StartTimestamp int64
// max_campaign_rewards are the maximum reward amounts to be disbursed at the end of the campaign
MaxCampaignRewards sdk.Coins
}
```
### TradingRewardCampaignInfo
`TradingRewardCampaignInfo` is a structure to be used for getting the trading reward campaign info.
```go theme={null}
type TradingRewardCampaignInfo struct {
// number of seconds of the duration of each campaign
CampaignDurationSeconds int64
// the trading fee quote denoms which will be counted for the rewards
QuoteDenoms []string
// the optional boost info for markets
TradingRewardBoostInfo *TradingRewardCampaignBoostInfo
// the marketIDs which are disqualified from being rewarded
DisqualifiedMarketIds []string
}
type TradingRewardCampaignBoostInfo struct {
BoostedSpotMarketIds []string
SpotMarketMultipliers []PointsMultiplier
BoostedDerivativeMarketIds []string
DerivativeMarketMultipliers []PointsMultiplier
}
type PointsMultiplier struct {
MakerPointsMultiplier math.LegacyDec
TakerPointsMultiplier math.LegacyDec
}
```
## FeeDiscountProposal
`FeeDiscountProposal` is a structure to be used for proposing a new fee discount schedule and durations.
```go theme={null}
type FeeDiscountSchedule struct {
// the bucket count, e.g., 30
BucketCount uint64
// the bucket duration, e.g., 1 day
BucketDuration int64
// the trading fee quote denoms which will be counted for the fee paid contribution
QuoteDenoms []string
// the fee discount tiers
TierInfos []*FeeDiscountTierInfo
// the marketIDs which are disqualified from contributing to the fee paid amount
DisqualifiedMarketIds []string
}
type FeeDiscountTierInfo struct {
MakerDiscountRate math.LegacyDec
TakerDiscountRate math.LegacyDec
StakedAmount math.Int
FeePaidAmount math.LegacyDec
}
```
## DerivativeMarketSettlementInfo
`DerivativeMarketSettlementInfo` is a structure to be used for the scheduled markets for settlement.
```go theme={null}
type DerivativeMarketSettlementInfo struct {
// market ID.
MarketId string
// settlement_price defines the settlement price
SettlementPrice math.LegacyDec
// starting_deficit defines starting deficit
StartingDeficit math.LegacyDec
}
```
## TradeLog
Trade logs are emitted in events to track the trading history.
```go theme={null}
type TradeLog struct {
Quantity math.LegacyDec
Price math.LegacyDec
// bytes32 subaccount ID that executed the trade
SubaccountId []byte
Fee math.LegacyDec
OrderHash []byte
}
type DerivativeTradeLog struct {
SubaccountId []byte
PositionDelta *PositionDelta
Payout math.LegacyDec
Fee math.LegacyDec
OrderHash []byte
}
```
## Enums
Enums are used to describe the order types, execution types and market status.
```protobuf theme={null}
enum OrderType {
UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"];
BUY = 1 [(gogoproto.enumvalue_customname) = "BUY"];
SELL = 2 [(gogoproto.enumvalue_customname) = "SELL"];
STOP_BUY = 3 [(gogoproto.enumvalue_customname) = "STOP_BUY"];
STOP_SELL = 4 [(gogoproto.enumvalue_customname) = "STOP_SELL"];
TAKE_BUY = 5 [(gogoproto.enumvalue_customname) = "TAKE_BUY"];
TAKE_SELL = 6 [(gogoproto.enumvalue_customname) = "TAKE_SELL"];
BUY_PO = 7 [(gogoproto.enumvalue_customname) = "BUY_PO"];
SELL_PO = 8 [(gogoproto.enumvalue_customname) = "SELL_PO"];
BUY_ATOMIC = 9 [ (gogoproto.enumvalue_customname) = "BUY_ATOMIC" ];
SELL_ATOMIC = 10 [ (gogoproto.enumvalue_customname) = "SELL_ATOMIC" ];
}
enum MarketStatus {
Unspecified = 0;
Active = 1;
Paused = 2;
Suspended = 3;
Demolished = 4;
Expired = 5;
}
enum ExecutionType {
UnspecifiedExecutionType = 0;
Market = 1;
LimitFill = 2;
LimitMatchRestingOrder = 3;
LimitMatchNewOrder = 4;
}
```
## GrantAuthorization
`GrantAuthorization` is used to track the grantee's address and amount of the granted stake that has been authorized by the granter for trading fee discounts.
```protobuf theme={null}
type GrantAuthorization struct {
Grantee string
Amount math.Int
}
```
## ActiveGrant
`ActiveGrant` is used to track the granter's address and amount of the granted stake (for trading fee discounts) in the grant that has been activated by the grantee.
```protobuf theme={null}
type ActiveGrant struct {
Granter string
Amount math.Int
}
```
## EffectiveGrant
`EffectiveGrant` is used to track the total amount of stake a granter has authorized in stake grants for trading fee discounts.
```protobuf theme={null}
type EffectiveGrant struct {
Granter string
NetGrantedStake math.Int
IsValid bool
}
```
# State Transitions
Source: https://docs.injective.network/developers-native/injective/exchange/04_state_transitions
# State Transitions
This document describes the state transition operations pertaining to:
* Deposit into exchange module account
* Withdraw from exchange module account
* Instant spot market launch
* Instant perpetual market launch
* Instant expiry futures market launch
* Spot limit order creation
* Batch creation of spot limit orders
* Spot market order creation
* Cancel spot order
* Batch cancellation of spot order
* Derivative limit order creation
* Batch derivative limit order creation
* Derivative market order creation
* Cancel derivative order
* Batch cancellation of derivative orders
* Transfer between subaccounts
* Transfer to external account
* Liquidating a position
* Increasing position margin
* Spot market param update proposal
* Exchange enable proposal
* Spot market launch proposal
* Perpetual market launch proposal
* Expiry futures market launch proposal
* Derivative market param update proposal
* Trading rewards launch proposal
* Trading rewards update proposal
* Begin-blocker
* End-blocker
* Fee discount schedule proposal
* Stake grant authorizations
* Stake grant activation
## Deposit into exchange module account
Deposit action is carried out by `MsgDeposit` which consists of `Sender`, `SubaccountId` and `Amount` fields.
**Note:** `SubaccountId` is optional and if it's not available, it's calculated dynamically from `Sender` address.
**Steps**
* Check that the denom specified in `msg.Amount` is a valid denom which exists in bank supply
* Send coins from individual account to `exchange` module account and if fail, just revert
* Get hash type of `subaccountID` from `msg.SubaccountId`, if it's zero subaccount, calculate dynamically from `msg.Sender` by using `SdkAddressToSubaccountID`
* Increment deposit amount for the `subaccountID` by `msg.Amount`
* Emit event for `EventSubaccountDeposit` with `msg.Sender`, `subaccountID` and `msg.Amount`
## Withdraw from exchange module account
Withdraw action is carried out by `MsgWithdraw` which consists of `Sender`, `SubaccountId` and `Amount` fields.
**Note:** The ownership of `msg.SubaccountId` by `msg.Sender` is validated on `msg.ValidateBasic` function.
**Steps**
* Get hash type of `subaccountID` from `msg.SubaccountId`
* Check the denom specified in `msg.Amount` is a valid denom which exists in bank supply
* Decrement withdraw amount from `subaccountID` by `msg.Amount`, if fail, revert
* Send coins from `exchange` module to `msg.Sender`
* Emit event for `EventSubaccountWithdraw` with `subaccountID`, `msg.Sender`, and `msg.Amount`
## Instant spot market launch
Instant spot market launch action is carried out by `MsgInstantSpotMarketLaunch` which consists of `Sender`, `Ticker`, `BaseDenom`, `QuoteDenom`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* Calculate `marketID` from `msg.BaseDenom` and `msg.QuoteDenom`
* Check if same market launch proposal exists by `marketID` and revert if already exists
* Launch spot market with `msg.Ticker`, `msg.BaseDenom`, `msg.QuoteDenom`, `msg.MinPriceTickSize`, `msg.MinQuantityTickSize` and revert if fail
* Send instant listing fee(params.SpotMarketInstantListingFee) from `msg.Sender` to `exchange` module account
* Lastly send the instant listing fee to the community spend pool
## Instant perpetual market launch
Instant perpetual market launch action is carried out by `MsgInstantPerpetualMarketLaunch` which consists of `Sender`, `Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleScaleFactor`, `OracleType`, `MakerFeeRate`, `TakerFeeRate`, `InitialMarginRatio`, `MaintenanceMarginRatio`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* Calculate `marketID` from `msg.Ticker`, `msg.QuoteDenom`, `msg.OracleBase`, `msg.OracleQuote` and `msg.OracleType`.
* Check if same market launch proposal exists by `marketID` and revert if already exists
* Send instant listing fee(params.DerivativeMarketInstantListingFee) from `msg.Sender` to `exchange` module account
* Launch perpetual market with required params on `msg` object and revert if fail
* Lastly send the instant listing fee to the community spend pool
## Instant expiry futures market launch
Instant expiry futures market launch action is carried out by `MsgInstantExpiryFuturesMarketLaunch` which consists of `Sender`, `Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleScaleFactor`, `OracleType`, `Expiry`, `MakerFeeRate`, `TakerFeeRate`, `InitialMarginRatio`, `MaintenanceMarginRatio`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* Calculate `marketID` from `msg.Ticker`, `msg.QuoteDenom`, `msg.OracleBase`, `msg.OracleQuote`, `msg.OracleType` and `msg.Expiry`.
* Check if same market launch proposal exists by `marketID` and revert if already exists
* Send instant listing fee(params.DerivativeMarketInstantListingFee) from `msg.Sender` to `exchange` module account
* Launch expiry futures market with required params on `msg` object and revert if fail
* Trigger `EventExpiryFuturesMarketUpdate` event with market info
* Lastly send the instant listing fee to the community spend pool
## Spot limit order creation
Spot limit order creation is carried out by `MsgCreateSpotLimitOrder` which consists of `Sender` and `Order`.
**Steps**
* Check spot exchange is enabled to make an order on spot market and if not revert
* Check order's price and quantity tick sizes fits market's min quantity and price tick size
* Increment subaccount's `TradeNonce`
* Reject if spot market id does not reference an active spot market
* Calculate unique order hash with `TradeNonce`
* Reject if the subaccount's available deposits does not have at least the required funds for the trade
* Decrement the available balance by the funds amount needed to fund the order
* Store the order in the transient limit order store and transient market indicator store
**Note:** The order in transient store is executed on endblocker or if not, put on long-live store.
## Batch creation of spot limit orders
Batch creation of spot limit orders is carried out by `MsgBatchCreateSpotLimitOrders` which consists of `Sender` and `Orders`.
**Steps**
* Loop over the `msg.Orders` and create spot limit order as in `MsgCreateSpotLimitOrder`
## Spot market order creation
Spot market order creation is carried out by `MsgCreateSpotMarketOrder` which consists of `Sender` and `Order`.
**Steps**
* Check spot exchange is enabled to make an order on spot market and if not revert
* Check order's price and quantity tick sizes fits market's min quantity and price tick size
* Increment subaccount's `TradeNonce`
* Reject if spot market id does not reference an active spot market
* Calculate unique order hash with `TradeNonce`
* Check available balance to fund the market order
* Calculate the worst acceptable price for the market order
* Decrement deposit's AvailableBalance by the balance hold
* Store the order in the transient spot market order store and transient market indicator store
## Cancel spot order
Spot order cancellation is carried out by `MsgCancelSpotOrder` which consists of `Sender` and `MarketId`, `SubaccountId` and `OrderHash`.
**Steps**
* Check spot exchange is enabled to execute the action and if not revert
* Reject if spot market id does not reference an active, suspended or demolished spot market
* Check spot limit order exists by `marketID`, `subaccountID` and `orderHash`
* Add back the margin hold to available balance
* Increment the available balance margin hold
* Delete the order state from ordersStore and ordersIndexStore
* Emit `EventCancelSpotOrder` event with marketID and order info
## Batch cancellation of spot orders
Batch cancellation of spot orders is carried out by `MsgBatchCancelSpotOrders` which consists of `Sender` and `Data`.
**Steps**
* Loop over the `msg.Data` and cancel spot order as in `MsgCancelSpotOrder`
## Derivative limit order creation
Derivative limit order creation is carried out by `MsgCreateDerivativeLimitOrder` which consists of `Sender` and `Order`.
**Steps**
* Check derivative exchange is enabled to make an order on derivative market and if not revert
* Reject if market order is already placed on the market by `subaccountID` (**Note:** Can't the market order and limit order core exist?)
* Get derivative market and markPrice by `marketID`
* Get orderbook metadata (`SubaccountOrderbookMetadata`) the for specified `marketID` and `subaccountID`
* Ensure limit order is valid:
* Market config (market id and tick sizes)
* Subaccount trade nonce
* Subaccount max order count
* If reduce-only order:
* Position with valid quantity and opposite direction exists
* If order would result in other reduce-only orders becoming stale, reject it
* If limit order:
* Enough subaccount deposits for margin hold
* If order is in opposite direction of existing position and results in other reduce-only orders becoming stale, cancel the stale reduce-only orders
* Store the order in the transient limit order store and transient market indicator store
* Update orderbook metadata for subaccount
## Batch creation of derivative limit orders
Batch creation of derivative limit orders is carried out by `MsgBatchCreateDerivativeLimitOrders` which consists of `Sender` and `Orders`.
**Steps**
* Loop over the `msg.Orders` and create derivative limit order as in `MsgCreateDerivativeLimitOrder`
## Derivative market order creation
Derivative market order creation is carried out by `MsgCreateDerivativeMarketOrder` which consists of `Sender` and `Order`.
**Steps**
* Check derivative exchange is enabled to make an order on derivative market and if not revert
* Check if `SubaccountID` that is going to make new order has limit derivative order or market order and reject. **Note:** Perpetual market can't place two market orders or both limit / market orders at the same time?
* Check order's price and quantity tick sizes fits market's min quantity and price tick size
* Increment Subaccount's `TradeNonce`
* Reject if derivative market id does not reference an active derivative market
* Calculate unique order hash with `TradeNonce`
* Check that the market order worst price reaches the best opposing resting orderbook price
* Check Order/Position Margin amount
* 1. If it's reduce only order
* A. Check if position for `subaccountID` on the market is not nil
* B. Check that the order can close the position
* C. Reject if position.quantity - AggregateReduceOnlyQuantity - order.quantity \< 0
* D. Set MarginHold as zero for no margin hold for selling positions
* 2. If it's not reduce only order
* A. Check available balance to fund the market order
* B. Reject if the subaccount's available deposits does not have at least the required funds for the trade
* C. Decrement deposit's AvailableBalance by the balance hold
* For an opposing position, if AggregateVanillaQuantity > position.quantity - AggregateReduceOnlyQuantity - order.FillableQuantity, the new reduce-only order might invalidate some existing reduce-only orders or itself be invalid, and do operations for that.
* Store the order in the transient derivative market order store and transient market indicator store
## Cancel derivative order
Derivative order cancellation is carried out by `MsgCancelDerivativeOrder` which consists of `Sender`, `MarketId`, `SubaccountId` and `OrderHash`.
**Steps**
* Check derivative exchange is enabled to execute the operation and if not revert
* Reject if derivative market id does not reference an active derivative market
* Check resting derivative limit order exists by `marketID`, `subaccountID` and `orderHash`
* Add back the margin hold to available balance
* Skip cancelling limit orders if their type shouldn't be cancelled
* Delete the order state from ordersStore, ordersIndexStore and subaccountOrderStore
* Update orderbook metadata for subaccount
* Emit `EventCancelDerivativeOrder` event with marketID and order info
## Batch cancellation of derivative orders
Batch cancellation of derivative orders is carried out by `MsgBatchCancelDerivativeOrders` which consists of `Sender` and `Data`.
**Steps**
* Loop over the `msg.Data` and cancel spot order as in `MsgCancelDerivativeOrder`
## Batch order updates
Batch updating orders is carried out by `MsgBatchUpdateOrders` which consists of `Sender` and `Orders`.
**Steps**
* Cancel all orders in all market id specified by `SpotMarketIdsToCancelAll` and `DerivativeMarketIdsToCancelAll` for specified subaccount id
* Loop over the `msg.SpotOrdersToCancel` and cancel spot limit order as in `MsgCancelSpotOrder`. If the cancel fails, continue to next order. The success of cancellations is reflected in the `MsgBatchUpdateOrdersResponse` as `SpotCancelSuccess`.
* Loop over the `msg.DerivativeOrdersToCancel` and cancel derivative limit order as in `MsgCancelDerivativeOrder`. If the cancel fails, continue to next order. The success of cancellations is reflected in the `MsgBatchUpdateOrdersResponse` as `DerivativeCancelSuccess`.
* Loop over the `msg.SpotOrdersToCreate` and create spot limit order as in `MsgCreateSpotOrder`. If the creation fails, continue to next order. Successful creations are reflected in the `MsgBatchUpdateOrdersResponse` as `SpotOrderHashes`.
* Loop over the `msg.DerivativeOrdersToCreate` and create derivative limit order as in `MsgCreateDerivativeOrder`. If the creation fails, continue to next order. Successful creations are reflected in the `MsgBatchUpdateOrdersResponse` as `DerivativeOrderHashes`.
## Transfer between subaccounts
Transfer between subaccounts is executed by `MsgSubaccountTransfer` which consists of `Sender`, `SourceSubaccountId`, `DestinationSubaccountId` and `Amount`.
**Steps**
* Withdraw deposit from `msg.SourceSubaccountId` for `msg.Amount`, if fail revert transaction
* Increment deposit of `msg.DestinationSubaccountId` by `msg.Amount`
* Emit event for `EventSubaccountBalanceTransfer` with `SrcSubaccountId`, `DstSubaccountId` and `msg.Amount`
**Note:** With subaccount transfer, no need to transfer actual coins from bank module but changing the records are enough.
## Transfer to external account
Transfer to external account is executed by `MsgExternalTransfer` which consists of `Sender`, `SourceSubaccountId`, `DestinationSubaccountId` and `Amount`.
**Steps**
* Withdraw deposit from `msg.SourceSubaccountId` for `msg.Amount`, if fail revert transaction
* Increment deposit of `msg.DestinationSubaccountId` by `msg.Amount`
* Emit event for `EventSubaccountBalanceTransfer` with `SrcSubaccountId`, `DstSubaccountId` and `msg.Amount`
**Note:** With subaccount transfer, no need to transfer actual coins from bank module but changing the records are enough.
1. Event should be different for subaccount transfer and external transfer.
2. There's no difference in subaccount transfer and external transfer, still need to keep different messages?
## Liquidating a position
Liquidating a position is executed by `MsgLiquidatePosition` which consists of `Sender`, `SubaccountId`, `MarketId` and `Order`.
**Steps**
* Check derivative exchange is enabled to liquidate a position on derivative market and if not revert
* Reject if derivative market id does not reference an active derivative market
* Get derivative market and markPrice by `marketID`
* Get position for `marketID` and `subaccountID`
* Calculate `liquidationPrice` and `bankruptcyPrice` from the position info
* Determine vaporize or liquidate and if not all of them, revert
* Cancel all reduce-only limit orders created by the position holder in the given market
* Apply funding and update position
* Cancel all market orders created by the position holder in the given market
* Check and increment subaccount nonce, compute order hash
* Calculate `liquidationOrder` hash
* Set the liquidation order into the storage
* Execute liquidation by matching position and liquidation order
* Handle differently based on the payout is positive or negative (insurance fund is involved here in calculation)
* Positive Payout:
1. Send half of the payout to liquidator (incentive for running liquidator bots)
2. Send the other half to the insurance fund (incentive for participating in insurance funds)
* Negative Payout - Four levels of escalation to retrieve the funds:
1. From trader's available balance
2. From trader's locked balance by cancelling his vanilla limit orders
3. From the insurance fund
4. Not enough funds available. Pause the market and add markets to the storage to be settled in next block, see `BeginBlocker` specs.
* If market is a perpetual market, upgrade VWAP data based on liquidation price and quantity
* If there's remaining in liquidation order, return back remains by cancelling order
## Increasing position margin
Increasing position margin is executed by `MsgIncreasePositionMargin` which consists of `Sender`, `SourceSubaccountId`, `DestinationSubaccountId`, `MarketId` and `Amount`.
**Steps**
* Check derivative exchange is enabled to increase position margin on derivative market and if not revert
* Reject if derivative market id does not reference an active derivative market
* Get deposit of `sourceSubaccountID`
* If `deposit.AvailableBalance` is lower than `msg.Amount`, revert
* Get position by `marketID` and `destinationSubaccountID` and if not exist, revert
* Reduce deposit amount of `sourceSubaccountID` by `msg.Amount`
* Increase position margin by `msg.Amount` and update position in the store
## Exchange enable proposal
The enable of market type is done by `ExchangeEnableProposal` which consists of `Title`, `Description` and `ExchangeType`.
**Steps**
* `ValidateBasic` for proposal
* If `p.ExchangeType` is spot market, enable spot exchange
* If `p.ExchangeType` is derivative market, enable derivative market
## Spot market launch proposal
Launch of spot market is handled by `SpotMarketLaunchProposal` which consists of `Title`, `Description`, `Ticker`, `BaseDenom`, `QuoteDenom`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* `ValidateBasic` for proposal
* Validate `BaseDenom` and `QuoteDenom` are valid
* Validate if same market does not exist by `msg.BaseDenom` and `msg.QuoteDenom`
* Calculate RelayerFeeShareRate based on exchange module params. **Note:** for INJ currency, relayer share rate is set to 100%
* Save spot market with calculated `ticker`, `baseDenom`, `quoteDenom`, `exchangeParams.DefaultSpotMakerFeeRate`, `exchangeParams.DefaultSpotTakerFeeRate`, `relayerFeeShareRate`, `minPriceTickSize`, `minQuantityTickSize`, `marketID`, and `MarketStatus_Active`.
## Perpetual market launch proposal
Perpetual market launch is handled by `PerpetualMarketLaunchProposal` which consists of `Title`, `Description`, `Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleScaleFactor`, `OracleType`, `MakerFeeRate`, `TakerFeeRate`, `InitialMarginRatio`, `MaintenanceMarginRatio`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* `ValidateBasic` for proposal
* Validate `quoteDenom`.
* Calculate `marketID` from `ticker`, `quoteDenom`, `oracleBase`, `oracleQuote`, `oracleType`
* Validate active or inactive perpetual market for `marketID` does not exist
* Try getting derivative market price to check price oracle by `oracleBase`, `oracleQuote`, `oracleScaleFactor`, `oracleType`
* Validate insurance fund exist for `marketID`
* Calculate `defaultFundingInterval`, `nextFundingTimestamp`, `relayerFeeShareRate` from `exchange` module params
* Execute `SetDerivativeMarketWithInfo` to set market info into the storage with `market`, `marketInfo` and `funding` objects
## Expiry futures market launch proposal
Expiry futures market launch is handled by `ExpiryFuturesMarketLaunchProposal` which consists of `Title`, `Description`, `Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleScaleFactor`, `OracleType`, `Expiry`, `MakerFeeRate`, `TakerFeeRate`, `InitialMarginRatio`, `MaintenanceMarginRatio`, `MinPriceTickSize` and `MinQuantityTickSize` fields.
**Steps**
* `ValidateBasic` for proposal
* Validate `quoteDenom`
* Calculate `marketID` from `p.Ticker`, `p.QuoteDenom`, `p.OracleBase`, `p.OracleQuote`, `p.OracleType` and `p.Expiry`
* Validate active or inactive expiry futures market for `marketID` does not exist
* If expiry time passed `ctx.BlockTime()` already, revert
* Try getting derivative market price to check price oracle by `oracleBase`, `oracleQuote`, `oracleScaleFactor`, `oracleType`
* Validate insurance fund exist for `marketID`
* Calculate RelayerFeeShareRate based on exchange module params. **Note:** for INJ currency, relayer share rate is set to 100%
* Execute `SetDerivativeMarketWithInfo` to set market info into the storage with `market`, `marketInfo` objects **Note:** TwapStartTimestamp is set to `expiry - thirtyMinutesInSeconds`.
## Spot market param update proposal
The update of spot market param is handled by `SpotMarketParamUpdateProposal` which consists of `Title`, `Description`, `MarketId`, `MakerFeeRate`, `TakerFeeRate`, `RelayerFeeShareRate`, `MinPriceTickSize`, `MinQuantityTickSize` and `Status`.
**Steps**
* `ValidateBasic` for proposal
* Get spot market by `p.MarketId` and if not exist, revert
* Reset the params for `MakerFeeRate`, `TakerFeeRate`, `RelayerFeeShareRate`, `MinPriceTickSize`, `MinQuantityTickSize` and `Status` if not empty, if empty keep as it is.
* Validate `MakerFeeRate` is bigger than `TakerFeeRate`.
## Derivative market param update proposal
Derivative market param update is handled by `DerivativeMarketParamUpdateProposal` which consists of `Title`, `Description`, `MarketId`, `InitialMarginRatio`, `MaintenanceMarginRatio`, `MakerFeeRate`, `TakerFeeRate`, `RelayerFeeShareRate`, `MinPriceTickSize`, `MinQuantityTickSize` and `Status`.
**Steps**
* `ValidateBasic` for proposal
* Validate Derivative market exists by `p.MarketId` and if not exist, revert
* Reset the params for `InitialMarginRatio`, `MaintenanceMarginRatio`, `MakerFeeRate`, `TakerFeeRate`, `RelayerFeeShareRate`, `MinPriceTickSize`, `MinQuantityTickSize` and `Status` if not empty, if empty keep as it is.
* Validate `MakerFeeRate` is bigger than `TakerFeeRate`.
* Validate `InitialMarginRatio` is bigger than `MaintenanceMarginRatio`.
* Schedule Derivative market param update and update finalization on Endblocker - **Note:** this is due to the orders update for derivative market param update - should make sure nothing panics here.
## Trading Rewards Campaign Launch Proposal
**Steps**
* `ValidateBasic` for proposal
* No existing campaign may exist.
* Campaign start timestamps must be in the future.
* Campaign quote denoms must exist.
* All start timestamps must match the duration.
* Set Campaign Data (Reward Pools, Info, Market Qualifications and Market Point Multipliers)
* Emit `EventTradingRewardCampaignUpdate`
## Trading Rewards Campaign Update Proposal
**Steps**
* `ValidateBasic` for proposal
* All `StartTimestamp` inside `CampaignRewardPoolsUpdates` must equal an existing campaign.
* `CampaignDurationSeconds` cannot be modified, but must match the current campaign.
* `CampaignRewardPoolsUpdates` cannot modify the current campaign and may contain nil values to delete a reward pool.
* Campaign start timestamps from `CampaignRewardPoolsAdditions` must be in the future.
* Any campaign quote denoms must exist.
* Delete Current Campaign Data (Info, Market Qualifications and Market Point Multipliers)
* Set Campaign Data (Info, Market Qualifications and Market Point Multipliers)
* Set Reward Pool Updates
* Set Reward Pool Additions
* Emit `EventTradingRewardCampaignUpdate`
## Fee Discount Schedule Proposal
**Steps**
* `ValidateBasic` for proposal
* If Current Fee Discount Schedule exists, delete it along with Market Qualifications
* Defined quote denoms must exist.
* If a restart of the fee cycle is required (bucket count, bucket duration or quote denoms changed), delete all account fee buckets and restart cycle.
* Set the first fee paid bucket timestamp as the current block time
* Set New Fee Discount Schedule, delete it along with Market Qualifications
* Set New Market Qualifications
## Stake Grant Authorizations
**Steps**
* Check if an existing grant from the granter already exists for the grantee
* Calculate the new total amount of stake granted to the grantee by subtracting the existing grant amounts and adding the new grant amounts (essentially overwrites the existing grant amounts with the new grant amounts)
* Ensure valid grant authorizations by making sure total amount granted is less than or equal to total amount staked by granter
* Update grant amounts for grantee
* Set grant to active if the current active grant is from the same granter or if there is no current active grant
* Emit `EventGrantAuthorizations` with granter and grants
## Stake Grant Activation
**Steps**
* Check to make sure grant from granter to grantee exists
* Check to make sure granter is not granting more than their total staked amount
* If grant amount is 0, delete the grant, otherwise write new grant amount to store
* Emit `EventGrantActivation` with grantee, granter, and amount
# Messages
Source: https://docs.injective.network/developers-native/injective/exchange/05_messages
# Messages
In this section we describe the processing of the exchange messages and the corresponding updates to the state. All
created/modified state objects specified by each message are defined within the [State Transitions](./04_state_transitions)
section.
## Msg/Deposit
`MsgDeposit` defines a SDK message for transferring coins from the sender's bank balance into the subaccount's exchange deposits.
```go theme={null}
type MsgDeposit struct {
Sender string
// (Optional) bytes32 subaccount ID to deposit funds into. If empty, the coin will be deposited to the sender's default
// subaccount address.
SubaccountId string
Amount types.Coin
}
```
**Fields description**
* `Sender` field describes the address who deposits.
* `SubaccountId` describes the ID of a sub-account to receive a deposit.
* `Amount` specifies the deposit amount.
## Msg/Withdraw
`MsgWithdraw` defines a SDK message for withdrawing coins from a subaccount's deposits to the user's bank balance.
```go theme={null}
type MsgWithdraw struct {
Sender string
// bytes32 subaccount ID to withdraw funds from
SubaccountId string
Amount types.Coin
}
```
**Fields description**
* `Sender` field describes the address to receive withdrawal.
* `SubaccountId` describes the ID of a sub-account to withdraw from.
* `Amount` specifies the withdrawal amount.
## Msg/InstantSpotMarketLaunch
`MsgInstantSpotMarketLaunch` defines a SDK message for creating a new spot market by paying listing fee without governance. The fee is sent to the community spend pool.
```go theme={null}
type MsgInstantSpotMarketLaunch struct {
Sender string
Ticker string
BaseDenom string
QuoteDenom string
MinPriceTickSize math.LegacyDec
MinQuantityTickSize math.LegacyDec
MinNotional math.LegacyDec
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Ticker` describes the ticker for the spot market.
* `BaseDenom` specifies the type of coin to use as the base currency.
* `QuoteDenom` specifies the type of coin to use as the quote currency.
* `MinPriceTickSize` defines the minimum tick size of the order's price.
* `MinQuantityTickSize` defines the minimum tick size of the order's quantity.
## Msg/InstantPerpetualMarketLaunch
`MsgInstantPerpetualMarketLaunch` defines a SDK message for creating a new perpetual futures market by paying listing fee without governance. The fee is sent to the community spend pool.
```go theme={null}
type MsgInstantPerpetualMarketLaunch struct {
Sender string
Ticker string
QuoteDenom string
OracleBase string
OracleQuote string
OracleScaleFactor uint32
OracleType types1.OracleType
MakerFeeRate math.LegacyDec
TakerFeeRate math.LegacyDec
InitialMarginRatio math.LegacyDec
MaintenanceMarginRatio math.LegacyDec
MinPriceTickSize math.LegacyDec
MinQuantityTickSize math.LegacyDec
MinNotional math.LegacyDec
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Ticker` field describes the ticker for the derivative market.
* `QuoteDenom` field describes the type of coin to use as the base currency.
* `OracleBase` field describes the oracle base currency.
* `OracleQuote` field describes the oracle quote currency.
* `OracleScaleFactor` field describes the scale factor for oracle prices.
* `OracleType` field describes the oracle type.
* `MakerFeeRate` field describes the trade fee rate for makers on the derivative market.
* `TakerFeeRate` field describes the trade fee rate for takers on the derivative market.
* `InitialMarginRatio` field describes the initial margin ratio for the derivative market.
* `MaintenanceMarginRatio` field describes the maintenance margin ratio for the derivative market.
* `MinPriceTickSize` field describes the minimum tick size of the order's price and margin.
* `MinQuantityTickSize` field describes the minimum tick size of the order's quantity.
## Msg/InstantExpiryFuturesMarketLaunch
`MsgInstantExpiryFuturesMarketLaunch` defines a SDK message for creating a new expiry futures market by paying listing fee without governance. The fee is sent to the community spend pool.
```go theme={null}
type MsgInstantExpiryFuturesMarketLaunch struct {
Sender string
Ticker string
QuoteDenom string
OracleBase string
OracleQuote string
OracleType types1.OracleType
OracleScaleFactor uint32
Expiry int64
MakerFeeRate math.LegacyDec
TakerFeeRate math.LegacyDec
InitialMarginRatio math.LegacyDec
MaintenanceMarginRatio math.LegacyDec
MinPriceTickSize math.LegacyDec
MinQuantityTickSize math.LegacyDec
MinNotional math.LegacyDec
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Ticker` field describes the ticker for the derivative market.
* `QuoteDenom` field describes the type of coin to use as the quote currency.
* `OracleBase` field describes the oracle base currency.
* `OracleQuote` field describes the oracle quote currency.
* `OracleScaleFactor` field describes the scale factor for oracle prices.
* `OracleType` field describes the oracle type.
* `Expiry` field describes the expiration time of the market.
* `MakerFeeRate` field describes the trade fee rate for makers on the derivative market.
* `TakerFeeRate` field describes the trade fee rate for takers on the derivative market.
* `InitialMarginRatio` field describes the initial margin ratio for the derivative market.
* `MaintenanceMarginRatio` field describes the maintenance margin ratio for the derivative market.
* `MinPriceTickSize` field describes the minimum tick size of the order's price and margin.
* `MinQuantityTickSize` field describes the minimum tick size of the order's quantity.
## Msg/CreateSpotLimitOrder
`MsgCreateSpotLimitOrder` defines a SDK message for creating a new spot limit order.
```go theme={null}
type MsgCreateSpotLimitOrder struct {
Sender string
Order SpotOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Order` field describes the order info.
## Msg/BatchCreateSpotLimitOrders
`MsgBatchCreateSpotLimitOrders` defines a SDK message for creating a new batch of spot limit orders.
```go theme={null}
type MsgBatchCreateSpotLimitOrders struct {
Sender string
Orders []SpotOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Orders` field describes the orders info.
## Msg/CreateSpotMarketOrder
`MsgCreateSpotMarketOrder` defines a SDK message for creating a new spot market order.
```go theme={null}
type MsgCreateSpotMarketOrder struct {
Sender string
Order SpotOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Order` field describes the order info.
## Msg/CancelSpotOrder
`MsgCancelSpotOrder` defines the message to cancel a spot order.
```go theme={null}
type MsgCancelSpotOrder struct {
Sender string
MarketId string
SubaccountId string
OrderHash string
Cid string
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `MarketId` field describes the id of the market where the order is placed.
* `SubaccountId` field describes the subaccount id that placed the order.
* `OrderHash` field describes the hash of the order.
## Msg/BatchCancelSpotOrders
`MsgBatchCancelSpotOrders` defines the message to cancel the spot orders in batch.
```go theme={null}
type MsgBatchCancelSpotOrders struct {
Sender string
Data []OrderData
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Data` field describes the orders to cancel.
## Msg/CreateDerivativeLimitOrder
`MsgCreateDerivativeLimitOrder` defines the message to create a derivative limit order.
```go theme={null}
type MsgCreateDerivativeLimitOrder struct {
Sender string
Order DerivativeOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Order` field describes the order info.
## Batch creation of derivative limit orders
`MsgBatchCreateDerivativeLimitOrders` describes the batch creation of derivative limit orders.
```go theme={null}
type MsgBatchCreateDerivativeLimitOrders struct {
Sender string
Orders []DerivativeOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Orders` field describes the orders info.
## Msg/CreateDerivativeMarketOrder
`MsgCreateDerivativeMarketOrder` is a message to create a derivative market order.
```go theme={null}
// A Cosmos-SDK MsgCreateDerivativeMarketOrder
type MsgCreateDerivativeMarketOrder struct {
Sender string
Order DerivativeOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Order` field describes the order info.
## Msg/CancelDerivativeOrder
`MsgCancelDerivativeOrder` is a message to cancel a derivative order.
```go theme={null}
type MsgCancelDerivativeOrder struct {
Sender string
MarketId string
SubaccountId string
OrderHash string
OrderMask int32
Cid string
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `MarketId` field describes the id of the market where the order is placed.
* `SubaccountId` field describes the subaccount id that placed the order.
* `OrderHash` field describes the hash of the order.
## Msg/BatchCancelDerivativeOrders
`MsgBatchCancelDerivativeOrders` is a message to cancel derivative orders in batch.
```go theme={null}
type MsgBatchCancelDerivativeOrders struct {
Sender string
Data []OrderData
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `Data` field describes the orders to cancel.
## Msg/SubaccountTransfer
`MsgSubaccountTransfer` is a message to transfer balance between sub-accounts.
```go theme={null}
type MsgSubaccountTransfer struct {
Sender string
SourceSubaccountId string
DestinationSubaccountId string
Amount types.Coin
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `SourceSubaccountId` field describes a source subaccount to send coins from.
* `DestinationSubaccountId` field describes a destination subaccount to send coins to.
* `Amount` field describes the amount of coin to send.
## Msg/ExternalTransfer
`MsgExternalTransfer` is a message to transfer balance from one of source account to external sub-account.
```go theme={null}
type MsgExternalTransfer struct {
Sender string
SourceSubaccountId string
DestinationSubaccountId string
Amount types.Coin
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `SourceSubaccountId` field describes a source subaccount to send coins from.
* `DestinationSubaccountId` field describes a destination subaccount to send coins to.
* `Amount` field describes the amount of coin to send.
## Msg/LiquidatePosition
`MsgLiquidatePosition` describes a message to liquidate an account's position
```go theme={null}
type MsgLiquidatePosition struct {
Sender string
SubaccountId string
MarketId string
// optional order to provide for liquidation
Order *DerivativeOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `SubaccountId` field describes a subaccount to receive liquidation amount.
* `MarketId` field describes a market where the position is in.
* `Order` field describes the order info.
## Msg/IncreasePositionMargin
`MsgIncreasePositionMargin` describes a message to increase margin of an account.
```go theme={null}
// A Cosmos-SDK MsgIncreasePositionMargin
type MsgIncreasePositionMargin struct {
Sender string
SourceSubaccountId string
DestinationSubaccountId string
MarketId string
// amount defines the amount of margin to add to the position
Amount math.LegacyDec
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `SourceSubaccountId` field describes a source subaccount to send balance from.
* `DestinationSubaccountId` field describes a destination subaccount to receive balance.
* `MarketId` field describes a market where positions are in.
* `Amount` field describes amount to increase.
## Msg/BatchUpdateOrders
`MsgBatchUpdateOrders` allows for the atomic cancellation and creation of spot and derivative limit orders, along with a new order cancellation mode. Upon execution, order cancellations (if any) occur first, followed by order creations (if any).
```go theme={null}
// A Cosmos-SDK MsgBatchUpdateOrders
// SubaccountId only used for the spot_market_ids_to_cancel_all and derivative_market_ids_to_cancel_all.
type MsgBatchUpdateOrders struct {
Sender string
SubaccountId string
SpotMarketIdsToCancelAll []string
DerivativeMarketIdsToCancelAll []string
SpotOrdersToCancel []OrderData
DerivativeOrdersToCancel []OrderData
SpotOrdersToCreate []SpotOrder
DerivativeOrdersToCreate []DerivativeOrder
}
```
**Fields description**
* `Sender` field describes the creator of this msg.
* `SubaccountId` field describes the sender's sub-account ID.
* `SpotMarketIdsToCancelAll` field describes a list of spot market IDs for which the sender wants to cancel all open orders.
* `DerivativeMarketIdsToCancelAll` field describes a list of derivative market IDs for which the sender wants to cancel all open orders.
* `SpotOrdersToCancel` field describes specific spot orders the sender wants to cancel.
* `DerivativeOrdersToCancel` field describes specific derivative orders the sender wants to cancel.
* `SpotOrdersToCreate` field describes spot orders the sender wants to create.
* `DerivativeOrdersToCreate` field describes derivative orders the sender wants to create.
## Msg/AuthorizeStakeGrants
`MsgAuthorizeStakeGrants` is a message used to grant another address with staked INJ balance for fee discount purposes. It can also be used to revoke/remove grants if the amount granted is set to 0.
```go theme={null}
type MsgAuthorizeStakeGrants struct {
Sender string
Grants []*GrantAuthorization
}
```
**Fields description**
* `Sender` describes the creator of this msg.
* `Grants` describes a list of grantees' addresses and grant amounts
## Msg/ActivateStakeGrant
`MsgActivateStakeGrant` is a message used to select/activate a stake grant for fee discount purposes.
```go theme={null}
type MsgActivateStakeGrant struct {
Sender string
Granter string
}
```
**Fields description**
* `Sender` describes the creator of this msg.
* `Granter` describes the address of the granter.
# Governance Proposals
Source: https://docs.injective.network/developers-native/injective/exchange/06_proposals
# Governance Proposals
## Proposal/SpotMarketParamUpdate
`SpotMarketParamUpdateProposal` defines an SDK message to propose an update of spot market params.
```go theme={null}
type SpotMarketParamUpdateProposal struct {
Title string
Description string
MarketId string
MakerFeeRate *math.LegacyDec
TakerFeeRate *math.LegacyDec
RelayerFeeShareRate *math.LegacyDec
MinPriceTickSize *math.LegacyDec
MinQuantityTickSize *math.LegacyDec
MinNotional *math.LegacyDec
Status MarketStatus
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `MarketId` describes the id of the market to change params.
* `MakerFeeRate` describes the target fee rate for makers.
* `TakerFeeRate` describes the target fee rate for takers.
* `RelayerFeeShareRate` describes the relayer fee share rate.
* `MinPriceTickSize` defines the minimum tick size of the order's price.
* `MinQuantityTickSize` defines the minimum tick size of the order's quantity.
* `Status` describes the target status of the market.
## Proposal/ExchangeEnable
`ExchangeEnableProposal` defines a message to propose enable of specific exchange type.
```go theme={null}
type ExchangeEnableProposal struct {
Title string
Description string
ExchangeType ExchangeType
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `ExchangeType` describes the type of exchange, spot or derivatives.
## Proposal/BatchExchangeModification
`BatchExchangeModificationProposal` defines a message to batch multiple proposals in the exchange module.
```go theme={null}
type BatchExchangeModificationProposal struct {
Title string
Description string
SpotMarketParamUpdateProposal []*SpotMarketParamUpdateProposal
DerivativeMarketParamUpdateProposal []*DerivativeMarketParamUpdateProposal
SpotMarketLaunchProposal []*SpotMarketLaunchProposal
PerpetualMarketLaunchProposal []*PerpetualMarketLaunchProposal
ExpiryFuturesMarketLaunchProposal []*ExpiryFuturesMarketLaunchProposal
TradingRewardCampaignUpdateProposal *TradingRewardCampaignUpdateProposal
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `SpotMarketParamUpdateProposal` describes the SpotMarketParamUpdateProposal.
* `DerivativeMarketParamUpdateProposal` describes the DerivativeMarketParamUpdateProposal.
* `SpotMarketLaunchProposal` describes the SpotMarketLaunchProposal.
* `PerpetualMarketLaunchProposal` describes the PerpetualMarketLaunchProposal.
* `ExpiryFuturesMarketLaunchProposal` describes the ExpiryFuturesMarketLaunchProposal.
* `TradingRewardCampaignUpdateProposal` describes the TradingRewardCampaignUpdateProposal.
## Proposal/SpotMarketLaunch
`SpotMarketLaunchProposal` defines an SDK message for proposing a new spot market through governance.
```go theme={null}
type SpotMarketLaunchProposal struct {
Title string
Description string
Ticker string
BaseDenom string
QuoteDenom string
MinPriceTickSize math.LegacyDec
MinQuantityTickSize math.LegacyDec
MinNotional math.LegacyDec
MakerFeeRate math.LegacyDec
TakerFeeRate math.LegacyDec
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Ticker` describes the ticker for the spot market.
* `BaseDenom` specifies the type of coin to use as the base currency.
* `QuoteDenom` specifies the type of coin to use as the quote currency.
* `MinPriceTickSize` defines the minimum tick size of the order's price.
* `MinQuantityTickSize` defines the minimum tick size of the order's quantity.
* `MakerFeeRate` field describes the trade fee rate for makers on the derivative market.
* `TakerFeeRate` field describes the trade fee rate for takers on the derivative market.
## Proposal/PerpetualMarketLaunch
`PerpetualMarketLaunchProposal` defines an SDK message for proposing a new perpetual futures market through governance.
```go theme={null}
type PerpetualMarketLaunchProposal struct {
Title string
Description string
Ticker string
QuoteDenom string
OracleBase string
OracleQuote string
OracleScaleFactor uint32
OracleType types1.OracleType
InitialMarginRatio math.LegacyDec
MaintenanceMarginRatio math.LegacyDec
MakerFeeRate math.LegacyDec
TakerFeeRate math.LegacyDec
MinPriceTickSize math.LegacyDec
MinQuantityTickSize math.LegacyDec
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Ticker` field describes the ticker for the derivative market.
* `QuoteDenom` field describes the type of coin to use as the base currency.
* `OracleBase` field describes the oracle base currency.
* `OracleQuote` field describes the oracle quote currency.
* `OracleScaleFactor` field describes the scale factor for oracle prices.
* `OracleType` field describes the oracle type.
* `MakerFeeRate` field describes the trade fee rate for makers on the derivative market.
* `TakerFeeRate` field describes the trade fee rate for takers on the derivative market.
* `InitialMarginRatio` field describes the initial margin ratio for the derivative market.
* `MaintenanceMarginRatio` field describes the maintenance margin ratio for the derivative market.
* `MinPriceTickSize` field describes the minimum tick size of the order's price and margin.
* `MinQuantityTickSize` field describes the minimum tick size of the order's quantity.
## Expiry futures market launch proposal
```go theme={null}
// ExpiryFuturesMarketLaunchProposal defines an SDK message for proposing a new expiry futures market through governance
type ExpiryFuturesMarketLaunchProposal struct {
Title string
Description string
// Ticker for the derivative market.
Ticker string
// type of coin to use as the quote currency
QuoteDenom string
// Oracle base currency
OracleBase string
// Oracle quote currency
OracleQuote string
// Scale factor for oracle prices.
OracleScaleFactor uint32
// Oracle type
OracleType types1.OracleType
// Expiration time of the market
Expiry int64
// initial_margin_ratio defines the initial margin ratio for the derivative market
InitialMarginRatio math.LegacyDec
// maintenance_margin_ratio defines the maintenance margin ratio for the derivative market
MaintenanceMarginRatio math.LegacyDec
// maker_fee_rate defines the exchange trade fee for makers for the derivative market
MakerFeeRate math.LegacyDec
// taker_fee_rate defines the exchange trade fee for takers for the derivative market
TakerFeeRate math.LegacyDec
// min_price_tick_size defines the minimum tick size of the order's price and margin
MinPriceTickSize math.LegacyDec
// min_quantity_tick_size defines the minimum tick size of the order's quantity
MinQuantityTickSize math.LegacyDec
// min_notional defines the minimum notional (in quote asset) required for orders in the market
MinNotional math.LegacyDec
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Ticker` field describes the ticker for the derivative market.
* `QuoteDenom` field describes the type of coin to use as the quote currency.
* `OracleBase` field describes the oracle base currency.
* `OracleQuote` field describes the oracle quote currency.
* `OracleScaleFactor` field describes the scale factor for oracle prices.
* `OracleType` field describes the oracle type.
* `Expiry` field describes the expiration time of the market.
* `MakerFeeRate` field describes the trade fee rate for makers on the derivative market.
* `TakerFeeRate` field describes the trade fee rate for takers on the derivative market.
* `InitialMarginRatio` field describes the initial margin ratio for the derivative market.
* `MaintenanceMarginRatio` field describes the maintenance margin ratio for the derivative market.
* `MinPriceTickSize` field describes the minimum tick size of the order's price and margin.
* `MinQuantityTickSize` field describes the minimum tick size of the order's quantity.
## Binary options market launch proposal
```go theme={null}
type BinaryOptionsMarketLaunchProposal struct {
Title string
Description string
// Ticker for the derivative contract.
Ticker string
// Oracle symbol
OracleSymbol string
// Oracle Provider
OracleProvider string
// Oracle type
OracleType types1.OracleType
// Scale factor for oracle prices.
OracleScaleFactor uint32
// expiration timestamp
ExpirationTimestamp int64
// expiration timestamp
SettlementTimestamp int64
// admin of the market
Admin string
// Address of the quote currency denomination for the binary options contract
QuoteDenom string
// maker_fee_rate defines the maker fee rate of a binary options market
MakerFeeRate math.LegacyDec
// taker_fee_rate defines the taker fee rate of a derivative market
TakerFeeRate math.LegacyDec
// min_price_tick_size defines the minimum tick size that the price and margin required for orders in the market
MinPriceTickSize math.LegacyDec
// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
MinQuantityTickSize math.LegacyDec
}
```
## Binary options market param update
```go theme={null}
type BinaryOptionsMarketParamUpdateProposal struct {
Title string
Description string
MarketId string
// maker_fee_rate defines the exchange trade fee for makers for the derivative market
MakerFeeRate *math.LegacyDec
// taker_fee_rate defines the exchange trade fee for takers for the derivative market
TakerFeeRate *math.LegacyDec
// relayer_fee_share_rate defines the relayer fee share rate for the derivative market
RelayerFeeShareRate *math.LegacyDec
// min_price_tick_size defines the minimum tick size of the order's price and margin
MinPriceTickSize *math.LegacyDec
// min_quantity_tick_size defines the minimum tick size of the order's quantity
MinQuantityTickSize *math.LegacyDec
// min_notional defines the minimum notional for orders
MinNotional *math.LegacyDec
// expiration timestamp
ExpirationTimestamp int64
// expiration timestamp
SettlementTimestamp int64
// new price at which market will be settled
SettlementPrice *math.LegacyDec
// admin of the market
Admin string
Status MarketStatus
OracleParams *ProviderOracleParams
}
```
## Proposal/DerivativeMarketParamUpdate
```go theme={null}
type OracleParams struct {
// Oracle base currency
OracleBase string
// Oracle quote currency
OracleQuote string
// Scale factor for oracle prices.
OracleScaleFactor uint32
// Oracle type
OracleType types1.OracleType
}
type DerivativeMarketParamUpdateProposal struct {
Title string
Description string
MarketId string
InitialMarginRatio *math.LegacyDec
MaintenanceMarginRatio *math.LegacyDec
MakerFeeRate *math.LegacyDec
TakerFeeRate *math.LegacyDec
RelayerFeeShareRate *math.LegacyDec
MinPriceTickSize *math.LegacyDec
MinQuantityTickSize *math.LegacyDec
MinNotional *math.LegacyDec
HourlyInterestRate *math.LegacyDec
HourlyFundingRateCap *math.LegacyDec
Status MarketStatus
OracleParams *OracleParams
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `MarketId` describes the id of the market to change params.
* `InitialMarginRatio` describes the target initial margin ratio.
* `MaintenanceMarginRatio` describes the target maintenance margin ratio.
* `MakerFeeRate` describes the target fee rate for makers.
* `TakerFeeRate` describes the target fee rate for takers.
* `RelayerFeeShareRate` describes the relayer fee share rate.
* `MinPriceTickSize` defines the minimum tick size of the order's price.
* `MinQuantityTickSize` defines the minimum tick size of the order's quantity.
* `Status` describes the target status of the market.
* `OracleParams` describes the new oracle parameters.
## Proposal/TradingRewardCampaignLaunch
`TradingRewardCampaignLaunchProposal` defines an SDK message for proposing to launch a new trading reward campaign.
```go theme={null}
type TradingRewardCampaignLaunchProposal struct {
Title string
Description string
CampaignInfo *TradingRewardCampaignInfo
CampaignRewardPools []*CampaignRewardPool
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `CampaignInfo` describes the CampaignInfo.
* `CampaignRewardPools` describes the CampaignRewardPools.
## Proposal/TradingRewardCampaignUpdate
`TradingRewardCampaignUpdateProposal` defines an SDK message for proposing to update an existing trading reward campaign.
```go theme={null}
type TradingRewardCampaignUpdateProposal struct {
Title string
Description string
CampaignInfo *TradingRewardCampaignInfo
CampaignRewardPoolsAdditions []*CampaignRewardPool
CampaignRewardPoolsUpdates []*CampaignRewardPool
}
```
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `CampaignRewardPoolsAdditions` describes the CampaignRewardPoolsAdditions.
* `CampaignRewardPoolsUpdates` describes the CampaignRewardPoolsUpdates.
## Proposal/FeeDiscount
`FeeDiscountProposal` defines an SDK message for proposing to launch or update a fee discount schedule.
```go theme={null}
type FeeDiscountProposal struct {
Title string
Description string
Schedule *FeeDiscountSchedule
}
```
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Schedule` describes the Fee discount schedule.
## Proposal/TradingRewardPendingPointsUpdate
`TradingRewardPendingPointsUpdateProposal` defines an SDK message to update reward points for certain addresses during the vesting period.
```go theme={null}
type TradingRewardPendingPointsUpdateProposal struct {
Title string
Description string
PendingPoolTimestamp int64
RewardPointUpdates *[]RewardPointUpdate
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `PendingPoolTimestamp` describes timestamp of the pending pool.
* `RewardPointUpdates` describes the RewardPointUpdate.
# BeginBlocker
Source: https://docs.injective.network/developers-native/injective/exchange/07_begin_block
# BeginBlocker
The exchange BeginBlocker runs at the start of every block in our defined order as the last module.
### 1. Process Hourly Fundings
1. Check the first to receive funding payments market. If the first market is not yet due to receive fundings (funding timestamp not reached), skip all fundings.
2. Otherwise go through each market one by one:
1. Skip market if funding timestamp is not yet reached.
2. Compute funding as `twap + hourlyInterestRate` where $\mathrm{twap = \frac{cumulativePrice}{timeInterval * 24}}$ with $\mathrm{timeInterval = lastTimestamp - startingTimestamp}$. The `cumulativePrice` is previously calculated with every trade as the time weighted difference between VWAP and mark price: $\mathrm{\frac{VWAP - markPrice}{markPrice} * timeElapsed}$.
3. Cap funding if required to the maximum defined by `HourlyFundingRateCap`.
4. Set next funding timestamp.
5. Emit `EventPerpetualMarketFundingUpdate`.
### 2. Process Markets Scheduled to Settle
For each market in the list of markets to settle:
1. Settle market with zero closing fee and current mark price.
1. Run socialized loss. This will calculate the total amount of funds missing in all of the market and then reduce the payout proportionally for each profitable position. For example a market with a total amount of 100 USDT missing funds and 10 profitable positions with identical quantity would result in a payout reduction of 10 USDT for each of the positions.
2. All positions are forcibly closed.
2. Delete from storage.
### 3. Process Matured Expiry Future Markets
For each time expiry market, iterate through starting with first to expire:
1. If market is premature, stop iteration.
2. If market is disabled, delete market from storage and go to next market.
3. Get cumulative price for the market from oracle.
4. If market is starting maturation, store `startingCumulativePrice` for market.
5. If market is matured, calculate the settlement price as $\mathrm{twap = (currentCumulativePrice - startingCumulativePrice) / twapWindow}$ and add to list of markets to be settled.
6. Settle all matured markets with defined closing fee and settlement price. The procedure is identical to the previous process of settling (see above). Note that the socialized loss is an optional step. In the regular case a market will not require any socialized loss.
7. Delete any settled markets from storage.
### 4. Process Trading Rewards
1. Check if the current trading rewards campaign is finished.
2. If the campaign is finished, distribute reward tokens to eligible traders.
1. Compute the available reward for each reward denom as `min(campaignRewardTokens, communityPoolRewardTokens)`
2. Get the trader rewards based on the trading share from the respective trader calculated as `accountPoints * totalReward / totalTradingRewards`.
3. Send reward tokens from community pool to trader.
4. Reset total and all account trading reward points.
5. Delete the current campaign ending timestamp.
3. If a new campaign is launched, set the next current campaign ending timestamp as `CurrentCampaignStartTimestamp + CampaignDurationSeconds`.
4. If no current campaign is ongoing and no new campaigns are launched, delete campaign info, market qualifications and market multipliers from storage.
### 5. Process Fee Discount Buckets
* If the oldest bucket's end timestamp is older than the `block.timestamp - bucketCount * bucketDuration`:
* Prune the oldest bucket
* Iterate over all `bucketStartTimestamp + account → FeesPaidAmount`:
* Subtract the `FeesPaidAmount` from each account's `totalPastBucketFeesPaidAmount`
* Delete the account's `account → {tier, TTL timestamp}`. Note that this technically isn't necessary for correctness since we check the TTL timestamps in the Endblocker but is a state pruning strategy.
* Update the `CurrBucketStartTimestamp ← CurrBucketStartTimestamp + BucketDuration`.
```
bucket count 5 and with 100 sec duration
120 220 320 420 520 220 320 420 520 620
| | | | | | --> | | | | | |
1 2 3 4 5 1 2 3 4 5
Current block.timestamp of 621:
621 - 5*100 = 121
120 is older than 121, so prune the last bucket and create a new bucket.
```
# EndBlocker
Source: https://docs.injective.network/developers-native/injective/exchange/08_end_block
# EndBlocker
The exchange EndBlocker runs at the end of every block in our defined order after governance and staking modules, and before the peggy, auction and insurance modules. It is particularly important that the governance module's EndBlocker runs before the exchange module's.
* Stage 0: Determine the fee discounts for all the accounts that have placed an order in a fee-discount supported market in the current block.
* Stage 1: Process all market orders in parallel - spot market and derivative market orders
* Markets orders are executed against the resting orderbook at the time of the beginning of the block.
* Note that market orders may be invalidated in the EndBlocker due to subsequently incoming oracle updates or limit order cancels.
* Stage 2: Persist market order execution to store
* Spot Markets
* Persist Spot market order execution data
* Emit relevant events
* `EventBatchSpotExecution`
* Derivative Markets
* Persist Derivative market order execution data
* Emit relevant events
* `EventBatchDerivativeExecution`
* `EventCancelDerivativeOrder`
* Stage 3: Process all limit orders in parallel - spot and derivative limit orders that are matching
* Limit orders are executed in a frequent batch auction mode to ensure fair matching prices, see below for details.
* Note that vanilla limit orders may be invalidated in the EndBlocker due to subsequently incoming oracle updates and reduce-only limit orders may be invalidated in the EndBlocker due to subsequently incoming orders which flip a position.
* Stage 4: Persist limit order matching execution + new limit orders to store
* Spot Markets
* Persist Spot Matching execution data
* Emit relevant events
* `EventNewSpotOrders`
* `EventBatchSpotExecution`
* Derivative Markets
* Persist Derivative Matching execution data
* Emit relevant events
* `EventNewDerivativeOrders`
* `EventBatchDerivativeExecution`
* `EventCancelDerivativeOrder`
* Stage 5: Persist perpetual market funding info
* Stage 6: Persist trading rewards total and account points.
* Stage 7: Persist new fee discount data, i.e., new fees paid additions and new account tiers.
* Stage 8: Process Spot Market Param Updates if any
* Stage 9: Process Derivative Market Param Updates if any
* Stage 10: Emit Deposit and Position Update Events
## Order Matching: Frequent Batch Auction (FBA)
The goal of FBA is to prevent any [Front-Running](https://www.investopedia.com/terms/f/frontrunning.asp). This is achieved by calculating a single clearing price for all matched orders in a given block.
1. Market orders are filled first against the resting orderbook at the time of the beginning of the block. While the resting orders are filled at their respective order prices, the market orders are all filled at a uniform clearing price with the same mechanism as limit orders. For an example for the market order matching in FBA fashion, look at the API docs [here](https://api.injective.exchange/#examples-market-order-matching).
2. Likewise limit orders are filled at a uniform clearing price. New limit orders are combined with the resting orderbook and orders are matched as long as there is still negative spread. The clearing price is either
a. the best buy/sell order in case the last matched order crosses the spread in that direction, the,
b. the mark price in case of derivative markets and the mark price is between the last matched orders or
c. the mid price.
For an example for the limit order matching in FBA fashion, look at the API docs [here](https://api.injective.exchange/#examples-limit-order-matching).
## Single Trade Calculations
* For a qualifying market compute the fee discounts:
* Fee discounts are applied as refunds and the fee paid contribution is recorded.
* Relayer fees are applied AFTER the fee discount is taken.
* For a qualifying market compute the trade reward point contribution:
* Obtain the FeePaidMultiplier for maker and taker.
* Compute the trade reward point contribution.
* Trade reward points are based on the discounted trading fee.
* Calculate fee refunds (or charges). There are several reasons why an order might get a fee refund after matching:
1. It's a limit order which is not matched or only partially matched which means it will become a resting limit order and switch from a taker to maker fee. The refund is `UnmatchedQuantity * (TakerFeeRate - MakerFeeRate)`. Note that for negative maker fees, we refund the `UnmatchedQuantity * TakerFeeRate` instead.
2. Fee discounts are applied. We refund the difference between the original fee paid and the fee paid after the discount.
3. The order is matched at a better price resulting in a different fee.
* For buy orders a better price means a lower price and thus a lower fee. We refund the fee price delta.
* For sell orders a better price means a higher price and thus a higher fee. We charge the fee price delta.
* You can find the respective code with an example [here](https://github.com/InjectiveLabs/injective-core/blob/80dbc4e9558847ff0354be5d19a4d8b0bba7da96/injective-chain/modules/exchange/keeper/derivative_orders_processor.go#L502). Please check the master branch for the latest chain code.
# Events
Source: https://docs.injective.network/developers-native/injective/exchange/09_events
# Events
The exchange module emits the following events:
```proto theme={null}
message EventBatchSpotExecution {
string market_id = 1;
bool is_buy = 2;
ExecutionType executionType = 3;
repeated TradeLog trades = 4;
}
message EventBatchDerivativeExecution {
string market_id = 1;
bool is_buy = 2;
bool is_liquidation = 3;
// nil for time expiry futures
string cumulative_funding = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = true
];
ExecutionType executionType = 5;
repeated DerivativeTradeLog trades = 6;
}
message EventLostFundsFromLiquidation {
string market_id = 1;
bytes subaccount_id = 2;
string lost_funds_from_available_during_payout = 3 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
string lost_funds_from_order_cancels = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
}
message EventBatchDerivativePosition {
string market_id = 1;
repeated SubaccountPosition positions = 2;
}
message EventDerivativeMarketPaused {
string market_id = 1;
string settle_price = 2;
string total_missing_funds = 3;
string missing_funds_rate = 4;
}
message EventBinaryOptionsMarketUpdate {
BinaryOptionsMarket market = 1 [
(gogoproto.nullable) = false
];
}
message EventNewSpotOrders {
string market_id = 1;
repeated SpotLimitOrder buy_orders = 2;
repeated SpotLimitOrder sell_orders = 3;
}
message EventNewDerivativeOrders {
string market_id = 1;
repeated DerivativeLimitOrder buy_orders = 2;
repeated DerivativeLimitOrder sell_orders = 3;
}
message EventCancelSpotOrder {
string market_id = 1;
SpotLimitOrder order = 2 [
(gogoproto.nullable) = false
];
}
message EventSpotMarketUpdate {
SpotMarket market = 1 [
(gogoproto.nullable) = false
];
}
message EventPerpetualMarketUpdate {
DerivativeMarket market = 1 [
(gogoproto.nullable) = false
];
PerpetualMarketInfo perpetual_market_info = 2[
(gogoproto.nullable) = true
];
PerpetualMarketFunding funding = 3[
(gogoproto.nullable) = true
];
}
message EventExpiryFuturesMarketUpdate {
DerivativeMarket market = 1 [
(gogoproto.nullable) = false
];
ExpiryFuturesMarketInfo expiry_futures_market_info = 3[
(gogoproto.nullable) = true
];
}
message EventPerpetualMarketFundingUpdate {
string market_id = 1;
PerpetualMarketFunding funding = 2[
(gogoproto.nullable) = false
];
bool is_hourly_funding = 3;
string funding_rate = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = true
];
string mark_price = 5 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = true
];
}
message EventSubaccountDeposit {
string src_address = 1;
bytes subaccount_id = 2;
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
message EventSubaccountWithdraw {
bytes subaccount_id = 1;
string dst_address = 2;
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
message EventSubaccountBalanceTransfer {
string src_subaccount_id = 1;
string dst_subaccount_id = 2;
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
message EventBatchDepositUpdate {
repeated DepositUpdate deposit_updates = 1;
}
message EventCancelDerivativeOrder {
string market_id = 1;
bool isLimitCancel = 2;
DerivativeLimitOrder limit_order = 3 [
(gogoproto.nullable) = true
];
DerivativeMarketOrderCancel market_order_cancel = 4 [
(gogoproto.nullable) = true
];
}
message EventFeeDiscountSchedule {
FeeDiscountSchedule schedule = 1;
}
message EventTradingRewardCampaignUpdate {
TradingRewardCampaignInfo campaign_info = 1;
repeated CampaignRewardPool campaign_reward_pools = 2;
}
message EventTradingRewardDistribution {
repeated AccountRewards account_rewards = 1;
}
message EventMarketBeyondBankruptcy {
string market_id = 1;
string settle_price = 2;
string missing_market_funds = 3;
}
message EventAllPositionsHaircut {
string market_id = 1;
string settle_price = 2;
string missing_funds_rate = 3;
}
message EventNewConditionalDerivativeOrder {
string market_id = 1;
DerivativeOrder order = 2;
bytes hash = 3;
bool is_market = 4;
}
message EventCancelConditionalDerivativeOrder {
string market_id = 1;
bool isLimitCancel = 2;
DerivativeLimitOrder limit_order = 3 [ (gogoproto.nullable) = true ];
DerivativeMarketOrder market_order = 4 [ (gogoproto.nullable) = true ];
}
message EventConditionalDerivativeOrderTrigger {
string market_id = 1;
bool isLimitTrigger = 2;
bytes triggered_order_hash = 3;
bytes placed_order_hash = 4;
string triggered_order_cid = 5;
}
message EventOrderFail {
bytes account = 1;
repeated bytes hashes = 2;
repeated uint32 flags = 3;
repeated string cids = 4;
}
message EventAtomicMarketOrderFeeMultipliersUpdated {
repeated MarketFeeMultiplier market_fee_multipliers = 1;
}
message EventOrderbookUpdate {
repeated OrderbookUpdate spot_updates = 1;
repeated OrderbookUpdate derivative_updates = 2;
}
message OrderbookUpdate {
uint64 seq = 1;
Orderbook orderbook = 2;
}
message Orderbook {
string market_id = 1;
repeated Level buy_levels = 2;
repeated Level sell_levels = 3;
}
message EventGrantAuthorizations {
string granter = 1;
repeated GrantAuthorization grants = 2;
}
message EventGrantActivation {
string grantee = 1;
string granter = 2;
string amount = 3 [
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
}
message EventInvalidGrant {
string grantee = 1;
string granter = 2;
}
message EventOrderCancelFail {
string market_id = 1;
string subaccount_id = 2;
string order_hash = 3;
string cid = 4;
string description = 5;
}
```
# Parameters
Source: https://docs.injective.network/developers-native/injective/exchange/10_params
# Parameters
The exchange module contains the following parameters:
| Key | Type | Example |
| ------------------------------------------- | -------------- | ------------------ |
| SpotMarketInstantListingFee | sdk.Coin | 100inj |
| DerivativeMarketInstantListingFee | sdk.Coin | 1000inj |
| DefaultSpotMakerFeeRate | math.LegacyDec | 0.1% |
| DefaultSpotTakerFeeRate | math.LegacyDec | 0.2% |
| DefaultDerivativeMakerFeeRate | math.LegacyDec | 0.1% |
| DefaultDerivativeTakerFeeRate | math.LegacyDec | 0.2% |
| DefaultInitialMarginRatio | math.LegacyDec | 5% |
| DefaultMaintenanceMarginRatio | math.LegacyDec | 2% |
| DefaultFundingInterval | int64 | 3600 |
| FundingMultiple | int64 | 3600 |
| RelayerFeeShareRate | math.LegacyDec | 40% |
| DefaultHourlyFundingRateCap | math.LegacyDec | 0.0625% |
| DefaultHourlyInterestRate | math.LegacyDec | 0.000416666% |
| MaxDerivativeOrderSideCount | int64 | 100 |
| InjRewardStakedRequirementThreshold | sdk.Coin | 25inj |
| TradingRewardsVestingDuration | int64 | 1209600 |
| LiquidatorRewardShareRate | math.LegacyDec | 0.05% |
| BinaryOptionsMarketInstantListingFee | sdk.Coin | 10inj |
| AtomicMarketOrderAccessLevel | string | SmartContractsOnly |
| SpotAtomicMarketOrderFeeMultiplier | math.LegacyDec | 2x |
| DerivativeAtomicMarketOrderFeeMultiplier | math.LegacyDec | 2x |
| BinaryOptionsAtomicMarketOrderFeeMultiplier | math.LegacyDec | 2x |
| MinimalProtocolFeeRate | math.LegacyDec | 0.00001% |
| IsInstantDerivativeMarketLaunchEnabled | bool | false |
| PostOnlyModeHeightThreshold | int64 | 1000 |
# MsgPrivilegedExecuteContract
Source: https://docs.injective.network/developers-native/injective/exchange/11_msg_privileged_execute_contract
# MsgPrivilegedExecuteContract
MsgPrivilegedExecuteContract defines a method for executing a Cosmwasm contract from the exchange module with privileged capabilities.
```go theme={null}
type MsgPrivilegedExecuteContract struct {
Sender string
// funds defines the user's bank coins used to fund the execution (e.g. 100inj).
Funds github_com_cosmos_cosmos_sdk_types.Coins
// contract_address defines the contract address to execute
ContractAddress string
// data defines the call data used when executing the contract
Data string
}
```
**Fields description**
* `Sender` describes the creator of this msg.
* `Funds` defines the user's bank coins used to fund the execution (e.g. 100inj).
* `ContractAddress` defines the contract address to execute.
* `Data` defines the call data used when executing the contract, see further details below.
**Contract Interface**
If you want to enable privileged actions on your contract, you must implement the following execute method:
```rust theme={null}
InjectiveExec {
origin: String,
name: String,
args: MyArgs,
}
```
* The `origin` field is the address of the user who sent the privileged action. You don't have to set this field yourself, it will be set by the exchange module.
* The `name` field is the name of the privileged action. You can define these to be whatever you want.
* The `args` field is the arguments of the privileged action. You can define these to be whatever you want.
A complete definition of the Data string in Golang is:
```go theme={null}
type ExecutionData struct {
Origin string `json:"origin"`
Name string `json:"name"`
MyArgs interface{} `json:"args"`
}
```
A user can then call the privileged action by sending a `MsgPrivilegedExecuteContract` with the following data:
```json theme={null}
{
sender: "inj...",
funds: "1000000000000000000inj",
contract_address: "inj...",
data: {
origin: "inj...",
name: "my_privileged_action",
args: {
...
}
}
}
```
**Supported Privileged Actions**
There are currently two supported privileged actions:
```go theme={null}
type PrivilegedAction struct {
SyntheticTrade *SyntheticTradeAction `json:"synthetic_trade"`
PositionTransfer *PositionTransfer `json:"position_transfer"`
}
```
These privileged actions must be set inside the Cosmwasm response data field, e.g.:
```rust theme={null}
let privileged_action = PrivilegedAction {
synthetic_trade: None,
position_transfer: position_transfer_action,
};
response = response.set_data(to_binary(&privileged_action)?);
```
**PositionTransfer**
The position transfer allows a contract to transfer a derivative position from its own subaccount to a user's subaccount. The position may not be liquidable. Solely the receiver pays a taker trading fee deducted from his balances.
Currently only transfers from the contract's subaccount to a user's subaccount are supported.
```go theme={null}
type PositionTransfer struct {
MarketID common.Hash `json:"market_id"`
SourceSubaccountID common.Hash `json:"source_subaccount_id"`
DestinationSubaccountID common.Hash `json:"destination_subaccount_id"`
Quantity math.LegacyDec `json:"quantity"`
}
```
**SyntheticTrade**
The synthetic trade allows a contract to execute a synthetic trade on behalf of a user for derivative markets. This is not touching the orderbook and is purely a synthetic trade. Taker trading fees still apply. The subaccount ids must be set to the contract's subaccount id and the user's subaccount id.
```go theme={null}
type SyntheticTradeAction struct {
UserTrades []*SyntheticTrade `json:"user_trades"`
ContractTrades []*SyntheticTrade `json:"contract_trades"`
}
type SyntheticTrade struct {
MarketID common.Hash `json:"market_id"`
SubaccountID common.Hash `json:"subaccount_id"`
IsBuy bool `json:"is_buy"`
Quantity math.LegacyDec `json:"quantity"`
Price math.LegacyDec `json:"price"`
Margin math.LegacyDec `json:"margin"`
}
```
# Error Codes
Source: https://docs.injective.network/developers-native/injective/exchange/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| -------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| exchange | 1 | failed to validate order |
| exchange | 2 | spot market not found |
| exchange | 3 | spot market exists |
| exchange | 4 | struct field error |
| exchange | 5 | failed to validate market |
| exchange | 6 | subaccount has insufficient deposits |
| exchange | 7 | unrecognized order type |
| exchange | 8 | position quantity insufficient for order |
| exchange | 9 | order hash is not valid |
| exchange | 10 | subaccount id is not valid |
| exchange | 11 | invalid ticker |
| exchange | 12 | invalid base denom |
| exchange | 13 | invalid quote denom |
| exchange | 14 | invalid oracle |
| exchange | 15 | invalid expiry |
| exchange | 16 | invalid price |
| exchange | 17 | invalid quantity |
| exchange | 18 | unsupported oracle type |
| exchange | 19 | order doesnt exist |
| exchange | 20 | spot limit orderbook fill invalid |
| exchange | 21 | perpetual market exists |
| exchange | 22 | expiry futures market exists |
| exchange | 23 | expiry futures market expired |
| exchange | 24 | no liquidity on the orderbook! |
| exchange | 25 | Orderbook liquidity cannot satisfy current worst price |
| exchange | 26 | insufficient margin |
| exchange | 27 | Derivative market not found |
| exchange | 28 | Position not found |
| exchange | 29 | Position direction does not oppose the reduce-only order |
| exchange | 30 | Price Surpasses Bankruptcy Price |
| exchange | 31 | Position not liquidable |
| exchange | 32 | invalid trigger price |
| exchange | 33 | invalid oracle type |
| exchange | 34 | invalid minimum price tick size |
| exchange | 35 | invalid minimum quantity tick size |
| exchange | 36 | invalid minimum order margin |
| exchange | 37 | Exceeds order side count |
| exchange | 38 | Subaccount cannot place a market order when a market order in the same market was already placed in same block |
| exchange | 39 | cannot place a conditional market order when a conditional market order in same relative direction already exists |
| exchange | 40 | An equivalent market launch proposal already exists. |
| exchange | 41 | Invalid Market Status |
| exchange | 42 | base denom cannot be same with quote denom |
| exchange | 43 | oracle base cannot be same with oracle quote |
| exchange | 44 | MakerFeeRate does not match TakerFeeRate requirements |
| exchange | 45 | MaintenanceMarginRatio cannot be greater than InitialMarginRatio |
| exchange | 46 | OracleScaleFactor cannot be greater than MaxOracleScaleFactor |
| exchange | 47 | Spot exchange is not enabled yet |
| exchange | 48 | Derivatives exchange is not enabled yet |
| exchange | 49 | Oracle price delta exceeds threshold |
| exchange | 50 | Invalid hourly interest rate |
| exchange | 51 | Invalid hourly funding rate cap |
| exchange | 52 | Only perpetual markets can update funding parameters |
| exchange | 53 | Invalid trading reward campaign |
| exchange | 54 | Invalid fee discount schedule |
| exchange | 55 | invalid liquidation order |
| exchange | 56 | Unknown error happened for campaign distributions |
| exchange | 57 | Invalid trading reward points update |
| exchange | 58 | Invalid batch msg update |
| exchange | 59 | Post-only order exceeds top of book price |
| exchange | 60 | Order type not supported for given message |
| exchange | 61 | Sender must match dmm account |
| exchange | 62 | already opted out of rewards |
| exchange | 63 | Invalid margin ratio |
| exchange | 64 | Provided funds are below minimum |
| exchange | 65 | Position is below initial margin requirement |
| exchange | 66 | Pool has non-positive total lp token supply |
| exchange | 67 | Passed lp token burn amount is greater than total lp token supply |
| exchange | 68 | unsupported action |
| exchange | 69 | position quantity cannot be negative |
| exchange | 70 | binary options market exists |
| exchange | 71 | binary options market not found |
| exchange | 72 | invalid settlement |
| exchange | 73 | account doesnt exist |
| exchange | 74 | sender should be a market admin |
| exchange | 75 | market is already scheduled to settle |
| exchange | 76 | market not found |
| exchange | 77 | denom decimal cannot be higher than max scale factor |
| exchange | 78 | state is invalid |
| exchange | 79 | transient orders up to cancellation not supported |
| exchange | 80 | invalid trade |
| exchange | 81 | no margin locked in subaccount |
| exchange | 82 | Invalid access level to perform action |
| exchange | 83 | Invalid address |
| exchange | 84 | Invalid argument |
| exchange | 85 | Invalid funds direction |
| exchange | 86 | No funds provided |
| exchange | 87 | Invalid signature |
| exchange | 88 | no funds to unlock |
| exchange | 89 | No msgs provided |
| exchange | 90 | No msg provided |
| exchange | 91 | Invalid amount |
| exchange | 92 | The current feature has been disabled |
| exchange | 93 | Order has too much margin |
| exchange | 94 | Subaccount nonce is invalid |
| exchange | 95 | insufficient funds |
| exchange | 96 | exchange is in post-only mode |
| exchange | 97 | client order id already exists |
| exchange | 98 | client order id is invalid. Max length is 36 chars |
| exchange | 99 | market cannot be settled in emergency mode |
| exchange | 100 | invalid notional |
| exchange | 101 | stale oracle price |
| exchange | 102 | invalid stake grant |
| exchange | 103 | insufficient stake for grant |
| exchange | 104 | invalid permissions |
| exchange | 105 | the decimals specified for the denom is incorrect |
# Exchange
Source: https://docs.injective.network/developers-native/injective/exchange/index
## Abstract
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.
The `exchange` module enables the exchange of tokens on two types of markets:
1. `Derivative Market`: Either a `Perpetual Swap Market` or a `Futures Market`.
2. `Spot Market`
## Contents
1. [Derivative Market Concepts](/developers-native/injective/exchange/00_derivative_market_concepts)
2. [Spot Market Concepts](/developers-native/injective/exchange/01_spot_market_concepts)
3. [Other Concepts](/developers-native/injective/exchange/02_other_concepts)
4. [State](/developers-native/injective/exchange/03_state)
5. [State Transitions](/developers-native/injective/exchange/04_state_transitions)
6. [Messages](/developers-native/injective/exchange/05_messages)
7. [Proposals](/developers-native/injective/exchange/06_proposals)
8. [Begin Block](/developers-native/injective/exchange/07_begin_block)
9. [End Block](/developers-native/injective/exchange/08_end_block)
10. [Events](/developers-native/injective/exchange/09_events)
11. [Params](/developers-native/injective/exchange/10_params)
12. [MsgPrivilegedExecuteContract](/developers-native/injective/exchange/11_msg_privileged_execute_contract)
# Injective
Source: https://docs.injective.network/developers-native/injective/index
## Injective Modules
# State
Source: https://docs.injective.network/developers-native/injective/insurance/01_state
# State
## Params
`Params` is a module-wide configuration structure that stores system parameters and defines overall functioning of the insurance module.
* Params: `Paramsspace("insurance") -> legacy_amino(params)`
```go theme={null}
type Params struct {
// default_redemption_notice_period_duration defines the default minimum notice period duration that must pass after an underwriter sends
// a redemption request before the underwriter can claim his tokens
DefaultRedemptionNoticePeriodDuration time.Duration
}
```
## Insurance Types
`InsuranceFund` defines all the information of the `Insurance Funds` by market.
```go theme={null}
type InsuranceFund struct {
// deposit denomination for the given insurance fund
DepositDenom string
// insurance fund pool token denomination for the given insurance fund
InsurancePoolTokenDenom string
// redemption_notice_period_duration defines the minimum notice period duration that must pass after an underwriter sends
// a redemption request before the underwriter can claim his tokens
RedemptionNoticePeriodDuration time.Duration
// balance of fund
Balance math.Int
// total share tokens minted
TotalShare math.Int
// marketID of the derivative market
MarketId string
// ticker of the derivative market
MarketTicker string
// Oracle base currency of the derivative market
OracleBase string
// Oracle quote currency of the derivative market
OracleQuote string
// Oracle type of the derivative market
OracleType types.OracleType
// Expiration time of the derivative market. Should be -1 for perpetual markets.
Expiry int64
}
```
`RedemptionSchedule` defines redemption schedules from users - redemption is not executed instantly but there's `redemption_notice_period_duration` specified per market.
```go theme={null}
type RedemptionSchedule struct {
// id of redemption schedule
Id uint64
// marketId of redemption schedule
MarketId string
// address of the redeemer
Redeemer string
// the time after which the redemption can be claimed
ClaimableRedemptionTime time.Time
// the insurance_pool_token amount to redeem
RedemptionAmount sdk.Coin
}
```
Additionally, we introduce `next_share_denom_id` and `next_redemption_schedule_id` to manage insurance fund share token
denom and redemption schedules from various users.
```go theme={null}
// GenesisState defines the insurance module's genesis state.
type GenesisState struct {
// params defines all the parameters of related to insurance.
Params Params
InsuranceFunds []InsuranceFund
RedemptionSchedule []RedemptionSchedule
NextShareDenomId uint64
NextRedemptionScheduleId uint64
}
```
## Pending Redemptions
Pending Redemptions Objects are kept to store all the information about redemption requests and to auto-withdraw when
the duration pass.
# Insurance
Source: https://docs.injective.network/developers-native/injective/insurance/index
## Abstract
This paper specifies the insurance module of the Injective Chain.
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.
Each insurance fund grows when positions in its corresponding derivative market are liquidated with positive equity, as half of the positive equity is sent to the insurance fund upon liquidation. When a position with negative equity is liquidated (i.e. the position has surpassed bankruptcy), the insurance fund is utilized to cover the missing equity.
## Contents
1. [State](/developers-native/injective/insurance/01_state)
2. [State Transitions](/developers-native/injective/insurance/02_state_transitions)
3. [Messages](/developers-native/injective/insurance/03_messages)
4. [End Block](/developers-native/injective/insurance/04_end_block)
5. [Events](/developers-native/injective/insurance/05_events)
6. [Params](/developers-native/injective/insurance/06_params)
7. [Future Improvements](/developers-native/injective/insurance/07_future_improvements)
# 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);
```
# Auth
Source: https://docs.injective.network/developers-native/query-chain/auth
Example code snippets to query the auth module on the chain.
## Using gRPC
### Fetch parameters such as max memo characters or tsx signature limit
```ts theme={null}
import { ChainGrpcAuthApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcAuthApi = new ChainGrpcAuthApi(endpoints.grpc);
const moduleParams = await chainGrpcAuthApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch account details associated with an injective address such as the account's address, sequence, or pub\_key
```ts theme={null}
import { ChainGrpcAuthApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcAuthApi = new ChainGrpcAuthApi(endpoints.grpc);
const injectiveAddress = "inj...";
const accountDetailsResponse = await chainGrpcAuthApi.fetchAccount(
injectiveAddress
);
console.log(accountDetailsResponse);
```
### Fetch list of accounts on chain
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcAuthApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcAuthApi = new ChainGrpcAuthApi(endpoints.grpc)
const injectiveAddress = 'inj...'
const pagination = {...} as PaginationOption
const accounts = await chainGrpcAuthApi.fetchAccounts(/* optional pagination params*/)
console.log(accounts)
```
## Using HTTP REST
### Fetch account details associated with an injective address such as the account's address, sequence, or pub\_key
```ts theme={null}
import { ChainRestAuthApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestAuthApi = new ChainRestAuthApi(endpoints.rest);
const injectiveAddress = "inj...";
const accountDetailsResponse = await chainRestAuthApi.fetchAccount(
injectiveAddress
);
console.log(accountDetailsResponse);
```
#### Fetch cosmos address from an injective address
```ts theme={null}
import { ChainRestAuthApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestAuthApi = new ChainRestAuthApi(endpoints.rest);
const injectiveAddress = "inj...";
const cosmosAddress = await chainRestAuthApi.fetchCosmosAccount(
injectiveAddress
);
console.log(cosmosAddress);
```
# Bank
Source: https://docs.injective.network/developers-native/query-chain/bank
Example code snippets to query the chain for bank module related data.
## Using gRPC
### Fetch bank module params
```ts line highlight={2} theme={null}
import { ChainGrpcBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc);
const moduleParams = await chainGrpcBankApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetching injective address's balances
```ts theme={null}
import { ChainGrpcBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc);
const injectiveAddress = "inj...";
const balances = await chainGrpcBankApi.fetchBalances(injectiveAddress);
console.log(balances);
```
### Fetching cosmos address' balances per base denom
```ts theme={null}
import { ChainGrpcBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc);
const cosmosAddress = "cosmos1..."; /* example is using Cosmos Hub */
const denom = "uatom";
const balance = await chainGrpcBankApi.fetchBalance({
accountAddress: cosmosAddress,
denom,
});
console.log(balance);
```
### Fetching total supply on chain
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcBankApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc)
const pagination = {...} as PaginationOption
const totalSupply = await chainGrpcBankApi.fetchTotalSupply(
pagination /* optional pagination parameter */
)
console.log(totalSupply)
```
## Using HTTP REST
### Fetching address's balances
```ts theme={null}
import { ChainRestBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestBankApi = new ChainRestBankApi(endpoints.rest);
const injectiveAddress = "inj...";
const balances = await chainRestBankApi.fetchBalances(injectiveAddress);
console.log(balances);
```
### Fetching cosmos address' balances per base denom
```ts theme={null}
import { ChainRestBankApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestBankApi = new ChainRestBankApi(endpoints.rest);
const cosmosAddress = "cosmos..."; /* example is using Cosmos Hub */
const denom = "uatom";
const balance = await chainRestBankApi.fetchBalance(cosmosAddress, denom);
console.log(balance);
```
# Distribution
Source: https://docs.injective.network/developers-native/query-chain/distribution
Example code snippets to query data related to delegating to validators from the chain.
## Using gRPC
### Fetch parameters such as the base and bonus proposer reward
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcDistributionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcDistributionApi = new ChainGrpcDistributionApi(endpoints.grpc);
const moduleParams = await chainGrpcDistributionApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch the amount and denom of rewards for a delegator delegating to a specific validator
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcDistributionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcDistributionApi = new ChainGrpcDistributionApi(endpoints.grpc);
const delegatorAddress = "inj...";
const validatorAddress = "injvaloper...";
const delegatorRewardsFromValidator =
await chainGrpcDistributionApi.fetchDelegatorRewardsForValidatorNoThrow({
delegatorAddress,
validatorAddress,
});
console.log(delegatorRewardsFromValidator);
```
### Fetch the amount and denom of all rewards for a delegator
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcDistributionApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcDistributionApi = new ChainGrpcDistributionApi(endpoints.grpc);
const delegatorAddress = "inj...";
const totalDelegatorRewards =
await chainGrpcDistributionApi.fetchDelegatorRewardsNoThrow(delegatorAddress);
console.log(totalDelegatorRewards);
```
# Exchange
Source: https://docs.injective.network/developers-native/query-chain/exchange
Example code snippets to query the exchange module on the chain.
## Using gRPC
### Fetch parameters such as the default spot and derivatives fees/trading rewards
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const moduleParams = await chainGrpcExchangeApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch the fee discount schedules
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const feeDiscountSchedule =
await chainGrpcExchangeApi.fetchFeeDiscountSchedule();
console.log(feeDiscountSchedule);
```
### Fetch the fee discounts associated with an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const injectiveAddress = "inj...";
const feeDiscountAccountInfo =
await chainGrpcExchangeApi.fetchFeeDiscountAccountInfo(injectiveAddress);
console.log(feeDiscountAccountInfo);
```
### Fetch the details regarding the trading reward campaign, such as the total reward points
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const tradingRewardsCampaign =
await chainGrpcExchangeApi.fetchTradingRewardsCampaign();
console.log(tradingRewardsCampaign);
```
### Fetch the trading rewards points for an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const injectiveAddress = "inj...";
const tradeRewardsPoints = await chainGrpcExchangeApi.fetchTradeRewardsPoints(
injectiveAddress
);
console.log(tradeRewardsPoints);
```
### Fetch the pending trading rewards points for injective addresses
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const injectiveAddresses = ["inj..."];
const pendingTradeRewardsPoints =
await chainGrpcExchangeApi.fetchPendingTradeRewardPoints(injectiveAddresses);
console.log(pendingTradeRewardsPoints);
```
#### Fetch the current positions, such as subaccountId, marketId, and position
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
// Replace with your own Injective address(es)
const injectiveAddresses = ["inj1..."];
const positions = await chainGrpcExchangeApi.fetchPositions(injectiveAddresses);
console.log(positions);
```
### Fetch the subaccount trade nonce
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcExchangeApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcExchangeApi = new ChainGrpcExchangeApi(endpoints.grpc);
const subaccountId = "0x...";
const subaccountTradeNonce =
await chainGrpcExchangeApi.fetchSubaccountTradeNonce(subaccountId);
console.log(subaccountTradeNonce);
```
# Governance
Source: https://docs.injective.network/developers-native/query-chain/governance
Example code snippets to query the governance module on the chain.
## Using gRPC
### Fetch parameters such as the voting period, max depositing period, or tallying details
```ts theme={null}
import { ChainGrpcGovApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc);
const moduleParams = await chainGrpcGovApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch proposals based on the status
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { ProposalStatusMap } from '@injectivelabs/sdk-ts/client/chain'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcGovApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc)
const status = 3 as ProposalStatusMap[keyof ProposalStatusMap]
const pagination = {...} as PaginationOption
const proposals = await chainGrpcGovApi.fetchProposals({
status,
pagination /* optional pagination params */
})
console.log(proposals)
```
### Fetch proposal details based on a proposal's id
```ts theme={null}
import { ChainGrpcGovApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc);
const proposalId = 123;
const proposalDetails = await chainGrpcGovApi.fetchProposal(proposalId);
console.log(proposalDetails);
```
### Fetch proposal deposits based on a proposal's id.
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcGovApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc)
const proposalId = 123
const pagination = {...} as PaginationOption
const proposalDeposits = await chainGrpcGovApi.fetchProposalDeposits({
proposalId,
pagination /* optiona pagination parameter */
})
console.log(proposalDeposits)
```
### Fetch proposal details based on a proposal's id
```ts theme={null}
import { ChainGrpcGovApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc);
const proposalId = 123;
const proposalDetails = await chainGrpcGovApi.fetchProposal(proposalId);
console.log(proposalDetails);
```
### Fetch proposal deposits based on a proposal's id
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcGovApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc)
const proposalId = 123
const pagination = {...} as PaginationOption
const proposalDeposits = await chainGrpcGovApi.fetchProposalDeposits({
proposalId,
pagination /* optional pagination param */
})
console.log(proposalDeposits)
```
### Fetch proposal votes based on a proposal's id
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcGovApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc)
const proposalId = 123
const proposalVotes = await chainGrpcGovApi.fetchProposalVotes({
proposalId,
pagination: /* optional pagination Options */
})
console.log(proposalVotes)
```
### Fetch proposal tally based on a proposal's id
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcGovApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcGovApi = new ChainGrpcGovApi(endpoints.grpc)
const proposalId = 123
const pagination = {...} as PaginationOption
const proposalTally = await chainGrpcGovApi.fetchProposalTally({
proposalId,
pagination /* optional pagination Options */
})
console.log(proposalTally)
```
# IBC
Source: https://docs.injective.network/developers-native/query-chain/ibc
Example code snippets to query the chain for IBC related data.
## Using gRPC
### Fetch denom trace from the IBC hash
```ts theme={null}
import { ChainGrpcIbcApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcIbcApi = new ChainGrpcIbcApi(endpoints.grpc);
const hash = "...";
const denomTrace = await chainGrpcIbcApi.fetchDenomTrace(hash);
console.log(denomTrace);
```
### Fetch list of denom traces
```ts theme={null}
import { ChainGrpcIbcApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcIbcApi = new ChainGrpcIbcApi(endpoints.grpc);
const denomTraces = await chainGrpcIbcApi.fetchDenomsTrace();
console.log(denomTraces);
```
# 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
# Insurance Funds
Source: https://docs.injective.network/developers-native/query-chain/insurance-funds
Example code snippets to query data related to the insurance fund on chain.
## Using gRPC
### Fetch default redemption notice period duration
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcInsuranceFundApi = new ChainGrpcInsuranceFundApi(endpoints.grpc);
const moduleParams = await chainGrpcInsuranceFundApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch insurance funds and associated metadata
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcInsuranceFundApi = new ChainGrpcInsuranceFundApi(endpoints.grpc);
const insuranceFunds = await chainGrpcInsuranceFundApi.fetchInsuranceFunds();
console.log(insuranceFunds);
```
### Fetch insurance fund and associated metadata based on the market ID
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcInsuranceFundApi = new ChainGrpcInsuranceFundApi(endpoints.grpc);
const marketId = "0x...";
const insuranceFund = await chainGrpcInsuranceFundApi.fetchInsuranceFund(
marketId
);
console.log(insuranceFund);
```
### Fetch estimated redemptions for a given injective address for a market
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcInsuranceFundApi = new ChainGrpcInsuranceFundApi(endpoints.grpc);
const marketId = "0x...";
const injectiveAddress = "inj...";
const estimatedRedemptions =
await chainGrpcInsuranceFundApi.fetchEstimatedRedemptions({
marketId,
address: injectiveAddress,
});
console.log(estimatedRedemptions);
```
### Fetch pending redemptions for a given injective address for a market
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcInsuranceFundApi = new ChainGrpcInsuranceFundApi(endpoints.grpc);
const marketId = "0x...";
const injectiveAddress = "inj...";
const pendingRedemptions =
await chainGrpcInsuranceFundApi.fetchPendingRedemptions({
marketId,
address: injectiveAddress,
});
console.log(pendingRedemptions);
```
# Mint
Source: https://docs.injective.network/developers-native/query-chain/mint
Example code snippets to query the mint module on the chain.
## Using gRPC
### Fetch parameters related to the mint module
```ts theme={null}
import { ChainGrpcMintApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcMintApi = new ChainGrpcMintApi(endpoints.grpc);
const moduleParams = await chainGrpcMintApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch inflation
```ts theme={null}
import { ChainGrpcMintApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcMintApi = new ChainGrpcMintApi(endpoints.grpc);
const inflation = await chainGrpcMintApi.fetchInflation();
console.log(inflation);
```
### Fetch the annual provisions
```ts theme={null}
import { ChainGrpcMintApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcMintApi = new ChainGrpcMintApi(endpoints.grpc);
const annualProvisions = await chainGrpcMintApi.fetchAnnualProvisions();
console.log(annualProvisions);
```
# Oracle
Source: https://docs.injective.network/developers-native/query-chain/oracle
Example code snippets to query the chain via the oracle api.
## Using gRPC
### Fetch parameters related to the oracle
```ts theme={null}
import { ChainGrpcOracleApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcOracleApi = new ChainGrpcOracleApi(endpoints.grpc);
const moduleParams = await chainGrpcOracleApi.fetchModuleParams();
console.log(moduleParams);
```
# Peggy
Source: https://docs.injective.network/developers-native/query-chain/peggy
Example code snippets to query the chain via the peggy api.
## Using gRPC
### Fetch parameters related to peggy
```ts theme={null}
import { ChainGrpcPeggyApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPeggyApi = new ChainGrpcPeggyApi(endpoints.grpc);
const moduleParams = await chainGrpcPeggyApi.fetchModuleParams();
console.log(moduleParams);
```
# Permissions
Source: https://docs.injective.network/developers-native/query-chain/permissions
Example code snippets to query data related to the permissions module on chain.
## Using gRPC
### Fetch all namespaces
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcPermissionsApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPermissionsApi = new ChainGrpcPermissionsApi(endpoints.grpc);
const allNamespaces = await chainGrpcPermissionsApi.fetchAllNamespaces();
console.log(allNamespaces);
```
### Fetch a namespace based on the denom
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcPermissionsApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPermissionsApi = new ChainGrpcPermissionsApi(endpoints.grpc);
const subdenom = "NINJA";
const includeRoles = true;
const namespace = await chainGrpcPermissionsApi.fetchNamespaceByDenom({
subdenom,
includeRoles: includeRoles,
});
console.log(namespace);
```
### Fetch all roles that are associated to an address based on the denom
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcPermissionsApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPermissionsApi = new ChainGrpcPermissionsApi(endpoints.grpc);
const injectiveAddress = "inj...";
const subdenom = "NINJA";
const addressRoles = await chainGrpcPermissionsApi.fetchAddressRoles({
injectiveAddress,
denom: subdenom,
});
console.log(addressRoles);
```
### Fetch all addresses that are associated to a given role for a denom
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcPermissionsApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPermissionsApi = new ChainGrpcPermissionsApi(endpoints.grpc);
const subdenom = "NINJA";
const role = "role";
const addressesByRole = await chainGrpcPermissionsApi.fetchAddressesByRole({
subdenom,
role: role,
});
console.log(addressesByRole);
```
### Fetch vouchers for a given injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcPermissionsApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcPermissionsApi = new ChainGrpcPermissionsApi(endpoints.grpc);
const injectiveAddress = "inj...";
const vouchers = await chainGrpcPermissionsApi.fetchVouchersForAddress(
injectiveAddress
);
console.log(vouchers);
```
# Staking
Source: https://docs.injective.network/developers-native/query-chain/staking
Example code snippets to query the chain's staking module
## Using gRPC
### Fetch parameters related to the staking module such as the unbonding time or bond denom
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcStakingApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc);
const moduleParams = await chainGrpcStakingApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch unbonded and bonded tokens for a pool
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcStakingApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc);
const pool = await chainGrpcStakingApi.fetchPool();
console.log(pool);
```
### Fetch validators and associated metadata
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcStakingApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc);
const validators = await chainGrpcStakingApi.fetchValidators();
console.log(validators);
```
### Fetch validator and associated metadata from a validator address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcStakingApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc);
const validatorAddress = "injvaloper...";
const validator = await chainGrpcStakingApi.fetchValidator(validatorAddress);
console.log(validator);
```
### Fetch delegations associated with a validator
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const validatorAddress = 'injvaloper...'
const pagination = {...} as PaginationOption
const delegations = await chainGrpcStakingApi.fetchValidatorDelegationsNoThrow({
validatorAddress,
pagination /* optional pagination options */
})
console.log(delegations)
```
### Fetch unbonding delegations associated with a validator
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const validatorAddress = 'injvaloper...'
const pagination = {...} as PaginationOption
const unbondingDelegations = await chainGrpcStakingApi.fetchValidatorUnbondingDelegationsNoThrow({
validatorAddress,
pagination /* optional pagination options */
})
console.log(unbondingDelegations)
```
### Fetch delegations associated with an injective address for a specific validator
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcStakingApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc);
const injectiveAddress = "inj...";
const validatorAddress = "injvaloper...";
const delegation = await chainGrpcStakingApi.fetchDelegation({
injectiveAddress,
validatorAddress,
});
console.log(delegation);
```
### Fetch delegations for an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const injectiveAddress = 'inj...'
const pagination = {...} as PaginationOption
const delegations = await chainGrpcStakingApi.fetchDelegationsNoThrow({
injectiveAddress,
pagination /* optional pagination options */
})
console.log(delegations)
```
### Fetch delegators for a validator
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const validatorAddress = 'injvaloper...'
const pagination = {...} as PaginationOption
const delegators = await chainGrpcStakingApi.fetchDelegatorsNoThrow({
validatorAddress,
pagination /* optional pagination options */
})
console.log(delegators)
```
### Fetch unbonding delegations for an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const injectiveAddress = 'inj...'
const pagination = {...} as PaginationOption
const unbondingDelegations = await chainGrpcStakingApi.fetchUnbondingDelegationsNoThrow({
injectiveAddress,
pagination /* optional pagination options */
})
console.log(unbondingDelegations)
```
### Fetch redelegations for an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcStakingApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcStakingApi = new ChainGrpcStakingApi(endpoints.grpc)
const injectiveAddress = 'inj...'
const pagination = {...} as PaginationOption
const unbondingDelegations = await chainGrpcStakingApi.fetchReDelegationsNoThrow({
injectiveAddress,
pagination /* optional pagination options */
})
console.log(unbondingDelegations)
```
# Tendermint
Source: https://docs.injective.network/developers-native/query-chain/tendermint
Example code snippets to query for chain node related data.
## Using HTTP REST
### Fetch the latest block info
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainRestTendermintApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestTendermintApi = new ChainRestTendermintApi(endpoints.rest);
const latestBlock = await chainRestTendermintApi.fetchLatestBlock();
console.log(latestBlock);
```
### Fetch chain node info
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainRestTendermintApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainRestTendermintApi = new ChainRestTendermintApi(endpoints.rest);
const nodeInfo = await chainRestTendermintApi.fetchNodeInfo();
console.log(nodeInfo);
```
# Token Factory
Source: https://docs.injective.network/developers-native/query-chain/token-factory
Example code snippets to query the chain for token factory module related data.
## Using gRPC
### Fetch all denoms created by *creator*
```ts lines highlight={1} theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcTokenFactoryApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcTokenFactoryApi = new ChainGrpcTokenFactoryApi(endpoints.grpc);
const creator = "inj...";
const denoms = await chainGrpcTokenFactoryApi.fetchDenomsFromCreator(creator);
console.log(denoms);
```
### Fetch denom authority metadata (i.e fetch admin of a token)
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcTokenFactoryApi } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcTokenFactoryApi = new ChainGrpcTokenFactoryApi(endpoints.grpc);
const creator = "inj...";
const subdenom = "NINJA";
const metadata = await chainGrpcTokenFactoryApi.fetchDenomAuthorityMetadata(
creator,
subdenom
);
console.log(metadata);
```
# Wasm
Source: https://docs.injective.network/developers-native/query-chain/wasm
Example code snippets to query the wasm module on chain
## Using gRPC
### Fetch contacts' account balance Note that pagination parameters can be passed to obtain additional accounts.
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { ChainGrpcWasmApi, PaginationOption } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc)
const contractAddress = 'inj...'
const pagination = {...} as PaginationOption
const contractAccountsBalance = await chainGrpcWasmApi.fetchContractAccountsBalance({
contractAddress,
pagination /* optional pagination options */
})
console.log(contractAccountsBalance)
```
### Fetch info related to a contract
```ts theme={null}
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const contractAddress = "inj...";
const contractInfo = await chainGrpcWasmApi.fetchContractInfo(contractAddress);
console.log(contractInfo);
```
### Fetch contract history
```ts theme={null}
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const contractAddress = "inj...";
const contractHistory = await chainGrpcWasmApi.fetchContractHistory(
contractAddress
);
console.log(contractHistory);
```
### Fetch the state of a smart contract
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { ChainGrpcWasmApi, toBase64 } from "@injectivelabs/sdk-ts/client/chain";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const contractAddress = "inj...";
const query = "...";
const queryFromObject = toBase64({ get_coin: {} });
const contractState = await chainGrpcWasmApi.fetchSmartContractState({
contractAddress,
query /* optional string query - HAS to be in base64 or use queryFromObject */,
});
console.log(contractState);
```
### Fetch the raw state of a smart contract
```ts theme={null}
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const contractAddress = "inj...";
const query = "...";
const queryFromObject = toBase64({ get_coin: {} });
const rawContractState = await chainGrpcWasmApi.fetchRawContractState({
contractAddress,
query /* optional string query - HAS to be in base64 or use queryFromObject */,
});
console.log(rawContractState);
```
### Fetch the codes associated with a contract
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { ChainGrpcWasmApi } from '@injectivelabs/sdk-ts/client/chain'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc)
const pagination = {...} as PaginationOption
const rawContractState = await chainGrpcWasmApi.fetchRawContractState(
pagination /* optional pagination options */
)
console.log(rawContractState)
```
### Fetch info associated with a contract code
```ts theme={null}
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc);
const codeId = 1;
const codeDetails = await chainGrpcWasmApi.fetchContractCode(codeId);
console.log(codeDetails);
```
### Fetch the contracts associated with a code
```ts theme={null}
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { ChainGrpcWasmApi } from '@injectivelabs/sdk-ts/client/chain'
const endpoints = getNetworkEndpoints(Network.Testnet)
const chainGrpcWasmApi = new ChainGrpcWasmApi(endpoints.grpc)
const codeId = 1
const pagination = {...} as PaginationOption
const contracts = await chainGrpcWasmApi.fetchContractCodeContracts({
codeId,
pagination /* optional pagination options */
})
console.log(contracts)
```
# WasmX
Source: https://docs.injective.network/developers-native/query-chain/wasmx
Example code snippets to query the wasmX module on chain
## Using gRPC
### Fetch parameters related to the wasmX module
```ts theme={null}
import { ChainGrpcWasmXApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmXApi = new ChainGrpcWasmXApi(endpoints.grpc);
const moduleParams = await chainGrpcWasmXApi.fetchModuleParams();
console.log(moduleParams);
```
### Fetch the wasmX module state
```ts theme={null}
import { ChainGrpcWasmXApi } from "@injectivelabs/sdk-ts/client/chain";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const chainGrpcWasmXApi = new ChainGrpcWasmXApi(endpoints.grpc);
const moduleState = await chainGrpcWasmXApi.fetchModuleState();
console.log(moduleState);
```
# Querying GraphQL Endpoints
Source: https://docs.injective.network/developers-native/query-ethereum
This guide shows how to query GraphQL endpoints using `HttpClient` from `@injectivelabs/utils`.
## Setup
```ts theme={null}
import { HttpClient } from '@injectivelabs/utils'
const client = new HttpClient('YOUR_GRAPHQL_ENDPOINT')
```
## Authentication
If your GraphQL endpoint requires authentication, set the headers:
```ts theme={null}
client.setConfig({
headers: {
authorization: 'Bearer YOUR_API_KEY'
}
})
```
## Making a Query
Structure your GraphQL query as a JSON string with `query` and optional `variables`:
```ts theme={null}
const query = JSON.stringify({
query: `
query GetData($id: ID!) {
entity(id: $id) {
id
name
value
}
}
`,
variables: {
id: '123'
}
})
const response = await client.post('', query)
console.log(response.data.data)
```
## Complete Example
```ts theme={null}
import { HttpClient } from '@injectivelabs/utils'
interface Token {
id: string
symbol: string
name: string
}
interface TokensResponse {
tokens: Token[]
}
const client = new HttpClient('YOUR_GRAPHQL_ENDPOINT')
// Set auth headers if required
client.setConfig({
headers: {
authorization: 'Bearer YOUR_API_KEY'
}
})
const query = JSON.stringify({
query: `
query GetTokens($first: Int!) {
tokens(first: $first, orderBy: symbol) {
id
symbol
name
}
}
`,
variables: {
first: 10
}
})
const response = await client.post('', query)
console.log(response.data.data.tokens)
```
## Error Handling
GraphQL responses may contain errors even with a 200 status code. Always check for errors:
```ts theme={null}
interface GraphQLResponse {
data?: T
errors?: Array<{ message: string }>
}
const response = await client.post }>('', query)
if (response.data.errors && response.data.errors.length > 0) {
throw new Error(response.data.errors[0].message)
}
const data = response.data.data
```
# Account
Source: https://docs.injective.network/developers-native/query-indexer-stream/account
Example code snippets to stream from the indexer for account module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream subaccount balance
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcAccountStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcAccountStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'subaccount-balance',
streamFactory: () => stream.streamSubaccountBalance({
subaccountId: '0x...',
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (balance) => {
console.log(balance)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Auction
Source: https://docs.injective.network/developers-native/query-indexer-stream/auction
Example code snippets to stream from the indexer for auction module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream auction bids
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcAuctionStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcAuctionStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'auction-bids',
streamFactory: () => stream.streamBids({
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (bids) => {
console.log(bids)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Derivatives
Source: https://docs.injective.network/developers-native/query-indexer-stream/derivatives
Example code snippets to stream from the indexer for derivatives market module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream orderbook updates
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-orderbook-updates',
streamFactory: () => stream.streamOrderbookUpdates({
marketIds: ['0x...'],
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderbookUpdates) => {
console.log(orderbookUpdates)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream derivative orders
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { OrderSide } from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-orders',
streamFactory: () => stream.streamOrders({
marketId: '0x...',
subaccountId: '0x...', // optional
orderSide: OrderSide.Buy, // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orders) => {
console.log(orders)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream derivative order history
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
TradeDirection,
TradeExecutionType,
} from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-order-history',
streamFactory: () => stream.streamOrdersHistory({
marketId: '0x...', // optional
subaccountId: '0x...', // optional
direction: TradeDirection.Buy, // optional
executionTypes: [TradeExecutionType.Market], // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderHistory) => {
console.log(orderHistory)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream derivative trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection } from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-trades',
streamFactory: () => stream.streamTrades({
marketIds: ['0x...'], // optional
subaccountId: '0x...', // optional
direction: TradeDirection.Buy, // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (trades) => {
console.log(trades)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream derivative positions
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-positions',
streamFactory: () => stream.streamPositions({
marketId: '0x...', // optional
subaccountId: '0x...', // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (positions) => {
console.log(positions)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream derivative markets
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcDerivativesStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcDerivativesStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'derivatives-markets',
streamFactory: () => stream.streamMarkets({
marketIds: ['0x...'], // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (markets) => {
console.log(markets)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Explorer
Source: https://docs.injective.network/developers-native/query-indexer-stream/explorer
Example code snippets to stream from the indexer for explorer module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream blocks
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcExplorerStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcExplorerStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'explorer-blocks',
streamFactory: () => stream.streamBlocks({
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (blocks) => {
console.log(blocks)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream blocks with transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcExplorerStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcExplorerStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'explorer-blocks-with-txs',
streamFactory: () => stream.streamBlocksWithTxs({
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (blocksWithTransactions) => {
console.log(blocksWithTransactions)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcExplorerStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcExplorerStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'explorer-transactions',
streamFactory: () => stream.streamTransactions({
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (transactions) => {
console.log(transactions)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Streaming the Indexer
Source: https://docs.injective.network/developers-native/query-indexer-stream/index
This section covers how to stream real-time data from the Injective Indexer API using StreamManagerV2.
## StreamManagerV2
StreamManagerV2 provides an event-based architecture for managing gRPC streams with automatic retry, exponential backoff, and comprehensive error handling.
### Key Features
* **Event-based lifecycle** - Listen to connect, disconnect, error, and data events
* **Automatic retry** - Configurable exponential backoff with retry limits
* **Error handling** - Distinguishes retryable vs non-retryable errors
* **Persistent mode** - Continue retrying indefinitely after max attempts
* **Fine-grained control** - Start, stop, and manage stream lifecycle
### Basic Usage
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcSpotStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcSpotStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'my-stream',
streamFactory: () => stream.streamOrders({
marketId: '0x...',
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (data) => {
console.log(data)
},
retryConfig: {
enabled: true,
maxAttempts: 5,
initialDelayMs: 1000,
maxDelayMs: 30000,
backoffMultiplier: 2,
persistent: true
}
})
// Event listeners
streamManager.on('connect', () => console.log('Connected'))
streamManager.on('disconnect', (reason) => console.log('Disconnected:', reason))
streamManager.on('error', (error) => console.error('Error:', error))
streamManager.on('stateChange', ({ from, to }) => console.log(`State: ${from} -> ${to}`))
// Start/stop
streamManager.start()
streamManager.stop()
```
### Available Stream Classes
* `IndexerGrpcAccountStreamV2` - Account balance and transaction streams
* `IndexerGrpcAccountPortfolioStreamV2` - Portfolio value streams
* `IndexerGrpcArchiverStreamV2` - Archiver data streams
* `IndexerGrpcAuctionStreamV2` - Auction bid streams
* `IndexerGrpcDerivativesStreamV2` - Derivatives market streams
* `IndexerGrpcExplorerStreamV2` - Blockchain explorer streams
* `IndexerGrpcMitoStreamV2` - Mito vault streams
* `IndexerGrpcOracleStreamV2` - Oracle price feed streams
* `IndexerGrpcSpotStreamV2` - Spot market streams
* `IndexerGrpcTradingStreamV2` - Trading automation streams
### Retry Configuration
```ts theme={null}
retryConfig: {
enabled: true, // Enable/disable retry
maxAttempts: 5, // Max retry attempts (0 = unlimited)
initialDelayMs: 1000, // Initial backoff delay
maxDelayMs: 30000, // Maximum backoff delay
backoffMultiplier: 2, // Exponential backoff multiplier
persistent: true // Continue with max delay after maxAttempts
}
```
### Event Types
* `connect` - Stream successfully connected
* `disconnect` - Stream disconnected with reason
* `error` - Stream error occurred
* `data` - New data received
* `stateChange` - Stream state changed
* `retry` - Retry attempt started
* `warn` - Warning message
## Stream Examples
* [Account](/developers-native/query-indexer-stream/account/) - Stream account updates
* [Archiver](/developers-native/query-indexer-stream/archiver/) - Stream archiver data
* [Auction](/developers-native/query-indexer-stream/auction/) - Stream auction updates
* [Derivatives](/developers-native/query-indexer-stream/derivatives/) - Stream derivatives market updates
* [Explorer](/developers-native/query-indexer-stream/explorer/) - Stream explorer updates
* [Mito](/developers-native/query-indexer-stream/mito/) - Stream Mito vault updates
* [Oracle](/developers-native/query-indexer-stream/oracle/) - Stream oracle price updates
* [Portfolio](/developers-native/query-indexer-stream/portfolio/) - Stream portfolio updates
* [Spot](/developers-native/query-indexer-stream/spot/) - Stream spot market updates
* [Trading](/developers-native/query-indexer-stream/trading/) - Stream trading automation updates
# Oracle
Source: https://docs.injective.network/developers-native/query-indexer-stream/oracle
Example code snippets to stream from the indexer for oracle module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream oracle prices
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcOracleStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcOracleStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'oracle-prices',
streamFactory: () => stream.streamOraclePrices({
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (oraclePrices) => {
console.log(oraclePrices)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream oracle prices by markets
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcOracleStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcOracleStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'oracle-prices-by-markets',
streamFactory: () => stream.streamOraclePricesByMarkets({
marketIds: ['0x...'],
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (oraclePrices) => {
console.log(oraclePrices)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Portfolio
Source: https://docs.injective.network/developers-native/query-indexer-stream/portfolio
Example code snippets to stream from the indexer for portfolio module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream an account's portfolio
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcAccountPortfolioStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcAccountPortfolioStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'account-portfolio',
streamFactory: () => stream.streamAccountPortfolio({
accountAddress: 'inj...',
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (portfolioResults) => {
console.log(portfolioResults)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Spot
Source: https://docs.injective.network/developers-native/query-indexer-stream/spot
Example code snippets to stream from the indexer for spot market module related data using StreamManagerV2.
## Using gRPC Stream with StreamManagerV2
### Stream orderbook updates
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
StreamManagerV2,
IndexerGrpcSpotStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcSpotStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'spot-orderbook-updates',
streamFactory: () => stream.streamOrderbookUpdates({
marketIds: ['0x...'],
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderbookUpdates) => {
console.log(orderbookUpdates)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream spot orders
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { OrderSide } from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcSpotStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcSpotStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'spot-orders',
streamFactory: () => stream.streamOrders({
marketId: '0x...',
subaccountId: '0x...', // optional
orderSide: OrderSide.Buy, // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orders) => {
console.log(orders)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream spot order history
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import {
TradeDirection,
TradeExecutionType,
} from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcSpotStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcSpotStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'spot-order-history',
streamFactory: () => stream.streamOrderHistory({
marketId: '0x...', // optional
subaccountId: '0x...', // optional
direction: TradeDirection.Buy, // optional
executionTypes: [TradeExecutionType.Market], // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (orderHistory) => {
console.log(orderHistory)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
### Stream spot trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection } from '@injectivelabs/sdk-ts/types'
import {
StreamManagerV2,
IndexerGrpcSpotStreamV2
} from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const stream = new IndexerGrpcSpotStreamV2(endpoints.indexer)
const streamManager = new StreamManagerV2({
id: 'spot-trades',
streamFactory: () => stream.streamTrades({
marketIds: ['0x...'], // optional
subaccountId: '0x...', // optional
direction: TradeDirection.Buy, // optional
callback: (response) => {
streamManager.emit('data', response)
}
}),
onData: (trades) => {
console.log(trades)
},
retryConfig: { enabled: true }
})
streamManager.on('connect', () => console.log('Stream connected'))
streamManager.start()
```
# Account
Source: https://docs.injective.network/developers-native/query-indexer/account
Example code snippets to query the indexer for subaccount related data.
## Using gRPC
### Fetch user's portfolio details
This includes available balance, unrealized Pnl, and portfolio value. Note: **deprecated** → use [Portfolio](../query-indexer/portfolio/#using-grpc) instead
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const injectiveAddress = "inj...";
const portfolio = await indexerGrpcAccountApi.fetchPortfolio(injectiveAddress);
console.log(portfolio);
```
### Fetch user's trading rewards per epoch
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const injectiveAddress = "inj...";
const epoch = -1; // current epoch
const tradingRewards = await indexerGrpcAccountApi.fetchRewards({
address: injectiveAddress,
epoch,
});
console.log(tradingRewards);
```
### Fetch subaccounts associated with an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const injectiveAddress = "inj...";
const subaccountsList = await indexerGrpcAccountApi.fetchSubaccountsList(
injectiveAddress
);
console.log(subaccountsList);
```
### Fetch balance of a subaccount for a specific denom
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const subaccountId = "0x...";
const denom = "inj";
const subaccountBalance = await indexerGrpcAccountApi.fetchSubaccountBalance(
subaccountId,
denom
);
console.log(subaccountBalance);
```
### Fetch of balances for a subaccount
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const subaccountId = "0x...";
const subaccountBalanceList =
await indexerGrpcAccountApi.fetchSubaccountBalancesList(subaccountId);
console.log(subaccountBalanceList);
```
### Fetch subaccount history
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcAccountApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer)
const subaccountId = '0x...'
const denom = 'inj'
const pagination = {...} as PaginationOption
const subaccountHistory = await indexerGrpcAccountApi.fetchSubaccountHistory({
subaccountId,
denom,
pagination /* optional param */
})
console.log(subaccountHistory)
```
### Fetch a summary of a subaccount's orders
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const subaccountId = "0x...";
const marketId = "0x";
const orderDirection = "buy";
const orderSummary = await indexerGrpcAccountApi.fetchSubaccountOrderSummary({
subaccountId,
marketId,
orderDirection,
});
console.log(orderSummary);
```
### Fetch states of spot or (and) derivatives orders
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAccountApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAccountApi = new IndexerGrpcAccountApi(endpoints.indexer);
const spotOrderHashes = ["0x..."];
const derivativeOrderHashes = ["0x..."];
const orderStates = await indexerGrpcAccountApi.fetchOrderStates({
spotOrderHashes,
derivativeOrderHashes,
});
console.log(orderStates);
```
# Auction
Source: https://docs.injective.network/developers-native/query-indexer/auction
Example code snippets to query the indexer for auction module related data.
## Using gRPC
### Fetch auction based off the round
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAuctionApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAuctionApi = new IndexerGrpcAuctionApi(endpoints.indexer);
const round = 1;
const auction = await indexerGrpcAuctionApi.fetchAuction(round);
console.log(auction);
```
### Fetch auctions
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcAuctionApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcAuctionApi = new IndexerGrpcAuctionApi(endpoints.indexer);
const auction = await indexerGrpcAuctionApi.fetchAuctions();
console.log(auction);
```
# Derivatives
Source: https://docs.injective.network/developers-native/query-indexer/derivatives
Example code snippets to query the indexer for derivative module related data.
## Using gRPC
### Fetch markets
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const markets = await indexerGrpcDerivativesApi.fetchMarkets();
console.log(markets);
```
### Fetch market based on a market id
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const marketId = "0x...";
const market = await indexerGrpcDerivativesApi.fetchMarket(marketId);
console.log(market);
```
### Fetch binary options markets
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const binaryOptionsMarket =
await indexerGrpcDerivativesApi.fetchBinaryOptionsMarkets();
console.log(binaryOptionsMarket);
```
### Fetch binary options market based on market id
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const marketId = "0x...";
const binaryOptionsMarket =
await indexerGrpcDerivativesApi.fetchBinaryOptionsMarket(marketId);
console.log(binaryOptionsMarket);
```
### Fetch a market's orderbook based on market id
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const marketId = "0x...";
const orderbook = await indexerGrpcDerivativesApi.fetchOrderbookV2(marketId);
console.log(orderbook);
```
### Fetch a market's orders
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { OrderSide } from '@injectivelabs/sdk-ts/types'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const orderSide = OrderSide.Buy /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const orders = await indexerGrpcDerivativesApi.fetchOrders({
marketId,
orderSide,
subaccountId,
pagination
})
console.log(orders)
```
### Fetch a market's order history
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { OrderSide, TradeDirection, PaginationOption, TradeExecutionType } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketIds = ['0x...'] /* optional param */
const executionTypes = [TradeExecutionType.Market] /* optional param */
const orderTypes = OrderSide.StopBuy /* optional param */
const direction = TradeDirection.Buy /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const orderHistory = await indexerGrpcDerivativesApi.fetchOrderHistory({
marketIds,
executionTypes,
orderTypes,
direction,
subaccountId,
pagination
})
console.log(orderHistory)
```
### Fetch a market's positions
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection, PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketIds = ['0x...'] /* optional param */
const direction = TradeDirection.Buy /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const positions = await indexerGrpcDerivativesApi.fetchPositions({
marketIds,
direction,
subaccountId,
pagination
})
console.log(positions)
```
### Fetch a market's trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection, PaginationOption, TradeExecutionType } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const executionTypes = [TradeExecutionType.Market] /* optional param */
const direction = TradeDirection.Buy /* optional param */
const subaccountId = '0x...'/* optional param */
const pagination = {...} as PaginationOption /* optional param */
const trades = await indexerGrpcDerivativesApi.fetchTrades({
marketId,
executionTypes,
direction,
subaccountId,
pagination
})
console.log(trades)
```
### Fetch funding payments for a market
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketIds = ['0x...'] /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const fundingPayments = await indexerGrpcDerivativesApi.fetchFundingPayments({
marketIds,
pagination
})
console.log(fundingPayments)
```
### Fetch funding rates for a market
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const fundingRates = await indexerGrpcDerivativesApi.fetchFundingRates({
marketId,
pagination
})
console.log(fundingRates)
```
### Fetch subaccount orders
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const subaccountOrders = await indexerGrpcDerivativesApi.fetchSubaccountOrdersList({
marketId,
subaccountId,
pagination
})
console.log(subaccountOrders)
```
### Fetch subaccount trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection, TradeExecutionType, PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcDerivativesApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const subaccountId = '0x...' /* optional param */
const executionType = TradeExecutionType.LimitFill /* optional param */
const direction = TradeDirection.Sell /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const subaccountTrades = await indexerGrpcDerivativesApi.fetchSubaccountTradesList({
marketId,
subaccountId,
executionType,
direction,
pagination
})
console.log(subaccountTrades)
```
### Fetch orderbooks for multiple markets
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const marketIds = ["0x..."];
const orderbooks = await indexerGrpcDerivativesApi.fetchOrderbooksV2(marketIds);
console.log(orderbooks);
```
### Fetch orderbook for a market
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcDerivativesApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(
endpoints.indexer
);
const marketId = "0x...";
const orderbook = await indexerGrpcDerivativesApi.fetchOrderbookV2(marketId);
console.log(orderbook);
```
## Using HTTP REST
### Fetch market summary, such as a history of prices and 24 hr volume
```ts theme={null}
import { IndexerRestDerivativesChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestDerivativesChronosApi = new IndexerRestDerivativesChronosApi(
`${endpoints.chronos}/api/chronos/v1/derivative`
);
const marketId = "0x...";
const marketSummary = await indexerRestDerivativesChronosApi.fetchMarketSummary(
marketId
);
console.log(marketSummary);
```
### Fetch all markets' summaries, such as a history of prices and 24 hr volume
```ts theme={null}
import { IndexerRestDerivativesChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestDerivativesChronosApi = new IndexerRestDerivativesChronosApi(
`${endpoints.chronos}/api/chronos/v1/derivative`
);
const marketSummaries =
await indexerRestDerivativesChronosApi.fetchMarketsSummary(marketId);
console.log(marketSummaries);
```
# Explorer
Source: https://docs.injective.network/developers-native/query-indexer/explorer
Example code snippets to query the indexer for explorer module related data.
## Using gRPC
### Fetch transaction by hash
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const txsHash = "...";
const transaction = await indexerGrpcExplorerApi.fetchTxByHash(txsHash);
console.log(transaction);
```
### Fetch an account transaction by address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const injectiveAddress = "inj...";
const account = await indexerGrpcExplorerApi.fetchAccountTx({
injectiveAddress,
});
console.log(account);
```
### Fetch a validator by address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const validatorAddress = "injvaloper...";
const validator = await indexerGrpcExplorerApi.fetchValidator(validatorAddress);
console.log(validator);
```
### Fetch a validator's uptime by address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const validatorAddress = "injvaloper...";
const validatorUptime = await indexerGrpcExplorerApi.fetchValidatorUptime(
validatorAddress
);
console.log(validatorUptime);
```
### Fetch a validator's uptime by address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const validatorAddress = "injvaloper...";
const validatorUptime = await indexerGrpcExplorerApi.fetchValidatorUptime(
validatorAddress
);
console.log(validatorUptime);
```
### Fetch Peggy deposit transactions from Ethereum
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const sender = "0x..."; /* optional parameter */
const receiver = "inj..."; /* optional parameter */
const limit = 100; /* optional pagination parameter */
const skip = 20; /* optional pagination parameter */
const peggyDeposits = await indexerGrpcExplorerApi.fetchPeggyDepositTxs({
sender,
receiver,
limit,
skip,
});
console.log(peggyDeposits);
```
### Fetch Peggy withdrawal transactions to Ethereum
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const receiver = "0x..."; /* optional parameter */
const sender = "inj..."; /* optional parameter */
const limit = 100; /* optional pagination parameter */
const skip = 20; /* optional pagination parameter */
const peggyWithdrawals = await indexerGrpcExplorerApi.fetchPeggyWithdrawalTxs({
sender,
receiver,
limit,
skip,
});
console.log(peggyWithdrawals);
```
### Fetch blocks
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const after = 30; /* optional pagination parameter */
const limit = 100; /* optional pagination parameter */
const blocks = await indexerGrpcExplorerApi.fetchBlocks({
after,
limit,
});
console.log(blocks);
```
### Fetch block by height
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const height = 123456;
const block = await indexerGrpcExplorerApi.fetchBlock(height);
console.log(block);
```
### Fetch transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const after = 20; /* optional pagination parameter */
const limit = 100; /* optional pagination parameter */
const transactions = await indexerGrpcExplorerApi.fetchTxs({
after,
limit,
});
console.log(transactions);
```
### Fetch IBC transfer transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerGrpcExplorerApi(endpoints.explorer);
const sender = "osmo...";
const receiver = "inj...";
const ibcTransactions = await indexerGrpcExplorerApi.fetchIBCTransferTxs({
sender,
receiver,
});
console.log(ibcTransactions);
```
## Using HTTP REST
### Fetch a block and details
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const blockHashHeight = 1;
const block = await indexerRestExplorerApi.fetchBlock(blockHashHeight);
console.log(block);
```
### Fetch blocks and details
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const before = 200; /* optional pagination param */
const limit = 100; /* optional pagination param */
const blocks = await indexerRestExplorerApi.fetchBlocks({
before,
limit,
});
console.log(blocks);
```
### Fetch blocks with transaction details
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const before = 200; /* optional pagination param */
const limit = 100; /* optional pagination param */
const blocks = await indexerRestExplorerApi.fetchBlocksWithTx({
before,
limit,
});
console.log(blocks);
```
### Fetch transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const after = 200; /* optional pagination param */
const limit = 100; /* optional pagination param */
const fromNumber = 1; /* optional param */
const toNumber = 100; /* optional param */
const transactions = await indexerRestExplorerApi.fetchTransactions({
after,
limit,
fromNumber,
toNumber,
});
console.log(transactions);
```
### Fetch transactions for an address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const account = "inj...";
const after = 200; /* optional pagination param */
const limit = 100; /* optional pagination param */
const fromNumber = 1; /* optional param */
const toNumber = 100; /* optional param */
const accountTransactions =
await indexerRestExplorerApi.fetchAccountTransactions({
account,
params: {
account,
after,
limit,
fromNumber,
toNumber,
},
});
console.log(accountTransactions);
```
### Fetch transaction using transaction hash
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const txsHash = "...";
const transaction = await indexerRestExplorerApi.fetchTransaction(txsHash);
console.log(transaction);
```
### Fetch validators
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const validators = await indexerRestExplorerApi.fetchValidators();
console.log(validators);
```
### Fetch validator uptime
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const validatorAddress = "injvalcons";
const validatorUptime = await indexerRestExplorerApi.fetchValidatorUptime(
validatorAddress
);
console.log(validatorUptime);
```
### Fetch a contract by contract address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const contractAddress = "inj...";
const contract = await indexerRestExplorerApi.fetchContract(contractAddress);
console.log(contract);
```
### Fetch contracts
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const limit = 100; /* optional pagination param */
const skip = 50; /* optional pagination param */
const contracts = await indexerRestExplorerApi.fetchContracts({
limit,
skip,
});
console.log(contracts);
```
### Fetch contract transactions
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const contractAddress = "inj...";
const limit = 100; /* optional pagination param */
const skip = 50; /* optional pagination param */
const transactions = await indexerRestExplorerApi.fetchContractTransactions({
contractAddress,
params: {
limit,
skip,
},
});
console.log(transactions);
```
### Fetch cosmwasm code details
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const codeId = 1;
const codeDetails = await indexerRestExplorerApi.fetchWasmCode(codeId);
console.log(codeDetails);
```
### Fetch wasm codes and details
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const limit = 100; /* optional pagination param */
const fromNumber = 50; /* optional pagination param */
const toNumber = 150; /* optional pagination param */
const codes = await indexerRestExplorerApi.fetchWasmCodes({
limit,
fromNumber,
toNumber,
});
console.log(codes);
```
### Fetch cw20 balances
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestExplorerApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestExplorerApi = new IndexerRestExplorerApi(
`${endpoints.explorer}/api/explorer/v1`
);
const address = "inj...";
const cw20Balances = await indexerRestExplorerApi.fetchCW20BalancesNoThrow(
address
);
console.log(cw20Balances);
```
# Querying the Indexer
Source: https://docs.injective.network/developers-native/query-indexer/index
This section covers how to query data from the Injective Indexer API.
* [Account](/developers-native/query-indexer/account/) - Query account data
* [Auction](/developers-native/query-indexer/auction/) - Query auction data
* [Derivatives](/developers-native/query-indexer/derivatives/) - Query derivatives market data
* [Explorer](/developers-native/query-indexer/explorer/) - Query explorer data
* [Insurance Funds](/developers-native/query-indexer/insurance-funds/) - Query insurance funds data
* [Markets](/developers-native/query-indexer/markets/) - Query market data
* [Leaderboard](/developers-native/query-indexer/leaderboard/) - Query leaderboard data
* [Mito](/developers-native/query-indexer/mito/) - Query Mito vault data
* [Oracle](/developers-native/query-indexer/oracle/) - Query oracle data
* [Portfolio](/developers-native/query-indexer/portfolio/) - Query portfolio data
* [Spot](/developers-native/query-indexer/spot/) - Query spot market data
* [Transaction](/developers-native/query-indexer/transaction/) - Query transaction data
# Insurance Funds
Source: https://docs.injective.network/developers-native/query-indexer/insurance-funds
Example code snippets to query the indexer for insurance fund module related data.
## Using gRPC
### Fetch redemptions for an injective address
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcInsuranceFundApi = new IndexerGrpcInsuranceFundApi(
endpoints.indexer
);
const injectiveAddress = "inj...";
const redemptions = await indexerGrpcInsuranceFundApi.fetchRedemptions({
injectiveAddress,
});
console.log(redemptions);
```
### Fetch insurance funds
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcInsuranceFundApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcInsuranceFundApi = new IndexerGrpcInsuranceFundApi(
endpoints.indexer
);
const insuranceFunds = await indexerGrpcInsuranceFundApi.fetchInsuranceFunds();
console.log(insuranceFunds);
```
# Leaderboard
Source: https://docs.injective.network/developers-native/query-indexer/leaderboard
Example code snippets to query the indexer for leaderboard module related data.
## Using HTTP REST
### Fetch leaderboard
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestLeaderboardChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcExplorerApi = new IndexerRestLeaderboardChronosApi(
`${endpoints.chronos}/api/chronos/v1/leaderboard`
);
const SelectList = {
Day: "1d",
Week: "7d",
};
const resolution = SelectList.Day;
const leaderboard = await indexerGrpcExplorerApi.fetchLeaderboard(resolution);
console.log(leaderboard);
```
# Markets
Source: https://docs.injective.network/developers-native/query-indexer/markets
Example code snippets to query the indexer for all markets data
## Using HTTP REST
### Fetch markets History
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerRestMarketChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestMarketChronosApi = new IndexerRestMarketChronosApi(
`${endpoints.chronos}/api/chronos/v1/market`
);
const SelectList = {
Hour: "60",
Day: "1d",
Week: "7d",
};
// const resolution = MARKETS_HISTORY_CHART_ONE_HOUR
// const countback = MARKETS_HISTORY_CHART_SEVEN_DAYS
const marketIds = ["0x"];
const countback = 154; // in unit of hours
const resolution = SelectList.Day;
const marketsHistory = await indexerRestMarketChronosApi.fetchMarketsHistory({
marketIds,
resolution,
countback,
});
console.log(marketsHistory);
```
# Mito
Source: https://docs.injective.network/developers-native/query-indexer/mito
Example code snippets to query the indexer for Mito vault module related data.
Mito Documentation has been moved here visit[ Mito's
Docs](https://docs.mito.fi/).
## (Outdated) Using gRPC
### Fetch a vault based off it's contract address, such as a vault's tvl or profits
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const contractAddress = "0x..."; /* optional param */
const slug = "derivative-vault"; /* optional param */
const vault = await indexerGrpcMitoApi.fetchVault({
contractAddress,
slug,
});
console.log(vault);
```
### Fetch vaults and associated details
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const vault = await indexerGrpcMitoApi.fetchVaults();
console.log(vault);
```
### Fetch the lp token price chart for a vault based on the vault address
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const vaultAddress = "inj...";
const from = 50; /* optional pagination params */
const to = 150; /* optional pagination params */
const lpTokenPriceChart = await indexerGrpcMitoApi.fetchLpTokenPriceChart({
vaultAddress,
from,
to,
});
console.log(lpTokenPriceChart);
```
### Fetch the tvl token chart for a vault based on the vault address
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const vaultAddress = "inj...";
const from = 50; /* optional pagination params */
const to = 150; /* optional pagination params */
const tvlChart = await indexerGrpcMitoApi.fetchTVLChartRequest({
vaultAddress,
from,
to,
});
console.log(tvlChart);
```
### Fetch the vaults associated with a holder of its lp tokens
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const holderAddress = "inj...";
const vaults = await indexerGrpcMitoApi.fetchVaultsByHolderAddress({
holderAddress,
});
console.log(vaults);
```
### Fetch the lp token holders from the vault address
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const vaultAddress = "inj...";
const holders = await indexerGrpcMitoApi.fetchLPHolders({
vaultAddress,
});
console.log(holders);
```
### Fetch the lp holder's portfolio
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const holderAddress = "inj...";
const portfolio = await indexerGrpcMitoApi.fetchHolderPortfolio(holderAddress);
console.log(portfolio);
```
### Fetch the leaderboard to see Pnl rankings
```ts theme={null}
import { IndexerGrpcMitoApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcMitoApi = new IndexerGrpcMitoApi(endpoints.indexer);
const leaderboard = await indexerGrpcMitoApi.fetchLeaderboard();
console.log(leaderboard);
```
# Oracle
Source: https://docs.injective.network/developers-native/query-indexer/oracle
Example code snippets to query the indexer for oracle module related data.
## Using gRPC
### Fetch list of oracles
```ts theme={null}
import { IndexerGrpcOracleApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcOracleApi = new IndexerGrpcOracleApi(endpoints.indexer);
const oracleList = await indexerGrpcOracleApi.fetchOracleList();
console.log(oracleList);
```
### Fetch price from the oracle
Base and Quote oracle symbols are always fetched from the market itself. They can be in a different representation than plain symbols (i.e hashes for `pyth` oracle).
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import {
IndexerGrpcOracleApi,
IndexerGrpcDerivativesApi,
} from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcDerivativesApi = new IndexerGrpcDerivativesApi(endpoints.indexer);
const indexerGrpcOracleApi = new IndexerGrpcOracleApi(endpoints.indexer);
// Fetch the list of derivative markets
const markets = await indexerGrpcDerivativesApi.fetchMarkets();
// Find the specific market by ticker
const market = markets.find((market) => market.ticker === "INJ/USDT PERP");
if (!market) {
throw new Error("Market not found");
}
// These values are a part of the market object
// fetched from the indexer i.e `oracleBase` and `oracleQuote`
const baseSymbol = market.oracleBase;
const quoteSymbol = market.oracleQuote;
const oracleType = market.oracleType;
const oraclePrice = await indexerGrpcOracleApi.fetchOraclePriceNoThrow({
baseSymbol,
quoteSymbol,
oracleType,
});
console.log(oraclePrice);
```
# Portfolio
Source: https://docs.injective.network/developers-native/query-indexer/portfolio
Example code snippets to query the indexer for portfolio module related data.
## Using gRPC
### Fetch portfolio based on injective address, such as bank balances and subaccount balances
```ts lines highlight={1} theme={null}
import { IndexerGrpcPortfolioApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcPortfolioApi = new IndexerGrpcPortfolioApi(endpoints.indexer);
const injectiveAddress = "inj...";
const portfolio = await indexerGrpcPortfolioApi.fetchAccountPortfolioBalances(
injectiveAddress
);
console.log(portfolio);
```
# Spot
Source: https://docs.injective.network/developers-native/query-indexer/spot
Example code snippets to query the indexer for spot market module related data.
## Using gRPC
### Fetch markets
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);
const markets = await indexerGrpcSpotApi.fetchMarkets();
console.log(markets);
```
### Fetch market based on market id
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);
const marketId = "0x...";
const market = await indexerGrpcSpotApi.fetchMarket(marketId);
console.log(market);
```
### Fetch market's orders
```ts theme={null}
import { OrderSide } from '@injectivelabs/sdk-ts/types'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { IndexerGrpcSpotApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const orderSide = OrderSide.Buy /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const orders = await indexerGrpcSpotApi.fetchOrders({
marketId,
orderSide,
subaccountId,
pagination
})
console.log(orders)
```
### Fetch market's order history
```ts theme={null}
import { TradeDirection, PaginationOption, TradeExecutionType, OrderSide } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcSpotApi } from '@injectivelabs/sdk-ts/client/indexer'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer)
const marketIds = ['0x...'] /* optional param */
const executionTypes = [TradeExecutionType.Market] /* optional param */
const orderTypes = OrderSide.Buy /* optional param */
const direction = TradeDirection.Buy /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const orderHistory = await indexerGrpcSpotApi.fetchOrderHistory({
marketIds,
executionTypes,
orderTypes,
direction,
subaccountId,
pagination
})
console.log(orderHistory)
```
### Fetch market's trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection, PaginationOption, TradeExecutionType } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcSpotApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const executionTypes = [TradeExecutionType.Market] /* optional param */
const direction = TradeDirection.Buy /* optional param */
const subaccountId = '0x...'/* optional param */
const pagination = {...} as PaginationOption /* optional param */
const trades = await indexerGrpcSpotApi.fetchTrades({
marketId,
executionTypes,
direction,
subaccountId,
pagination
})
console.log(trades)
```
### Fetch list of subaccount orders
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcSpotApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const subaccountId = '0x...' /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const subaccountOrders = await indexerGrpcSpotApi.fetchSubaccountOrdersList({
marketId,
subaccountId,
pagination
})
console.log(subaccountOrders)
```
### Fetch list of subaccount trades
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { TradeDirection, TradeExecutionType, PaginationOption } from '@injectivelabs/sdk-ts/types'
import { IndexerGrpcSpotApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer)
const marketId = '0x...' /* optional param */
const subaccountId = '0x...' /* optional param */
const executionType = TradeExecutionType.LimitFill /* optional param */
const direction = TradeDirection.Sell /* optional param */
const pagination = {...} as PaginationOption /* optional param */
const subaccountTrades = await indexerGrpcSpotApi.fetchSubaccountTradesList({
marketId,
subaccountId,
executionType,
direction,
pagination
})
console.log(subaccountTrades)
```
### Fetch orderbooks for multiple markets
```ts theme={null}
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);
const marketIds = ["0x..."];
const orderbooks = await indexerGrpcSpotApi.fetchOrderbooksV2(marketIds);
console.log(orderbooks);
```
### Fetch orderbook for a market
```ts theme={null}
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);
const marketId = "0x...";
const orderbook = await indexerGrpcSpotApi.fetchOrderbookV2(marketId);
console.log(orderbook);
```
## Using HTTP REST
### Fetch market summary, such as a history of prices and 24 hr volume
```ts theme={null}
import { IndexerRestSpotChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestSpotChronosApi = new IndexerRestSpotChronosApi(
`${endpoints.chronos}/api/chronos/v1/spot`
);
const marketId = "0x...";
const marketSummary = await indexerRestSpotChronosApi.fetchMarketSummary(
marketId
);
console.log(marketSummary);
```
### Fetch all markets summaries, such as a history of prices and 24 hr volume
```ts theme={null}
import { IndexerRestSpotChronosApi } from "@injectivelabs/sdk-ts/client/indexer";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerRestSpotChronosApi = new IndexerRestSpotChronosApi(
`${endpoints.chronos}/api/chronos/v1/spot`
);
const marketSummaries = await indexerRestSpotChronosApi.fetchMarketsSummary(
marketId
);
console.log(marketSummaries);
```
# Web3 Gateway Transactions
Source: https://docs.injective.network/developers-native/query-indexer/transaction
Example code snippets to query the indexer for transaction module related data. Used only when interacting with the [Web3Gateway](../transactions/web3-gateway/)
## Using gRPC
### Fetch response for preparing a transaction
```ts theme={null}
import { EvmChainId } from '@injectivelabs/ts-types'
import { Msgs } from '@injectivelabs/sdk-ts/core/modules'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { IndexerGrpcTransactionApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcTransactionApi = new IndexerGrpcTransactionApi(endpoints.indexer)
const address = '0x...' // ethereum address
const chainId = EvmChainId.Sepolia
const message = { ... } as Msgs
const memo = '...'
const prepareTxResponse = await indexerGrpcTransactionApi.prepareTxRequest({
address,
chainId,
message,
memo
})
console.log(prepareTxResponse)
```
### Fetch response for preparing a cosmos transaction
```ts theme={null}
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { IndexerGrpcTransactionApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcTransactionApi = new IndexerGrpcTransactionApi(endpoints.indexer)
const address = 'inj...'
const message = { ... }
const prepareCosmosTxResponse = await indexerGrpcTransactionApi.prepareCosmosTxRequest({
address,
message
})
console.log(prepareCosmosTxResponse)
```
### Fetch response for broadcasting transactions using the Web3Gateway
Use `MsgBroadcasterWithPk` to broadcast transactions within a node/CLI environment, which can be found in `@injectivelabs/sdk-ts`.
Use `@injectivelabs/wallet-core`'s `MsgBroadcaster` class for more details on broadcasting a transactions in a browser environment.
```ts theme={null}
import { Msgs } from '@injectivelabs/sdk-ts/core/modules'
import { ChainId, EvmChainId } from '@injectivelabs/ts-types'
import { WalletStrategy } from '@injectivelabs/wallet-strategy'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { IndexerGrpcTransactionApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcTransactionApi = new IndexerGrpcTransactionApi(endpoints.indexer)
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 walletStrategy = new WalletStrategy({
chainId,
evmOptions: {
evmChainId,
rpcUrl: evmRpcEndpoint,
},
})
const address = '0x...' // ethereum address
const message = { ... } as Msgs
const memo = '...'
const response = { ... } // response from prepareTxRequest
const signature = await walletStrategy.signEip712TypedData(
response.getData(),
address,
) /* see @injectivelabs/wallet-strategy implementation of WalletStrategy. Essentially, you use the signEip712TypedData method of the wallet, if the wallet supports signing ethereum transactions */
const broadcastTxResponse = await indexerGrpcTransactionApi.broadcastTxRequest({
signature,
chainId,
message,
txResponse: response
})
console.log(broadcastTxResponse)
```
### Fetch response for broadcasting a cosmos transactions.
```ts theme={null}
import { TxRaw } from '@injectivelabs/sdk-ts/types'
import { getNetworkEndpoints, Network } from '@injectivelabs/networks'
import { IndexerGrpcTransactionApi } from '@injectivelabs/sdk-ts/client/indexer'
const endpoints = getNetworkEndpoints(Network.Testnet)
const indexerGrpcTransactionApi = new IndexerGrpcTransactionApi(endpoints.indexer)
const address = 'inj...' // ethereum address
const signature = '...' // base64
const txRaw = { ... } as TxRaw
const pubKey = {
type: string,
value: string // base64
}
const broadcastCosmosTxResponse = await indexerGrpcTransactionApi.broadcastCosmosTxRequest({
address,
signature,
txRaw,
pubKey
})
console.log(broadcastCosmosTxResponse)
```
### Fetch Web3Gateway Fee Payer
```ts theme={null}
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { IndexerGrpcTransactionApi } from "@injectivelabs/sdk-ts/client/indexer";
const endpoints = getNetworkEndpoints(Network.Testnet);
const indexerGrpcTransactionApi = new IndexerGrpcTransactionApi(
endpoints.indexer
);
const feePayer = await indexerGrpcTransactionApi.fetchFeePayer();
console.log(feePayer);
```
# 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
```
# 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.).
# Core
Source: https://docs.injective.network/developers-native/core/index
## Core Modules
# State Transitions
Source: https://docs.injective.network/developers-native/injective/insurance/02_state_transitions
# State Transitions
This document describes the state transition operations pertaining to:
* Creating an insurance fund
* Underwriting an insurance fund
* Request a redemption from the insurance fund
* Automatic processing of matured redemption requests
## Creating insurance fund
**Params description**
`Sender` field describes the creator of an insurance fund .
`Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleType`, `Expiry` fields describe the derivative market info
that the insurance fund associated to.
`InitialDeposit` field describes the initial deposit amount to be put on the insurance fund.
**Steps**
* Get `MarketId` for the insurance fund - **Note**, market could be not available yet on `exchange` and it's not an
issue
* Ensure if insurance fund associated to the `MarketId` does not exist
* Ensure if initial deposit amount is not zero
* Get `shareDenom` that is unique - it's incremented when share denom is requested for insurance fund creation or when
underwriting insurance fund that has zero balance and non-zero total share denom supply.
* Send coins from creator's account to insurance fund module account
* Create insurance fund object with `DefaultRedemptionNoticePeriodDuration` and with the params provided
* Set `Balance` of fund object to initial deposit amount
* Mint `InsuranceFundInitialSupply` (10^18) `shareDenom` tokens to creator account
* Save insurance fund object to store
* Register newly created insurance fund `shareDenom` metadata inside BankKeeper
## Underwriting an insurance fund
**Params description**
`Sender` field describes the underwriter of an insurance fund .
`MarketId` field describes the derivative market id to the insurance fund.
`Deposit` field describes the deposit amount to be added on the insurance fund.
**Steps**
* Ensure if insurance fund associated to the `MarketId` does exist
* Send underwriting tokens from sender's account to module account
* Make actions based on the status of insurance fund associated to the `MarketId`.
* A. when `Balance` and `ShareDenomSupply` are zero
1. mint `InsuranceFundInitialSupply` (10^18) to the sender.
2. set `Balance` to deposit amount
3. set `ShareDenomSupply` to `InsuranceFundInitialSupply`
* B. when `Balance` is zero and `ShareDenomSupply` is not zero
1. change `ShareDenom` of the the insurance fund to start new insurance fund from beginning.
2. register newly created `ShareDenom` in bank keeper
3. mint `InsuranceFundInitialSupply` (10^18) to the sender.
4. set `Balance` to deposit amount
5. set `ShareDenomSupply` to `InsuranceFundInitialSupply`
* C. when `Balance` is not zero and `ShareDenomSupply` is zero
1. mint `InsuranceFundInitialSupply` (10^18) to the sender.
2. increase `Balance` by deposit amount
3. set `ShareDenomSupply` to `InsuranceFundInitialSupply`
* D. when both `Balance` and `ShareDenomSupply` are not zero - normal case
1. increase `Balance` by deposit amount
2. mint `prev_ShareDenomSupply * deposit_amount / prev_Balance` amount of `ShareDenom` to sender
3. increase `ShareDenomSupply` with mint amount
* Save insurance fund object to store
## Requesting a redemption from an insurance fund
**Params description**
`Sender` field describes the redemption requester of an insurance fund .
`MarketId` field describes the derivative market id associated to the insurance fund.
`Amount` field describes the share token amount to be redeemed.
**Steps**
* Ensure insurance fund associated to the `MarketId` does exist
* Send `ShareDenom` to module account
* Get new redemption schedule ID
* Calculate `ClaimTime` from insurance fund's redemption notice period duration and current block time
* Calculate key to store pending redemption (redemption schedule)
* Create redemption schedule object with details
* Store redemption schedule object to store
## Insurance fund actions on liquidation events in derivative market
**Steps**
* `exchange` module finds relative insurance fund from the insurance keeper.
* if `missingFund` is positive, it withdraws the amount from the insurance fund through `WithdrawFromInsuranceFund`.
* if `missingFund` is negative, it deposits the amount into the insurance fund through `DepositIntoInsuranceFund`.
## Automatic processing of pending redemptions
**Steps**
Iterate all matured redemptions by sorted order by `ClaimTime` and perform the following actions:
* If `ClaimTime` is after current block time, break early
* Ensure the insurance fund exist for matured redemption schedule
* Calculate redeem amount from share amount - `shareAmt * fund.Balance * fund.TotalShare`
* Send calculate redeem amount from module account to redeemer account
* Burn share tokens sent to the module account at the time of redemption schedule
* Delete redemption schedule object
* Reduce insurance fund's `Balance` by redeem amount
* Store updated insurance object to store
# Hooks
Other modules may register operations to execute when a certain event has occurred within insurance fund. These events
can be registered to execute either right `Before` or `After` the exchange event (as per the hook name). The following
hooks can registered with the exchange:
**Note**: Hooks are not available and exchange module calls insurance keeper function directly.
**Steps**
When liquidation event happen in derivative market
* `exchange` module finds relative insurance fund from the insurance keeper.
* if `missingFund` is positive, it withdraws the amount from the insurance fund through `WithdrawFromInsuranceFund`.
* if `missingFund` is negative, it deposits the amount into the insurance fund through `DepositIntoInsuranceFund`.
# Messages
Source: https://docs.injective.network/developers-native/injective/insurance/03_messages
# Messages
In this section we describe the processing of the exchange messages and the corresponding updates to the state. All created/modified state objects specified by each message are defined within the [state](./02_state_transitions) section.
## Msg/CreateInsuranceFund
`MsgCreateInsuranceFund` defines a message to create an insurance fund for a derivative market.
```protobuf theme={null}
// MsgCreateInsuranceFund a message to create an insurance fund for a derivative market.
message MsgCreateInsuranceFund {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// Creator of the insurance fund.
string sender = 1;
// Ticker for the derivative market.
string ticker = 2;
// Coin denom to use for the market quote denom
string quote_denom = 3;
// Oracle base currency
string oracle_base = 4;
// Oracle quote currency
string oracle_quote = 5;
// Oracle type
injective.oracle.v1beta1.OracleType oracle_type = 6;
// Expiration time of the market. Should be -1 for perpetual markets.
int64 expiry = 7;
// Initial deposit of the insurance fund
cosmos.base.v1beta1.Coin initial_deposit = 8 [(gogoproto.nullable) = false];
}
```
**Fields description**
* `Sender` field describes the creator of an insurance fund .
* `Ticker`, `QuoteDenom`, `OracleBase`, `OracleQuote`, `OracleType`, `Expiry` fields describe the derivative market info
that the insurance fund corresponds to.
* `InitialDeposit` specifies the initial deposit amount used to underwrite the insurance fund.
Disclaimer: When creating an insurance fund a small portion of shares (1%) will be reserved by the fund itself (protocol owned liquidity). A value of 1 USD is recommended as first subscription.
Motivation behind this feature is to avoid potential rounding issues when underwriting to a fund. For example, without having protocol owned liquidity, if the original fund creator would take out most of their shares leaving but a small amount, the value of the share token could diverge drastically from the original value. The next underwriter would then have to provide a much larger deposit despite gaining the same amount of shares.
## Msg/Underwrite
`MsgUnderwrite` defines a message to underwrite an insurance fund
```protobuf theme={null}
// MsgUnderwrite defines a message for depositing coins to underwrite an insurance fund
message MsgUnderwrite {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// Address of the underwriter.
string sender = 1;
// MarketID of the insurance fund.
string market_id = 2;
// Amount of quote_denom to underwrite the insurance fund.
cosmos.base.v1beta1.Coin deposit = 3 [(gogoproto.nullable) = false];
}
```
**Fields description**
* `Sender` field describes the underwriter of an insurance fund .
* `MarketId` field describes the derivative market id to the insurance fund.
* `Deposit` field describes the deposit amount to be added on the insurance fund.
## Msg/RequestRedemption
`MsgRequestRedemption` defines a message to request redemption from the insurance fund.
```protobuf theme={null}
// MsgRequestRedemption defines a message for requesting a redemption of the sender's insurance fund tokens
message MsgRequestRedemption {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// Address of the underwriter requesting a redemption.
string sender = 1;
// MarketID of the insurance fund.
string market_id = 2;
// Insurance fund share token amount to be redeemed.
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
```
**Fields description**
* `Sender` field describes the redemption requester of an insurance fund .
* `MarketId` field describes the derivative market id associated to the insurance fund.
* `Amount` field describes the share token amount to be redeemed.
# End-Block
Source: https://docs.injective.network/developers-native/injective/insurance/04_end_block
# End-Block
At each EndBlock, redemption requests that have matured are automatically processed, resulting in the insurance pool token for the redemption being burned and the pro-rata quote currency amount corresponding to the redemption being withdrawn from the insurance module to the underwriter's balances. More details can be found in the in Automatic withdrawal of pending redemptions in the [state transitions](./02_state_transitions) section.
# Events
Source: https://docs.injective.network/developers-native/injective/insurance/05_events
# Events
The insurance module emits the following events:
## Handlers
### MsgCreateInsuranceFund
| Type | Attribute Key | Attribute Value |
| ---------------------------------------------------- | ------------- | --------------- |
| injective.insurance.v1beta1.EventInsuranceFundUpdate | fund | |
### MsgUnderwrite
| Type | Attribute Key | Attribute Value |
| ---------------------------------------------------- | ------------- | --------------- |
| injective.insurance.v1beta1.EventInsuranceFundUpdate | fund | |
### MsgRequestRedemption
| Type | Attribute Key | Attribute Value |
| -------------------------------------------------- | ------------- | --------------- |
| injective.insurance.v1beta1.EventRequestRedemption | schedule | |
## EndBlocker
| Type | Attribute Key | Attribute Value |
| ---------------------------------------------------- | ------------- | --------------- |
| injective.insurance.v1beta1.EventInsuranceFundUpdate | fund | |
| injective.insurance.v1beta1.EventWithdrawRedemption | schedule | |
| injective.insurance.v1beta1.EventWithdrawRedemption | redeem\_coin | |
# Parameters
Source: https://docs.injective.network/developers-native/injective/insurance/06_params
# Parameters
The insurance module contains the following parameter:
| Key | Type | Example |
| --------------------------------------------- | ------------- | --------------------- |
| default\_redemption\_notice\_period\_duration | time.Duration | `time.Hour * 24 * 14` |
# Future Improvements
Source: https://docs.injective.network/developers-native/injective/insurance/07_future_improvements
# Future improvements
## Precision Loss Edge Case Handling
Insurance Fund share tokens currently have a decimal scale of `10^18`. There could be potential problems using this as user deposits amount could be various and in the future, share token's price could be very higher or lower.
# Error Codes
Source: https://docs.injective.network/developers-native/injective/insurance/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| --------- | ---------- | --------------------------------- |
| insurance | 1 | insurance fund already exists |
| insurance | 2 | insurance fund not found |
| insurance | 3 | redemption already exists |
| insurance | 4 | invalid deposit amount |
| insurance | 5 | invalid deposit denom |
| insurance | 6 | insurance payout exceeds deposits |
| insurance | 7 | invalid ticker |
| insurance | 8 | invalid quote denom |
| insurance | 9 | invalid oracle |
| insurance | 10 | invalid expiration time |
| insurance | 11 | invalid marketID |
| insurance | 12 | invalid share denom |
# Concepts
Source: https://docs.injective.network/developers-native/injective/ocr/01_concepts
# Concepts
The `ocr` module is to store chainlink's OCR information into on-chain by verified members.
Off-chain reporting consists of N nodes (oracles), gathering data from external sources. Reports are being exchanged in a p2p fashion between oracles to get signatures of approval. A subset of nodes (transmitters) is identified by the `ocr` module on-chain, they must submit the reports to module, the first transmitter who hits the chain gets an extra reward to cover gas costs. Other transmitters are not. All oracles participating in the round are getting paid. `ocr` module stores median value from the reports.
## OCR Terminology
The protocol periodically sends **oracle reports** to the OCR module. The reporting protocol is comprised of three components: **pacemaker**, **report generation** and **transmission**.
**Pacemaker**
The pacemaker drives the report generation process which is structured in **epochs**. Each epoch has a designatd leader who the pacemaker then tasks with starting the report generation protocol. If the leader does not produce a valid report in time, the pacemaker also aborts the current report generation and starts a new epoch.
**Report Generation**
For a given epoch, the report generation protocol enters into **rounds** where **observations** are gathered and (given conditions are met such as heartbeat and deviation) a signed oracle **report** is generated. The rounds are controlled by a leader node who controls the frequency of rounds, gathers the observations and generates the report.
**Transmission**
The transmission protocol then transmits the generated report to the OCR module.
## Off-chain OCR integration
* Provide means to communicate with Injective using sdk-go
* Read data from the module, such as a list of approved oracles
* Submit reports as Msgs (Implement `ContractTransmitter`)
* Implement `OffchainConfigDigester`
* Implement `OnchainKeyring` for producing signatures that will work on the target chain module
* Implement `ContractConfigTracker` for tracking changes of the chain module config (gov approved)
Notes:
* Reports are timestamped in Epoch-Round fashion
* `ocr` module verifies the signatures of oracles on the report
* `ocr` module records oracles who contributed to a report, for the payout
* `ocr` module stores the median of the observations
* `ocr` module provides extra reward for the first submitter of a Msg
### Integration Overview
Chainlink has several [price data feeds](https://data.chain.link/ethereum/mainnet/stablecoins) including:
* 80 Crypto/USD pairs (e.g. ETH/USD, BTC/USD)
* 17 Stablecoin pairs (e.g. USDT/USD, USDC/USD)
* 73 ETH pairs (e.g. LINK/ETH)
* 17 Forex pairs (e.g. GBP/USD, CNY/USD)
A derivative market on Injective specifies the following oracle parameters:
* An oracleBase (e.g. BTC)
* An oracleQuote (e.g. USDT)
* An oracleType (e.g. Chainlink)
Thus for a BTC/USDT derivative market on Injective, the oracleBase would be BTC/USD, the oracleQuote would be USDT/USD and the oracleType would be Chainlink. The price for the market would then be obtained by dividing the BTC/USD price with the USDT/USD price, leaving the BTC/USDT price.
# State
Source: https://docs.injective.network/developers-native/injective/ocr/02_state
# State
Genesis state defines the initial state of the module to be used to setup the module.
```go theme={null}
// GenesisState defines the OCR module's genesis state.
type GenesisState struct {
// params defines all the parameters of related to OCR.
Params Params
// feed_configs stores all of the supported OCR feeds
FeedConfigs []*FeedConfig
// latest_epoch_and_rounds stores the latest epoch and round for each feedId
LatestEpochAndRounds []*FeedEpochAndRound
// feed_transmissions stores the last transmission for each feed
FeedTransmissions []*FeedTransmission
// latest_aggregator_round_ids stores the latest aggregator round ID for each feedId
LatestAggregatorRoundIds []*FeedLatestAggregatorRoundIDs
// reward_pools stores the reward pools
RewardPools []*RewardPool
// feed_observation_counts stores the feed observation counts
FeedObservationCounts []*FeedCounts
// feed_transmission_counts stores the feed transmission counts
FeedTransmissionCounts []*FeedCounts
// pending_payeeships stores the pending payeeships
PendingPayeeships []*PendingPayeeship
}
```
## Params
`Params` is a module-wide configuration that stores system parameters and defines overall functioning of the ocr module.
This module is modifiable by governance using params update proposal natively supported by `gov` module.
Struct for the `ocr` module params store.
```go theme={null}
type Params struct {
// Native denom for LINK coin in the bank keeper
LinkDenom string
// The block number interval at which payouts are made
PayoutBlockInterval uint64
// The admin for the OCR module
ModuleAdmin string
}
```
## FeedConfig
`FeedConfig` is to manage the configurations of feed and it exists one per feed.
```go theme={null}
type FeedConfig struct {
// signers ith element is address ith oracle uses to sign a report
Signers []string
// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
Transmitters []string
// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
F uint32
// onchain_config contains properties relevant only for the Cosmos module.
OnchainConfig *OnchainConfig
// offchain_config_version version of the serialization format used for "offchain_config" parameter
OffchainConfigVersion uint64
// offchain_config serialized data used by oracles to configure their offchain operation
OffchainConfig []byte
}
```
### FeedConfigInfo
`FeedConfigInfo` is storing the information that needs to be updated more often for each transmission event.
```go theme={null}
type FeedConfigInfo struct {
LatestConfigDigest []byte
F uint32
N uint32
// config_count ordinal number of this config setting among all config settings
ConfigCount uint64
LatestConfigBlockNumber int64
}
```
### Transmission
`Transmission` is the unit to save transition information on the store.
```go theme={null}
// Transmission records the median answer from the transmit transaction at
// time timestamp
type Transmission struct {
Answer math.LegacyDec
ObservationsTimestamp int64
TransmissionTimestamp int64
}
```
### Report
`Report` is the unit to save report information on the store.
```go theme={null}
type Report struct {
ObservationsTimestamp int64
Observers []byte
Observations []math.LegacyDec
}
```
`ReportToSign` saves the information that needs to be signed by observers.
```go theme={null}
type ReportToSign struct {
ConfigDigest []byte
Epoch uint64
Round uint64
ExtraHash []byte
// Opaque report
Report []byte
}
```
### OnchainConfig
`OnchainConfig` saves the configuration that needs to be managed on-chain for feed config.
```go theme={null}
type OnchainConfig struct {
// chain_id the ID of the Cosmos chain itself.
ChainId string
// feed_id is an unique ID for the target of this config
FeedId string
// lowest answer the median of a report is allowed to be
MinAnswer math.LegacyDec
// highest answer the median of a report is allowed to be
MaxAnswer math.LegacyDec
// Fixed LINK reward for each observer
LinkPerObservation math.Int
// Fixed LINK reward for transmitter
LinkPerTransmission math.Int
// Native denom for LINK coin in the bank keeper
LinkDenom string
// Enables unique reports
UniqueReports bool
// short human-readable description of observable this feed's answers pertain to
Description string
// feed administrator
FeedAdmin string
// feed billing administrator
BillingAdmin string
}
```
### ContractConfig
`ContractConfig` saves the configuration that is related to contract to store OCR.
```go theme={null}
type ContractConfig struct {
// config_count ordinal number of this config setting among all config settings
ConfigCount uint64
// signers ith element is address ith oracle uses to sign a report
Signers []string
// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
Transmitters []string
// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
F uint32
// onchain_config serialized config that is relevant only for the module.
OnchainConfig []byte
// offchain_config_version version of the serialization format used for "offchain_config" parameter
OffchainConfigVersion uint64
// offchain_config serialized data used by oracles to configure their offchain operation
OffchainConfig []byte
}
```
### FeedProperties
`FeedProperties` is a unit to store the properties of feed by id.
```go theme={null}
type FeedProperties struct {
// feed_id is an unique ID for the target of this config
FeedId string
// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
F uint32
// offchain_config_version version of the serialization format used for "offchain_config" parameter
OffchainConfigVersion uint64
// offchain_config serialized data used by oracles to configure their offchain operation
OffchainConfig []byte
// lowest answer the median of a report is allowed to be
MinAnswer math.LegacyDec
// highest answer the median of a report is allowed to be
MaxAnswer math.LegacyDec
// Fixed LINK reward for each observer
LinkPerObservation math.Int
// Fixed LINK reward for transmitter
LinkPerTransmission math.Int
// Enables unique reports
UniqueReports bool
// short human-readable description of observable this feed's answers pertain to
Description string
}
```
### PendingPayeeship
`PendingPayeeship` is a record that is stored when a person is delegating payeeship to another address.
When proposed payee accept this, this record is removed.
```go theme={null}
type PendingPayeeship struct {
FeedId string
Transmitter string
ProposedPayee string
}
```
# Messages
Source: https://docs.injective.network/developers-native/injective/ocr/03_messages
# Messages
In this section we describe the processing of the ocr messages and the corresponding updates to the state.
## Msg/CreateFeed
`MsgCreateFeed` is a message to create feed config and it is restricted message that is executable by module admin.
```protobuf theme={null}
message MsgCreateFeed {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
FeedConfig config = 2;
}
```
**Steps**
* Ensure `Sender` is module admin
* Ensure `msg.Config.OnchainConfig.LinkDenom` is module param's `LinkDenom`
* Set `OnchainConfig.ChainId` from `ctx.ChainID`
* Ensure `FeedConfig` with same `FeedId` does not exist
* Set latest `EpochAndRound` to `(0, 0)`
* Set feed config for `feedId`
* Set feed trasmissions count and observations count to 1
## Msg/UpdateFeed
`MsgCreateFeed` is a message to update feed config and it is restricted message that is executable by feed admin or feed billing admin.
```protobuf theme={null}
message MsgUpdateFeed {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
// feed_id is an unique ID for the target of this config
string feed_id = 2;
// signers ith element is address ith oracle uses to sign a report
repeated string signers = 3;
// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
repeated string transmitters = 4;
// Fixed LINK reward for each observer
string link_per_observation = 5[
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = true
];
// Fixed LINK reward for transmitter
string link_per_transmission = 6[
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = true
];
// Native denom for LINK coin in the bank keeper
string link_denom = 7;
// feed administrator
string feed_admin = 8;
// feed billing administrator
string billing_admin = 9;
}
```
**Steps**
* Get previous feed config by `feedId` and ensure it exists
* Ensure `Sender` is feed admin or feed billing admin
* Ensure billing admin is not changing Signers, Transmitters and feed admin
* Process rewards payout for previous feed config
* Delete previous feed transmission and observation counts
* Set latest `EpochAndRound` to `(0, 0)`
* Update signers, transmitters, `LinkPerObservation`, `LinkPerTransmission`, `LinkDenom`, `FeedAdmin`, `BillingAdmin` if set.
## Msg/Transmit
`MsgTransmit` is a message to transmit a report for specific feed. When broadcasting the message, there should be enough amount of signatures from observers to be accepted.
```protobuf theme={null}
message MsgTransmit {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// Address of the transmitter
string transmitter = 1;
bytes config_digest = 2;
string feed_id = 3;
uint64 epoch = 4;
uint64 round = 5;
bytes extra_hash = 6;
Report report = 7;
repeated bytes signatures = 8;
}
```
**Steps**
* Get epoch and round for `feedId`
* Ensure that the report is not staled one by checking `msg.Epoch` and `msg.Round`
* Get feed config and config info from `feedId`
* Check msg.ConfigDigest equals to feed config info's latest config digest
* Check if transmitter is valid transmitter configured in `feedConfig`
* Save transmitter report
* Emit event for trasmission
* Validate signatures and the number of signatures
* Increment feed observation and transmission counts
## Msg/FundFeedRewardPool
`MsgFundFeedRewardPool` is a message to add funds to feed reward pool to be given to transmitters and observers.
```protobuf theme={null}
message MsgFundFeedRewardPool {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
string feed_id = 2;
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
```
**Steps**
* Get previous reward pool amount from `feedId`
* If previous amount is empty, initiate the pool amount with zero integer
* Ensure previous amount denom is not different from deposit denom if exist
* Send coins from account to the module account (`ocr` module)
* Update reward pool amount with `amount` field addition
* Call `AfterFundFeedRewardPool` hook if hooks is set
## Msg/WithdrawFeedRewardPool
`MsgFundFeedRewardPool` is a message to withdraw funds from feed reward pool and is restricted to feed admin or billing admin.
```protobuf theme={null}
message MsgWithdrawFeedRewardPool {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
string feed_id = 2;
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
```
**Steps**
* Get feed config from `feedId`
* Ensure `msg.Sender` is `feedAdmin` or `billingAdmin`
* Process reward for the feed
* Withdraw specified amount `msg.Amount` from module account
## Msg/SetPayees
`MsgSetPayees` is a message to set payee for transmitters - it is restricted to feed admin. Once it's set, it should be changed only by payee.
```protobuf theme={null}
message MsgSetPayees {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
string feed_id = 2;
// addresses oracles use to transmit the reports
repeated string transmitters = 3;
// addresses of payees corresponding to list of transmitters
repeated string payees = 4;
}
```
**Steps**
* Get feed config from `feedId` and ensure that feed config exists
* Ensure `msg.Sender` is feed admin
* Iterating `msg.Transmitters`,
* 1. Ensure payee is set already for the transmitter
* 2. Set payee for the transmitter
## Msg/TransferPayeeship
`MsgTransferPayeeship` is a message to transfer payeeship for a specific transmitter of feed. After execution, pending payeeship object is created.
```protobuf theme={null}
message MsgTransferPayeeship {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// transmitter address of oracle whose payee is changing
string sender = 1;
string transmitter = 2;
string feed_id = 3;
// new payee address
string proposed = 4;
}
```
**Steps**
* Get feed config from `feedId` and ensure that feed config exists
* Ensure msg.Sender is current payee
* Check previous pending payeeship transfer record and ensure previous payeeship transfer does not conflict
* Set payeeship transfer record
## Msg/AcceptPayeeship
`MsgTransferPayeeship` is a message to accept payeeship for a specific transmitter of feed.
```protobuf theme={null}
message MsgAcceptPayeeship {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// new payee address
string payee = 1;
// transmitter address of oracle whose payee is changing
string transmitter = 2;
string feed_id = 3;
}
```
**Steps**
* Get feed config from `feedId` and ensure that feed config exists
* Get pending payeeship transfer record for `msg.Transmitter` and `feedId`
* Reset payee for `feedId` and `transmitter`
* Delete pending payeeship transfer for `transmitter` of `feedId`
# Governance Proposals
Source: https://docs.injective.network/developers-native/injective/ocr/04_proposals
# Governance Proposals
## SetConfigProposal
`SetConfigProposal` is a proposal to set feed config by governance.
```protobuf theme={null}
message SetConfigProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
FeedConfig config = 3;
}
```
**Steps**
* Validate basics for the proposal
* Ensure module's `LinkDenom` is same as proposal's `LinkDenom`
* set `p.Config.OnchainConfig.ChainId` from `ctx.ChainID`
* Set feed config for `feedId`
* Set feed transmissions and observations count for `Config.Transmitters`
## SetBatchConfigProposal
`SetBatchConfigProposal` is a proposal to set multiple feed configs at once by governance.
```protobuf theme={null}
message SetBatchConfigProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
// signers ith element is address ith oracle uses to sign a report
repeated string signers = 3;
// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
repeated string transmitters = 4;
// Native denom for LINK coin in the bank keeper
string link_denom = 5;
repeated FeedProperties feed_properties = 6;
}
```
# Begin-Block
Source: https://docs.injective.network/developers-native/injective/ocr/05_begin_block
# Begin-Block
At each BeginBlock, it checks if it's time for payout interval and if it's time, it process payout for all feeds.
**Steps**
* Ensure it's the begin block of payout interval
* While iterating all feed configs, process reward payouts
# null
Source: https://docs.injective.network/developers-native/injective/ocr/06_hooks
# Hooks
Other modules may register operations to execute when a certain event has occurred within ocr module. The following hooks can registered with ocr:
* `AfterSetFeedConfig(ctx sdk.Context, feedConfig *FeedConfig)`
* called after feed config is created or updated
* `AfterTransmit(ctx sdk.Context, feedId string, answer math.LegacyDec, timestamp int64)`
* called when info is transmitted
* `AfterFundFeedRewardPool(ctx sdk.Context, feedId string, newPoolAmount sdk.Coin)`
* called when feed reward pool is updated
Note:
`oracle` module is accepting `AfterTransmit` hook to store cumulative price when transmission is made.
# Events
Source: https://docs.injective.network/developers-native/injective/ocr/07_events
# Events
The ocr module emits the following events:
## Handlers
### MsgCreateFeed
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------- |
| message | action | MsgCreateFeed |
| message | sender | |
### MsgUpdateFeed
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------- |
| message | action | MsgUpdateFeed |
| message | sender | |
### MsgTransmit
| Type | Attribute Key | Attribute Value |
| -------------------- | --------------------- | --------------- |
| EventNewTransmission | FeedId | |
| EventNewTransmission | AggregatorRoundId | |
| EventNewTransmission | Answer | |
| EventNewTransmission | Transmitter | |
| EventNewTransmission | ObservationsTimestamp | |
| EventNewTransmission | Observations | |
| EventNewTransmission | Observers | |
| EventNewTransmission | ConfigDigest | |
| EventNewTransmission | EpochAndRound | |
| EventTransmitted | ConfigDigest | |
| EventTransmitted | Epoch | |
| message | action | MsgTransmit |
| message | sender | |
### MsgFundFeedRewardPool
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------------- |
| message | action | MsgFundFeedRewardPool |
| message | sender | |
### MsgWithdrawFeedRewardPool
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | ------------------------- |
| message | action | MsgWithdrawFeedRewardPool |
| message | sender | |
### MsgSetPayees
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------- |
| message | action | MsgSetPayees |
| message | sender | |
### MsgTransferPayeeship
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | -------------------- |
| message | action | MsgTransferPayeeship |
| message | sender | |
### MsgAcceptPayeeship
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | ------------------ |
| message | action | MsgAcceptPayeeship |
| message | sender | |
## Proposals
### SetConfigProposal
| Type | Attribute Key | Attribute Value |
| -------------- | ------------------------- | --------------- |
| EventConfigSet | ConfigDigest | |
| EventConfigSet | PreviousConfigBlockNumber | |
| EventConfigSet | Config | |
| EventConfigSet | ConfigInfo | |
### SetBatchConfigProposal
| Type | Attribute Key | Attribute Value |
| ----------------- | ------------------------- | --------------- |
| EventConfigSet\[] | ConfigDigest | |
| EventConfigSet\[] | PreviousConfigBlockNumber | |
| EventConfigSet\[] | Config | |
| EventConfigSet\[] | ConfigInfo | |
## BeginBlocker
| Type | Attribute Key | Attribute Value |
| ---- | ------------- | --------------- |
# Params
Source: https://docs.injective.network/developers-native/injective/ocr/08_params
# Parameters
The ocr module contains the following parameters:
| Key | Type | Example |
| ------------------- | ------ | ------- |
| LinkDenom | string | link |
| PayoutBlockInterval | uint64 | 100 |
| ModuleAdmin | string | |
# Error Codes
Source: https://docs.injective.network/developers-native/injective/ocr/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------ | ---------- | ---------------------------------------- |
| ocr | 1 | stale report |
| ocr | 2 | incomplete proposal |
| ocr | 3 | repeated oracle address |
| ocr | 4 | too many signers |
| ocr | 5 | incorrect config |
| ocr | 6 | config digest doesn't match |
| ocr | 7 | wrong number of signatures |
| ocr | 8 | incorrect signature |
| ocr | 9 | no transmitter specified |
| ocr | 10 | incorrect transmission data |
| ocr | 11 | no transmissions found |
| ocr | 12 | median value is out of bounds |
| ocr | 13 | LINK denom doesn't match |
| ocr | 14 | Reward Pool doesn't exist |
| ocr | 15 | wrong number of payees and transmitters |
| ocr | 16 | action is restricted to the module admin |
| ocr | 17 | feed already exists |
| ocr | 19 | feed doesnt exists |
| ocr | 20 | action is admin-restricted |
| ocr | 21 | insufficient reward pool |
| ocr | 22 | payee already set |
| ocr | 23 | action is payee-restricted |
| ocr | 24 | feed config not found |
# OCR
Source: https://docs.injective.network/developers-native/injective/ocr/index
## Abstract
OCR module is to store chainlink's OCR(Off-Chain Report) info into the chain storage.
Feed configuration is managed by module admin and report move to on-chain by transmitters and observers.
Transmitters and observers are rewarded in LINK token on the chain configured by governance.
While storing feed information, module provide hooks where oracle module can use for the calculation of cumulative price for futures market.
## Contents
1. [Concepts](/developers-native/injective/ocr/01_concepts)
2. [State](/developers-native/injective/ocr/02_state)
3. [Messages](/developers-native/injective/ocr/03_messages)
4. [Proposals](/developers-native/injective/ocr/04_proposals)
5. [Begin-Block](/developers-native/injective/ocr/05_begin_block)
6. [Hooks](/developers-native/injective/ocr/06_hooks)
7. [Events](/developers-native/injective/ocr/07_events)
8. [Parameters](/developers-native/injective/ocr/08_params)
# State
Source: https://docs.injective.network/developers-native/injective/oracle/01_state
# State
## Params
The oracle module parameters.
```protobuf theme={null}
message Params {
option (gogoproto.equal) = true;
string pyth_contract = 1;
}
```
## PriceState
PriceState is common type to manage cumulative price and latest price along with timestamp for all oracle types.
```protobuf theme={null}
message PriceState {
string price = 1 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
string cumulative_price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
int64 timestamp = 3;
}
```
where
* `Price` represents the normalized decimal price
* `CumulativePrice` represents the cumulative price for a given oracle price feed since the start of the oracle price feed's creation.
* `Timestamp` represents the time at which the blocktime at which the price state was relayed.
Note that the `CumulativePrice` value follows the convention set by the [Uniswap V2 Oracle](https://uniswap.org/docs/v2/core-concepts/oracles/) and is used to allows modules to calculate Time-Weighted Average Price (TWAP) between 2 arbitrary block time intervals (t1, t2).
$\mathrm{TWAP = \frac{CumulativePrice_2 - CumulativePrice_1}{Timestamp_2 - Timestamp_1}}$
## Band
Band price data for a given symbol are represented and stored as follows:
* BandPriceState: `0x01 | []byte(symbol) -> ProtocolBuffer(BandPriceState)`
```protobuf theme={null}
message BandPriceState {
string symbol = 1;
string rate = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false];
uint64 resolve_time = 3;
uint64 request_ID = 4;
PriceState price_state = 5 [(gogoproto.nullable) = false];
}
```
Note that the `Rate` is the raw USD rate for the `Symbol` obtained from the Band chain which has is scaled by 1e9 (e.g. a price of 1.42 is 1420000000) while the PriceState has the normalized decimal price (e.g. 1.42).
Band relayers are stored by their address as follows.
* BandRelayer: `0x02 | RelayerAddr -> []byte{}`
## Band IBC
This section describes all the state management to maintain the price by connecting to Band chain via IBC.
* LatestClientID is maintained to manage unique clientID for band IBC packets. It is increased by 1 when sending price request packet into bandchain.
- LatestClientID: `0x32 -> Formated(LatestClientID)`
* LatestRequestID is maintained to manage unique `BandIBCOracleRequests`. Incremented by 1 when creating a new `BandIBCOracleRequest`.
- LatestRequestID: `0x36 -> Formated(LatestRequestID)`
* Band IBC price data for a given symbol is stored as follows:
- BandPriceState: `0x31 | []byte(symbol) -> ProtocolBuffer(BandPriceState)`
```protobuf theme={null}
message BandPriceState {
string symbol = 1;
string rate = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false];
uint64 resolve_time = 3;
uint64 request_ID = 4;
PriceState price_state = 5 [(gogoproto.nullable) = false];
}
```
* BandIBCCallDataRecord is stored as follows when sending price request packet into bandchain:
- CalldataRecord: `0x33 | []byte(ClientId) -> ProtocolBuffer(CalldataRecord)`
```protobuf theme={null}
message CalldataRecord {
uint64 client_id = 1;
bytes calldata = 2;
}
```
* BandIBCOracleRequest is stored as follows when the governance configure oracle requests to send:
- BandOracleRequest: `0x34 | []byte(RequestId) -> ProtocolBuffer(BandOracleRequest)`
```protobuf theme={null}
message BandOracleRequest {
// Unique Identifier for band ibc oracle request
uint64 request_id = 1;
// OracleScriptID is the unique identifier of the oracle script to be executed.
int64 oracle_script_id = 2;
// Symbols is the list of symbols to prepare in the calldata
repeated string symbols = 3;
// AskCount is the number of validators that are requested to respond to this
// oracle request. Higher value means more security, at a higher gas cost.
uint64 ask_count = 4;
// MinCount is the minimum number of validators necessary for the request to
// proceed to the execution phase. Higher value means more security, at the
// cost of liveness.
uint64 min_count = 5;
// FeeLimit is the maximum tokens that will be paid to all data source providers.
repeated cosmos.base.v1beta1.Coin fee_limit = 6 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
// PrepareGas is amount of gas to pay to prepare raw requests
uint64 prepare_gas = 7;
// ExecuteGas is amount of gas to reserve for executing
uint64 execute_gas = 8;
}
```
* BandIBCParams is stored as follows and configured by governance:
- BandIBCParams: `0x35 -> ProtocolBuffer(BandIBCParams)`
`BandIBCParams` contains the information for IBC connection with band chain.
```protobuf theme={null}
message BandIBCParams {
// true if Band IBC should be enabled
bool band_ibc_enabled = 1;
// block request interval to send Band IBC prices
int64 ibc_request_interval = 2;
// band IBC source channel
string ibc_source_channel = 3;
// band IBC version
string ibc_version = 4;
// band IBC portID
string ibc_port_id = 5;
}
```
Note:
1. `BandIbcEnabled` describes the status of band ibc connection
2. `IbcSourceChannel`, `IbcVersion`, `IbcPortId` are common parameters required for IBC connection.
3. `IbcRequestInterval` describes the automatic price fetch request interval that is automatically triggered on injective chain on beginblocker.
## Coinbase
Coinbase price data for a given symbol ("key") are represented and stored as follows:
* CoinbasePriceState: `0x21 | []byte(key) -> CoinbasePriceState`
```protobuf theme={null}
message CoinbasePriceState {
// kind should always be "prices"
string kind = 1;
// timestamp of the when the price was signed by coinbase
uint64 timestamp = 2;
// the symbol of the price, e.g. BTC
string key = 3;
// the value of the price scaled by 1e6
uint64 value = 4;
// the price state
PriceState price_state = 5 [(gogoproto.nullable) = false];
}
```
More details about the Coinbase price oracle can be found in the [Coinbase API docs](https://docs.pro.coinbase.com/#oracle) as well as this explanatory [blog post](https://blog.coinbase.com/introducing-the-coinbase-price-oracle-6d1ee22c7068).
Note that the `Value` is the raw USD price data obtained from Coinbase which has is scaled by 1e6 (e.g. a price of 1.42 is 1420000) while the PriceState has the normalized decimal price (e.g. 1.42).
## Pricefeed
Pricefeed price data for a given base quote pair are represented and stored as follows:
* PriceFeedInfo: `0x11 + Keccak256Hash(base + quote) -> PriceFeedInfo`
```protobuf theme={null}
message PriceFeedInfo {
string base = 1;
string quote = 2;
}
```
* PriceFeedPriceState: `0x12 + Keccak256Hash(base + quote) -> PriceFeedPriceState`
```protobuf theme={null}
message PriceFeedState {
string base = 1;
string quote = 2;
PriceState price_state = 3;
repeated string relayers = 4;
}
```
* PriceFeedRelayer: `0x13 + Keccak256Hash(base + quote) + relayerAddr -> relayerAddr`
## Provider
Provider price feeds are represented and stored as follows:
* ProviderInfo: `0x61 + provider + @@@ -> ProviderInfo`
```protobuf theme={null}
message ProviderInfo {
string provider = 1;
repeated string relayers = 2;
}
```
* ProviderIndex: `0x62 + relayerAddress -> provider`
* ProviderPrices: `0x63 + provider + @@@ + symbol -> ProviderPriceState`
```protobuf theme={null}
message ProviderPriceState {
string symbol = 1;
PriceState state = 2;
}
```
## Pyth
Pyth prices are represented and stored as follows:
* PythPriceState: `0x71 + priceID -> PythPriceState`
```protobuf theme={null}
message PythPriceState {
bytes price_id = 1;
string ema_price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
string ema_conf = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
string conf = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
uint64 publish_time = 5;
PriceState price_state = 6 [(gogoproto.nullable) = false];
}
```
## Stork
Stork prices are represented and stored as follows:
* StorkPriceState: `0x81 + symbol -> PythPriceState`
```protobuf theme={null}
message StorkPriceState {
// timestamp of the when the price was signed by stork
uint64 timestamp = 1;
// the symbol of the price, e.g. BTC
string symbol = 2;
// the value of the price scaled by 1e18
string value = 3 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// the price state
PriceState price_state = 5 [ (gogoproto.nullable) = false ];
}
```
Stork publishers are represented and stored as follows:
* Publisher: `0x82 + stork_publisher -> publisher`
```protobuf theme={null}
string stork_publisher
```
# Keepers
Source: https://docs.injective.network/developers-native/injective/oracle/02_keeper
# Keepers
The oracle module currently provides three different exported keeper interfaces which can be passed to other modules
which need to read price feeds. Modules should use the least-permissive interface which provides the functionality they
require.
## Oracle Module ViewKeeper
The oracle module ViewKeeper provides the ability to obtain price data as well as cumulative price data for any
supported oracle type and oracle pair.
```go theme={null}
type ViewKeeper interface {
GetPrice(ctx sdk.Context, oracletype types.OracleType, base string, quote string) *math.LegacyDec // Returns the price for a given pair for a given oracle type.
GetCumulativePrice(ctx sdk.Context, oracleType types.OracleType, base string, quote string) *math.LegacyDec // Returns the cumulative price for a given pair for a given oracle type.
}
```
Note that the `GetPrice` for Coinbase oracles returns the 5 minute TWAP price.
## Band
The BandKeeper provides the ability to create/modify/read/delete BandPricefeed and BandRelayer.
```go theme={null}
type BandKeeper interface {
GetBandPriceState(ctx sdk.Context, symbol string) *types.BandPriceState
SetBandPriceState(ctx sdk.Context, symbol string, priceState types.BandPriceState)
GetAllBandPriceStates(ctx sdk.Context) []types.BandPriceState
GetBandReferencePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
IsBandRelayer(ctx sdk.Context, relayer sdk.AccAddress) bool
GetAllBandRelayers(ctx sdk.Context) []string
SetBandRelayer(ctx sdk.Context, relayer sdk.AccAddress)
DeleteBandRelayer(ctx sdk.Context, relayer sdk.AccAddress)
}
```
## Band IBC
The BandIBCKeeper provides the ability to create/modify/read/delete BandIBCOracleRequest, BandIBCPriceState, BandIBCLatestClientID and BandIBCCallDataRecord.
```go theme={null}
type BandIBCKeeper interface {
SetBandIBCOracleRequest(ctx sdk.Context, req types.BandOracleRequest)
GetBandIBCOracleRequest(ctx sdk.Context) *types.BandOracleRequest
DeleteBandIBCOracleRequest(ctx sdk.Context, requestID uint64)
GetAllBandIBCOracleRequests(ctx sdk.Context) []*types.BandOracleRequest
GetBandIBCPriceState(ctx sdk.Context, symbol string) *types.BandPriceState
SetBandIBCPriceState(ctx sdk.Context, symbol string, priceState types.BandPriceState)
GetAllBandIBCPriceStates(ctx sdk.Context) []types.BandPriceState
GetBandIBCReferencePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
GetBandIBCLatestClientID(ctx sdk.Context) uint64
SetBandIBCLatestClientID(ctx sdk.Context, clientID uint64)
SetBandIBCCallDataRecord(ctx sdk.Context, clientID uint64, bandIBCCallDataRecord []byte)
GetBandIBCCallDataRecord(ctx sdk.Context, clientID uint64) *types.CalldataRecord
}
```
## Coinbase
The CoinbaseKeeper provides the ability to create, modify and read CoinbasePricefeed data.
```go theme={null}
type CoinbaseKeeper interface {
GetCoinbasePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
HasCoinbasePriceState(ctx sdk.Context, key string) bool
GetCoinbasePriceState(ctx sdk.Context, key string) *types.CoinbasePriceState
SetCoinbasePriceState(ctx sdk.Context, priceData *types.CoinbasePriceState) error
GetAllCoinbasePriceStates(ctx sdk.Context) []*types.CoinbasePriceState
}
```
The `GetCoinbasePrice` returns the 5 minute TWAP price of the CoinbasePriceState based off the `CoinbasePriceState.Timestamp` values provided by Coinbase.
## PriceFeeder
The PriceFeederKeeper provides the ability to create/modify/read/delete PriceFeedPrice and PriceFeedRelayer.
```go theme={null}
type PriceFeederKeeper interface {
IsPriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress) bool
GetAllPriceFeedStates(ctx sdk.Context) []*types.PriceFeedState
GetAllPriceFeedRelayers(ctx sdk.Context, baseQuoteHash common.Hash) []string
SetPriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress)
SetPriceFeedRelayerFromBaseQuoteHash(ctx sdk.Context, baseQuoteHash common.Hash, relayer sdk.AccAddress)
DeletePriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress)
HasPriceFeedInfo(ctx sdk.Context, priceFeedInfo *types.PriceFeedInfo) bool
GetPriceFeedInfo(ctx sdk.Context, baseQuoteHash common.Hash) *types.PriceFeedInfo
SetPriceFeedInfo(ctx sdk.Context, priceFeedInfo *types.PriceFeedInfo)
GetPriceFeedPriceState(ctx sdk.Context, base string, quote string) *types.PriceState
SetPriceFeedPriceState(ctx sdk.Context, oracleBase, oracleQuote string, priceState *types.PriceState)
GetPriceFeedPrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
}
```
## Stork
The StorkKeeper provides the ability to create/modify/read StorkPricefeed and StorkPublishers data.
```go theme={null}
type StorkKeeper interface {
GetStorkPrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
IsStorkPublisher(ctx sdk.Context, address string) bool
SetStorkPublisher(ctx sdk.Context, address string)
DeleteStorkPublisher(ctx sdk.Context, address string)
GetAllStorkPublishers(ctx sdk.Context) []string
SetStorkPriceState(ctx sdk.Context, priceData *types.StorkPriceState)
GetStorkPriceState(ctx sdk.Context, symbol string) types.StorkPriceState
GetAllStorkPriceStates(ctx sdk.Context) []*types.StorkPriceState
}
```
The GetStorkPrice returns the price(`value`) of the StorkPriceState.
# Messages
Source: https://docs.injective.network/developers-native/injective/oracle/03_messages
# Messages
## MsgRelayBandRates
Authorized Band relayers can relay price feed data for multiple symbols with the `MsgRelayBandRates` message.
The registered handler iterates over all the symbols present in the `MsgRelayBandRates` and creates/updates the
`BandPriceState` for each symbol.
```protobuf theme={null}
message MsgRelayBandRates {
string relayer = 1;
repeated string symbols = 2;
repeated uint64 rates = 3;
repeated uint64 resolve_times = 4;
repeated uint64 requestIDs = 5;
}
```
This message is expected to fail if the Relayer is not an authorized Band relayer.
## MsgRelayCoinbaseMessages
Relayers of Coinbase provider can send price data using `MsgRelayCoinbaseMessages` message.
Each Coinbase `Messages` is authenticated by the `Signatures` provided by the Coinbase oracle address `0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC`, thus allowing anyone to submit the `MsgRelayCoinbaseMessages`.
```protobuf theme={null}
message MsgRelayCoinbaseMessages {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
repeated bytes messages = 2;
repeated bytes signatures = 3;
}
```
This message is expected to fail if signature verification fails or if the Timestamp submitted is not more recent than the last previously submitted Coinbase price.
## MsgRelayPriceFeedPrice
Relayers of PriceFeed provider can send the price feed using `MsgRelayPriceFeedPrice` message.
```protobuf theme={null}
// MsgRelayPriceFeedPrice defines a SDK message for setting a price through the pricefeed oracle.
message MsgRelayPriceFeedPrice {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
repeated string base = 2;
repeated string quote = 3;
// price defines the price of the oracle base and quote
repeated string price = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
}
```
This message is expected to fail if the Relayer (`Sender`) is not an authorized pricefeed relayer for the given Base Quote pair or if the price is greater than 10000000.
## MsgRequestBandIBCRates
`MsgRequestBandIBCRates` is a message to instantly broadcast a request to bandchain.
```protobuf theme={null}
// MsgRequestBandIBCRates defines a SDK message for requesting data from BandChain using IBC.
message MsgRequestBandIBCRates {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
uint64 request_id = 2;
}
```
Anyone can broadcast this message and no specific authorization is needed.
The handler checks if `BandIbcEnabled` flag is true and go ahead sending a request.
## MsgRelayPythPrices
`MsgRelayPythPrices` is a message for the Pyth contract relay prices to the oracle module.
```protobuf theme={null}
// MsgRelayPythPrices defines a SDK message for updating Pyth prices
message MsgRelayPythPrices {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string sender = 1;
repeated PriceAttestation price_attestations = 2;
}
message PriceAttestation {
string product_id = 1;
bytes price_id = 2;
int64 price = 3;
uint64 conf = 4;
int32 expo = 5;
int64 ema_price = 6;
uint64 ema_conf = 7;
PythStatus status = 8;
uint32 num_publishers = 9;
uint32 max_num_publishers = 10;
int64 attestation_time = 11;
int64 publish_time = 12;
}
enum PythStatus {
// The price feed is not currently updating for an unknown reason.
Unknown = 0;
// The price feed is updating as expected.
Trading = 1;
// The price feed is not currently updating because trading in the product has been halted.
Halted = 2;
// The price feed is not currently updating because an auction is setting the price.
Auction = 3;
}
```
This message is expected to fail if the Relayer (`sender`) does not equal the Pyth contract address as defined in the
oracle module Params.
## MsgRelayStorkPrices
`MsgRelayStorkPrices` is a message for the Stork contract relay prices to the oracle module.
```protobuf theme={null}
// MsgRelayStorkPrices defines a SDK message for relaying price message from Stork API.
message MsgRelayStorkPrices {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (cosmos.msg.v1.signer) = "sender";
string sender = 1;
repeated AssetPair asset_pairs = 2;
}
message AssetPair {
string asset_id = 1;
repeated SignedPriceOfAssetPair signed_prices = 2;
}
message SignedPriceOfAssetPair {
string publisher_key = 1;
uint64 timestamp = 2;
string price = 3 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
bytes signature = 4;
}
```
This message is expected to fail if:
* the Relayer (`sender`) is not an authorized oracle publisher or if `assetId` is not unique amongst the provided asset pairs
* ECDSA signature verification fails for the `SignedPriceOfAssetPair`
* the difference between timestamps exceeds the `MaxStorkTimestampIntervalNano` (500 milliseconds).
## MsgRelayProviderPrices
Relayers of a particular Provider can send the price feed using `MsgRelayProviderPrices` message.
```protobuf theme={null}
// MsgRelayProviderPrice defines a SDK message for setting a price through the provider oracle.
message MsgRelayProviderPrices {
option (amino.name) = "oracle/MsgRelayProviderPrices";
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (cosmos.msg.v1.signer) = "sender";
string sender = 1;
string provider = 2;
repeated string symbols = 3;
repeated string prices = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
}
```
This message is expected to fail if the Relayer (`Sender`) is not an authorized pricefeed relayer for the given Base Quote pair or if the price is greater than 10000000.
# Governance Proposals
Source: https://docs.injective.network/developers-native/injective/oracle/04_proposals
# Governance Proposals
## GrantBandOraclePrivilegeProposal
Band Oracle privileges can be granted to Relayer accounts of Band provider through a `GrantBandOraclePrivilegeProposal`.
```protobuf theme={null}
// Grant Privileges
message GrantBandOraclePrivilegeProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
repeated string relayers = 3;
}
```
## RevokeBandOraclePrivilegeProposal
Band Oracle privileges can be revoked from Relayer accounts of Band provider through a `RevokeBandOraclePrivilegeProposal`.
```protobuf theme={null}
// Revoke Privileges
message RevokeBandOraclePrivilegeProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
repeated string relayers = 3;
}
```
## GrantPriceFeederPrivilegeProposal
Price feeder privileges for a given base quote pair can be issued to relayers through a `GrantPriceFeederPrivilegeProposal`.
```protobuf theme={null}
// Grant Privileges
message GrantPriceFeederPrivilegeProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
string base = 3;
string quote = 4;
repeated string relayers = 5;
}
```
## RevokePriceFeederPrivilegeProposal
Price feeder privileges can be revoked from Relayer accounts through a `RevokePriceFeederPrivilegeProposal`.
```protobuf theme={null}
// Revoke Privileges
message RevokePriceFeederPrivilegeProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
string base = 3;
string quote = 4;
repeated string relayers = 5;
}
```
## AuthorizeBandOracleRequestProposal
This proposal is to add a band oracle request into the list. When this is accepted, injective chain fetches one more price info from bandchain.
```protobuf theme={null}
message AuthorizeBandOracleRequestProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
BandOracleRequest request = 3 [(gogoproto.nullable) = false];
}
```
## UpdateBandOracleRequestProposal
This proposal is used for deleting a request or updating the request.
When `DeleteRequestId` is not zero, it deletes the request with the id and finish its execution.
When `DeleteRequestId` is zero, it update the request with id `UpdateOracleRequest.RequestId` to UpdateOracleRequest.
```protobuf theme={null}
message UpdateBandOracleRequestProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
uint64 delete_request_id = 3;
BandOracleRequest update_oracle_request = 4;
}
```
## EnableBandIBCProposal
This proposal is to enable IBC connection between Band chain and Injective chain.
When the proposal is approved, it updates the BandIBCParams into newer one configured on the proposal.
```protobuf theme={null}
message EnableBandIBCProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string title = 1;
string description = 2;
BandIBCParams band_ibc_params = 3 [(gogoproto.nullable) = false];
}
```
The details of `BandIBCParams`, can be checked at **[State](./01_state)**
## GrantStorkPublisherPrivilegeProposal
Stork Publisher privileges can be granted from Publishers through a `GrantStorkPublisherPrivilegeProposal`.
```protobuf theme={null}
// Grant Privileges
message GrantStorkPublisherPrivilegeProposal {
option (amino.name) = "oracle/GrantStorkPublisherPrivilegeProposal";
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
string title = 1;
string description = 2;
repeated string stork_publishers = 3;
}
```
## RevokeStorkPublisherPrivilegeProposal
Stork Publisher privileges can be revoked from Publishers through a `RevokeStorkPublisherPrivilegeProposal`.
```protobuf theme={null}
// Revoke Privileges
message RevokeStorkPublisherPrivilegeProposal {
option (amino.name) = "oracle/RevokeStorkPublisherPrivilegeProposal";
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
string title = 1;
string description = 2;
repeated string stork_publishers = 3;
}
```
# Events
Source: https://docs.injective.network/developers-native/injective/oracle/05_events
# Events
The oracle module emits the following events:
## Band
```protobuf theme={null}
message SetBandPriceEvent {
string relayer = 1;
string symbol = 2;
string price = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
uint64 resolve_time = 4;
uint64 request_id = 5;
}
message SetBandIBCPriceEvent {
string relayer = 1;
repeated string symbols = 2;
repeated string prices = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
uint64 resolve_time = 4;
uint64 request_id = 5;
int64 client_id = 6;
}
message EventBandIBCAckSuccess {
string ack_result = 1;
int64 client_id = 2;
}
message EventBandIBCAckError {
string ack_error = 1;
int64 client_id = 2;
}
message EventBandIBCResponseTimeout {
int64 client_id = 1;
}
```
## Chainlink
```protobuf theme={null}
message SetChainlinkPriceEvent {
string feed_id = 1;
string answer = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
uint64 timestamp = 3;
}
```
## Coinbase
```protobuf theme={null}
message SetCoinbasePriceEvent {
string symbol = 1;
string price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
uint64 timestamp = 3;
}
```
## Provider
```protobuf theme={null}
message SetProviderPriceEvent {
string provider = 1;
string relayer = 2;
string symbol = 3;
string price = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
}
```
## Pricefeed
```protobuf theme={null}
message SetPriceFeedPriceEvent {
string relayer = 1;
string base = 2;
string quote = 3;
// price defines the price of the oracle base and quote
string price = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
}
```
## Pyth
```protobuf theme={null}
message EventSetPythPrices {
repeated PythPriceState prices = 1;
}
```
## Stork
```protobuf theme={null}
message EventSetStorkPrices {
repeated StorkPriceState prices = 1;
}
```
# Future Improvements
Source: https://docs.injective.network/developers-native/injective/oracle/06_future_improvements
# Future Improvements
Extend support for other oracle providers including Chainlink, Razor, DIA, API3, UMA, Pyth as well as Band oracle data through IBC.
Of the following above, development is ongoing to integrate Band and Pyth oracle data through IBC, as well as Chainlink oracle data through the OCR (off-chain reporting) mechanism.
# Error Codes
Source: https://docs.injective.network/developers-native/injective/oracle/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------ | ---------- | ---------------------------------------- |
| oracle | 1 | relayer address is empty |
| oracle | 2 | bad rates count |
| oracle | 3 | bad resolve times |
| oracle | 4 | bad request ID |
| oracle | 5 | relayer not authorized |
| oracle | 6 | bad price feed base count |
| oracle | 7 | bad price feed quote count |
| oracle | 8 | unsupported oracle type |
| oracle | 9 | bad messages count |
| oracle | 10 | bad Coinbase message |
| oracle | 11 | bad Ethereum signature |
| oracle | 12 | bad Coinbase message timestamp |
| oracle | 13 | Coinbase price not found |
| oracle | 14 | Prices must be positive |
| oracle | 15 | Prices must be less than 10 million. |
| oracle | 16 | Invalid Band IBC Request |
| oracle | 17 | sample error |
| oracle | 18 | invalid packet timeout |
| oracle | 19 | invalid symbols count |
| oracle | 20 | could not claim port capability |
| oracle | 21 | invalid IBC Port ID |
| oracle | 22 | invalid IBC Channel ID |
| oracle | 23 | invalid Band IBC request interval |
| oracle | 24 | Invalid Band IBC Update Request Proposal |
| oracle | 25 | Band IBC Oracle Request not found |
| oracle | 26 | Base Info is empty |
| oracle | 27 | provider is empty |
| oracle | 28 | invalid provider name |
| oracle | 29 | invalid symbol |
| oracle | 30 | relayer already exists |
| oracle | 31 | provider price not found |
| oracle | 32 | invalid oracle request |
| oracle | 33 | no price for oracle was found |
| oracle | 34 | no address for Pyth contract found |
| oracle | 35 | unauthorized Pyth price relay |
| oracle | 36 | unauthorized Pyth price relay |
| oracle | 37 | unauthorized Pyth price relay |
| oracle | 38 | unauthorized Pyth price relay |
| oracle | 39 | empty price attestations |
| oracle | 40 | bad Stork message timestamp |
| oracle | 41 | sender stork is empty |
| oracle | 42 | invalid stork signature |
| oracle | 43 | stork asset id not unique |
# Oracle
Source: https://docs.injective.network/developers-native/injective/oracle/index
## Abstract
This specification specifies the oracle module, which is primarily used by the `exchange` modules to obtain external price data.
## Workflow
1. New price feed providers must first obtain oracle privileges through a governance proposal which grants privileges to a list of relayers. The exception to this is for the Coinbase price oracle, as anyone can send Coinbase price updates since they are already exclusively signed by the Coinbase oracle private key.
**Example Grant Proposals**: `GrantBandOraclePrivilegeProposal`, `GrantPriceFeederPrivilegeProposal`
2. Once the governance proposal is approved, the specified relayers can publish oracle data by sending relay messages specific to their oracle type.
**Example Relay Messages**:`MsgRelayBandRates`, `MsgRelayPriceFeedPrice`, `MsgRelayCoinbaseMessages` etc
3. Upon receiving the relay message, the oracle module checks if the relayer account has grant privileges and persists the latest price data in the state.
4. Other Cosmos-SDK modules can then fetch the latest price data for a specific provider by querying the oracle module.
**Note**: In case of any discrepancy, the price feed privileges can be revoked through governance
**Example Revoke Proposals**: `RevokeBandOraclePrivilegeProposal`, `RevokePriceFeederPrivilegeProposal` etc
## Band IBC integration flow
Cosmos SDK blockchains are able to interact with each other using IBC and Injective support the feature to fetch price feed from bandchain via IBC.
1. To communicate with BandChain's oracle using IBC, Injective Chain must first initialize a communication channel with the oracle module on the BandChain using relayers.
2. Once the connection has been established, a pair of channel identifiers is generated -- one for the Injective Chain and one for Band. The channel identifier is used by Injective Chain to route outgoing oracle request packets to Band. Similarly, Band's oracle module uses the channel identifier when sending back the oracle response.
3. To enable band IBC integration after setting up communication channel, the governance proposal for `EnableBandIBCProposal` should pass.
4. And then, the list of prices to be fetched via IBC should be determined by `AuthorizeBandOracleRequestProposal` and `UpdateBandOracleRequestProposal`.
5. Once BandIBC is enabled, chain periodically sends price request IBC packets (`OracleRequestPacketData`) to bandchain and bandchain responds with the price via IBC packet (`OracleResponsePacketData`). Band chain is providing prices when there are threshold number of data providers confirm and it takes time to get the price after sending requests. To request price before the configured interval, any user can broadcast `MsgRequestBandIBCRates` message which is instantly executed.
## Contents
1. [State](/developers-native/injective/oracle/01_state)
2. [Keeper](/developers-native/injective/oracle/02_keeper)
3. [Messages](/developers-native/injective/oracle/03_messages)
4. [Proposals](/developers-native/injective/oracle/04_proposals)
5. [Events](/developers-native/injective/oracle/05_events)
6. [Improvements](/developers-native/injective/oracle/06_future_improvements)
# Definitions
Source: https://docs.injective.network/developers-native/injective/peggy/01_definitions
# Intro
This doc aims to provide an overview of `Peggy` (Injective's Ethereum bridge) from a technical perspective and dive deep into its operational logic.
Peggy is the name of the custom Cosmos SDK module built on Injective as well as the Ethereum contract (Peggy.sol) which make up both sides of the bridge.
Connected via a middle-man process called `Peggo` users can securely move token assets between networks.
To suggest improvements, please open a GitHub issue.
### Key definitions
Words matter and we seek clarity in the terminology so we can have clarity in our thinking and communication.
To help better understand, some key definitions are:
* `Operator` - this is a person (or people) who control and operate `Validator` and `Orchestrator` processes
* `Validator` - this is an Injective Chain validating node (eg. `injectived` process)
* `Validator Set` - the (active) set of Injective Chain `Validators` (Valset) along with their respective voting power as determined by their stake weight. Each validator is associated with an Ethereum address to be represented on the Ethereum network
* `Orchestrator (Peggo)` - the off-chain process (`peggo`) that plays the middleman role between Injective and Ethereum. Orchestrators are responsible for keeping the bridge online and require active endpoints to fully synced Injective (Ethereum) nodes
* `Peggy module` - the counterparty Cosmos module for `Peggy contract`. Besides providing services to bridge token assets, it automatically reflects on the active `Validator Set` as it changes over time. The update is later applied on Ethereum via `Peggo`
* `Peggy Contract` - The Ethereum contract that holds all the ERC-20 tokens. It also maintains a compressed checkpointed representation of the Injective Chain `Validator Set` using `Delegate Keys` and normalized powers
* `Delegate Keys` - when an `Operator` sets up their `Orchestrator` for the first time they register (on Injective) their `Validator`'s address with an Ethereum address. The corresponding key is used to sign messages and represent that validator on Ethereum.
Optionally, one delegate Injective Chain account key can be provided to sign Injective messages (eg `Claims`) on behalf of the `Validator`
* `Peggy Tx pool (withdrawals)` - when a user wishes to move their asset from Injective to Ethereum their individual tx gets pooled with others with the same asset
* `Peggy Batch pool` - pooled withdrawals are batched together (by an `Orchestrator`) to be signed off and eventually relayed to Ethereum. These batches are kept within this pool
* `Claim` - a signed proof (by an `Orchestrator`) that an event occurred in the `Peggy contract`
* `Attestation` - an aggregate of claims for a particular event nonce emitted from `Peggy contract`. After a majority of `Orchestrators` attests to a claim, the event is acknowledged and executed on Injective
* `Majority` - the majority of Injective network, 2/3 + 1 validators
* `Deposit` - an asset transfer initiated from Ethereum to Injective
* `Withdrawal` - an asset transfer initiated from Injective to Ethereum (present in `Peggy Tx pool`)
* `Batch` - a batch of withdrawals with the same token type (present in `Peggy Batch pool`)
# Workflow
Source: https://docs.injective.network/developers-native/injective/peggy/02_workflow
# Workflow
## Conceptual Overview
To recap, each `Operator` is responsible for maintaining 2 secure processes:
1. A fully synced Injective Chain `Validator` node (`injectived` process)
2. The `Orchestrator` service (`peggo orchestrator` process) which interacts with both networks. Implicitly, an RPC endpoint to a fully synced Ethereum node is required as well (see peggo .env example)
Combined, these 2 entities accomplish 3 things:
* Move token assets from Ethereum to Injective
* Move token assets from Injective to Ethereum
* Keep the `Peggy.sol` contract in sync with the active `Validator Set` on Injective
It is possible to run `peggo` without ever being a `Validator`. Peggo automatically runs in "relayer mode" when configured to run with an address **not associated** with a `Validator`.
In this mode, only 2 things can happen:
* new token batches can be created on Injective
* confirmed valsets/batches can be relayed to Ethereum
## Types of Assets
### Native Ethereum assets
Any asset originating from Ethereum which implements the ERC-20 standard can be transferred from Ethereum to Injective by calling the `sendToInjective` function on the [Peggy.sol](https://github.com/InjectiveLabs/peggo/blob/master/solidity/contracts/Peggy.sol) contract which transfers tokens from the sender's balance to the Peggy contract.
The `Operators` all run their `peggo` processes which submit `MsgDepositClaim` messages describing the deposit they have observed. Once more than 66% of all voting power has submitted a claim for this specific deposit representative tokens are minted and issued to the Injective Chain address that the sender requested.
These representative tokens have a denomination prefix of `peggy` concatenated with the ERC-20 token hex address, e.g. `peggy0xdac17f958d2ee523a2206206994597c13d831ec7`.
### Native Cosmos SDK assets
An asset native to a Cosmos SDK chain (e.g. `ATOM`) first must be represented on Ethereum before it's possible to bridge it. To do so, the [Peggy contract](https://github.com/InjectiveLabs/peggo/blob/master/solidity/contracts/Peggy.sol) allows anyone to create a new ERC-20 token representing a Cosmos asset by calling the `deployERC20` function.
This endpoint is not permissioned, so it is up to the validators and the users of the Peggy bridge to declare any given ERC-20 token as the representation of a given asset.
When a user on Ethereum calls `deployERC20` they pass arguments describing the desired asset. [Peggy.sol](https://github.com/InjectiveLabs/peggo/blob/master/solidity/contracts/Peggy.sol) uses an ERC-20 factory to deploy the actual ERC-20 contract and assigns ownership of the entire balance of the new token to the Peggy contract itself before emitting an `ERC20DeployedEvent`.
The peggo orchestrators observe this event and decide if a Cosmos asset has been accurately represented (correct decimals, correct name, no pre-existing representation). If this is the case, the ERC-20 contract address is adopted and stored as the definitive representation of that Cosmos asset on Ethereum.
## `Orchestrator` (Peggo) subprocesses
The `peggo orchestrator` process consists of 4 subprocesses running concurrently at exact intervals (loops). These are:
* `Signer` which signs new `Validator Set` updates and `Token Batches` with the `Operator`'s Ethereum keys and submits using [messages](./04_messages.md#Ethereum-Signer-messages).
* `Oracle` which observes Ethereum events and sends them as [claims](./04_messages.md#Oracle-messages) to Injective.
* `Relayer` which submits confirmed `Validator Set` updates and `Token Batches` to the `Peggy Contract` on Ethereum
* `Batch Creator` which observes (new) withdrawals on Injective and decides which of these to batch according to their type and the configured `PEGGO_MIN_BATCH_FEE_USD` value
### Batch Creator
The purpose of the `Batch Creator` is only in creating token batches on the Injective side. The relevant `Peggy module` RPC is not permissioned so anyone can create a batch.
When a user wants to withdraw assets from Injective to Ethereum they send a special message to Injective (`MsgSendToEth`) which adds their withdrawal to `Peggy Tx Pool`.
`Batch Creator` continually queries the pool for withdrawals (by token type) and issues a `MsgRequestBatch` to Injective when a potential batch satisfies the configured `PEGGO_MIN_BATCH_FEE_USD` value (see .env example).
On the receiving end, all pooled withdrawals matching the token type in the request are moved from the `Outgoing Tx Pool` as a single batch and placed in the `Outgoing Batch Pool`.
### Signer
The responsibility of Signer is to provide confirmations that an `Operator (Orchestrator)` is partaking in bridge activity. Failure to provide these confirmations results in slashing penalties for the orchestrator's `Validator`.
In other words, this process **must be running at all times** for a `Validator` node.
Any payload moving in the Injective->Ethereum pipeline (`Validator Set` updates/`Token Batches`) requires `Validator` signatures to be successfully relayed to Ethereum. Certain calls on `Peggy Contract` accept an array of signatures to be checked against the `Validator Set` in the contract itself.
`Orchestrators` make these signatures with their `Delegate Ethereum address`: this is an Ethereum address decided by the `Operator` upon initial setup ([SetOrchestratorAddress](./04_messages.md#setorchestratoraddresses)). This address then represents that validator on the Ethereum blockchain and will be added as a signing member of the multisig with a weighted voting power as close as possible to the Injective Chain voting power.
Whenever `Signer` finds that there is a unconfirmed valset update (token batch) present within the `Peggy Module` it issues a `MsgConfirmValset` (`MsgConfirmBatch`) as proof that the operating `Validator` is active in bridge activity.
### Oracle
Monitors the Ethereum network for new events involving the `Peggy Contract`.
Every event emitted by the contract has a unique event nonce. This nonce value is crucial in coordinating `Orchestrators` to properly observe contract activity and make sure Injective acknowledges them via `Claims`.
Multiple claims of the same nonce make up an `Attestation` and when the majority (2/3) of orchestrators have observed an event its particular logic gets executed on Injective.
If 2/3 of the validators can not agree on a single `Attestation`, the oracle is halted. This means no new events will be relayed from Ethereum until some of the validators change their votes. There is no slashing condition for this, with reasoning outlined in the [slashing spec](./05_slashing)
There are 4 types of events emitted from Peggy.sol:
1. `TransactionBatchExecutedEvent` - event indicating that a token batch (withdrawals) has been successfully relayed to Ethereum
2. `ValsetUpdatedEvent` - event indicating that a `Validator Set` update has been successfully relayed to Ethereum
3. `SendToInjectiveEvent` - event indicating that a new deposit to Injective has been initiated
4. `ERC20DeployedEvent` - event indicating a new Cosmos token has been registered on Ethereum
Injective's `Oracle` implementation ignores the last 12 blocks on Ethereum to ensure block finality. In reality, this means latest events are observed 2-3 minutes after they occurred.
### Relayer
`Relayer` bundles valset updates (or token batches) along with their confirmations into an Ethereum transaction and sends it to the `Peggy contract`.
Keep in mind that these messages cost a variable amount of money based on wildly changing Ethereum gas prices, so it's not unreasonable for a single batch to cost over a million gas.
A major design decision for our relayer rewards was to always issue them on the Ethereum chain. This has downsides, namely some strange behavior in the case of validator set update rewards.
But the upsides are undeniable, because the Ethereum messages pay `msg.sender` any existing bot in the Ethereum ecosystem will pick them up and try to submit them. This makes the relaying market much more competitive and less prone to cabal like behavior.
## End-to-end Lifecycle
This document describes the end to end lifecycle of the Peggy bridge.
### Peggy Smart Contract Deployment
In order to deploy the Peggy contract, the validator set of the native chain (Injective Chain) must be known. Upon deploying the Peggy contract suite (Peggy Implementation, Proxy contract, and ProxyAdmin contracts), the Peggy contract (the Proxy contract) must be initialized with the validator set.
Upon initialization a `ValsetUpdatedEvent` is emitted from the contract.
The proxy contract is used to upgrade Peggy Implementation contract which is needed for bug fixing and potential improvements during initial phase. It is a simple wrapper or "proxy" which users interact with directly and is in charge of forwarding transactions to the Peggy implementation contract, which contains the logic. The key concept to understand is that the implementation contract can be replaced but the proxy (the access point) is never changed.
The ProxyAdmin is a central admin for the Peggy proxy, which simplifies management. It controls upgradability and ownership transfers. The ProxyAdmin contract itself has a built-in expiration time which, once expired, prevents the Peggy implementation contract from being upgraded in the future.
Then the following peggy genesis params should be updated:
1. `bridge_ethereum_address` with Peggy proxy contract address
2. `bridge_contract_start_height` with the height at which the Peggy proxy contract was deployed
This completes the bootstrap of the Peggy bridge and the chain can be started. Afterward, `Operators` should start their `peggo` processes and eventually observe that the initial `ValsetUpdatedEvent` is attested on Injective.
### **Updating Injective Chain validator set on Ethereum**
A validator set is a series of Ethereum addresses with attached normalized powers used to represent the Injective validator set (Valset) in the Peggy contract on Ethereum. The Peggy contract stays in sync with the Injective Chain validator set through the following mechanism:
1. **Creating a new Valset on Injective:** A new Valset is automatically created on the Injective Chain when either:
* the cumulative difference of the current validator set powers compared to the last recorded Valset exceeds 5%
* a validator begins unbonding from the set
2. **Confirming a Valset on Injective:** Each `Operator` is responsible for confirming Valset updates that are created on Injective. The `Signer` process sends these confirmations via `MsgConfirmValset` by having the validator's delegated Ethereum key sign over a compressed representation of the Valset data. The `Peggy module` verifies the validity of the signature and persists it to its state.
3. **Updating the Valset on the Peggy contract:** After a 2/3+ 1 majority of validators have submitted their confirmations for a given Valset, `Relayer` submits the new Valset data to the Peggy contract by calling `updateValset`.
The Peggy contract then validates the data, updates the valset checkpoint, transfers valset rewards to sender and emits a `ValsetUpdatedEvent`.
4. **Acknowledging the `ValsetUpdatedEvent` on Injective:** `Oracle` witnesses the `ValsetUpdatedEvent` on Ethereum, and sends a `MsgValsetUpdatedClaim` which informs the `Peggy module` that the Valset has been updated on Ethereum.
5. **Pruning Valsets on Injective:** Once a 2/3 majority of validators send their claim for a given `ValsetUpdateEvent`, all the previous valsets are pruned from the `Peggy module` state.
6. **Validator Slashing:** Validators are subject to slashing after a configured window of time (`SignedValsetsWindow`) for not providing confirmations. Read more [valset slashing](./05_slashing)
***
### **Transferring ERC-20 tokens from Ethereum to Injective**
ERC-20 tokens are transferred from Ethereum to Injective through the following mechanism:
1. **Depositing ERC-20 tokens on the Peggy Contract:** A user initiates a transfer of ERC-20 tokens from Ethereum to Injective by calling the `sendToInjective` function on the Peggy contract which deposits tokens on the Peggy contract and emits a `SendToInjectiveEvent`.
The deposited tokens will remain locked until withdrawn at some undetermined point in the future. This event contains the amount and type of tokens, as well as a destination address on the Injective Chain to receive the funds.
2. **Confirming the deposit:** Each `Oracle` witnesses the `SendToInjectiveEvent` and sends a `MsgDepositClaim` which contains the deposit information to the Peggy module.
3. **Minting tokens on the Injective:** Once a majority of validators confirm the deposit claim, the deposit is processed.
* If the asset is Ethereum originated, the tokens are minted and transferred to the intended recipient's address on the Injective Chain.
* If the asset is Cosmos-SDK originated, the coins are unlocked and transferred to the intended recipient's address on the Injective Chain.
***
### **Withdrawing tokens from Injective to Ethereum**
1. **Request Withdrawal from Injective:** A user can initiate the transfer of assets from the Injective Chain to Ethereum by sending a `MsgSendToEth` transaction to the peggy module.
* If the asset is Ethereum native, the represented tokens are burnt.
* If the asset is Cosmos SDK native, coins are locked in the module. The withdrawal is then added to `Outgoing Tx Pool`.
2. **Batch Creation:** A `Batch Creator` observes the pool of pending withdrawals. The batch creator (or any external third party) then requests a batch of to be created for given token by sending `MsgRequestBatch` to the Injective Chain. The `Peggy module` collects withdrawals matching the token type into a batch and puts it in `Outgoing Batch Pool`.
3. **Batch Confirmation:** Upon detecting the existence of an Outgoing Batch, the `Signer` signs over the batch with its Ethereum key and submits a `MsgConfirmBatch` tx to the Peggy module.
4. **Submit Batch to Peggy Contract:** Once a majority of validators confirm the batch, the `Relayer` calls `submitBatch` on the Peggy contract with the batch and its confirmations. The Peggy contract validates the signatures, updates the batch checkpoint, processes the batch ERC-20 withdrawals, transfers the batch fee to the tx sender and emits a `TransactionBatchExecutedEvent`.
5. **Send Withdrawal Claim to Injective:** `Oracles` witness the `TransactionBatchExecutedEvent` and send a `MsgWithdrawClaim` containing the withdrawal information to the Peggy module.
6. **Prune Batches** Once a majority of validators submit their `MsgWithdrawClaim` , the batch is deleted along and all previous batches are cancelled on the Peggy module. Withdrawals in cancelled batches get moved back into `Outgoing Tx Pool`.
7. **Batch Slashing:** Validators are responsible for confirming batches and are subject to slashing if they fail to do so. Read more on [batch slashing](./05_slashing).
Note while that batching reduces individual withdrawal costs dramatically, this comes at the cost of latency and implementation complexity. If a user wishes to withdraw quickly they will have to pay a much higher fee. However this fee will be about the same as the fee every withdrawal from the bridge would require in a non-batching system.
# State
Source: https://docs.injective.network/developers-native/injective/peggy/03_state
# State
This doc lists all the data Peggy module reads/writes to its state as KV pairs
### Module Params
Params is a module-wide configuration structure that stores parameters and defines overall functioning of the peggy module. Detailed specification for each parameter can be found in the [Parameters section](./08_params).
| key | Value | Type | Encoding |
| ------------- | ------------- | -------------- | ---------------- |
| `[]byte{0x4}` | Module params | `types.Params` | Protobuf encoded |
### Validator Info
#### Ethereum Address by Validator
Stores `Delegate Ethereum address` indexed by the `Validator`'s account address
| key | Value | Type | Encoding |
| ------------------------------------- | ---------------- | ---------------- | ---------------- |
| `[]byte{0x1} + []byte(validatorAddr)` | Ethereum address | `common.Address` | Protobuf encoded |
#### Validator by Ethereum Address
Stores `Validator` account address indexed by the `Delegate Ethereum address`
| key | Value | Type | Encoding |
| ----------------------------------- | ----------------- | ---------------- | ---------------- |
| `[]byte{0xfb} + []byte(ethAddress)` | Validator address | `sdk.ValAddress` | Protobuf encoded |
#### OrchestratorValidator
When a validator would like to delegate their voting power to another key. The value is stored using the orchestrator address as the key
| Key | Value | Type | Encoding |
| ----------------------------------- | -------------------------------------------- | -------- | ---------------- |
| `[]byte{0xe8} + []byte(AccAddress)` | Orchestrator address assigned by a validator | `[]byte` | Protobuf encoded |
### Valset
This is the validator set of the bridge. Created automatically by `Peggy module` during EndBlocker.
Stored in two possible ways, first with a height and second without (unsafe). Unsafe is used for testing and export and import of state.
```go theme={null}
type Valset struct {
Nonce uint64
Members []*BridgeValidator
Height uint64
RewardAmount math.Int
RewardToken string
}
```
| key | Value | Type | Encoding |
| ------------------------------------------ | ------------- | -------------- | ---------------- |
| `[]byte{0x2} + nonce (big endian encoded)` | Validator set | `types.Valset` | Protobuf encoded |
### SlashedValsetNonce
The latest validator set slash nonce. This is used to track which validator set needs to be slashed and which already has been.
| Key | Value | Type | Encoding |
| -------------- | ----- | ------ | ---------------------- |
| `[]byte{0xf5}` | Nonce | uint64 | encoded via big endian |
### ValsetNonce
Nonce of the latest validator set. Updated on each new validator set.
| key | Value | Type | Encoding |
| -------------- | ----- | -------- | ---------------------- |
| `[]byte{0xf6}` | Nonce | `uint64` | encoded via big endian |
### Valset Confirmation
`Singer` confirmation for a particular validator set. See [oracle messages](./04_messages.md#ValsetConfirm)
| Key | Value | Type | Encoding |
| ------------------------------------------- | ---------------------- | ------------------------ | ---------------- |
| `[]byte{0x3} + (nonce + []byte(AccAddress)` | Validator Confirmation | `types.MsgValsetConfirm` | Protobuf encoded |
### Batch Confirmation
`Singer` confirmation for a particular token batch. See [oracle messages](./04_messages.md#ConfirmBatch)
| Key | Value | Type | Encoding |
| ------------------------------------------------------------------- | ---------------------------- | ----------------------- | ---------------- |
| `[]byte{0xe1} + []byte(tokenContract) + nonce + []byte(AccAddress)` | Validator Batch Confirmation | `types.MsgConfirmBatch` | Protobuf encoded |
### OutgoingTransferTx
User withdrawals are pooled together in `Peggy Tx Pool` ready to be batched later by a `Batch Creator`.
Each withdrawal is indexed by a unique nonce set by the `Peggy module` when the withdrawal was received.
```go theme={null}
type OutgoingTransferTx struct {
Id uint64
Sender string
DestAddress string
Erc20Token *ERC20Token
Erc20Fee *ERC20Token
}
```
| Key | Value | Type | Encoding |
| -------------------------------------- | ---------------------------- | -------- | ------------------ |
| `[]byte{0x7} + []byte("lastTxPoolId")` | nonce of outgoing withdrawal | `uint64` | Big endian encoded |
### LastTXPoolID
Monotonically increasing value for each withdrawal received by Injective
| Key | Value | Type | Encoding |
| -------------------------------------- | ----------------------- | -------- | ------------------ |
| `[]byte{0x6} + []byte("lastTxPoolId")` | Last used withdrawal ID | `uint64` | Big endian encoded |
### OutgoingTxBatch
`OutgoingTxBatch` represents a collection of withdrawals of the same token type. Created on every successful `MsgRequestBatch`.
Stored in two possible ways, first with a height and second without (unsafe). Unsafe is used for testing and export and import of state.
Currently [Peggy.sol](https://github.com/InjectiveLabs/peggo/blob/master/solidity/contracts/Peggy.sol) is hardcoded to only accept batches with a single token type and only pay rewards in that same token type.
```go theme={null}
type OutgoingTxBatch struct {
BatchNonce uint64
BatchTimeout uint64
Transactions []*OutgoingTransferTx
TokenContract string
Block uint64
}
```
| key | Value | Type | Encoding |
| ------------------------------------------------------------------ | -------------------------------- | ----------------------- | ---------------- |
| `[]byte{0xa} + []byte(tokenContract) + nonce (big endian encoded)` | A batch of outgoing transactions | `types.OutgoingTxBatch` | Protobuf encoded |
| `[]byte{0xb} + block (big endian encoded)` | A batch of outgoing transactions | `types.OutgoingTxBatch` | Protobuf encoded |
### LastOutgoingBatchID
Monotonically increasing value for each batch created on Injective by some `Batch Creator`
| Key | Value | Type | Encoding |
| ------------------------------------- | ------------------ | -------- | ------------------ |
| `[]byte{0x7} + []byte("lastBatchId")` | Last used batch ID | `uint64` | Big endian encoded |
### SlashedBlockHeight
Represents the latest slashed block height. There is always only a singe value stored.
| Key | Value | Type | Encoding |
| -------------- | --------------------------------------- | -------- | ------------------ |
| `[]byte{0xf7}` | Latest height a batch slashing occurred | `uint64` | Big endian encoded |
### LastUnbondingBlockHeight
Represents the latest bloch height at which a `Validator` started unbonding from the `Validator Set`. Used to determine slashing conditions.
| Key | Value | Type | Encoding |
| -------------- | ---------------------------------------------------- | -------- | ------------------ |
| `[]byte{0xf8}` | Latest height at which a Validator started unbonding | `uint64` | Big endian encoded |
### TokenContract & Denom
A denom that is originally from a counter chain will be from a contract. The token contract and denom are stored in two ways. First, the denom is used as the key and the value is the token contract. Second, the contract is used as the key, the value is the denom the token contract represents.
| Key | Value | Type | Encoding |
| -------------------------------------- | ---------------------- | -------- | --------------------- |
| `[]byte{0xf3} + []byte(denom)` | Token contract address | `[]byte` | stored in byte format |
| `[]byte{0xf4} + []byte(tokenContract)` | Token denom | `[]byte` | stored in byte format |
### LastObservedValset
This entry represents the last observed Valset that was successfully relayed to Ethereum. Updates after an attestation of `ValsetUpdatedEvent` has been processed on Injective.
| Key | Value | Type | Encoding |
| -------------- | -------------------------------- | -------------- | ---------------- |
| `[]byte{0xfa}` | Last observed Valset on Ethereum | `types.Valset` | Protobuf encoded |
### LastEventNonce
The nonce of the last observed event on Ethereum. This is set when `TryAttestation()` is called. There is always only a single value held in this store.
| Key | Value | Type | Encoding |
| -------------- | ------------------------- | -------- | ------------------ |
| `[]byte{0xf2}` | Last observed event nonce | `uint64` | Big endian encoded |
### LastObservedEthereumHeight
This block height of the last observed event on Ethereum. There will always only be a single value stored in this store.
| Key | Value | Type | Encoding |
| -------------- | ----------------------------- | -------- | ---------------- |
| `[]byte{0xf9}` | Last observed Ethereum Height | `uint64` | Protobuf encoded |
### LastEventByValidator
This is the last observed event on Ethereum from a particular `Validator`. Updated every time the asssociated `Orchestrator` sends an event claim.
```go theme={null}
type LastClaimEvent struct {
EthereumEventNonce uint64
EthereumEventHeight uint64
}
```
| Key | Value | Type | Encoding |
| ------------------------------------------ | ------------------------------------- | ---------------------- | ---------------- |
| `[]byte{0xf1} + []byte(validator address)` | Last observed event by some Validator | `types.LastClaimEvent` | Protobuf encoded |
### Attestation
Attestation is an aggregate of claims that eventually becomes observed by all orchestrators as more votes (claims) are coming in. Once observed the claim's particular logic gets executed.
Each attestation is bound to a unique event nonce (generated by `Peggy contract`) and they must be processed in order. This is a correctness issue, if relaying out of order transaction replay attacks become possible.
```go theme={null}
type Attestation struct {
Observed bool
Votes []string
Height uint64
Claim *types.Any
}
```
| Key | Value | Type | Encoding |
| -------------------------------------------------------------------- | ------------------------------------- | ------------------- | ---------------- |
| `[]byte{0x5} + event nonce (big endian encoded) + []byte(claimHash)` | Attestation of occurred events/claims | `types.Attestation` | Protobuf encoded |
### PastEthSignatureCheckpoint
A computed hash indicating that a validator set/token batch in fact existed on Injective. This checkpoint also exists in `Peggy contract`.
Updated on each new valset update and token batch creation.
| Key | Value | Type | Encoding |
| -------------- | ----------------------------------------- | ----------------- | -------------------- |
| `[]byte{0x1b}` | Last created checkpoint hash on Injective | `gethcommon.Hash` | store in byte format |
### EthereumBlacklist
A list of known malicious Ethereum addresses that are prevented from using the bridge.
| Key | Value | Type | Encoding |
| ----------------------------------------- | ------------------- | ----------------- | ---------------------- |
| `[]byte{0x1c} + []byte(ethereum address)` | Empty \[]byte slice | `gethcommon.Hash` | stored in byte format] |
# Messages
Source: https://docs.injective.network/developers-native/injective/peggy/04_messages
# Messages
This is a reference document for Peggy message types. For code reference and exact arguments see the [proto definitions](https://github.com/InjectiveLabs/injective-core/blob/master/proto/injective/peggy/v1/msgs.proto).
## User messages
These are messages sent on the Injective Chain peggy module by the end user. See [workflow](./02_workflow) for a more detailed summary of the entire deposit and withdraw process.
### SendToEth
Sent to Injective whenever a user wishes to make a withdrawal back to Ethereum. Submitted amount is removed from the user's balance immediately.
The withdrawal is added to the outgoing tx pool as a `types.OutgoingTransferTx` where it will remain until it is included in a batch.
```go theme={null}
type MsgSendToEth struct {
Sender string // sender's Injective address
EthDest string // receiver's Ethereum address
Amount types.Coin // amount of tokens to bridge
BridgeFee types.Coin // additional fee for bridge relayers. Must be of same token type as Amount
}
```
### CancelSendToEth
This message allows the user to cancel a specific withdrawal that is not yet batched. User balance is refunded (`Amount` + `BridgeFee`).
```go theme={null}
type MsgCancelSendToEth struct {
TransactionId uint64 // unique tx nonce of the withdrawal
Sender string // original sender of the withdrawal
}
```
### SubmitBadSignatureEvidence
This call allows anyone to submit evidence that a validator has signed a valset or batch that never existed. Subject contains the batch or valset.
```go theme={null}
type MsgSubmitBadSignatureEvidence struct {
Subject *types1.Any
Signature string
Sender string
}
```
## Batch Creator Messages
These messages are sent by the `Batch Creator` subprocess of `peggo`
### RequestBatch
This message is sent whenever some `Batch Creator` finds pooled withdrawals that when batched would satisfy their minimum batch fee (`PEGGO_MIN_BATCH_FEE_USD`).
After receiving this message the `Peggy module` collects all withdrawals of the requested token denom, creates a unique token batch (`types.OutgoingTxBatch`) and places it in the `Outgoing Batch pool`.
Withdrawals that are batched cannot be cancelled with `MsgCancelSendToEth`.
```go theme={null}
type MsgRequestBatch struct {
Orchestrator string // orchestrator address interested in creating the batch. Not permissioned.
Denom string // the specific token whose withdrawals will be batched together
}
```
## Oracle Messages
These messages are sent by the `Oracle` subprocess of `peggo`
### DepositClaim
Sent to Injective when a `SendToInjectiveEvent` is emitted from the `Peggy contract`.
This occurs whenever a user is making an individual deposit from Ethereum to Injective.
```go theme={null}
type MsgDepositClaim struct {
EventNonce uint64 // unique nonce of the event
BlockHeight uint64 // Ethereum block height at which the event was emitted
TokenContract string // contract address of the ERC20 token
Amount sdkmath.Int // amount of deposited tokens
EthereumSender string // sender's Ethereum address
CosmosReceiver string // receiver's Injective address
Orchestrator string // address of the Orchestrator which observed the event
}
```
### WithdrawClaim
Sent to Injective when a `TransactionBatchExecutedEvent` is emitted from the `Peggy contract`.
This occurs when a `Relayer` has successfully called `submitBatch` on the contract to complete a batch of withdrawals.
```go theme={null}
type MsgWithdrawClaim struct {
EventNonce uint64 // unique nonce of the event
BlockHeight uint64 // Ethereum block height at which the event was emitted
BatchNonce uint64 // nonce of the batch executed on Ethereum
TokenContract string // contract address of the ERC20 token
Orchestrator string // address of the Orchestrator which observed the event
}
```
### ValsetUpdatedClaim
Sent to Injective when a `ValsetUpdatedEvent` is emitted from the `Peggy contract`.
This occurs when a `Relayer` has successfully called `updateValset` on the contract to update the `Validator Set` on Ethereum.
```go theme={null}
type MsgValsetUpdatedClaim struct {
EventNonce uint64 // unique nonce of the event
ValsetNonce uint64 // nonce of the valset
BlockHeight uint64 // Ethereum block height at which the event was emitted
Members []*BridgeValidator // members of the Validator Set
RewardAmount sdkmath.Int // Reward for relaying the valset update
RewardToken string // reward token contract address
Orchestrator string // address of the Orchestrator which observed the event
}
```
### ERC20DeployedClaim
Sent to Injective when a `ERC20DeployedEvent` is emitted from the `Peggy contract`.
This occurs whenever the `deployERC20` method is called on the contract to issue a new token asset eligible for bridging.
```go theme={null}
type MsgERC20DeployedClaim struct {
EventNonce uint64 // unique nonce of the event
BlockHeight uint64 // Ethereum block height at which the event was emitted
CosmosDenom string // denom of the token
TokenContract string // contract address of the token
Name string // name of the token
Symbol string // symbol of the token
Decimals uint64 // number of decimals the token has
Orchestrator string // address of the Orchestrator which observed the event
}
```
## Signer Messages
These messages are sent by the `Signer` subprocess of `peggo`
### ConfirmBatch
When `Signer` finds a batch that the `Orchestrator` (`Validator`) has not signed off, it constructs a signature with its `Delegated Ethereum Key` and sends the confirmation to Injective.
It's crucial that a `Validator` eventually provides their confirmation for a created batch as they will be slashed otherwise.
```go theme={null}
type MsgConfirmBatch struct {
Nonce uint64 // nonce of the batch
TokenContract string // contract address of batch token
EthSigner string // Validator's delegated Ethereum address (previously registered)
Orchestrator string // address of the Orchestrator confirming the batch
Signature string // Validator's signature of the batch
}
```
### ValsetConfirm
When `Signer` finds a valset update that the `Orchestrator` (`Validator`) has not signed off, it constructs a signature with its `Delegated Ethereum Key` and sends the confirmation to Injective.
It's crucial that a `Validator` eventually provides their confirmation for a created valset update as they will be slashed otherwise.
```go theme={null}
type MsgValsetConfirm struct {
Nonce uint64 // nonce of the valset
Orchestrator string // address of the Orchestrator confirming the valset
EthAddress string // Validator's delegated Ethereum address (previously registered)
Signature string // Validator's signature of the valset
}
```
## Relayer Messages
The `Relayer` does not send any message to Injective, rather it constructs Ethereum transactions with Injective data to update the `Peggy contract` via `submitBatch` and `updateValset` methods.
## Validator Messages
These are messages sent directly using the validator's message key.
### SetOrchestratorAddresses
Sent to Injective by an `Operator` managing a `Validator` node. Before being able to start their `Orchestrator` (`peggo`) process, they must register a chosen Ethereum address to represent their `Validator` on Ethereum.
Optionally, an additional Injective address can be provided (`Orchestrator` field) to represent that `Validator` in the bridging process (`peggo`). Defaults to `Validator`'s own address if omitted.
```go theme={null}
type MsgSetOrchestratorAddresses struct {
Sender string // address of the Injective validator
Orchestrator string // optional Injective address to represent the Validator in the bridging process (Defaults to Sender if left empty)
EthAddress string // the Sender's (Validator) delegated Ethereum address
}
```
This message sets the Orchestrator's delegate keys.
# Slashing
Source: https://docs.injective.network/developers-native/injective/peggy/05_slashing
# Slashing
### Security Concerns
The **Validator Set** is the actual set of keys with stake behind them, which are slashed for double-signs or other
misbehavior. We typically consider the security of a chain to be the security of a *Validator Set*. This varies on
each chain, but is our gold standard. Even IBC offers no more security than the minimum of both involved Validator Sets.
The **Eth bridge relayer** is a binary run alongside the main `injectived` daemon by the validator set. It exists purely as a matter of code organization and is in charge of signing Ethereum transactions, as well as observing events on Ethereum and bringing them into the Injective Chain state. It signs transactions bound for Ethereum with an Ethereum key, and signs over events coming from Ethereum with an Injective Chain account key. We can add slashing conditions to any mis-signed message by any *Eth Signer* run by the *Validator Set* and be able to provide the same security as the *Validator Set*, just a different module detecting evidence of malice and deciding how much to slash. If we can prove a transaction signed by any *Eth Signer* of the *Validator Set* was illegal or malicious, then we can slash on the Injective Chain side and potentially provide 100% of the security of the *Validator Set*. Note that this also has access to the 3 week unbonding period to allow evidence to slash even if they immediately unbond.
Below are various slashing conditions we use in Peggy.
## PEGGYSLASH-01: Signing fake validator set or tx batch evidence
This slashing condition is intended to stop validators from signing over a validator set and nonce that has never existed on the Injective Chain. It works via an evidence mechanism, where anyone can submit a message containing the signature of a validator over a fake validator set. This is intended to produce the effect that if a cartel of validators is formed with the intention of submitting a fake validator set, one defector can cause them all to be slashed.
```go theme={null}
// This call allows anyone to submit evidence that a
// validator has signed a valset, batch, or logic call that never
// existed. Subject contains the batch, valset, or logic call.
type MsgSubmitBadSignatureEvidence struct {
Subject *types1.Any
Signature string
Sender string
}
```
**Implementation considerations:**
The trickiest part of this slashing condition is determining that a validator set has never existed on Injective. To save space, we will need to clean up old validator sets. We could keep a mapping of validator set hash to true in the KV store, and use that to check if a validator set has ever existed. This is more efficient than storing the whole validator set, but its growth is still unbounded. It might be possible to use other cryptographic methods to cut down on the size of this mapping. It might be OK to prune very old entries from this mapping, but any pruning reduces the deterrence of this slashing condition.
The implemented version of this slashing condition stores a map of hashes for all past events, this is smaller than storing entire batches or validator sets and doesn't have to be accessed as frequently. A possible but not currently implemented efficiency optimization would be to remove hashes from this list after a given period. But this would require storing more metadata about each hash.
Currently automatic evidence submission is not implemented in the relayer. By the time a signature is visible on Ethereum it's already too late for slashing to prevent bridge highjacking or theft of funds. Furthermore since 66% of the validator set is required to perform this action anyways that same controlling majority could simply refuse the evidence. The most common case envisioned for this slashing condition is to break up a cabal of validators trying to take over the bridge by making it more difficult for them to trust one another and actually coordinate such a theft.
The theft would involve exchanging of slashable Ethereum signatures and open up the possibility of a manual submission of this message by any defector in the group.
Currently this is implemented as an ever growing array of hashes in state.
## PEGGYSLASH-02: Failure to sign tx batch
This slashing condition is triggered when a validator does not sign a transaction batch within `SignedBatchesWindow` upon it's creation by the Peggy module. This prevents two bad scenarios-
1. A validator simply does not bother to keep the correct binaries running on their system,
2. A cartel of >1/3 validators unbond and then refuse to sign updates, preventing any batches from getting enough signatures to be submitted to the Peggy Ethereum contract.
## PEGGYSLASH-03: Failure to sign validator set update
This slashing condition is triggered when a validator does not sign a validator set update which is produced by the Peggy module. This prevents two bad scenarios-
1. A validator simply does not bother to keep the correct binaries running on their system,
2. A cartel of >1/3 validators unbond and then refuse to sign updates, preventing any validator set updates from getting enough signatures to be submitted to the Peggy Ethereum contract. If they prevent validator set updates for longer than the Injective Chain unbonding period, they can no longer be punished for submitting fake validator set updates and tx batches (PEGGYSLASH-01 and PEGGYSLASH-03).
To deal with scenario 2, PEGGYSLASH-03 will also need to slash validators who are no longer validating, but are still in the unbonding period for up to `UnbondSlashingValsetsWindow` blocks. This means that when a validator leaves the validator set, they will need to keep running their equipment for at least `UnbondSlashingValsetsWindow` blocks. This is unusual for the Injective Chain, and may not be accepted by the validators.
The current value of `UnbondSlashingValsetsWindow` is 25,000 blocks, or about 12-14 hours. We have determined this to be a safe value based on the following logic. So long as every validator leaving the validator set signs at least one validator set update that they are not contained in then it is guaranteed to be possible for a relayer to produce a chain of validator set updates to transform the current state on the chain into the present state.
It should be noted that both PEGGYSLASH-02 and PEGGYSLASH-03 could be eliminated with no loss of security if it where possible to perform the Ethereum signatures inside the consensus code. This is a pretty limited feature addition to Tendermint that would make Peggy far less prone to slashing.
## PEGGYSLASH-04: Submitting incorrect Eth oracle claim (Disabled for now)
The Ethereum oracle code (currently mostly contained in attestation.go), is a key part of Peggy. It allows the Peggy module to have knowledge of events that have occurred on Ethereum, such as deposits and executed batches. PEGGYSLASH-03 is intended to punish validators who submit a claim for an event that never happened on Ethereum.
**Implementation considerations**
The only way we know whether an event has happened on Ethereum is through the Ethereum event oracle itself. So to implement this slashing condition, we slash validators who have submitted claims for a different event at the same nonce as an event that was observed by >2/3s of validators.
Although well-intentioned, this slashing condition is likely not advisable for most applications of Peggy. This is because it ties the functioning of the Injective Chain which it is installed on to the correct functioning of the Ethereum chain. If there is a serious fork of the Ethereum chain, different validators behaving honestly may see different events at the same event nonce and be slashed through no fault of their own. Widespread unfair slashing would be very disruptive to the social structure of the Injective Chain.
Maybe PEGGYSLASH-04 is not necessary at all:
The real utility of this slashing condition is to make it so that, if >2/3 of the validators form a cartel to all submit a fake event at a certain nonce, some number of them can defect from the cartel and submit the real event at that nonce. If there are enough defecting cartel members that the real event becomes observed, then the remaining cartel members will be slashed by this condition. However, this would require >1/2 of the cartel members to defect in most conditions.
If not enough of the cartel defects, then neither event will be observed, and the Ethereum oracle will just halt. This is a much more likely scenario than one in which PEGGYSLASH-04 is actually triggered.
Also, PEGGYSLASH-04 will be triggered against the honest validators in the case of a successful cartel. This could act to make it easier for a forming cartel to threaten validators who do not want to join.
## PEGGYSLASH-05: Failure to submit Eth oracle claims (Disabled for now)
This is similar to PEGGYSLASH-04, but it is triggered against validators who do not submit an oracle claim that has been observed. In contrast to PEGGYSLASH-04, PEGGYSLASH-05 is intended to punish validators who stop participating in the oracle completely.
**Implementation considerations**
Unfortunately, PEGGYSLASH-05 has the same downsides as PEGGYSLASH-04 in that it ties the correct operation of the Injective Chain to the Ethereum chain. Also, it likely does not incentivize much in the way of correct behavior. To avoid triggering PEGGYSLASH-05, a validator simply needs to copy claims which are close to becoming observed. This copying of claims could be prevented by a commit-reveal scheme, but it would still be easy for a "lazy validator" to simply use a public Ethereum full node or block explorer, with similar effects on security. Therefore, the real usefulness of PEGGYSLASH-05 is likely minimal
PEGGYSLASH-05 also introduces significant risks. Mostly around forks on the Ethereum chain. For example recently OpenEthereum failed to properly handle the Berlin hardfork, the resulting node 'failure' was totally undetectable to automated tools. It didn't crash so there was no restart to perform, blocks where still being produced although extremely slowly. If this had occurred while Peggy was running with PEGGYSLASH-05 active it would have caused those validators to be removed from the set. Possibly resulting in a very chaotic moment for the chain as dozens of validators where removed for little to no fault of their own.
Without PEGGYSLASH-04 and PEGGYSLASH-05, the Ethereum event oracle only continues to function if >2/3 of the validators voluntarily submit correct claims. Although the arguments against PEGGYSLASH-04 and PEGGYSLASH-05 are convincing, we must decide whether we are comfortable with this fact. Alternatively we must be comfortable with the Injective Chain potentially halting entirely due to Ethereum generated factors.
# End-Block
Source: https://docs.injective.network/developers-native/injective/peggy/06_end_block
# EndBlocker
Upon the end of each block the following operations are performed to the state of the module
## 1. Slashing
### Validator slashing
A validator is slashed for not signing over a valset update which passed the `SignedValsetsWindow`.
In other words, if a validator fails to provide the confirmation for a valset update within a preconfigured amount of time, they will be slashed for `SlashFractionValset` portion of their stake and get jailed immediately.
### Batch Slashing
A validator is slashed for not signing over a batch which passed the `SignedBatchesWindow`.
In other words, if a validator fails to provide the confirmation for a batch within a preconfigured amount of time, they will be slashed for `SlashFractionBatch` portion of their stake and get jailed immediately.
## 2. Cancelling timed out batches
Any batch still present in the `Outgoing Batch pool` whose `BatchTimeout` (a designated Ethereum height by which the batch should have executed) is exceeded gets removed from the pool and the withdrawals are reinserted back into the `Outgoing Tx pool`.
## 3. Creating new Valset updates
A new `Validator Set` update will be created automatically when:
* there is a power diff greater than 5% between the latest and current validator set
* a validator begins unbonding
The new validator set is eventually relayed to `Peggy contract` on Ethereum.
## 4. Pruning old validator sets
Previously observed valsets that passed the `SignedValsetsWindow` are removed from the state
## 5. Attestation processing
Processes all attestations (an aggregate of claims for a particular event) currently being voted on. Each attestation is processed one by one to ensure each `Peggy contract` event is processed.
After each processed attestation the module's `lastObservedEventNonce` and `lastObservedEthereumBlockHeight` are updated.
Depending on the type of claim in the attestation, the following is executed:
* `MsgDepositClaim`: deposited tokens are minted/unlocked for the receiver address
* `MsgWithdrawClaim`: corresponding batch is removed from the outgoing pool and any previous batch is cancelled
* `MsgValsetUpdatedClaim`: the module's `LastObservedValset` is updated
* `MsgERC20DeployedClaim`: new token metadata is validated and registered within the module's state (`denom <-> token_contract`)
## 6. Cleaning up processed attestations
Previously processed attestations (height earlier that `lastObservedEthereumBlockHeight`) are removed from the module state
# Events
Source: https://docs.injective.network/developers-native/injective/peggy/07_events
# Events
The peggy module emits the following events:
## EndBlocker
### EventAttestationObserved
| Type | Attribute Key | Attribute Value |
| ------- | ----------------- | --------------------------- |
| int32 | attestation\_type | `{attestation_type}` |
| string | bridge\_contract | `{bridge_contract_address}` |
| uint64 | bridge\_chain\_id | `{bridge_chain_id}` |
| \[]byte | attestation\_id | `{attestation_id}` |
| uint64 | nonce | `{event_nonce}` |
### EventValidatorSlash
| Type | Attribute Key | Attribute Value |
| ------ | ------------------ | ----------------------- |
| string | reason | `{reason_for_slashing}` |
| int64 | power | `{validator_power}` |
| string | consensus\_address | `{consensus_addr}` |
| string | operator\_address | `{operator_addr}` |
| string | moniker | `{validator_moniker}` |
## Handler
### EventSetOrchestratorAddresses
| Type | Attribute Key | Attribute Value |
| ------ | ---------------------- | --------------------- |
| string | validator\_address | `{validator_addr}` |
| string | orchestrator\_address | `{orchestrator_addr}` |
| string | operator\_eth\_address | `{eth_addr}` |
### EventSendToEth
| Type | Attribute Key | Attribute Value |
| -------- | ---------------- | ---------------- |
| message | outgoing\_tx\_id | `{tx_id}` |
| string | sender | `{sender_addr}` |
| string | receiver | `{dest_addr}` |
| sdk.Coin | amount | `{token_amount}` |
| sdk.Coin | bridge\_fee | `{token_amount}` |
### EventBridgeWithdrawCanceled
| Type | Attribute Key | Attribute Value |
| --------------------- | ----------------- | ------------------- |
| withdrawal\_cancelled | bridge\_contract | `{bridge_contract}` |
| withdrawal\_cancelled | bridge\_chain\_id | `{bridge_chain_id}` |
### EventOutgoingBatch
| Type | Attribute Key | Attribute Value |
| --------- | --------------------- | ---------------- |
| string | denom | `{token_denom}` |
| string | orchestrator\_address | `{orch_addr}` |
| uint64 | batch\_nonce | `{batch_nonce}` |
| uint64 | batch\_timeout | `{block_height}` |
| \[]uint64 | batch\_tx\_ids | `{ids}` |
### EventOutgoingBatchCanceled
| Type | Attribute Key | Attribute Value |
| ------ | ----------------- | ------------------- |
| string | bridge\_contract | `{bridge_contract}` |
| uint64 | bridge\_chain\_id | `{bridge_chain_id}` |
| uint64 | batch\_id | `{id}` |
| uint64 | nonce | `{nonce}` |
### EventValsetConfirm
| Type | Attribute Key | Attribute Value |
| ------ | --------------------- | --------------- |
| uint64 | valset\_nonce | `{nonce}` |
| string | orchestrator\_address | `{prch_addr}` |
### EventConfirmBatch
| Type | Attribute Key | Attribute Value |
| ------ | --------------------- | --------------- |
| uint64 | batch\_nonce | `{nonce}` |
| string | orchestrator\_address | `{orch_addr}` |
### EventDepositClaim
| Type | Attribute Key | Attribute Value |
| ------- | --------------------- | ------------------- |
| uint64 | event\_nonce | `{event_nonce}` |
| uint64 | event\_height | `{event_height}` |
| \[]byte | attestation\_id | `{attestation_key}` |
| string | ethereum\_sender | `{sender_addr}` |
| string | cosmos\_receiver | `{receiver_addr}` |
| string | token\_contract | `{contract_addr}` |
| sdk.Int | amount | `{token_amount}` |
| string | orchestrator\_address | `{orch_addr}` |
| string | data | `{custom_data}` |
### EventWithdrawClaim
| Type | Attribute Key | Attribute Value |
| ------- | --------------------- | ------------------- |
| uint64 | event\_nonce | `{event_nonce}` |
| uint64 | event\_height | `{event_height}` |
| \[]byte | attestation\_id | `{attestation_key}` |
| uint64 | batch\_nonce | `{batch_nonce}` |
| string | token\_contract | `{contract_addr}` |
| string | orchestrator\_address | `{orch_addr}` |
### EventERC20DeployedClaim
| Type | Attribute Key | Attribute Value |
| ------- | --------------------- | ------------------------ |
| uint64 | event\_nonce | `{event_nonce}` |
| uint64 | event\_height | `{event_height}` |
| \[]byte | attestation\_id | `{attestation_key}` |
| string | cosmos\_denom | `{token_denom}` |
| string | token\_contract | `{token_conntract_addr}` |
| string | name | `{token_name}` |
| string | symbol | `{token_symbol}` |
| uint64 | decimals | `{token_decimals}` |
| string | orchestrator\_address | `{orch_addr}` |
### EventValsetUpdateClaim
| Type | Attribute Key | Attribute Value |
| -------------------- | --------------------- | ----------------------- |
| uint64 | event\_nonce | `{event_nonce}` |
| uint64 | event\_height | `{event_height}` |
| \[]byte | attestation\_id | `{attestation_key}` |
| uint64 | valset\_nonce | `{valset_nonce}` |
| \[]\*BridgeValidator | valset\_members | `{array_of_validators}` |
| sdk.Int | reward\_amount | `{amount}` |
| string | reward\_token | `{contract_addr}` |
| string | orchestrator\_address | `{orch_addr}` |
# Parameters
Source: https://docs.injective.network/developers-native/injective/peggy/08_params
# Params
This document describes and advises configuration of the Peggy module's parameters. The default parameters can be found in the genesis.go of the peggy module.
```go theme={null}
type Params struct {
PeggyId string
ContractSourceHash string
BridgeEthereumAddress string
BridgeChainId uint64
SignedValsetsWindow uint64
SignedBatchesWindow uint64
SignedClaimsWindow uint64
TargetBatchTimeout uint64
AverageBlockTime uint64
AverageEthereumBlockTime uint64
SlashFractionValset math.LegacyDec
SlashFractionBatch math.LegacyDec
SlashFractionClaim math.LegacyDec
SlashFractionConflictingClaim math.LegacyDec
UnbondSlashingValsetsWindow uint64
SlashFractionBadEthSignature math.LegacyDec
CosmosCoinDenom string
CosmosCoinErc20Contract string
ClaimSlashingEnabled bool
BridgeContractStartHeight uint64
ValsetReward types.Coin
}
```
## `peggy_id`
A random 32 byte value to prevent signature reuse, for example if the
Injective Chain validators decided to use the same Ethereum keys for another chain
also running Peggy we would not want it to be possible to play a deposit
from chain A back on chain B's Peggy. This value IS USED ON ETHEREUM so
it must be set in your genesis.json before launch and not changed after
deploying Peggy. Changing this value after deploying Peggy will result
in the bridge being non-functional. To recover just set it back to the original
value the contract was deployed with.
## `contract_source_hash`
The code hash of a known good version of the Peggy contract
solidity code. This can be used to verify the correct version
of the contract has been deployed. This is a reference value for
governance action only it is never read by any Peggy code
## `bridge_ethereum_address`
is address of the bridge contract on the Ethereum side, this is a
reference value for governance only and is not actually used by any
Peggy module code.
The Ethereum bridge relayer use this value to interact with Peggy contract for querying events and submitting valset/batches to Peggy contract.
## `bridge_chain_id`
The bridge chain ID is the unique identifier of the Ethereum chain. This is a reference value only and is not actually used by any Peggy code
These reference values may be used by future Peggy client implementations to allow for consistency checks.
## Signing windows
* `signed_valsets_window`
* `signed_batches_window`
* `signed_claims_window`
These values represent the time in blocks that a validator has to submit
a signature for a batch or valset, or to submit a claim for a particular
attestation nonce.
In the case of attestations this clock starts when the
attestation is created, but only allows for slashing once the event has passed.
Note that that claims slashing is not currently enabled see [slashing spec](./05_slashing)
## `target_batch_timeout`
This is the 'target' value for when batches time out, this is a target because
Ethereum is a probabilistic chain and you can't say for sure what the block
frequency is ahead of time.
## Ethereum timing
* `average_block_time`
* `average_ethereum_block_time`
These values are the average Injective Chain block time and Ethereum block time respectively
and they are used to compute what the target batch timeout is. It is important that
governance updates these in case of any major, prolonged change in the time it takes
to produce a block
## Slash fractions
* `slash_fraction_valset`
* `slash_fraction_batch`
* `slash_fraction_claim`
* `slash_fraction_conflicting_claim`
The slashing fractions for the various peggy related slashing conditions. The first three
refer to not submitting a particular message, the third for failing to submit a claim and the last for submitting a different claim than other validators.
Note that claim slashing is currently disabled as outlined in the [slashing spec](./05_slashing)
## `valset_reward`
Valset reward is the reward amount paid to a relayer when they relay a valset to the Peggy contract on Ethereum.
# Relay Semantics
Source: https://docs.injective.network/developers-native/injective/peggy/09_relay_semantics
# Relay Semantics
This document is designed to assist developers in implementing alternate Peggy relayers. The two major components of the Orchestrator which interact with Ethereum. The Peggy bridge has been designed for increased efficiency, not for ease of use. This means there are many implicit requirements of these external binaries which this document does it's best to make explicit.
The Peggy `orchestrator` combines three distinct roles that need to be performed by external binaries in the Peggy bridge. This document highlights the requirements of the `relayer` which is one of those roles included in the `orchestrator`.
## Semantics for Validator set update relaying
### Sorting and Ordering of the Validator set and signatures
When updating the validator set in the Peggy contract you must provide a copy of the old validator set. This *MUST* only be taken from the last ValsetUpdated event on the Ethereum chain.
Providing the old validator set is part of a storage optimization, instead of storing the entire validator set in Ethereum storage it is instead provided by each caller and stored in the much cheaper Ethereum event queue. No sorting of any kind is performed in the Peggy contract, meaning the list of validators and their new signatures must be submitted in exactly the same order as the last call.
For the purpose of normal operation this requirement can be shortened to 'sort the validators by descending power, and by Eth address bytes where power is equal'. Since the peggy module produces the validator sets they should always come in order. It is not possible for the relayer to change this order since it is part of the signature. But a change in this sorting method on the Peggy module side would halt valset updates and essentially decouple the bridge unless your implementation is smart enough to take a look at the last submitted order rather than blindly following sorting.
### Deciding what Validator set to relay
The Injective Chain simply produces a stream of validator sets, it does not make any judgement on how they are relayed. It's up to the relayer implementation to determine how to optimize the gas costs of this relaying operation.
For example lets say we had validator sets `A, B, C, and D` each is created when there is a 5% power difference between the last Peggy validator set snapshot in the store and the currently active validator set.
5% is an arbitrary constant. The specific value chosen here is a tradeoff made by the chain between how up to date the Ethereum validator set is and the cost to keep it updated. The higher this value is the lower the portion of the voting validator set is needed to highjack the bridge in the worst case. If we made a new validator set update every block 66% would need to collude, the 5% change threshold means 61% of the total voting power colluding in a given validator set may be able to steal the funds in the bridge.
```
A -> B -> C -> D
5% 10% 15%
```
The relayer should iterate over the event history for the Peggy Ethereum contract, it will determine that validator set A is currently in the Peggy bridge. It can choose to either relay validator sets B, C and then D or simply submit validator set D. Provided all validators have signed D it has more than 66% voting power and can pass on it's own. Without paying potentially several hundred dollars more in Ethereum to relay the intermediate sets.
Performing this check locally somehow, before submitting transactions, is essential to a cost effective relayer implementation. You can either use a local Ethereum signing implementation and sum the powers and signatures yourself, or you can simply use the `eth_call()` Ethereum RPC to simulate the call on your Ethereum node.
Note that `eth_call()` often has funny gotchas. All calls fail on Geth based implementations if you don't have any Ethereum to pay for gas, while on Parity based implementations your gas inputs are mostly ignored and an accurate gas usage is returned.
## Semantics for transaction batch relaying
In order to submit a transaction batch you also need to submit the last set of validators and their staking powers. This is to facilitate the same storage optimization mentioned there.
### Deciding what batch to relay
Making a decision about which batch to relay is very different from deciding which validator set to relay. Batch relaying is primarily motivated by fees, not by a desire to maintain the integrity of the bridge. So the decision mostly comes down to fee computation, this is further complicated by the concept of 'batch requests'. Which is an unpermissioned transaction that requests the Peggy module generate a new batch for a specific token type.
Batch requests are designed to allow the user to withdraw their tokens from the send to Ethereum tx pool at any time up until a relayer shows interest in actually relaying them. While transactions are in the pool there's no risk of a double spend if the user is allowed to withdraw them by sending a MsgCancelSendToEth. Once the transaction enters a batch due to a 'request batch' that is no longer the case and the users funds must remain locked until the Oracle informs the Peggy module that the batch containing the users tokens has become somehow invalid to submit or has been executed on Ethereum.
A relayer uses the query endpoint `BatchFees` to iterate over the send to Eth tx pool for each token type, the relayer can then observe the price for the ERC-20 tokens being relayed on a dex and compute the gas cost of executing the batch (via `eth_call()`) as well as the gas cost of liquidating the earnings on a dex if desired. Once a relayer determines that a batch is good and profitable it can send a `MsgRequestBatch` and the batch will be created for the relayer to relay.
There are also existing batches, which the relayer should also judge for profitability and make an attempt at relaying using much the same method.
# Future Improvements
Source: https://docs.injective.network/developers-native/injective/peggy/10_future_improvements
# Future Improvements
### Native Ethereum Signing
Validators run a required `Eth Signer` in the peggo orchestrator because we can not yet insert this sort of simple signature logic into Cosmos SDK based chains without significant modification to Tendermint. This may be possible in the future with [modifications to Tendermint](https://github.com/tendermint/tendermint/issues/6066).
It should be noted that both [PEGGYSLASH-02](./05_slashing) and [PEGGYSLASH-03](./05_slashing) could be eliminated with no loss of security if it where possible to perform the Ethereum signatures inside the consensus code. This is a pretty limited feature addition to Tendermint that would make Peggy far less prone to slashing.
### Improved Validator Incentives
Currently validators in Peggy have only one carrot - the extra activity brought to the chain by a functioning bridge.
There are on the other hand a lot of negative incentives (sticks) that the validators must watch out for. These are outlined in the [slashing spec](./05_slashing).
One negative incentive that is not covered under slashing is the cost of submitting oracle submissions and signatures. Currently these operations are not incentivized, but still cost the validators fees to submit. This isn't a severe issue considering the relatively cheap transaction fees on the Injective Chain currently, but of course is an important factor to consider as transaction fees rise.
Some positive incentives for correctly participating in the operation of the bridge should be under consideration. In addition to eliminating the fees for mandatory submissions.
# Error Codes
Source: https://docs.injective.network/developers-native/injective/peggy/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------ | ---------- | ------------------------------------------------- |
| peggy | 1 | internal |
| peggy | 2 | duplicate |
| peggy | 3 | invalid |
| peggy | 4 | timeout |
| peggy | 5 | unknown |
| peggy | 6 | empty |
| peggy | 7 | outdated |
| peggy | 8 | unsupported |
| peggy | 9 | non contiguous event nonce |
| peggy | 10 | no unbatched txs found |
| peggy | 11 | can not set orchestrator addresses more than once |
| peggy | 12 | supply cannot exceed max ERC20 value |
| peggy | 13 | invalid ethereum sender on claim |
| peggy | 14 | invalid ethereum destination |
| peggy | 15 | missing previous claim for validator |
# Peggy
Source: https://docs.injective.network/developers-native/injective/peggy/index
## Abstract
The peggy module enables the Injective Chain to support a trustless, on-chain bidirectional ERC-20 token bridge to Ethereum. In this system,
holders of ERC-20 tokens on Ethereum can convert their ERC-20 tokens to Cosmos-native coins on
the Injective Chain and vice-versa.
This decentralized bridge is secured and operated by the validators of the Injective Chain.
## Contents
1. [Definitions](/developers-native/injective/peggy/01_definitions)
2. [Workflow](/developers-native/injective/peggy/02_workflow)
3. [State](/developers-native/injective/peggy/03_state)
4. [Messages](/developers-native/injective/peggy/04_messages)
5. [Slashing](/developers-native/injective/peggy/05_slashing)
6. [End-Block](/developers-native/injective/peggy/06_end_block)
7. [Events](/developers-native/injective/peggy/07_events)
8. [Parameters](/developers-native/injective/peggy/08_params)
### Components
1. **[Peggy](https://etherscan.io/address/0xF955C57f9EA9Dc8781965FEaE0b6A2acE2BAD6f3) smart contract on Ethereum**
2. **Peggy module on the Injective Chain**
3. **[Peggo](https://github.com/InjectiveLabs/peggo) (off-chain relayer aka orchestrator)**
* **Oracle** (Observes events of Peggy contract and send claims to the Peggy module)
* **EthSigner** (Signs Valset and Batch confirmations to the Peggy module)
* **Batch Requester** (Sends batch token withdrawal requests to the Peggy module)
* **Valset Relayer** (Submits Validator set updates to the Peggy contract)
* **Batch Relayer** (Submits batches of token withdrawals to the Peggy contract)
In addition to running an `injectived` node to sign blocks, Injective Chain validators must also run the `peggo` orchestrator to relay data from the Peggy smart contract on Ethereum and the Peggy module on the Injective Chain.
### Peggo Functionalities
1. **Maintaining an up-to-date checkpoint of the Injective Chain validator set on Ethereum**
2. **Transferring ERC-20 tokens from Ethereum to the Injective Chain**
3. **Transferring pegged tokens from the Injective Chain to Ethereum**
# Key Concepts
Source: https://docs.injective.network/developers-native/injective/permissions/01_concepts
## Overview
The `Permissions` module enables the creation of permissioned assets through an RBAC (Role-Based Access Control) system. The module serves as a primitive to launch any asset requiring varying levels of permissions such as stablecoins (issued by centralized entities), tokenized Treasuries, and other RWAs (Real World Assets).
Supported features include restrictions on sending/receiving (freezing assets), control over access to minting and burning, pausing specific actions across all addresses, administrative actions management, and support for Wasm contract hooks for further development on top of the module.
## How the `Permissions` Module Works
### Relation to `TokenFactory` Module
The `Permissions` module is tightly coupled with the `TokenFactory` module. It allows users to create restrictions on sending, receiving, minting, and burning of a specific `TokenFactory` token by creating a namespace for the token denom, in which actions are assigned to roles, and roles are assigned to addresses. In this manner, only addresses with the correct roles are permitted to carry out the corresponding actions. The module essentially builds an optional permissions layer on top of `TokenFactory` denoms.
Since the `Permissions` module relies on the existence of a `TokenFactory` denom to function, the `TokenFactory` denom should be launched first, followed by the creation of a `Permissions` namespace.
* See [https://docs.injective.network/developers/modules/injective/tokenfactory](https://docs.injective.network/developers/modules/injective/tokenfactory) for documentation on the `TokenFactory` module
* See [https://docs.injective.network/guides/launch-a-token](https://docs.injective.network/guides/launch-a-token) for a guide on launching a token with the module
* Note: The `TokenFactory` admin should not be changed to the null address (inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49) because the `Permissions` module requires the creator of the `Permissions` namespace to be the admin of the corresponding `TokenFactory` denom.
Once the `TokenFactory` denom is live and a `Permissions` namespace is created for the token, checks are performed during token mint, burn, send, and receive to ensure the correct permissions are in place for the relevant action.
### Components of a Namespace
As an implementation of an RBAC system for permissioned assets, the `Permissions` module defines several basic actions that can be assigned to roles within a namespace. A namespace serves as the container for all rules, parameters, and assigned permissions for a single asset. Note that each asset (denom) can only have one namespace.
At its core, a namespace comprises:
* Denom
* Actions
* Roles
* Role Managers
* Role Actors
* Policy Statuses
* Policy Managers
* Wasm Contract Hook
**Denom**
* The denomination of the `TokenFactory` asset. The denom in the namespace corresponds to the `TokenFactory` asset with the same name, of which the creator of the namespace is also the admin of the `TokenFactory` asset
**Actions**
* Actions are mapped/assigned to roles to give the role permission to execute the action
* Actions are split into two categories, user actions and namespace management actions. The following is a list of all possible permissioned actions in a namespace:
* User Actions:
* `MINT` - Can mint to any address using the `receiver` field as long as that address has permissions for the `RECEIVE` action. If unspecified, the default address is the message sender’s address
* `RECEIVE` - The address is able to receive tokens
* `BURN` - Burn own funds
* `SEND` - Can send to any address with `RECEIVE` permissions
* `SUPER_BURN` - Burn funds from anyone’s wallet except own wallet, unless the user also has Burn permissions
* Namespace Management Actions:
* `MODIFY_POLICY_MANAGERS` - Change the policy managers and their capabilities (if they can disable or seal) within the namespace
* `MODIFY_CONTRACT_HOOK` - Change the Wasm contract hook
* `MODIFY_ROLE_PERMISSIONS` - Change the mapping of roles to permitted actions (change what each role is allowed to do)
* `MODIFY_ROLE_MANAGERS` - Change the managers that determine who can have a role
* Actions are identified by a unique power of 2 integer (for internal bitmasking purposes):
* `MINT` = 1
* `RECEIVE` = 2
* `BURN` = 4
* `SEND` = 8
* `SUPER_BURN` = 16
* `MODIFY_POLICY_MANAGERS` = 134217728
* `MODIFY_CONTRACT_HOOK` = 268435456
* `MODIFY_ROLE_PERMISSIONS` = 536870912
* `MODIFY_ROLE_MANAGERS` = 1073741824
* Permissions are identified by the sum of all permitted action values in base 10
* E.g. permission to receive, burn, and send would be 14 (2 + 4 + 8)
**Roles**
* Roles consist of a subset of actions (permissions) for which an actor is permitted to carry out. Each role can have zero, one, or multiple actions assigned by a Role Manager
* A role’s actions represent the permitted actions for which an address assigned the role can execute. In other words, roles give permission to perform the actions specified by the role
* Roles are created when the namespace is launched or upon updating the namespace with new role-action mappings by a user permitted to execute the `MODIFY_ROLE_PERMISSIONS` action
* Roles are generally namespace (admin) defined, though there are two special types of roles:
* `EVERYONE` Role
* There is a default `EVERYONE` role assigned to all addresses that do not have any other roles
* If no roles are assigned to an actor, then the `EVERYONE` role applies until they are assigned another role, at which point the `EVERYONE` role ceases to apply
* Note that this role does not have to be assigned any permissions, though **the `EVERYONE` role must be defined at creation time** (it can be defined with no actions assigned)
* The `EVERYONE` role is not allowed to have permissions for `MINT`, `SUPER_BURN`, or any of the namespace management actions. This means only `SEND`, `RECEIVE`, and `BURN` are permitted actions to be assigned to the `EVERYONE` role
* Blacklist Role
* Any role that has no permitted actions (including `EVERYONE`) will be considered a blacklist role. The blacklist role can be called any unique name.
* Blacklist roles supercede all other roles such that any address assigned a blacklist role will have all permissions revoked until the address is no longer blacklisted
* If the blacklist role is removed from the address, all other roles/permissions the address previously had will be reinstated
* In the case of `EVERYONE` having no permissions, once an address receives another non-blacklist role, the address will no longer have zero permissions/be blacklisted since `EVERYONE` only applies when there are no other roles
**Role Managers**
* Role managers have the ability to assign roles to other addresses (role actors)
* For each role defined within the namespace, one or more role managers are needed to manage the list of addresses for a particular role
* This includes roles for namespace management actions:
* `MODIFY_POLICY_MANAGERS` Role Managers
* `MODIFY_CONTRACT_HOOK` Role Managers
* `MODIFY_ROLE_PERMISSIONS` Roles Managers
* `MODIFY_ROLE_MANAGERS` Role Managers
* The role manager can only assign roles for which they are the specific role manager of
* An address can be a role manager of multiple roles at the same time
**Role Actors**
* Role actors, or simply actors, are addresses that have been given one or more roles by the respective role managers
* Role actors are permitted to execute any action for which any of their roles allow
* For example, if role ABC had permissions to mint, send, and receive, then any role actor with role ABC would be permitted to `mint`, `send`, and `receive` the namespace asset. Suppose role XYZ had `burn` and `mint` permissions, and an actor had role ABC as well as XYZ. Then the actor would be permitted to `mint`, `send`, `receive`, and `burn` the asset.
**Policy Statuses**
* Each action has a policy status associated with it which can be enabled/disabled and/or sealed (can be enabled or disabled without being sealed, or can be enabled or disabled and sealed). Internally, this consists of two booleans for `IsDisabled` and `IsSealed`
* Actions are not disabled and not sealed by default
* The policy status determines the status of an action across the entire namespace. If an action policy is disabled, no user will be able to execute the action until it is enabled again
* Sealing a user action policy will irreversibly set the action policy for `IsDisabled`
* **Note: Sealing a namespace management action (**`MODIFY_POLICY_MANAGERS`**,** `MODIFY_CONTRACT_HOOK`**,** `MODIFY_ROLE_PERMISSIONS`**,** `MODIFY_ROLE_MANAGERS`**) policy will result in the action being permanently disabled. While user actions and namespace management actions can both be sealed as enabled, the resulting behavior of the respective namespace management actions will be effectively permanently disabled, while user actions will be permanently enabled.**
**Policy Managers**
* Policy managers are in charge of setting the policy status for their approved actions.
* When the policy managers for an action are set, they can be given permission to disable and/or seal the action policy through the `PolicyManagerCapabilities` field.
* Policy manager capabilities describe whether an address (the policy manager) can disable or seal an action. Another way to put it: they describe the extent to which a policy manager can alter the policy state of an action.
* Note that the policy manager should have permissions for at least one of the actions, since there would be no point to having a policy manager that cannot update the action policy status. If both permissions are removed, the policy manager will be removed entirely.
**Wasm Contract Hook**
* The Wasm contract hook is useful for building extended features on top of the permissioned namespace and `TokenFactory` primitives
* Wasm contract hooks are invoked upon an address receiving a permissioned asset. The hook can be set by any address with the role to perform the `MODIFY_CONTRACT_HOOK` action
* Information passed to the contract includes `fromAddr`, `toAddr`, `action`, and `amount`. The action is currently passed as `Action_RECEIVE`
### Interacting with a Namespace
There are four relevant messages to interact with the permissions module:
* `MsgCreateNamespace` - Create the namespace with initial settings/parameters for role permissions, role managers, policy statuses, policy managers/capabilities, and/or the Wasm contract hook
* `MsgUpdateNamespace` - Update role permissions, role managers, policy statuses, policy managers/capabilities, and/or the Wasm contract hook
* `MsgUpdateActorRoles` - Assign or revoke roles from addresses
* `MsgClaimVoucher` - Claim voucher for assets that failed to transfer from an Injective module to the recipient (due to lack of `RECEIVE` permissions). The voucher can only be claimed by the intended recipient once the recipient address has `RECEIVE` permissions. For transfers occurring between externally owned addresses, vouchers are not created upon failed transfer due to lack of permissions; the transaction is reverted instead
### Default Namespace Values
Default namespace values for role managers, policy statuses, and policy managers will be assigned during namespace creation under the following conditions:
* If no role managers are set for any roles:
* The namespace creator is set as the role manager for ALL roles
* Note: If at least one role manager is specified for any role upon launch of the namespace, no default role managers are set, even for roles with unspecified role managers
* If any policy statuses are not set for a given action:
* The policy status is set to not disabled and not sealed for the action (unless otherwise specified during creation)
* If no policy managers/policy manager capabilities (both are represented in a single data structure) are set for any actions:
* The policy manager for all actions is set to the namespace creator
* The policy manager is given the ability to both disable/enable the action and seal the action policy status
> **Caution: Default namespace values will not prevent a namespace from being rendered unusable during creation if incorrect permissions are set.**
* To prevent this from occurring, **namespace management roles should be mapped to namespace management actions during namespace creation, and role managers should also be set for each namespace management role** (especially roles that can modify role managers and role provisions) upon namespace creation.
* This will prevent cases where the namespace cannot be updated when roles are not properly mapped to permissions, role permissions/role managers cannot be updated because no address has permission to update role managers or assign roles, and no address has permission to create/provision new roles
### Updating Namespaces with Governance
Namespace settings can be overridden by governance, but not by default. This prevents users from arbitrarily editing namespaces they do not own through governance. To enable namespace updates through governance, the `Gov` module address should be assigned the appropriate roles/permissions.
# State
Source: https://docs.injective.network/developers-native/injective/permissions/02_state
## Namespaces
```go theme={null}
// Namespace defines a permissions namespace
type Namespace struct {
Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"`
ContractHook string `protobuf:"bytes,2,opt,name=contract_hook,json=contractHook,proto3" json:"contract_hook,omitempty"`
RolePermissions []*Role `protobuf:"bytes,3,rep,name=role_permissions,json=rolePermissions,proto3" json:"role_permissions,omitempty"`
ActorRoles []*ActorRoles `protobuf:"bytes,4,rep,name=actor_roles,json=actorRoles,proto3" json:"actor_roles,omitempty"`
RoleManagers []*RoleManager `protobuf:"bytes,5,rep,name=role_managers,json=roleManagers,proto3" json:"role_managers,omitempty"`
PolicyStatuses []*PolicyStatus `protobuf:"bytes,6,rep,name=policy_statuses,json=policyStatuses,proto3" json:"policy_statuses,omitempty"`
PolicyManagerCapabilities []*PolicyManagerCapability `protobuf:"bytes,7,rep,name=policy_manager_capabilities,json=policyManagerCapabilities,proto3" json:"policy_manager_capabilities,omitempty"`
}
```
## Roles
```go theme={null}
// Role defines a set of permitted actions with a name and unique ID
type Role struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
RoleId uint32 `protobuf:"varint,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"`
Permissions uint32 `protobuf:"varint,3,opt,name=permissions,proto3" json:"permissions,omitempty"`
}
```
* As previously mentioned, Permissions is the sum of all permitted [Action](https://www.notion.so/Action-13e7a004ab7580ef8c16cebd37096d91?pvs=21) values
## ActorRoles
```go theme={null}
// AddressRoles defines roles for an actor
type ActorRoles struct {
Actor string `protobuf:"bytes,1,opt,name=actor,proto3" json:"actor,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
}
```
## RoleManagers
```go theme={null}
// RoleManager defines roles that a manager address can give to actors
type RoleManager struct {
Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
}
```
## PolicyStatus
```go theme={null}
// PolicyStatus defines whether an action is disabled or enabled and if the policy is sealed
type PolicyStatus struct {
Action Action `protobuf:"varint,1,opt,name=action,proto3,enum=injective.permissions.v1beta1.Action" json:"action,omitempty"`
IsDisabled bool `protobuf:"varint,2,opt,name=is_disabled,json=isDisabled,proto3" json:"is_disabled,omitempty"`
IsSealed bool `protobuf:"varint,3,opt,name=is_sealed,json=isSealed,proto3" json:"is_sealed,omitempty"`
}
```
## PolicyManagerCapability
```go theme={null}
// PolicyManagerCapability defines if the policy manager of an action can disable or seal the action policy
type PolicyManagerCapability struct {
Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"`
Action Action `protobuf:"varint,2,opt,name=action,proto3,enum=injective.permissions.v1beta1.Action" json:"action,omitempty"`
CanDisable bool `protobuf:"varint,3,opt,name=can_disable,json=canDisable,proto3" json:"can_disable,omitempty"`
CanSeal bool `protobuf:"varint,4,opt,name=can_seal,json=canSeal,proto3" json:"can_seal,omitempty"`
}
```
## Action
```go theme={null}
// each Action enum value should be a power of two
type Action int32
const (
// 0 is reserved for ACTION_UNSPECIFIED
Action_UNSPECIFIED Action = 0
// 1 is reserved for MINT
Action_MINT Action = 1
// 2 is reserved for RECEIVE
Action_RECEIVE Action = 2
// 4 is reserved for BURN
Action_BURN Action = 4
// 8 is reserved for SEND
Action_SEND Action = 8
// 16 is reserved for SUPER_BURN
Action_SUPER_BURN Action = 16
// 2^27 is reserved for MODIFY_POLICY_MANAGERS
Action_MODIFY_POLICY_MANAGERS Action = 134217728
// 2^28 is reserved for MODIFY_CONTRACT_HOOK
Action_MODIFY_CONTRACT_HOOK Action = 268435456
// 2^29 is reserved for MODIFY_ROLE_PERMISSIONS
Action_MODIFY_ROLE_PERMISSIONS Action = 536870912
// 2^30 is reserved for MODIFY_ROLE_MANAGERS
Action_MODIFY_ROLE_MANAGERS Action = 1073741824
```
# State Transitions
Source: https://docs.injective.network/developers-native/injective/permissions/03_state_transitions
## Create Namespace
```protobuf theme={null}
message MsgCreateNamespace {
option (amino.name) = "permissions/MsgCreateNamespace";
option (cosmos.msg.v1.signer) = "sender";
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
Namespace namespace = 2 [ (gogoproto.nullable) = false ];
}
// Namespace defines a permissions namespace
message Namespace {
string denom = 1; // tokenfactory denom to which this namespace applies to
string contract_hook = 2; // address of smart contract to apply code-based restrictions
repeated Role role_permissions = 3; // permissions for each role
repeated ActorRoles actor_roles = 4; // roles for each actor
repeated RoleManager role_managers = 5; // managers for each role
repeated PolicyStatus policy_statuses = 6; // status for each policy
repeated PolicyManagerCapability policy_manager_capabilities = 7; // capabilities for each manager for each policy
}
// Role is only used for storage
message Role {
string name = 1;
uint32 role_id = 2;
uint32 permissions = 3;
}
// AddressRoles defines roles for an actor
message ActorRoles {
string actor = 1;
repeated string roles = 2;
}
// RoleManager defines roles for a manager address
message RoleManager {
string manager = 1;
repeated string roles = 2;
}
message PolicyStatus {
Action action = 1;
bool is_disabled = 2;
bool is_sealed = 3;
}
message PolicyManagerCapability {
string manager = 1;
Action action = 2;
bool can_disable = 3;
bool can_seal = 4;
}
// each Action enum value should be a power of two
enum Action {
// 0 is reserved for ACTION_UNSPECIFIED
UNSPECIFIED = 0;
// 1 is reserved for MINT
MINT = 1;
// 2 is reserved for RECEIVE
RECEIVE = 2;
// 4 is reserved for BURN
BURN = 4;
// 8 is reserved for SEND
SEND = 8;
// 16 is reserved for SUPER_BURN
SUPER_BURN = 16;
//
// MANAGER ACTIONS BELOW
//
// 2^27 is reserved for MODIFY_POLICY_MANAGERS
MODIFY_POLICY_MANAGERS = 0x8000000; // 2^27 or 134217728
// 2^28 is reserved for MODIFY_CONTRACT_HOOK
MODIFY_CONTRACT_HOOK = 0x10000000; // 2^28 or 268435456
// 2^29 is reserved for MODIFY_ROLE_PERMISSIONS
MODIFY_ROLE_PERMISSIONS = 0x20000000; // 2^29 or 536870912
// 2^30 is reserved for MODIFY_ROLE_MANAGERS
MODIFY_ROLE_MANAGERS = 0x40000000; // 2^30 or 1073741824
}
```
## Update Namespace
```protobuf theme={null}
message MsgUpdateNamespace {
option (amino.name) = "permissions/MsgUpdateNamespace";
option (cosmos.msg.v1.signer) = "sender";
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string denom = 2; // denom whose namespace updates are to be applied
message SetContractHook { string new_value = 1; }
SetContractHook contract_hook = 3; // address of smart contract to apply code-based restrictions
repeated Role role_permissions = 4; // role permissions to update
repeated RoleManager role_managers = 5; // role managers to update
repeated PolicyStatus policy_statuses = 6; // policy statuses to update
repeated PolicyManagerCapability policy_manager_capabilities = 7; // policy manager capabilities to update
}
message Role {
string name = 1;
uint32 role_id = 2;
uint32 permissions = 3;
}
// RoleManager defines roles for a manager address
message RoleManager {
string manager = 1;
repeated string roles = 2;
}
message PolicyStatus {
Action action = 1;
bool is_disabled = 2;
bool is_sealed = 3;
}
message PolicyManagerCapability {
string manager = 1;
Action action = 2;
bool can_disable = 3;
bool can_seal = 4;
}
// each Action enum value should be a power of two
enum Action {
// 0 is reserved for ACTION_UNSPECIFIED
UNSPECIFIED = 0;
// 1 is reserved for MINT
MINT = 1;
// 2 is reserved for RECEIVE
RECEIVE = 2;
// 4 is reserved for BURN
BURN = 4;
// 8 is reserved for SEND
SEND = 8;
// 16 is reserved for SUPER_BURN
SUPER_BURN = 16;
//
// MANAGER ACTIONS BELOW
//
// 2^27 is reserved for MODIFY_POLICY_MANAGERS
MODIFY_POLICY_MANAGERS = 0x8000000; // 2^27 or 134217728
// 2^28 is reserved for MODIFY_CONTRACT_HOOK
MODIFY_CONTRACT_HOOK = 0x10000000; // 2^28 or 268435456
// 2^29 is reserved for MODIFY_ROLE_PERMISSIONS
MODIFY_ROLE_PERMISSIONS = 0x20000000; // 2^29 or 536870912
// 2^30 is reserved for MODIFY_ROLE_MANAGERS
MODIFY_ROLE_MANAGERS = 0x40000000; // 2^30 or 1073741824
}
```
## Update Actor Roles
* Roles can be given or revoked from addresses with `MsgUpdateActorRoles`
```protobuf theme={null}
message MsgUpdateActorRoles {
option (amino.name) = "permissions/MsgUpdateActorRoles";
option (cosmos.msg.v1.signer) = "sender";
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string denom = 2; // namespace denom to which this updates are applied
repeated RoleActors role_actors_to_add = 3; // roles to add for given actors
repeated RoleActors role_actors_to_revoke = 5; // roles to revoke from given actors
}
message RoleActors {
string role = 1;
repeated string actors = 2;
}
```
## Claim Voucher
```protobuf theme={null}
message MsgClaimVoucher {
option (amino.name) = "permissions/MsgClaimVoucher";
option (cosmos.msg.v1.signer) = "sender";
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string denom = 2;
}
```
# How to Launch Permissioned Assets
Source: https://docs.injective.network/developers-native/injective/permissions/04_launch_permissioned_asset
Permissioned assets can be launched using [Injective APIs/SDKs](https://api.injective.exchange/#permissions) or the Injective CLI, `injectived`. See [https://docs.injective.network/toolkits/injectived](https://docs.injective.network/toolkits/injectived) for more information on using the Injective CLI.
```bash theme={null}
injectived tx permissions [command]
```
* There are four transaction commands available through the CLI:
* `create-namespace`
* Used to create a permissioned namespace from a json file for a `TokenFactory` denom
* When creating a namespace, the address must be the admin of the same `TokenFactory` denom. Otherwise the namespace cannot be launched
* `update-namespace`
* Used to update the namespace parameters including:
* Contract hook
* Role permissions
* Role managers
* Policy statuses
* Policy managers
* Namespace updates are incremental, so unless a change is explicitly stated in the JSON, existing state will be untouched
* `update-namespace-roles`
* Used to assign roles to addresses and revoke roles from addresses
* Like with namespace updates, role updates are also incremental
* `claim-voucher`
* Mainly used when a user is not authorized to receive a permissioned asset but is sent funds from an Injective module. The funds will be held in an Injective module address until the user receives the correct permissions to receive the asset
## `create-namespace`
```bash theme={null}
injectived tx permissions create-namespace [flags]
```
* The json file should have the following format (remove all comments before submitting):
```json theme={null}
{ // Remove all comments before submitting!
"denom": "factory/inj1address/myTokenDenom",
"contract_hook": "ContractHookAddress",
"role_permissions": [ // CAUTION: makes sure to set role permissions for all namespace management roles!
{
"name": "EVERYONE",
"role_id": 0,
"permissions": 10 // SEND (8) + RECEIVE (2); excludes MINT, SUPER_BURN, and management actions
},
{
"name": "admin",
"role_id": 1,
"permissions": 2013265920 // MODIFY_ROLE_PERMISSIONS, MODIFY_ROLE_MANAGERS, etc. (all namespace management actions)
},
{
"name": "user",
"role_id": 2,
"permissions": 15 // MINT (1), RECEIVE (2), BURN (4), SEND (8)
}
],
"actor_roles": [
{
"actor": "inj1specificactoraddress",
"roles": ["admin"]
},
{
"actor": "inj1anotheractoraddress",
"roles": ["user"]
}
],
"role_managers": [ // CAUTION: Make sure to set role managers for all namespace management roles!
{
"manager": "inj1manageraddress",
"roles": ["admin"]
}
],
"policy_statuses": [
{
"action": 1, // Action_MINT
"is_disabled": false,
"is_sealed": false
},
{
"action": 4, // Action_BURN
"is_disabled": false,
"is_sealed": false
}
],
"policy_manager_capabilities": [
{
"manager": "inj1policymanageraddress",
"action": 268435456, // MODIFY_CONTRACT_HOOK
"can_disable": true,
"can_seal": false
}
]
}
```
## `update-namespace`
```json theme={null}
injectived tx permissions update-namespace [flags]
```
* Namespace updates are incremental, so unless a change is explicitly stated in the JSON, existing state will be untouched
```json theme={null}
{ // Remove all comments before submitting!
"denom": "factory/inj1address/myTokenDenom",
"contract_hook": {
"new_value": "newContractHookAddress"
},
"role_permissions": [
{
"name": "user",
"role_id": 2,
"permissions": 10 // RECEIVE (2) + SEND (8)
},
{
"name": "EVERYONE",
"role_id": 0,
"permissions": 0 // Revoke all permissions
}
],
"role_managers": [
{
"manager": "inj1manageraddress",
"roles": ["admin", "user"]
}
],
"policy_statuses": [
{
"action": 1, // MINT
"is_disabled": true,
"is_sealed": false
},
{
"action": 4, // BURN
"is_disabled": false,
"is_sealed": true
}
],
"policy_manager_capabilities": [
{
"manager": "inj1policymanageraddress",
"action": 536870912, // MODIFY_ROLE_PERMISSIONS
"can_disable": true,
"can_seal": false
}
]
}
```
## `update-namespace-roles`
```json theme={null}
injectived tx permissions update-namespace-roles [flags]
```
```json theme={null}
{
"denom": "factory/inj1address/myTokenDenom",
"role_actors_to_add": [
{
"role": "admin",
"actors": [
"inj1actoraddress1",
"inj1actoraddress2"
]
},
{
"role": "user",
"actors": [
"inj1actoraddress3"
]
}
],
"role_actors_to_revoke": [
{
"role": "user",
"actors": [
"inj1actoraddress4"
]
},
{
"role": "admin",
"actors": [
"inj1actoraddress5"
]
}
]
}
```
## `claim-voucher`
```bash theme={null}
injectived tx permissions claim-voucher
```
* No JSON is needed for this command since the only parameter needed is the denom
# Error Codes
Source: https://docs.injective.network/developers-native/injective/permissions/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ----------- | ---------- | ---------------------------------- |
| permissions | 2 | namespace for denom already exists |
| permissions | 3 | unauthorized account |
| permissions | 4 | invalid genesis |
| permissions | 5 | invalid namespace |
| permissions | 6 | invalid permissions |
| permissions | 7 | unknown role |
| permissions | 8 | unknown contract address |
| permissions | 9 | restricted action |
| permissions | 10 | invalid role |
| permissions | 11 | namespace for denom does not exist |
| permissions | 12 | wasm hook query error |
| permissions | 13 | voucher not found |
| permissions | 14 | invalid contract hook |
| permissions | 15 | unknown policy |
| permissions | 16 | unauthorized policy change |
# Permissions
Source: https://docs.injective.network/developers-native/injective/permissions/index
## Abstract
Bringing real world permissioned assets (e.g. tokenized treasury yield products) on-chain require certain levels of control over asset actions/properties such as transfers, holders (whitelists), and more.
The `permissions` module allows managing certain prefixed actions and roles for real world assets and permissioned denoms created within a namespace on the chain-level. It provides a flexible and extensible way to define and enforce permissions and roles and serves as the entry point through which real world assets can be natively issued and managed on Injective.
## Contents
1. [Concepts](/developers-native/injective/permissions/01_concepts)
2. [State](/developers-native/injective/permissions/02_state)
3. [State Transitions](/developers-native/injective/permissions/03_state_transitions)
4. [How to Launch Permissioned Assets](/developers-native/injective/permissions/04_launch_permissioned_asset)
5. [Error Codes](/developers-native/injective/permissions/99_errors)
# Concepts
Source: https://docs.injective.network/developers-native/injective/tokenfactory/01_concepts
# Concepts
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 (if enabled)
* 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 `""`, meaning no account has admin privileges
of the asset.
# State
Source: https://docs.injective.network/developers-native/injective/tokenfactory/02_state
# State
The tokenfactory module keeps state of the following primary objects:
## Denom Authority Metadata
* 0x02 + | + denom + | + 0x01 ⇒ `DenomAuthorityMetadata`
## Denom Creators
* 0x03 + | + creator + | denom ⇒ denom
```protobuf theme={null}
// DenomAuthorityMetadata specifies metadata for addresses that have specific
// capabilities over a token factory denom.
message DenomAuthorityMetadata {
option (gogoproto.equal) = true;
// Can be empty for no admin, or a valid injective address
string admin = 1 [ (gogoproto.moretags) = "yaml:\"admin\"" ];
// true if the admin can burn tokens from other addresses
bool admin_burn_allowed = 2 [ (gogoproto.moretags) = "yaml:\"admin_burn_allowed\"" ];
}
```
Genesis state defines the initial state of the module to be used to setup the module.
```protobuf theme={null}
// GenesisState defines the tokenfactory module's genesis state.
message GenesisState {
// params defines the parameters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];
repeated GenesisDenom factory_denoms = 2 [
(gogoproto.moretags) = "yaml:\"factory_denoms\"",
(gogoproto.nullable) = false
];
}
// GenesisDenom defines a tokenfactory denom that is defined within genesis
// state. The structure contains DenomAuthorityMetadata which defines the
// denom's admin.
message GenesisDenom {
option (gogoproto.equal) = true;
string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
DenomAuthorityMetadata authority_metadata = 2 [
(gogoproto.moretags) = "yaml:\"authority_metadata\"",
(gogoproto.nullable) = false
];
}
```
## Params
`Params` is a module-wide configuration that stores system parameters and defines overall functioning of the tokenfactory module.
This module is modifiable by governance using params update proposal natively supported by `gov` module.
Struct for the `ocr` module params store.
```protobuf theme={null}
// Params defines the parameters for the tokenfactory module.
message Params {
repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
(gogoproto.moretags) = "yaml:\"denom_creation_fee\"",
(gogoproto.nullable) = false
];
}
```
# null
Source: https://docs.injective.network/developers-native/injective/tokenfactory/03_messages
# Messages
In this section we describe the processing of the tokenfactory messages and the corresponding updates to the state.
## Messages
### CreateDenom
Creates a denom of `factory/{creator address}/{subdenom}` given the denom creator
address, subdenom and associated metadata (name, symbol, decimals). Subdenoms can contain `[a-zA-Z0-9./]`.
`allow_admin_burn` can be set to true to allow the admin to burn tokens from other addresses.
```protobuf theme={null}
message MsgCreateDenom {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
// subdenom can be up to 44 "alphanumeric" characters long.
string subdenom = 2 [ (gogoproto.moretags) = "yaml:\"subdenom\"" ];
string name = 3 [ (gogoproto.moretags) = "yaml:\"name\"" ];
string symbol = 4 [ (gogoproto.moretags) = "yaml:\"symbol\"" ];
uint32 decimals = 5 [ (gogoproto.moretags) = "yaml:\"decimals\"" ];
// true if admins are allowed to burn tokens from other addresses
bool allow_admin_burn = 6 [ (gogoproto.moretags) = "yaml:\"allow_admin_burn\"" ];}
```
**State Modifications:**
* Fund community pool with the denom creation fee from the creator address, set
in `Params`.
* Set `DenomMetaData` via bank keeper.
* Set `AuthorityMetadata` for the given denom to store the admin for the created
denom `factory/{creator address}/{subdenom}`. Admin is automatically set as the
Msg sender.
* Add denom to the `CreatorPrefixStore`, where a state of denoms created per
creator is kept.
### Mint
Minting of a specific denom is only allowed for the current admin.
Note, the current admin is defaulted to the creator of the denom.
```protobuf theme={null}
message MsgMint {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
cosmos.base.v1beta1.Coin amount = 2 [
(gogoproto.moretags) = "yaml:\"amount\"",
(gogoproto.nullable) = false
];
}
```
**State Modifications:**
* Safety check the following
* Check that the denom minting is created via `tokenfactory` module
* Check that the sender of the message is the admin of the denom
* Mint designated amount of tokens for the denom via `bank` module
### Burn
Burning of a specific denom is only allowed for the current admin.
Note, the current admin is defaulted to the creator of the denom.
```protobuf theme={null}
message MsgBurn {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
cosmos.base.v1beta1.Coin amount = 2 [
(gogoproto.moretags) = "yaml:\"amount\"",
(gogoproto.nullable) = false
];
}
```
**State Modifications:**
* Safety check the following
* Check that the denom minting is created via `tokenfactory` module
* Check that the sender of the message is the admin of the denom
* Burn designated amount of tokens for the denom via `bank` module
### ChangeAdmin
Change the admin of a denom. Note, this is only allowed to be called by the current admin of the denom. After the admin address is set to zero address, token holders can still execute `MsgBurn` for tokens they possess.
```protobuf theme={null}
message MsgChangeAdmin {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string denom = 2 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
string newAdmin = 3 [ (gogoproto.moretags) = "yaml:\"new_admin\"" ];
}
```
### SetDenomMetadata
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. The admin can also disable the admin burn
capability, if enabled.
```protobuf theme={null}
message MsgSetDenomMetadata {
option (amino.name) = "injective/tokenfactory/set-denom-metadata";
option (cosmos.msg.v1.signer) = "sender";
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
cosmos.bank.v1beta1.Metadata metadata = 2 [
(gogoproto.moretags) = "yaml:\"metadata\"",
(gogoproto.nullable) = false
];
message AdminBurnDisabled {
// true if the admin burn capability should be disabled
bool should_disable = 1 [ (gogoproto.moretags) = "yaml:\"should_disable\"" ];
}
AdminBurnDisabled admin_burn_disabled = 3 [ (gogoproto.moretags) = "yaml:\"admin_burn_disabled\"" ];
}
```
**State Modifications:**
* Check that sender of the message is the admin of denom
* Modify `AuthorityMetadata` state entry to change the admin of the denom and to potentially disable admin burn capability.
## Expectations from the chain
The chain's bech32 prefix for addresses can be at most 16 characters long.
This comes from denoms having a 128 byte maximum length, enforced from the SDK,
and us setting longest\_subdenom to be 44 bytes.
A token factory token's denom is: `factory/{creator address}/{subdenom}`
Splitting up into sub-components, this has:
* `len(factory) = 7`
* `2 * len("/") = 2`
* `len(longest_subdenom)`
* `len(creator_address) = len(bech32(longest_addr_length, chain_addr_prefix))`.
Longest addr length at the moment is `32 bytes`. Due to SDK error correction
settings, this means `len(bech32(32, chain_addr_prefix)) = len(chain_addr_prefix) + 1 + 58`.
Adding this all, we have a total length constraint of `128 = 7 + 2 + len(longest_subdenom) + len(longest_chain_addr_prefix) + 1 + 58`.
Therefore `len(longest_subdenom) + len(longest_chain_addr_prefix) = 128 - (7 + 2 + 1 + 58) = 60`.
The choice between how we standardized the split these 60 bytes between maxes
from longest\_subdenom and longest\_chain\_addr\_prefix is somewhat arbitrary.
Considerations going into this:
* Per [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32)
the technically longest HRP for a 32 byte address ('data field') is 31 bytes.
(Comes from encode(data) = 59 bytes, and max length = 90 bytes)
* subdenom should be at least 32 bytes so hashes can go into it
* longer subdenoms are very helpful for creating human readable denoms
* chain addresses should prefer being smaller. The longest HRP in cosmos to date is 11 bytes. (`persistence`)
For explicitness, its currently set to `len(longest_subdenom) = 44` and `len(longest_chain_addr_prefix) = 16`.
Please note, if the SDK increases the maximum length of a denom from 128 bytes,
these caps should increase.
So please don't make code rely on these max lengths for parsing.
# Events
Source: https://docs.injective.network/developers-native/injective/tokenfactory/04_events
# Events
The tokenfactory module emits the following events:
An EventCreateTFDenom is emitted upon MsgCreateDenom execution, which creates a new token factory denom.
```protobuf theme={null}
message EventCreateTFDenom {
string account = 1;
string denom = 2;
}
```
An EventMintTFDenom is emitted upon MsgMint execution, which mints a new token factory denom for a recipient.
```protobuf theme={null}
message EventMintTFDenom {
string recipient_address = 1;
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false];
}
```
An EventBurnDenom is emitted upon MsgBurn execution, which burns a specified amount for any denom for a user.
```protobuf theme={null}
message EventBurnDenom {
string burner_address = 1;
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false];
}
```
An EventChangeTFAdmin is emitted upon MsgChangeAdmin execution, which changes the admin address for a new token factory denom.
```protobuf theme={null}
message EventChangeTFAdmin {
string denom = 1;
string new_admin_address = 2;
}
```
An EventSetTFDenomMetadata is emitted upon MsgSetDenomMetadata execution, which sets the token factory denom metadata for a given token factory denom.
```protobuf theme={null}
message EventSetTFDenomMetadata {
string denom = 1;
cosmos.bank.v1beta1.Metadata metadata = 2[(gogoproto.nullable) = false];
}
```
# Params
Source: https://docs.injective.network/developers-native/injective/tokenfactory/05_params
# Parameters
The tokenfactory module contains the following parameters:
```protobuf theme={null}
// Params defines the parameters for the tokenfactory module.
message Params {
repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
(gogoproto.moretags) = "yaml:\"denom_creation_fee\"",
(gogoproto.nullable) = false
];
}
```
# Error Codes
Source: https://docs.injective.network/developers-native/injective/tokenfactory/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------------ | ---------- | -------------------------------------------------------------------- |
| tokenfactory | 2 | attempting to create a denom that already exists (has bank metadata) |
| tokenfactory | 3 | unauthorized account |
| tokenfactory | 4 | invalid denom |
| tokenfactory | 5 | invalid creator |
| tokenfactory | 6 | invalid authority metadata |
| tokenfactory | 7 | invalid genesis |
| tokenfactory | 8 | subdenom too long, max length is %d bytes |
| tokenfactory | 9 | subdenom too short, min length is %d bytes |
| tokenfactory | 10 | nested subdenom too short, each one should have at least %d bytes |
| tokenfactory | 11 | creator too long, max length is %d bytes |
| tokenfactory | 12 | denom does not exist |
| tokenfactory | 13 | amount has to be positive |
# Tokenfactory
Source: https://docs.injective.network/developers-native/injective/tokenfactory/index
## Abstract
The tokenfactory module allows for the permissionless creation of new bank denom tokens.
## Contents
1. [Concepts](/developers-native/injective/tokenfactory/01_concepts)
2. [State](/developers-native/injective/tokenfactory/02_state)
3. [Messages](/developers-native/injective/tokenfactory/03_messages)
4. [Events](/developers-native/injective/tokenfactory/04_events)
5. [Parameters](/developers-native/injective/tokenfactory/05_params)
# Concepts
Source: https://docs.injective.network/developers-native/injective/wasmx/01_concepts
## Concepts
### Begin blocker execution
Smart contracts can only respond to incoming messages and do not have the ability to execute actions on their own schedule. The Wasmx module allows contracts to be registered and called in the begin blockers section of each block.
To be eligible for this, each registered contract must respond to the sudo message called `begin_blocker` which can only be called by the chain itself and not directly by any user or other contract. This ensures that the "begin\_blocker" message can be trusted.
### Registration
Upon registering a contract, the user must declare a gas price, which is the amount they are willing to pay for contract execution, as well as a gas limit, which is the maximum amount of gas that can be consumed during the execution of the contract.
Currently, contract registration can only be done through a governance proposal. This proposal, if approved, will add the contract at a specific address to the list of contracts that are run during each "begin blockers" period.
For security reasons, the proposer must specify a code\_id for the contract, which will be verified upon registration and each time the contract is executed. This is to prevent an attacker from registering a benign contract but later upgrading it to a malicious one. The proposer can request to be exempt from this check when registering the contract to avoid delays when a new version of the contract is released, but this may affect the voting results depending on the trustworthiness of the proposer.
The proposer can also request for the contract to be "pinned," meaning it is loaded and kept in memory, which can greatly improve the performance of the contract.
### Deregistration
A contract can be deregistered through a governance proposal, which can be initiated by anyone, including the contract owner if they no longer require the contract or by any other individual if the contract is found to be malicious.
If contract fails to execute due to insufficient gas it will be automatically deregistered.
When contract is deregistered, wasmx will call special `deregister{}` callback (if present) as a sudo message in the contract.
### Deactivation
A contract can be deactivated automatically if it runs out of gas, or manually by the contract owner. When a contract is deactivated, wasmx will call a special `deactivate{}` callback (if present) as a sudo message in the contract. The contract can be reactivated by the contract owner.
### Fee Grant
The Wasmx module allows other addresses (contracts, EOAs) to pay for the Begin blocker execution of other contracts through the [`x/feegrant`](https://docs.cosmos.network/main/modules/feegrant) module.
When a contract is being registered for the first time, users specify the `FundingMode` which indicates how the contract's execution will be funded. Three modes are supported:
* `SelfFunded` - contract will pay for its own execution (default)
* `GrantOnly` - contract will execute if its associated allowance covers for it (provided by the `GranterAddress` in the `ContractRegistrationRequest`)
* `Dual` - contract will prioritize spending its allowance's funds. In case the allowance cannot cover for execution, it will use its own funds instead
Given there are 3 kinds of allowances provided by the `x/feegrant` module (Basic, Periodic and AllowedMsg), the wasmx module supports only Basic and Periodic. Granting an `AllowedMsgAllowance` to a contract is discouraged as any contract attempting to use this kind of allowance will error by default.
### Pausing, params update
The owner of a contract has the ability to deactivate or activate the contract at any time without requiring a governance vote. They can also update the parameters for contract execution, such as the gas price or gas limit, at any time.
### Batch methods
For convenience, the Wasmx module provides batch versions of some of the previously mentioned proposals, such as batch registration and deregistration, as well as a batch version of the StoreCodeProposal. These batch versions allow multiple proposals to be processed at the same time, rather than individually.
# Data
Source: https://docs.injective.network/developers-native/injective/wasmx/02_data
## Data
### RegisteredContract
Data stored about each contract
```go theme={null}
type RegisteredContract struct {
// limit of gas per BB execution
GasLimit uint64 json:"gas_limit,omitempty"
// gas price that contract is willing to pay for execution in BeginBlocker
GasPrice uint64 json:"gas_price,omitempty"
// is contract currently active
IsExecutable bool json:"is_executable,omitempty"
// code_id that is allowed to be executed (to prevent malicious updates) - if nil/0 any code_id can be executed
CodeId uint64 json:"code_id,omitempty"ł
// optional - admin addr that is allowed to update contract data
AdminAddress string json:"admin_address,omitempty"
// address of an account providing grant for execution
GranterAddress string
// enum indicating how contract's execution is funded
FundMode FundingMode
}
type FundingMode int32
const (
FundingMode_Unspecified FundingMode = 0
FundingMode_SelfFunded FundingMode = 1
FundingMode_GrantOnly FundingMode = 2
FundingMode_Dual FundingMode = 3
)
```
# Governance Proposals
Source: https://docs.injective.network/developers-native/injective/wasmx/03_proposals
## Governance Proposals
### ContractRegistrationRequest
`ContractRegistrationRequest` is a base message for registering new contracts (shouldn't be used directly but as a part of proposal)
```go theme={null}
type ContractRegistrationRequest struct {
ContractAddress string
GasLimit uint64
GasPrice uint64
PinContract bool
AllowUpdating bool
CodeId uint64
ContractAdmin string
GranterAddress string
FundMode FundingMode
}
```
**Fields description**
* `ContractAddress` - unique Identifier for contract instance to be registered.
* `GasLimit` - Maximum gas to be used for the smart contract execution.
* `GasPrice` - Gas price to be used for the smart contract execution.
* `PinContract` - should contract be pinned.
* `AllowUpdating`- defines wether contract owner can migrate it without need to register again (if false only current code\_id will be allowed to be executed)
* `CodeId` - code\_id of the contract being registered - will be verified on execution to allow last minute change (after votes were cast)
* `AdminAddress` - optional address of admin account (that will be allowed to pause or update contract params)
* `GranterAddress` - address of an account which granted funds for execution. Must be set if `FundMode` is other than `SelfFunded` (see below for an explanation)
`FundingMode` indicates how the contract will fund its own execution.
```go theme={null}
enum FundingMode {
Unspecified = 0;
SelfFunded = 1;
GrantOnly = 2;
Dual = 3;
}
```
* `SelfFunded` - contract will use its own funds to execute.
* `GrantOnly` - contract wil only use funds provided by the grant.
* `Dual` - contract will first deplete grant's funds before using its own.
### ContractRegistrationRequestProposal
`ContractRegistrationRequestProposal` defines an SDK message to register a single contract in wasmx contract registry.
```go theme={null}
type ContractRegistrationRequestProposal struct {
Title string
Description string
ContractRegistrationRequest ContractRegistrationRequest
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `ContractRegistrationRequest` contains contract registration request (as described above)
### BatchContractRegistrationRequestProposal
`BatchContractRegistrationRequestProposal` defines an SDK message to register a batch of contracts in wasmx contract registry.
```go theme={null}
type BatchContractRegistrationRequestProposal struct {
Title string
Description string
ContractRegistrationRequests []ContractRegistrationRequest
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `ContractRegistrationRequests` contains a list of contracts registration requests (as described above)
### BatchStoreCodeProposal
`BatchStoreCodeProposal` defines an SDK message to store a batch of contracts in wasm.
```go theme={null}
type BatchStoreCodeProposal struct {
Title string
Description string
Proposals []types.StoreCodeProposal
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Proposals` contains a list of store code proposals (as defined by Cosmos wasm module)
### BatchContractDeregistrationProposal
`BatchContractDeregistrationProposal` defines an SDK message to deregister a batch of contracts in wasm.
```go theme={null}
type BatchContractDeregistrationProposal struct {
Title string
Description string
Contracts []string
}
```
**Fields description**
* `Title` describes the title of the proposal.
* `Description` describes the description of the proposal.
* `Contracts` contains a list of addresses of contracts to be deregistered
# Messages
Source: https://docs.injective.network/developers-native/injective/wasmx/04_messages
## Messages
### MsgUpdateContract
Updates registered contract execution params (gas price, limit). Can also define a new admin account.
Can be called only by admin (if defined) or contract itself.
```go theme={null}
type MsgUpdateContract struct {
Sender string `json:"sender,omitempty"`
// Unique Identifier for contract instance to be registered.
ContractAddress string `json:"contract_address,omitempty"`
// Maximum gas to be used for the smart contract execution.
GasLimit uint64 `json:"gas_limit,omitempty"`
// gas price to be used for the smart contract execution.
GasPrice uint64 `json:"gas_price,omitempty"`
// optional - admin account that will be allowed to perform any changes
AdminAddress string `json:"admin_address,omitempty"`
}
```
### MsgDeactivateContract
Deactivates a registered contract (it will no longer be executed in begin blocker)
```go theme={null}
type MsgDeactivateContract struct {
Sender string `json:"sender,omitempty"`
// Unique Identifier for contract instance to be activated.
ContractAddress string `json:"contract_address,omitempty"`
}
```
### MsgActivateContract
Reactivates a registered contract (it will be executed in begin blocker from now on again)
```go theme={null}
type MsgActivateContract struct {
Sender string `json:"sender,omitempty"`
// Unique Identifier for contract instance to be activated.
ContractAddress string `json:"contract_address,omitempty"`
}
```
### MsgExecuteContract
Invokes a function defined within the smart contract. Function and parameters are encoded in `ExecuteMsg`, which is a JSON message encoded in Base64.
```go theme={null}
type MsgExecuteContract struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Contract sdk.AccAddress `json:"contract" yaml:"contract"`
ExecuteMsg core.Base64Bytes `json:"execute_msg" yaml:"execute_msg"`
Coins sdk.Coins `json:"coins" yaml:"coins"`
}
```
### MsgMigrateContract
Can be issued by the owner of a migratable smart contract to reset its code ID to another one. `MigrateMsg` is a JSON message encoded in Base64.
```go theme={null}
type MsgMigrateContract struct {
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Contract sdk.AccAddress `json:"contract" yaml:"contract"`
NewCodeID uint64 `json:"new_code_id" yaml:"new_code_id"`
MigrateMsg core.Base64Bytes `json:"migrate_msg" yaml:"migrate_msg"`
}
```
### MsgUpdateContractOwner
Can be issued by the smart contract's owner to transfer ownership.
```go theme={null}
type MsgUpdateContractOwner struct {
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
NewOwner sdk.AccAddress `json:"new_owner" yaml:"new_owner"`
Contract sdk.AccAddress `json:"contract" yaml:"contract"`
}
```
# Params
Source: https://docs.injective.network/developers-native/injective/wasmx/05_params
## Params
The subspace for the wasmx module is `wasmx`.
```go theme={null}
type Params struct {
// Set the status to active to indicate that contracts can be executed in begin blocker.
IsExecutionEnabled bool ` json:"is_execution_enabled,omitempty"`
// Maximum aggregate total gas to be used for the contract executions in the BeginBlocker.
MaxBeginBlockTotalGas uint64 `json:"max_begin_block_total_gas,omitempty"`
// the maximum gas limit each individual contract can consume in the BeginBlocker.
MaxContractGasLimit uint64 `json:"max_contract_gas_limit,omitempty"`
// min_gas_price defines the minimum gas price the contracts must pay to be executed in the BeginBlocker.
MinGasPrice uint64 `json:"min_gas_price,omitempty"`
}
```
# Error Codes
Source: https://docs.injective.network/developers-native/injective/wasmx/99_errors
This document lists the error codes used in the module.
| Module | Error Code | description |
| ------ | ---------- | ------------------------------- |
| wasmx | 1 | invalid gas limit |
| wasmx | 2 | invalid gas price |
| wasmx | 3 | invalid contract address |
| wasmx | 4 | contract already registered |
| wasmx | 5 | duplicate contract |
| wasmx | 6 | no contract addresses found |
| wasmx | 7 | invalid code id |
| wasmx | 8 | not possible to deduct gas fees |
| wasmx | 9 | missing granter address |
| wasmx | 10 | granter address does not exist |
| wasmx | 11 | invalid funding mode |
# Wasmx
Source: https://docs.injective.network/developers-native/injective/wasmx/index
## Abstract
The `wasmx` module handles integration of [CosmWasm](https://cosmwasm.com) smart contracts with Injective Chain.
Its main function is to provide a method for contracts to be executed in the begin blocker section of each block.
A contract may be automatically deactivated if it runs out of gas but can be reactivated by the contract owner.
It also includes helper methods for managing contracts, such as a batch code storage proposal. These functions allow for seamless integration of CosmWasm contracts with the Injective Chain and provide useful tools for managing and maintaining those contracts.
## Contents
1. [Concepts](/developers-native/injective/wasmx/01_concepts)
2. [Data](/developers-native/injective/wasmx/02_data)
3. [State](/developers-native/injective/wasmx/03_proposals)
4. [Messages](/developers-native/injective/wasmx/04_messages)
5. [Params](/developers-native/injective/wasmx/05_params)
# Injective FAQ
Source: https://docs.injective.network/faq
## Fundamentals
Q: What account address types are supported in Injective?
A: There are 2 address types supported:
* Bech32 (`inj...`), which is primarily used when interacting via Cosmos wallets/ tools
* Hexadecimal (`0x...`), which is primarily used when interacting via EVM wallets/ tools
***
Q: Is there a way to find which Injective Cosmos address is mapped to which Injective EVM address?
A: The mapping between these address types is done through a mathematical operation,
which is 1 to 1, and bidirectional.
* Live example: [Injective Testnet Faucet](https://testnet.faucet.injective.network/)
* Docs: [TS code examples](https://docs.injective.network/developers/convert-addresses)
***
## Infrastructure
Q: When maintaining a private node:
* Should we store 2.5 Ti archival data (event provider)?
* Can we skip that part and make indexer work?
A: Event provider can be pruned. One can use the public event provider endpoint for the initial sync. Then resort to local deployment, but only from the latest height. Therefore, yes can be skipped.
***
## EVM
Q: Does Injective have a deployment of the [`multicall3`](https://www.multicall3.com/) smart contract?
A: Yes.
* Injective Mainnet `multicall3`: [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://blockscout.injective.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract)
* Injective Testnet `multicall3`: [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://testnet.blockscout.injective.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract)
***
# Glossary
Source: https://docs.injective.network/glossary
Pocket size cheat sheet for Injective. Use this glossary to learn about terms specific to Injective.
### Active set
The validators that participate in consensus and receive rewards.
### Airdrops
Additional rewards given to delegators through certain validators that are separate from staking rewards. Airdrops are generally given by applications in the Injective ecosystem to increase visibility.
### Arbitrage
A process by which users seek to capitalize on a price difference across markets. Arbitrageurs typically purchase assets in one market and sell them in another market for a higher price.
### Blockchain
An unchangeable ledger of transactions copied among a network of independent computer systems.
### Blocks
Groups of information stored on a blockchain. Each block contains transactions that are grouped, verified, and signed by validators.
### Bonded validator
A validator in the active set participating in consensus. Bonded validators are able to earn rewards.
### Bonding
When a user delegates or bonds INJ to a validator to receive staking rewards. Validators never have ownership of a delegator's INJ, even when bonded. Delegating, bonding, and staking generally refer to the same process.
### Burn
The permanent destruction of assets. Injective burns INJ after each burn auction.
### Burn Auction
A weekly event in which community members can use INJ to bid for 60% of all exchange fees collected by Injective. The INJ used by the winning bidder will be burnt.
### Commission
The percentage of staking rewards a validator keeps before distributing the rest of the rewards to delegators. A validator’s income relies solely on this commission. Validators set their own commission rates.
### Community pool
A special fund designated for funding community projects. Any community member can create a governance proposal to spend the tokens in the community pool. If the proposal passes, the funds are spent as specified in the proposal.
### Consensus
A system used by validators or miners to agree that each block of transactions in a blockchain is correct. Injective uses the Tendermint consensus framework. Validators earn rewards for participating in consensus. Visit the [Tendermint official documentation site](https://docs.tendermint.com/) for more information.
### Cosmos-SDK
The open-source framework the Injective blockchain is built on. For more information, check out the [Cosmos SDK Documentation](https://docs.cosmos.network/).
### CosmWasm
The library utilized by Injective to power on-chain smart contracts. For more information, check out the CosmWasm Documentation.
### dApp
Decentralized application. An application built on a decentralized platform.
### DDoS
Distributed denial of service attack. When an attacker floods a network with traffic or requests in order to disrupt service.
### DeFi
Decentralized finance. A movement away from traditional finance and toward systems that do not require financial intermediaries.
### Delegate
When users or delegators add their INJ to a validator's stake in exchange for rewards. Delegated INJ is bonded to a validator. Validators never have ownership of a delegator's INJ. Delegating, bonding, and staking generally refer to the same process.
### Delegator
A user who delegates, bonds, or stakes INJ to a validator to earn rewards. Delegating, bonding, and staking generally refer to the same process.
### Devnet
Development network. A network that operates independently of the mainnet, allowing users to test new features or products without disrupting the primary network.
### Frequent Batch Auction (FBA)
The model used by Injective for on-chain order matching. This model uses capital more efficiently compared to Continuous Double Auction (CDA), which is utilized by most of the Centralized exchanges offering crypto derivatives and traditional financial markets. The Frequent Batch Auction model also eliminates front-running possibilities.
### Full node
A computer connected to the \[Injective mainnet that can validate transactions and interact with Injective. All active validators run full nodes.
### Gas Fees
Compute fees added on to all transactions to avoid spamming. Validators set minimum gas prices and reject transactions that have implied gas prices below this threshold.
### Governance
Governance is the democratic process that allows users and validators to make changes to Injective. Community members submit, vote, and implement proposals. One staked INJ is equal to one vote.
### Governance proposal
A written submission for a change or addition to the Injective protocol. Topics of proposals can vary from community pool spending, software changes, parameter changes, or any change pertaining to Injective.
### IBC
Inter-Blockchain Communication. The technology that enables different blockchains to interact with each other. IBC allows for assets to be traded and transacted across different blockchains.
### INJ
The native token of Injective.
### injectived
The command-line interface for interacting with an Injective node.
For more information on injectived, see `injectived` guides.
### Injective core
The official source code for Injective.
For more information on the Injective core, see Injective core modules.
### Injective Hub
Inejctive's platform for wallets, governance, staking and INJ burn auctions.
To learn about the features of Injective Hub, visit the Injective Hub guide.
### Inactive set
Validators that are not in the active set. These validators do not participate in consensus and do not earn rewards.
### Jailed
Validators who misbehave are jailed or excluded from the active set for a period of time.
### Maximum Extractable Value (MEV)
The maximum value that can be extracted from block production in excess of the standard block reward and gas fees by including, excluding, and changing the order of transactions in a block.
Injective is MEV-resistant.
### Module
A section of the Injective core that represents a particular function of Injective. Visit the Injective core module specifications for more information.
### Oracle
A 3rd party service enabling Injective to access external, real-world data. Typically, this is the price feed.
### Pools
Groups of tokens. Supply pools represent the total supply of tokens in a market.
### Proof of Stake
A validation method utilized by blockchains in which validators are chosen to propose blocks according to the number of coins they hold.
### Quorum
The minimum amount of votes needed to make an election viable. 33% of all staked INJ must vote to meet quorum. If quorum is not met before the voting period ends, the proposal fails, and the proposer's deposit is burned.
### Redelegate
When a delegator wants to transfer their bonded INJ to a different validator. Redelegating INJ is instant and does not require a 21-day unbonding period.
### Rewards
Revenue generated from fees given to validators and disbursed to delegators.
### Self-delegation
The amount of INJ a validator bonds to themselves. Also referred to as self-bond.
### Slashing
Punishment for validators that misbehave. Validators lose part of their stake when they get slashed.
### Slippage
The difference in an asset’s price between the start and end of a transaction.
### Stake
The amount of INJ bonded to a validator.
### Staking
When a user delegates or bonds their INJ to an active validator to receive rewards. Bonded INJ adds to a validator's stake. Validators provide their stakes as collateral to participate in the consensus process. Validators with larger stakes are chosen to participate more often. Validators receive staking rewards for their participation. A validator's stake can be slashed if the validator misbehaves. Validators never have ownership of a delegator's INJ, even when staking.
For more information on staking, visit the concepts page.
### Tendermint consensus
The consensus framework used by Injective. First, a validator proposes a new block. Other validators vote on the block in two rounds. If a block receives a two-thirds majority or greater of yes votes in both rounds, it gets added to the blockchain. Validators get rewarded with the block's transaction fees. Proposers get rewarded extra. Each validator is chosen to propose based on their weight. Check out the [Tendermint official documentation](https://docs.tendermint.com/) for more information.
### Mainnet
Injective's blockchain network where all transactions take place.
### Testnet
A version of the mainnet used for testing. The testnet does not use real assets. You can use the testnet to get familiar with transactions and the overall network.
### Total stake
The total amount of INJ bonded to a delegator, including self-bonded INJ.
### Unbonded validator
A validator that is not in the active set and does not participate in consensus or receive rewards. Some unbonded validators may be jailed.
### Unbonding validator
A validator transitioning from the active set to the inactive set. An unbonding validator does not participate in consensus or earn rewards. The unbonding process takes 21 days.
### Unbonded INJ
INJ that can be freely traded and is not staked to a validator.
### Unbonding
When a delegator decides to undelegate their INJ from a validator. This process takes 21 days. No rewards accrue during this period. This action cannot be stopped once executed.
### Unbonding INJ
INJ that is transitioning from bonded to unbonded. INJ that is unbonding cannot be traded freely. The unbonding process takes 21 days. No rewards accrue during this period. This action cannot be stopped once executed.
### Undelegate
When a delegator no longer wants to have their INJ bonded to a validator. This process takes 21 days. No rewards accrue during this period. This action cannot be stopped once executed.
### Uptime
The amount of time a validator is active in a given timeframe. Validators with low up time may be slashed.
### Validator
An Injective blockchain miner that is responsible for verifying transactions on the blockchain. Validators run programs called full nodes that allow them to participate in consensus, verify blocks, participate in governance, and receive rewards. Only the active set of validators can participate in consensus.
### Weight
The measure of a validator's total stake. Validators with higher weights get selected more often to propose blocks. A validator's weight is also a measure of their voting power in governance.
# References
Source: https://docs.injective.network/references
Important references and links for the Injective Ecosystem
## Developer Resources
### Developer Tools and Resources
Developer tools and resources to get you building on Injective
| **Resource** | **Description** |
| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [MCP Server](./developers/ai/mcp) | Connect AI assistants to Injective documentation using the Model Context Protocol |
| [Injective 101](https://injective.notion.site/Injective-101-589dedc4c9c04531aae503dbb235d443) | One-stop-shop for Injective resources |
| [Injectived](./developers/injectived/) | Command-line interface and node daemon that connects to Injective |
| [Injective Explorer](https://explorer.injective.network/) | Analytics platform that enables anyone to search addresses, trades, tokens, transactions, and other activities on Injective |
| [Injective Local](https://github.com/InjectiveLabs/injective-local) | Injective testnet and ecosystem containerized with Docker and orchestrated with a simple docker-compose file. |
| [Injective REST API](https://lcd.injective.network/swagger/) | Swagger API explorer |
| [Injective TypeScript SDK](https://docs.ts.injective.network) | Build dApps on Injectuve using TypeScript |
| [Injective API Reference](https://api.injective.exchange) | Detailed API documentation for interacting with Injective for traders |
| [Cosmovisor](./infra/cosmovisor/) | Small process manager around Cosmos SDK binaries that monitors the governance module |
| [CosmosSDK](https://docs.cosmos.network/main/build) | The Cosmos SDK documentation serving as a valuable resource for developers integrating with the Injective ecosystem |
### Ecosystem Tools and Resources
Tools and resources developed by ecosystem developers and partners
| **Resource** | **Description** |
| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Desig](https://desig.io/) | Multi-party computation (MPC) multisig solution |
| [DiscoverINJ](https://alpha.discoverinj.com/) | UI smart contract query / execute |
| [The Graph](https://docs.substreams.dev/tutorials/intro-to-tutorials/injective) | Substreams for extracting data from the Injective blockchain. |
| [NewMetric](https://app.newmetric.xyz/) | Custom, high-performance RPC nodes |
| [Notifi](https://injective.com/notifications/) | Receive notifications for the latest chain updates, ecosystem developments, market insights, and more via Notifi |
| [Pyth](https://docs.pyth.network/home) | Price feed oracles and market data |
| [Starship](https://docs.cosmology.zone/starship) | Unified development environment that allows developers to spin up a fully simulated mini-cosmos ecosystem and write end-to-end test cases |
| [SubQuery](https://github.com/subquery/cosmos-subql-starter/tree/main/Injective/injective-starter) | Open-source data indexer providing custom APIs |
| [Remix IDE](https://docs.welldonestudio.io/code/deploy-and-run/injective) | Smart Contract Deployment & Development APIs |
| [Synternet](https://docs.synternet.com/build/data-layer/developer-portal/subscribe-to-streams) | Subscribe or publish real-time live data stream |
| [Wormhole](https://docs.wormhole.com/wormhole) | Cross-chain messaging protocol |
| [DAO DAO](https://daodao.zone/dao/injective) | DAO DAO helps you build and operate DAOs by providing a visual interface to easily interact with the underlying smart contracts that run on the blockchain. DAO DAO also offers a multisig solution. |
### Helpful Repos
Helpful repositories for developing on and integrating Injective
| **Repo** | **Description** |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| [CosmWasm101](https://github.com/InjectiveLabs/CosmWasm101) | Guide to match the CosmWasm 101 presentation for the 2023 Injective Global Hackathon |
| [cw20-adapter](https://github.com/InjectiveLabs/cw20-adapter/tree/master/contracts/cw20-adapter) | Contract that allows exchanging CW-20 tokens for Injective-chain issued native tokens (using the TokenFactory module) and vice-versa |
| [cw-injective](https://github.com/InjectiveLabs/cw-injective) | Packages that can be used to integrate CosmWasm with Injective |
| [injective-rust](https://github.com/InjectiveLabs/injective-rust) | A place where all rust binding generators for Injective-Core live. |
| [injective-ts-examples](https://github.com/InjectiveLabs/injective-ts-examples) | Helpful repo to get started building on the injective-ts library |
| [injective-ui](https://github.com/InjectiveLabs/injective-ui) | Collection of UI packages to ease development on Injective |
| [swap-contract](https://github.com/InjectiveLabs/swap-contract) | Open source atomic token swap contract that showcasing an instant swap between two different tokens |
| [test-tube](https://github.com/injectiveLabs/test-tube) | Generic library for building testing environments for CosmWasm smart contracts |
### Developer Support
Find developer support on Discord or Telegram
1. Join the [Injective Discord server](https://discord.gg/injective) and find the relevant channel
2. Join the [Injective Developer Telegram channel](https://t.me/+qorn-J06fzA0YTZl)
### Public Endpoints
For a list of public endpoints, visit [public endpoints](./infra/public-endpoints/ "mention")
### Private / Dedicated Node Services
For a full list of private node services, see [premium endpoints](./infra/premium-endpoints/ "mention")