Skip links

Cross-chain Protocol

OIP v0.5 defines a staged coordination protocol for delivering cross-chain messages across two or more chains. The protocol resolves a single problem that one-shot bridge designs cannot: partial application of a multi-chain state change. When a regulatory action freezes an asset that exists on Base, Arbitrum, and Optimism, the freeze must take effect on all three chains or on none of them. Anything in between leaves the asset in a state where one chain treats it as restricted while another treats it as freely transferable, which is exactly the failure mode that breaks regulatory enforceability.

This page specifies the cross-chain protocol at the message-round level: the three-phase PREPARE/COMMIT/FINALIZE handshake, how each AtomicityStrategy reshapes that handshake, the rollback path for ALL_OR_NOTHING failures, and the two-tier finality structure that lets OSS permit pre-trading activity before secondary settlement reaches Base L2. The OSS-internal lifecycle that wraps these rounds (Bind-Verify-Commit) is also clarified to prevent a known naming collision around the word “commit”.


Scope

This page specifies the cross-chain message rounds that OIP v0.5 mandates. Adjacent topics are covered elsewhere and are referenced from here:

TopicWhere
How AtomicityStrategy is decided per message and how routing maps it to a coordination modeRouting
The four CoherenceState values (SYNCED, SYNCING, DIVERGED, ROLLBACK_INCOMPLETE) and Reconciliation procedureState Management
Per-phase retry policy, idempotency, and DLQ behaviorFault Tolerance
Error codes returned at each phaseError Reference

Three-phase Broadcast: PREPARE → COMMIT → FINALIZE

Cross-chain message delivery in OIP v0.5 is a three-phase broadcast. Each phase is a separate message round between the sending OSS node and every target chain participating in the message’s TargetScope. A round completes when every target has acknowledged, or when the phase’s timeout expires.

The phases are intentionally separated because they have different failure semantics. PREPARE failures are clean to recover from because no chain has changed state yet; COMMIT failures may leave partial application that needs reversal; FINALIZE failures only affect the “confirmed” label, not the underlying state.

PhasePurposeEffect of Partial Failure
PREPAREProbe each target chain for applicability and reserve resources (locks)No state change. Discard the prepared lock and abort.
COMMITApply the actual state change on each targetSome chains may already be applied; rollback may be required.
FINALIZEConfirm the change and release coordination locksState is already applied; only the “confirmed” label is missing.
Three-phase broadcast sequence Sequence diagram. Sender OSS node communicates with three target chains. Three message rounds: PREPARE with PREPARE_ACK, COMMIT with COMMIT_ACK, FINALIZE with FINALIZE_ACK. THREE-PHASE BROADCAST SEQUENCE Sender OSS Target Chain 1 Target Chain 2 Target Chain 3 PREPARE PREPARE_ACK (×N) [ all PREPARE_ACK received within prepareTimeoutMs ] COMMIT COMMIT_ACK (×N) PRIMARY FINALITY ISSUED FINALIZE [ locks released, coherence = SYNCED ]
THREE-PHASE BROADCAST SEQUENCE
PREPARE
Sender OSS dispatches PREPARE to all target chains in parallel. Each chain responds with PREPARE_ACK reserving locks.
[ all PREPARE_ACK received within prepareTimeoutMs ]
COMMIT
Sender OSS dispatches COMMIT to all target chains. Each chain applies the state change and responds with COMMIT_ACK.
PRIMARY FINALITY ISSUED
FINALIZE
Sender OSS dispatches FINALIZE to all target chains, releasing coordination locks.
[ locks released, coherence = SYNCED ]

Figure 1: Three-phase broadcast sequence with three target chains

Each phase message is delivered alongside its own ACK round. ACKs reuse the standard ACK message type defined in Message Protocol, but they carry a phase-aware ackType that distinguishes coordination responses from terminal responses, and a PhaseAckContext that links the response to the originating round.

interface CrossChainPhasePayload {
  phase: "PREPARE" | "COMMIT" | "FINALIZE";
  originalMessageId: string;        // messageId of the initial cross-chain message
  phaseSequence: number;            // 0 = PREPARE, 1 = COMMIT, 2 = FINALIZE
  innerPayload: OIPPayload;         // the actual message payload being delivered
}

// Phase-aware ackType values (in addition to the terminal RECEIVED/PROCESSED/FAILED/REJECTED set)
type PhaseAckType =
  | "PHASE_PREPARE_OK"   // PREPARE accepted; resources reserved
  | "PHASE_COMMIT_OK"    // COMMIT applied
  | "PHASE_FINALIZE_OK"  // FINALIZE confirmed
  | "PHASE_REJECT";      // any phase refused

interface PhaseAckContext {
  phase: "PREPARE" | "COMMIT" | "FINALIZE";
  originalMessageId: string;        // matches the originating phase message
  chainId: string;                  // CAIP-2 of the responding chain
  reservedResources?: string[];     // PHASE_PREPARE_OK only: lock tokens or reservation IDs
}

An OIP node MUST treat the three rounds as a single logical delivery: the same originalMessageId identifies the message across all phases, and ACKs from earlier phases MUST be received before the next phase is dispatched (with the BEST_EFFORT exception described below). The terminal ACK semantics (RECEIVED, PROCESSED, FAILED, REJECTED) reported in Message Protocol remain in force, but coordination responses are reported via the phase-aware ackType values above so that coordinators do not confuse a per-round response with a terminal one.

The PhaseAckContext.reservedResources field is significant: a PHASE_PREPARE_OK response is not a passive acknowledgement but an active resource reservation. The responding chain reports the lock tokens it holds on behalf of the message, and those tokens are released either by the corresponding FINALIZE round or by the lock TTL expiring. Implementations MUST surface these tokens to the coordinator so that retry, rollback, and timeout paths reference the same reservation.

Atomicity-driven Phase Behavior

The three-phase shape is fixed, but each AtomicityStrategy reshapes which phases run, what timeouts apply, and how partial failure is handled. Routing selects the strategy per message; this section specifies the resulting behavior. Note that messages with Priority.CRITICAL are forced to ALL_OR_NOTHING regardless of any value the sender placed in RoutingMetadata.atomicity; the escalation rule is specified in Routing.

Figure 2: Atomicity-driven reshaping of the three-phase broadcast

BEST_EFFORT

BEST_EFFORT collapses the three rounds into a single COMMIT round dispatched in parallel to all targets. PREPARE is skipped because the strategy permits partial application by definition; reserving locks across all targets would only add coordination cost without changing the outcome on failure. FINALIZE may be skipped or replaced by a lightweight ACK.

Targets that fail are logged and the message is considered delivered with respect to the targets that succeeded. There is no rollback, no DIVERGED transition, and no Reconciliation trigger. BEST_EFFORT is appropriate for messages whose value is informational (heartbeats, queries, observability traffic) rather than state-changing.

GUARANTEED

GUARANTEED runs the full three-phase broadcast against the message’s TargetScope. Failure semantics are layered by phase, and each layer has a specific normative behavior:

  • PREPARE failure: clean abort with no state change on any chain. The PHASE_PREPARE_OK reservation is discarded and locks acquired for the prepared message are released, but locks acquired by the OSS pipeline before PREPARE remain held during retry. PREPARE failures are retried under the message-level retry policy in Fault Tolerance; locks must persist across retries (see Lock TTL alignment below).
  • COMMIT partial failure: the asset transitions to DIVERGED coherence and Reconciliation against the primary finality record is triggered (see State Management § Cross-chain Coherence).
  • FINALIZE failure: no auto-rollback. State is already applied on the chains that committed; rolling back on a transient FINALIZE failure would be more dangerous than tolerating an unconfirmed label. FINALIZE is retried; chains that did not receive FINALIZE keep their lock until the lock’s TTL expires, at which point the lock is released automatically (Lock timeout safety, State Management § Preemptive Lock Protocol).

Recommended phase timeouts are:

AtomicityprepareTimeoutMscommitTimeoutMsfinalizeTimeoutMs
GUARANTEED5000100005000
ALL_OR_NOTHING5000100005000

A timeout marks the phase as failed. Phase failure handling then follows the rules above, not a generic retry; the retry policy in Fault Tolerance applies to message-level redelivery, not to whether the phase outcome is reinterpreted.

ALL_OR_NOTHING

ALL_OR_NOTHING runs the same three phases as GUARANTEED but with two strict additions: the routing scope is overridden to ALL_CHAINS regardless of the message’s declared TargetScope, and partial application is forbidden at COMMIT.

  • PREPARE failure: full abort. No chain has changed state, so each participating chain discards the prepared message and releases its lock. No rollback is needed.
  • COMMIT partial failure: chains that already committed receive an explicit rollback message; all chains release their locks. The asset’s coherence state may transition to ROLLBACK_INCOMPLETE if the rollback itself fails to converge in time.
  • FINALIZE failure: behaves as in GUARANTEED. State is already applied on every chain, so auto-rollback is not triggered; FINALIZE is retried, then escalated to operator. Chains without FINALIZE rely on lock TTL expiry for cleanup.

The reason the PREPARE-failure path in ALL_OR_NOTHING is “clean” is that no chain has applied state yet at PREPARE. PREPARE only reserves locks and probes applicability; aborting requires nothing more than dropping the prepared message and releasing the lock. This is structurally why OIP separates PREPARE from COMMIT in the first place.

ALL_OR_NOTHING COMMIT Rollback

When ALL_OR_NOTHING fails after one or more chains have already committed, the sender issues a RollbackMessage targeting the affected chains.

interface RollbackMessage {
  rollbackTargetMessageId: string;  // originalMessageId of the COMMIT to be rolled back
  rollbackReason: string;           // human-readable failure reason
  affectedChains: string[];         // chains that successfully committed and now need to revert
  rollbackDeadline: string;         // ISO 8601, maxRollbackWindow upper bound
}

The rollback message itself is dispatched with BEST_EFFORT atomicity. This is intentional: if the rollback were ALL_OR_NOTHING, a rollback failure would require a “rollback of the rollback”, which leads to unbounded recursion. BEST_EFFORT puts the recovery responsibility on a single round and falls back to a coherence-state transition when the round does not converge.

If all affected chains do not complete the rollback within maxRollbackWindow (default 60 seconds), the asset transitions to the ROLLBACK_INCOMPLETE coherence state and waits for governance intervention. The detailed resolution path for that state, including the GOVERNANCE-message exception that allows resolution traffic through during a freeze, is specified in State Management § Cross-chain Coherence.

Lock TTL Alignment with Retry Window

An OIP-compliant cross-chain implementation MUST align lock TTLs with the message-level retry window. The constraint is straightforward: locks acquired for a cross-chain message must remain held throughout that message’s retry window, otherwise an in-flight retry can race against another transaction that acquired the released lock.

The required relationship is:

Lock TTLmessage TTL+(max_retries×max_backoff)

Using the retry policy in Fault Tolerance, the longest retry windows in OIP v0.5 are STATE_SYNC at 60s × 5 = 300s and GOVERNANCE EXECUTE at 60s × 7 = 420s. An implementation processing these message types over a cross-chain coordination round MUST configure the lock TTL so that messageTtlSeconds + retryWindow fits within the lock’s expiry.

This requirement is normative because it bridges two separate specification regions: lock semantics (Preemptive Lock Protocol) and delivery semantics (Retry Policies). The cross-chain protocol is the place where the two interact, so the alignment is stated here. Specific lock state mechanics are in State Management; specific retry policies are in Fault Tolerance.

Two-tier Finality

OIP cross-chain delivery is structured around two distinct finality tiers. The separation matters because trading on tokenized assets must be permitted before external settlement to Base L2 completes; otherwise, every cross-chain message would inherit Base L2’s finality latency.

TierWhenWhat is FinalizedTrading Effect
Primary FinalityGKR proof issuance inside OSSOSS State Root for the message; deterministic application across the participating chainsTrading is permitted (Pre-trading Finality)
Secondary Finalityfflonk → zkVerify → Base L2 settlementExternally verifiable proof of the primary finality recordExternal settlement guarantee; trading already enabled at Tier 1
Two-tier finality timeline Horizontal timeline. Primary finality at GKR proof issuance enables trading. Secondary finality at Base L2 settlement comes later. A gap recovery path is shown for cases where external propagation lags or fails. TWO-TIER FINALITY TIMELINE time COMMIT round PRIMARY FINALITY GKR proof issued trading permitted FINALIZE propagation window (sub-second to minutes) if exceeded → pending sync queue redelivery → DIVERGED → Reconciliation SECONDARY FINALITY Base L2 settlement
TWO-TIER FINALITY TIMELINE
PRIMARY FINALITY
GKR proof issued inside OSS at the end of the COMMIT round
trading permitted
propagation window: sub-second to minutes
if exceeded → pending sync queue redelivery → DIVERGED → Reconciliation
SECONDARY FINALITY
Base L2 settlement via fflonk → zkVerify

Figure 3: Primary finality enables trading; secondary finality is external settlement

Primary Finality

Primary finality is reached when the OSS issues the GKR proof for the message’s epoch and the corresponding OSS State Root is fixed. From that moment, the message’s effect on the participating chains is deterministically settled inside OSS. The PrimaryFinalityRecord is persisted as the source of truth used by Reconciliation, by Force Inclusion path settlement, and by any subsequent gap-recovery procedure.

interface PrimaryFinalityRecord {
  ossStateRoot: string;             // 0x-prefixed hex, 32 bytes
  gkrProofHash: string;             // hash of the issued GKR proof
  issuedAt: string;                 // ISO 8601
  ossEpoch: number;
  appliedMessageIds: string[];      // messages applied in this epoch
  participatingChains: string[];    // chains affected
}

Trading on tokenized assets is permitted from the primary finality timestamp. This is what “Pre-trading Finality” means in OIP v0.5: the system does not require external settlement on Base L2 before allowing the next on-chain action against the asset. The cost of waiting for Base L2 would be tens of seconds to minutes per message, which is incompatible with the latency requirements that motivated OSS in the first place.

Secondary Finality

Secondary finality is the externally verifiable settlement of the primary finality record. The OSS aggregates GKR proofs into an fflonk wrapper, submits the wrapper to zkVerify for batch verification, and the resulting attestation is recorded on Base L2. Secondary finality typically lags primary finality by tens of seconds to minutes; trading is unaffected because trading authorization is already granted at the primary tier.

The two tiers are designed to be consistent in normal operation and recoverable on divergence. The primary record always wins: if the secondary settlement reflects a different state from the primary record, the secondary is treated as wrong and is reconciled toward the primary.

Primary–Secondary Gap Recovery

When primary finality is issued but secondary finality fails to land within the expected window, OIP triggers a graduated recovery sequence:

  1. Gap detection: A HEARTBEAT message reports syncProgress.lagSeconds. If this value crosses the configured threshold (default 300 seconds), the gap is flagged.
  2. Redelivery from pending sync queue: Messages that have not reached secondary finality are redelivered. The same messageId guarantees idempotent application on chains where they already landed (see Fault Tolerance).
  3. Lock-window redelivery: While the original lock is still held, redelivery proceeds without acquiring new locks. After lock expiry, recovery first reacquires a lock and then proceeds.
  4. Reconciliation trigger: If accumulated redelivery attempts fail, the asset transitions to DIVERGED and Reconciliation forces every participating chain back to the primary finality record’s state.

The Reconciliation procedure itself, including the system-issued STATE_SYNC messages it dispatches and the conditions under which the asset returns to SYNCED, is specified in State Management § Cross-chain Coherence.

Force Inclusion and Primary Finality

Transactions that enter the L3 through Force Inclusion (the operator-bypass path through Base L2) skip the standard OIP validation pipeline. To keep the same finality semantics across both paths, OIP defines a constrained primary-finality acceptance rule:

  • Primary finality for a Force Inclusion transaction is recognized only if the RWA Registry self-defense check passes. The self-defense check enforces authority and state-invariant constraints at the contract layer, replacing the OIP validation that was bypassed.
  • External propagation (secondary finality) for Force Inclusion transactions is held until the D-quencer is restored. Once restored, the OSS pending sync queue picks up the deferred propagation.

The combined rule is that both the OIP path and the Force Inclusion path produce the same primary-finality semantics. Trading authorization, Reconciliation source-of-truth, and downstream behavior do not depend on which path the transaction took.

Relationship with Bind-Verify-Commit (BVC)

OSS implementations follow the Bind-Verify-Commit pattern at the OSS-node lifecycle layer. The cross-chain rounds specified on this page sit inside the BVC.Commit phase; they are not a competing pattern. The two layers serve different purposes, but they share the word “commit”, which can be a source of confusion.

BVC and three-phase broadcast nesting Outer container is the BVC lifecycle with three phases Bind Verify Commit. Inside the Commit phase, the three-phase broadcast PREPARE COMMIT FINALIZE is nested showing one or more rounds may be issued. BVC ENVELOPE AND CROSS-CHAIN ROUNDS BVC LIFECYCLE (OSS-NODE LAYER) Bind acquire preemptive locks across all domains Verify validate transition issue GKR proof Commit primary finality reached at end of round CROSS-CHAIN ROUND (REPEATABLE) PREPARE probe + lock COMMIT apply state FINALIZE release round may run once or repeat per cross-chain message BVC.Commit completes after all rounds + primary finality
BVC ENVELOPE AND CROSS-CHAIN ROUNDS
BVC LIFECYCLE (OSS-NODE LAYER)
Bind
Acquire preemptive locks across all domains.
Verify
Validate state transition and issue GKR proof.
Commit
Primary finality reached at end of round.
CROSS-CHAIN ROUND (REPEATABLE)
PREPARE
probe + lock
COMMIT
apply state
FINALIZE
release
round may run once or repeat per cross-chain message; BVC.Commit completes after all rounds + primary finality

Figure 4: Cross-chain rounds are nested inside BVC.Commit, not a parallel pattern

TermLayerMeaning
BVC.CommitOSS-node sync lifecycleThe final phase of an OSS sync job, completed when all participating domains reflect the new state and primary finality is issued
COMMIT (in PREPARE/COMMIT/FINALIZE)Cross-chain message coordinationThe middle round of the three-phase broadcast, where state is actually applied on each chain

Inside a single BVC.Commit, OSS issues one or more PREPARE/COMMIT/FINALIZE rounds. The two “commit” labels are not the same instant: the inner COMMIT is one round inside the outer envelope, and BVC.Commit completes only after every required round has finished and primary finality is recorded. The naming is preserved because BVC is the canonical OSS-layer term defined in the Oraclizer system specification, and the three-phase round names follow staged-coordination convention; OIP v0.5 does not rename either term and instead documents the distinction explicitly.


Implementation Notes

  • Phase identification: Implementations MUST include CrossChainPhasePayload.phase and phaseSequence in every cross-chain round message. RoutingMetadata alone is not sufficient because routing data does not differentiate between rounds of the same delivery.
  • Phase ACK ackType: Coordination ACKs MUST use one of PHASE_PREPARE_OK, PHASE_COMMIT_OK, PHASE_FINALIZE_OK, or PHASE_REJECT in ackType, paired with a populated PhaseAckContext. Using terminal values (PROCESSED, FAILED) for a phase response is non-conformant.
  • Idempotent ACKs: ACKs for any phase MUST be idempotent. A target chain that receives the same phase message twice MUST return the same ACK; duplicate processing on the receiver side is forbidden.
  • Lock TTL alignment (MUST): Configure Lock TTL ≥ message TTL + (max_retries × max_backoff) for every cross-chain message type the implementation supports. Operating with a shorter lock TTL is non-conformant because retries can race against released locks.
  • Phase-level retry vs message-level retry: Phase timeouts terminate a phase; they do not reissue it. Message-level redelivery (Fault Tolerance retry policy) is what reissues the round. Implementers should not blur the two.
  • Rollback messages are BEST_EFFORT by design: Do not promote a rollback to GUARANTEED or ALL_OR_NOTHING. The unbounded recursion that results is a documented anti-pattern.
  • Primary finality is the source of truth: All recovery procedures (gap recovery, Reconciliation, Force Inclusion settlement) reconcile against the primary finality record. Implementations MUST persist PrimaryFinalityRecord in a verifiable form for the long term, even after locks have expired.

Related Pages

  • Routing: how AtomicityStrategy is decided per message and how it maps to coordination behavior, including the CRITICAL → ALL_OR_NOTHING escalation rule.
  • State Management: coherence states (SYNCED, SYNCING, DIVERGED, ROLLBACK_INCOMPLETE), Reconciliation procedure, Preemptive Lock Protocol, and Pending Sync Queue Invariant.
  • Regulatory Actions: how CRITICAL-priority regulatory actions interact with cross-chain delivery.
  • Fault Tolerance: at-least-once delivery, idempotency, retry policies, and DLQ.
  • Error Reference: cross-chain error codes (CROSS_CHAIN_STATE_CONFLICT, DIVERGED_STATE_DETECTED, RECONCILIATION_REQUIRED, ROLLBACK_INCOMPLETE_REJECT).

Table of Contents