This guide is for fresh integration of Cosmos EVM v0.5.0 into new chains. For migration from previous versions, see the specific migration guides.
Prerequisites
- Cosmos SDK v0.53.x based chain
- IBC-Go v10.x
- Go 1.23+
- Understanding of Cosmos SDK module integration
Critical Interface Requirements: v0.5.0 requires implementing specific interfaces (
AppWithPendingTxStream
, evmserver.Application
) with compile-time enforcement. Missing these will cause build failures.Step 1: Dependencies & Imports
Update go.mod
Copy
Ask AI
require (
github.com/cosmos/cosmos-sdk v0.53.4
github.com/cosmos/ibc-go/v10 v10.3.0
github.com/cosmos/evm v0.5.0
)
replace (
// Required: Use Cosmos fork of go-ethereum
github.com/ethereum/go-ethereum => github.com/cosmos/go-ethereum v1.15.11-cosmos-0
)
Key Imports for app.go
Key Imports for app.go
Copy
Ask AI
// EVM-specific imports
import (
// EVM ante handlers (updated import path in v0.5.0)
evmante "github.com/cosmos/evm/ante"
cosmosevmante "github.com/cosmos/evm/ante/evm"
// Configuration
evmconfig "github.com/cosmos/evm/config"
srvflags "github.com/cosmos/evm/server/flags"
// Mempool (new in v0.5.0)
evmmempool "github.com/cosmos/evm/mempool"
// Modules
"github.com/cosmos/evm/x/erc20"
erc20keeper "github.com/cosmos/evm/x/erc20/keeper"
erc20types "github.com/cosmos/evm/x/erc20/types"
"github.com/cosmos/evm/x/feemarket"
feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper"
"github.com/cosmos/evm/x/precisebank"
precisebankkeeper "github.com/cosmos/evm/x/precisebank/keeper"
precisebanktypes "github.com/cosmos/evm/x/precisebank/types"
"github.com/cosmos/evm/x/vm"
evmkeeper "github.com/cosmos/evm/x/vm/keeper"
evmtypes "github.com/cosmos/evm/x/vm/types"
// Override IBC transfer for ERC20 support
"github.com/cosmos/evm/x/ibc/transfer"
transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper"
// Server interface
evmserver "github.com/cosmos/evm/server"
"github.com/ethereum/go-ethereum/common"
)
Step 2: App Structure Definition
App Struct with Required Fields
App Struct with Required Fields
Copy
Ask AI
// App extends BaseApp with EVM functionality
type App struct {
*baseapp.BaseApp
// Encoding
legacyAmino *codec.LegacyAmino
appCodec codec.Codec
interfaceRegistry types.InterfaceRegistry
txConfig client.TxConfig
// REQUIRED: Client context for EVM operations
clientCtx client.Context
// REQUIRED: Pending transaction listeners for mempool integration
pendingTxListeners []evmante.PendingTxListener
// Store keys
keys map[string]*storetypes.KVStoreKey
tkeys map[string]*storetypes.TransientStoreKey
memKeys map[string]*storetypes.MemoryStoreKey
// Standard Cosmos SDK keepers
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
StakingKeeper *stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
DistrKeeper distrkeeper.Keeper
GovKeeper govkeeper.Keeper
// ... other standard keepers
// REQUIRED: EVM-specific keepers
FeeMarketKeeper feemarketkeeper.Keeper
EVMKeeper *evmkeeper.Keeper
Erc20Keeper erc20keeper.Keeper
PreciseBankKeeper precisebankkeeper.Keeper // Critical for 18-decimal precision
TransferKeeper transferkeeper.Keeper // EVM-enhanced IBC transfer
// REQUIRED: EVM mempool (new in v0.5.0)
EVMMempool *evmmempool.ExperimentalEVMMempool
// Module management
ModuleManager *module.Manager
BasicModuleManager module.BasicManager
sm *module.SimulationManager
}
Required Interface Methods
Copy
Ask AI
// REQUIRED: SetClientCtx for EVM operations
func (app *App) SetClientCtx(clientCtx client.Context) {
app.clientCtx = clientCtx
}
// REQUIRED: AppWithPendingTxStream interface methods
func (app *App) RegisterPendingTxListener(listener func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, listener)
}
func (app *App) onPendingTx(hash common.Hash) {
for _, listener := range app.pendingTxListeners {
listener(hash)
}
}
App Constructor Return Type
Copy
Ask AI
// CRITICAL: Must return evmserver.Application, not servertypes.Application
func NewApp(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) evmserver.Application { // NOT servertypes.Application
// ... implementation
return app
}
Step 3: Store Keys & Module Setup
Store Key Registration
Store Key Registration
Copy
Ask AI
// Add EVM-specific store keys to your existing keys
keys := storetypes.NewKVStoreKeys(
// Standard SDK keys...
authtypes.StoreKey,
banktypes.StoreKey,
// ... other standard keys
// REQUIRED: EVM module store keys
evmtypes.StoreKey,
feemarkettypes.StoreKey,
erc20types.StoreKey,
precisebanktypes.StoreKey, // For 18-decimal precision
ibctransfertypes.StoreKey, // EVM-enhanced IBC transfer
)
tkeys := storetypes.NewTransientStoreKeys(
evmtypes.TransientKey, // Required for EVM state transitions
)
Module Registration
Module Registration
Copy
Ask AI
// Module manager with EVM modules
app.ModuleManager = module.NewManager(
// Standard SDK modules...
auth.NewAppModule(appCodec, app.AccountKeeper, ...),
bank.NewAppModule(appCodec, app.BankKeeper, ...),
// ... other standard modules
// IBC modules (use EVM-enhanced transfer)
ibc.NewAppModule(app.IBCKeeper),
transferModule, // EVM-enhanced, not standard IBC transfer
// REQUIRED: EVM modules in dependency order
vm.NewAppModule(app.EVMKeeper, app.AccountKeeper, app.AccountKeeper.AddressCodec()),
feemarket.NewAppModule(app.FeeMarketKeeper),
erc20.NewAppModule(app.Erc20Keeper, app.AccountKeeper),
precisebank.NewAppModule(app.PreciseBankKeeper, app.BankKeeper, app.AccountKeeper),
)
Step 4: Keeper Initialization
Initialization Order (Critical)
Initialization Order (Critical)
Copy
Ask AI
// 1. Standard SDK keepers first
app.AccountKeeper = authkeeper.NewAccountKeeper(...)
app.BankKeeper = bankkeeper.NewKeeper(...)
app.StakingKeeper = stakingkeeper.NewKeeper(...)
// ... other SDK keepers
// 2. PreciseBank keeper (wraps bank keeper for 18-decimal precision)
app.PreciseBankKeeper = precisebankkeeper.NewKeeper(
appCodec,
keys[precisebanktypes.StoreKey],
app.BankKeeper,
app.AccountKeeper,
)
// 3. FeeMarket keeper (required by EVM)
app.FeeMarketKeeper = feemarketkeeper.NewKeeper(
appCodec,
authtypes.NewModuleAddress(govtypes.ModuleName),
keys[feemarkettypes.StoreKey],
tkeys[feemarkettypes.TransientKey],
app.GetSubspace(feemarkettypes.ModuleName),
)
// 4. EVM keeper (MUST be before ERC20 keeper)
tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer))
app.EVMKeeper = evmkeeper.NewKeeper(
appCodec,
keys[evmtypes.StoreKey],
tkeys[evmtypes.TransientKey],
keys,
authtypes.NewModuleAddress(govtypes.ModuleName),
app.AccountKeeper,
app.PreciseBankKeeper, // Use PreciseBank for 18-decimal support
app.StakingKeeper,
app.FeeMarketKeeper,
&app.ConsensusParamsKeeper,
&app.Erc20Keeper, // Forward reference
tracer,
)
// 5. ERC20 keeper (after EVM keeper)
app.Erc20Keeper = erc20keeper.NewKeeper(
keys[erc20types.StoreKey],
appCodec,
authtypes.NewModuleAddress(govtypes.ModuleName),
app.AccountKeeper,
app.PreciseBankKeeper,
app.EVMKeeper,
app.StakingKeeper,
)
// 6. Enhanced IBC Transfer keeper for ERC20 support
app.TransferKeeper = transferkeeper.NewKeeper(
appCodec,
keys[ibctransfertypes.StoreKey],
app.GetSubspace(ibctransfertypes.ModuleName),
app.IBCKeeper.ChannelKeeper,
app.IBCKeeper.ChannelKeeper,
app.IBCKeeper.PortKeeper,
app.AccountKeeper,
app.PreciseBankKeeper, // Use PreciseBank
scopedTransferKeeper,
app.Erc20Keeper, // EVM integration
)
Step 5: Ante Handler Integration
Complete Ante Handler Setup
Complete Ante Handler Setup
Copy
Ask AI
func (app *App) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) {
options := evmante.HandlerOptions{
Cdc: app.appCodec,
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
ExtensionOptionChecker: cosmosevmtypes.HasDynamicFeeExtensionOption,
EvmKeeper: app.EVMKeeper,
FeegrantKeeper: app.FeeGrantKeeper,
IBCKeeper: app.IBCKeeper,
FeeMarketKeeper: app.FeeMarketKeeper,
SignModeHandler: txConfig.SignModeHandler(),
SigGasConsumer: evmante.SigVerificationGasConsumer,
// v0.5.0 new parameters
MaxTxGasWanted: maxGasWanted, // From app.toml evm.max-tx-gas-wanted
TxFeeChecker: cosmosevmante.NewDynamicFeeChecker(app.FeeMarketKeeper),
PendingTxListener: app.onPendingTx, // Required for mempool integration
}
if err := options.Validate(); err != nil {
panic(fmt.Sprintf("ante handler options validation failed: %v", err))
}
// Import path changed from evmd/ante to evm/ante in v0.5.0
app.SetAnteHandler(evmante.NewAnteHandler(options))
}
Call During App Construction
Copy
Ask AI
// In NewApp constructor, AFTER keeper initialization
maxGasWanted := cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted))
app.setAnteHandler(app.txConfig, maxGasWanted)
Step 6: Mempool Integration (v0.5.0 Required)
Complete Mempool Setup
Complete Mempool Setup
Copy
Ask AI
// In NewApp constructor, AFTER ante handler is set
if cosmosevmtypes.GetChainConfig() != nil {
// Get configuration from app.toml and genesis.json
blockGasLimit := evmconfig.GetBlockGasLimit(appOpts, logger)
minTip := evmconfig.GetMinTip(appOpts, logger)
// Configure EVM mempool
mempoolConfig := &evmmempool.EVMMempoolConfig{
AnteHandler: app.GetAnteHandler(), // Must be set first
BlockGasLimit: blockGasLimit, // From genesis consensus params
MinTip: minTip, // From app.toml evm.min-tip (v0.5.0)
}
// Initialize EVM mempool
evmMempool := evmmempool.NewExperimentalEVMMempool(
app.CreateQueryContext,
logger,
app.EVMKeeper,
app.FeeMarketKeeper,
app.txConfig,
app.clientCtx,
mempoolConfig,
)
app.EVMMempool = evmMempool
// REQUIRED: Replace BaseApp mempool
app.SetMempool(evmMempool)
// REQUIRED: Set custom CheckTx handler
checkTxHandler := evmmempool.NewCheckTxHandler(evmMempool)
app.SetCheckTxHandler(checkTxHandler)
// REQUIRED: Set custom PrepareProposal handler
abciProposalHandler := baseapp.NewDefaultProposalHandler(evmMempool, app)
abciProposalHandler.SetSignerExtractionAdapter(
evmmempool.NewEthSignerExtractionAdapter(
sdkmempool.NewDefaultSignerExtractionAdapter(),
),
)
app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
}
Step 7: Precompile Registration
Interface-Based Precompile Setup
Interface-Based Precompile Setup
Copy
Ask AI
// Create precompile options with address codecs
type Optionals struct {
AddressCodec address.Codec
ValidatorAddrCodec address.Codec
ConsensusAddrCodec address.Codec
}
func defaultOptionals() Optionals {
return Optionals{
AddressCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
ValidatorAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
ConsensusAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
}
}
// Register static precompiles with interface-based constructors
func NewAvailableStaticPrecompiles(
stakingKeeper stakingkeeper.Keeper,
distributionKeeper distributionkeeper.Keeper,
bankKeeper cmn.BankKeeper, // Interface, not concrete type
erc20Keeper erc20keeper.Keeper,
transferKeeper transferkeeper.Keeper,
channelKeeper channelkeeper.Keeper,
govKeeper govkeeper.Keeper,
slashingKeeper slashingkeeper.Keeper,
cdc codec.Codec,
opts ...Option,
) (map[common.Address]vm.PrecompiledContract, error) {
options := defaultOptionals()
for _, opt := range opts {
opt(&options)
}
precompiles := make(map[common.Address]vm.PrecompiledContract)
// Bank precompile
bankPrecompile, err := bankprecompile.NewPrecompile(bankKeeper, cdc)
if err != nil {
return nil, fmt.Errorf("failed to instantiate bank precompile: %w", err)
}
precompiles[bankPrecompile.Address()] = bankPrecompile
// Distribution precompile
distributionPrecompile, err := distprecompile.NewPrecompile(
cmn.DistributionKeeper(distributionKeeper),
cdc,
options.AddressCodec,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate distribution precompile: %w", err)
}
precompiles[distributionPrecompile.Address()] = distributionPrecompile
// Staking precompile
stakingPrecompile, err := stakingprecompile.NewPrecompile(
cmn.StakingKeeper(stakingKeeper),
cdc,
options.AddressCodec,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate staking precompile: %w", err)
}
precompiles[stakingPrecompile.Address()] = stakingPrecompile
// ICS20 precompile (parameter order changed in v0.4.0)
ics20Precompile, err := ics20precompile.NewPrecompile(
bankKeeper, // bankKeeper FIRST (changed in v0.4.0)
stakingKeeper,
transferKeeper,
channelKeeper,
cdc,
options.AddressCodec,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate ics20 precompile: %w", err)
}
precompiles[ics20Precompile.Address()] = ics20Precompile
// Governance precompile (address codec required in v0.4.0)
govPrecompile, err := govprecompile.NewPrecompile(
cmn.GovKeeper(govKeeper),
cdc,
options.AddressCodec, // Required in v0.4.0
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate gov precompile: %w", err)
}
precompiles[govPrecompile.Address()] = govPrecompile
// Slashing precompile
slashingPrecompile, err := slashingprecompile.NewPrecompile(
cmn.SlashingKeeper(slashingKeeper),
cdc,
options.ValidatorAddrCodec,
options.ConsensusAddrCodec,
)
if err != nil {
return nil, fmt.Errorf("failed to instantiate slashing precompile: %w", err)
}
precompiles[slashingPrecompile.Address()] = slashingPrecompile
// Bech32 precompile
bech32Precompile, err := bech32.NewPrecompile()
if err != nil {
return nil, fmt.Errorf("failed to instantiate bech32 precompile: %w", err)
}
precompiles[bech32Precompile.Address()] = bech32Precompile
// P256 precompile (secp256r1)
p256Precompile, err := p256.NewPrecompile()
if err != nil {
return nil, fmt.Errorf("failed to instantiate p256 precompile: %w", err)
}
precompiles[p256Precompile.Address()] = p256Precompile
return precompiles, nil
}
Step 8: CLI Command Integration
Required CLI Wrapper
Copy
Ask AI
// cmd/myapp/cmd/root.go
// REQUIRED: Wrapper for SDK commands that expect servertypes.Application
sdkAppCreatorWrapper := func(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) servertypes.Application {
return ac.newApp(logger, db, traceStore, appOpts)
}
// Use wrapper for pruning and snapshot commands
rootCmd.AddCommand(
pruning.Cmd(sdkAppCreatorWrapper, app.DefaultNodeHome),
snapshot.Cmd(sdkAppCreatorWrapper),
)
Step 9: Configuration Management
app.toml EVM Section
app.toml EVM Section
Copy
Ask AI
# EVM configuration (v0.5.0)
[evm]
# Minimum priority fee for mempool (in wei)
min-tip = 0
# Maximum gas for eth transactions in check tx mode
max-tx-gas-wanted = 0
# EIP-155 chain ID (separate from Cosmos chain ID)
evm-chain-id = 262144
# EVM execution tracer (empty = disabled)
tracer = ""
# SHA3 preimage tracking (not implemented yet)
cache-preimage = false
[json-rpc]
# Enable JSON-RPC server
enable = true
address = "127.0.0.1:8545"
ws-address = "127.0.0.1:8546"
# Required for transaction queries
enable-indexer = true
# API namespaces (txpool requires mempool)
api = "eth,net,web3,txpool,debug"
# Performance settings
gas-cap = 25000000
filter-cap = 200
logs-cap = 10000
block-range-cap = 10000
genesis.json EVM Parameters
genesis.json EVM Parameters
Copy
Ask AI
{
"app_state": {
"evm": {
"params": {
"evm_denom": "atoken",
"extra_eips": [],
"evm_channels": [],
"access_control": {
"create": {"access_type": "ACCESS_TYPE_PERMISSIONLESS"},
"call": {"access_type": "ACCESS_TYPE_PERMISSIONLESS"}
},
"active_static_precompiles": [
"0x0000000000000000000000000000000000000100",
"0x0000000000000000000000000000000000000400",
"0x0000000000000000000000000000000000000800",
"0x0000000000000000000000000000000000000801",
"0x0000000000000000000000000000000000000802",
"0x0000000000000000000000000000000000000804",
"0x0000000000000000000000000000000000000805",
"0x0000000000000000000000000000000000000806",
"0x0000000000000000000000000000000000000807"
],
"history_serve_window": 8192
}
},
"feemarket": {
"params": {
"no_base_fee": false,
"base_fee_change_denominator": 8,
"elasticity_multiplier": 2,
"enable_height": "0",
"base_fee": "1000000000",
"min_gas_price": "0",
"min_gas_multiplier": "0.5"
}
},
"erc20": {
"params": {
"enable_erc20": true,
"permissionless_registration": true
}
}
}
}
Step 10: Account Configuration
18-Decimal Precision Setup
Copy
Ask AI
// Set power reduction for 18-decimal base unit (EVM standard)
func init() {
sdk.DefaultPowerReduction = sdkmath.NewIntFromBigInt(
new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil),
)
}
Coin Type Configuration
Copy
Ask AI
// Use coin type 60 for Ethereum compatibility
const CoinType uint32 = 60
Step 11: Testing & Verification
Build Verification
Copy
Ask AI
# Verify clean build
go mod tidy
go build ./...
# Test app interfaces (will fail at compile-time if missing)
go build -v ./cmd/myapp
Functionality Testing
Functionality Testing
Copy
Ask AI
# Start node
myevmd start
# Test JSON-RPC connectivity
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
-H "Content-Type: application/json" http://localhost:8545
# Test new eth_createAccessList method
curl -X POST --data '{
"jsonrpc":"2.0",
"method":"eth_createAccessList",
"params":[{"from":"0x...","to":"0x...","data":"0x..."}, "latest"],
"id":1
}' -H "Content-Type: application/json" http://localhost:8545
# Test mempool functionality
curl -X POST --data '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":1}' \
-H "Content-Type: application/json" http://localhost:8545
# Test precompile functionality
cast call 0x0000000000000000000000000000000000000804 \
"balanceOf(address)" 0x... --rpc-url http://localhost:8545
Configuration Validation
Copy
Ask AI
# Verify configuration is loaded correctly
myevmd start --help | grep "evm\."
# Check genesis parameters
myevmd export | jq '.app_state.evm.params'
# Verify mempool configuration
myevmd export | jq '.app_state.erc20'
Step 12: Production Considerations
Security Settings
Security Settings
Copy
Ask AI
[json-rpc]
# Production security settings
allow-insecure-unlock = false
enable-profiling = false
ws-origins = ["yourdomain.com"]
[evm]
# Set minimum tip for spam protection
min-tip = 1000000000 # 1 Gwei
# Limit gas for resource protection
max-tx-gas-wanted = 50000000 # 50M gas
Performance Settings
Performance Settings
Copy
Ask AI
[json-rpc]
# Optimize for high load
gas-cap = 25000000
filter-cap = 1000
logs-cap = 50000
http-timeout = "30s"
batch-request-limit = 100
[evm]
# Disable tracing in production
tracer = ""
Common Integration Issues
Issue: Build Failure - Interface Not Satisfied
Copy
Ask AI
error: *App does not implement evmserver.Application
evmserver.Application
:
Copy
Ask AI
- func NewApp(...) servertypes.Application {
+ func NewApp(...) evmserver.Application {
Issue: Runtime Panic - Missing Interface Methods
Copy
Ask AI
panic: App must implement AppWithPendingTxStream interface
Copy
Ask AI
func (app *App) RegisterPendingTxListener(listener func(common.Hash)) {
app.pendingTxListeners = append(app.pendingTxListeners, listener)
}
Issue: Mempool Configuration Error
Copy
Ask AI
error: mempool config must not be nil
Mempool Configuration Fix
Copy
Ask AI
mempoolConfig := &evmmempool.EVMMempoolConfig{
AnteHandler: app.GetAnteHandler(), // Must be set AFTER ante handler
BlockGasLimit: blockGasLimit,
MinTip: minTip, // From app.toml evm.min-tip (v0.5.0)
}
Issue: JSON-RPC Methods Not Working
Copy
Ask AI
error: eth_createAccessList not found
Copy
Ask AI
[json-rpc]
enable-indexer = true
api = "eth,net,web3,txpool"
Integration Checklist
- Dependencies updated to v0.5.0
- App implements
evmserver.Application
interface -
AppWithPendingTxStream
interface methods implemented - All EVM keepers initialized in correct order
- PreciseBank configured for 18-decimal precision
- Ante handler setup with all v0.5.0 parameters
- Mempool integration with MinTip configuration
- Precompiles registered with interface constructors
- CLI commands use SDK wrapper
- app.toml EVM section configured
- genesis.json parameters set correctly
- Build succeeds without errors
- JSON-RPC functionality verified
- Mempool operations tested
- Precompile calls working