流程概览
- 创建升级分支,并冻结会影响 schema 的变更
- 导出升级前状态并归档节点配置
- 将
cosmos/evm 升级到 v0.4.0,并对齐 Cosmos SDK/IBC/CometBFT 约束
- 调整 keeper 和 AppModule(导入、构造函数、
RegisterServices)
- 添加 client context 字段和
SetClientCtx 方法
- 添加 pending transaction listener 支持
- 如果你已有 token pair,请迁移 ERC20 预编译(参见本节)
- 审查并迁移 EVM 与 FeeMarket 参数(EIP-1559 参数、denom/decimals)
- 在你的 UpgradeHandler 中实现 store/params 迁移
准备工作
- 创建分支:
git switch -c upgrade/evm-v0.4
- 确保升级前构建干净且测试全部通过
- 备份当前的 params/genesis,便于后续对比
git switch -c upgrade/evm-v0.4
go test ./...
evmd export > pre-upgrade-genesis.json
依赖升级
锁定 EVM 版本并整理依赖
在 go.mod 中升级 cosmos/evm 依赖:
- github.com/cosmos/evm v0.3.1
+ github.com/cosmos/evm v0.4.0
传递依赖升级
检查是否存在小版本依赖升级(例如 google.golang.org/protobuf、github.com/gofrs/flock、github.com/consensys/gnark-crypto):
继续下一步之前,先在这里解决所有版本冲突。
App 构造函数返回类型与 CLI 命令接线
将应用中的 newApp 返回类型从 servertypes.Application 改为 evmserver.Application;而仍然期望 SDK app creator 的 CLI 命令则需要一个包装器。
修改返回类型
// cmd/myapp/cmd/root.go
import (
evmserver "github.com/cosmos/evm/server"
)
func (a appCreator) newApp(
l log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) evmserver.Application { // 从 servertypes.Application 改为此类型
// ...
}
为期望 SDK 类型的命令提供包装器
创建一个轻量包装器,并将其用于 pruning.Cmd 和 snapshot.Cmd:
// cmd/myapp/cmd/root.go
sdkAppCreatorWrapper := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return ac.newApp(l, d, w, ao)
}
rootCmd.AddCommand(
pruning.Cmd(sdkAppCreatorWrapper, myapp.DefaultNodeHome),
snapshot.Cmd(sdkAppCreatorWrapper),
)
添加 clientCtx 和 SetClientCtx
把 clientCtx 加到你的 app 对象中:
// app/app.go
import (
"github.com/cosmos/cosmos-sdk/client"
)
type MyApp struct {
// ... 现有字段
clientCtx client.Context
}
func (app *MyApp) SetClientCtx(clientCtx client.Context) {
app.clientCtx = clientCtx
}
Pending-tx listener 支持
导入 EVM ante 包和 geth common:
// app/app.go
import (
"github.com/cosmos/evm/ante"
"github.com/ethereum/go-ethereum/common"
)
App 状态:listeners 切片
为 listeners 添加一个新字段:
// app/app.go
type MyApp struct {
// ... 现有字段
pendingTxListeners []ante.PendingTxListener
}
注册方法
添加一个公开方法,按 txHash 注册 listener:
// app/app.go
func (app *MyApp) RegisterPendingTxListener(listener func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, listener)
}
预编译:Optionals + codec 注入
新增导入
// app/keepers/precompiles.go
import (
"cosmossdk.io/core/address"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
sdk "github.com/cosmos/cosmos-sdk/types"
)
定义 Optionals + 默认值 + 函数式选项
创建一个小型 options 容器,并从应用的 bech32 配置中提取合理默认值:
// app/keepers/precompiles.go
type Optionals struct {
AddressCodec address.Codec // 供 gov/staking 使用
ValidatorAddrCodec address.Codec // 供 slashing 使用
ConsensusAddrCodec address.Codec // 供 slashing 使用
}
func defaultOptionals() Optionals {
return Optionals{
AddressCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
ValidatorAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
ConsensusAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
}
}
type Option func(*Optionals)
func WithAddressCodec(c address.Codec) Option {
return func(o *Optionals) { o.AddressCodec = c }
}
func WithValidatorAddrCodec(c address.Codec) Option {
return func(o *Optionals) { o.ValidatorAddrCodec = c }
}
func WithConsensusAddrCodec(c address.Codec) Option {
return func(o *Optionals) { o.ConsensusAddrCodec = c }
}
4.3 更新预编译工厂以接收 options
// app/keepers/precompiles.go
func NewAvailableStaticPrecompiles(
ctx context.Context,
// ... 其他参数
opts ...Option,
) map[common.Address]vm.PrecompiledContract {
options := defaultOptionals()
for _, opt := range opts {
opt(&options)
}
// ... 其余实现
}
4.4 修改各个预编译构造函数
ICS-20 预编译 现在需要将 bankKeeper 放在第一位:
- ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
- stakingKeeper,
+ ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
+ bankKeeper,
+ stakingKeeper,
transferKeeper,
&channelKeeper,
// ...
Gov 预编译 现在需要一个 AddressCodec:
- govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc)
+ govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc, options.AddressCodec)
ERC20 预编译迁移
对于已有 ERC20 token pair 的链,此迁移是必需的ERC20 预编译的存储机制在 v0.4.0 中发生了根本变化。如果不正确迁移,你的 ERC20 token 将无法再通过 EVM 访问。
如果你的链中存在以下任一情况,请在升级时包含此迁移:
- 已转换为 ERC20 的 IBC token
- 具有 ERC20 表示的 token factory token
- 存储中任何已有的
DynamicPrecompiles 或 NativePrecompiles
完整迁移说明见:ERC20 预编译迁移指南
将以下内容加入你的 upgrade handler:
// 在你的 upgrade handler 中
store := ctx.KVStore(storeKeys[erc20types.StoreKey])
const addressLength = 42
// 迁移 dynamic precompiles
if oldData := store.Get([]byte("DynamicPrecompiles")); len(oldData) > 0 {
for i := 0; i < len(oldData); i += addressLength {
address := common.HexToAddress(string(oldData[i : i+addressLength]))
erc20Keeper.SetDynamicPrecompile(ctx, address)
}
store.Delete([]byte("DynamicPrecompiles"))
}
// 迁移 native precompiles
if oldData := store.Get([]byte("NativePrecompiles")); len(oldData) > 0 {
for i := 0; i < len(oldData); i += addressLength {
address := common.HexToAddress(string(oldData[i : i+addressLength]))
erc20Keeper.SetNativePrecompile(ctx, address)
}
store.Delete([]byte("NativePrecompiles"))
}
升级后,请验证迁移是否成功:
# 检查 ERC20 余额(如果升级前已有 token,这里不应为 0)
cast call $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url http://localhost:8545
# 验证状态中的 precompiles
mantrachaind export | jq '.app_state.erc20.dynamic_precompiles'
构建与快速测试
-
编译:
-
冒烟测试(本地单节点):
- 启动节点,确认 RPC 能正常启动
- 部署一个简单合约,验证 events 和 logs
- 发送几笔 1559 交易,确认 base-fee 行为看起来正常
- (可选)注册一个 pending-tx listener,并在交易进入 mempool 时记录其 hash
发布检查清单
- 打包新的二进制文件(如果你使用 Cosmovisor,也包括其 upgrade 目录)
- 确认所有验证者构建的是同一个 commit(没有
replace 行)
- 只有在你修改了默认值时才共享
app.toml diff;否则请从新的二进制重新生成该文件,再重新应用自定义配置
- 升级后:在前 20-50 个区块内监控 mempool/pending tx 日志、base-fee 变化以及合约事件
常见问题与处理办法
-
忘记给 CLI 命令加包装器 →
pruning/snapshot panic 或类型错误:
- 确保传给这些命令的是
sdkAppCreatorWrapper,而不是 ac.newApp
-
ICS-20 预编译构建报错:
- 你很可能没有把
bankKeeper 放在第一个参数;更新对应调用位置
-
治理预编译地址解析失败:
- 通过默认值或
WithAddressCodec(...) 提供正确的 AddressCodec
-
Listeners 始终不触发:
- 在 app 构造期间或模块初始化阶段通过
RegisterPendingTxListener 注册
最小代码片段
App listeners
// app/app.go
import (
"github.com/cosmos/evm/ante"
"github.com/ethereum/go-ethereum/common"
)
type MyApp struct {
// ...
pendingTxListeners []ante.PendingTxListener
}
func (app *MyApp) RegisterPendingTxListener(l func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, l)
}
CLI 包装器
// cmd/myapp/cmd/root.go
sdkAppCreatorWrapper := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return ac.newApp(l, d, w, ao)
}
rootCmd.AddCommand(
pruning.Cmd(sdkAppCreatorWrapper, myapp.DefaultNodeHome),
snapshot.Cmd(sdkAppCreatorWrapper),
)
预编译 options 与用法
// app/keepers/precompiles.go
opts := []Option{
// 仅当你使用非标准前缀/codecs 时才覆盖默认值
WithAddressCodec(myAcctCodec),
WithValidatorAddrCodec(myValCodec),
WithConsensusAddrCodec(myConsCodec),
}
pcs := NewAvailableStaticPrecompiles(ctx, /* ... keepers ... */, opts...)
额外的破坏性变更(v0.3.1 → v0.4.1)
鸣谢:感谢 Mantrachain 团队识别出这些额外的迁移要求。
CallEVM 签名变更
CallEVM 函数现在需要一个类型为 *big.Int 的 gasCap 参数。你可以传入 nil 以使用默认行为。
函数签名(来自 x/vm/keeper/call_evm.go:20):
func (k Keeper) CallEVM(
ctx sdk.Context,
abi abi.ABI,
from, contract common.Address,
commit bool,
gasCap *big.Int, // 新参数
method string,
args ...interface{},
) (*types.MsgEthereumTxResponse, error)
迁移示例(来自 x/erc20/keeper/evm.go:87):
// v0.4.x 之前:不存在 gasCap 参数
- res, err := k.evmKeeper.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, "decimals")
// v0.4.x 之后:必须提供 gasCap 参数(可以为 nil)
+ res, err := k.evmKeeper.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, nil, "decimals")
使用自定义 Gas Cap:
// 如果你需要指定 gas cap
import "math/big"
gasLimit := big.NewInt(100000)
res, err := k.evmKeeper.CallEVM(ctx, abi, from, contract, false, gasLimit, "balanceOf", account)
真实使用示例:
x/erc20/keeper/evm.go:87 - decimals 调用
x/erc20/keeper/evm.go:143 - balanceOf 调用
x/ibc/callbacks/keeper/keeper.go:193 - 带自定义 gas cap 的 approve 调用
测试 Keyring 导入路径
测试 keyring 包已重组到其独立的子目录中。
导入变更:
- testkeyring "github.com/cosmos/evm/testutil"
+ testkeyring "github.com/cosmos/evm/testutil/keyring"
测试中的示例(来自 tests/integration/precompiles/erc20/test_setup.go:10):
package erc20
import (
"github.com/stretchr/testify/suite"
testkeyring "github.com/cosmos/evm/testutil/keyring"
)
type PrecompileTestSuite struct {
suite.Suite
keyring testkeyring.Keyring
}
func (s *PrecompileTestSuite) SetupTest() {
// Create keyring with 2 accounts
keyring := testkeyring.New(2)
// Get addresses for pre-funding
options := []network.ConfigOption{
network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...),
}
}
可用函数(来自 testutil/keyring/keyring.go:58):
New(nAccs int) Keyring - 创建包含 n 个账户的 keyring
GetPrivKey(index int) - 获取私钥
GetAddr(index int) - 获取 Ethereum 地址
GetAccAddr(index int) - 获取 Cosmos SDK 地址
GetAllAccAddrs() - 获取所有 SDK 地址
打标签前验证
go.mod 中不存在针对 github.com/cosmos/evm 的 replace 行
- 节点可正常启动,并暴露预期的 RPC 命名空间
- 合约可部署和调用;事件可流式输出;手续费市场行为正常
- (如适用)ICS-20 转账正常,且预编译合约可执行
Process Overview
- Create an upgrade branch and freeze schema-affecting changes
- Export a pre-upgrade state and archive node configs
- Bump
cosmos/evm to v0.4.0 and align Cosmos SDK/IBC/CometBFT constraints
- Rewire keepers and AppModule (imports, constructors,
RegisterServices)
- Add client context field and
SetClientCtx method
- Add pending transaction listener support
- Migrate ERC20 precompiles if you have existing token pairs (see [this section]./(erc20-precompiles-migration))
- Audit and migrate EVM & FeeMarket params (EIP-1559 knobs, denom/decimals)
- Implement store/params migrations in your UpgradeHandler
Prep
- Create a branch:
git switch -c upgrade/evm-v0.4
- Ensure a clean build + tests green pre-upgrade
- Snapshot your current params/genesis for comparison later
git switch -c upgrade/evm-v0.4
go test ./...
evmd export > pre-upgrade-genesis.json
Dependency bumps
Pin EVM and tidy
Bump the cosmos/evm dependency in go.mod:
- github.com/cosmos/evm v0.3.1
+ github.com/cosmos/evm v0.4.0
Transitive bumps
Check for minor dependency bumps (e.g., google.golang.org/protobuf, github.com/gofrs/flock, github.com/consensys/gnark-crypto):
Resolve any version conflicts here before moving on.
App constructor return type & CLI command wiring
Update your app’s newApp to return an evmserver.Application rather than servertypes.Application, and CLI commands that still expect an SDK app creator require a wrapper.
Change the return type
// cmd/myapp/cmd/root.go
import (
evmserver "github.com/cosmos/evm/server"
)
func (a appCreator) newApp(
l log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) evmserver.Application { // Changed from servertypes.Application
// ...
}
Provide a wrapper for commands that expect the SDK type
Create a thin wrapper and use it for pruning.Cmd and snapshot.Cmd:
// cmd/myapp/cmd/root.go
sdkAppCreatorWrapper := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return ac.newApp(l, d, w, ao)
}
rootCmd.AddCommand(
pruning.Cmd(sdkAppCreatorWrapper, myapp.DefaultNodeHome),
snapshot.Cmd(sdkAppCreatorWrapper),
)
Add clientCtx and SetClientCtx
Add the clientCtx to your app object:
// app/app.go
import (
"github.com/cosmos/cosmos-sdk/client"
)
type MyApp struct {
// ... existing fields
clientCtx client.Context
}
func (app *MyApp) SetClientCtx(clientCtx client.Context) {
app.clientCtx = clientCtx
}
Pending-tx listener support
Imports
Import the EVM ante package and geth common:
// app/app.go
import (
"github.com/cosmos/evm/ante"
"github.com/ethereum/go-ethereum/common"
)
App state: listeners slice
Add a new field for listeners:
// app/app.go
type MyApp struct {
// ... existing fields
pendingTxListeners []ante.PendingTxListener
}
Registration method
Add a public method to register a listener by txHash:
// app/app.go
func (app *MyApp) RegisterPendingTxListener(listener func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, listener)
}
Precompiles: optionals + codec injection
New imports
// app/keepers/precompiles.go
import (
"cosmossdk.io/core/address"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
sdk "github.com/cosmos/cosmos-sdk/types"
)
Define Optionals + defaults + functional options
Create a small options container with sane defaults pulled from the app’s bech32 config:
// app/keepers/precompiles.go
type Optionals struct {
AddressCodec address.Codec // used by gov/staking
ValidatorAddrCodec address.Codec // used by slashing
ConsensusAddrCodec address.Codec // used by slashing
}
func defaultOptionals() Optionals {
return Optionals{
AddressCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
ValidatorAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
ConsensusAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
}
}
type Option func(*Optionals)
func WithAddressCodec(c address.Codec) Option {
return func(o *Optionals) { o.AddressCodec = c }
}
func WithValidatorAddrCodec(c address.Codec) Option {
return func(o *Optionals) { o.ValidatorAddrCodec = c }
}
func WithConsensusAddrCodec(c address.Codec) Option {
return func(o *Optionals) { o.ConsensusAddrCodec = c }
}
4.3 Update the precompile factory to accept options
// app/keepers/precompiles.go
func NewAvailableStaticPrecompiles(
ctx context.Context,
// ... other params
opts ...Option,
) map[common.Address]vm.PrecompiledContract {
options := defaultOptionals()
for _, opt := range opts {
opt(&options)
}
// ... rest of implementation
}
4.4 Modify individual precompile constructors
ICS-20 precompile now needs bankKeeper first:
- ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
- stakingKeeper,
+ ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
+ bankKeeper,
+ stakingKeeper,
transferKeeper,
&channelKeeper,
// ...
Gov precompile now requires an AddressCodec:
- govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc)
+ govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc, options.AddressCodec)
ERC20 Precompiles Migration
This migration is required for chains with existing ERC20 token pairsThe storage mechanism for ERC20 precompiles has fundamentally changed in v0.4.0. Without proper migration, your ERC20 tokens will become inaccessible via EVM.
Include this migration with your upgrade if your chain has:
- IBC tokens converted to ERC20
- Token factory tokens with ERC20 representations
- Any existing
DynamicPrecompiles or NativePrecompiles in storage
Implementation
For complete migration instructions, see: ERC20 Precompiles Migration Guide
Add this to your upgrade handler:
// In your upgrade handler
store := ctx.KVStore(storeKeys[erc20types.StoreKey])
const addressLength = 42
// Migrate dynamic precompiles
if oldData := store.Get([]byte("DynamicPrecompiles")); len(oldData) > 0 {
for i := 0; i < len(oldData); i += addressLength {
address := common.HexToAddress(string(oldData[i : i+addressLength]))
erc20Keeper.SetDynamicPrecompile(ctx, address)
}
store.Delete([]byte("DynamicPrecompiles"))
}
// Migrate native precompiles
if oldData := store.Get([]byte("NativePrecompiles")); len(oldData) > 0 {
for i := 0; i < len(oldData); i += addressLength {
address := common.HexToAddress(string(oldData[i : i+addressLength]))
erc20Keeper.SetNativePrecompile(ctx, address)
}
store.Delete([]byte("NativePrecompiles"))
}
Verification
Post-upgrade, verify your migration succeeded:
# Check ERC20 balance (should NOT be 0 if tokens existed before)
cast call $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url http://localhost:8545
# Verify precompiles in state
mantrachaind export | jq '.app_state.erc20.dynamic_precompiles'
Build & quick tests
-
Compile:
-
Smoke tests (local single-node):
- Start your node; ensure RPC starts cleanly
- Deploy a trivial contract; verify events and logs
- Send a couple 1559 txs and confirm base-fee behavior looks sane
- (Optional) register a pending-tx listener and log hashes as they enter the mempool
Rollout checklist
- Package the new binary (and Cosmovisor upgrade folder if you use it)
- Confirm all validators build the same commit (no
replace lines)
- Share an
app.toml diff only if you changed defaults; otherwise regenerate the file from the new binary and re-apply customizations
- Post-upgrade: monitor mempool/pending tx logs, base-fee progression, and contract events for the first 20-50 blocks
Pitfalls & remedies
-
Forgot wrapper for CLI commands →
pruning/snapshot panic or wrong type:
- Ensure you pass
sdkAppCreatorWrapper (not ac.newApp) into those commands
-
ICS-20 precompile build error:
- You likely didn’t pass
bankKeeper first; update the call site
-
Governance precompile address parsing fails:
- Provide the correct
AddressCodec via defaults or WithAddressCodec(...)
-
Listeners never fire:
- Register with
RegisterPendingTxListener during app construction or module init
Minimal code snippets
App listeners
// app/app.go
import (
"github.com/cosmos/evm/ante"
"github.com/ethereum/go-ethereum/common"
)
type MyApp struct {
// ...
pendingTxListeners []ante.PendingTxListener
}
func (app *MyApp) RegisterPendingTxListener(l func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, l)
}
CLI wrapper
// cmd/myapp/cmd/root.go
sdkAppCreatorWrapper := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return ac.newApp(l, d, w, ao)
}
rootCmd.AddCommand(
pruning.Cmd(sdkAppCreatorWrapper, myapp.DefaultNodeHome),
snapshot.Cmd(sdkAppCreatorWrapper),
)
Precompile options & usage
// app/keepers/precompiles.go
opts := []Option{
// override defaults only if you use non-standard prefixes/codecs
WithAddressCodec(myAcctCodec),
WithValidatorAddrCodec(myValCodec),
WithConsensusAddrCodec(myConsCodec),
}
pcs := NewAvailableStaticPrecompiles(ctx, /* ... keepers ... */, opts...)
Additional Breaking Changes (v0.3.1 → v0.4.1)
Credits: Thanks to the Mantrachain team for identifying these additional migration requirements.
CallEVM Signature Change
The CallEVM function now requires a gasCap parameter of type *big.Int. You can pass nil for default behavior.
Function Signature (from x/vm/keeper/call_evm.go:20):
func (k Keeper) CallEVM(
ctx sdk.Context,
abi abi.ABI,
from, contract common.Address,
commit bool,
gasCap *big.Int, // New parameter
method string,
args ...interface{},
) (*types.MsgEthereumTxResponse, error)
Migration Example (from x/erc20/keeper/evm.go:87):
// Before v0.4.x - gasCap parameter didn't exist
- res, err := k.evmKeeper.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, "decimals")
// After v0.4.x - gasCap parameter required (can be nil)
+ res, err := k.evmKeeper.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, nil, "decimals")
With Custom Gas Cap:
// If you need to specify a gas cap
import "math/big"
gasLimit := big.NewInt(100000)
res, err := k.evmKeeper.CallEVM(ctx, abi, from, contract, false, gasLimit, "balanceOf", account)
Real usage examples:
x/erc20/keeper/evm.go:87 - decimals call
x/erc20/keeper/evm.go:143 - balanceOf call
x/ibc/callbacks/keeper/keeper.go:193 - approve call with custom gas cap
Test Keyring Import Path
The test keyring package has been reorganized into its own subdirectory.
Import Change:
- testkeyring "github.com/cosmos/evm/testutil"
+ testkeyring "github.com/cosmos/evm/testutil/keyring"
Example from Tests (from tests/integration/precompiles/erc20/test_setup.go:10):
package erc20
import (
"github.com/stretchr/testify/suite"
testkeyring "github.com/cosmos/evm/testutil/keyring"
)
type PrecompileTestSuite struct {
suite.Suite
keyring testkeyring.Keyring
}
func (s *PrecompileTestSuite) SetupTest() {
// Create keyring with 2 accounts
keyring := testkeyring.New(2)
// Get addresses for pre-funding
options := []network.ConfigOption{
network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...),
}
}
Available Functions (from testutil/keyring/keyring.go:58):
New(nAccs int) Keyring - Create keyring with n accounts
GetPrivKey(index int) - Get private key
GetAddr(index int) - Get Ethereum address
GetAccAddr(index int) - Get Cosmos SDK address
GetAllAccAddrs() - Get all SDK addresses
Verify before tagging
go.mod has no replace lines for github.com/cosmos/evm
- Node boots with expected RPC namespaces
- Contracts deploy/call; events stream; fee market behaves
- (If applicable) ICS-20 transfers work and precompiles execute