Skip to main content

Contract Hierarchy

CCTP’s architecture is built on a modular contract design with clear separation of concerns:

Core Contracts

TokenMessenger

Inheritance: Rescuable Purpose: Entry point for users initiating cross-chain USDC transfers Key Relationships:
  • Calls ITokenMinter to burn tokens on source chain and mint on destination
  • Calls IMessageTransmitter to send cross-chain messages
  • Implements IMessageHandler to receive and process incoming messages
  • Maintains mapping of remote TokenMessenger contracts by domain
State Variables:
// Immutable reference to local MessageTransmitter
IMessageTransmitter public immutable localMessageTransmitter;

// Message body format version
uint32 public immutable messageBodyVersion;

// Local TokenMinter for burn/mint operations
ITokenMinter public localMinter;

// Remote TokenMessenger addresses by domain
mapping(uint32 => bytes32) public remoteTokenMessengers;

MessageTransmitter

Inheritance: Pausable, Rescuable, Attestable Purpose: Handles cross-chain message sending, receiving, and attestation verification Key Relationships:
  • Invokes IMessageHandler.handleReceiveMessage() on recipient contracts
  • Inherits attester management from Attestable
  • Enforces pause controls for emergency situations
State Variables:
// Domain identifier for this chain
uint32 public immutable localDomain;

// Message format version
uint32 public immutable version;

// Maximum message size in bytes
uint256 public maxMessageBodySize;

// Incrementing nonce for outgoing messages
uint64 public nextAvailableNonce;

// Nonce replay prevention: hash(sourceDomain, nonce) => used flag
mapping(bytes32 => uint256) public usedNonces;

TokenMinter

Inheritance: TokenController, Pausable, Rescuable Purpose: Manages token minting and burning operations with burn limits Key Relationships:
  • Only callable by authorized localTokenMessenger
  • Interacts with IMintBurnToken interface for actual mint/burn
  • Inherits token pair management from TokenController
State Variables:
// Authorized TokenMessenger that can call mint/burn
address public localTokenMessenger;

// Inherited from TokenController:
// - burnLimitsPerMessage: per-token burn limits
// - remoteTokensToLocalTokens: token address mappings

Role-Based Access Control

CCTP implements granular access control through specialized role contracts:

Ownable2Step

Purpose: Two-step ownership transfer to prevent accidental loss of control Key Features:
  • Owner must propose new owner
  • New owner must accept to complete transfer
  • Prevents typos in address entry from permanently losing access
Functions:
function transferOwnership(address newOwner) external onlyOwner
function acceptOwnership() external

Pausable

Purpose: Emergency stop mechanism for security incidents Key Features:
  • Separate pauser role from owner
  • whenNotPaused modifier blocks critical functions
  • Quick response capability without ownership transfer
Functions:
function pause() external onlyPauser
function unpause() external onlyPauser  
function updatePauser(address newPauser) external onlyOwner
Usage: Applied to:
  • TokenMessenger.depositForBurn()
  • MessageTransmitter.sendMessage() and receiveMessage()
  • TokenMinter.mint() and burn()

Rescuable

Purpose: Recover accidentally sent ERC20 tokens Key Features:
  • Separate rescuer role from owner
  • Can rescue any ERC20 token sent to contract
  • Uses OpenZeppelin’s SafeERC20 for safe transfers
Functions:
function rescueERC20(
    IERC20 tokenContract,
    address to,
    uint256 amount
) external onlyRescuer

function updateRescuer(address newRescuer) external onlyOwner

Attestable

Purpose: Manage authorized attesters and signature verification Key Features:
  • Maintains set of enabled attester addresses
  • Configurable signature threshold (m-of-n multisig)
  • Separate attesterManager role
  • ECDSA signature recovery and validation
Functions:
function enableAttester(address attester) external onlyAttesterManager
function disableAttester(address attester) external onlyAttesterManager
function setSignatureThreshold(uint256 newThreshold) external onlyAttesterManager
function updateAttesterManager(address newManager) external onlyOwner
State Variables:
// Threshold for valid attestation (m in m/n multisig)
uint256 public signatureThreshold;

// Set of enabled attester addresses (n in m/n multisig)
EnumerableSet.AddressSet private enabledAttesters;

// Address that can manage attesters
address private _attesterManager;
See Attestation for detailed signature verification logic.

TokenController

Purpose: Manage token pair mappings and burn limits Key Features:
  • Links remote tokens to local tokens by domain
  • Enforces per-message burn limits
  • Separate tokenController role
Functions:
function linkTokenPair(
    address localToken,
    uint32 remoteDomain,
    bytes32 remoteToken
) external onlyTokenController

function setMaxBurnAmountPerMessage(
    address localToken,
    uint256 burnLimitPerMessage  
) external onlyTokenController

Proxy Pattern for Upgradeability

All three core contracts (TokenMessenger, MessageTransmitter, TokenMinter) are deployed behind AdminUpgradableProxy contracts:

AdminUpgradableProxy

Purpose: Upgradeable proxy with admin-only upgrade functions Key Features:
  • Based on EIP-1967 transparent proxy pattern
  • Admin calls don’t forward to implementation (prevents function selector clashes)
  • Non-admin calls always forward to implementation
  • Uses assembly for gas-efficient storage slot access
Architecture:
┌─────────────────────────────────────┐
│         User / Contract             │
└──────────────┬──────────────────────┘


┌─────────────────────────────────────┐
│     AdminUpgradableProxy            │
│  ┌──────────────────────────────┐   │
│  │ Admin Storage Slot           │   │
│  │ Implementation Storage Slot  │   │
│  └──────────────────────────────┘   │
│                                     │
│  if (msg.sender == admin) {         │
│    execute admin functions          │
│  } else {                           │
│    delegatecall to implementation   │
│  }                                  │
└──────────────┬──────────────────────┘
               │ delegatecall

┌─────────────────────────────────────┐
│   Implementation Contract           │
│   (TokenMessenger/MessageTransmitter│
│    /TokenMinter)                    │
│  ┌──────────────────────────────┐   │
│  │ Business Logic               │   │
│  │ State Variables              │   │
│  └──────────────────────────────┘   │
└─────────────────────────────────────┘
Admin Functions:
// View current admin address
function admin() external view returns (address)

// View current implementation address  
function implementation() external view returns (address)

// Change proxy admin
function changeAdmin(address newAdmin) external ifAdmin

// Upgrade implementation
function upgradeTo(address newImplementation) external ifAdmin

// Upgrade and initialize in one transaction
function upgradeToAndCall(
    address newImplementation,
    bytes calldata data
) external payable ifAdmin
Storage Layout:
  • Uses EIP-1967 standard storage slots to avoid collisions
  • Admin slot: keccak256("eip1967.proxy.admin") - 1
  • Implementation slot: keccak256("eip1967.proxy.implementation") - 1

Upgrade Process

  1. Deploy new implementation contract
  2. Admin calls proxy.upgradeTo(newImplementation)
  3. Proxy updates implementation storage slot
  4. All subsequent calls route to new implementation
  5. Existing storage and state preserved
Upgrade Safety: Storage layout must be compatible between implementation versions. Adding new variables is safe; reordering or removing variables can corrupt state.

Message Format

CCTP uses a fixed-format message structure defined in Message.sol:
FieldBytesTypeIndex
version4uint320
sourceDomain4uint324
destinationDomain4uint328
nonce8uint6412
sender32bytes3220
recipient32bytes3252
destinationCaller32bytes3284
messageBodydynamicbytes116
Design Rationale:
  • Fixed-size fields prevent hash collisions
  • destinationCaller enables permissioned receiving
  • Dynamic messageBody supports custom burn message formats

Cross-Domain Communication

CCTP enables token transfers between domains through coordinated contract interactions:

Security Architecture

Multiple layers ensure system security:
  1. Access Control: Role-based permissions limit critical operations
  2. Attestation: Multi-signature validation prevents unauthorized mints
  3. Nonce Tracking: Replay attack prevention per (domain, nonce) pair
  4. Burn Limits: Per-message caps limit damage from compromised keys
  5. Pause Controls: Emergency stop for security incidents
  6. Upgradeability: Fix vulnerabilities without redeployment
  7. Domain Validation: Ensure messages route to correct chains

Next Steps

Message Flow

Follow a transfer through the complete lifecycle

Attestation

Deep dive into signature verification

Build docs developers (and LLMs) love