Skip to content

Circle CCTP V2 Protocol

Technical documentation for Circle's Cross-Chain Transfer Protocol integration in Quantum DEX.

Overview

Circle's Cross-Chain Transfer Protocol (CCTP) enables native USDC transfers between blockchains without wrapped tokens or liquidity pools.

Core Benefits

  • Native USDC on all supported chains
  • Capital efficient (no liquidity pools)
  • Permissionless (open protocol)
  • Audited and maintained by Circle
  • Composable with any application

Architecture

Components

TokenMessenger

  • Handles USDC burn on source chain
  • Emits deposit message events
  • Manages sender and recipient mapping

MessageTransmitter

  • Receives and verifies attestations
  • Executes mint on destination chain
  • Implements nonce tracking and replay protection

Attestation Service

  • Monitors burn events across chains
  • Generates cryptographic attestations
  • Signs messages with Circle's private key

Protocol Flow

Source Chain → TokenMessenger (Burn) → Attestation Service

Destination Chain ← MessageTransmitter (Mint) ← Signed Attestation

Timing:

  • Burn confirmation: 10-30 seconds
  • Attestation: 60-90 seconds
  • Mint confirmation: 10-30 seconds
  • Total: ~2 minutes

Smart Contracts

Burn Function

solidity
function depositForBurn(
    uint256 amount,
    uint32 destinationDomain,
    bytes32 mintRecipient,
    address burnToken
) external returns (uint64 nonce)

Parameters:

  • amount: USDC amount (6 decimals)
  • destinationDomain: Target chain domain ID
  • mintRecipient: Recipient address (bytes32 format)
  • burnToken: USDC contract address

Returns:

  • nonce: Unique message identifier

Mint Function

solidity
function receiveMessage(
    bytes message,
    bytes attestation
) external returns (bool success)

Parameters:

  • message: Original burn message
  • attestation: Circle's signed attestation

Returns:

  • success: True if mint succeeded

Domain Identifiers

ChainDomain ID
Ethereum0
Avalanche1
Optimism2
Arbitrum3
Base6
Polygon7

Testnet chains use the same domain IDs as mainnet. Use correct contract addresses for each environment.

Message Format

Burn Message

typescript
type BurnMessage = {
  version: number;
  sourceDomain: number;
  destinationDomain: number;
  nonce: bigint;
  sender: string;
  recipient: string;
  destinationCaller: string;
  messageBody: bytes;
}

Message Body

typescript
type MessageBody = {
  version: number;
  burnToken: string;
  mintRecipient: string;
  amount: bigint;
  messageSender: string;
}

Implementation

Frontend Integration

typescript
// Approve USDC
await usdcContract.approve(
  tokenMessengerAddress,
  amount
);

// Burn tokens
const tx = await tokenMessenger.depositForBurn(
  amount,
  destinationDomain,
  recipientBytes32,
  usdcAddress
);

// Wait for attestation
const attestation = await fetchAttestation(
  tx.hash,
  sourceChainId
);

// Mint on destination
await messageTransmitter.receiveMessage(
  message,
  attestation
);

Attestation Polling

typescript
async function fetchAttestation(
  txHash: string,
  sourceChain: number
): Promise<string> {
  const apiUrl = getAttestationAPI(sourceChain);

  while (true) {
    const response = await fetch(`${apiUrl}/${txHash}`);
    const data = await response.json();

    if (data.attestation) {
      return data.attestation;
    }

    await sleep(5000);
  }
}

Poll Circle's attestation API every 5 seconds until attestation is available.

Recovery System

Quantum DEX implements persistent storage for failed mints:

typescript
type PendingMint = {
  message: string;
  attestation: string;
  fromChainId: number;
  toChainId: number;
  amount: string;
  burnTxHash: string;
};

const useBridgeStore = create(
  persist(
    (set) => ({ /*...*/ }),
    {
      name: 'quantum-bridge-storage',
      partialize: (state) => ({
        pendingMint: state.pendingMint
      })
    }
  )
);

Data persists in localStorage across sessions. Users can retry mint anytime.

Security

Message Verification

Signature Validation:

  • Attestation signed by Circle's private key
  • ECDSA signature verification on-chain
  • Public key rotation supported

Replay Protection:

  • Nonce tracking per source domain
  • Used nonces marked permanently
  • Same attestation cannot be reused

Domain Validation:

  • Source domain must match chain
  • Destination domain must be valid
  • Cross-domain attacks prevented

Transaction Limits

Per-Transaction:

  • Testnet minimum: 1 USDC
  • Testnet maximum: 10,000 USDC
  • Mainnet: Dynamic limits per chain

Daily Limits:

  • Aggregate caps per chain
  • Based on available liquidity
  • Monitored by Circle

Attack Mitigation

Protected:

  • Replay attacks (nonce system)
  • Front-running (atomic operations)
  • MEV extraction (deterministic)
  • Rug pulls (no custody)

User Responsibility:

  • Verify contract addresses
  • Manage token approvals
  • Validate recipient address
  • Check transaction details

Performance

Gas Costs

Ethereum Mainnet:

  • Approval: ~46,000 gas (~$2-5)
  • Burn: ~120,000 gas (~$5-15)
  • Mint: ~150,000 gas (~$6-20)

L2 Networks:

  • Approval: ~46,000 gas (~$0.01-0.10)
  • Burn: ~120,000 gas (~$0.02-0.20)
  • Mint: ~150,000 gas (~$0.03-0.30)

Arc Testnet:

  • Approval: ~46,000 gas (~$0.001)
  • Burn: ~120,000 gas (~$0.002)
  • Mint: ~150,000 gas (~$0.003)

Attestation Time

Expected: 60-90 seconds

Maximum: 3-5 minutes during network congestion

Factors Affecting Time:

  • Source chain finality requirements
  • Attestation service load
  • Network conditions

Testing

Testnet Endpoints

Attestation API:

https://iris-api-sandbox.circle.com/attestations/{txHash}

Contract Addresses: Verify addresses in Circle's documentation for each testnet.

Test Flow

  1. Get testnet USDC from faucet
  2. Approve TokenMessenger
  3. Execute burn transaction
  4. Poll attestation API
  5. Verify attestation signature
  6. Execute mint on destination
  7. Confirm USDC balance

Common Issues

Burn Fails:

  • Check USDC balance
  • Verify approval amount
  • Ensure sufficient gas
  • Confirm contract address

Attestation Delayed:

  • Wait 60-90 seconds minimum
  • Check burn transaction finalized
  • Verify source chain status
  • Poll attestation API manually

Mint Fails:

  • Confirm gas on destination
  • Verify attestation validity
  • Check nonce not used
  • Review error message

Integration Examples

Basic Bridge

typescript
import { ethers } from 'ethers';

async function bridgeUSDC(
  amount: string,
  fromChain: number,
  toChain: number,
  recipient: string
) {
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();

  // Get contracts
  const usdc = new ethers.Contract(usdcAddress, usdcAbi, signer);
  const messenger = new ethers.Contract(messengerAddress, messengerAbi, signer);

  // Approve
  const approveTx = await usdc.approve(messengerAddress, amount);
  await approveTx.wait();

  // Burn
  const burnTx = await messenger.depositForBurn(
    amount,
    toChain,
    ethers.utils.formatBytes32String(recipient),
    usdcAddress
  );
  const receipt = await burnTx.wait();

  return receipt.transactionHash;
}

Mint with Attestation

typescript
async function mintUSDC(
  message: string,
  attestation: string
) {
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();

  const transmitter = new ethers.Contract(
    transmitterAddress,
    transmitterAbi,
    signer
  );

  const mintTx = await transmitter.receiveMessage(
    message,
    attestation
  );

  const receipt = await mintTx.wait();
  return receipt.transactionHash;
}

Error Handling

typescript
try {
  const txHash = await bridgeUSDC(amount, fromChain, toChain, recipient);
  console.log('Burn successful:', txHash);
} catch (error) {
  if (error.code === 'INSUFFICIENT_FUNDS') {
    console.error('Not enough gas');
  } else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
    console.error('Transaction will fail, check parameters');
  } else {
    console.error('Burn failed:', error.message);
  }
}

Monitoring

Track Bridge Status

typescript
type BridgeStatus = 'burning' | 'attesting' | 'minting' | 'complete' | 'failed';

async function monitorBridge(burnTxHash: string): Promise<BridgeStatus> {
  // Wait for burn confirmation
  const burnReceipt = await provider.waitForTransaction(burnTxHash);
  if (!burnReceipt.status) return 'failed';

  // Poll for attestation
  let attestation;
  while (!attestation) {
    try {
      const response = await fetch(`${attestationAPI}/${burnTxHash}`);
      const data = await response.json();
      attestation = data.attestation;
    } catch {
      await sleep(5000);
    }
  }

  return 'attesting';
}

Event Listening

typescript
// Listen for burn events
messenger.on('DepositForBurn', (
  nonce,
  burnToken,
  amount,
  depositor,
  mintRecipient,
  destinationDomain,
  destinationTokenMessenger,
  destinationCaller
) => {
  console.log('Burn detected:', {
    nonce: nonce.toString(),
    amount: ethers.utils.formatUnits(amount, 6),
    destination: destinationDomain
  });
});

// Listen for mint events
transmitter.on('MessageReceived', (
  caller,
  sourceDomain,
  nonce,
  sender,
  messageBody
) => {
  console.log('Mint detected:', {
    nonce: nonce.toString(),
    source: sourceDomain
  });
});

Best Practices

Development

  • Test on testnet first
  • Verify all contract addresses
  • Implement proper error handling
  • Add transaction retry logic
  • Store attestation data safely

Production

  • Monitor gas prices
  • Set reasonable transaction limits
  • Implement rate limiting
  • Log all transactions
  • Provide clear user feedback

User Experience

  • Show real-time progress
  • Estimate completion time accurately
  • Allow transaction cancellation before burn
  • Persist incomplete bridges
  • Provide recovery mechanism

Resources

Circle Documentation:

Quantum DEX:

FAQ

How does CCTP differ from traditional bridges?

CCTP burns and mints native USDC. Traditional bridges lock tokens and mint wrapped versions.

What happens if attestation service is down?

Attestations are retrievable via API anytime after burn. No time limit for minting.

Can I bridge other tokens?

CCTP only supports USDC. Other tokens require different bridge protocols.

Is there a maximum bridge amount?

Yes, per-transaction and daily limits exist. Check TokenMinter contract for current limits.

How long are attestations valid?

Attestations have no expiration. Mint anytime after receiving attestation.

Can I cancel after burning?

No. Once USDC is burned, complete the mint on destination or lose funds.

What if mint transaction fails?

Use Quantum DEX's recovery system to retry. Attestation remains valid.

Built on Arc Network