diff --git a/packages/contracts-bedrock/scripts/checks/test-validation/exclusions.toml b/packages/contracts-bedrock/scripts/checks/test-validation/exclusions.toml index 8adb91a6091be..f9d36bf2cd7a9 100644 --- a/packages/contracts-bedrock/scripts/checks/test-validation/exclusions.toml +++ b/packages/contracts-bedrock/scripts/checks/test-validation/exclusions.toml @@ -91,4 +91,5 @@ contracts = [ "OptimismPortal2_MigrateLiquidity_Test", # Interop tests hosted in the OptimismPortal2 test file "OptimismPortal2_MigrateToSuperRoots_Test", # Interop tests hosted in the OptimismPortal2 test file "OptimismPortal2_UpgradeInterop_Test", # Interop tests hosted in the OptimismPortal2 test file + "OPContractsManager_TestBase", # Setup helper for creator pattern dispute games ] diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 7af882bfa8638..db5c0b7734ded 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -432,16 +432,6 @@ "internalType": "contract IDelayedWETH", "name": "delayedWETHPermissionlessGameProxy", "type": "address" - }, - { - "internalType": "contract IFaultDisputeGameV2", - "name": "faultDisputeGameV2", - "type": "address" - }, - { - "internalType": "contract IPermissionedDisputeGameV2", - "name": "permissionedDisputeGameV2", - "type": "address" } ], "internalType": "struct OPContractsManager.DeployOutput", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json index 28e694d5e4f5f..85e42e8fc7e0f 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json @@ -330,16 +330,6 @@ "internalType": "contract IDelayedWETH", "name": "delayedWETHPermissionlessGameProxy", "type": "address" - }, - { - "internalType": "contract IFaultDisputeGameV2", - "name": "faultDisputeGameV2", - "type": "address" - }, - { - "internalType": "contract IPermissionedDisputeGameV2", - "name": "permissionedDisputeGameV2", - "type": "address" } ], "internalType": "struct OPContractsManager.DeployOutput", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 4f6e00c353ed7..c0e20654836c6 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -20,8 +20,8 @@ "sourceCodeHash": "0xfca613b5d055ffc4c3cbccb0773ddb9030abedc1aa6508c9e2e7727cc0cd617b" }, "src/L1/OPContractsManager.sol:OPContractsManager": { - "initCodeHash": "0x01d2eceec1f7a9f7ca232cdda10da279477c7418545be4834989ab0b5007dd40", - "sourceCodeHash": "0xd226c3b4242b6a4b69ec0554146866dc262110ce3b5ce7e594d5dc0013acb7f2" + "initCodeHash": "0x52b95f66d9f57020b90c6f1ae1e1bd329c7ed32f3357d2b73f200a7f339c68da", + "sourceCodeHash": "0x8916646d6b7f5eddb545bbc5f0a9b85b191fa52a1d3fb935c022a841da33d5be" }, "src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": { "initCodeHash": "0x57d6a6729d887ead009d518e8f17fa0d26bfc97b8efe1494ab4ef8dbb000d109", diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 1af345066132f..a8d2cc26ee360 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing -import { Test, stdStorage, StdStorage } from "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; import { VmSafe } from "forge-std/Vm.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { DeployOPChain_TestBase } from "test/opcm/DeployOPChain.t.sol"; @@ -12,13 +12,7 @@ import { DelegateCaller } from "test/mocks/Callers.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Deploy } from "scripts/deploy/Deploy.s.sol"; import { VerifyOPCM } from "scripts/deploy/VerifyOPCM.s.sol"; -import { DeployOPChain } from "scripts/deploy/DeployOPChain.s.sol"; import { Config } from "scripts/libraries/Config.sol"; -import { Types } from "scripts/libraries/Types.sol"; -import { DeployImplementations } from "scripts/deploy/DeployImplementations.s.sol"; -import { DeploySuperchain } from "scripts/deploy/DeploySuperchain.s.sol"; -import { StandardConstants } from "scripts/deploy/StandardConstants.sol"; -import { ProtocolVersion } from "src/L1/ProtocolVersions.sol"; // Libraries import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; @@ -30,6 +24,7 @@ import { DevFeatures } from "src/libraries/DevFeatures.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IProxy } from "interfaces/universal/IProxy.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; @@ -61,6 +56,8 @@ import { OPContractsManagerStandardValidator } from "src/L1/OPContractsManager.sol"; import { OPContractsManagerStandardValidator } from "src/L1/OPContractsManagerStandardValidator.sol"; +import { IPermissionedDisputeGameV2 } from "../../interfaces/dispute/v2/IPermissionedDisputeGameV2.sol"; +import { IFaultDisputeGame } from "../../interfaces/dispute/IFaultDisputeGame.sol"; /// @title OPContractsManager_Harness /// @notice Exposes internal functions for testing. @@ -311,11 +308,61 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } } +/// @title OPContractsManager_Init +/// @notice Base contract for OPContractsManager tests that need v2 functionality +contract OPContractsManager_TestBase is DeployOPChain_TestBase { + IOPContractsManager.DeployOutput internal chainDeployOutput1; + IOPContractsManager.DeployOutput internal chainDeployOutput2; + IDelayedWETH internal standaloneDelayedWETH; + + function setUp() public virtual override { + DeployOPChain_TestBase.setUp(); + + chainDeployOutput1 = createChainContracts(100); + chainDeployOutput2 = createChainContracts(101); + + vm.deal(address(chainDeployOutput1.ethLockboxProxy), 100 ether); + vm.deal(address(chainDeployOutput2.ethLockboxProxy), 100 ether); + + // Deploy a standalone DelayedWETH for tests to avoid ProxyAdmin ownership issues + standaloneDelayedWETH = deployStandaloneDelayedWETH(); + } + + /// @notice Deploy a standalone DelayedWETH contract for testing + function deployStandaloneDelayedWETH() internal returns (IDelayedWETH) { + // Deploy proxy + address delayedWETHProxy = DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(this)))) + }); + + // Deploy implementation + address delayedWETHImpl = address(opcm.implementations().delayedWETHImpl); + + // Initialize the proxy with the implementation + IProxy(payable(delayedWETHProxy)).upgradeTo(delayedWETHImpl); + IDelayedWETH(payable(delayedWETHProxy)).initialize(chainDeployOutput1.systemConfigProxy); + + return IDelayedWETH(payable(delayedWETHProxy)); + } + + /// @notice Helper function to deploy a new set of L1 contracts via OPCM. + /// @param _l2ChainId The L2 chain ID to deploy the contracts for. + function createChainContracts(uint256 _l2ChainId) internal returns (IOPContractsManager.DeployOutput memory) { + deployOPChainInput.l2ChainId = _l2ChainId; + IOPContractsManager.DeployInput memory deployInput = toOPCMDeployInput(deployOPChainInput); + deployInput.saltMixer = string.concat("test-", vm.toString(_l2ChainId)); + + return opcm.deploy(deployInput); + } +} + /// @title OPContractsManager_TestInit /// @notice Reusable test initialization for `OPContractsManager` tests. contract OPContractsManager_TestInit is CommonTest { IOPContractsManager.DeployOutput internal chainDeployOutput1; IOPContractsManager.DeployOutput internal chainDeployOutput2; + IDelayedWETH internal standaloneDelayedWETH; function setUp() public virtual override { super.setUp(); @@ -325,6 +372,27 @@ contract OPContractsManager_TestInit is CommonTest { vm.deal(address(chainDeployOutput1.ethLockboxProxy), 100 ether); vm.deal(address(chainDeployOutput2.ethLockboxProxy), 100 ether); + + // Deploy a standalone DelayedWETH for tests to avoid ProxyAdmin ownership issues + standaloneDelayedWETH = deployStandaloneDelayedWETH(); + } + + /// @notice Deploy a standalone DelayedWETH contract for testing + function deployStandaloneDelayedWETH() internal returns (IDelayedWETH) { + // Deploy proxy + address delayedWETHProxy = DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(this)))) + }); + + // Deploy implementation + address delayedWETHImpl = address(opcm.implementations().delayedWETHImpl); + + // Initialize the proxy with the implementation + IProxy(payable(delayedWETHProxy)).upgradeTo(delayedWETHImpl); + IDelayedWETH(payable(delayedWETHProxy)).initialize(chainDeployOutput1.systemConfigProxy); + + return IDelayedWETH(payable(delayedWETHProxy)); } /// @notice Sets up the environment variables for the VerifyOPCM test. @@ -432,23 +500,15 @@ contract OPContractsManager_ChainIdToBatchInboxAddress_Test is Test { /// @title OPContractsManager_AddGameType_Test /// @notice Tests the `addGameType` function of the `OPContractsManager` contract. -contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { +contract OPContractsManager_AddGameType_Test is OPContractsManager_TestBase { event GameTypeAdded( uint256 indexed l2ChainId, GameType indexed gameType, IDisputeGame newDisputeGame, IDisputeGame oldDisputeGame ); - function setUp() public virtual override { - super.setUp(); - - // Skip AddGameType tests when V2 dispute games are enabled - // TODO(#17260): Remove skip when V2 dispute game support for addGameType implemented - skipIfDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES); - } - /// @notice Tests that we can add a PermissionedDisputeGame implementation with addGameType. function test_addGameType_permissioned_succeeds() public { // Create the input for the Permissioned game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.PERMISSIONED_CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(true); // Run the addGameType call. IOPContractsManager.AddGameOutput memory output = addGameType(input); @@ -467,9 +527,9 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } /// @notice Tests that we can add a FaultDisputeGame implementation with addGameType. - function test_addGameType_cannon_succeeds() public { + function test_addGameType_permissionless_succeeds() public { // Create the input for the Permissionless game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(false); // Run the addGameType call. IOPContractsManager.AddGameOutput memory output = addGameType(input); @@ -489,7 +549,7 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { /// @notice Tests that we can add a SuperPermissionedDisputeGame implementation with addGameType. function test_addGameType_permissionedSuper_succeeds() public { // Create the input for the Super game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.SUPER_PERMISSIONED_CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(true); input.disputeGameType = GameTypes.SUPER_PERMISSIONED_CANNON; // Since OPCM will start with the standard Permissioned (non-Super) game type we won't have @@ -528,9 +588,10 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } /// @notice Tests that we can add a SuperFaultDisputeGame implementation with addGameType. - function test_addGameType_superCannon_succeeds() public { + function test_addGameType_permissionlessSuper_succeeds() public { // Create the input for the Super game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.SUPER_CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(false); + input.disputeGameType = GameTypes.SUPER_CANNON; // Run the addGameType call. IOPContractsManager.AddGameOutput memory output = addGameType(input); @@ -550,31 +611,8 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { /// @notice Tests that addGameType will revert if the game type is not supported. function test_addGameType_unsupportedGameType_reverts() public { - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameType.wrap(2000)); - - // Run the addGameType call, should revert. - IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](1); - inputs[0] = input; - (bool success,) = address(opcm).delegatecall(abi.encodeCall(IOPContractsManager.addGameType, (inputs))); - assertFalse(success, "addGameType should have failed"); - } - - /// @notice Tests that addGameType will revert if the game type is cannon-kona and the dev feature is not enabled - function test_addGameType_cannonKonaGameTypeDisabled_reverts() public { - skipIfDevFeatureEnabled(DevFeatures.CANNON_KONA); - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.CANNON_KONA); - - // Run the addGameType call, should revert. - IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](1); - inputs[0] = input; - (bool success,) = address(opcm).delegatecall(abi.encodeCall(IOPContractsManager.addGameType, (inputs))); - assertFalse(success, "addGameType should have failed"); - } - - /// @notice Tests that addGameType will revert if the game type is cannon-kona and the dev feature is not enabled - function test_addGameType_superCannonKonaGameTypeDisabled_reverts() public { - skipIfDevFeatureEnabled(DevFeatures.CANNON_KONA); - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.SUPER_CANNON_KONA); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(false); + input.disputeGameType = GameType.wrap(2000); // Run the addGameType call, should revert. IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](1); @@ -595,7 +633,7 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { ) ); vm.etch(address(delayedWETH), hex"01"); - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(false); input.delayedWETH = delayedWETH; IOPContractsManager.AddGameOutput memory output = addGameType(input); assertValidGameType(input, output); @@ -603,8 +641,10 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } function test_addGameType_outOfOrderInputs_reverts() public { - IOPContractsManager.AddGameInput memory input1 = newGameInputFactory(GameType.wrap(2)); - IOPContractsManager.AddGameInput memory input2 = newGameInputFactory(GameType.wrap(1)); + IOPContractsManager.AddGameInput memory input1 = newGameInputFactory(false); + input1.disputeGameType = GameType.wrap(2); + IOPContractsManager.AddGameInput memory input2 = newGameInputFactory(false); + input2.disputeGameType = GameType.wrap(1); IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](2); inputs[0] = input1; inputs[1] = input2; @@ -615,7 +655,7 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } function test_addGameType_duplicateGameType_reverts() public { - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(false); IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](2); inputs[0] = input; inputs[1] = input; @@ -637,7 +677,7 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } function test_addGameType_notDelegateCall_reverts() public { - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.PERMISSIONED_CANNON); + IOPContractsManager.AddGameInput memory input = newGameInputFactory(true); IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](1); inputs[0] = input; @@ -645,6 +685,182 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { opcm.addGameType(inputs); } + /// @notice Test that addGameType with v2 flag uses v2 implementation for PERMISSIONED_CANNON + function test_addGameType_permissionedCannonV2_succeeds() public { + // Deploy OPCM with v2 flag enabled + IOPContractsManager opcmV2 = _deployOPCMWithV2Flag(); + + // Deploy a chain first with v2-enabled OPCM + IOPContractsManager.DeployInput memory deployInput = IOPContractsManager.DeployInput({ + roles: IOPContractsManager.Roles({ + opChainProxyAdminOwner: makeAddr("opChainProxyAdminOwner"), + systemConfigOwner: makeAddr("systemConfigOwner"), + batcher: makeAddr("batcher"), + unsafeBlockSigner: makeAddr("unsafeBlockSigner"), + proposer: makeAddr("proposer"), + challenger: makeAddr("challenger") + }), + basefeeScalar: 100, + blobBasefeeScalar: 200, + l2ChainId: 10001, + startingAnchorRoot: abi.encode(Proposal({ root: Hash.wrap(bytes32(uint256(0x123))), l2SequenceNumber: 1 })), + saltMixer: "test-salt", + gasLimit: 30_000_000, + disputeGameType: GameTypes.PERMISSIONED_CANNON, + disputeAbsolutePrestate: Claim.wrap(bytes32(uint256(0x456))), + disputeMaxGameDepth: 73, + disputeSplitDepth: 30, + disputeClockExtension: Duration.wrap(uint64(3 hours)), + disputeMaxClockDuration: Duration.wrap(uint64(3.5 days)) + }); + + vm.startPrank(address(this)); + IOPContractsManager.DeployOutput memory output = opcmV2.deploy(deployInput); + vm.stopPrank(); + + // Get the v2 implementation address from OPCM + IOPContractsManager.Implementations memory impls = opcmV2.implementations(); + + // Prepare to add PERMISSIONED_CANNON game type with a different prestate + IOPContractsManager.AddGameInput[] memory gameConfigs = new IOPContractsManager.AddGameInput[](1); + gameConfigs[0] = IOPContractsManager.AddGameInput({ + disputeGameType: GameTypes.PERMISSIONED_CANNON, + systemConfig: output.systemConfigProxy, + proxyAdmin: output.opChainProxyAdmin, + delayedWETH: output.delayedWETHPermissionedGameProxy, // Use existing DelayedWETH to avoid proxy upgrade + disputeAbsolutePrestate: Claim.wrap(bytes32(uint256(0x789))), // Different prestate + disputeMaxGameDepth: 73, + disputeSplitDepth: 30, + disputeClockExtension: Duration.wrap(uint64(3 hours)), + disputeMaxClockDuration: Duration.wrap(uint64(3.5 days)), + initialBond: 1 ether, + vm: IBigStepper(makeAddr("vm")), + permissioned: true, + saltMixer: "salt1" + }); + + // Transfer DisputeGameFactory ownership to test contract temporarily for delegatecall + // This is needed because addGameType uses delegatecall and needs to call setImplementation + vm.prank(makeAddr("opChainProxyAdminOwner")); + output.disputeGameFactoryProxy.transferOwnership(address(this)); + + // Add the game type via delegatecall + (bool success, bytes memory rawGameOut) = + address(opcmV2).delegatecall(abi.encodeCall(IOPContractsManager.addGameType, (gameConfigs))); + assertTrue(success, "addGameType failed"); + + IOPContractsManager.AddGameOutput[] memory addGameOutputs = + abi.decode(rawGameOut, (IOPContractsManager.AddGameOutput[])); + + // Transfer DisputeGameFactory ownership back to the original owner + output.disputeGameFactoryProxy.transferOwnership(makeAddr("opChainProxyAdminOwner")); + + // Verify v2 implementation is registered in DisputeGameFactory + address registeredImpl = address(output.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); + + // Verify implementation address matches permissionedDisputeGameV2Impl + assertEq( + registeredImpl, + address(impls.permissionedDisputeGameV2Impl), + "DisputeGameFactory should have v2 PermissionedDisputeGame implementation registered" + ); + + // Verify that the returned fault dispute game is the v2 implementation + assertEq( + address(addGameOutputs[0].faultDisputeGame), + address(impls.permissionedDisputeGameV2Impl), + "addGameType should return v2 PermissionedDisputeGame implementation" + ); + } + + /// @notice Test that addGameType with v2 flag uses v2 implementation for CANNON (permissionless) + function test_addGameType_permissionlessCannonV2_succeeds() public { + // Deploy OPCM with v2 flag enabled + IOPContractsManager opcmV2 = _deployOPCMWithV2Flag(); + + // Deploy a chain first with v2-enabled OPCM + IOPContractsManager.DeployInput memory deployInput = IOPContractsManager.DeployInput({ + roles: IOPContractsManager.Roles({ + opChainProxyAdminOwner: makeAddr("opChainProxyAdminOwner"), + systemConfigOwner: makeAddr("systemConfigOwner"), + batcher: makeAddr("batcher"), + unsafeBlockSigner: makeAddr("unsafeBlockSigner"), + proposer: makeAddr("proposer"), + challenger: makeAddr("challenger") + }), + basefeeScalar: 100, + blobBasefeeScalar: 200, + l2ChainId: 10002, + startingAnchorRoot: abi.encode(Proposal({ root: Hash.wrap(bytes32(uint256(0x123))), l2SequenceNumber: 1 })), + saltMixer: "test-salt-2", + gasLimit: 30_000_000, + disputeGameType: GameTypes.PERMISSIONED_CANNON, + disputeAbsolutePrestate: Claim.wrap(bytes32(uint256(0x456))), + disputeMaxGameDepth: 73, + disputeSplitDepth: 30, + disputeClockExtension: Duration.wrap(uint64(3 hours)), + disputeMaxClockDuration: Duration.wrap(uint64(3.5 days)) + }); + + vm.startPrank(address(this)); + IOPContractsManager.DeployOutput memory output = opcmV2.deploy(deployInput); + vm.stopPrank(); + + // Get the v2 implementation address from OPCM + IOPContractsManager.Implementations memory impls = opcmV2.implementations(); + + // Prepare to add CANNON game type (permissionless) + IOPContractsManager.AddGameInput[] memory gameConfigs = new IOPContractsManager.AddGameInput[](1); + gameConfigs[0] = IOPContractsManager.AddGameInput({ + disputeGameType: GameTypes.CANNON, + systemConfig: output.systemConfigProxy, + proxyAdmin: output.opChainProxyAdmin, + delayedWETH: output.delayedWETHPermissionedGameProxy, // Use existing DelayedWETH to avoid proxy upgrade + disputeAbsolutePrestate: Claim.wrap(bytes32(uint256(0xabc))), // Different prestate + disputeMaxGameDepth: 73, + disputeSplitDepth: 30, + disputeClockExtension: Duration.wrap(uint64(3 hours)), + disputeMaxClockDuration: Duration.wrap(uint64(3.5 days)), + initialBond: 1 ether, + vm: IBigStepper(makeAddr("vm")), + permissioned: false, // Permissionless CANNON + saltMixer: "salt2" + }); + + // Transfer DisputeGameFactory ownership to test contract temporarily for delegatecall + // This is needed because addGameType uses delegatecall and needs to call setImplementation + vm.prank(makeAddr("opChainProxyAdminOwner")); + output.disputeGameFactoryProxy.transferOwnership(address(this)); + + // Add the game type via delegatecall + (bool success, bytes memory rawGameOut) = + address(opcmV2).delegatecall(abi.encodeCall(IOPContractsManager.addGameType, (gameConfigs))); + assertTrue(success, "addGameType failed"); + + IOPContractsManager.AddGameOutput[] memory addGameOutputs = + abi.decode(rawGameOut, (IOPContractsManager.AddGameOutput[])); + + // Transfer DisputeGameFactory ownership back to the original owner + output.disputeGameFactoryProxy.transferOwnership(makeAddr("opChainProxyAdminOwner")); + + // Verify v2 implementation is registered in DisputeGameFactory + address registeredImpl = address(output.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON)); + + // Verify implementation address matches faultDisputeGameV2Impl + assertEq( + registeredImpl, + address(impls.faultDisputeGameV2Impl), + "DisputeGameFactory should have v2 FaultDisputeGame implementation registered" + ); + + // Verify that the returned fault dispute game is the v2 implementation + assertEq( + address(addGameOutputs[0].faultDisputeGame), + address(impls.faultDisputeGameV2Impl), + "addGameType should return v2 FaultDisputeGame implementation" + ); + } + function addGameType(IOPContractsManager.AddGameInput memory input) internal returns (IOPContractsManager.AddGameOutput memory) @@ -670,13 +886,13 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { return addGameOutAll[0]; } - function newGameInputFactory(GameType _gameType) internal view returns (IOPContractsManager.AddGameInput memory) { + function newGameInputFactory(bool permissioned) internal view returns (IOPContractsManager.AddGameInput memory) { return IOPContractsManager.AddGameInput({ saltMixer: "hello", systemConfig: chainDeployOutput1.systemConfigProxy, proxyAdmin: chainDeployOutput1.opChainProxyAdmin, - delayedWETH: IDelayedWETH(payable(address(0))), - disputeGameType: _gameType, + delayedWETH: standaloneDelayedWETH, + disputeGameType: GameType.wrap(permissioned ? 1 : 0), disputeAbsolutePrestate: Claim.wrap(bytes32(hex"deadbeef1234")), disputeMaxGameDepth: 73, disputeSplitDepth: 30, @@ -684,8 +900,7 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { disputeMaxClockDuration: Duration.wrap(302400), initialBond: 1 ether, vm: IBigStepper(address(opcm.implementations().mipsImpl)), - permissioned: _gameType.raw() == GameTypes.PERMISSIONED_CANNON.raw() - || _gameType.raw() == GameTypes.SUPER_PERMISSIONED_CANNON.raw() + permissioned: permissioned }); } @@ -739,56 +954,6 @@ contract OPContractsManager_AddGameType_Test is OPContractsManager_TestInit { } } -/// @title OPContractsManager_AddGameTypeCannonKonaEnabled_Test -/// @notice Tests the `addGameType` function of the `OPContractsManager` contract with CANNON_KONA enabled. -contract OPContractsManager_AddGameType_CannonKonaEnabled_Test is OPContractsManager_AddGameType_Test { - function setUp() public override { - setDevFeatureEnabled(DevFeatures.CANNON_KONA); - super.setUp(); - } - - /// @notice Tests that addGameType will revert if the game type is cannon-kona and the dev feature is not enabled - function test_addGameType_cannonKonaGameType_succeeds() public { - // Create the input for the cannon-kona game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.CANNON_KONA); - - // Run the addGameType call. - IOPContractsManager.AddGameOutput memory output = addGameType(input); - assertValidGameType(input, output); - - // Check the values on the new game type. - IPermissionedDisputeGame notPDG = IPermissionedDisputeGame(address(output.faultDisputeGame)); - - // Proposer call should revert because this is a permissionless game. - vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args - notPDG.proposer(); - - // L2 chain ID call should not revert because this is not a Super game. - assertNotEq(notPDG.l2ChainId(), 0, "l2ChainId should not be zero"); - } - - /// @notice Tests that addGameType will revert if the game type is cannon-kona and the dev feature is not enabled - function test_addGameType_superCannonKonaGameType_succeeds() public { - // Create the input for the cannon-kona game type. - IOPContractsManager.AddGameInput memory input = newGameInputFactory(GameTypes.SUPER_CANNON_KONA); - - // Run the addGameType call. - IOPContractsManager.AddGameOutput memory output = addGameType(input); - assertValidGameType(input, output); - - // Grab the new game type. - IPermissionedDisputeGame notPDG = IPermissionedDisputeGame(address(output.faultDisputeGame)); - - // Proposer should fail, this is a permissionless game. - vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args - notPDG.proposer(); - - // Super games don't have the l2ChainId function. - vm.expectRevert(); // nosemgrep: sol-safety-expectrevert-no-args - notPDG.l2ChainId(); - } -} - /// @title OPContractsManager_UpdatePrestate_Test /// @notice Tests the `updatePrestate` function of the `OPContractsManager` contract. contract OPContractsManager_UpdatePrestate_Test is OPContractsManager_TestInit { @@ -797,12 +962,6 @@ contract OPContractsManager_UpdatePrestate_Test is OPContractsManager_TestInit { function setUp() public override { super.setUp(); - - // Skip UpdatePrestate tests when V2 dispute games enabled - // UpdatePrestate feature not yet implemented for V2 - // TODO(#17261): Remove skip when V2 dispute game support for updatePrestate implemented - skipIfDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES); - prestateUpdater = opcm; } @@ -1039,7 +1198,7 @@ contract OPContractsManager_UpdatePrestate_Test is OPContractsManager_TestInit { saltMixer: "hello", systemConfig: chainDeployOutput1.systemConfigProxy, proxyAdmin: chainDeployOutput1.opChainProxyAdmin, - delayedWETH: IDelayedWETH(payable(address(0))), + delayedWETH: standaloneDelayedWETH, disputeGameType: GameType.wrap(permissioned ? 1 : 0), disputeAbsolutePrestate: Claim.wrap(bytes32(hex"deadbeef1234")), disputeMaxGameDepth: 73, @@ -1297,7 +1456,7 @@ contract OPContractsManager_Migrate_Test is OPContractsManager_TestInit { Claim absolutePrestate2 = Claim.wrap(bytes32(hex"DEAD")); /// @notice Function requires interop portal. - function setUp() public virtual override { + function setUp() public override { super.setUp(); skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); } @@ -1372,11 +1531,6 @@ contract OPContractsManager_Migrate_Test is OPContractsManager_TestInit { assertEq(address(_disputeGameFactory.gameImpls(GameTypes.SUPER_CANNON)), address(0)); assertEq(address(_disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)), address(0)); assertEq(address(_disputeGameFactory.gameImpls(GameTypes.SUPER_PERMISSIONED_CANNON)), address(0)); - if (isDevFeatureEnabled(DevFeatures.CANNON_KONA)) { - // Only explicitly zeroed out if feature is enabled. Otherwise left unchanged (which may still be 0). - assertEq(address(_disputeGameFactory.gameImpls(GameTypes.CANNON_KONA)), address(0)); - assertEq(address(_disputeGameFactory.gameImpls(GameTypes.SUPER_CANNON_KONA)), address(0)); - } } /// @notice Tests that the migration function succeeds when requesting to use the @@ -1669,34 +1823,17 @@ contract OPContractsManager_Migrate_Test is OPContractsManager_TestInit { } } -/// @title OPContractsManager_Migrate_CannonKonaEnabled_Test -/// @notice Tests the `migrate` function of the `OPContractsManager` contract. -contract OPContractsManager_Migrate_CannonKonaEnabled_Test is OPContractsManager_Migrate_Test { +/// @title OPContractsManager_Version_Test +/// @notice Tests the `version` function of the `OPContractsManager` contract. +contract OPContractsManager_Version_Test is OPContractsManager_TestInit { + OPContractsManager.AddGameInput[] internal gameInput; + function setUp() public override { - setDevFeatureEnabled(DevFeatures.CANNON_KONA); super.setUp(); } - function test_migrate_zerosOutCannonKonaGameTypes_succeeds() public { - IOPContractsManagerInteropMigrator.MigrateInput memory input = _getDefaultInput(); - - // Grab the existing DisputeGameFactory for each chain. - IDisputeGameFactory oldDisputeGameFactory1 = - IDisputeGameFactory(payable(chainDeployOutput1.systemConfigProxy.disputeGameFactory())); - IDisputeGameFactory oldDisputeGameFactory2 = - IDisputeGameFactory(payable(chainDeployOutput2.systemConfigProxy.disputeGameFactory())); - // Ensure cannon kona games have implementations - oldDisputeGameFactory1.setImplementation(GameTypes.CANNON_KONA, IDisputeGame(address(1))); - oldDisputeGameFactory2.setImplementation(GameTypes.CANNON_KONA, IDisputeGame(address(1))); - oldDisputeGameFactory1.setImplementation(GameTypes.SUPER_CANNON_KONA, IDisputeGame(address(2))); - oldDisputeGameFactory2.setImplementation(GameTypes.SUPER_CANNON_KONA, IDisputeGame(address(2))); - - // Execute the migration. - _doMigration(input); - - // Assert that the old game implementations are now zeroed out. - _assertOldGamesZeroed(oldDisputeGameFactory1); - _assertOldGamesZeroed(oldDisputeGameFactory2); + function test_semver_works() public view { + assertNotEq(abi.encode(opcm.version()), abi.encode(0)); } } @@ -1709,110 +1846,6 @@ contract OPContractsManager_Migrate_CannonKonaEnabled_Test is OPContractsManager /// DeployOPChain_TestBase so we can use its setup to deploy the implementations similarly /// to how a real deployment would happen. contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { - using stdStorage for StdStorage; - - event Deployed(uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); - - // This helper function is used to convert the input struct type defined in DeployOPChain.s.sol - // to the input struct type defined in OPContractsManager.sol. - function toOPCMDeployInput(Types.DeployOPChainInput memory _doi) - internal - returns (IOPContractsManager.DeployInput memory) - { - bytes memory startingAnchorRoot = new DeployOPChain().startingAnchorRoot(); - return IOPContractsManager.DeployInput({ - roles: IOPContractsManager.Roles({ - opChainProxyAdminOwner: _doi.opChainProxyAdminOwner, - systemConfigOwner: _doi.systemConfigOwner, - batcher: _doi.batcher, - unsafeBlockSigner: _doi.unsafeBlockSigner, - proposer: _doi.proposer, - challenger: _doi.challenger - }), - basefeeScalar: _doi.basefeeScalar, - blobBasefeeScalar: _doi.blobBaseFeeScalar, - l2ChainId: _doi.l2ChainId, - startingAnchorRoot: startingAnchorRoot, - saltMixer: _doi.saltMixer, - gasLimit: _doi.gasLimit, - disputeGameType: _doi.disputeGameType, - disputeAbsolutePrestate: _doi.disputeAbsolutePrestate, - disputeMaxGameDepth: _doi.disputeMaxGameDepth, - disputeSplitDepth: _doi.disputeSplitDepth, - disputeClockExtension: _doi.disputeClockExtension, - disputeMaxClockDuration: _doi.disputeMaxClockDuration - }); - } - - /// @notice Helper function to deploy OPCM with v2 flag enabled - function _deployOPCMWithV2Flag() internal returns (IOPContractsManager) { - // Deploy Superchain contracts first - DeploySuperchain deploySuperchain = new DeploySuperchain(); - DeploySuperchain.Output memory dso = deploySuperchain.run( - DeploySuperchain.Input({ - superchainProxyAdminOwner: makeAddr("superchainProxyAdminOwner"), - protocolVersionsOwner: makeAddr("protocolVersionsOwner"), - guardian: makeAddr("guardian"), - paused: false, - requiredProtocolVersion: bytes32(ProtocolVersion.unwrap(ProtocolVersion.wrap(1))), - recommendedProtocolVersion: bytes32(ProtocolVersion.unwrap(ProtocolVersion.wrap(2))) - }) - ); - - // Deploy implementations with v2 flag enabled - DeployImplementations deployImplementations = new DeployImplementations(); - DeployImplementations.Output memory dio = deployImplementations.run( - DeployImplementations.Input({ - withdrawalDelaySeconds: 100, - minProposalSizeBytes: 200, - challengePeriodSeconds: 300, - proofMaturityDelaySeconds: 400, - disputeGameFinalityDelaySeconds: 500, - mipsVersion: StandardConstants.MIPS_VERSION, - faultGameV2MaxGameDepth: 73, - faultGameV2SplitDepth: 30, - faultGameV2ClockExtension: 10800, - faultGameV2MaxClockDuration: 302400, - superchainConfigProxy: dso.superchainConfigProxy, - protocolVersionsProxy: dso.protocolVersionsProxy, - superchainProxyAdmin: dso.superchainProxyAdmin, - upgradeController: dso.superchainProxyAdmin.owner(), - challenger: challenger, - devFeatureBitmap: DevFeatures.DEPLOY_V2_DISPUTE_GAMES // Enable v2 flag here - }) - ); - - return dio.opcm; - } - - /// @notice Helper function to create a permissioned game v2 through the factory - function _createPermissionedGameV2( - IDisputeGameFactory factory, - address proposer - ) - internal - returns (IPermissionedDisputeGame) - { - // Check if there's an init bond required for the game type - uint256 initBond = factory.initBonds(GameTypes.PERMISSIONED_CANNON); - - // Fund the proposer if needed - if (initBond > 0) { - vm.deal(proposer, initBond); - } - - // We use vm.startPrank to set both msg.sender and tx.origin to the proposer - vm.startPrank(proposer, proposer); - - IDisputeGame gameProxy = factory.create{ value: initBond }( - GameTypes.PERMISSIONED_CANNON, Claim.wrap(bytes32(uint256(1))), abi.encode(bytes32(uint256(2))) - ); - - vm.stopPrank(); - - return IPermissionedDisputeGame(address(gameProxy)); - } - function test_deploy_l2ChainIdEqualsZero_reverts() public { IOPContractsManager.DeployInput memory input = toOPCMDeployInput(deployOPChainInput); input.l2ChainId = 0; @@ -1834,49 +1867,22 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { emit Deployed(deployOPChainInput.l2ChainId, address(this), bytes("")); opcm.deploy(toOPCMDeployInput(deployOPChainInput)); } + /// @notice Test that deploy without v2 flag doesn't set v2 implementations - /// @notice Test that deploy without v2 flag doesn't set v2 implementations for PERMISSIONED_CANNON - function test_deployPermissionedWithoutV2Flag_succeeds() public { + function test_deployWithoutV2Flag_succeeds() public { // Convert DOI to OPCM input and deploy IOPContractsManager.DeployInput memory opcmInput = toOPCMDeployInput(deployOPChainInput); + vm.startPrank(address(this)); IOPContractsManager.DeployOutput memory output = opcm.deploy(opcmInput); - IOPContractsManager.Implementations memory impls = opcm.implementations(); + vm.stopPrank(); // Check that v1 implementation is registered for PERMISSIONED_CANNON address registeredImpl = address(output.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); assertEq(registeredImpl, address(output.permissionedDisputeGame)); - - address registeredPermissionedImpl = - address(output.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); - assertNotEq( - registeredPermissionedImpl, - address(0), - "DisputeGameFactory should have v2 PermissionedDisputeGame registered for PERMISSIONED_CANNON" - ); - assertEq( - registeredPermissionedImpl, address(output.permissionedDisputeGame), "Should be using v2 implementation" - ); - - // Create a game proxy to test immutable fields - IPermissionedDisputeGame gameV2Proxy = - _createPermissionedGameV2(output.disputeGameFactoryProxy, opcmInput.roles.proposer); - - // Verify immutable fields on the game proxy - assertEq(address(gameV2Proxy.vm()), address(impls.mipsImpl), "VM should match MIPS implementation"); - assertEq( - address(gameV2Proxy.anchorStateRegistry()), address(output.anchorStateRegistryProxy), "ASR should match" - ); - assertEq(address(gameV2Proxy.weth()), address(output.delayedWETHPermissionedGameProxy), "WETH should match"); - assertEq(gameV2Proxy.l2ChainId(), opcmInput.l2ChainId, "L2 chain ID should match"); - - // For permissioned game, check proposer and challenger - IPermissionedDisputeGame permissionedProxy = IPermissionedDisputeGame(address(gameV2Proxy)); - assertEq(permissionedProxy.proposer(), opcmInput.roles.proposer, "Proposer should match"); - assertEq(permissionedProxy.challenger(), opcmInput.roles.challenger, "Challenger should match"); } - /// @notice Test that deploy with v2 flag would set v2 implementations for PERMISSIONED_CANNON - function test_deployPermissionedWithV2Flag_succeeds() public { + /// @notice Test that deploy with v2 flag would set v2 implementations + function test_deployWithV2Flag_succeeds() public { IOPContractsManager opcmV2 = _deployOPCMWithV2Flag(); assertTrue(opcmV2.isDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES), "V2 flag should be enabled"); @@ -1895,51 +1901,38 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { // Set up deploy input for the v2-enabled OPCM deployOPChainInput.opcm = address(opcmV2); IOPContractsManager.DeployInput memory opcmInput = toOPCMDeployInput(deployOPChainInput); + + vm.startPrank(address(this)); IOPContractsManager.DeployOutput memory output = opcmV2.deploy(opcmInput); + vm.stopPrank(); - // Verify that the DisputeGameFactory has registered the v2 implementation for PERMISSIONED_CANNON game type - address registeredPermissionedImpl = - address(output.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); - assertNotEq( - registeredPermissionedImpl, - address(0), - "DisputeGameFactory should have v2 PermissionedDisputeGame registered for PERMISSIONED_CANNON" + // Verify that v2 dispute game contracts are deployed and non-zero + assertTrue(address(output.permissionedDisputeGame) != address(0), "PermissionedDisputeGame should be deployed"); + assertTrue(address(output.faultDisputeGame) != address(0), "FaultDisputeGame should be deployed"); + + assertEq( + output.permissionedDisputeGame.version(), + IPermissionedDisputeGameV2(impls.permissionedDisputeGameV2Impl).version(), + "PermissionedDisputeGame should use V2" ); assertEq( - registeredPermissionedImpl, - address(impls.permissionedDisputeGameV2Impl), - "Should be using v2 implementation" + output.faultDisputeGame.version(), + IFaultDisputeGame(impls.faultDisputeGameV2Impl).version(), + "FaultDisputeGame should use V2" ); - // Create a game proxy to test immutable fields - IPermissionedDisputeGame gameV2Proxy = - _createPermissionedGameV2(output.disputeGameFactoryProxy, opcmInput.roles.proposer); + // Verify that v1 permissioned dispute game is still deployed (for backward compatibility) + assertTrue( + address(output.permissionedDisputeGame) != address(0), "PermissionedDisputeGame v1 should still be deployed" + ); - // Verify immutable fields on the game proxy - assertEq(address(gameV2Proxy.vm()), address(impls.mipsImpl), "VM should match MIPS implementation"); + // Verify that the DisputeGameFactory has registered the v2 implementation for PERMISSIONED_CANNON game type + address registeredPermissionedImpl = + address(output.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); assertEq( - address(gameV2Proxy.anchorStateRegistry()), address(output.anchorStateRegistryProxy), "ASR should match" + registeredPermissionedImpl, + address(output.permissionedDisputeGame), + "DisputeGameFactory should have v2 PermissionedDisputeGame registered for PERMISSIONED_CANNON" ); - assertEq(address(gameV2Proxy.weth()), address(output.delayedWETHPermissionedGameProxy), "WETH should match"); - assertEq(gameV2Proxy.l2ChainId(), opcmInput.l2ChainId, "L2 chain ID should match"); - - // For permissioned game, check proposer and challenger - IPermissionedDisputeGame permissionedProxy = IPermissionedDisputeGame(address(gameV2Proxy)); - assertEq(permissionedProxy.proposer(), opcmInput.roles.proposer, "Proposer should match"); - assertEq(permissionedProxy.challenger(), opcmInput.roles.challenger, "Challenger should match"); - } -} - -/// @title OPContractsManager_Version_Test -/// @notice Tests the `version` function of the `OPContractsManager` contract. -contract OPContractsManager_Version_Test is OPContractsManager_TestInit { - OPContractsManager.AddGameInput[] internal gameInput; - - function setUp() public override { - super.setUp(); - } - - function test_semver_works() public view { - assertNotEq(abi.encode(opcm.version()), abi.encode(0)); } } diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 4eebfb2863b5a..4ec94634f02da 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -7,10 +7,12 @@ import { DeploySuperchain } from "scripts/deploy/DeploySuperchain.s.sol"; import { DeployImplementations } from "scripts/deploy/DeployImplementations.s.sol"; import { DeployOPChain } from "scripts/deploy/DeployOPChain.s.sol"; import { StandardConstants } from "scripts/deploy/StandardConstants.sol"; +import { Constants as ScriptConstants } from "scripts/libraries/Constants.sol"; import { Types } from "scripts/libraries/Types.sol"; import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { Claim, Duration, GameType, GameTypes } from "src/dispute/lib/Types.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; contract DeployOPChain_TestBase is Test { DeploySuperchain deploySuperchain; @@ -56,6 +58,8 @@ contract DeployOPChain_TestBase is Test { Duration disputeMaxClockDuration = Duration.wrap(3.5 days); IOPContractsManager opcm; + event Deployed(uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); + function setUp() public virtual { deploySuperchain = new DeploySuperchain(); deployImplementations = new DeployImplementations(); @@ -122,6 +126,77 @@ contract DeployOPChain_TestBase is Test { operatorFeeConstant: 0 }); } + + /// @notice Helper function to deploy OPCM with v2 flag enabled + function _deployOPCMWithV2Flag() internal returns (IOPContractsManager) { + // Deploy Superchain contracts first + DeploySuperchain.Output memory dso = deploySuperchain.run( + DeploySuperchain.Input({ + superchainProxyAdminOwner: makeAddr("superchainProxyAdminOwner"), + protocolVersionsOwner: makeAddr("protocolVersionsOwner"), + guardian: makeAddr("guardian"), + paused: false, + requiredProtocolVersion: requiredProtocolVersion, + recommendedProtocolVersion: recommendedProtocolVersion + }) + ); + + // Deploy implementations with v2 flag enabled + DeployImplementations.Output memory dio = deployImplementations.run( + DeployImplementations.Input({ + withdrawalDelaySeconds: 100, + minProposalSizeBytes: 200, + challengePeriodSeconds: 300, + proofMaturityDelaySeconds: 400, + disputeGameFinalityDelaySeconds: 500, + mipsVersion: StandardConstants.MIPS_VERSION, + faultGameV2MaxGameDepth: 73, + faultGameV2SplitDepth: 30, + faultGameV2ClockExtension: 10800, + faultGameV2MaxClockDuration: 302400, + superchainConfigProxy: dso.superchainConfigProxy, + protocolVersionsProxy: dso.protocolVersionsProxy, + superchainProxyAdmin: dso.superchainProxyAdmin, + upgradeController: dso.superchainProxyAdmin.owner(), + challenger: challenger, + devFeatureBitmap: DevFeatures.DEPLOY_V2_DISPUTE_GAMES // Enable v2 flag here + }) + ); + + return dio.opcm; + } + + // This helper function is used to convert the input struct type defined in DeployOPChain.s.sol + // to the input struct type defined in OPContractsManager.sol. + function toOPCMDeployInput(Types.DeployOPChainInput memory _doi) + internal + view + virtual + returns (IOPContractsManager.DeployInput memory) + { + return IOPContractsManager.DeployInput({ + roles: IOPContractsManager.Roles({ + opChainProxyAdminOwner: _doi.opChainProxyAdminOwner, + systemConfigOwner: _doi.systemConfigOwner, + batcher: _doi.batcher, + unsafeBlockSigner: _doi.unsafeBlockSigner, + proposer: _doi.proposer, + challenger: _doi.challenger + }), + basefeeScalar: _doi.basefeeScalar, + blobBasefeeScalar: _doi.blobBaseFeeScalar, + l2ChainId: _doi.l2ChainId, + startingAnchorRoot: abi.encode(ScriptConstants.DEFAULT_OUTPUT_ROOT()), + saltMixer: _doi.saltMixer, + gasLimit: _doi.gasLimit, + disputeGameType: _doi.disputeGameType, + disputeAbsolutePrestate: _doi.disputeAbsolutePrestate, + disputeMaxGameDepth: _doi.disputeMaxGameDepth, + disputeSplitDepth: _doi.disputeSplitDepth, + disputeClockExtension: _doi.disputeClockExtension, + disputeMaxClockDuration: _doi.disputeMaxClockDuration + }); + } } contract DeployOPChain_Test is DeployOPChain_TestBase {