Organization
- @optimistic-ethereum
Engagement Type
Cantina Reviews
Period
-
Repositories
Findings
Informational
3 findings
0 fixed
3 acknowledged
Informational3 findings
DEPLOY_V2_DISPUTE_GAMES rollback leaves V2 args on legacy game
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
r0bert
Description
When
DEPLOY_V2_DISPUTE_GAMESis enabled,OPContractsManager.registerPermissionedGameV2writes the 124‑byte constructor payload into the factory viasetImplementation(gameType, impl, gameArgs). Later, switching the feature flag off falls back to the three-argument helper, which only invokessetImplementation(gameType, impl)and never clearsgameArgs[gameType]. BecauseDisputeGameFactory.createalways concatenates the stored constructor blob, the stale V2 payload is still appended when cloning the legacyFaultDisputeGame. That legacy initializer rejects any calldata length other than 122 bytes and reverts withBadExtraData(), so every rollback deployment bricked fault-proof creation once V2 had been enabled. Impact: toggling off the feature via the documented governance flow leaves the system unable to produce fault proofs until an operator manually deletes the stored args.Recommendation
When reinstalling the legacy implementation, explicitly clear the cached constructor blob (for example
delete gameArgs[_gameType];before calling the two-argument setter).Permissioned bond starts at 0 until addGameType
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
r0bert
Description
_registerPermissionedGameV2registers the v2 permissioned game without touchingDisputeGameFactory.setInitBond, so a fresh factory keeps the default zero bond. The team clarified that new chains intentionally launch with only the permissioned game and no bond; once permissionless proofs are enabled viaopcm.addGameType, that upgrade also sets the init bond for both permissioned and permissionless games. This sequencing is expected and should be documented so the zero bond at genesis isn’t mistaken for a misconfiguration.Recommendation
No code change required. Document the deployment sequence to clarify that the permissioned game bond is intentionally zero at genesis and will be aligned with the permissionless bond when
opcm.addGameTyperuns.Fork-Based Upgrade Tests Summary
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
r0bert
Description
We invested effort in building manual, fork-based tests on an Optimism fork so we could observe the real contract-level behavior and gas costs of the u17 upgrade and deployment flows. Running them against live chain state ensures what we see mirrors actual production transactions.
The fork tests can be located in the following gist.
-
The
test_upgrade_u16_to_u17_forkscenario starts from a freshly deployed U16 rollup configuration and delegates toOPContractsManagerUpgrader.upgrade. The delegatecall itself costs about 4.99 M gas and proves that the non-interop upgrade path is sound: every critical proxy:SystemConfig,OptimismPortal2, L1 bridges,L1CrossDomainMessenger,DisputeGameFactory,AnchorStateRegistryandDelayedWETHlands on the U17 implementation while ownership remains with the configured proxy-admin owner. Because no dev features are enabled, the legacyETHLockboxpointer is left untouched, the portal retains its ETH and the permissioned dispute game still swaps via the two-argument setter (so any stale V2 constructor blob could still block a rollback). -
The
test_deploy_u17_chain_end_to_endpath invokesu17Deployer.deployonce, at a cost of roughly 12.94 M gas, to stand up a fully interop-enabled U17 stack. WithDevFeatures.OPTIMISM_PORTAL_INTEROPbaked into the contracts container, the call instantiatesSystemConfig, OptimismPortalInterop,ETHLockbox, all three L1 bridges, the L1 messenger,OptimismMintableERC20Factory,DisputeGameFactory,AnchorStateRegistry,DelayedWETHand the legacy permissioned dispute game. The test then inspects every initializer outcome (owners, batcher hash, unsafe signer, PDG constructor params, start anchor, lockbox authorization, ASR/DGF wiring) to demonstrate that a single transaction yields an immediately usable, interop-ready rollup. This effectively serves as the reference deploy script for new chains. -
Finally, the
test_upgrade_u16_to_u17_interop_migrates_lockboxcovers the interop-enabled upgrade. After seeding the legacy portal with 5 ETH, it runsOPContractsManagerUpgrader.upgrade(gas ~5.00 M) with the interop dev feature enabled, then, still acting as the proxy-admin owner, callsSystemConfig.setFeature("ETH_LOCKBOX", true) followed byOptimismPortalInterop.migrateLiquidity. This sequence is essential because the upgrader itself neither flips the feature flag nor sweeps funds. The test confirms the portal still points to the existing lockbox proxy (the upgrade reuses it), the manual migration drains the portal balance to zero while crediting the lockbox with exactly 5 ETH, and the portal remains authorized. In effect it documents the post-upgrade runbook: toggle the feature, run the migration and verify that the lockbox holds the ETH.
Notes & potential follow-ups
It would be helpful adding a path that clears stale dispute-game constructor blobs to remove the lingering rollback hazard. Also, another thing to remark is that the u17 deployment test consumes ~12.9M gas, which should be acceptable operationally but is noteworthy for planning and cost predictability.