Understanding Faster-Than-Finality (FTF) Transfers in CCIP 2.0

Context

When sending a cross-chain message, the safest approach is to wait until the source chain has fully finalized the transaction before executing it on the destination chain. Finality refers to the level of assurance that past transactions processed by a blockchain are extremely difficult or impossible to revert. On Ethereum, for example, finality takes 2 epochs, approximately 12.8–19 minutes. However, for some use cases, waiting for full source chain finality can materially harm the user experience.

The main risk of executing a cross-chain transaction before the source chain reaches finality is that a block reorganization (reorg) could occur. A reorg occurs when the chain replaces its most recent blocks with a different set of blocks. When this happens, the original source chain transaction could be reordered, altered, or dropped from the chain entirely, regardless of whether the cross-chain transaction had already executed on the destination chain. In effect, a reorg can result in a double spend that either inflates total token supply or leaves the transferred tokens unbacked, depending on the transfer method used.

CCIP 2.0's default finality behavior is identical to prior CCIP versions: all messages wait for full source chain finality before attestation and execution on the destination chain. Faster-Than-Finality (FTF) is an opt-in capability. Existing token pools, senders, and receivers deployed under previous versions of CCIP continue to work as-is and wait for full finality.

Legacy (V1) pools and receivers cannot opt into FTF. The OnRamp reverts an FTF send through a legacy pool (FTFNotSupportedOnPoolV1), and the OffRamp treats a legacy receiver as allowing only full finality on inbound execution.

FTF requires explicit opt-in at every layer that participates in the message path. All layers default to full finality (WAIT_FOR_FINALITY_FLAG):

LayerWhat must permit FTF
SenderSets requestedFinalityConfig in ExtraArgsV3 to a non-finality mode (a block depth or WAIT_FOR_SAFE_FLAG)
Token poolConfigures an allowedFinalityConfig that permits the requested mode (token transfers only)
Committee Verifier (and any other CCVs)Each CCV's allowedFinalityConfig must permit the request
ExecutorExecutor contract's allowedFinalityConfig must permit the request
ReceiverFor messages that call ccipReceive, the receiver's allowedFinalityConfig must permit the request

If any layer rejects the requested mode, the message cannot be sent (revert at ccipSend) or cannot be executed (the OffRamp's finality check fails and the message is recorded as FAILURE). See FINALITY_INVARIANTS.md.

A Concrete Example

Suppose Alice sends a cross-chain token transfer from Ethereum to Arbitrum. She opts into FTF with a confirmation depth of 2 blocks (~24 seconds) to get a fast experience. This works only because the token issuer has explicitly enabled FTF on their pool. Without that, Alice's request would revert. The token pool, Committee Verifier, executor, and (if applicable) destination receiver must all have configured allowedFinalityConfig to permit her requested depth. Otherwise, her ccipSend reverts on the source chain or delivery fails on the destination.

Step 1: Alice's transfer is included in Ethereum block 100. After 2 block confirmations (block 102), the Committee Verifier attests the message, provided no reorg has been detected for that message number. A submitter then calls OffRamp.execute on Arbitrum, and Alice receives her tokens.

Step 2: Ethereum experiences a 3-block reorg. Blocks 100–102 are replaced by a new fork. In this new fork, Alice's transaction may still exist but can land in a different block with different ordering.

Step 3: Because the reorg (3 blocks) was deeper than Alice's confirmation depth (2 blocks), if attestation and execution already completed for the pre-reorg message, the original delivery on Arbitrum stands. A subsequent CCIPMessageSent event on the canonical fork may produce a different message ID, which results in double execution if that new ID is also attested and executed.

Why the message ID may change: messageId = keccak256(encodedMessage), and the encoded payload commits to every field in the message, not just user data. Two common reasons the ID differs after a reorg:

  1. A different messageNumber. The OnRamp assigns a strictly monotonic messageNumber per (source OnRamp, destination chain) lane. After a reorg, the counter on the canonical fork reflects whatever sends landed there. Other CCIP messages near the reorg window that did not reorg may already have consumed higher message numbers on the canonical chain. When Alice's transfer is included again, it is assigned the next number from canonical OnRamp state, which is often not the number it held on the discarded fork. A different messageNumber alone produces a different message ID, even if Alice's intent (amount, receiver, data) is unchanged.
  2. Different committed pool or token fields. For token transfers, messageId is computed after lockOrBurn, so pool-returned data (e.g., destPoolData) is part of the hash. A re-send can also differ in these committed fields even when the user-facing transfer looks the same.

Because execution is keyed by message ID, not by "Alice's transfer" or by messageNumber alone, the pre-reorg delivery and the post-reorg delivery are tracked as two independent messages. Both can reach SUCCESS on the destination.

If the Committee Verifier detects the reorg before attesting (via its reorg tracker), it ignores the FTF depth for that message number and waits for full source finality before attesting. This narrows the double-execution window but does not eliminate it. Double execution remains possible when attestation and execution already occurred before the reorg was detected.

How CCIP 2.0 Handles Reorgs

CCIP 2.0 uses a message-ID-based execution model with reorg tracking and reorg quarantining in the default Committee Verifier to minimize risk during FTF transfers.

  • Message ID keying: Each message's execution on the destination chain is tracked by a unique message ID (messageId = keccak256(encodedMessage): a hash of the full encoded payload, which commits to messageNumber, source/destination, receiver, token data including pool-returned fields, finality preference, and user data). Two different payloads always produce different IDs and are tracked independently in s_executionStates[messageId]. After a reorg, logically the "same" transfer can hash to a new ID if its assigned messageNumber differs (because other non-reorged sends on the canonical fork advanced the OnRamp counter) or if committed pool/token fields differ on re-send.
  • Reorg tracking (Committee Verifier, offchain): When CCIP's default Committee Verifier detects that a source chain reorg has occurred, all affected message numbers are quarantined, meaning the Committee Verifier pauses new attestations for those messages until the source chain reaches finality. When the Committee Verifier's source reader detects that a previously seen message disappeared from the canonical chain (indicating a reorg), it tracks the message number for the affected destination lane. While tracked, the verifier ignores the sender's FTF confirmation depth and waits for full source-chain finality before producing an attestation. Tracking is per (source chain, destination chain) lane, not a global pause on all messages. Once the message block is finalized, the message number is removed from tracking and normal processing resumes. This behavior is specific to the default Committee Verifier offchain service.
  • Bounded risk: Together, these mechanisms mean that double execution can only occur when your chosen confirmation depth is less than or equal to the depth of the reorg and the pre-reorg message was attested and executed before the reorg was detected and handled. If Alice had chosen a confirmation depth of 5 blocks and the reorg was only 3 blocks deep, her message would have been unaffected, because the reorg would have been resolved before the Committee Verifier attested. This bounded-risk guarantee depends on the reorg tracking provided by CCIP's default Committee Verifier. Without it, the risk profile of FTF messaging may differ depending on how your chosen verifier handles reorgs.

Additional CCVs: If a message requires other CCVs (e.g., the CCTP Verifier for USDC), each CCV's offchain service applies its own finality and reorg policy. The Committee Verifier's reorg tracking does not govern those attestations, so evaluate each required CCV independently before opting into FTF.

What This Means for You

Your confirmation settingReorg happens?Outcome (with CCIP's default Committee Verifier)
Wait for finality (default)Any reorgNo impact. Your message executes exactly once. This is the default behavior for all CCIP transfers.
FTF, chosen block confirmations > reorg depthYes, but shallowNo impact. The reorg was resolved before your message was acted on.
FTF, chosen block confirmations ≤ reorg depthYes, and deep enoughPossible double execution. Your application needs corrective logic.

"No impact" in row 2 assumes the reorg is shallow enough that attestation had not yet occurred at your chosen depth, or that reorg tracking deferred attestation until after the reorg resolved. Row 3 covers the case where attestation and execution already happened under a fork that was later replaced.

If you do not explicitly configure a finality setting, your experience is unchanged from prior versions of CCIP. FTF applies only if the participants in your message path (pool, CCVs, executor, and receiver where applicable) have all configured allowedFinalityConfig to permit FTF and a sender explicitly requests a non-default finality mode.

If you opt into FTF, your risk exposure is directly proportional to how aggressively you set your confirmation depth relative to the reorg profile of your source chain. On a chain where reorgs are typically 1–2 blocks deep, a confirmation depth of 5 blocks balances speed and safety. On a chain with deeper or more frequent reorgs, increase the confirmation depth or implement corrective actions in your application, such as idempotency keys or insurance mechanisms.

Users who wait for full finality are not affected by the FTF behavior of other users on the same lane.

Token-only transfers: For token-only messages (no ccipReceive callback), the destination receiver's allowedFinalityConfig is not consulted. Pool and lane CCV finality rules still apply. dApps using token-only delivery still inherit FTF/reorg risk on the token side.

Operational edge case: If a source chain's finalized checkpoint itself reorgs after messages were attested at full finality, recovery may require manual intervention for that chain. This case is separate from FTF.

Choosing Your Finality Setting by Chain Type

Key principle: Unless you are prepared to mitigate FTF risk or have a strong need for FTF in your use case, wait for full finality (the CCIP default). See Finality by Chain for expected time to finality for integrated chains.

When using FTF, the core tradeoff is speed vs. reorg risk. Since double execution may occur when your confirmation depth is less than or equal to the reorg depth, the right setting depends on the reorg profile of your source chain.

Senders request exactly one finality mode per message (full finality, a single block depth, or WAIT_FOR_SAFE_FLAG). Receivers and pools may configure multiple permitted modes simultaneously (e.g., accept safe-tag requests or depth ≥ 5 or full finality). See FinalityCodec and FTF - dApps for receiver-side configuration.

L1 chains with instant or fast finality: Use the default (full finality), since these chains already finalize fast enough for most use cases. CCIP still defaults to WAIT_FOR_FINALITY_FLAG unless you explicitly opt in, even on chains with fast native finality.

Ethereum L2s: During the ~12.8 minutes it takes Ethereum to reach finality (2 epochs = 64 slots × 12 seconds each = 768 seconds), each L2 progresses by different amounts depending on its block time. These L2 blocks represent "soft confirmations" from centralized sequencers. The L2 transactions themselves don't inherit Ethereum's full security until they're posted and finalized on L1, which takes that same ~12.8-minute finality window (for optimistic rollups) or longer (for ZK rollups that need proof generation time).

For example, Arbitrum with its 0.25-second block time produces 3,072 blocks in the same window where Ethereum finalizes just 64.

Many of the OP Stack chains (e.g., Ink, Soneium, World Chain, Mode) use a 2-second block time, so these chains produce ~384 blocks during Ethereum's time to finality.

Setting minimum block confirmations on an L2 therefore depends on (a) the L2's block time and (b) where your risk tolerance falls between trusting the sequencer (fewest block confirmations) and relying on the underlying Ethereum chain's security (full finality).

Newer or less battle-tested chains: Wait for full finality. The reorg surface on these chains is unpredictable, and the cost of building reliable corrective logic can outweigh the latency savings.

FTF for USDC Delivered via CCTP

CCIP 2.0 supports FTF for USDC transfers on lanes that integrate with Circle's CCTP. On these lanes, finality for USDC token transfers is governed by CCTP finality thresholds, whose block confirmations vary by chain. For transfers with data or a non-zero message gas limit, both the CCTP finality and the message's requested finality determine the overall speed.

The CCTP Verifier integrates with the CCTP smart contracts and Circle's CCTP attestation service.

Get the latest Chainlink content straight to your inbox.