Overview
IFT (Interchain Fungible Token) is a standard for tokens that can move between chains over IBC v2. Tokens are burned on the source chain and minted on the destination chain. This differs from other escrow-based token standards, which lock tokens in escrow and issue a wrapped representation on the destination chain. IFT enables fungible transfers across chains: the same token exists natively on every chain it is bridged to. IFT is built on ICS-27 GMP. Each transfer is a GMP packet whose payload instructs the destination IFT contract to mint tokens to the receiver. EVM implementation:solidity-ibc-eureka/contracts/utils/IFTBaseUpgradeable.sol
How it works
Connecting a bridge
Before any tokens can move, each IFT contract must be told which chain it can talk to and who to trust on that chain. Two chains deploy IFT and register each other as counterparties. This is done by callingregisterIFTBridge, which stores an IFTBridge record keyed by IBC client ID:
clientId identifies an IBC light client that lives on the chain. A light client tracks the state of a specific remote chain. It receives and verifies state attestations so the local chain can trust that events (like a burn) actually happened on the other side. iftTransfer sends the GMP packet over the IBC client identified by that clientId.
Every bridge can be scoped to a chain pair: a light client and a counterparty IFT contract. To bridge to a second chain, register a second bridge with a different clientId.
Mutual registration
Registration must happen on both sides before transfers succeed. ThecounterpartyIFTAddress field is used by iftMint as an authorization check. When a GMP packet arrives, the IFT contract verifies that the GMP account’s sender matches the registered counterpartyIFTAddress. If the remote chain registers a different address, or hasn’t registered at all, the mint reverts.
In practice, both IFT contracts are deployed and each is registered on the other chain; then transfers can flow in both directions.
Sending (iftTransfer)
- Caller calls
iftTransfer(clientId, receiver, amount, timeoutTimestamp)on the source IFT contract. - The IFT contract burns
amounttokens from the caller. - It calls
ICS27GMP.sendCallwith a mint payload for the counterparty chain, sending a GMP packet over IBC. - On the destination chain, the GMP module routes the packet to the IFT contract via a derived GMP account.
- The IFT contract mints
amounttokens toreceiver.
Receiving (iftMint)
iftMint is called by the GMP account, which is an address on the destination chain derived deterministically from (destinationClientId, senderAddress, salt). Before minting, the IFT contract checks:
- The caller is a known GMP account (via
ICS27GMP.getAccountIdentifier). - The GMP account’s
senderfield matches thecounterpartyIFTAddressregistered for the bridge. - The account’s
saltis empty (IFT only trusts unsalted accounts).
Failure and refunds
Every in-flight transfer is stored as aPendingTransfer. If the packet times out or the destination call returns an error, the IBC callbacks mechanism calls back into the IFT contract, which re-mints the burned tokens to the original sender.
From the sender’s perspective: either tokens arrive on the destination, or they are returned. There is no case where tokens are permanently lost.
Transferring tokens
iftTransfer
The IIFT interface defines two iftTransfer overloads:
clientId: the IBC client ID on the local chain that represents the destination chain. CallgetIFTBridge(clientId)to confirm a bridge is registered for that client.receiver: the address on the destination chain as a string. For EVM counterparties, pass a hex address. TheEVMIFTSendCallConstructorusesStrings.tryParseAddressto parse it into a Solidityaddresstype when encoding the mint call.amount: in the ERC20’s smallest unit (equivalent to wei for 18-decimal tokens).timeoutTimestamp: absolute Unix timestamp in seconds. If the packet is not relayed before this time, it will time out and the sender is refunded.
Events
Checking in-flight transfers
PendingTransfer{sender, amount} if the transfer is still in-flight. Reverts with IFTPendingTransferNotFound if the sequence has no pending transfer (already completed, refunded, or never existed). The sequence is included in the IFTTransferInitiated event.
EVM integration
The IBC demo shows a reference implementation of IFT integration between an EVM and a Cosmos chain. Note that this is a demo and not a production-ready implementation, and the Cosmos IFT module is provided as reference-only. The steps below use the demo as a reference.Contracts
Prerequisites
ICS26RouterandICS27GMPmust already be deployed and wired (see EVM deployment).- An IBC client must be created on the local chain representing the counterparty chain.
- The account performing bridge registration must hold the
authorityrole on the IFT contract (ownerforIFTOwnable).
1. Deploy IFTOwnable
IFTOwnable is the reference EVM implementation: a UUPS upgradeable logic contract extending IFTBaseUpgradeable, with the deployer as owner. It is deployed behind an ERC1967Proxy. From MinimalDeploy.s.sol:
ics27Gmp is the address of the already-deployed ICS27GMP proxy.
2. Deploy a SendCall constructor
The SendCall constructor encodes the mint payload for the counterparty chain. For an EVM counterparty, deploy EVMIFTSendCallConstructor:
3. Register the bridge
CallregisterIFTBridge on the IFT contract (lib/ibc.sh):
clientId: the IBC client ID on the local EVM chain representing the counterparty chain.counterpartyIFTAddress: the address that will appear as the GMP packet’ssenderfrom the counterparty chain. For an EVM counterparty, this is the EIP-55 checksummed address of the counterparty IFT contract.ICS27GMP.sendCallrecords the caller using EIP-55 checksum casing, andiftMintdoes an exact string match against this value.iftSendCallConstructor: the address of the constructor deployed in step 2.
4. Send a transfer
The following command illustrates sending an IFT transfer:lib/demo.sh), $AMOUNT is in the ERC20’s base unit.