Skip to content
Lattice

CCTPHookExecutor

Git Source

Inherits: ICCTPHookExecutor

Title: CCTPHookExecutor

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

The role-less, fund-less contract every inbound CCTP hook is executed through — NOT a facet, NOT cut into the diamond. It exists so that the ONLY thing that ever calls an attacker-named hook target is a contract with no funds, no roles and no delegatecall reach; attacker bytes in a CCTP burn's hookData therefore gain nothing beyond a plain EOA's authority. Deployed once per diamond by the adapter's init (new CCTPHookExecutor(address(this)), delegatecalled so address(this) is the diamond) and stored immutably — there is deliberately NO setter (a swappable executor would be a forgeable trust anchor).

CCTP does NOT execute hooks; the destination recipient does. This executor is that recipient's indirection: executeHook is gated to the immutable relay diamond and calls the target with the FIXED ICCTPHookReceiver.onCCTPHook selector via assembly, forwarding no value, dropping ALL returndata (return-bomb safe) and NEVER bubbling the target's revert — it returns a plain bool success. The one exception is a target that exhausts the gas of its OWN frame (CCTPHookOutOfGas): that reverts the whole relay, so a relayer cannot starve the target's own frame while the mint stands. An out-of-gas inside a sub-call the target makes is NOT visible here — the target reverts normally and this returns false — unless the target re-raises it as ICCTPHookReceiver requires. Every context argument the relay passes is read from the Circle-ATTESTED message, never from hookData.

Constants

relay

The diamond that deployed this executor — the ONLY address permitted to call executeHook.

address public immutable relay;

Returns

NameTypeDescription
<none>address

Functions

constructor

constructor(address relay_);

Parameters

NameTypeDescription
relay_addressThe diamond deploying this executor (its only permitted caller). Non-zero by construction —
the init passes address(this) (the diamond), which is never zero.

executeHook

Calls target.onCCTPHook(...) with Circle-attested context, swallowing any revert/returndata.

MUST revert CCTPHookExecutorUnauthorized unless msg.sender == relay. The call uses a FIXED selector so payload can never choose the invoked function, forwards no value, and returns success = false if the target reverts or return-bombs. It MUST revert CCTPHookOutOfGas instead when the target fails with at most 1/63 of the pre-call gas left (its own frame starved or out of gas). This detects exhaustion in the target's OWN frame only: a sub-call's out-of-gas leaves the target its 1/64 reserve, so it reads as an ordinary revert (false) unless the target burns all its remaining gas to re-raise it.

function executeHook(
        uint32 sourceDomain,
        bytes32 sender,
        bytes32 mintRecipient,
        uint256 amount,
        bytes32, /* nonce */
        address target,
        bytes calldata payload
    ) external returns (bool success);

Parameters

NameTypeDescription
sourceDomainuint32Attested CCTP source domain of the burn.
senderbytes32Attested bytes32 burner on the source domain.
mintRecipientbytes32Attested bytes32 mint recipient on this chain.
amountuint256The USDC amount actually minted to mintRecipient (attested burn amount minus
attested feeExecuted).
_bytes32Attested CCTP nonce (passed through for the relay's HookExecuted bookkeeping).
targetaddressThe hook target decoded from the Lattice hookData envelope (attacker-chosen).
payloadbytesThe attacker-controlled hook payload tail.

Returns

NameTypeDescription
successboolWhether target.onCCTPHook(...) returned without reverting.