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: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.
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:
lib/ibc.sh.
What it does
The script uses the committed forge workspace atibc/forge/ and downloads prebuilt contract bytecode from the cosmos/solidity-ibc-eureka release bundle. Then the following runs inside a Foundry container:
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 byMinimalDeploy.s.sol in order. Each contract’s constructor or initializer depends on addresses from the steps before it.
| Step | Source | Purpose | Notes |
|---|---|---|---|
1. AccessManager | OpenZeppelin AccessManager | Role-based access control for the IBC contract suite | Deployer becomes the ADMIN_ROLE holder |
2. ICS26Router (ERC1967 proxy) | contracts/ICS26Router.sol | IBC packet hub: validates packet sequencing and timeouts, routes packets to app contracts | Initialized with the AccessManager address |
3. ICS27Account | contracts/utils/ICS27Account.sol | CREATE2 proxy account used by ICS27GMP to derive and hold a client-derived account address | No constructor arguments |
4. ICS27GMP (ERC1967 proxy) | contracts/ICS27GMP.sol | ICS-27 General Message Passing app: parses GMP payloads and dispatches calls to target contracts | Initialized with the ICS26Router, ICS27Account, and AccessManager addresses |
5. ICS26Router.addIBCApp | - | Registers ICS27GMP as the handler for gmpport | Must be called before any packets can be routed to GMP |
6. IFTOwnable (ERC-20, ERC1967 proxy) | cosmos/solidity-ibc-eureka | IFT token: iftTransfer burns tokens and emits an IBC packet; iftMint mints on packet receive | Initialized with the deployer address, token name/symbol, and the ICS27GMP address |
| Contract | Source | Deployed in | Role |
|---|---|---|---|
AttestationLightClient | contracts/light-clients/attestation/AttestationLightClient.sol | create-clients | Verifies Cosmos packet state via m-of-n attestor ECDSA signatures; registered with ICS26Router.addClient |
CosmosIFTSendCallConstructor | contracts/utils/CosmosIFTSendCallConstructor.sol | wire | Encodes 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 toIFTOwnableat deploy timeIFTOwnable: registered in thewirestep as the EVM counterparty for the Cosmosuiftdenom
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), useEVMIFTSendCallConstructor 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:AccessManagerfirst: bothICS26RouterandICS27GMPtake its address ininitializeICS27AccountbeforeICS27GMP: GMP’s initializer takes the account implementation addressIFTOwnablelast: initialized with theICS27GMPaddress
Access control
TheAccessManager 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 deploysIFTOwnable, 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.