Transfer USDC with Data
USDC is a digital dollar backed 100% and is always redeemable 1:1 for US dollars. The stablecoin is issued by Circle on multiple blockchain platforms.
This guide will first explain how Chainlink CCIP enables native USDC transfers when both the source and destination blockchains support Circle's Cross-Chain Transfer Protocol (CCTP).
Additionally, it will outline how CCIP also supports transferring Bridged USDC on blockchains that are not CCTP-enabled, allowing projects to later migrate to CCTP-enabled transfers if approved by Circle.
The hands-on tutorial at the end demonstrates how to use Chainlink CCIP to transfer USDC and arbitrary data from a smart contract on Avalanche Fuji to a smart contract on Ethereum Sepolia.
Note: In addition to programmable token transfers, you can also use CCIP to transfer USDC tokens without data. Check the Mainnets and Testnets configuration pages to learn on which blockchains CCIP supports USDC transfers.
Architecture
Native USDC vs. Bridged USDC
New blockchains frequently encounter the cold start problem—a scarcity of initial liquidity and limited user adoption that hampers the development and functionality of decentralized applications (dApps). Without sufficient stablecoin liquidity, essential use cases such as borrowing, lending, and trading remain constrained.
To overcome this challenge, Circle introduced the Bridged USDC Standard. This standard facilitates the seamless issuance of Bridged USDC on any EVM-compatible blockchain, enabling third-party teams to deploy USDC without awaiting native support from Circle. By standardizing bridged token issuance, Circle aims to:
- Reduce Liquidity Fragmentation: Ensure a unified and efficient USDC supply across multiple blockchains.
- Enhance User Experience: Minimize complexities and inconsistencies in managing multiple bridged USDC versions.
- Facilitate Seamless Upgrades: Enable projects to transition smoothly to Native USDC once their blockchain is approved for native issuance via the Cross-Chain Transfer Protocol (CCTP).
Projects using Bridged USDC can easily migrate to Native USDC once their blockchain is approved for CCTP by Circle. This migration ensures that user balances, contract addresses, and integrations remain intact, eliminating the need for complex token swaps or liquidity migrations. For more detailed information, refer to Circle's Bridged USDC Standard.
How CCIP Works with USDC
Chainlink CCIP maintains a consistent API regardless of whether the transfer involves Native USDC or Bridged USDC. Here's how it operates in both scenarios:
- The sender has to interact with the CCIP router to initiate a cross-chain transaction, similar to the process for any other token transfers. See the Transfer Tokens guide to learn more.
- The process uses the same onchain components including the Router, OnRamp, Commit Store, OffRamp, and Token Pool.
- The process uses the same offchain components including the Committing DON, Executing DON, and the Risk Management Network.
- USDC transfers also benefit from CCIP additional security provided by the Risk Management Network.
Native USDC (CCTP-enabled)
The diagram below shows that the USDC token pools and Executing DON handle the integration with Circle's contracts and offchain CCTP Attestation API. As with any other supported ERC-20 token, USDC has a linked token pool on each supported blockchain to facilitate OnRamp and OffRamp operations. To learn more about these components, read the architecture page.
The following describes the operational process:
- On the source blockchain:
- When the sender initiates a transfer of USDC, the USDC token pool interacts with CCTP's contract to burn USDC tokens and specifies the USDC token pool address on the destination blockchain as the authorized caller to mint them.
- CCTP burns the specified USDC tokens and emits an associated CCTP event.
- Offchain:
- The Circle attestation service listens to CCTP events on the source blockchain.
- The CCIP Executing DON listens to relevant CCTP events on the source blockchain. When it captures such an event, it calls the Circle Attestation service API to request an attestation. An attestation is a signed authorization to mint the specified amount of USDC on the destination blockchain.
- On the destination blockchain:
- The Executing DON provides the attestation to the OffRamp contract.
- The OffRamp contract calls the USDC token pool with the USDC amount to be minted, the Receiver address, and the Circle attestation.
- The USDC token pool calls the CCTP contract. The CCTP contract verifies the attestation signature before minting the specified USDC amount into the Receiver.
- If there is data in the CCIP message and the Receiver is not an EOA, then the OffRamp contract transmits the CCIP message via the Router contract to the Receiver.
Bridged USDC (non-CCTP-enabled)
To facilitate USDC transfers between a blockchain that issues native USDC and another that only supports Bridged USDC, Chainlink CCIP employs the Lock and Mint mechanism. This approach ensures that each Bridged USDC token on the destination blockchain is fully backed by an equivalent amount of native USDC locked on the source blockchain. Accounting for Non-CCTP-Enabled USDC is isolated on a per-blockchain basis for security and risk mitigation.
- Lock native USDC on Source: The Lock and Release token pool locks the specified USDC tokens.
- Mint Bridged USDC on Destination: This Bridged USDC serves as a proxy for the original USDC, enabling liquidity and user adoption without direct integration with CCTP. A Burn and Mint token pool is recommended for the Destination Chain; it will mint Bridged USDC for incoming transfers and burn Bridged USDC for outgoing transfers. Other token pools are also supported, depending on the preferences and constraints of the Destination Chain.
Example
In this tutorial, you will learn how to send USDC tokens from a smart contract on Avalanche Fuji to a smart contract on Ethereum Sepolia using Chainlink CCIP and pay CCIP fees in LINK tokens. The process uses the following steps:
- Transfer USDC and Data: Initiate a transfer of USDC tokens and associated data from the Sender contract on Avalanche Fuji. The data includes the required arguments and the signature of the
stakefunction from the Staker contract. - Receive and Stake: The Receiver contract on Ethereum Sepolia receives the tokens and data. Then, it uses this data to make a low-level call to the Staker contract, executing the
stakefunction to stake USDC on behalf of a beneficiary. - Redeem Staked Tokens: The beneficiary can redeem the staked tokens for USDC later.
The purpose of including the function signature and arguments in the data is to demonstrate how arbitrary data can support a variety of scenarios and use cases. By sending specific instructions within the data, you can define various interactions between smart contracts across different blockchain networks and make your decentralized application more flexible and powerful.
Before you begin
- You should understand how to write, compile, deploy, and fund a smart contract. If you need to brush up on the basics, read this tutorial, which will guide you through using the Solidity programming language, interacting with the MetaMask wallet and working within the Remix Development Environment.
- Your account must have some AVAX and LINK tokens on Avalanche Fuji and ETH tokens on Ethereum Sepolia. You can use the Chainlink faucet to acquire testnet tokens.
- Check the CCIP Directory to confirm that USDC are supported for your lane. In this example, you will transfer tokens from Avalanche Fuji to Ethereum Sepolia so check the list of supported tokens here.
- Use the Circle faucet to acquire USDC tokens on Avalanche Fuji.
- Learn how to fund your contract. This guide shows how to fund your contract in LINK, but you can use the same guide for funding your contract with any ERC-20 tokens as long as they appear in the list of tokens in MetaMask.
Tutorial
Deploy your contracts
Deploy the Sender contract on Avalanche Fuji:
-
Compile your contract.
-
Deploy, fund your sender contract on Avalanche Fuji and enable sending messages to Ethereum Sepolia:
-
Open MetaMask and select the network Avalanche Fuji.
-
In Remix IDE, click on Deploy & Run Transactions and select Injected Provider - MetaMask from the environment list. Remix will then interact with your MetaMask wallet to communicate with Avalanche Fuji.
-
Fill in your blockchain's router, LINK, and USDC contract addresses. The router and USDC addresses can be found on the CCIP Directory and the LINK contract address on the LINK token contracts page. For Avalanche Fuji, the addresses are:
- Router address:
0xf694e193200268f9a4868e4aa017a0118c9a8177 - LINK contract address:
0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 - USDC contract address:
0x5425890298aed601595a70AB815c96711a31Bc65
- Router address:
-
Click the transact button. After you confirm the transaction, the contract address appears on the Deployed Contracts list. Note your contract address.
-
Open MetaMask and fund your contract with USDC tokens. You can transfer
1USDC to your contract. -
Fund your contract with LINK tokens. You can transfer
70LINK to your contract. In this example, LINK is used to pay the CCIP fees.Note: This transaction fee is significantly higher than normal due to gas spikes on Sepolia. To run this tutorial, you can get additional testnet LINK from faucets.chain.link or use a supported testnet other than Sepolia.
-
Deploy the Staker and Receiver contracts on Ethereum Sepolia. Configure the Receiver contract to receive CCIP messages from the Sender contract:
-
Deploy the Staker contract:
-
Open MetaMask and select the network Ethereum Sepolia.
-
Compile your contract.
-
In Remix IDE, under Deploy & Run Transactions, make sure the environment is still Injected Provider - MetaMask.
-
Fill in the usdc contract address. The usdc contract address can be found on the CCIP Directory. For Ethereum Sepolia, the usdc contract address is:
0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238.
-
Click the transact button. After you confirm the transaction, the contract address appears on the Deployed Contracts list.
Note your contract address.
-
-
Deploy the Receiver contract:
-
Compile your contract.
-
In Remix IDE, under Deploy & Run Transactions, make sure the environment is still Injected Provider - MetaMask and that you are still connected to Ethereum Sepolia.
-
Fill in your blockchain's router, LINK, and Staker contract addresses. The router and usdc addresses can be found on the CCIP Directory and the Staker contract address from the previous step. For Ethereum Sepolia, the addresses are:
- Router address:
0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 - USDC contract address:
0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 - Staker address: Copied from the previous step
- Router address:
-
Configure the Receiver contract to receive CCIP messages from the Sender contract:
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your Receiver contract deployed on Ethereum Sepolia.
-
Fill in the arguments of the setSenderForSourceChain function:
Argument Value and Description _sourceChainSelector 14767482510784806043
The chain selector of Avalanche Fuji. You can find it on the CCIP Directory._sender Your sender contract address at Avalanche Fuji.
The sender contract address. -
Click on
transactand confirm the transaction on MetaMask.
-
-
Configure the Sender contract on Avalanche Fuji:
-
Open MetaMask and select the network Avalanche Fuji.
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your Sender contract deployed on Avalanche Fuji.
-
Fill in the arguments of the setReceiverForDestinationChain function:
Argument Value and Description _destinationChainSelector 16015286601757825753
The chain selector of Ethereum Sepolia. You can find it on the CCIP Directory._receiver Your receiver contract address at Ethereum Sepolia.
The receiver contract address. -
Fill in the arguments of the setGasLimitForDestinationChain: function:
Argument Value and Description _destinationChainSelector 16015286601757825753
The chain selector of Ethereum Sepolia. You can find it on the CCIP Directory._gasLimit 200000
The gas limit for the execution of the CCIP message on the destination chain.
-
At this point:
- You have one sender contract on Avalanche Fuji, one staker contract and one receiver contract on Ethereum Sepolia.
- You enabled the sender contract to send messages to the receiver contract on Ethereum Sepolia.
- You set the gas limit for the execution of the CCIP message on Ethereum Sepolia.
- You enabled the receiver contract to receive messages from the sender contract on Avalanche Fuji.
- You funded the sender contract with USDC and LINK tokens on Avalanche Fuji.
Transfer and Receive tokens and data and pay in LINK
You will transfer 1 USDC and arbitrary data, which contains the encoded stake function name and parameters for calling Staker's stake function on the destination chain. The parameters contain the amount of staked tokens and the beneficiary address. The CCIP fees for using CCIP will be paid in LINK.
-
Transfer tokens and data from Avalanche Fuji:
-
Open MetaMask and select the network Avalanche Fuji.
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your smart contract deployed on Avalanche Fuji.
-
Fill in the arguments of the sendMessagePayLINK function:
Argument Value and Description _destinationChainSelector 16015286601757825753
CCIP Chain identifier of the destination blockchain (Ethereum Sepolia in this example). You can find each chain selector on the CCIP Directory._beneficiary The beneficiary of the Staker tokens on Ethereum Sepolia. You can set your own EOA (Externally Owned Account) so you can redeem the Staker tokens in exchange for USDC tokens. _amount 1000000
The token amount (1 USDC). -
Click on
transactand confirm the transaction on MetaMask. -
After the transaction is successful, record the transaction hash. Here is an example of a transaction on Avalanche Fuji.
-
-
Open the CCIP explorer and search your cross-chain transaction using the transaction hash.
-
The CCIP transaction is completed once the status is marked as "Success". In this example, the CCIP message ID is 0xcb0fad9eec6664ad959f145cc4eb023924faded08baefc29952205ee37da7f13.
-
Check the balance of the beneficiary on the destination chain:
-
Open MetaMask and select the network Ethereum Sepolia.
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your Staker contract deployed on Ethereum Sepolia.
-
Call the
balanceOffunction with the beneficiary address.
-
Notice that the balance of the beneficiary is 1,000,000 Staker tokens. The Staker contract has the same number of decimals as the USDC token, which is 6. This means the beneficiary has 1 USDC staked and can redeem it by providing the same amount of Staker tokens.
-
-
Redeem the staked tokens:
-
Open MetaMask and make sure the network is Ethereum Sepolia.
-
Make sure you are connected with the beneficiary account.
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your Staker contract deployed on Ethereum Sepolia.
-
Call the
redeemfunction with the amount of Staker tokens to redeem. In this example, the beneficiary will redeem 1,000,000 Staker tokens. When confirming, MetaMask will confirm that you will transfer the Staker tokens in exchange for USDC tokens.
-
Confirm the transaction on MetaMask. After the transaction is successful, the beneficiary will receive 1 USDC tokens.
-
Explanation
The smart contracts featured in this tutorial are designed to interact with CCIP to send and receive USDC tokens and data across different blockchains. The contract code contains supporting comments clarifying the functions, events, and underlying logic. We will explain the Sender, Staker, and Receiver contracts further.
Sender Contract
undefined
The Sender contract is responsible for initiating the transfer of USDC tokens and data. Here's how it works:
-
Initializing the contract:
- When deploying the contract, you define the router address, LINK contract address, and USDC contract address.
- These addresses are essential for interacting with the CCIP router and handling token transfers.
-
sendMessagePayLINKfunction:- This function sends USDC tokens, the encoded function signature of the
stakefunction, and arguments (beneficiary address and amount) to the Receiver contract on the destination chain. - Constructs a CCIP message using the
EVM2AnyMessagestruct. - Computes the necessary fees using the router's
getFeefunction. - Ensures the contract has enough LINK to cover the fees and approves the router transfer of LINK on its behalf.
- Dispatches the CCIP message to the destination chain by executing the router's
ccipSendfunction. - Emits a
MessageSentevent.
- This function sends USDC tokens, the encoded function signature of the
Staker Contract
undefined
The Staker contract manages the staking and redemption of USDC tokens. Here's how it works:
-
Initializing the contract:
- When deploying the contract, you define the USDC token address.
- This address is essential for interacting with the USDC token contract.
-
stakefunction:- Allows staking of USDC tokens on behalf of a beneficiary.
- Transfers USDC from the caller (
msg.sender) to the contract, then mints an equivalent amount of staking tokens to the beneficiary.
-
redeemfunction:- Allows beneficiaries to redeem their staked tokens for USDC.
- Burns the staked tokens and transfers the equivalent USDC to the beneficiary.
Receiver Contract
undefined
The Receiver contract handles incoming cross-chain messages, processes them, and interacts with the Staker contract to stake USDC on behalf of the beneficiary. Here's how it works:
-
Initializing the Contract:
- When deploying the contract, you define the router address, USDC token address, and staker contract address.
- These addresses are essential for interacting with the CCIP router, USDC token, and Staker contracts.
-
ccipReceivefunction:- The entry point for the CCIP router to deliver messages to the contract.
- Validates the sender and processes the message, ensuring it comes from the correct sender contract on the source chain.
-
Processing Message:
- Calls the
processMessagefunction, which is external to leverage Solidity's try/catch error handling mechanism. - Inside
processMessage, it calls the_ccipReceivefunction for further message processing.
- Calls the
-
_ccipReceivefunction:- Checks if the received token is USDC. If not, it reverts.
- Makes a low-level call to the
stakefunction of the Staker contract using the encoded function signature and arguments from the received data. - Emits a
MessageReceivedevent upon successful processing.
-
Error Handling:
- If an error occurs during processing, the catch block within ccipReceive is executed.
- The
messageIdof the failed message is added tos_failedMessages, and the message content is stored ins_messageContents. - A
MessageFailedevent is emitted, allowing for later identification and reprocessing of failed messages.
-
retryFailedMessagefunction:- Allows the contract owner to retry a failed message and recover the associated tokens.
- Updates the error code for the message to
RESOLVEDto prevent multiple retries. - Transfers the locked tokens associated with the failed message to the specified beneficiary as an escape hatch.
-
getFailedMessagesfunction:- Retrieves a paginated list of failed messages for inspection.