0) 准备

  • 创建分支:git switch -c upgrade/evm-v0.5。
  • 确保升级前构建干净且测试全部通过。
  • 备份当前的 params/genesis,便于后续对比。

1) 依赖升级(go.mod)

  • 将 github.com/cosmos/evm 升级到 v0.5.0,然后执行:
go mod tidy

2) 修复 "github.com/cosmos/evm/types" 导入

v0.5.0 移除了 github.com/cosmos/evm/types,并将相关文件按功能分别迁移到对应目录。 完整变更列表请参考这个 PR。 下面列出了 evmd 中已迁移的引用。

evmd 中导入变更摘要:

已移除的导入:
- cosmosevmtypes "github.com/cosmos/evm/types"
新增的导入:
+ antetypes "github.com/cosmos/evm/ante/types"
+ evmaddress "github.com/cosmos/evm/encoding/address"
+ "github.com/cosmos/evm/utils"

已迁移项目的详细映射:

  • AttoPowerReduction → 已迁移到 "github.com/cosmos/evm/utils"
    - sdk.DefaultPowerReduction = cosmosevmtypes.AttoPowerReduction
    + sdk.DefaultPowerReduction = utils.AttoPowerReduction
    
  • HasDynamicFeeExtensionOption → 已迁移到 "github.com/cosmos/evm/ante/types"
    - ExtensionOptionChecker: cosmosevmtypes.HasDynamicFeeExtensionOption,
    + ExtensionOptionChecker: antetypes.HasDynamicFeeExtensionOption,
    
  • 地址 Codec 函数 → 新包 "github.com/cosmos/evm/encoding/address" 使用 evmaddress.NewEvmCodec() 初始化地址 codec:
    app.AccountKeeper = authkeeper.NewAccountKeeper(
        appCodec,
        runtime.NewKVStoreService(keys[authtypes.StoreKey]),
        authtypes.ProtoBaseAccount,
        evmconfig.GetMaccPerms(),
        evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
        sdk.GetConfig().GetBech32AccountAddrPrefix(),
        authAddr,
    )
    
  • Bip44CoinType、BIP44HDPath → 已迁移到 "github.com/cosmos/evm/crypto/hd"
  • GenesisState → 已移除,因为 evmd 目录中已有重复对象,而测试版本位于 "github.com/cosmos/evm/testutil"

3) app.go 中的应用接线

Mempool

自定义初始化器

现在可以通过辅助函数处理 mempool 配置。如果你希望使用 app.toml 和 CLI flags 中的配置,可以重构你的 mempool 设置:
  • 之前:在 app.go 中手动配置
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: blockGasLimit,
    MinTip:        minTip,
}
evmMempool := evmmempool.NewExperimentalEVMMempool(
    app.CreateQueryContext, logger, app.EVMKeeper, app.FeeMarketKeeper,
    app.txConfig, app.clientCtx, mempoolConfig,
)
if err := app.configureEVMMempool(appOpts, logger); err != nil {
    panic(fmt.Sprintf("failed to configure EVM mempool: %s", err.Error()))
}
该辅助函数会从 appOpts 读取配置;如果未提供则使用默认值。注意,NewExperimentalEVMMempool 现在新增了一个 cosmosPoolMaxTx 参数,推荐默认值为 4096 或 0(不设上限)。

简单配置迁移

如果 BlockGasLimit 为 0,则默认使用 100_000_000。如果未设置 BroadCastTxFn,也会使用默认值。
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: 100_000_000, // or 0 to use default
}
evmMempool := evmmempool.NewExperimentalEVMMempool(
    app.CreateQueryContext, logger, app.EVMKeeper, app.FeeMarketKeeper, app.txConfig, app.clientCtx, mempoolConfig
)

高级场景:迁移你的自定义配置

PR #496 用 EVMMempoolConfig 中的配置替换了预构建的 pool:
  • 用配置替换 pool
    • 已移除:TxPool *txpool.TxPool、CosmosPool sdkmempool.ExtMempool
    • 已新增:LegacyPoolConfig *legacypool.Config、CosmosPoolConfig *sdkmempool.PriorityNonceMempoolConfig[math.Int]
如果你之前自己构建了自定义 pool:
 mempoolConfig := &evmmempool.EVMMempoolConfig{
-   TxPool:     customTxPool,
-   CosmosPool: customCosmosPool,
+   LegacyPoolConfig: &legacyCfg,  // or nil for defaults
+   CosmosPoolConfig: &cosmosCfg,  // or nil for defaults
   AnteHandler:      app.GetAnteHandler(),
   BroadCastTxFn:    myBroadcast, // optional
 }
自定义配置示例:
// EVM legacy txpool tuning
legacyCfg := legacypool.DefaultConfig
legacyCfg.PriceLimit = 2
mempoolConfig.LegacyPoolConfig = &legacyCfg

// Cosmos priority mempool tuning
cosmosCfg := sdkmempool.PriorityNonceMempoolConfig[math.Int]{}
cosmosCfg.TxPriority = sdkmempool.TxPriority[math.Int]{
    GetTxPriority: func(goCtx context.Context, tx sdk.Tx) math.Int {
        // Custom priority function
    },
    Compare:  func(a, b math.Int) int { return a.BigInt().Cmp(b.BigInt()) },
    MinValue: math.ZeroInt(),
}
mempoolConfig.CosmosPoolConfig = &cosmosCfg

// Custom EVM broadcast (optional)
mempoolConfig.BroadCastTxFn = func(txs []*ethtypes.Transaction) error { return nil }

新配置项

PR #698 为 EVM mempool 新增了可通过 app.toml 或 CLI flags 设置的配置项。这些配置项可用于细粒度调整 EVM legacy pool 的行为。
通过 app.toml 配置
现在可以在 app.toml 的 [evm.mempool] 段下使用以下 mempool 配置项:
[evm.mempool]
# PriceLimit is the minimum gas price to enforce for acceptance into the pool (in wei)
price-limit = 1

# PriceBump is the minimum price bump percentage to replace an already existing transaction (nonce)
price-bump = 10

# AccountSlots is the number of executable transaction slots guaranteed per account
account-slots = 16

# GlobalSlots is the maximum number of executable transaction slots for all accounts
global-slots = 5120

# AccountQueue is the maximum number of non-executable transaction slots permitted per account
account-queue = 64

# GlobalQueue is the maximum number of non-executable transaction slots for all accounts
global-queue = 1024

# Lifetime is the maximum amount of time non-executable transaction are queued
lifetime = "3h0m0s"
通过 CLI Flags 配置
这些选项也可以通过 CLI flags 设置:
  • --evm.mempool.price-limit(默认值:1)
  • --evm.mempool.price-bump(默认值:10)
  • --evm.mempool.account-slots(默认值:16)
  • --evm.mempool.global-slots(默认值:5120)
  • --evm.mempool.account-queue(默认值:64)
  • --evm.mempool.global-queue(默认值:1024)
  • --evm.mempool.lifetime(默认值:3h0m0s)
Cosmos Mempool 最大交易数
新增了 --mempool.max-txs flag,用于限制 Cosmos mempool 中允许的最大交易数。设置为 0 或 -1 表示不设上限(默认值:0)。 NewExperimentalEVMMempool 的函数签名也已变更,新增了 cosmosPoolMaxTx 字段:
func NewExperimentalEVMMempool(
	getCtxCallback func(height int64, prove bool) (sdk.Context, error),
	logger log.Logger,
	vmKeeper VMKeeperI,
	feeMarketKeeper FeeMarketKeeperI,
	txConfig client.TxConfig,
	clientCtx client.Context,
	config *EVMMempoolConfig,
+	cosmosPoolMaxTx int,
)

EVM Chain ID

现在会直接从 appOpts 获取 EVM chain ID,而不是作为参数传入。在 app.go 中,chain ID 的获取方式如下:
evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
参考实现见 evmd/app.go:216。 现在 EVM Keeper 也会接收 evmChainID 作为参数:
app.EVMKeeper = evmkeeper.NewKeeper(
		appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], keys,
		authtypes.NewModuleAddress(govtypes.ModuleName),
		app.AccountKeeper,
		app.PreciseBankKeeper,
		app.StakingKeeper,
		app.FeeMarketKeeper,
		&app.ConsensusParamsKeeper,
		&app.Erc20Keeper,
+		evmChainID,
		tracer,
	)

函数签名变更

在 app.go 中,从 NewApp 签名中移除 evmChainID 和 evmAppOptions。
// NewExampleApp returns a reference to an initialized EVMD.
func NewExampleApp(
	logger log.Logger,
	db dbm.DB,
	traceStore io.Writer,
	loadLatest bool,
	appOpts servertypes.AppOptions,
-	evmChainID uint64,
-	evmAppOptions evmconfig.EVMOptionsFn,
	baseAppOptions ...func(*baseapp.BaseApp),
) *EVMD {
之后,删除所有对该函数的引用中对应的入参。 然后,移除所有调用 evmAppOptions 的引用: app.go
-	if err := evmAppOptions(evmChainID); err != nil {
-		panic(err)
-	}
root.go
- noOpEvmAppOptions := func(_ uint64) error {
-		return nil
-	}
- if initClientCtx.ChainID != "" {
-		if err := config.EvmAppOptions(config.EVMChainID); err != nil {
-			panic(err)
-		}
-	}
evmd_config.go、chain_id.go、config.go、constants.go 已迁移到 github.com/cosmos/evm/config,因此可以从你的仓库中移除。

Denom 配置

#661 移除了通过 app.go 实例化链配置的方式, 并将其迁移到 state 或 genesis 中。 必须移除所有对 EvmAppOptions 的使用,因为调用该配置器会导致链在启动时运行时 panic。

默认预编译合约

默认预编译合约已迁移到 /evm/precompiles/types/defaults.go,函数名改为 DefaultStaticPrecompiles。函数签名也已变更,现在 Erc20Keeper 和 TransferKeeper 需要以指针形式传入。最后,WithStaticPrecompiles 构建器函数现在可以在 keeper 实例化时一并调用,而不是在之后调用。新的接线方式如下:
	app.EVMKeeper = evmkeeper.NewKeeper(
		appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], keys,
		authtypes.NewModuleAddress(govtypes.ModuleName),
		app.AccountKeeper,
		app.PreciseBankKeeper,
		app.StakingKeeper,
		app.FeeMarketKeeper,
		&app.ConsensusParamsKeeper,
		&app.Erc20Keeper,
		evmChainID,
		tracer,
	).WithStaticPrecompiles(
		precompiletypes.DefaultStaticPrecompiles(
			*app.StakingKeeper,
			app.DistrKeeper,
			app.PreciseBankKeeper,
			&app.Erc20Keeper, // UPDATED
			&app.TransferKeeper, // UPDATED
			app.IBCKeeper.ChannelKeeper,
			app.GovKeeper,
			app.SlashingKeeper,
			appCodec,
		),
	)

UpgradeHandler

由于配置已经迁移到 state 和 genesis,如果你的链不满足以下条件,就必须包含 UpgradeHandler。
  1. 你在 x/vm params 中设置的 EVM Denom,必须已经在 x/bank 中注册了对应的 DenomMetadata。
  2. 你的 EVM Denom 必须在 DenomMetadata 中关联了 display denom。
    1. EVM Denom 对应的 display denom 必须具有正确的小数位数(例如对于 uatom,atom 的小数位应为 6)。
  3. 你的链是 18 位小数链。
  4. 调用 InitEvmCoinInfo,在模块 store 中初始化 EVM coin metadata。
在你的 UpgradeHandler 中:
  • 如果你的链没有为 EVM Denom 设置 DenomMetadata,则必须补上。
  • 如果你链上的 EVM denom 不是 18 位小数,必须在 x/vm params 中添加 ExtendedDenomOptions。
  • 设置好 metadata 后,初始化 EVM coin info:
// After setting denom metadata
if err := app.EVMKeeper.InitEvmCoinInfo(ctx); err != nil {
    return nil, err
}
完整实现请参考升级示例。

4) 构建与快速测试

go build ./...
在单节点上做冒烟测试:
  • 发送几笔 EVM tx;确认 promotion/broadcast(或你自定义的 BroadCastTxFn)正常。
  • 发送 Cosmos tx;确认排序符合你的 CosmosPoolConfig 配置(如果做过自定义)。

0) Prep

  • Create a branch: git switch -c upgrade/evm-v0.5.
  • Ensure a clean build + tests green pre-upgrade.
  • Snapshot your current params/genesis for comparison later.

1) Dependency bumps (go.mod)

  • Bump github.com/cosmos/evm to v0.5.0 and run:
go mod tidy

2) Fix "github.com/cosmos/evm/types" imports

v0.5.0 removes github.com/cosmos/evm/types and moves files to their folders, respective to function. For a complete list of changes, refer to this PR. The following list includes references within evmd that have been moved.

Summary of import changes in evmd:

Removed import:
- cosmosevmtypes "github.com/cosmos/evm/types"
Added imports:
+ antetypes "github.com/cosmos/evm/ante/types"
+ evmaddress "github.com/cosmos/evm/encoding/address"
+ "github.com/cosmos/evm/utils"

Detailed mapping of moved items:

  • AttoPowerReduction → moved to "github.com/cosmos/evm/utils"
    - sdk.DefaultPowerReduction = cosmosevmtypes.AttoPowerReduction
    + sdk.DefaultPowerReduction = utils.AttoPowerReduction
    
  • HasDynamicFeeExtensionOption → moved to "github.com/cosmos/evm/ante/types"
    - ExtensionOptionChecker: cosmosevmtypes.HasDynamicFeeExtensionOption,
    + ExtensionOptionChecker: antetypes.HasDynamicFeeExtensionOption,
    
  • Address Codec functions → new package "github.com/cosmos/evm/encoding/address" Use evmaddress.NewEvmCodec() for address codec initialization:
    app.AccountKeeper = authkeeper.NewAccountKeeper(
        appCodec,
        runtime.NewKVStoreService(keys[authtypes.StoreKey]),
        authtypes.ProtoBaseAccount,
        evmconfig.GetMaccPerms(),
        evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
        sdk.GetConfig().GetBech32AccountAddrPrefix(),
        authAddr,
    )
    
  • Bip44CoinType, BIP44HDPath → moved to "github.com/cosmos/evm/crypto/hd"
  • GenesisState → removed as a duplicate object can be found in the evmd folder and a testing version is in "github.com/cosmos/evm/testutil"

3) App wiring in app.go

Mempool

Custom initializer

The mempool configuration can now be handled by a helper function. If you prefer to use the configuration from app.toml and CLI flags, you can refactor your mempool setup:
  • Before: Manual configuration in app.go
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: blockGasLimit,
    MinTip:        minTip,
}
evmMempool := evmmempool.NewExperimentalEVMMempool(
    app.CreateQueryContext, logger, app.EVMKeeper, app.FeeMarketKeeper,
    app.txConfig, app.clientCtx, mempoolConfig,
)
if err := app.configureEVMMempool(appOpts, logger); err != nil {
    panic(fmt.Sprintf("failed to configure EVM mempool: %s", err.Error()))
}
The helper function reads configuration from appOpts or applies defaults if omitted. Note that NewExperimentalEVMMempool now takes an additional cosmosPoolMaxTx parameter, with a recommended default value being 4096 or 0 (uncapped).

Simple config migration

If BlockGasLimit is 0, it defaults to 100_000_000. If BroadCastTxFn is not set, it’s also set to a default value.
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: 100_000_000, // or 0 to use default
}
evmMempool := evmmempool.NewExperimentalEVMMempool(
    app.CreateQueryContext, logger, app.EVMKeeper, app.FeeMarketKeeper, app.txConfig, app.clientCtx, mempoolConfig
)

Advanced setups: migrate your customizations

PR #496 replaced pre-built pools with configs in EVMMempoolConfig:
  • Replace pools with configs
    • Removed: TxPool *txpool.TxPool, CosmosPool sdkmempool.ExtMempool
    • Added: LegacyPoolConfig *legacypool.Config, CosmosPoolConfig *sdkmempool.PriorityNonceMempoolConfig[math.Int]
If you built custom pools yourself:
 mempoolConfig := &evmmempool.EVMMempoolConfig{
-   TxPool:     customTxPool,
-   CosmosPool: customCosmosPool,
+   LegacyPoolConfig: &legacyCfg,  // or nil for defaults
+   CosmosPoolConfig: &cosmosCfg,  // or nil for defaults
   AnteHandler:      app.GetAnteHandler(),
   BroadCastTxFn:    myBroadcast, // optional
 }
Example custom configs:
// EVM legacy txpool tuning
legacyCfg := legacypool.DefaultConfig
legacyCfg.PriceLimit = 2
mempoolConfig.LegacyPoolConfig = &legacyCfg

// Cosmos priority mempool tuning
cosmosCfg := sdkmempool.PriorityNonceMempoolConfig[math.Int]{}
cosmosCfg.TxPriority = sdkmempool.TxPriority[math.Int]{
    GetTxPriority: func(goCtx context.Context, tx sdk.Tx) math.Int {
        // Custom priority function
    },
    Compare:  func(a, b math.Int) int { return a.BigInt().Cmp(b.BigInt()) },
    MinValue: math.ZeroInt(),
}
mempoolConfig.CosmosPoolConfig = &cosmosCfg

// Custom EVM broadcast (optional)
mempoolConfig.BroadCastTxFn = func(txs []*ethtypes.Transaction) error { return nil }

New Configuration Options

PR #698 adds new configuration options for the EVM mempool that can be set via app.toml or CLI flags. These options allow fine-tuning of the EVM legacy pool behavior.
Configuration via app.toml
The following mempool configuration options are now available in app.toml under the [evm.mempool] section:
[evm.mempool]
# PriceLimit is the minimum gas price to enforce for acceptance into the pool (in wei)
price-limit = 1

# PriceBump is the minimum price bump percentage to replace an already existing transaction (nonce)
price-bump = 10

# AccountSlots is the number of executable transaction slots guaranteed per account
account-slots = 16

# GlobalSlots is the maximum number of executable transaction slots for all accounts
global-slots = 5120

# AccountQueue is the maximum number of non-executable transaction slots permitted per account
account-queue = 64

# GlobalQueue is the maximum number of non-executable transaction slots for all accounts
global-queue = 1024

# Lifetime is the maximum amount of time non-executable transaction are queued
lifetime = "3h0m0s"
Configuration via CLI Flags
These options can also be set via CLI flags:
  • --evm.mempool.price-limit (default: 1)
  • --evm.mempool.price-bump (default: 10)
  • --evm.mempool.account-slots (default: 16)
  • --evm.mempool.global-slots (default: 5120)
  • --evm.mempool.account-queue (default: 64)
  • --evm.mempool.global-queue (default: 1024)
  • --evm.mempool.lifetime (default: 3h0m0s)
Cosmos Mempool Max Transactions
A new flag --mempool.max-txs allows limiting the maximum number of transactions in the Cosmos mempool. Set to 0 or -1 for unbounded (default: 0). The function signature for NewExperimentalEVMMempool also changed to add a cosmosPoolMaxTx field:
func NewExperimentalEVMMempool(
	getCtxCallback func(height int64, prove bool) (sdk.Context, error),
	logger log.Logger,
	vmKeeper VMKeeperI,
	feeMarketKeeper FeeMarketKeeperI,
	txConfig client.TxConfig,
	clientCtx client.Context,
	config *EVMMempoolConfig,
+	cosmosPoolMaxTx int,
)

EVM Chain ID

The EVM chain ID is now retrieved directly from appOpts instead of being passed as a parameter. In app.go, the chain ID is obtained using:
evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
See evmd/app.go:216 for the reference implementation. The EVM Keeper now also takes in evmChainID as a parameter:
app.EVMKeeper = evmkeeper.NewKeeper(
		appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], keys,
		authtypes.NewModuleAddress(govtypes.ModuleName),
		app.AccountKeeper,
		app.PreciseBankKeeper,
		app.StakingKeeper,
		app.FeeMarketKeeper,
		&app.ConsensusParamsKeeper,
		&app.Erc20Keeper,
+		evmChainID,
		tracer,
	)

Function Signature Changes

In app.go, remove evmChainID and evmAppOptions from the NewApp signature.
// NewExampleApp returns a reference to an initialized EVMD.
func NewExampleApp(
	logger log.Logger,
	db dbm.DB,
	traceStore io.Writer,
	loadLatest bool,
	appOpts servertypes.AppOptions,
-	evmChainID uint64,
-	evmAppOptions evmconfig.EVMOptionsFn,
	baseAppOptions ...func(*baseapp.BaseApp),
) *EVMD {
Afterwards, fix any reference to the function by removing the inputs. Then, remove any reference of evmAppOptions being called: app.go
-	if err := evmAppOptions(evmChainID); err != nil {
-		panic(err)
-	}
root.go
- noOpEvmAppOptions := func(_ uint64) error {
-		return nil
-	}
- if initClientCtx.ChainID != "" {
-		if err := config.EvmAppOptions(config.EVMChainID); err != nil {
-			panic(err)
-		}
-	}
evmd_config.go, chain_id.go, config.go, constants.go have been moved to github.com/cosmos/evm/config and may be removed to your repo.

Denom Configs

#661 removes the instantiation of chain configs via app.go and moves them to state or genesis. It is critical to remove any use of EvmAppOptions as calling the configurator will panic the chain at runtime during startup.

Default Precompiles

Default precompiles have been moved to /evm/precompiles/types/defaults.go and the function name was changed to DefaultStaticPrecompiles. The function signature has also changed, and now takes pointers as inputs for the Erc20Keeper and TransferKeeper. Finally, the WithStaticPrecompiles builder function can now happen alongside the keeper instantiation, and not after. The new wiring is shown below:
	app.EVMKeeper = evmkeeper.NewKeeper(
		appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], keys,
		authtypes.NewModuleAddress(govtypes.ModuleName),
		app.AccountKeeper,
		app.PreciseBankKeeper,
		app.StakingKeeper,
		app.FeeMarketKeeper,
		&app.ConsensusParamsKeeper,
		&app.Erc20Keeper,
		evmChainID,
		tracer,
	).WithStaticPrecompiles(
		precompiletypes.DefaultStaticPrecompiles(
			*app.StakingKeeper,
			app.DistrKeeper,
			app.PreciseBankKeeper,
			&app.Erc20Keeper, // UPDATED
			&app.TransferKeeper, // UPDATED
			app.IBCKeeper.ChannelKeeper,
			app.GovKeeper,
			app.SlashingKeeper,
			appCodec,
		),
	)

UpgradeHandler

As the configs have been moved to state and genesis, you must include an UpgradeHandler if your chain does not satisfy the following conditions.
  1. Your EVM Denom set in the x/vm params must have DenomMetadata registered for it in x/bank.
  2. Your EVM Denom must have a display denom associated with it in DenomMetadata.
    1. The display denom for the EVM Denom must have an accurate decimal value (i.e. for uatom, atom must have a decimal value of 6.
  3. Your chain is an 18-decimal chain.
  4. Call InitEvmCoinInfo to initialize EVM coin metadata in the module store.
In your UpgradeHandler:
  • If your chain does not have DenomMetadata set for the EVM Denom, you must include it.
  • If your chain’s EVM denom is not 18 decimals, you must add ExtendedDenomOptions to your x/vm params.
  • After setting metadata, initialize EVM coin info:
// After setting denom metadata
if err := app.EVMKeeper.InitEvmCoinInfo(ctx); err != nil {
    return nil, err
}
Please refer to the upgrade example for the complete implementation.

4) Build & quick tests

go build ./...
Smoke test on a single node:
  • Send a few EVM txs; confirm promotion/broadcast (or your BroadCastTxFn).
  • Send Cosmos txs; confirm ordering reflects your CosmosPoolConfig (if customized).