This migration contains breaking changes that require code updates v0.5.0 introduces significant changes to precompile interfaces, VM parameters, mempool architecture, and ante handlers. Review all breaking changes before upgrading.
Breaking Changes Summary
New Features
0) Preparation
Create an upgrade branch and prepare your environment:
git switch -c upgrade/evm-v0.5
go test ./...
evmd export > pre-upgrade-genesis.json
1) Dependency Updates
Update go.mod
- github.com/cosmos/evm v0.4.1
+ github.com/cosmos/evm v0.5.0
2) VM Parameter Changes
BREAKING: allow_unprotected_txs Parameter Removed
The allow_unprotected_txs
parameter has been removed from VM module parameters. Non-EIP-155 transaction acceptance is now managed per-node.
Migration Required:
Update Genesis Files:
{
"app_state": {
"vm": {
"params": {
"evm_denom": "atest",
"extra_eips": [],
- "allow_unprotected_txs": false,
"evm_channels": [],
"access_control": {...},
"active_static_precompiles": [...],
+ "history_serve_window": 8192
}
}
}
}
Update Parameter Validation:
If you have custom parameter validation logic, remove references to allow_unprotected_txs
:
/ Remove any code referencing AllowUnprotectedTxs
- if !params.AllowUnprotectedTxs {
- / validation logic
- }
NEW: history_serve_window Parameter
Added for EIP-2935 block hash storage support.
Default value: 8192
blocks
Purpose: Controls how many historical block hashes to serve
Range: Must be > 0, recommended ≤ 8192 for optimal performance
3) Precompile Interface Changes
BREAKING: Constructor Interface Updates
All precompile constructors now accept keeper interfaces instead of concrete implementations for better decoupling.
New Interface Definitions:
New Interface Definitions
/ precompiles / common / interfaces . go
type BankKeeper interface {
IterateAccountBalances ( ctx context . Context , account sdk . AccAddress , cb func ( coin sdk . Coin ) bool )
GetBalance ( ctx context . Context , addr sdk . AccAddress , denom string ) sdk . Coin
SendCoins ( ctx context . Context , fromAddr sdk . AccAddress , toAddr sdk . AccAddress , amt sdk . Coins ) error
/ ... other methods
}
type StakingKeeper interface {
BondDenom ( ctx context . Context ) ( string , error )
GetDelegatorValidators ( ctx context . Context , delegatorAddr sdk . AccAddress , maxRetrieve uint32 ) ( stakingtypes . Validators , error )
/ ... other methods
}
See all 13 lines
Migration Steps:
Update Precompile Assembly:
Precompile Assembly Update
/ evmd/precompiles.go or app/precompiles.go
func NewAvailableStaticPrecompiles(
/ ... other params
) map[common.Address]vm.PrecompiledContract {
precompiles := make(map[common.Address]vm.PrecompiledContract)
/ Bank precompile - interface now required
- bankPrecompile, err := bankprecompile.NewPrecompile(bankKeeper, ...)
+ bankPrecompile, err := bankprecompile.NewPrecompile(
+ common.BankKeeper(bankKeeper), / Cast to interface
+ / ... other params
+ )
/ Distribution precompile - simplified parameters
- distributionPrecompile, err := distributionprecompile.NewPrecompile(
- distributionKeeper, stakingKeeper, authzKeeper, cdc, options.AddressCodec
- )
+ distributionPrecompile, err := distributionprecompile.NewPrecompile(
+ common.DistributionKeeper(distributionKeeper),
+ cdc, options.AddressCodec
+ )
/ Staking precompile - keeper interface
- stakingPrecompile, err := stakingprecompile.NewPrecompile(stakingKeeper, ...)
+ stakingPrecompile, err := stakingprecompile.NewPrecompile(
+ common.StakingKeeper(stakingKeeper),
+ / ... other params
+ )
return precompiles
}
See all 31 lines
Update Custom Precompiles:
If you have custom precompiles, update their constructors to accept interfaces:
/ Custom precompile constructor
- func NewMyPrecompile(bankKeeper bankkeeper.Keeper, stakingKeeper stakingkeeper.Keeper) (*MyPrecompile, error) {
+ func NewMyPrecompile(bankKeeper common.BankKeeper, stakingKeeper common.StakingKeeper) (*MyPrecompile, error) {
return &MyPrecompile{
bankKeeper: bankKeeper,
stakingKeeper: stakingKeeper,
}, nil
}
4) Mempool Changes
Configuration-Based Architecture
The mempool now uses configuration objects instead of pre-built pools for better flexibility.
Migration for Standard Setups:
If you use default mempool configuration, minimal changes are needed:
/ Existing code continues to work
mempoolConfig := & evmmempool . EVMMempoolConfig {
AnteHandler : app . GetAnteHandler (),
BlockGasLimit : 100_000_000 , / or 0 for default
}
evmMempool := evmmempool . NewExperimentalEVMMempool (
app . CreateQueryContext ,
logger ,
app . EVMKeeper ,
app . FeeMarketKeeper ,
app . txConfig ,
app . clientCtx ,
mempoolConfig
)
See all 14 lines
Migration for Advanced Setups:
If you previously built custom pools:
Advanced Mempool Configuration
mempoolConfig := &evmmempool.EVMMempoolConfig{
- TxPool: customTxPool,
- CosmosPool: customCosmosPool,
+ LegacyPoolConfig: &legacypool.Config{
+ PriceLimit: 2,
+ / ... other legacy pool settings
+ },
+ CosmosPoolConfig: &sdkmempool.PriorityNonceMempoolConfig[math.Int]{
+ TxPriority: sdkmempool.TxPriority[math.Int]{
+ GetTxPriority: customPriorityFunc,
+ Compare: math.IntComparator,
+ MinValue: math.ZeroInt(),
+ },
+ },
AnteHandler: app.GetAnteHandler(),
BroadcastTxFn: customBroadcastFunc, / optional
BlockGasLimit: 100_000_000,
}
See all 18 lines
5) Ante Handler Changes
The ante handler system has been optimized to remove unnecessary EVM instance creation.
What Changed:
CanTransfer
ante decorator no longer creates StateDB instances
EVM instance removal improves performance for balance checks
Signature verification optimizations
Migration Impact:
Standard setups: No changes required
Custom ante handlers: Verify compatibility with new CanTransfer
behavior
Custom Ante Handler Updates:
If you have custom ante handlers that depend on EVM instance creation during balance checks:
/ Custom ante handler example
func (d MyCustomDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
/ Balance checking now optimized - no EVM instance creation
- evm := d.evmKeeper.NewEVM(ctx, ...)
- stateDB := evm.StateDB
- balance := stateDB.GetBalance(address)
/ Use keeper method instead
+ balance := d.evmKeeper.GetBalance(ctx, address)
return next(ctx, tx, simulate)
}
Field Mapping (v0.4.x → v0.5.0)
Removed fields:
TxPool
(pre-built EVM pool)
CosmosPool
(pre-built Cosmos pool)
New/Replacement fields:
LegacyPoolConfig
(configure legacy EVM txpool behavior)
CosmosPoolConfig
(configure Cosmos PriorityNonceMempool
behavior)
BlockGasLimit
(required; 0
uses fallback 100_000_000
)
BroadcastTxFn
(optional callback; defaults to broadcasting via clientCtx
)
MinTip
(optional minimum tip for EVM selection)
6) Global Mempool Removal
BREAKING: Singleton Pattern Eliminated
The global mempool registry has been removed in favor of direct injection.
What Was Removed:
mempool.SetGlobalEVMMempool()
mempool.GetGlobalEVMMempool()
Global mempool singleton pattern
Migration Steps:
Update JSON-RPC Server Initialization:
/ server/start.go or equivalent
- mempool.SetGlobalEVMMempool(evmMempool)
jsonRPCServer, err := jsonrpc.StartJSONRPC(
ctx,
clientCtx,
logger.With("module", "jsonrpc"),
+ evmMempool, / Pass mempool directly
config,
indexer,
)
See all 11 lines
Update RPC Backend:
If you have custom RPC backends:
Custom RPC Backend Update
/ Custom RPC backend
func NewCustomBackend(
/ ... other params
+ mempool *evmmempool.ExperimentalEVMMempool,
) *CustomBackend {
- mempool := mempool.GetGlobalEVMMempool()
return &CustomBackend{
mempool: mempool,
/ ... other fields
}
}
See all 12 lines
7) EIP-7702 EOA Code Delegation
NEW FEATURE: Account Abstraction for EOAs
EIP-7702 enables externally owned accounts to temporarily execute smart contract code through authorization lists.
What’s New:
SetCodeTx Transaction Type: New transaction type supporting code delegation
Authorization Signatures: Signed permissions for code delegation
Temporary Execution: EOAs can execute contract logic for single transactions
Account Abstraction: Multi-sig, time-locks, automated strategies
Implementation: x/vm/keeper/state_transition.go:426+
Usage Example:
/ Enable EOA to execute as multicall contract
const authorization = await signAuthorization ({
chainId: 9000 ,
address: multicallContractAddress ,
nonce: await wallet . getNonce (),
}, wallet );
const tx = {
type: 4 , / SetCodeTxType
authorizationList: [ authorization ],
to: wallet . address ,
data: multicall . interface . encodeFunctionData ( "batchCall" , [ calls ]),
gasLimit: 500000 ,
};
await wallet . sendTransaction ( tx );
Developer Impact:
Enhanced Wallets: EOAs can have programmable features
Better UX: Batched operations, custom validation logic
Account Abstraction: Multi-sig and advanced security features
No Migration: Existing EOAs enhanced without changes
8) EIP-2935 Block Hash Storage
NEW FEATURE: Historical Block Hash Access
EIP-2935 provides standardized access to historical block hashes via contract storage.
What’s New:
BLOCKHASH
opcode now queries contract storage for historical hashes
New history_serve_window
parameter controls storage depth
Compatible with Ethereum’s EIP-2935 specification
Configuration:
/ Genesis parameter
"history_serve_window" : 8192 / Default : 8192 blocks
Usage for Developers:
/ Smart contract can now reliably access historical block hashes
contract HistoryExample {
function getRecentBlockHash ( uint256 blockNumber ) public view returns ( bytes32 ) {
/ Works for blocks within history_serve_window range
return blockhash (blockNumber);
}
}
Performance Considerations:
Larger history_serve_window
values increase storage requirements
Default of 8192 provides good balance of utility and performance
Values > 8192 may impact node performance
8) New RPC Methods
eth_createAccessList
New JSON-RPC method for creating access lists to optimize transaction costs.
Usage:
curl -X POST \
-H "Content-Type: application/json" \
--data '{
"jsonrpc": "2.0",
"method": "eth_createAccessList",
"params": [{
"to": "0x...",
"data": "0x...",
"gas": "0x...",
"gasPrice": "0x..."
}, "latest"],
"id": 1
}' \
http://localhost:8545
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"accessList" : [
{
"address" : "0x..." ,
"storageKeys" : [ "0x..." ]
}
],
"gasUsed" : "0x..."
}
}
Gas Estimation Optimization
Short-circuit plain transfers: Simple ETH transfers now bypass complex gas estimation
Optimistic bounds: Uses MaxUsedGas
for better initial estimates
Result: Significantly faster eth_estimateGas
performance
State Management
Reduced EVM instances: Fewer unnecessary EVM instance creations
Storage optimizations: Empty storage checks implemented per EIP standards
Block notifications: Improved timing prevents funding errors
Mempool Enhancements
Nonce gap handling: Better error handling for transaction sequencing
Configuration flexibility: Tunable parameters for different network conditions
10) Testing Your Migration
Pre-Upgrade Checklist
# 1. Backup current state
evmd export > pre-upgrade-state.json
# 2. Document existing parameters
evmd query vm params > pre-upgrade-params.json
# 3. Note active precompiles
evmd query erc20 token-pairs > pre-upgrade-token-pairs.json
See all 8 lines
Post-Upgrade Verification
Post-Upgrade Verification
# 1. Verify node starts successfully
evmd start
# 2. Test EVM functionality
cast send --rpc-url http://localhost:8545 --private-key $PRIVATE_KEY \
0x... "transfer(address,uint256)" 0x... 1000
# 3. Verify new RPC methods
curl -X POST \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_createAccessList","params":[...],"id":1}' \
http://localhost:8545
# 4. Test precompile functionality
cast call 0x... "balanceOf(address)" 0x... --rpc-url http://localhost:8545
# 5. Verify EIP-2935 support
cast call --rpc-url http://localhost:8545 \
$CONTRACT_ADDRESS "getBlockHash(uint256)" $BLOCK_NUMBER
See all 19 lines
Integration Tests
/ Example integration test
func TestV050Migration ( t * testing . T ) {
/ Test mempool configuration
config := & evmmempool . EVMMempoolConfig {
AnteHandler : anteHandler ,
BlockGasLimit : 100_000_000 ,
}
mempool := evmmempool . NewExperimentalEVMMempool ( ... )
require . NotNil ( t , mempool )
/ Test precompile interfaces
bankPrecompile , err := bankprecompile . NewPrecompile (
common . BankKeeper ( bankKeeper ),
)
require . NoError ( t , err )
/ Test parameter validation
params := vmtypes . NewParams ( ... )
require . Equal ( t , uint64 ( 8192 ), params . HistoryServeWindow )
require . False ( t , hasAllowUnprotectedTxs ( params )) / Should be removed
}
See all 21 lines
11) Rollback Plan
If issues arise during migration:
# 1. Stop the upgraded node
systemctl stop evmd
# 2. Restore pre-upgrade binary
cp evmd-v0.4.1 /usr/local/bin/evmd
# 3. Restore pre-upgrade genesis (if needed)
cp pre-upgrade-genesis.json ~/.evmd/config/genesis.json
# 4. Restart with previous version
systemctl start evmd
12) Common Migration Issues
Issue: Precompile Constructor Errors
error: cannot use bankKeeper (type bankkeeper.Keeper) as type common.BankKeeper
Solution: Cast concrete keepers to interfaces:
bankPrecompile , err := bankprecompile . NewPrecompile (
common . BankKeeper ( bankKeeper ), / Add interface cast
)
Issue: Genesis Validation Failure
error: unknown field 'allow_unprotected_txs' in vm params
Solution: Remove the parameter from genesis:
# Update genesis.json to remove allow_unprotected_txs
# Add history_serve_window with default value 8192
Issue: Mempool Initialization Panic
panic: config must not be nil
Solution: Always provide mempool configuration:
config := & evmmempool . EVMMempoolConfig {
AnteHandler : app . GetAnteHandler (),
BlockGasLimit : 100_000_000 ,
}
Issue: Global Mempool Access
error: undefined: mempool.GetGlobalEVMMempool
Solution: Pass mempool directly instead of using global access:
/ Pass mempool as parameter
func NewRPCService ( mempool * evmmempool . ExperimentalEVMMempool ) {
/ Use injected mempool
}
13) Summary
v0.5.0 introduces significant improvements in performance, EVM compatibility, and code architecture:
EIP-2935 enables reliable historical block hash access
Precompile interfaces improve modularity and testing
Mempool optimizations provide better configurability
Performance improvements reduce gas estimation latency
New RPC methods enhance developer experience
Review all breaking changes carefully and test thoroughly before deploying to production.
For additional support, refer to: