Skip to main content

Upgrading a Chain From Output Roots to Super Roots

This guide depends on the interop feature, which is still in development. Do not follow it on production chains.
This runbook walks you through upgrading a single OP Stack chain from output-root dispute games (PERMISSIONED_CANNON, CANNON or CANNON_KONA) to super-root dispute games (SUPER_PERMISSIONED or SUPER_CANNON_KONA). It runs as a single opcm.upgrade call against the chain’s existing per-chain AnchorStateRegistry and DisputeGameFactory. The chain’s permission model is preserved: a permissioned chain stays permissioned with SUPER_PERMISSIONED only; a permissionless chain runs both super game types with SUPER_CANNON_KONA as the respected one. Differences between the two are called out inline. By the end you will have a chain proposing super-root claims via op-proposer using a single-chain super-root RPC endpoint and monitoring them with op-dispute-mon. On permissionless chains, op-challenger defends SUPER_CANNON_KONA claims. On permissioned chains, op-dispute-mon detects invalid SUPER_PERMISSIONED claims, op-challenger updates the AnchorStateRegistry as games finalize. Every pre-migration dispute game already in flight continues to resolve, and proven withdrawals are not invalidated.
This is opcm.upgrade, not opcm.migrate — the chain keeps its own per-chain AnchorStateRegistry and DisputeGameFactory.
Looking to switch from permissioned to permissionless fault proofs? See Migrating to permissionless fault proofs. This runbook is for chains already running fault proofs that need to move from output-root games to super-root games.

Before You Begin

You should be familiar with running superchain-ops upgrade tasks, signing through the standard signing workflow, and operating op-proposer, op-challenger, and op-dispute-mon. See Upgrade using superchain-ops for the signing workflow.

Required tooling

The versions below are the ones the optimism repo’s mise.toml pins; older versions of cast in particular may not parse the function-selector syntax used in this runbook. You also need a checkout of the optimism monorepo to run op-fetcher (used in the next step).

Gather the Required Inputs

Collect everything below before you start. Chain identity. The chain’s per-chain addresses live in the superchain-registry repository under superchain/configs/<network>/<chain-name>.toml. Open that file and read chain_id, addresses.SystemConfigProxy, and addresses.L1StandardBridgeProxy. Chain addresses and roles. Use op-fetcher to derive the rest of the on-chain configuration in a single call. It runs an embedded forge script against $L1_RPC and resolves every per-chain proxy, role, and the current fault-proof status from SystemConfig and L1StandardBridge:
Read the values you need from chain.json: Chain type. The upgrade preserves the chain’s existing permission model. chain.json reports the current respected game type at .faultProofs.respectedGameType:
  • 1 (PERMISSIONED_CANNON) — chain is permissioned. The new respected game type is 5 (SUPER_PERMISSIONED).
  • 0 (CANNON) or 8 (CANNON_KONA) — chain is permissionless. The new respected game type is 9 (SUPER_CANNON_KONA).
Init bonds. SUPER_PERMISSIONED (game type 5) is bondless and must use an initBond of 0. SUPER_CANNON_KONA (game type 9) uses 0.08 ether (80000000000000000 wei), the standard value used on existing chains. Release artifacts and infrastructure.

Verify the Preconditions

Before you change any configuration:
  • Confirm the template injects overrides.cfg.startingAnchorRoot. Open superchain-ops/src/template/OPCMUpgradeV800.sol and check _buildExtraInstructions. As of writing it only injects overrides.cfg.startingRespectedGameType and PermittedProxyDeployment. Without an overrides.cfg.startingAnchorRoot override, OPCM falls back to the existing on-chain startingAnchorRoot, which on a pre-upgrade chain is an output-root-shaped value with a block-number l2SequenceNumber — not a valid super-root anchor. The template must be extended to read a starting super-root anchor from TOML and add it as a third extra instruction. Fix the template before continuing.
  • Confirm the RPC endpoint returns a single-chain super root. Either the chain’s op-node RPC endpoint or an op-supernode /<chainID>/ endpoint can be used. Do not use a multi-chain op-supernode root endpoint; it computes roots across its full dependency set.
  • Verify the single-chain RPC endpoint is healthy. For an op-node or namespaced op-supernode endpoint, confirm it reports a recent finalized L2 head and can produce the corresponding super root:
    If an op-node returns a SafeDB error, configure --safedb.path, let the node populate records through derivation, and retry. optimism_syncStatus can succeed while SafeDB is disabled, but superroot_atTimestamp cannot serve a non-genesis timestamp without SafeDB.
  • Verify the prestate server serves the new hash.

Stage the Off-Chain Configuration

Apply these changes before you submit the on-chain upgrade. After the changes ship, the components keep operating against existing pre-migration games and pick up super-root games automatically once the upgrade lands.

Update op-challenger

Update the running op-challenger for the chain. Keep --rollup-rpc and the existing --game-types so existing games continue to be defended. Add the super-root trace type only on permissionless chains, where SUPER_CANNON_KONA is enabled. On a permissionless chain, confirm the startup log lists super-cannon-kona as registered and shows no connection errors against the single-chain super-root RPC endpoint. On a permissioned chain, confirm the existing trace types remain registered; do not configure a SUPER_PERMISSIONED trace type. op-challenger is still required on permissioned chains to update the AnchorStateRegistry as SUPER_PERMISSIONED games finalize.

Update op-dispute-mon

Update the running op-dispute-mon for the chain. Keep --rollup-rpc. op-dispute-mon discovers games from the existing DisputeGameFactory and does not need a game-type filter change. After the restart, confirm the existing-games metrics keep populating.

Leave op-proposer Unchanged for Now

Skip op-proposer until cutover. Its configuration changes in Switch op-proposer to Super Roots.

Generate the Starting Anchor Super Root

The upgrade must re-initialise <ASR> with a super-root-shaped starting anchor. The existing on-chain startingAnchorRoot is in output-root form (its l2SequenceNumber is an L2 block number), so the template must inject a fresh super-root value via overrides.cfg.startingAnchorRoot in extraInstructions. Confirm in the precondition above that the template has been extended to read this from TOML and pass it through. Compute the value using superroot_atTimestamp. Pick the current finalized timestamp (or any recent finalized timestamp), then ask the single-chain RPC endpoint for the super root at that timestamp.
Capture two values:
  • super_root (a bytes32 hash from .data.super_root) — passed as Proposal.root in overrides.cfg.startingAnchorRoot.
  • timestamp (the uint64 you passed in) — passed as Proposal.l2SequenceNumber. This field is the timestamp itself, not a block number or sequence index.

Build the superchain-ops Task

Author a new task directory under superchain-ops/src/tasks/<network>/<NNN-name>/ using the OPCMUpgradeV800 template. The schema below assumes the template has been extended (per the precondition above) to also read a starting super-root anchor and inject it as overrides.cfg.startingAnchorRoot.

Configure config.toml

The template derives everything else (per-game-type config, SuperchainConfig, validator, etc.) automatically — no further TOML required.

Capture expectedValidationErrors

Run the task in simulation mode against an L1 fork from inside the task directory:
If the validator’s output does not match expectedValidationErrors in TOML, the simulation reverts with a message of the form Unexpected errors: <actual>; expected: <expectedValidationErrors>. Read the actual codes from that revert message. The default expectation is no errorsexpectedValidationErrors = "" and a clean simulation. Any non-empty error string must be reviewed code by code, not copy-pasted. For each code:
  • Resolve it if it points at a fixable input — a wrong address, a wrong prestate, a template release mismatch, a missing override, or a registry entry that needs updating.
  • Add it to expectedValidationErrors only after justifying it with an inline comment in the TOML explaining why it is structurally expected for this chain.
The simulation passes once every printed code has been either resolved or knowingly added with justification. Treat any code you cannot explain as a hard stop.

Execute the Upgrade

  1. Stop op-proposer for the chain. Existing games continue to resolve; only new proposals halt.
  2. Leave op-challenger and op-dispute-mon running. Both were configured earlier and pick up super-root games automatically.
  3. Sign and broadcast the task using your team’s standard superchain-ops signing workflow.
  4. Wait for the L1 transaction to confirm.
The on-chain effect:
  • <ASR> is re-initialised with the supplied anchor and respected game type. The retirement timestamp is not bumped, so existing in-flight games stay valid.
  • The super dispute game implementation or implementations are installed on <DGF>.
  • Implementation pointers for the game types passed with enabled = false are cleared, blocking creation of new games of those types.

Verify the Upgrade On-Chain

Run the checks below before you cut op-proposer over.
If any check fails, stop, do not start the new proposer, and escalate via your standard incident-response channel. Recovery from a half-cut state with a misbehaving proposer is materially harder than recovery from a paused proposer.

Common Checks

Permissionless Chain Checks

Permissioned Chain Checks

Switch op-proposer to Super Roots

Start op-proposer with the new configuration. Other flags such as --proposal-interval and --poll-interval stay the same. After start-up, verify:
  • The logs show the proposer polling the configured single-chain super-root RPC endpoint and contain no references to --rollup-rpc.
  • Within one --proposal-interval, the proposer submits a new game. Inspect it:
  • Recompute the super root for the proposed timestamp and confirm it matches the proposal:
    This confirms op-proposer is correctly configured and passing valid super roots.

Verify After the Upgrade

Run these checks once the proposer is stable.
  • Pre-migration game still resolves. Pick a still-in-flight pre-migration game (use op-dispute-mon dashboards or gameAtIndex to find one) and watch its status until it resolves:
  • op-challenger traces SUPER_CANNON_KONA when enabled. On a permissionless chain, verify the startup log lists super-cannon-kona and ongoing logs contain no scheduler or game-poll errors as type-9 games appear. On a permissioned chain, op-challenger continues to update the AnchorStateRegistry when SUPER_PERMISSIONED games finalize but does not need to post claims.
  • op-dispute-mon reports super-root games. Confirm op_dispute_mon_games reports the enabled super game types and op_dispute_mon_failed_games remains zero. On a permissioned chain, alert on op_dispute_mon_games_agreement{status="disagree_defender_wins"} and Unexpected game result logs for SUPER_PERMISSIONED.
  • No new pre-migration games can be created. Optionally call DisputeGameFactory.create with one of the disabled game types and confirm it reverts.
  • Anchor advances on first super-game finalisation. The first anchor update typically lands ~7 days after the first super-root game is created (game duration plus DISPUTE_GAME_FINALITY_DELAY_SECONDS). Do not block the rollout on this — schedule a follow-up to re-run getAnchorRoot() after that window and confirm it returns the new game’s claim instead of the starting root. Track via op-dispute-mon rather than waiting in-line.