Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt

Use this file to discover all available pages before exploring further.

The IBC stack on the EVM side is a set of Solidity contracts deployed to the Besu chain in the demo. This step deploys the core contracts (the IBC router, the GMP application, and the IFT token) and registers the GMP port on the router so it can route packets. Two additional contracts are deployed in later steps: AttestationLightClient in the create-clients step once the attestor address is known, and CosmosIFTSendCallConstructor in wire once the client-derived account address is computed. This command runs the deployment:
./setup.sh deploy
The logic for this command is in lib/ibc.sh.
The chain used in this demo illustrates how to use IBC modules on the Cosmos side, but you can also use the Solidity deployment on Cosmos chains with EVM capabilities.If you are operating a Cosmos EVM chain and need to access IBC through the EVM, you can deploy the same Solidity contracts in this guide to your Cosmos EVM chain.

What it does

The script uses the committed forge workspace at ibc/forge/ and downloads prebuilt contract bytecode from the cosmos/solidity-ibc-eureka release bundle. Then the following runs inside a Foundry container:
forge script scripts/MinimalDeploy.s.sol \
  --rpc-url <YOUR_EVM_RPC_URL> \
  --private-key <DEPLOYER_PRIVATE_KEY> \
  --broadcast \
  --chain-id <YOUR_CHAIN_ID>
The deployer address is both the AccessManager admin and the relayer EOA. OpenZeppelin AccessManager defaults unset restricted functions to ADMIN_ROLE, which the deployer holds, so addIBCApp, recvPacket, ackPacket, and timeoutPacket all work without additional role grants. This is sufficient for a single-validator devnet; production deployments need explicit role wiring (see Access control below). See the table below for the full deploy order and constructor arguments.

Deployed Contracts

The following steps are run by MinimalDeploy.s.sol in order. Each contract’s constructor or initializer depends on addresses from the steps before it.
StepSourcePurposeNotes
1. AccessManagerOpenZeppelin AccessManagerRole-based access control for the IBC contract suiteDeployer becomes the ADMIN_ROLE holder
2. ICS26Router (ERC1967 proxy)contracts/ICS26Router.solIBC packet hub: validates packet sequencing and timeouts, routes packets to app contractsInitialized with the AccessManager address
3. ICS27Accountcontracts/utils/ICS27Account.solCREATE2 proxy account used by ICS27GMP to derive and hold a client-derived account addressNo constructor arguments
4. ICS27GMP (ERC1967 proxy)contracts/ICS27GMP.solICS-27 General Message Passing app: parses GMP payloads and dispatches calls to target contractsInitialized with the ICS26Router, ICS27Account, and AccessManager addresses
5. ICS26Router.addIBCApp-Registers ICS27GMP as the handler for gmpportMust be called before any packets can be routed to GMP
6. IFTOwnable (ERC-20, ERC1967 proxy)cosmos/solidity-ibc-eurekaIFT token: iftTransfer burns tokens and emits an IBC packet; iftMint mints on packet receiveInitialized with the deployer address, token name/symbol, and the ICS27GMP address
Two more contracts are deployed in later steps:
ContractSourceDeployed inRole
AttestationLightClientcontracts/light-clients/attestation/AttestationLightClient.solcreate-clientsVerifies Cosmos packet state via m-of-n attestor ECDSA signatures; registered with ICS26Router.addClient
CosmosIFTSendCallConstructorcontracts/utils/CosmosIFTSendCallConstructor.solwireEncodes the MsgIFTMint GMP payload for EVM-to-Cosmos transfers

Resolving the IFT Contract Address

After the Forge script completes, the script reads the broadcast artifacts (broadcast/<script>/<chain-id>/run-latest.json) to extract the deployed IFTOwnable proxy address and persists it as IFT_CONTRACT_ADDR in ibc/state.env. This address is used in the wire step to register the EVM contract as the counterparty for the Cosmos-side uift denom.

Deployed Address Usage

The three core addresses produced by this step are used throughout the rest of the setup:
  • ICS26Router: referenced by the attestor config (router_address), relayer config (ics_26_router_address), and proof API config (ics26_address)
  • ICS27GMP: passed as an initializer argument to IFTOwnable at deploy time
  • IFTOwnable: registered in the wire step as the EVM counterparty for the Cosmos uift denom

Applying this to your own chain

IBC Solidity Contracts

For a complete list of the IBC Solidity contracts, see the cosmos/solidity-ibc-eureka repository.

EVM Deployments

The chain used in this demo illustrates how to use IBC modules on the Cosmos side, but you can also use the Solidity deployment on Cosmos chains with EVM capabilities. Deploying IBC Solidity contracts to a Cosmos EVM chain is no different than deploying to any other EVM chain. If you are connecting two Solidity deployments (EVM on both sides), use EVMIFTSendCallConstructor instead of CosmosIFTSendCallConstructor when constructing IFT send calls.

Deploy order

The order is fixed because each contract’s initializer takes addresses from contracts deployed before it. The main constraints are:
  • AccessManager first: both ICS26Router and ICS27GMP take its address in initialize
  • ICS27Account before ICS27GMP: GMP’s initializer takes the account implementation address
  • IFTOwnable last: initialized with the ICS27GMP address

Access control

The AccessManager is initialized with the deployer address as admin. The admin can grant and revoke roles and configure which addresses are permitted to call restricted functions on ICS26Router and ICS27GMP. Use a secure address for this role. See the OpenZeppelin Access Control documentation for guidance on managing roles and permissions. For permissioned relayers, the relayer address should hold a role that covers only recvPacket, ackPacket, and timeoutPacket on ICS26Router.

IFT contract

The demo deploys IFTOwnable, which uses OwnableUpgradeable for access control: a single owner address controls all privileged functions. IFTAccessManaged provides more fine-grained control, allowing each function to be restricted to a different role. For a fully custom implementation, extend IFTBaseUpgradeable and implement _onlyAuthority() with your own logic. The full interface is defined in IIFT.sol.

Next steps

With the contracts deployed, the next step is to create the attestation light clients on both chains and register their counterparties.