The x/erc20 module from cosmos/evm enables bidirectional conversion between Cosmos SDK coins and ERC20 tokens within the EVM runtime.
For conceptual understanding of Single Token Representation v2, see Single Token Representation.

Parameters

The module parameters control token conversion and registration (source):
ParameterTypeDefaultDescription
enable_erc20booltrueEnable token conversions globally
permissionless_registrationboolfalseAllow anyone to register ERC20 tokens

Parameter Details

State

The module maintains token pair mappings and allowances (source):
ObjectKeyValueDescription
TokenPair0x01 + IDTokenPairToken pair configuration
TokenPairByERC200x02 + addressIDLookup by ERC20 address
TokenPairByDenom0x03 + denomIDLookup by denomination
Allowance0x04 + hashAllowanceERC20 allowances
NativePrecompiles0x05 + addressboolNative precompile registry
DynamicPrecompiles0x06 + addressboolDynamic precompile registry

Token Pair Structure

type TokenPair struct {
    Erc20Address  string  // Hex address of ERC20 contract
    Denom         string  // Cosmos coin denomination
    Enabled       bool    // Conversion enable status
    ContractOwner Owner   // OWNER_MODULE or OWNER_EXTERNAL
}

Token Pair Registration

Registration Methods

  1. Automatic Registration (IBC Tokens)
    • IBC tokens (denoms starting with “ibc/”) are automatically registered on first receipt
    • No governance proposal or user action required
    • Creates ERC20 precompile at deterministic address
  2. Permissionless Registration (ERC20 Contracts)
    • When permissionless_registration parameter is true
    • Any user can register existing ERC20 contracts via MsgRegisterERC20
    • Useful for integrating existing ERC20 tokens
  3. Governance Registration
    • Always available regardless of parameter settings
    • Can register any ERC20 contract or create new token pairs
    • Required when permissionless_registration is false

Messages

MsgRegisterERC20

Register existing ERC20 contracts for conversion (source):
message MsgRegisterERC20 {
    string signer = 1;
    repeated string erc20addresses = 2;
}
Requirements:
  • permissionless_registration enabled OR sender is governance authority
  • Valid ERC20 contract at address
  • Contract not already registered
  • Contract implements standard ERC20 interface

MsgConvertCoin

Convert Cosmos coins to ERC20 tokens:
message MsgConvertCoin {
    Coin coin = 1;      // Amount and denom to convert
    string receiver = 2; // Hex address to receive ERC20
    string sender = 3;   // Bech32 address of sender
}
Validation:
  • Token pair exists and enabled
  • Sender has sufficient balance
  • Valid receiver address

MsgConvertERC20

Convert ERC20 tokens to Cosmos coins:
message MsgConvertERC20 {
    string contract_address = 1;  // ERC20 contract
    string amount = 2;            // Amount to convert
    string receiver = 3;          // Bech32 address
    string sender = 4;            // Hex address of sender
}
Validation:
  • Token pair exists and enabled
  • Sender has sufficient ERC20 balance
  • Valid receiver address

MsgToggleConversion

Enable/disable conversions for a token pair (governance only):
message MsgToggleConversion {
    string authority = 1;  // Must be governance account
    string token = 2;      // Address or denom
}

MsgUpdateParams

Update module parameters (governance only):
message MsgUpdateParams {
    string authority = 1;  // Must be governance account
    Params params = 2;     // New parameters
}

Conversion Flows

Native Coin → ERC20

Steps:
  1. Validate token pair enabled
  2. Transfer coins to module account
  3. Mint equivalent ERC20 to receiver
  4. Emit conversion event

ERC20 → Native Coin

Steps:
  1. Validate token pair enabled
  2. For module-owned: burn ERC20
  3. For external: transfer to module
  4. Release native coins from escrow
  5. Emit conversion event

Precompile System

Native Precompiles

Automatically created for Cosmos coins at deterministic addresses:
// Address derivation
address = keccak256("erc20|<denom>")[:20]
Interface:
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

Dynamic Precompiles (WERC20)

Optional wrapped interface for registered tokens:
interface IWERC20 is IERC20 {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

IBC Integration

IBC Middleware v1

Standard IBC transfer integration (source):
// Automatic registration and conversion on receive
OnRecvPacket(packet) {
    // Auto-register new IBC tokens (with "ibc/" prefix)
    if !tokenPairExists && hasPrefix(denom, "ibc/") {
        RegisterERC20Extension(denom)
    }
    
    // Auto-convert to ERC20 for Ethereum addresses
    if isEthereumAddress(receiver) {
        convertToERC20(voucher, receiver)
    }
}

// Automatic conversion on acknowledgment
OnAcknowledgementPacket(packet, ack) {
    if wasConverted {
        convertBackToCosmos(refund)
    }
}
Automatic Registration:
  • IBC tokens (denoms starting with “ibc/”) are automatically registered on first receipt
  • Factory tokens (denoms starting with “factory/”) are skipped
  • Native chain tokens require explicit registration

IBC Middleware v2

Enhanced IBC v2 support (source):
  • New packet format support
  • Improved error handling
  • Multi-hop awareness
  • Backward compatibility

Events

Registration Events

EventAttributesWhen Emitted
register_erc20erc20_address, cosmos_coinToken pair registered
toggle_token_conversionerc20_address, cosmos_coinConversion toggled

Conversion Events

EventAttributesWhen Emitted
convert_coinsender, receiver, amount, cosmos_coin, erc20_tokenCoin → ERC20
convert_erc20sender, receiver, amount, cosmos_coin, erc20_tokenERC20 → Coin

IBC Events

EventAttributesWhen Emitted
ibc_transfer_conversionsender, receiver, denom, amountAuto-conversion during IBC

Queries

gRPC

service Query {
    // Get module parameters
    rpc Params(QueryParamsRequest) returns (QueryParamsResponse);
    
    // Get all token pairs
    rpc TokenPairs(QueryTokenPairsRequest) returns (QueryTokenPairsResponse);
    
    // Get specific token pair
    rpc TokenPair(QueryTokenPairRequest) returns (QueryTokenPairResponse);
}

CLI

# Query module parameters
evmd query erc20 params

Integration Examples

DeFi Protocol Integration

// Using precompiled ERC20 interface for IBC tokens
const ibcToken = new ethers.Contract(
    "0x80b5a32e4f032b2a058b4f29ec95eefeeb87adcd", // Precompile address
    ERC20_ABI,
    signer
);

// Standard ERC20 operations work seamlessly
await ibcToken.approve(dexRouter, amount);
await dexRouter.swapExactTokensForTokens(...);

Automatic IBC Conversion

// IBC transfer automatically converts to ERC20
const packet = {
    sender: "cosmos1...",
    receiver: "0x...",  // EVM address triggers conversion
    token: { denom: "uatom", amount: "1000000" }
};
// Token arrives as ERC20 in receiver's EVM account

Manual Conversion Flow

// Convert native to ERC20
await client.signAndBroadcast(address, [
    {
        typeUrl: "/cosmos.evm.erc20.v1.MsgConvertCoin",
        value: {
            coin: { denom: "uatom", amount: "1000000" },
            receiver: "0xEthereumAddress",
            sender: "cosmos1..."
        }
    }
]);

Best Practices

Chain Integration

  1. Token Registration Review
    • Audit contracts before registration
    • Verify standard compliance
    • Check for malicious behavior
  2. Precompile Configuration
    • Enable precompiles for frequently used tokens
    • Monitor gas consumption
    • Set appropriate gas costs
  3. IBC Setup
    • Configure middleware stack correctly
    • Test auto-conversion flows
    • Monitor conversion events

Security Considerations

  1. Contract Validation
    // Verify standard interface
    IERC20(token).totalSupply();
    IERC20(token).balanceOf(address(this));
    
  2. Event Monitoring
    • Track conversion events
    • Monitor for unusual patterns
    • Alert on large conversions
  3. Emergency Response
    • Disable conversions via governance
    • Toggle specific token pairs
    • Have incident response plan

Troubleshooting

Common Issues

IssueCauseSolution
”token pair not found”Token not registeredRegister via governance
”token pair disabled”Conversions toggled offEnable via governance
”insufficient balance”Low balance for conversionCheck balance in correct format
”invalid recipient”Wrong address formatUse hex for EVM, bech32 for Cosmos
”module disabled”enable_erc20 = falseEnable via governance

Debug Commands

# Check if token is registered
evmd query erc20 token-pair uatom

# Check module parameters
evmd query erc20 params

# Check account balance (both formats)
evmd query bank balances cosmos1...
evmd query vm balance 0x...

References

Source Code