The GMP application
A GMP call travels as a packet carrying the call data to the other chain. A GMP application is registered on a chain’s router under a single port, and a chain generally runs one GMP contract per IBC deployment. In IBC-solidity, GMP is the ICS27GMP contract, which implements the ics-027-gmp specification on the portgmpport.
Sending a call
A sender starts a call with one message that names where the call goes, what to run there, and how long it may take. In IBC-solidity, that message is aSendCallMsg:
sourceClient: the local client identifier to send over.receiver: the target contract’s address, as a string.salt: bytes that pick which of the sender’s destination accounts makes the call.payload: the call data for the target, encoded the way the destination chain’s GMP implementation expects.timeoutTimestamp: an absolute time, in unix seconds. The timeout must be in the future.memo: an optional string carried with the call.
application/x-solidity-abi.
The destination account
GMP deploys an account on the destination chain that makes the call on the sender’s behalf. The target sees that account as its caller, so the address it reads maps to one sender, on one client, under one salt:
Executing the call on the destination
Once the packet reaches the destination chain, the router hands it to that chain’s GMP application, which turns it into a call:- Checks the packet’s version, encoding, and both port identifiers against its own constants.
- Confirms the payload carries call data.
- Builds the account identifier from the destination client identifier plus the packet’s sender and salt, and gets or deploys that account.
- Reads the target’s address out of the receiver string.
- Asks the account to call the target with the payload as call data.
- Wraps whatever the target returned as the acknowledgement.
Acknowledgements and callbacks
The outcome comes back as an acknowledgement. A relayer submits it with a proof back to the source chain, and the source chain router verifies it with the light client before clearing the packet’s commitment. The source chain GMP application then reads the outcome from the acknowledgement bytes, comparing them against the one value that means failure. A sending application hears an outcome only if it implements the sender callbacks. In IBC-solidity, that means two functions plus the ERC165 interface detection:success argument. Its message carries what an application needs to act on that outcome: the packet’s sequence number, the payload it originally sent, and the acknowledgement bytes. It also carries both client identifiers and the relayer’s address. The timeout callback takes no success argument, and its message is the same one without the acknowledgement. ICS27GMP and accounts describes both callback structs.
An application matches a callback to the call it made by client and sequence number together. Sequences are counted per client, so a sender that sends over two clients can hold two packets with the same sequence.
What comes back
Each ending hands the sender something different. The callbacks reach only a sender that implements them:- Success: an acknowledgement carrying the target’s return data, which the sender decodes to read the result.
- Failure: a fixed error acknowledgement, and the acknowledgement callback runs with success false.
- Timeout: no acknowledgement at all, and the timeout callback runs instead.
IBCAppRecvPacketCallbackError event, so finding out why means reading that chain’s logs.
The account forwards the destination call with all remaining gas and turns an empty revert into a FailedCall error, so the router writes the error acknowledgement and the packet is consumed. A target that most likely ran out of gas, one whose empty revert leaves the account less than an eighth of the gas it had before the call, is the exception: the account reverts without data, the router rejects the receive outright, and the packet stays in flight, so a relayer can retry it with more gas. Packets carry no gas limit, so a relayer can still starve a target that catches running out of gas itself into an error acknowledgement.
A packet that is never received ends in a timeout. Once the deadline passes, a relayer proves non-receipt on the source chain, and GMP fires the timeout callback if the sender implements it. A packet cannot be both executed and timed out, because the destination refuses a packet whose deadline has passed.
GMP keeps no per-packet state, so a failure or a timeout leaves it nothing to undo. Reversing anything is the sender’s job, which is why IFT keeps a pending record and refunds from it.
Build on GMP
Any contract can use GMP to act across chains. IFT is an example of a contract built on GMP: one token contract is the sender on its own chain and the target on the other, and it moves tokens with nothing but GMP calls. What GMP gives you is an authenticated caller on the destination chain, execution at most once, and an outcome a relayer can carry back to the source chain. What you write is the target contract, plus the two callback functions and ERC165 interface detection if you want to hear that outcome. Two things belong to the contract you build:- Authorization: GMP proves which contract is calling, and your target decides whether to accept it. IFT accepts a mint only from a counterparty it has registered.
- State: It is important to implement logic and state for failures and refunds. For example, IFT keeps a pending record of what it burned, and refunds from that record when a call fails or times out.