Skip to main content

ERC20 Module

The ERC20 module lets existing bank denoms (IBC bridged tokens, USDC, TokenFactory and Peggy denoms) be used from the Injective EVM. It maintains a mapping between token pairs in its storage, associating an ERC20 contract address with its bank denom. The Bank precompile reads that mapping to resolve which denom an ERC20 call operates on. The module serves two purposes:
  1. Storage: maps bank denom ↔ ERC20 address
  2. Message type: lets users establish new token pair mappings by issuing a chain message
Tokens deployed directly on the Injective EVM against the Bank precompile do not need a token pair. Their denom is derived from the contract address as erc20:0x.... This page covers the pairing path for denoms that already exist on the native side. For the conceptual overview of both, see MultiVM Token Standard.

Creating a Token Pair

Three types of bank denom can have an associated token pair, each with different rules: Peggy and IBC pairing is permissionless, so the template is forced. That combination is deliberate: anyone can make a bridged token usable in the EVM, and nobody can attach their own contract to a denom they do not control. The pair creator becomes the owner of a deployed MintBurnBankERC20. On a governance created pair, that owner is the governance module address.

Prerequisite: the denom must have supply

Pair creation requires the bank denom to already exist with nonzero supply. Submitting before the first mint fails with ErrUnknownBankDenom. For a TokenFactory launch, submit MsgCreateDenom, MsgMint and MsgCreateTokenPair as a single atomic transaction so the three cannot diverge.

Permissions namespaces force the fixed supply template

If the TokenFactory denom has a permissions namespace whose policy disables MINT, BURN or SUPER_BURN, then:
  • the chain deploys FixedSupplyBankERC20 instead of MintBurnBankERC20, and
  • any custom ERC20 address in the message is rejected with ErrInvalidERC20Address.
This is an OR across all three actions. Disabling only BURN still produces FixedSupplyBankERC20, which exposes no mint entrypoint at all.
This is driven by the namespace policy status for an action, not by role permissions. A namespace created without explicit policy statuses is backfilled with permissive defaults for every action, so defining roles alone does not force the fixed-supply template. Only an explicitly disabled policy does.

What Pair Creation Validates

When an ERC20 address is supplied, the module checks that:
  1. no token pair already exists for either the bank denom or the ERC20 address,
  2. the account at that address exists and holds contract code,
  3. the derived erc20:0x... denom has neither circulating supply nor bank metadata, otherwise ErrExistingERC20DenomSupply,
  4. the contract answers a symbol() call under a 300,000 gas cap, otherwise ErrInvalidERC20Address.
Passing validation does not mean the contract is MTS compatible. The symbol() probe only establishes that something ERC20 shaped lives at the address. A stock OpenZeppelin ERC20 passes every check above while keeping its balances in its own EVM storage, completely detached from the bank module. The result is a pair whose two sides disagree.Recovering from that requires a governance MsgDeleteTokenPair to remove the mapping, followed by a fresh registration against a compatible contract. Deleting the mapping does not reconcile any balances that already diverged. Verify the implementation before you pair, not after.
Despite its name, ErrExistingERC20DenomSupply fires on supply or metadata. A contract that wrote metadata in its constructor is rejected even with zero supply.

Custom Contracts for TokenFactory Denoms

A custom contract must route balance, supply, transfer, mint and burn through the Bank precompile. The practical way to guarantee that is to extend BankERC20 from Injective’s Solidity contracts, which implements exactly that delegation.

Deploy with empty metadata

BankERC20’s constructor calls bank.setMetadata whenever name or symbol is not empty, or decimals is nonzero. For an unpaired contract the precompile resolves the denom to erc20:0x<contract>, so that call writes metadata for a denom you do not want, and pair creation then fails with ErrExistingERC20DenomSupply. Deploy with an empty name, an empty symbol and zero decimals. This is an enforced requirement for the pairing path, not a style preference.
BankERC20 is abstract and cannot be deployed on its own. After pairing, name(), symbol() and decimals() resolve through the precompile to the factory denom’s bank metadata, which the denom admin controls with MsgSetDenomMetadata. Do not mint an independent erc20:0x... supply before pairing, for the same reason.
This is the opposite of a direct EVM deployment. There, setting metadata in the constructor and minting an erc20:0x... supply is the intended path, because the contract’s own derived denom is the token. See MultiVM Token Standard.

Mint and Burn Authority

ERC20 ownership and native TokenFactory authority are separate checks, and satisfying one does not satisfy the other. This is the most common surprise on this path. MintBurnBankERC20.mint() is onlyOwner, so the EVM caller must be the contract owner. The call then reaches the Bank precompile, which submits a native MsgMint with the ERC20 contract address as the sender, not the EVM caller. The native check therefore applies to the contract address: Burns are stricter. An ERC20 burn() or burnFrom() reaches the chain as MsgBurn with the contract as sender and the holder as the burn-from address, which makes it a burn-from rather than a self-burn:
allow_admin_burn defaults to false on injectived tx tokenfactory create-denom and can only be set at creation. It is a one way latch: once false or disabled it can never be re-enabled. A denom created without --allow-admin-burn can never burn through its paired ERC20. Decide this before you create the denom.
Changing the denom admin with MsgChangeAdmin does not change the ERC20’s owner(), and transferring ERC20 ownership does not change the denom admin. If you rotate one, rotate the other.

Authorizing the contract

To mint through the paired ERC20, the contract’s address must be authorized on the native side. Convert the EVM address to its bech32 form first, since the two are the same account in different encodings:
Then either transfer the denom admin to TOKEN_INJ with MsgChangeAdmin, or grant it a role carrying MINT in the permissions namespace. Transferring the admin gives the contract sole mint authority, so keep the ERC20 owner key safe: it becomes the only way to mint.

Metadata Authority by Origin

Peggy and IBC denoms arrive with no name, symbol or decimals, and no account on Injective has authority to set them directly. Plan the governance proposal as part of the launch rather than as cleanup after wallets already show a blank token.
Decimals are effectively write once. Once bank metadata records a nonzero decimals, changing it fails with cannot update denom metadata decimals. Get the value right on the first write.

Worked Example: Pairing a Custom Contract

This pairs a custom contract with an existing TokenFactory denom on testnet.
Deploy the contract with empty metadata, then pair it:
Omit --erc20 to let the chain deploy MintBurnBankERC20 instead. Verify that both views agree:
An EVM mint before the contract address is authorized on the native side reverts with failed to mint: unauthorized account. BankERC20 catches the precompile revert and prefixes it, so the underlying cause is the TokenFactory authorization check, not the ERC20 onlyOwner modifier. Authorize the contract as described above, then retry.

Gas

MsgCreateTokenPair gives template deployment a 2,000,000 EVM gas cap. That is the contract deployment budget, not the transaction total, which also covers ante handling and any other messages in the transaction. Use at least --gas 3000000 for a standalone pair creation. Below roughly 2.1M the transaction fails with a gas computation error. Simulate with POST /cosmos/tx/v1beta1/simulate and apply headroom rather than relying on a fixed number across releases.

Deleting a Token Pair

MsgDeleteTokenPair is restricted to governance. It removes the mapping only. It does not migrate, reconcile or burn balances on either side.

Reference

MultiVM Token Standard

Concepts, the four token origins, and which path applies to your token.

Bank Precompile

Interface, denom resolution, and the semantics behind each method.

ERC20 Module Messages

Native message and state specification.

TokenFactory Messages

Denom creation, mint, burn and admin semantics.
Last modified on October 26, 2026