Skip to main content
Predeployed contracts (also called preinstalls) are EVM contracts that exist in chain state at a specific address from genesis. Because the address is fixed and known in advance, the same contract can exist at the same address across every chain that includes it, making them useful for infrastructure that needs to be reliably reachable everywhere.

Default contracts

Cosmos EVM includes five default preinstalls (x/vm/types/preinstall.go):

Enabling at genesis

Preinstalls are set in the app_state.evm.preinstalls array of genesis.json. How you populate that array depends on how you generate genesis for your chain.
  • Using local_node.sh: evmd init does not populate preinstalls automatically. you will need to update local_node.sh to write the default preinstalls into genesis.json before the node starts. Each entry requires the contract name, address, and compiled bytecode:
    local_node.sh
    Bytecodes are defined in x/vm/types/preinstall.go.
  • Using programmatic genesis (e.g., evmd testnet): Preinstalls are set in NewEVMGenesisState() in evmd/genesis.go. The reference chain already sets evmGenState.Preinstalls = evmtypes.DefaultPreinstalls there, so all five defaults are included automatically.
  • Custom genesis generation: Include the preinstalls array directly in your genesis.json under app_state.evm, with each entry specifying name, address, and hex-encoded code. Example:
    genesis.json

Add custom contracts

  1. To deploy a contract beyond the defaults, open evmd/genesis.go and update NewEVMGenesisState. The example below uses a minimal 10-byte contract (0x600160005260206000f3) that returns 1 on any call — replace the name, address, and code with your own:
evmd/genesis.go
  1. Add a jq patch to local_node.sh after the last genesis customization line (.consensus.params.block.max_gas), before the # Change proposal periods comment. Use += to append without overwriting any previously set preinstalls:
local_node.sh
Requirements for a valid preinstall:
  • Valid Ethereum address (0x prefix, 40 hex characters)
  • Must not conflict with existing contracts or precompile addresses (0x1–0x9FF)
  • Non-empty, valid EVM bytecode (hex encoded)
  1. Rebuild the binary and start the chain:
The -y flag wipes any existing chain data and reinitializes from genesis, which is required for the preinstall to take effect.
  1. Once the chain is running, open a new terminal and confirm the contract is installed by running the following commands:
Expected output for code:
The value is the contract bytecode base64-encoded. This decodes to 0x600160005260206000f3, which is the bytecode set in genesis.go and local_node.sh. A non-empty value confirms the code was written to state. Expected output for account:
  • code_hash: a non-empty hash confirms the contract exists at this address
  • balance: "0": preinstalls are deployed with no native token balance, which is expected
  • nonce: "0": preinstalls are not deployed via a transaction, so the nonce starts at 0

Add contracts after launch

The following methods are examples of ways to deploy predeployed contracts after the chain is running.

Deploy via governance proposal

Use MsgRegisterPreinstalls to deploy contracts on a running chain via governance:
proposal.json

Deploy via chain upgrade handler

Include preinstalls in a coordinated chain upgrade:
app/upgrades/v2/upgrades.go