Skip to main content
This guide is for teams that move value off an OP Stack chain based on what happened on it, such as centralized exchanges, third-party bridges, liquidity networks, and custodians. It covers two requirements that the protocol cannot enforce for you:
  • Only take off-chain actions, such as crediting a user’s incoming transfer or filling a bridge order, once the transaction is in a safe block, and wait for a finalized block for high-value actions.
  • Monitor the chain’s withdrawal pause on Layer 1 (L1), and stop taking those off-chain actions like crediting a user’s account while the L2’s withdrawals are paused.
This guide uses “incoming transfers” for funds your users send to you on the OP Stack chain, and “outgoing transfers” for funds you send to the chain. In OP Stack protocol terms, a deposit moves from L1 to Layer 2 (L2), and a withdrawal moves from L2 to L1.

Before you begin

You need the following to use the examples in this guide:

Wait for safe blocks before acting off-chain

Crediting an exchange balance, filling a bridge order, or releasing funds on another chain are hard to undo. If the transaction you acted on is later removed from the chain because of a chain reorg, you may incur a loss. Depending on your risk tolerance and how high-value the action is, wait until that transaction is in a safe or finalized block before you act. These confirmation levels describe the risk of a transaction being reordered or removed from the chain. Safe blocks can still be affected by L1 reorgs. Neither level establishes that the assets are legitimate or the chain is free of security issues.

Understand the risk of unsafe blocks

An unsafe block is the Sequencer’s claim about the chain. The canonical chain is whatever OP Stack nodes derive from the batch data that the chain’s batcher posts to L1. Anyone who controls the batcher key can post batch data that omits or reorders transactions the Sequencer already shared as unsafe blocks. When a node derives a batch that does not match its unsafe chain, it reorgs the unsafe chain to match within minutes of the batch landing on L1. The standard 12-hour sequencing window is only the upper bound for batch data to appear, not a grace period before a reorg. If the data does not appear within the window, nodes derive the chain without those transactions, and they are dropped. If you act on an unsafe block, an attacker holding the compromised batcher key can make a real transfer to you, get paid out, and then remove the transfer from the canonical chain. You have no way to recover the funds through the protocol. For more information, see Transaction finality.

Choose a confirmation level

The following table lists the minimum block tag to wait for, by type of action. Where you draw the line between low and high value is a business decision. A reasonable starting point is the threshold you already use to decide how many confirmations to require on Ethereum itself, since safe carries the same L1 reorg risk as an unfinalized Ethereum block. On OP Mainnet, transactions typically reach safe within a few minutes and finalized within about 15 to 30 minutes. Chains that post batches less often take longer to reach safe.
If you act on unsafe blocks to offer faster service, you are fully trusting the chain operator’s Sequencer and batcher key for that value. Cap the total value you have outstanding against unsafe blocks at an amount you can afford to lose.

Check that a transaction is safe

Use the safe or finalized block tag with standard JSON-RPC methods. A transaction is safe when it succeeded, its block number is at or below the safe head, and its block is still the canonical block at that height. The following script checks all three conditions. Save it as check-safe.sh, fill in the placeholders, and run it with bash check-safe.sh:
Set TAG=finalized for the stricter check. The script exits with status 0 only when the transaction is safe (or finalized), so callers can gate on the exit code. It reads the node several times, and an L1 reorg can move the safe head backward between reads while the transaction’s block stays canonical. Reading the head on both sides of the canonical hash check means that case returns not yet safe instead of safe. The result is still a point-in-time answer: a later L1 reorg can undo a safe result until the block is finalized. A safe status only tells you that L1 has fixed the transaction’s position on the chain. You still need your usual checks on what the transaction did, such as decoding Ethereum Request for Comments 20 (ERC-20) Transfer logs. Query a node you operate. The node’s consensus client computes the safe and finalized heads from L1 data, so they are only as trustworthy as the node reporting them and the L1 RPC it reads from. A public or third-party RPC endpoint, including the chain operator’s own, asks you to trust whoever runs it. Alert when the safe head stops advancing. If batches stop landing on L1, the safe head stalls and incoming transfers queue up without being credited. That is the correct behavior, but your team should know when it happens.

Monitor the withdrawal pause

Every standard OP Stack chain has a Guardian that can pause withdrawals from the chain to L1. The pause is recorded in the SuperchainConfig contract on L1, so its status is public and anyone can read it. It can apply to one chain, to a group of chains that share an ETHLockbox, or to every chain that shares the same SuperchainConfig. For more information, see Pausing the bridge.

Understand what a pause means for you

A pause means the Guardian has stopped withdrawals through the canonical bridge because of a suspected or confirmed security issue. A pause can be precautionary, and it does not always mean funds are at risk. The pause does not stop the L2 chain. Blocks keep being produced, transactions keep executing, and deposits from L1 keep arriving. If an attacker has found a way to mint or steal assets on L2, the canonical bridge is closed to them, so your exchange or bridge becomes their way out.

Respond to a pause

Take the following steps when a chain you support is paused:
  1. Stop moving assets to and from the chain. Exchanges should hold incoming transfers from the chain without crediting them, and stop processing outgoing transfers to it. Bridges should stop filling orders that originate on the chain and stop sending funds to it.
  2. Get more information. Check the chain’s status page, such as the OP Mainnet status page.
  3. Resume deliberately. Treat resuming as a decision your team makes, not an automatic switch. Confirm that OptimismPortal.paused() returns false, and check the chain’s status page for guidance before you credit the transfers you held. Do not rely on an Unpaused event alone: a pause that expires emits no event, and lifting a chain-specific pause does not lift an active global pause.
You do not need to wait to be contacted before acting. The pause status is the signal. It is visible on L1 the moment the pause transaction lands, potentially before a public announcement is made.

Check the pause status

Call paused() on the chain’s OptimismPortal proxy on L1. It returns true when either the chain-specific pause or the global pause is active. The following command checks one chain:
For OP Mainnet, use the OptimismPortal proxy address listed in OP Mainnet contract addresses. For other chains, use the OptimismPortalProxy value from the chain’s configuration in the Superchain Registry. Check every chain you support against the latest L1 block, at least once per L1 block (every 12 seconds). Use latest rather than safe or finalized for this check. You’ll want the earliest possible signal. Read L1 from a node you operate or an L1 RPC provider you trust, for the same reason as on L2, because an RPC endpoint that lags or misreports can hide a pause from you.
Do not call paused() with no arguments on the SuperchainConfig contract. That function only reports the global pause and misses a pause that targets a single chain. OptimismPortal.paused() resolves the chain’s pause identifier for you.

Subscribe to pause events

To react faster than polling, watch the SuperchainConfig contract for the following events:
The identifier parameter is not indexed, so you cannot filter on it as a log topic. Filter on the event signature instead, and decode identifier from the log data. The identifier tells you which chains the event applies to:
  • address(0) is the global pause and affects every chain that uses this SuperchainConfig.
  • A chain’s own identifier depends on its contract version and configuration. In contract releases v4.1.0 through v8.0.0, the identifier is the chain’s ETHLockbox address if the ETH_LOCKBOX feature is enabled on its SystemConfig, and its OptimismPortal address if it is not. A nonzero ethLockbox() on the OptimismPortal does not by itself mean the feature is enabled. Check the feature with isFeatureEnabled(bytes32) on the SystemConfig, passing ETH_LOCKBOX encoded with cast format-bytes32-string ETH_LOCKBOX.
Whichever identifier a chain uses, OptimismPortal.paused() resolves it for you and remains the check to rely on. Read the SuperchainConfig address with superchainConfig() on the OptimismPortal. The contract also emits Paused when the Guardian extends an existing pause. Use events as a trigger to re-check, and keep polling OptimismPortal.paused() as the source of truth. Polling is required because a pause expires automatically about three months after it starts unless the Guardian extends it, and expiry does not emit an event. paused() accounts for expiry, so do not compute it yourself.

Check your integration

Confirm that your integration meets each of the following conditions:
  • Off-chain actions wait for safe, and high-value actions wait for finalized.
  • Safe and finalized status comes from a node you operate, backed by an L1 RPC endpoint you trust.
  • Any value you release on unsafe blocks has a cap.
  • An alert fires when the safe head stops advancing.
  • OptimismPortal.paused() is polled for every supported chain on every L1 block.
  • When a chain is paused, your systems automatically stop incoming and outgoing transfers for that chain and alert your on-call team.
  • Resuming after a pause is a manual decision.

Next steps