Skip to main content

Swap tokens cross-chain

Learn how to use the Balanced contracts to swap a non-native token for another asset on supported blockchains.

You can interact with the Balanced smart contracts to swap tokens between blockchains. In this guide, you’ll learn how to query contract data, calculate the swap amounts, and encode the payload to swap USD from Base for AVAX on Avalanche.

Get the source code

For more details and advanced configurations, view the source code, documentation, and examples in the Balanced Examples repository.

Prerequisites

To use this guide, you’ll need a basic understanding of Solidity, smart contract interactions, and cross-chain transaction formats.

Here are the values you’ll need to swap USDC on Base AVAX on Avalanche:

ItemValue
xCall contract address on Base0x7fdde482956770D148E055f9d2893f84a1B6B00B
xCall Manager contract address on Base0xbcbd42Ab3c9d219b9c992Cc984DD2a77AfD95EF3
asset Manager contract address on Base0xDccd213951D8214fBACa720728474E2cEf9d247B
Balanced Router contract address on ICONcx21e94c08c03daee80c25d8ee3ea22a20786ec231
Pool ID for USDC/bnUSD pool0x44
Pool ID for AVAX/bnUSD pool0x46
Network ID for Avalanche0xa86a.avax
Network ID for Base0x2105.base
Network ID for Base0x1.icon

You’ll also need the ABI for these contracts:

1. Query the contract data

  1. Call the getProtocols() method on the xCall Manager contract on the source chain (Base).

  2. Call the getFee(string _net, bool _rollback, string[] _sources) method on the xCall contract on the source chain (Base).

  • _net is the network label of the destination chain
  • _rollback is a boolean that indicates if the transaction should roll back if it fails
  • _sources is the array list obtained from the getProtocols() method

2. Calculate the swap amounts

We can now calculate the required amount of USDC to send (swap amount + fee):

  1. Call the getPoolStats(string id) method on the DEX contract to get the pool information for USDC/bnUSD and AVAX/bnUSD. Learn how to query a liquidity pool.

  2. Use the pool prices to calculate the amount of AVAX to receive after slippage:

  • AVAX amount = (USDC amount x USDC/bnUSD price) ÷ (AVAX/bnUSD price)
  • AVAX to receive = AVAX amount - (AVAX amount x slippage)

3. Encode the payload

It’s time to encode the payload for the transaction. The payload is a list of values encoded with RLP that uses this format:

[
"_swap", // operation identifier for Balanced
NETWORK_ADDRESS_OF_RECEIVER,
MIN_AMOUNT_TO_RECEIVE,
[ 2, SMART_CONTRACT_OF_BNUSD_TOKEN ],
[ 1, SMART_CONTRACT_OF_AVAX_TOKEN ],
]
  • NETWORK_ADDRESS_OF_RECEIVER is the recipient wallet address. Use the NETWORK_ID/WALLET_ADDRESS format, e.g. 0xa86a.avax/0xf963B069aDCbc094cFe2500a668C1399aeC8F803.

  • MIN_AMOUNT_TO_RECEIVE is the minimum amount of AVAX to receive after the swap, calculated in the previous step.

  • The [ #, SMART_CONTRACT_OF_TOKEN ] variables are the path for the swap. Our USDC > AVAX example will swap USDC for bnUSD, then bnUSD for AVAX.

    Each step in the swap path is an array with two values. The first value is an identifier that indicates whether to swap from the liquidity pool (1) or the Stability Fund on ICON (2). (2 is only necessary for swaps that involve a stablecoin other than bnUSD, like this one.) The second value is the smart contract address of the token on ICON.

Here’s an example of the payload before RLP encoding:

[
'_swap',
'0xa86a.avax/0xf963B069aDCbc094cFe2500a668C1399aeC8F803',
34120090037249480,
[ 2, 'cx88fd7df7ddff82f7cc735c871dc519838cb235bb' ],
[ 1, 'cx66a031cc3bd305c76371fb586e93801b948254f0' ]
]

The encoded payload is added to the data field of the transaction. The value field should be set to the amount of USDC to swap.

4. Initiate the swap

Send the transaction by calling the deposit method on the Asset Manager contract on the source chain (Base).

deposit(
address token,
uint256 amount,
string to,
bytes data
)
  • token is the address for the token to swap
  • amount is the amount of USDC required for the swap and transaction fees
  • to is the address of the Balanced Router contract on the ICON blockchain
  • data is the encoded payload
{
'token': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
'amount': '0xf4240',
'to': '0x1.icon/cx21e94c08c03daee80c25d8ee3ea22a20786ec231',
'data': '0xf875855f73776170b63078613836612e617661782f30786639363342303639614443626330393463466532353030613636384331333939616543384638303387769cb8eebbff0cd702950188fd7df7ddff82f7cc735c871dc519838cb235bbd701950166a031cc3bd305c76371fb586e93801b948254f0'
}

Once the transaction has been sent, it will automatically route through the ICON blockchain and complete the swap.