ERC20 模块(x/erc20)实现了单一代币表示 v2(STRv2),确保 Cosmos coin 与 EVM ERC-20 代币之间的无缝互操作。它会自动为 IBC 代币创建 ERC-20 表示,并维护双向转换。
模块概览
用途:在 Cosmos 与 EVM 环境中提供统一的代币表示 核心功能:- 为 Cosmos coin 和 IBC 代币自动创建 ERC-20 包装
- 代币双向转换(Cosmos 与 EVM)
- 在确定性地址上提供原生代币预编译
- 为 IBC 代币动态生成预编译
- 跟踪 ERC-20 allowance,以实现跨环境兼容
- 单一代币表示可防止流动性碎片化
单一代币表示 v2(STRv2)
解决的问题
如果没有 STRv2,代币会存在于彼此分离的“世界”中:- Cosmos 侧:Bank 模块跟踪
ibc/ABC...面额 - EVM 侧:独立的 ERC-20 合约,具有不同地址
- 结果:代币碎片化、流动性割裂、账务处理复杂
STRv2 解决方案
STRv2 确保代币只有一种表示:- IBC 代币到达 → 自动在确定性地址获得 ERC-20 包装
- 用户需要 EVM 访问 → 将 bank 余额转换为 ERC-20(总供应量保持不变)
- 用户需要 Cosmos 访问 → 将 ERC-20 转回 bank 余额
- 结果:同一种代币,可在两个环境中访问,流动性统一
配置方式
ERC20 模块在链启动前通过 genesis.json 进行配置。方法 1:直接编辑 JSON
直接编辑~/.evmd/config/genesis.json:
方法 2:使用 jq 命令行工具
来自 local_node.sh:247-248:参数
enable_erc20
作用:启用或禁用整个 ERC20 模块功能的总开关。 类型:bool
有效值:
true- 启用 ERC-20 转换和 token pair(推荐)false- 禁用所有 ERC-20 功能
true(params.go:26)
配置:
- 当为
true时:用户可以在 Cosmos 表示与 ERC-20 表示之间转换 - 当为
false时:所有转换都会被禁用,代币保持原生格式 - genesis 之后不能直接修改,必须通过治理提案
true- 兼容 EVM 的链的标准配置false- 不提供 EVM 代币桥接的纯 Cosmos 链
permissionless_registration
作用:控制谁可以注册新的 token pair(为 Cosmos 代币创建 ERC-20 包装)。 类型:bool
有效值:
true- 任何人都可以注册 token pair(推荐用于公链)false- 只有治理可以注册 token pair(许可式)
true(params.go:27)
配置:
true(无许可)时:
- 任意用户都可以调用
RegisterCoin或RegisterERC20消息 - IBC 代币会自动获得 ERC-20 包装
- 对新代币提供最快的接入体验
- 符合标准的类以太坊行为
false(许可式)时:
- 只有治理提案可以创建 token pair
- 可以更严格地控制哪些代币拥有 ERC-20 表示
- 新代币接入速度更慢
- 适合精心筛选的代币列表
true,以匹配以太坊式用户体验
Genesis 状态
token_pairs
作用:定义 Cosmos 面额与 ERC-20 合约地址之间的映射关系。 类型:TokenPair 对象数组
结构:(token_pair.go:32-39)
OWNER_MODULE = 1- 由 erc20 模块持有(原生 Cosmos 代币)OWNER_EXTERNAL = 2- 由外部 EOA 持有(原生 ERC-20 代币)
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE - 原生代币的标准地址(遵循以太坊惯例)
校验:(genesis.go:30-46)
- 不允许重复的 ERC-20 地址
- 不允许重复的面额
- Cosmos denom 格式必须合法
- Ethereum 地址格式必须合法
native_precompiles
作用:列出应作为有状态预编译暴露的 ERC-20 合约地址,用于原生 Cosmos 代币。 类型:十六进制地址数组(字符串) 目的:让 EVM 智能合约能够在确定性地址上与原生 Cosmos 代币交互 配置:- 每个预编译地址都必须有一个对应且已启用的 token pair(genesis.go:54-56)
- 预编译提供由 bank 模块支持的 ERC-20 接口
IERC20(0xEeee...).transfer()
dynamic_precompiles
作用:与 native_precompiles 类似,但用于在 genesis 之后动态到达的 IBC 代币。 类型:十六进制地址数组(字符串) 默认值:[](genesis 时为空,在 IBC 转账过程中自动填充)
配置:
- IBC 代币通过
ibc/transfer模块到达 - ERC20 模块自动使用确定性地址创建 token pair
- 地址由 IBC denom 哈希派生(token_pair.go:18-29)
- 动态预编译地址被加入列表
- EVM 合约现在即可与该 IBC 代币交互
utils.GetIBCDenomAddress(denom) 根据 IBC denom 计算确定性地址
示例:
- IBC denom:
ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2 - 派生得到的 ERC-20 地址:
0x...(由 denom 哈希确定性生成)
allowances
作用:存储在 Cosmos 与 EVM 转换过程中需要保留的 ERC-20 allowance(approve/transferFrom)。 类型:Allowance 对象数组
结构:
[](genesis 时为空)
配置:
- 不允许重复的 allowance
- allowance 必须引用已存在的 token pair
- 地址和金额必须合法
完整配置示例
基于 local_node.sh:247-248:运行时操作
查询 Token Pair
转换代币
注册新的 Token Pair
注册 ERC-20 代币(如果 permissionless_registration=true)
evmd tx erc20 register-erc20 0xTokenAddress —from mykey —chain-id mychain-1在 Solidity 中使用 IBC 代币
常见问题与解决方案
问题:找不到代币对
现象:error: token pair not found for denom X
原因:该 Cosmos 代币不存在 ERC-20 包装器
解决方案:
问题:转换被禁用
现象:error: token pair is not enabled
原因:代币对已存在,但 enabled=false
解决方案:提交治理提案以启用该代币对
问题:无法访问预编译合约
现象:对预编译地址的 EVM 调用失败或返回空代码 原因:该地址未包含在native_precompiles 或 dynamic_precompiles 中
解决方案:
- 对于原生代币:在 genesis 中添加到
native_precompiles - 对于 IBC 代币:确保代币对已存在且已启用
问题:IBC 代币没有 ERC-20 地址
现象:IBC 代币已到账,但未创建 ERC-20 包装器 原因:enable_erc20=false 或模块未被正确初始化
解决方案:
- 检查
enable_erc20参数是否为true - 检查模块是否已在 app.go 以及 BeginBlockers/EndBlockers 中注册
相关文档
源代码参考
- 模块实现: x/erc20
- 参数类型: x/erc20/types/params.go
- Genesis 状态: x/erc20/types/genesis.go
- 代币对逻辑: x/erc20/types/token_pair.go
- IBC 回调: x/erc20/keeper/ibc_callbacks.go
- 预编译管理: x/erc20/keeper/precompiles.go
- Genesis 设置: local_node.sh:247-248
- ERC-20 字节码: x/erc20/types/constants.go:8
The ERC20 module (
x/erc20) implements Single Token Representation v2 (STRv2), ensuring seamless interoperability between Cosmos coins and EVM ERC-20 tokens. It automatically creates ERC-20 representations for IBC tokens and maintains bidirectional conversion.
Module Overview
Purpose: Provide unified token representation across Cosmos and EVM environments Key Functionality:- Automatic ERC-20 wrapper creation for Cosmos coins and IBC tokens
- Bidirectional token conversion (Cosmos EVM)
- Native token precompiles at deterministic addresses
- Dynamic precompile generation for IBC tokens
- ERC-20 allowance tracking for cross-environment compatibility
- Single token representation prevents fragmentation
Single Token Representation v2 (STRv2)
Problem Solved
Without STRv2, tokens exist in separate “worlds”:- Cosmos side: Bank module tracks
ibc/ABC...denominations - EVM side: Separate ERC-20 contracts with different addresses
- Result: Token fragmentation, broken liquidity, complex accounting
STRv2 Solution
STRv2 ensures a single token representation:- IBC token arrives → Automatically gets ERC-20 wrapper at deterministic address
- User wants EVM access → Convert bank balance to ERC-20 (same total supply)
- User wants Cosmos access → Convert ERC-20 back to bank balance
- Result: Same token, accessible from both environments, unified liquidity
Configuration Methods
The ERC20 module is configured through genesis.json before chain launch.Method 1: Direct JSON Editing
Edit~/.evmd/config/genesis.json directly:
Method 2: Using jq Command-Line Tool
From local_node.sh:247-248:Parameters
enable_erc20
What It Does: Master switch to enable/disable the entire ERC20 module functionality. Type:bool
Valid Values:
true- Enable ERC-20 conversions and token pairs (recommended)false- Disable all ERC-20 functionality
true (params.go:26)
Configuration:
- When
true: Users can convert between Cosmos and ERC-20 representations - When
false: All conversions disabled, tokens stay in their native format - Cannot be changed after genesis without governance proposal
true- Standard for EVM-compatible chainsfalse- Pure Cosmos chain without EVM token bridging
permissionless_registration
What It Does: Controls who can register new token pairs (create ERC-20 wrappers for Cosmos tokens). Type:bool
Valid Values:
true- Anyone can register token pairs (recommended for public chains)false- Only governance can register token pairs (permissioned)
true (params.go:27)
Configuration:
true (permissionless):
- Any user can call
RegisterCoinorRegisterERC20messages - IBC tokens automatically get ERC-20 wrappers
- Fastest UX for new tokens
- Standard Ethereum-like behavior
false (permissioned):
- Only governance proposals can create token pairs
- More control over which tokens get ERC-20 representations
- Slower onboarding for new tokens
- Useful for curated token lists
true for public chains to match Ethereum UX
Genesis State
token_pairs
What It Does: Defines the mappings between Cosmos denominations and ERC-20 contract addresses. Type: Array ofTokenPair objects
Structure: (token_pair.go:32-39)
OWNER_MODULE = 1- Owned by erc20 module (native Cosmos token)OWNER_EXTERNAL = 2- Owned by external EOA (native ERC-20 token)
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE - Standard address for native token (matches Ethereum convention)
Validation: (genesis.go:30-46)
- No duplicate ERC-20 addresses
- No duplicate denominations
- Valid Cosmos denom format
- Valid Ethereum address format
native_precompiles
What It Does: List of ERC-20 contract addresses that should be accessible as stateful precompiles for native Cosmos tokens. Type: Array of hex addresses (strings) Purpose: Enable EVM smart contracts to interact with native Cosmos tokens at deterministic addresses Configuration:- Each precompile address MUST have a corresponding enabled token pair (genesis.go:54-56)
- Precompile provides ERC-20 interface backed by bank module
IERC20(0xEeee...).transfer() for native token
dynamic_precompiles
What It Does: Similar to native_precompiles but for IBC tokens that arrive dynamically after genesis. Type: Array of hex addresses (strings) Default:[] (empty at genesis, populated automatically during IBC transfers)
Configuration:
- IBC token arrives via
ibc/transfermodule - ERC20 module automatically creates token pair with deterministic address
- Address is derived from IBC denom hash (token_pair.go:18-29)
- Dynamic precompile address added to list
- EVM contracts can now interact with IBC token
utils.GetIBCDenomAddress(denom) to compute deterministic address from IBC denom
Example:
- IBC denom:
ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2 - Derived ERC-20 address:
0x...(deterministic from denom hash)
allowances
What It Does: Stores ERC-20 allowances (approve/transferFrom) that need to persist across Cosmos-EVM conversions. Type: Array ofAllowance objects
Structure:
[] (empty at genesis)
Configuration:
- No duplicate allowances
- Allowance must reference existing token pair
- Valid addresses and amounts
Complete Configuration Example
Based on local_node.sh:247-248:Runtime Operations
Query Token Pairs
Convert Tokens
Register New Token Pair
EVM Integration
Using Native Token in Solidity
Using IBC Token in Solidity
Common Issues and Solutions
Issue: Token Pair Not Found
Symptom:error: token pair not found for denom X
Cause: No ERC-20 wrapper exists for the Cosmos token
Solution:
Issue: Conversion Disabled
Symptom:error: token pair is not enabled
Cause: Token pair exists but enabled=false
Solution: Submit governance proposal to enable token pair
Issue: Precompile Not Accessible
Symptom: EVM calls to precompile address fail or return no code Cause: Address not innative_precompiles or dynamic_precompiles
Solution:
- For native tokens: Add to
native_precompilesin genesis - For IBC tokens: Ensure token pair exists and is enabled
Issue: IBC Token No ERC-20 Address
Symptom: IBC token arrives but no ERC-20 wrapper created Cause:enable_erc20=false or module not properly initialized
Solution:
- Check
enable_erc20parameter istrue - Check module is in app.go and BeginBlockers/EndBlockers
Related Documentation
- Building Your Chain Guide - Main configuration walkthrough
- VM Module - EVM configuration
- IBC Module - IBC token handling
Source Code References
- Module Implementation: x/erc20
- Parameter Types: x/erc20/types/params.go
- Genesis State: x/erc20/types/genesis.go
- Token Pair Logic: x/erc20/types/token_pair.go
- IBC Callbacks: x/erc20/keeper/ibc_callbacks.go
- Precompile Management: x/erc20/keeper/precompiles.go
- Genesis Setup: local_node.sh:247-248
- ERC-20 Bytecode: x/erc20/types/constants.go:8