Send Tokens Cross-Chain
There are three ways you can use Axelar to transfer ERC-20 tokens across different blockchains:
- 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. - Transfer assets using a deposit address generated with the AxelarJS SDK.
- 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.