Skip to main content

Overview

ICS27-GMP (also called ics27-2) is the IBC v2 protocol for general cross-chain contract execution. A sender on one chain can trigger a function call on a destination chain. The destination chain executes the call through a deterministically derived account. Specification: ICS-027-GMP Implementations: The port used by GMP is gmpport.

How it works

  1. A sender calls sendCall (EVM) or submits MsgSendCall (Cosmos) with a receiver address, a payload, and a salt.
  2. GMP constructs a GMPPacketData packet with the sender’s address, the receiver, the salt, and the payload, then submits it via the IBC router.
  3. On the destination chain, onRecvPacket derives or creates a GMP account for (destinationClientId, sender, salt). This account is the on-chain identity that executes the call.
  4. The GMP account calls receiver with payload.
  5. The return value is returned to the sender as an IBC acknowledgement.
The GMP account is created on first use. For the same (clientId, sender, salt), the account address is always the same and can be computed before the packet is sent.

Data structures

GMPPacketData

This is the IBC packet payload. It is constructed by GMP and carried over IBC. In the Solidity implementation (IICS27GMPMsgs.sol):
In the Go implementation (packet.proto):

AccountIdentifier

Uniquely identifies a GMP account on the destination chain. The derived account address is a deterministic function of these three values. Solidity (IICS27GMPMsgs.sol):
Go (account.proto):

Sending a GMP packet

EVM: sendCall

The primary entry point on the EVM side is ICS27GMP.sendCall:
Where SendCallMsg is (IICS27GMPMsgs.sol):
sendCall captures msg.sender as an EIP-55 checksummed string and builds the packet:
The packet is then sent via ICS26Router.sendPacket with:
  • sourcePort = destPort = "gmpport"
  • version = "ics27-2"
  • encoding = "application/x-solidity-abi"

Cosmos: MsgSendCall

The Cosmos SDK message (tx.proto):
If encoding is empty, it defaults to application/x-solidity-abi (msg_server.go). Validation limits (msgs.go):

GMP account derivation

Each (clientId, sender, salt) maps to a single account on the destination chain. The account is created on first packet receive; however, its address can be computed before any packet is sent.

EVM

The account is a BeaconProxy deployed with Create2 (ICS27GMP.sol):
To compute the address without deploying:
The clientId used here is the destination client ID set in onRecvPacket.

Cosmos

The account address is derived deterministically from the AccountIdentifier fields using a length-prefixed key built from clientId, sender, and salt, then passed to address.Module("gmp-accounts", key). The implementation is in account.go. To query the derived address on a running chain:

Payload encoding

The payload field is opaque bytes interpreted by the destination chain runtime. The following example is from the Cosmos IFT demo tutorial, which uses GMP for transfers between EVM and Cosmos chains. For EVM → Cosmos IFT transfers, CosmosIFTSendCallConstructor builds the payload as a protojson-encoded CosmosTx containing a MsgIFTMint (CosmosIFTSendCallConstructor.sol):
The signer field is the GMP account address (ICA) on the Cosmos chain, which must match the account that onRecvPacket will derive. The EVM sender must be EIP-55 checksummed when querying the ICA.

Supported encodings

Defined in packet.go (application/x-solidity-abi is also a constant in ICS27Lib.sol):

Acknowledgements

onRecvPacket returns the raw return value of the destination call wrapped in a GMPAcknowledgement:
On timeout or error, a universal error acknowledgement is returned. The sender can implement onAcknowledgementPacket and onTimeoutPacket callbacks via the ICS-30 callbacks middleware.

receiver field

receiver is the address of the contract to call on the destination chain. On EVM, onRecvPacket parses it as a Solidity address and calls it via the GMP account:

Demo: IFT transfers using GMP

The IBC demo uses GMP for both directions of IFT token transfers. The demo runs in Docker and uses shell helpers (cast_in_net, run_in) to execute commands inside containers. The commands below show the underlying operations for illustration.

EVM → Cosmos

In the demo, IFTOwnable.iftTransfer burns tokens on the EVM side and sends a GMP packet to mint them on Cosmos. The call chain is:
  1. iftTransfer(clientId, receiver, amount, timeoutTimestamp) calls the internal _iftTransfer
  2. _iftTransfer burns the tokens, then calls CosmosIFTSendCallConstructor.constructMintCall to build the payload, then calls ICS27GMP.sendCall with:
    • sourceClient: the EVM client ID
    • receiver: the Cosmos IFT module account address (the counterpartyIFTAddress registered in registerIFTBridge)
    • salt: empty for this demo
    • payload: protojson-encoded CosmosTx containing MsgIFTMint
  3. On the Cosmos side, GMP derives the account for (destClientId, iftContractAddress, ""), executes the MsgIFTMint message in the payload, and mints tokens to the receiver.
The demo triggers this with (lib/demo.sh):

Cosmos → EVM

tx ift transfer sends a MsgSendCall with an ABI-encoded iftMint(address, uint256) payload. The MsgSendCall.sender is the IFT module account address. On the EVM side, GMP derives the account for (destClientId, iftModuleAddress, "") and calls IFTOwnable.iftMint through it. iftMint verifies that the calling GMP account’s sender field matches the registered counterparty IFT address before minting. The demo triggers this with (lib/demo.sh):

Integrating GMP

Cosmos

An example of the full wiring of the GMP module is in sandbox-ledger PR #1, which adds GMP to an existing Cosmos SDK chain. The steps are:
  1. Add module account permissions in maccPerms:
  1. Register the store key:
  1. Add the keeper to your app struct and initialize it:
  1. Register the IBC v2 route:
To receive acknowledgement and timeout callbacks (required if you’re building an app like IFT on top of GMP), wrap with the callbacks v2 middleware before registering. In the sandbox-ledger, the IFT keeper implements the callback interface and is passed as the third argument:
  1. Register in the module manager:
Also add gmptypes.ModuleName to SetOrderBeginBlockers, SetOrderEndBlockers, and SetOrderInitGenesis.

EVM chain

This deployment is taken from the IBC demo. GMP on the EVM side is two deployment steps. The full script is MinimalDeploy.s.sol.
  1. The ICS27Account and ICS27GMP logic contracts are deployed, then GMP is wrapped in an ERC1967 proxy:
ICS27Account is the logic contract for GMP’s beacon proxy accounts. In the demo, ICS27GMP is wrapped in an ERC1967 proxy and initialized with the router, account logic, and access manager.
  1. The ICS26Router is registered with the GMP contract:
After this call, the router forwards all packets on port gmpport to ICS27GMP.onRecvPacket.