默认合约
Cosmos EVM 包含五个默认 preinstall(x/vm/types/preinstall.go):
| 合约 | 地址 | 用途 | 文档 |
|---|---|---|---|
| Create2 | 0x4e59b44847b379578588920ca78fbf26c0b4956c | 使用 CREATE2 进行确定性合约部署 | EIP-1014 |
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 | 在一笔交易中批量执行多个合约调用 | Repo · Site |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | 为任意 ERC20 提供基于签名的代币授权 | Repo · Docs |
| Safe Singleton Factory | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 | 在确定性地址部署 Safe 多签钱包 | Repo · Docs |
| EIP-2935 | 0x0000F90827F1C53a10cb7A02335B175320002935 | 历史区块哈希存储 | EIP-2935 |
在创世时启用
Preinstall 通过genesis.json 中的 app_state.evm.preinstalls 数组进行设置。如何填充该数组,取决于你为链生成创世文件的方式。
-
使用
local_node.sh:evmd init不会自动填充 preinstalls。你需要更新local_node.sh,在节点启动前将默认 preinstalls 写入genesis.json。每个条目都需要提供合约名称、地址和编译后的字节码:字节码定义见local_node.shx/vm/types/preinstall.go。 -
使用程序化创世生成(例如
evmd testnet):Preinstall 在evmd/genesis.go的NewEVMGenesisState()中设置。参考链已经在这里设置了evmGenState.Preinstalls = evmtypes.DefaultPreinstalls,因此会自动包含全部五个默认项。 -
自定义创世生成:直接在
genesis.json的app_state.evm下包含preinstalls数组,并为每个条目指定name、address和十六进制编码的code。示例:genesis.json
添加自定义合约
- 如果要部署默认项之外的合约,请打开
evmd/genesis.go并更新NewEVMGenesisState。下面的示例使用一个最小的 10 字节合约(0x600160005260206000f3),它会在任意调用时返回1。请将名称、地址和代码替换为你自己的值:
evmd/genesis.go
- 在
local_node.sh中最后一条创世自定义语句(.consensus.params.block.max_gas)之后、# Change proposal periods注释之前,添加一段jqpatch。使用+=追加内容,这样不会覆盖之前已设置的任何 preinstall:
local_node.sh
- 有效的 Ethereum 地址(
0x前缀,40 个十六进制字符) - 不能与现有合约或预编译地址(
0x1–0x9FF)冲突 - 非空且有效的 EVM 字节码(十六进制编码)
- 重新构建二进制并启动链:
-y 标志会清除现有链数据并从创世重新初始化,这是让 preinstall 生效所必需的。
- 链运行后,打开一个新终端,并运行以下命令确认合约已安装:
code 的预期输出:
0x600160005260206000f3,这正是 genesis.go 和 local_node.sh 中设置的字节码。非空值说明代码已写入状态。
account 的预期输出:
code_hash:非空哈希说明该地址上存在合约balance: "0":preinstall 部署时不带原生代币余额,这是预期行为nonce: "0":preinstall 不是通过交易部署的,因此 nonce 从 0 开始
在链启动后添加合约
以下方法展示了在链已经运行后部署预部署合约的几种方式。通过治理提案部署
使用MsgRegisterPreinstalls 通过治理在运行中的链上部署合约:
proposal.json
通过链升级处理器部署
在协调的链升级中包含 preinstall:app/upgrades/v2/upgrades.go
Predeployed contracts (also called preinstalls) are EVM contracts that exist in chain state at a specific address from genesis. Because the address is fixed and known in advance, the same contract can exist at the same address across every chain that includes it, making them useful for infrastructure that needs to be reliably reachable everywhere.
Default contracts
Cosmos EVM includes five default preinstalls (x/vm/types/preinstall.go):
| Contract | Address | Purpose | Docs |
|---|---|---|---|
| Create2 | 0x4e59b44847b379578588920ca78fbf26c0b4956c | Deterministic contract deployment using CREATE2 | EIP-1014 |
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 | Batch multiple contract calls in one transaction | Repo · Site |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Signature-based token approvals for any ERC20 | Repo · Docs |
| Safe Singleton Factory | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 | Deploy Safe multisig wallets at deterministic addresses | Repo · Docs |
| EIP-2935 | 0x0000F90827F1C53a10cb7A02335B175320002935 | Historical block hash storage | EIP-2935 |
Enabling at genesis
Preinstalls are set in theapp_state.evm.preinstalls array of genesis.json. How you populate that array depends on how you generate genesis for your chain.
-
Using
local_node.sh:evmd initdoes not populate preinstalls automatically. you will need to updatelocal_node.shto write the default preinstalls intogenesis.jsonbefore the node starts. Each entry requires the contract name, address, and compiled bytecode:Bytecodes are defined inlocal_node.shx/vm/types/preinstall.go. -
Using programmatic genesis (e.g.,
evmd testnet): Preinstalls are set inNewEVMGenesisState()inevmd/genesis.go. The reference chain already setsevmGenState.Preinstalls = evmtypes.DefaultPreinstallsthere, so all five defaults are included automatically. -
Custom genesis generation: Include the
preinstallsarray directly in yourgenesis.jsonunderapp_state.evm, with each entry specifyingname,address, and hex-encodedcode. Example:genesis.json
Add custom contracts
- To deploy a contract beyond the defaults, open
evmd/genesis.goand updateNewEVMGenesisState. The example below uses a minimal 10-byte contract (0x600160005260206000f3) that returns1on any call — replace the name, address, and code with your own:
evmd/genesis.go
- Add a
jqpatch tolocal_node.shafter the last genesis customization line (.consensus.params.block.max_gas), before the# Change proposal periodscomment. Use+=to append without overwriting any previously set preinstalls:
local_node.sh
- Valid Ethereum address (0x prefix, 40 hex characters)
- Must not conflict with existing contracts or precompile addresses (0x1–0x9FF)
- Non-empty, valid EVM bytecode (hex encoded)
- Rebuild the binary and start the chain:
-y flag wipes any existing chain data and reinitializes from genesis, which is required for the preinstall to take effect.
- Once the chain is running, open a new terminal and confirm the contract is installed by running the following commands:
code:
0x600160005260206000f3, which is the bytecode set in genesis.go and local_node.sh. A non-empty value confirms the code was written to state.
Expected output for account:
code_hash: a non-empty hash confirms the contract exists at this addressbalance: "0": preinstalls are deployed with no native token balance, which is expectednonce: "0": preinstalls are not deployed via a transaction, so the nonce starts at 0
Add contracts after launch
The following methods are examples of ways to deploy predeployed contracts after the chain is running.Deploy via governance proposal
UseMsgRegisterPreinstalls to deploy contracts on a running chain via governance:
proposal.json
Deploy via chain upgrade handler
Include preinstalls in a coordinated chain upgrade:app/upgrades/v2/upgrades.go