Default contracts
Cosmos EVM includes five default preinstalls (x/vm/types/preinstall.go):
| Contract | Address | Purpose | Docs |
|---|---|---|---|
| Create2 | 0x4e59b44847b379578588920ca78fbf26c0b4956c | Deterministic contract deployment using CREATE2 | EIP-1014 |
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 | Batch multiple contract calls in one transaction | Repo · Site |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Signature-based token approvals for any ERC20 | Repo · Docs |
| Safe Singleton Factory | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 | Deploy Safe multisig wallets at deterministic addresses | Repo · Docs |
| EIP-2935 | 0x0000F90827F1C53a10cb7A02335B175320002935 | Historical block hash storage | EIP-2935 |
Enabling at genesis
Preinstalls are set in theapp_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 initdoes not populate preinstalls automatically. you will need to updatelocal_node.shto write the default preinstalls intogenesis.jsonbefore the node starts. Each entry requires the contract name, address, and compiled bytecode:Bytecodes are defined inlocal_node.shx/vm/types/preinstall.go. -
Using programmatic genesis (e.g.,
evmd testnet): Preinstalls are set inNewEVMGenesisState()inevmd/genesis.go. The reference chain already setsevmGenState.Preinstalls = evmtypes.DefaultPreinstallsthere, so all five defaults are included automatically. -
Custom genesis generation: Include the
preinstallsarray directly in yourgenesis.jsonunderapp_state.evm, with each entry specifyingname,address, and hex-encodedcode. Example:genesis.json
Add custom contracts
- To deploy a contract beyond the defaults, open
evmd/genesis.goand updateNewEVMGenesisState. The example below uses a minimal 10-byte contract (0x600160005260206000f3) that returns1on any call — replace the name, address, and code with your own:
evmd/genesis.go
- Add a
jqpatch tolocal_node.shafter the last genesis customization line (.consensus.params.block.max_gas), before the# Change proposal periodscomment. Use+=to append without overwriting any previously set preinstalls:
local_node.sh
- 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)
- Rebuild the binary and start the chain:
-y flag wipes any existing chain data and reinitializes from genesis, which is required for the preinstall to take effect.
- Once the chain is running, open a new terminal and confirm the contract is installed by running the following commands:
code:
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 addressbalance: "0": preinstalls are deployed with no native token balance, which is expectednonce: "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
UseMsgRegisterPreinstalls 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