Quickstart: Cross-chain USDC Transfer

Test out CCTP functionality with this script that transfers USDC between testnet addresses on two different chains.

Cross-Chain Transfer Protocol (CCTP) is a permissionless on-chain utility that can burn native USDC on a source chain and mint native USDC of the same amount on a destination chain. Developers can embed CCTP into their apps to provide users with the most capital-efficient way to transfer USDC across chains, unifying liquidity across the ecosystem and simplifying user experience.

To get started with CCTP, follow the example script provided here. The example uses web3.js to transfer USDC from an address on ETH Goerli testnet to another address on AVAX Fuji testnet.

The script has 5 steps:

  1. The first step approves ETH TokenMessenger contract to withdraw USDC from the provided eth address.
const approveTx = await usdcEthContract.methods.approve(ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS, amount).send({gas: approveTxGas})
  1. The second step executes depositForBurn function on the ETH TokenMessenger contract deployed in Goerli testnet.
const burnTx = await ethTokenMessengerContract.methods.depositForBurn(amount, AVAX_DESTINATION_DOMAIN, destinationAddressInBytes32, USDC_ETH_CONTRACT_ADDRESS).send();
  1. The third step extracts messageBytes emitted by MessageSent event from depositForBurn transaction logs and hashes the retrieved messageBytes using the keccak256 hashing algorithm.
const transactionReceipt = await web3.eth.getTransactionReceipt(burnTx.transactionHash);
const eventTopic = web3.utils.keccak256('MessageSent(bytes)')
const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic)
const messageBytes = web3.eth.abi.decodeParameters(['bytes'], log.data)[0]
const messageHash = web3.utils.keccak256(messageBytes);
  1. The fourth step polls the attestation service to acquire the signature using the messageHash from the previous step.
let attestationResponse = {status: 'pending'};
while(attestationResponse.status != 'complete') {
    const response = await fetch(`https://iris-api-sandbox.circle.com/attestations/${messageHash}`);
    attestationResponse = await response.json()
    await new Promise(r => setTimeout(r, 2000));
  1. The last step calls the receiveMessage function on AVAX MessageTransmitter contract to receive USDC at AVAX address.

Note: The attestation service is rate-limited. Please limit your requests to less than 10 per second.

const receiveTx = await avaxMessageTransmitterContract.receiveMessage(receivingMessageBytes, signature);

What’s Next