> ## Documentation Index
> Fetch the complete documentation index at: https://docs.optimism.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Interop reorg awareness

> How OP Stack interop enables low-latency interop and avoids the double-spend problem.

<Info>OP Stack interop is in active development. Some features may be experimental.</Info>

[A chain reorganization, or "reorg",](https://www.alchemy.com/overviews/what-is-a-reorg#what-happens-to-reorgs-after-the-merge) happens when validators disagree on the most accurate version of the blockchain.
If not handled correctly, reorgs in a cross-chain context could result in a [double-spend problem](https://en.wikipedia.org/wiki/Double-spending).
The most frequent solution to mitigate the double-spend problem is to wait for Ethereum finality; however, that solution results in high latency cross-chain communication and a poor user experience.

<Expandable title="What is double-spending?">
  ```mermaid theme={null}

  flowchart LR
      classDef ok fill:#8F8

      subgraph src["Source chain"]
          direction TB
          debit["Tokens debited"]
          emit["Initiating message emitted"]
          debit --> emit
      end

      subgraph dst["Destination chain"]
          direction TB
          receive["Initiating message verified"]
          credit["Tokens credited"]
          receive --> credit
      end

      emit ==>|cross-chain message|receive

      class debit,emit,receive,credit ok
  ```

  In a normal asset transfer tokens are debited on the source chain first, then a message is sent to the destination chain.
  When that message is received, the tokens are credited on the destination chain, where the user can now use those tokens.

  ```mermaid theme={null}

  flowchart LR
      classDef bad fill:#F89

      subgraph src["Source chain (after reorg)"]
          direction TB
          gone["Initiating transaction reorged out<br/>tokens never debited"]
      end

      subgraph dst["Destination chain"]
          direction TB
          receive["Stale initiating message accepted"]
          credit["Tokens credited"]
          receive --> credit
      end

      gone -.->|message no longer canonical|receive

      class gone,receive,credit bad
  ```

  A double-spend problem occurs when the destination chain receives a valid initiating message, but due to issues on the source chain, such as a reorg, that initiating transaction is no longer valid.
  When that happens, the tokens are still on the source chain, but they are also on the destination chain.

  The mechanism varies by asset.
  ETH is locked and unlocked in an on-chain liquidity contract; SuperchainERC20 tokens are burned and minted.
  The double-spend risk is the same either way.
</Expandable>

Most solutions to mitigate the double-spend problem rely on [L1 finality](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/#finality). However, that solution results in high latency and poor user experience.

To mitigate the double-spend problem while delivering a low-latency cross-chain experience, OP Stack interop uses [block safety levels](/op-stack/interop/explainer#block-safety-levels).
This means users can transfer assets across OP Stack chains with 1-block latency, and should a reorg happen, either both the source and destination transactions remain or both of them revert.
In every case, there is no window of opportunity to double spend.

## Block safety levels

```mermaid theme={null}

graph LR
    classDef safe fill:#8F8
    classDef unsafe fill:#F89

    subgraph A ["Chain A"]
      A100["A<sub>100</sub>"]-->A101["A<sub>101</sub>"]-->A102["A<sub>102</sub>"]-->A103["A<sub>103</sub>"]
    end
    subgraph B ["Chain B"]
      B300["B<sub>300</sub>"]-->B301["B<sub>301</sub>"]-->B302["B<sub>302</sub>"]-->B303["B<sub>303</sub>"]
    end
    A101-.->B302
    B300-.->A100
    class A100,B300,B301 safe
    class A101,A102,A103,B302,B303 unsafe
```

In the diagram above, solid arrows show derivation of a block from the previous block in the chain. Dotted arrows go from a block with an initiating message (source) to a block with the matching executing message (destination). Throughout this page, blocks are colored by safety level: finalized (grey), safe (green), or unsafe (red).

Blockchain A has only published block A<sub>100</sub> to L1. Block A<sub>101</sub> is still unsafe, and so is every block that depends on it — directly (A<sub>102</sub> and B<sub>302</sub>) or indirectly (A<sub>103</sub> and B<sub>303</sub>). Even if blocks B<sub>302</sub> and B<sub>303</sub> have themselves been written to L1, they cannot become safe until A<sub>101</sub> does, because a block is only treated as safe once every initiating message it references is also at least safe. When A<sub>101</sub> eventually lands on L1, the chain of dependent blocks can promote in turn.

The message between A<sub>101</sub> and B<sub>302</sub> can be an asset moving across the bridge.
In that case, the initiating message (A<sub>101</sub>) burns `n` tokens on the source chain (A), and the executing message (B<sub>302</sub>) mints `n` tokens on the destination chain (B).

### How the dependency check is enforced

The dependency rule above — a block is only safe once every initiating message it references is also safe — is enforced by [op-supernode](/op-stack/interop/supernode), the component that hosts the consensus layer of every chain in a dependency set together. For each L2 timestamp, op-supernode's interop activity decides between *wait*, *advance*, *invalidate*, and *rewind*. The decision is persisted to a write-ahead log before being applied, so the supernode picks up after a restart in the same state it was in before.

Each chain's CL stays the single source of truth for safety on its own chain. The supernode influences advancement through interfaces the chain itself controls, never by reaching into the CL or the execution layer. The rest of this page describes the situations that drive each decision.

### L1 reorg

L1 reorgs typically happen at the unsafe head — only the most recent L1 blocks, before they have enough confirmations to be considered safe. Each chain's CL already filters L1 derivation through L1 safe and finalized labels, so almost all L1 reorgs never reach the L2 derivation pipeline at all.

When an L1 reorg does affect L2, one of two things happens:

* **The replacement L1 block carries the same batch data as the original.** Derivation is deterministic, so the L2 chain it produces is identical, and the reorg is a no-op from the L2 perspective.
* **The replacement L1 block does not carry that batch data.** The sequencer notices and reposts the affected batch in a later L1 block. As long as the batch lands again before the [sequencer window](https://specs.optimism.io/glossary.html#sequencing-window) elapses (3600 L1 blocks ≈ 12 hours on standard chains like OP Mainnet and Unichain), derivation reproduces the same L2 chain. If the window does elapse without the batch reappearing, the affected L2 blocks are replaced with deposit-only blocks (see [Invalid block](#invalid-block) below).

If a leaked L1 reorg leaves op-supernode with inconsistent per-chain views of L1, the interop activity issues a *rewind*: verified state is rolled back and re-derived once L1 settles.

L1 reorgs do not by themselves break interop guarantees. Either the data comes back and L2 stays identical, or the chain falls back to deposit-only blocks for that span — the same behavior as if the sequencer had simply gone offline.

### Equivocation

Sequencers inform the rest of the OP Stack chains about a new block in two ways:

* The gossip protocol, which is typically used as soon as the block is created.
  The problem is that the gossip protocol does not create a commitment.
* Posting to L1, which typically happens a few minutes after the block is created.
  The reason is cost; it is much cheaper if compression and L1 posting are done in large batches rather than for each individual block.

Equivocation happens when a sequencer publishes a block over the gossip protocol that differs from the one that eventually gets written to L1. The L1 version wins, and [op-supernode](#how-the-dependency-check-is-enforced) issues an *invalidate* for every unsafe dependent block (local or cross-chain) that built on the gossiped version.

```mermaid theme={null}

graph LR
    classDef finalized fill:#CCC
    classDef safe fill:#8F8
    classDef unsafe fill:#F89

    subgraph A ["Chain A"]
      A100["A<sub>100</sub>"]-->A101["A<sub>101</sub>"]-->A102["A<sub>102</sub>"]-->A103["A<sub>103</sub>"]
      A100-->A101b["A'<sub>101</sub>"]-->A102b["A'<sub>102</sub>"]-->A103b["A'<sub>103</sub>"]
    end
    subgraph B ["Chain B"]
      B300["B<sub>300</sub>"]-->B301["B<sub>301</sub>"]-->B302["B<sub>302</sub>"]-->B303["B<sub>303</sub>"]
      B300-->B301b["B'<sub>301</sub>"]-->B302b["B'<sub>302</sub>"]-->B303b["B'<sub>303</sub>"]
    end
    subgraph C ["Chain C"]
      C200["C<sub>200</sub>"]-->C201["C<sub>201</sub>"]-->C202["C<sub>202</sub>"]-->C203["C<sub>203</sub>"]
      C202-->C203b["C'<sub>203</sub>"]
    end
    A101-.->B302
    A101-.->B301
    A101b-.->B302b
    A101b-.->B301b    
    B300-.->A100
    B302-.->C203
    B302b-.->C203b    
    C200-.->B301    
    class C200 finalized
    class A100,B300,C201,C202 safe
    class A101b,A102b,A103b,B301b,B302b,B303b,C203b safe
    class A101,A102,A103,B301,B302,B303,C203 unsafe
```

A block in another chain can only depend on a specific block here if it referenced one of that block's logs in an executing message — and a block only earns the safe label once every initiating message it depends on has also reached at least safe. So the equivocation between A<sub>101</sub> and A'<sub>101</sub> only invalidates *unsafe* blocks: A<sub>102</sub> and A<sub>103</sub> on chain A (extending the bad history), B<sub>301</sub> and B<sub>302</sub> on chain B (which referenced A<sub>101</sub> directly) along with B<sub>303</sub> (which follows them on chain B), and C<sub>203</sub> on chain C (which referenced B<sub>302</sub>). All those unsafe blocks are reorged out and replaced by the corresponding `'`-marked blocks, which derive from A'<sub>101</sub>. Anything previously labeled safe or finalized is untouched.

### Invalid block

A block can also be canonically invalidated outright. If verifiers determine that an L2 block cannot be reproduced from L1 — for example because the batch claims an initiating message that does not exist on the source chain — the canonical chain replaces it with a *deposit-only block*: a block that contains only the deposit transactions forced through L1, with all sequencer transactions stripped out. For interop-specific failures like that example, [op-supernode](#how-the-dependency-check-is-enforced) is the verifier that makes the call.

<Expandable title="What makes a block invalid?">
  There are several potential reasons:

  * The block posted to L1 includes incorrect information — for example, a batch claims an initiating message that the source chain never actually emitted, so any executing message that references it cannot be reproduced.
  * The block was never posted at all. If the [sequencer window](https://specs.optimism.io/glossary.html#sequencing-window) elapses (3600 L1 blocks ≈ 12 hours on standard chains) without a batch covering an L1 epoch, verifiers fall back to a deposit-only block for that epoch.
</Expandable>

Functionally this is equivalent to equivocation, and the dependency rules above handle it the same way: only unsafe blocks (and any chain of blocks depending on them transitively) are reshaped, and any block that had already reached the safe label is untouched.

## Next steps

* Read the [interop explainer](/op-stack/interop/explainer) for the rest of the architecture.
* Read about [op-supernode](/op-stack/interop/supernode), the component that derives every chain in the dependency set together and enforces the safety levels described above.
* Read the [cross-chain security measures](/op-stack/security/interop-security) for safe interoperability.
* View more [interop guides and tutorials](/app-developers/guides/interoperability/get-started).
