ZK Dispute Game Migration
Table of Contents
- Overview
- In-Flight Withdrawal Safety
- Common Mechanics
- Path A: SFDG → ZKDG (Isolated Chain)
- Path B: SPDG → ZKDG (Isolated Chain)
- Path C: Shared SFDG → Shared ZKDG
Overview
All migrations from an existing dispute game type to ZKDisputeGame are performed via
OPCMv2.upgrade(). No standalone migrate() contract is in scope. Three migration paths are
supported:
| Path | Source | Target | Context |
|---|---|---|---|
| A | SuperFaultDisputeGame (SFDG) | ZKDisputeGame (ZKDG) | Isolated chain |
| B | SuperPermissionedDisputeGame (SPDG) | ZKDisputeGame (ZKDG) | Isolated chain |
| C | SuperFaultDisputeGame (SFDG) | ZKDisputeGame (ZKDG) | Interop set (e.g., OPM + Unichain) |
All paths share the same core mechanics. Path-specific differences are concentrated in a small number of steps.
In-Flight Withdrawal Safety
MCP clones embed the implementation address at deployment time. Old SFDG/SPDG game clones created
before the migration retain their original implementation bytecode forever. New game clones
created after setImplementation() in DisputeGameFactory use the ZKDG implementation.
wasRespectedGameTypeWhenCreated is snapshotted at game creation. AnchorStateRegistry.isGameClaimValid()
reads this flag during withdrawal finalization. Games created under the old respected type remain
valid for in-flight withdrawal finalization even after the migration.
rootClaimByChainId is declared on all game types in scope (SFDG, SPDG, ZKDG). The Portal
calls it on all games whose type is in the isSuperGame allowlist. Old games already return the
correct answer via their existing implementation. No special handling is needed for pre-migration
games during Portal withdrawal verification.
Common Mechanics
The following steps are executed in every migration path via OPCMv2.upgrade():
-
Deploy the new implementation. Deploy
ZKDisputeGameand register it inOPContractsManagerContainer.Implementations. -
Swap the implementation in
DisputeGameFactory. Call the 3-argument overload:disputeGameFactory.setImplementation( GameTypes.ZK_GAME_TYPE, address(zkImpl), gameArgs // variable-length CWIA bytes );Old game clones are unaffected. New games created after this call use
zkImpl. -
Disable the old implementation.
disputeGameFactory.setImplementation(sourceGameType, address(0), "");This prevents new games from being created under the old type.
-
Update the respected game type in
AnchorStateRegistry.anchorStateRegistry.setRespectedGameType(GameTypes.ZK_GAME_TYPE); -
Add
ZK_GAME_TYPEtoGameTypes.isSuperGame(). Required forOptimismPortalto callrootClaimByChainIdonZKDGgames during withdrawal verification. If this was already shipped at initialZKDGlaunch, this step is a no-op. -
Reinitialize
AnchorStateRegistry(no-op for these paths). Both SFDG and SPDG already commit to super roots — the samebytes32hash format thatZKDGuses as its starting state. The existing anchor root is therefore a valid starting point forZKDGparent validation without modification. No ASR reinitializer is required for Path A, B, or C.Note: An ASR reinitializer would only be necessary when migrating from a classic
FaultDisputeGame(which commits to a per-chain output root rather than a super root). That path is not in scope here.
Path A: SFDG → ZKDG (Isolated Chain)
Single chain with its own DisputeGameFactory, AnchorStateRegistry, and OptimismPortal.
Execute Common Mechanics in a single OPCMv2.upgrade() call.
The ASR reinitializer fires once. No idempotency concerns arise since there is only one chain in scope.
Path B: SPDG → ZKDG (Isolated Chain)
Single chain currently using the permissioned dispute game, migrating to permissionless ZK proofs.
Execute Common Mechanics in a single OPCMv2.upgrade() call with the
following additional considerations:
- Cross-mode transition: this is a permissioned → permissionless transition. The OPCM upgrade
logic MUST explicitly allow
SPDG → ZK_GAME_TYPEas a valid source-to-target game type pair (i.e., it cannot assume source and target share the same permission model). - Operational readiness: permissionless infrastructure (op-challenger, prover network) MUST be operational and funded before the respected game type is switched. This is the chain operator's responsibility and is not enforced by OPCM, but MUST be verified prior to executing the upgrade.
Path C: Shared SFDG → Shared ZKDG
Note: This path is intended as future work. The design below documents the intended approach but has not yet been implemented.
This case covers chains sharing a single DisputeGameFactory,
AnchorStateRegistry, and ETHLockbox under the interop model.
Execute Common Mechanics. Because multiple chains share the same
AnchorStateRegistry, steps 4 and 5 (updating the respected game type and the isSuperGame
allowlist) affect the shared ASR and must be applied exactly once regardless of how many chains
are in the interop set.
Shared Infrastructure and Idempotent Upgrades
OPCMv2.upgrade() was designed to process one chain at a time. When applied to an interop set,
the shared AnchorStateRegistry upgrade steps may be triggered multiple times (once per chain
processed). Two approaches are available:
Option 1 — Idempotent operations: Design the ASR update steps so that applying them a second time for a subsequent chain in the set is a safe no-op (e.g., a stateful check such as "already set to this game type").
Option 2 — Multi-chain bulk function: Introduce a function that accepts multiple SystemConfig
addresses, upgrades shared infrastructure once, and handles per-chain state in a loop. This
follows the existing bulk migration patterns but is scoped to the SFDG → ZKDG transition.
The choice between these options is left to the OPCM implementation. Either approach MUST guarantee that shared infrastructure (ASR, ETHLockbox) is updated exactly once, regardless of how many chains are in the interop set.
Because SFDG already commits to super roots, no ASR anchor reinitialization is required (see step 6 in Common Mechanics).