Skip to main content

Chain Configuration Guide

When building your custom chain based on evmd, you’ll need to configure several key parameters. This guide walks through each configuration option from the evmd default setup and shows you how to customize it for your chain.
This guide focuses on the practical configuration steps. For comprehensive parameter references and advanced topics, see the migration guides and module-specific documentation.

evmd Default Configuration

The reference implementation (evmd) comes with these defaults:
ConfigurationDefault ValueCustomizable
Binary NameevmdYes (rename)
Cosmos Chain IDcosmos_262144-1Yes (genesis)
EVM Chain ID262144Yes (genesis)
Custom OpcodesNoneYes (advanced)
Token Pairs1 (native token)Yes (genesis)
Denominationatest (18 decimals)Yes (genesis)
EVM PermissioningPermissionlessYes (genesis)
Enabled PrecompilesAll 9 precompilesYes (genesis)
Let’s configure each of these for your custom chain.

1. Binary Name

What It Is: The name of your compiled blockchain executable. Default: evmd

How to Change

1

Rename Directory

cd /path/to/cosmos-evm
mv evmd yourchain
2

Update Go Files

# Update all references in Go files
find . -type f -name "*.go" -exec sed -i 's/evmd/yourchain/g' {} \;
3

Update go.mod

Edit the module path in go.mod:
module github.com/your-org/your-chain

// Update import paths that reference evmd
4

Update Makefile

Edit the Makefile to use your binary name:
BINARY_NAME := yourchain

install:
	go install ./yourchain/cmd/yourchain
5

Build

go mod tidy
make install

# Or build directly
go build -o yourchain ./yourchain/cmd/yourchain
After renaming, your binary will be yourchain instead of evmd. All CLI commands will use this new name (e.g., yourchain start instead of evmd start).

2. Cosmos Chain ID

What It Is: The unique identifier for your blockchain in the Cosmos ecosystem. Used for IBC connections and governance. Default: cosmos_262144-1 Format: {name}_{evmChainID}-{version}

How to Change

Set during chain initialization:
yourchain init mynode --chain-id yourchain_262144-1
Recommended Formats:
  • Testnet: yourchain_262144-1
  • Mainnet: yourchain-1 (after registering a unique EVM chain ID)
The Cosmos chain ID is separate from the EVM chain ID. The format name_evmChainID-version helps maintain consistency between both IDs, but it’s not required.

Changing After Genesis

While technically possible through a coordinated upgrade, changing the Cosmos chain ID requires all nodes to update and restart simultaneously. Plan your chain ID carefully before launch.

3. EVM Chain ID

What It Is: The EIP-155 chain ID used for Ethereum transaction replay protection. Default: 262144 Where Configured: Retrieved from app options in app.go

How to Change

The EVM chain ID cannot be changed after genesis without breaking transaction replay protection. Choose carefully before launch.
The EVM chain ID is retrieved from app options in app.go:
// evmd/app.go line ~216
evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
You can set it via CLI flag when starting the chain:
yourchain start --evm.chain-id 262144
Or configure it in app.toml:
[evm]
chain-id = 262144

Choosing an EVM Chain ID

Avoid these ranges:
  • 1 - Ethereum Mainnet
  • 2-10 - Ethereum testnets
  • 1-999 - Generally reserved by Ethereum Foundation
  • 137 - Polygon
  • Common production chains - Check chainlist.org
For local development and testnets:
  • Use 262144 (evmd default)
  • Or any high number unlikely to conflict
  • No registration needed
For mainnet launches:
  • Choose a unique ID > 1,000,000
  • Register at chainlist.org
  • Verify no conflicts with existing chains
  • Document your choice publicly

4. Custom Opcodes

What It Is: Custom EVM operations beyond standard Ethereum opcodes. Default: None
Custom opcodes are an advanced feature. Most chains should use standard Ethereum opcodes with the optional extra_eips parameter instead.

Adding Extra EIPs

Instead of custom opcodes, enable additional Ethereum Improvement Proposals:
{
  "app_state": {
    "vm": {
      "params": {
        "extra_eips": [2200, 2929, 3198]
      }
    }
  }
}
Common EIPs:
  • 2200 - Net gas metering
  • 2929 - Gas cost increases for state access
  • 3198 - BASEFEE opcode
  • 3529 - Reduction in refunds
For teams that need custom EVM functionality:
  1. Create activator functions in your chain’s eips/ directory
  2. Register in the activators map
  3. Add to extra_eips in genesis
// yourchain/eips/custom.go
func ActivateMyCustomOpcode(jt *vm.JumpTable) {
    // Define custom opcode behavior
}

// Register in activators
var CustomActivators = map[int]func(*vm.JumpTable){
    9999: ActivateMyCustomOpcode,
}
Then in genesis:
{
  "extra_eips": [9999]
}
This is rarely needed - consider if standard EVM functionality meets your needs first.

5. Token Pairs (ERC20 Module)

What It Is: Mappings between Cosmos native tokens and ERC20 contract representations. Default: 1 pair for the native token

How to Configure

For comprehensive ERC20 module configuration, see the ERC20 Module Documentation.
Token pairs are configured in genesis under the erc20 module:
{
  "app_state": {
    "erc20": {
      "native_precompiles": [
        "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
      ],
      "token_pairs": [
        {
          "erc20_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
          "denom": "atest",
          "enabled": true,
          "contract_owner": 1
        }
      ]
    }
  }
}

Default Native Token Pair

The evmd example uses a special ERC20 address for the native token:
  • Address: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
  • Denom: atest (or your chain’s native denom)
  • Purpose: Allows native tokens to be used in EVM contracts as ERC20
Default Setup: Most chains start with just the native token pair. Additional pairs can be registered after launch through governance or permissionless registration (if enabled).

6. Token Denomination

What It Is: Your chain’s native token denomination and decimal precision. Default: atest (18 decimals, atto-prefix)

Understanding Token Configuration

Token configuration involves three components:
  1. Bank Metadata - Defines denominations and decimals
  2. VM Parameters - Specifies which denom is the EVM native token
  3. Extended Denom Options - Required only for non-18 decimal tokens

Common Denomination Prefixes

PrefixDecimalsExampleUsed For
a (atto)18atokenEVM-compatible chains (recommended)
u (micro)6uatom, ustakeTraditional Cosmos chains
Base0-8satsBitcoin-style
Recommendation: Use 18 decimals (atto prefix) for new chains. This provides:
  • Direct EVM compatibility
  • Simpler codebase (no precisebank needed)
  • Better UX for Ethereum users
  • Standard across EVM ecosystems

Setting Denomination in Genesis

The denomination is set in multiple places that must be consistent:
# After initializing your chain
GENESIS=~/.yourchain/config/genesis.json

# Set denomination in all modules
jq '.app_state["staking"]["params"]["bond_denom"]="atoken"' $GENESIS > tmp && mv tmp $GENESIS
jq '.app_state["gov"]["params"]["min_deposit"][0]["denom"]="atoken"' $GENESIS > tmp && mv tmp $GENESIS
jq '.app_state["vm"]["params"]["evm_denom"]="atoken"' $GENESIS > tmp && mv tmp $GENESIS
jq '.app_state["mint"]["params"]["mint_denom"]="atoken"' $GENESIS > tmp && mv tmp $GENESIS

# Set bank metadata
jq '.app_state["bank"]["denom_metadata"]=[{
  "description": "The native staking token",
  "denom_units": [
    {"denom": "atoken", "exponent": 0, "aliases": ["attotoken"]},
    {"denom": "token", "exponent": 18, "aliases": []}
  ],
  "base": "atoken",
  "display": "token",
  "name": "Token",
  "symbol": "TOKEN"
}]' $GENESIS > tmp && mv tmp $GENESIS
Critical: The following must all use the same base denomination:
  • staking.params.bond_denom
  • mint.params.mint_denom
  • gov.params.min_deposit[0].denom
  • vm.params.evm_denom
  • bank.denom_metadata[0].base

7. EVM Permissioning

What It Is: Access control for deploying and calling smart contracts. Default: Permissionless (anyone can deploy and call contracts)

How to Configure

EVM permissions are set in genesis under vm.params.access_control:
  • Permissionless (Default)
  • Permissioned Deployment
  • Restricted (Blocklist)
  • Fully Closed
{
  "app_state": {
    "vm": {
      "params": {
        "access_control": {
          "create": {
            "access_type": 0,  // 0 = PERMISSIONLESS
            "access_control_list": []
          },
          "call": {
            "access_type": 0,
            "access_control_list": []
          }
        }
      }
    }
  }
}
Use Case: Public chains, testnets, open ecosystems

Access Control Types

TypeValueBehavioraccess_control_list
Permissionless0Anyone can perform actionIgnored
Restricted1Block addresses in listBlocklist
Permissioned2Only addresses in listAllowlist
Recommendation: Start with permissionless for testnets. For mainnets, consider permissioned deployment initially, then transition to permissionless through governance once the chain is stable.

Modifying After Genesis

Access control can be changed through governance proposals:
# Example: Enable permissionless deployment via governance
yourchain tx gov submit-proposal param-change proposal.json \
  --from validator \
  --chain-id yourchain-1

8. Enabled Precompiles

What It Is: Native Cosmos SDK functionality exposed as EVM smart contracts. Default: All 9 static precompiles enabled

Available Precompiles

For complete precompile documentation including Solidity interfaces and usage examples, see the Precompiles Overview.
AddressNameDescriptionTypical Use Case
0x0100P256P256 cryptographic operationsWeb3 auth, secure signing
0x0400Bech32Cosmos ↔ Ethereum address conversionCross-chain operations
0x0800StakingValidator staking operationsLiquid staking, auto-compounding
0x0801DistributionReward distribution queriesReward dashboards, auto-claiming
0x0802ICS20IBC token transfersCross-chain DeFi
0x0803VestingVesting account managementToken vesting contracts
0x0804BankNative token transfersPayment contracts
0x0805GovernanceOn-chain votingDAO integration
0x0806SlashingValidator slashing infoValidator monitoring

How to Configure

  • Enable All (evmd default)
  • Enable Specific Precompiles
  • Disable All
  • In app.go
{
  "app_state": {
    "vm": {
      "params": {
        "active_static_precompiles": [
          "0x0000000000000000000000000000000000000100",
          "0x0000000000000000000000000000000000000400",
          "0x0000000000000000000000000000000000000800",
          "0x0000000000000000000000000000000000000801",
          "0x0000000000000000000000000000000000000802",
          "0x0000000000000000000000000000000000000803",
          "0x0000000000000000000000000000000000000804",
          "0x0000000000000000000000000000000000000805",
          "0x0000000000000000000000000000000000000806"
        ]
      }
    }
  }
}
Use Case: Maximum Cosmos SDK integration, full-featured chains
Security Consideration: Only enable precompiles your chain actually needs. Each enabled precompile increases the attack surface and testing complexity.

Modifying After Genesis

Precompiles can be enabled or disabled through governance:
# Propose adding a precompile
yourchain tx gov submit-proposal param-change proposal.json \
  --from validator

Configuration Workflow

Here’s the recommended order for configuring your chain:
1

Plan Your Configuration

Decide on:
  • Binary name
  • Chain IDs (Cosmos and EVM)
  • Token denomination and decimals
  • Precompiles needed
  • Access control policy
2

Rename and Rebrand

# Rename binary
mv evmd yourchain

# Update references
find . -type f -name "*.go" -exec sed -i 's/evmd/yourchain/g' {} \;

# Update go.mod
# Edit module path
3

Initialize Chain

yourchain init mynode --chain-id yourchain-1
4

Configure Genesis

Edit ~/.yourchain/config/genesis.json:
GENESIS=~/.yourchain/config/genesis.json

# Set denominations
jq '.app_state.staking.params.bond_denom="atoken"' $GENESIS > tmp && mv tmp $GENESIS
jq '.app_state.vm.params.evm_denom="atoken"' $GENESIS > tmp && mv tmp $GENESIS

# Set bank metadata
# Set precompiles
# Set access control
5

Configure app.toml

Edit ~/.yourchain/config/app.toml:
[evm]
chain-id = 262144  # Set EVM chain ID
min-tip = 1000000000

[json-rpc]
enable = true
address = "0.0.0.0:8545"
6

Test Locally

yourchain start
Verify:
  • Chain starts successfully
  • RPC is accessible
  • Precompiles are enabled
  • Token denomination is correct
7

Deploy

For production:
  • Coordinate genesis with validators
  • Distribute final genesis file
  • Launch at coordinated time

Configuration Examples

Reference: local_node.sh

Looking for a real-world example? The local_node.sh script in the Cosmos EVM repository is the best reference for complete chain configuration. It demonstrates how evmd sets up genesis and app.toml for local development.
The local_node.sh script performs all the configuration steps automatically. Here’s what it does:
The script modifies genesis.json to configure:
  • Chain ID: Sets to cosmos_262144-1
  • Token denomination: Configures atest (18 decimals) across all modules
  • Bank metadata: Sets up denom metadata with proper exponents
  • Precompiles: Enables all 9 static precompiles by default
  • Validator configuration: Creates genesis validator with initial stake
  • Genesis accounts: Funds test accounts for development
The script configures app.toml with development-friendly settings:
[evm]
chain-id = 262144
tracer = "json"  # Enable EVM tracing for debugging
min-tip = 0      # No minimum tip for local testing

[json-rpc]
enable = true
address = "0.0.0.0:8545"
ws-address = "0.0.0.0:8546"
api = ["eth", "net", "web3", "txpool", "debug"]  # All APIs for testing
allow-unprotected-txs = true                      # Allow unsigned txs locally
enable-indexer = true                             # Enable transaction indexing
These settings are optimized for local development and should be adjusted for production.
The local_node.sh script accepts flags that control configuration:
  • -y: Overwrites previous database (fresh genesis)
  • -n: Preserves previous database (resume)
  • --no-install: Skips binary rebuild
  • --remote-debugging: Builds with debug symbols
Example usage:
# Fresh start with all default configurations
./local_node.sh -y

# Resume from previous state
./local_node.sh -n
To use local_node.sh as a template for your chain:
  1. Copy the script: cp local_node.sh my_chain_setup.sh
  2. Update chain name: Change evmd to your binary name
  3. Modify genesis values: Update denom, chain ID, precompiles
  4. Adjust app.toml settings: Configure for your use case (testnet/mainnet)
  5. Add custom logic: Include any chain-specific setup
The script serves as a complete example of the configuration workflow described in this guide.
Production Considerations: The local_node.sh configuration is optimized for local development. For testnets and mainnets:
  • Disable debug APIs in JSON-RPC
  • Require transaction signatures (allow-unprotected-txs = false)
  • Set appropriate min-tip value
  • Use secure key management (not test keys)
  • Configure proper peer discovery and networking

Complete 18-Decimal Chain

#!/bin/bash
# Initialize and configure an 18-decimal chain

BINARY="yourchain"
CHAIN_ID="yourchain-1"
EVM_CHAIN_ID="262144"
DENOM="atoken"
GENESIS=~/.yourchain/config/genesis.json

# Initialize
$BINARY init mynode --chain-id $CHAIN_ID

# Configure denominations
jq ".app_state.staking.params.bond_denom=\"$DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.mint.params.mint_denom=\"$DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.gov.params.min_deposit[0].denom=\"$DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.vm.params.evm_denom=\"$DENOM\"" $GENESIS > tmp && mv tmp $GENESIS

# Set bank metadata (18 decimals)
jq ".app_state.bank.denom_metadata=[{
  \"description\": \"The native staking token\",
  \"denom_units\": [
    {\"denom\": \"$DENOM\", \"exponent\": 0, \"aliases\": [\"attotoken\"]},
    {\"denom\": \"token\", \"exponent\": 18, \"aliases\": []}
  ],
  \"base\": \"$DENOM\",
  \"display\": \"token\",
  \"name\": \"Token\",
  \"symbol\": \"TOKEN\"
}]" $GENESIS > tmp && mv tmp $GENESIS

# Enable specific precompiles
jq '.app_state.vm.params.active_static_precompiles=[
  "0x0000000000000000000000000000000000000100",
  "0x0000000000000000000000000000000000000400",
  "0x0000000000000000000000000000000000000800",
  "0x0000000000000000000000000000000000000804"
]' $GENESIS > tmp && mv tmp $GENESIS

echo "Configuration complete!"

Complete 6-Decimal Chain with PreciseBank

#!/bin/bash
# Initialize and configure a 6-decimal chain

BINARY="yourchain"
CHAIN_ID="yourchain-1"
EVM_CHAIN_ID="262144"
BASE_DENOM="utoken"
EXTENDED_DENOM="atoken"
GENESIS=~/.yourchain/config/genesis.json

# Initialize
$BINARY init mynode --chain-id $CHAIN_ID

# Configure denominations
jq ".app_state.staking.params.bond_denom=\"$BASE_DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.mint.params.mint_denom=\"$BASE_DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.gov.params.min_deposit[0].denom=\"$BASE_DENOM\"" $GENESIS > tmp && mv tmp $GENESIS
jq ".app_state.vm.params.evm_denom=\"$BASE_DENOM\"" $GENESIS > tmp && mv tmp $GENESIS

# Set bank metadata (6 decimals)
jq ".app_state.bank.denom_metadata=[{
  \"description\": \"The native staking token\",
  \"denom_units\": [
    {\"denom\": \"$BASE_DENOM\", \"exponent\": 0},
    {\"denom\": \"token\", \"exponent\": 6}
  ],
  \"base\": \"$BASE_DENOM\",
  \"display\": \"token\",
  \"name\": \"Token\",
  \"symbol\": \"TOKEN\"
}]" $GENESIS > tmp && mv tmp $GENESIS

# Set extended denom options (REQUIRED for 6 decimals)
jq ".app_state.vm.params.extended_denom_options={
  \"extended_denom\": \"$EXTENDED_DENOM\"
}" $GENESIS > tmp && mv tmp $GENESIS

echo "Configuration complete! Remember to add precisebank module to app.go"

Next Steps

Now that your chain is configured:
  1. Test Locally: Use ./local_node.sh to test all configurations
  2. Deploy Contracts: Verify precompiles work as expected
  3. Set Up Validators: Prepare for testnet/mainnet launch
  4. Document Configuration: Share configuration choices with users
  5. Plan Governance: Determine which parameters may change post-launch

Further Reading


For additional support, visit the Cosmos EVM GitHub repository or join the Cosmos developer community.