启用预编译合约
预编译合约通过vm 模块中的 active_static_precompiles 参数启用。只有这里列出的地址才能在运行时被调用。有关所有内置预编译合约及其地址的完整列表,请参阅预编译合约概览。
- 在
app.go中使用.WithStaticPrecompiles()将预编译合约接入 EVM keeper。标准做法是传入precompiletypes.DefaultStaticPrecompiles(...),它包含所有内置预编译合约:
evmd/app.go
DefaultStaticPrecompiles 替换为你自己的构建链(见下文的添加自定义预编译合约)。
- 在你的创世配置中设置启用的预编译合约(
evmd/genesis.go):
x/vm/types/precompiles.go 中。更多信息请参阅预编译合约概览。
已经注册的预编译合约也可以在链上线后,通过面向 vm 模块 active_static_precompiles 参数的治理参数变更提案来启用或禁用。若要新增真正全新的自定义预编译合约,则必须执行链升级,因为其实现位于 Go 二进制中。
添加自定义预编译合约
下面的示例添加了一个有状态的DenomSupply 预编译合约,它只包含一个 supplyOf 方法,用于直接从 Cosmos bank 模块读取代币总供应量。这个示例展示了基础预编译合约的核心模式:注入一个 Cosmos SDK keeper,并使用 RunNativeAction 在 EVM 调用中访问链上的实时状态。
1. 创建预编译合约包
创建目录precompiles/denomsupply/,并在其中添加两个文件:
precompiles/denomsupply/abi.json:Solidity ABI:
precompiles/denomsupply/abi.json
precompiles/denomsupply/denomsupply.go:实现代码:
precompiles/denomsupply/denomsupply.go
cmn.Precompile 来工作,而不是直接自行处理上下文。RunNativeAction 会负责设置 SDK 上下文、管理 gas 计量,并处理快照与回滚,从而确保预编译合约调用能够正确参与 EVM 交易原子性。在闭包内部,p.bankKeeper 用于访问 bank 模块状态。
2. 注册地址
在x/vm/types/precompiles.go 中,在常量块结束的 ) 之前添加一个常量(第 17 行),并在 AvailableStaticPrecompiles 结束的 } 之前将其追加进去(第 35 行,会因插入常量而整体下移 1 行)。地址必须按排序顺序排列。
添加常量(插入到第 17 行结束的 ) 之前):
x/vm/types/precompiles.go
AvailableStaticPrecompiles 切片中(插入到第 35 行结束的 } 之前,会因前一次插入而整体下移 1 行):
x/vm/types/precompiles.go
3. 添加构建方法
在precompiles/types/static_precompiles.go 中,为新包添加 import(插入到第 16 行 ics02precompile 之前,位于 govprecompile 与 ics02precompile 之间):
precompiles/types/static_precompiles.go
With 方法。将你的预编译合约所需的 keeper 作为参数传入:
precompiles/types/static_precompiles.go
4. 接入应用
在precompiles/types/defaults.go 中,把你的方法添加到构建链里,方式是替换第 89 行的 WithSlashingPrecompile。bankKeeper 已经是 DefaultStaticPrecompiles 的参数之一:
precompiles/types/defaults.go
5. 在创世阶段启用
由于evmd/genesis.go 已经使用了 evmtypes.AvailableStaticPrecompiles,因此只要在步骤 2 中把你的地址加入该切片即可,无需修改 genesis.go。
如果你在本地开发中使用 local_node.sh,该脚本会通过 jq 命令硬编码预编译合约列表,而不会在运行时读取 AvailableStaticPrecompiles。请在 local_node.sh 第 244 行之前(即第 243 行 active_static_precompiles 的 jq 命令后面的空行处)插入以下内容,以追加你的地址:
local_node.sh
6. 构建并验证
100025807224055573593873019。
Precompiles are smart contract interfaces at fixed addresses where the implementation runs as native Go code. Cosmos EVM ships with precompiles for staking, governance, IBC, and more. As a chain builder you control which ones are active and can add your own. For more information and to see the full list of available precompiles, see the precompiles overview.
Enabling Precompiles
Precompiles are enabled via theactive_static_precompiles parameter in the vm module. Only addresses listed here are callable at runtime. For the full list of built-in precompiles and their addresses, see the precompiles overview.
- Wire precompiles into the EVM keeper in
app.gousing.WithStaticPrecompiles(). The standard way is to passprecompiletypes.DefaultStaticPrecompiles(...), which includes all built-in precompiles:
evmd/app.go
DefaultStaticPrecompiles with your own builder chain (see Adding a Custom Precompile below).
- Set the active precompiles in your genesis configuration (
evmd/genesis.go):
x/vm/types/precompiles.go. See the precompiles overview for more information.
Already-registered precompiles can also be enabled or disabled after launch via a governance parameter change proposal targeting the vm module’s active_static_precompiles param. Adding a genuinely new custom precompile requires a chain upgrade, since the implementation lives in the Go binary.
Adding a Custom Precompile
The following example adds a statefulDenomSupply precompile with a single supplyOf method that reads total token supply directly from the Cosmos bank module. This demonstrates the core pattern for basic precompiles: injecting a Cosmos SDK keeper and using RunNativeAction to access live chain state from an EVM call.
1. Create the precompile package
Create a directoryprecompiles/denomsupply/ with two files:
precompiles/denomsupply/abi.json — the Solidity ABI:
precompiles/denomsupply/abi.json
precompiles/denomsupply/denomsupply.go — the implementation:
precompiles/denomsupply/denomsupply.go
cmn.Precompile rather than handling context directly. RunNativeAction sets up the SDK context, manages gas metering, and handles snapshot/revert so that precompile calls participate correctly in EVM transaction atomicity. Inside the closure, p.bankKeeper provides access to the bank module’s state.
2. Register the address
Inx/vm/types/precompiles.go, add a constant before the closing ) of the const block (line 17) and append it to AvailableStaticPrecompiles before its closing } (line 35, shifted +1 by the constant insert). Addresses must be in sorted order.
Add the constant (inserted before the closing ) at line 17):
x/vm/types/precompiles.go
AvailableStaticPrecompiles slice (inserted before the closing } at line 35, shifted +1 by the previous insert):
x/vm/types/precompiles.go
3. Add a builder method
Inprecompiles/types/static_precompiles.go, add the import for the new package (inserted before ics02precompile at line 16, between govprecompile and ics02precompile):
precompiles/types/static_precompiles.go
With method at the very end of the file. Pass any keepers your precompile needs as parameters:
precompiles/types/static_precompiles.go
4. Wire it into the app
Inprecompiles/types/defaults.go, add your method to the builder chain by replacing line 89 (WithSlashingPrecompile). The bankKeeper is already a parameter of DefaultStaticPrecompiles:
precompiles/types/defaults.go
5. Activate at genesis
Becauseevmd/genesis.go already uses evmtypes.AvailableStaticPrecompiles, adding your address to that slice in Step 2 is sufficient — no change to genesis.go is required.
If you use local_node.sh for local development, that script hardcodes the precompile list via a jq command and does not read from AvailableStaticPrecompiles at runtime. Insert the following before line 244 of local_node.sh (the blank line after the active_static_precompiles jq command at line 243) to append your address:
local_node.sh
6. Build and verify
100025807224055573593873019 in the output.