From e2bd142e620e2d85f9875eea2c71734ac7db1ed0 Mon Sep 17 00:00:00 2001 From: Flux <175354924+0xiamflux@users.noreply.github.com> Date: Mon, 19 Jan 2026 20:10:59 -0500 Subject: [PATCH 1/2] fix: set fault and permission games implementations to non-zero address on v2 codepath --- .../scripts/deploy/DeployOPChain.s.sol | 25 ++++++++--- .../test/opcm/DeployOPChain.t.sol | 41 +++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 6f491e15d11..a17f1168a21 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -28,7 +28,7 @@ import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { GameTypes } from "src/dispute/lib/Types.sol"; +import { GameTypes, GameType } from "src/dispute/lib/Types.sol"; contract DeployOPChain is Script { /// @notice The default init bond for the dispute games. @@ -223,9 +223,24 @@ contract DeployOPChain is Script { /// @return output_ The output parameters. function _fromOPCMV2OutputToOutput(IOPContractsManagerV2.ChainContracts memory _chainContracts) internal - pure + view returns (Output memory output_) { + // PERMISSIONED_CANNON must be enabled. + address permissionedDgImpl = + address(_chainContracts.disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)); + + // We first find the respected game type, if it's a fault game we can set the faultDisputeGame to the respected + // game implementation, otherwise we set it to address(0). + // This is to account for scenarios where both fault games are enabled. + GameType respectedGameType = _chainContracts.anchorStateRegistry.respectedGameType(); + address respectedGameImpl = address(_chainContracts.disputeGameFactory.gameImpls(respectedGameType)); + bool isFaultGame = ( + respectedGameType.raw() == GameTypes.CANNON.raw() || respectedGameType.raw() == GameTypes.CANNON_KONA.raw() + || respectedGameType.raw() == GameTypes.SUPER_CANNON.raw() + || respectedGameType.raw() == GameTypes.SUPER_CANNON_KONA.raw() + ); + output_ = Output({ opChainProxyAdmin: _chainContracts.proxyAdmin, addressManager: _chainContracts.addressManager, @@ -238,10 +253,10 @@ contract DeployOPChain is Script { ethLockboxProxy: _chainContracts.ethLockbox, disputeGameFactoryProxy: _chainContracts.disputeGameFactory, anchorStateRegistryProxy: _chainContracts.anchorStateRegistry, - faultDisputeGame: IFaultDisputeGame(address(0)), - permissionedDisputeGame: IPermissionedDisputeGame(address(0)), + faultDisputeGame: isFaultGame ? IFaultDisputeGame(respectedGameImpl) : IFaultDisputeGame(address(0)), + permissionedDisputeGame: IPermissionedDisputeGame(permissionedDgImpl), delayedWETHPermissionedGameProxy: _chainContracts.delayedWETH, - delayedWETHPermissionlessGameProxy: IDelayedWETH(payable(address(0))) + delayedWETHPermissionlessGameProxy: IDelayedWETH(payable(_chainContracts.delayedWETH)) }); } diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index b341f68cbaf..6cecd743eba 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -308,6 +308,47 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), 0, "CANNON init bond"); } + /// @notice Tests that faultDisputeGame is set to address(0) and permissionedDisputeGame is set to the correct + /// implementation for GameTypes.PERMISSIONED_CANNON. + function test_run_faultDisputeGamePermissionedCannon_succeeds() public { + skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); + + _assertDisputeGames(GameTypes.PERMISSIONED_CANNON, false); + } + + /// @notice Tests that faultDisputeGame is set to GameTypes.CANNON. + function test_run_faultDisputeGameCannon_succeeds() public { + skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); + + _assertDisputeGames(GameTypes.CANNON, true); + } + + /// @notice Tests that faultDisputeGame is set to GameTypes.CANNON_KONA. + function test_run_faultDisputeGameCannonKona_succeeds() public { + skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); + + _assertDisputeGames(GameTypes.CANNON_KONA, true); + } + + /// @notice Helper function that runs DeployOPChain.run and asserts DeployOPChain.Output.faultDisputeGame and + /// DeployOPChain.Output.permissionedDisputeGame are set to the correct implementations. + function _assertDisputeGames(GameType _gameType, bool _expectFault) internal { + deployOPChainInput.disputeGameType = _gameType; + + DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput); + + address expectedPermissioned = address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); + assertEq(address(doo.permissionedDisputeGame), expectedPermissioned, "PDG impl"); + + if (_expectFault) { + address expectedFault = address(doo.disputeGameFactoryProxy.gameImpls(_gameType)); + assertEq(address(doo.faultDisputeGame), expectedFault, "FDG impl"); + assertNotEq(address(doo.faultDisputeGame), address(0), "FDG non-zero"); + } else { + assertEq(address(doo.faultDisputeGame), address(0), "FDG zero"); + } + } + /// @notice Checks for additional assertions that are not covered by the basic non-zero and code checks in /// `DeployOPChain.checkOutput`. /// @param doo The output of the deployment. From 9c4a77e2108a5b392a58bfe6ab1bfad785959fcc Mon Sep 17 00:00:00 2001 From: Flux <175354924+0xiamflux@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:50:42 -0500 Subject: [PATCH 2/2] chore: make faultDisputeGame 0 on DeployOPChain --- .../scripts/deploy/DeployOPChain.s.sol | 14 ++--------- .../test/opcm/DeployOPChain.t.sol | 25 +++++++------------ 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index a17f1168a21..72db7c96c93 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -230,17 +230,6 @@ contract DeployOPChain is Script { address permissionedDgImpl = address(_chainContracts.disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)); - // We first find the respected game type, if it's a fault game we can set the faultDisputeGame to the respected - // game implementation, otherwise we set it to address(0). - // This is to account for scenarios where both fault games are enabled. - GameType respectedGameType = _chainContracts.anchorStateRegistry.respectedGameType(); - address respectedGameImpl = address(_chainContracts.disputeGameFactory.gameImpls(respectedGameType)); - bool isFaultGame = ( - respectedGameType.raw() == GameTypes.CANNON.raw() || respectedGameType.raw() == GameTypes.CANNON_KONA.raw() - || respectedGameType.raw() == GameTypes.SUPER_CANNON.raw() - || respectedGameType.raw() == GameTypes.SUPER_CANNON_KONA.raw() - ); - output_ = Output({ opChainProxyAdmin: _chainContracts.proxyAdmin, addressManager: _chainContracts.addressManager, @@ -253,7 +242,8 @@ contract DeployOPChain is Script { ethLockboxProxy: _chainContracts.ethLockbox, disputeGameFactoryProxy: _chainContracts.disputeGameFactory, anchorStateRegistryProxy: _chainContracts.anchorStateRegistry, - faultDisputeGame: isFaultGame ? IFaultDisputeGame(respectedGameImpl) : IFaultDisputeGame(address(0)), + // Explicitly set to address(0) maintaining consistency with OPCM v1 behavior. + faultDisputeGame: IFaultDisputeGame(address(0)), permissionedDisputeGame: IPermissionedDisputeGame(permissionedDgImpl), delayedWETHPermissionedGameProxy: _chainContracts.delayedWETH, delayedWETHPermissionlessGameProxy: IDelayedWETH(payable(_chainContracts.delayedWETH)) diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 6cecd743eba..b8f141bae8d 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -313,40 +313,33 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { function test_run_faultDisputeGamePermissionedCannon_succeeds() public { skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); - _assertDisputeGames(GameTypes.PERMISSIONED_CANNON, false); + _assertDisputeGames(GameTypes.PERMISSIONED_CANNON); } - /// @notice Tests that faultDisputeGame is set to GameTypes.CANNON. + /// @notice Tests that faultDisputeGame is set to address(0) when disputeGameType is GameTypes.CANNON. function test_run_faultDisputeGameCannon_succeeds() public { skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); - _assertDisputeGames(GameTypes.CANNON, true); + _assertDisputeGames(GameTypes.CANNON); } - /// @notice Tests that faultDisputeGame is set to GameTypes.CANNON_KONA. + /// @notice Tests that faultDisputeGame is set to address(0) when disputeGameType is GameTypes.CANNON_KONA. function test_run_faultDisputeGameCannonKona_succeeds() public { skipIfDevFeatureDisabled(DevFeatures.OPCM_V2); - _assertDisputeGames(GameTypes.CANNON_KONA, true); + _assertDisputeGames(GameTypes.CANNON_KONA); } - /// @notice Helper function that runs DeployOPChain.run and asserts DeployOPChain.Output.faultDisputeGame and - /// DeployOPChain.Output.permissionedDisputeGame are set to the correct implementations. - function _assertDisputeGames(GameType _gameType, bool _expectFault) internal { + /// @notice Helper function that runs DeployOPChain.run and asserts DeployOPChain.Output.faultDisputeGame is set to + /// address(0) and DeployOPChain.Output.permissionedDisputeGame is set to the correct implementation. + function _assertDisputeGames(GameType _gameType) internal { deployOPChainInput.disputeGameType = _gameType; DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput); address expectedPermissioned = address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); assertEq(address(doo.permissionedDisputeGame), expectedPermissioned, "PDG impl"); - - if (_expectFault) { - address expectedFault = address(doo.disputeGameFactoryProxy.gameImpls(_gameType)); - assertEq(address(doo.faultDisputeGame), expectedFault, "FDG impl"); - assertNotEq(address(doo.faultDisputeGame), address(0), "FDG non-zero"); - } else { - assertEq(address(doo.faultDisputeGame), address(0), "FDG zero"); - } + assertEq(address(doo.faultDisputeGame), address(0), "FDG should be set to address(0)"); } /// @notice Checks for additional assertions that are not covered by the basic non-zero and code checks in