Overview
A cross-chain USDC transfer via CCTP involves three main phases:- Source Chain: User initiates burn and message emission
- Attestation: Off-chain service validates and signs the message
- Destination Chain: User or relayer completes the transfer with minting
The examples below show a transfer from Ethereum (Domain 0) to Avalanche (Domain 1), but the flow is identical for any supported chain pair.
Phase 1: Source Chain - depositForBurn
Step 1: User Approves USDC
Before callingdepositForBurn(), users must approve the TokenMessenger contract to spend their USDC:
Step 2: Call depositForBurn
User initiates the cross-chain transfer:Step 3: Contract Execution Flow
InsideTokenMessenger.depositForBurn(), the following occurs:
Step 4: MessageTransmitter Sends Message
MessageTransmitter.sendMessage() formats and emits the cross-chain message:
Step 5: Extract Message from Event
The emittedMessageSent event contains the complete message bytes needed for attestation:
Phase 2: Attestation Service
Step 6: Attestation Service Observes Event
Circle’s attestation service continuously monitors all supported chains forMessageSent events:
- Event Detection: Service detects new
MessageSentevent - Message Validation:
- Verifies transaction is confirmed
- Checks burn amount is within limits
- Validates destination domain is supported
- Confirms source contract is legitimate
- Signature Generation:
- Hashes the complete message bytes
- Signs the hash with authorized attester private keys
- Combines signatures into attestation format
Step 7: Poll for Attestation
Users fetch the attestation via Circle’s API:- Concatenated 65-byte ECDSA signatures (v + r + s)
- Number of signatures equals
signatureThreshold - Signatures must be in increasing order of attester address
Phase 3: Destination Chain - receiveMessage
Step 8: Call receiveMessage
With the attestation, anyone can complete the transfer on the destination chain:Step 9: MessageTransmitter Validates Message
MessageTransmitter.receiveMessage() performs extensive validation:
Step 10: TokenMessenger Handles Message
TokenMessenger.handleReceiveMessage() processes the burn message:
Step 11: TokenMinter Mints USDC
Finally,TokenMinter.mint() creates new USDC on the destination chain:
Complete Example
Here’s a full working example combining all steps:Advanced: depositForBurnWithCaller
For permissioned receiving, usedepositForBurnWithCaller():
- Relayer-exclusive completion (prevent front-running)
- Smart contract-only receivers
- Conditional minting logic
Timing and Finality
Attestation Delay:- Typical: 10-20 minutes after source chain confirmation
- Depends on: Chain finality requirements, attestation service processing
- Immediate once
receiveMessage()transaction confirms - Subject to destination chain’s block time
Error Handling
Common failure scenarios:Next Steps
Attestation
Understand signature verification in detail
Quickstart
Try the example integration yourself