Skip to content
Lattice

CCTPBridgeAdapter

Git Source

Inherits: ICCTPBridgeAdapter

Title: CCTPBridgeAdapter

Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Circle CCTP v2 (https://github.com/circlefin/evm-cctp-contracts)

Circle CCTP v2 USDC token-bridge facet: depositForBurn burns USDC on this chain toward an ERC-7930 recipient, relayMessage forwards an Iris-attested inbound message to the CCTP transmitter for minting. CCTP is a TOKEN BRIDGE (burn-and-mint), NOT an ERC-7786 message gateway — this facet is deliberately not an IERC7786GatewaySource and never routes through OpenBridge.

Stateless delegator — logic/storage live in CCTPBridgeAdapterLib. The outbound burn is nonReentrant with strict CEI and exact-amount approval hygiene; the inbound relay is a PERMISSIONLESS passthrough (trust roots in Circle's Iris attester set + denylist, not this contract), except that relayMessage refuses a Lattice hooked burn from anyone but its mintRecipient so the hook cannot be skipped. A Lattice hooked burn requires the destination domain's destinationCaller to be the destination diamond.

Notes:

  • lattice-version: 0.1.0
  • lattice-source: Circle

Functions

depositForBurn

Burns amount USDC (pulled from msg.sender) for minting to the ERC-7930 recipient on its destination chain via CCTP. Approves the TokenMessenger for EXACTLY amount, then resets to 0.

function depositForBurn(uint256 amount, bytes calldata recipient) external virtual;

Parameters

NameTypeDescription
amountuint256
recipientbytes

depositForBurnWithHook

Like depositForBurn but attaches CCTP v2 hookData to the burn message (via TokenMessengerV2.depositForBurnWithHook) for the destination recipient to execute. Reverts CCTPEmptyHookData if hookData is empty (use depositForBurn for a hook-less burn). When hookData is a Lattice envelope (HOOK_MAGIC ‖ target ‖ payload) it also reverts CCTPHookWithoutDestinationCaller unless the destination domain's destinationCaller is set: it MUST be the destination diamond, or anyone could mint via Circle's transmitter and skip the hook. Other hookData is not guarded (the destination relays it plainly). The lock is PER DOMAIN, so it also routes plain depositForBurn burns to that domain through the destination diamond.

function depositForBurnWithHook(uint256 amount, bytes calldata recipient, bytes calldata hookData)
        external
        virtual;

Parameters

NameTypeDescription
amountuint256
recipientbytes
hookDatabytes

relayMessage

PERMISSIONLESS passthrough: forwards an Iris-attested CCTP message to the transmitter, which mints USDC directly to the recipient. Reverts CCTPRelayFailed if the transmitter returns false. A burn carrying a valid Lattice hook envelope (one relayMessageWithHook accepts) reverts CCTPHookRelayRequired BEFORE minting unless the caller is its attested mintRecipient (an escape hatch that delivers the funds without running the hook).

function relayMessage(bytes calldata message, bytes calldata attestation) external virtual;

Parameters

NameTypeDescription
messagebytes
attestationbytes

relayMessageWithHook

PERMISSIONLESS relay that ALSO executes a Lattice hook envelope carried in the burn message: it validates the message is a BurnMessageV2 addressed to this adapter's TokenMessenger carrying a valid Lattice hookData envelope, mints via the transmitter, THEN calls the decoded hook target through the CCTPHookExecutor with Circle-ATTESTED context. Hook execution is LENIENT — a reverting/return-bombing target does NOT revert the relay (the mint stands, nonce consumed) — but a target whose OWN frame runs out of gas reverts the whole relay with ICCTPHookExecutor.CCTPHookOutOfGas, leaving the nonce live for a retry with more gas. A sub-call out-of-gas reads as an ordinary revert unless the target re-raises it (ICCTPHookReceiver). Reverts CCTPNotBurnMessage / CCTPInvalidHookData on a non-conforming message BEFORE minting, and CCTPRelayFailed if the transmitter returns false.

function relayMessageWithHook(bytes calldata message, bytes calldata attestation) external virtual;

Parameters

NameTypeDescription
messagebytes
attestationbytes

registerChainDomain

Registers chainId ⇒ CCTP domain. Admin only. Verify the domain table at deploy.

function registerChainDomain(uint256 chainId, uint32 domain) external virtual;

Parameters

NameTypeDescription
chainIduint256
domainuint32

configureDomain

Sets the per-domain maxFee, minFinalityThreshold and destinationCaller. Admin only.

function configureDomain(uint32 domain, uint256 maxFee, uint32 minFinalityThreshold, bytes32 destinationCaller)
        external
        virtual;

Parameters

NameTypeDescription
domainuint32
maxFeeuint256
minFinalityThresholduint32
destinationCallerbytes32

tokenMessenger

The CCTP v2 TokenMessengerV2 this adapter burns through.

function tokenMessenger() external view virtual returns (address);

Returns

NameTypeDescription
<none>address

messageTransmitter

The CCTP v2 MessageTransmitterV2 this adapter relays inbound messages to.

function messageTransmitter() external view virtual returns (address);

Returns

NameTypeDescription
<none>address

usdc

The bridged USDC token (burn source of funds is msg.sender, not the Diamond balance).

function usdc() external view virtual returns (address);

Returns

NameTypeDescription
<none>address

getDomain

The CCTP domain registered for chainId (meaningless unless isChainRegistered is true).

function getDomain(uint256 chainId) external view virtual returns (uint32);

Parameters

NameTypeDescription
chainIduint256

Returns

NameTypeDescription
<none>uint32

domainOwner

The chainId that registered domain (0 = unregistered).

function domainOwner(uint32 domain) external view virtual returns (uint256);

Parameters

NameTypeDescription
domainuint32

Returns

NameTypeDescription
<none>uint256

isChainRegistered

Whether chainId has a registered CCTP domain (distinguishes domain 0 = Ethereum from unset).

function isChainRegistered(uint256 chainId) external view virtual returns (bool);

Parameters

NameTypeDescription
chainIduint256

Returns

NameTypeDescription
<none>bool

getDomainConfig

The per-domain config used when burning toward domain.

function getDomainConfig(uint32 domain)
        external
        view
        virtual
        returns (uint256 maxFee, uint32 minFinalityThreshold, bytes32 destinationCaller);

Parameters

NameTypeDescription
domainuint32

Returns

NameTypeDescription
maxFeeuint256
minFinalityThresholduint32
destinationCallerbytes32

hookExecutor

The role-less, fund-less CCTPHookExecutor this diamond routes inbound hooks through (deployed once at init, immutable — no setter). Never the zero address on an initialized adapter.

function hookExecutor() external view virtual returns (address);

Returns

NameTypeDescription
<none>address

exportSelectors

ERC-8153 selector export: this facet's cuttable selectors, tightly packed (4 bytes each).

Excludes exportSelectors() itself (0x0ef22643) - it is never cut into a diamond. Order matches forge inspect CCTPBridgeAdapter methodIdentifiers (alphabetical by signature); kept in exact parity by ExportSelectorsParityTest. Chunks: configureDomain(uint32,uint256,uint32,bytes32) 0x4736cc95 depositForBurn(uint256,bytes) 0x3d8f1160 depositForBurnWithHook(uint256,bytes,bytes) 0x14385403 domainOwner(uint32) 0x3f1f7a9f getDomain(uint256) 0x1a7a98e2 getDomainConfig(uint32) 0xf7d38221 hookExecutor() 0xef21b76f isChainRegistered(uint256) 0xb9e06402 messageTransmitter() 0x7b04c181 registerChainDomain(uint256,uint32) 0xa3e6f535 relayMessage(bytes,bytes) 0x29351b45 relayMessageWithHook(bytes,bytes) 0x96692d34 tokenMessenger() 0x46117830 usdc() 0x3e413bee

function exportSelectors() external pure virtual returns (bytes memory selectors);

Returns

NameTypeDescription
selectorsbytes