Send Tokens Cross-Chain

There are three ways you can use Axelar to transfer ERC-20 tokens across different blockchains:

  1. Call sendToken() to send a token on any Axelar-supported source chain to a recipient on any Axelar-supported destination chain. This can be done from EVM chains, Cosmos-based chains, or the AxelarJS SDK.
  2. Transfer assets using a deposit address generated with the AxelarJS SDK.
  3. Build your own Interchain Token if your token is not natively supported.

Call sendToken() on an EVM source chain

To send a token from an EVM source chain:

1. Find the source chain’s Gateway contract address

Locate the source chain’s Gateway contract address on either the mainnet or the testnet.

EVM chains use Axelar Gateway smart contracts to send tokens. These are application-layer smart contracts that send and receive payloads as well as monitor transaction state.

All Gateway contracts implement the IAxelarGateway interface, which has a public method called sendToken() that transfers tokens between chains:

function sendToken(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount
) external;

2. Call the source chain’s approve() method

Transferring tokens through a Gateway is similar to doing a typical ERC-20 token transfer. You’ll need to call the source chain’s approve() method (inherited from the ERC-20 interface) to allow the Gateway to transfer a specific token in a specific amount.

function approve(address spender, uint256 amount) external returns (bool);

spender is the source chain’s Gateway contract address on either the mainnet or the testnet.

3. Call sendToken() on the source chain’s Gateway contract

Call sendToken() on the source chain’s Gateway contract to transfer the tokens. For example:

sendToken(
"avalanche", // destination chain name
"0xF16DfB26e1FEc993E085092563ECFAEaDa7eD7fD", // some destination wallet address (should be your own)
"axlUSDC", // asset symbol, can be differ by chain, see above
100000000 // amount (in atomic units)
)

Once you call sendToken(), watch for the tokens to appear at the address on the destination chain.

Call sendToken() on a Cosmos-based source chain

For Cosmos-based source chains, sendToken() is a simple IBC transfer of any asset supported on the Axelar network. The message is sent to the address axelar1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5, which is the designated address on the Axelar network for receiving GMP messages, and includes a memo field with the following payload:

{
destination_chain,
destination_address,
payload: null,
type: 3, // corresponds to the `sendToken` command on Axelar
}

Use the AxelarJS SDK to call sendToken()

The AxelarJS SDK allows any frontend application to call sendToken() with one line of JS code.

EVM chain example

import {
AxelarAssetTransfer,
CHAINS,
Environment,
SendTokenParams,
} from "@axelar-network/axelarjs-sdk";
import { ethers, Wallet } from "ethers";
const api = new AxelarAssetTransfer({ environment: Environment.TESTNET });
const getSigner = () => {
const privateKey = PRIVATE_KEY;
return new Wallet(privateKey);
};
async function test() {
const provider = new ethers.providers.JsonRpcProvider(
"https://api.avax-test.network/ext/bc/C/rpc",
);
const signer = getSigner().connect(provider);
const requestOptions: SendTokenParams = {
fromChain: CHAINS.TESTNET.AVALANCHE,
toChain: CHAINS.TESTNET.OSMOSIS,
destinationAddress: "osmo1x3z2vepjd7fhe30epncxjrk0lehq7xdqe8ltsn",
asset: { symbol: "aUSDC" },
amountInAtomicUnits: "5000000",
options: {
evmOptions: {
signer,
provider,
txOptions: null as any,
approveSendForMe: true,
},
},
};
return api.sendToken(requestOptions);
}

Cosmos-based chain example

import {
AxelarAssetTransfer,
CHAINS,
Environment,
SendTokenParams,
} from "@axelar-network/axelarjs-sdk";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
const api = new AxelarAssetTransfer({ environment: Environment.TESTNET });
const getSigner = async () => {
const mnemonic = MNEMONIC;
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "osmo" });
};
async function test() {
const offlineSigner = await getSigner();
const requestOptions: SendTokenParams = {
fromChain: CHAINS.TESTNET.OSMOSIS,
toChain: CHAINS.TESTNET.AVALANCHE,
destinationAddress: "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
asset: {
denom:
"ibc/6F34E1BD664C36CE49ACC28E60D62559A5F96C4F9A6CCE4FC5A67B2852E24CFE",
}, //aUSDC
amountInAtomicUnits: "1000000",
options: {
cosmosOptions: {
cosmosDirectSigner: offlineSigner,
rpcUrl: "https://rpc.osmotest5.osmosis.zone",
fee: {
gas: "250000",
amount: [{ denom: "uosmo", amount: "30000" }],
},
},
},
};
return api.sendToken(requestOptions);
}

Transfer assets using a deposit address

A deposit address is a temporary one-time address created and monitored by Axelar’s Relayer Services. Deposit addresses generally function for up to 24 hours.

Use a deposit address if:

  • You need functionality not offered by the sendToken() method, such as Cosmos-to-X.
  • You want to allow token transfers from wallets that do not interact with Axelar, such as when withdrawing funds from a centralized exchange.

To transfer assets using a deposit address, install the AxelarJS SDK and initiate an AxelarAssetTransfer.

Build an Interchain Token

Interchain Tokens are ERC-20 tokens that are available on multiple blockchains. With Axelar’s Interchain Token Service (ITS), you can either create new Interchain Tokens from scratch or update tokens that already exist on an Ethereum blockchain. If your token is not supported by Axelar, you can turn it into an Interchain Token to make cross-chain transfers.

Edit on GitHub