Docs
IntegrationsEVM

Transfers

Send an interchain token to another chain, from ITS or from the token contract.

A transfer takes the tokens on the source chain, sends a message through the ITS Hub, and gives the tokens to the recipient on the destination chain. How the tokens are taken and given depends on the token manager's type: burned and minted, or locked and unlocked. See Token managers.

For setup and the variables used below, see the EVM overview.

From ITS

Call interchainTransfer on ITS. It works for every token connected to ITS:

function interchainTransfer(
    bytes32 tokenId,
    string calldata destinationChain,
    bytes calldata destinationAddress,
    uint256 amount,
    bytes calldata metadata, // empty for a plain transfer
    uint256 gasValue
) external payable;
# Send 25 tokens to your own address on BSC
cast send $ITS "interchainTransfer(bytes32,string,bytes,uint256,bytes,uint256)" \
  $TOKEN_ID binance $ME 25000000000000000000 0x $FEE --value $FEE \
  --rpc-url $RPC --private-key $PK
  • destinationAddress is the recipient, as bytes in the destination chain's format; see Recipient addresses.
  • amount is in the token's smallest unit on this chain. The hub converts it if the token has different decimals on the destination chain.
  • metadata is empty for a plain transfer. To call a contract with the tokens, see Transfers with data.
  • gasValue pays for the message. Send the same amount as the transaction value; see Gas.

For lock and unlock tokens, and MINT_BURN_FROM tokens, approve ITS to take amount first. Interchain tokens and MINT_BURN tokens need no approval. See the token contract.

On chains running ITS v2.2.0, a shorter form without metadata and gasValue also works, with the gas paid from msg.value: interchainTransfer(tokenId, destinationChain, destinationAddress, amount). See Versions.

Here's a transfer on Axelarscan.

From the token

Interchain tokens, and custom tokens that inherit InterchainTokenStandard, have their own interchainTransfer, which needs no token ID. Gas is paid from msg.value:

function interchainTransfer(
    string calldata destinationChain,
    bytes calldata recipient,
    uint256 amount,
    bytes calldata metadata
) external payable; // on the token
cast send $TOKEN "interchainTransfer(string,bytes,uint256,bytes)" \
  binance $ME 25000000000000000000 0x --value $FEE \
  --rpc-url $RPC --private-key $PK

Interchain tokens need no approval. A custom token with a lock and unlock type still needs an approval for ITS.

interchainTransferFrom(sender, destinationChain, recipient, amount, metadata) sends tokens from another account that approved you, like transferFrom.

Recipient addresses

destinationAddress is bytes, encoded for the destination chain:

DestinationEncoding
EVM, SuiThe address, e.g. 0x510e…
SolanaThe 32-byte public key, decoded from base58
StellarThe address string, as ASCII bytes
XRPLThe address string, as ASCII bytes

Axelar's scripts encode it this way: see encodeITSDestination in axelar-contract-deployments.

Flow limits

A token can have a flow limit on each chain. A transfer over the limit reverts on the source chain, or fails on arrival, and can be executed again in a later window.

Track it

On the source chain, ITS emits:

event InterchainTransfer(
    bytes32 indexed tokenId,
    address indexed sourceAddress,
    string destinationChain,
    bytes destinationAddress,
    uint256 amount,
    bytes32 indexed dataHash // 0 for a transfer without data
);

On the destination chain, ITS emits InterchainTransferReceived when it gives the tokens. In between, follow the message on Axelarscan; see Track a message.

Errors

On the source chain:

ErrorCause
UntrustedChainThe destination chain isn't connected to ITS, or its name has the wrong case
TakeTokenFailedThe tokens couldn't be taken: unknown token ID, balance, approval or flow limit. The cause, if any, is in its data
ZeroAmountamount is 0
EmptyDestinationAddressdestinationAddress is empty
PauseITS is paused

On the destination chain, the execution fails with GiveTokenFailed if the tokens can't be given, e.g. over the flow limit or without the right to mint. The message stays approved, and can be executed again.

Next steps

On this page