diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index aaaeb32456f..9937c5dbf4f 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -254,6 +254,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme AllowCustomDisputeParameters: true, OperatorFeeScalar: cfg.GasPriceOracleOperatorFeeScalar, OperatorFeeConstant: cfg.GasPriceOracleOperatorFeeConstant, + SuperchainConfig: superDeployment.SuperchainConfigProxy, UseCustomGasToken: cfg.UseCustomGasToken, }) if err != nil { diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index bd5dc5c88af..3c46acd8155 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -361,6 +361,8 @@ func TestEndToEndApply(t *testing.T) { require.True(t, exists, "Native asset liquidity predeploy should exist in L2 genesis") require.Equal(t, amount, account.Balance, "Native asset liquidity predeploy should have the configured balance") }) + + // TODO: Add tests for OPCMV2 } func TestGlobalOverrides(t *testing.T) { diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 115bb14f9af..a0298c71b66 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -43,6 +43,7 @@ type DeployOPChainInput struct { OperatorFeeScalar uint32 OperatorFeeConstant uint64 + SuperchainConfig common.Address UseCustomGasToken bool } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 26dfaab3356..5fad788d25e 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -135,6 +135,7 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters, OperatorFeeScalar: thisIntent.OperatorFeeScalar, OperatorFeeConstant: thisIntent.OperatorFeeConstant, + SuperchainConfig: st.SuperchainDeployment.SuperchainConfigProxy, UseCustomGasToken: thisIntent.IsCustomGasTokenEnabled(), }, nil } diff --git a/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerV2.sol b/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerV2.sol index 48fc1aefa97..4e2c609400a 100644 --- a/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerV2.sol +++ b/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerV2.sol @@ -80,6 +80,7 @@ interface IOPContractsManagerV2 { uint256 l2ChainId; IResourceMetering.ResourceConfig resourceConfig; DisputeGameConfig[] disputeGameConfigs; + bool useCustomGasToken; } struct ExtraInstruction { @@ -135,9 +136,7 @@ interface IOPContractsManagerV2 { function version() external view returns (string memory); /// @notice Upgrades Superchain-wide contracts. - function upgradeSuperchain(SuperchainUpgradeInput memory _inp) - external - returns (SuperchainContracts memory); + function upgradeSuperchain(SuperchainUpgradeInput memory _inp) external returns (SuperchainContracts memory); /// @notice Deploys and wires a complete OP Chain per the provided configuration. function deploy(FullConfig memory _cfg) external returns (ChainContracts memory); diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 9e743b2fa4f..abe36f4cef9 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -542,7 +542,8 @@ contract Deploy is Deployer { gasLimit: uint64(cfg.l2GenesisBlockGasLimit()), l2ChainId: cfg.l2ChainID(), resourceConfig: Constants.DEFAULT_RESOURCE_CONFIG(), - disputeGameConfigs: disputeGameConfigs + disputeGameConfigs: disputeGameConfigs, + useCustomGasToken: cfg.useCustomGasToken() }); } } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 9b4841229d4..fe8d12dde98 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -201,8 +201,7 @@ contract DeployOPChain is Script { config_ = IOPContractsManagerV2.FullConfig({ saltMixer: _input.saltMixer, - // TODO: Add superchain config to OPCM v2. - superchainConfig: ISuperchainConfig(address(0)), + superchainConfig: _input.superchainConfig, proxyAdminOwner: _input.opChainProxyAdminOwner, systemConfigOwner: _input.systemConfigOwner, unsafeBlockSigner: _input.unsafeBlockSigner, @@ -214,7 +213,8 @@ contract DeployOPChain is Script { gasLimit: _input.gasLimit, l2ChainId: _input.l2ChainId, resourceConfig: Constants.DEFAULT_RESOURCE_CONFIG(), - disputeGameConfigs: disputeGameConfigs + disputeGameConfigs: disputeGameConfigs, + useCustomGasToken: _input.useCustomGasToken }); } @@ -318,7 +318,10 @@ contract DeployOPChain is Script { address(_o.ethLockboxProxy) ); - if (!isDevFeatureV2DisputeGamesEnabled(_i.opcm)) { + // OPCM v2 always uses v2 dispute games, so only check v1 feature flag if v2 is not enabled + bool useV2Games = isDevFeatureOpcmV2Enabled(_i.opcm) + || (!isDevFeatureOpcmV2Enabled(_i.opcm) && isDevFeatureV2DisputeGamesEnabled(_i.opcm)); + if (!useV2Games) { // Only check dispute game contracts if v2 dispute games are not enabled. // When v2 contracts are enabled, we no longer deploy dispute games per chain addrs2 = Solarray.extend(addrs2, Solarray.addresses(address(_o.permissionedDisputeGame))); @@ -347,19 +350,16 @@ contract DeployOPChain is Script { SystemConfig: address(_o.systemConfigProxy), L1ERC721Bridge: address(_o.l1ERC721BridgeProxy), ProtocolVersions: address(0), - SuperchainConfig: address(0) + SuperchainConfig: address(_i.superchainConfig) }); // Check dispute games and get superchain config address expectedPDGImpl = address(_o.permissionedDisputeGame); - ISuperchainConfig superchainConfig; if (isDevFeatureOpcmV2Enabled(_i.opcm)) { // OPCM v2: use implementations from v2 contract IOPContractsManagerV2 opcmV2 = IOPContractsManagerV2(_i.opcm); expectedPDGImpl = opcmV2.implementations().permissionedDisputeGameV2Impl; - // TODO: Add superchain config to OPCM v2. - superchainConfig = ISuperchainConfig(address(0)); } else { // OPCM v1: use implementations from v1 contract IOPContractsManager opcm = IOPContractsManager(_i.opcm); @@ -367,7 +367,6 @@ contract DeployOPChain is Script { // With v2 game contracts enabled, we use the predeployed pdg implementation expectedPDGImpl = opcm.implementations().permissionedDisputeGameV2Impl; } - superchainConfig = opcm.superchainConfig(); } ChainAssertions.checkDisputeGameFactory( @@ -378,7 +377,7 @@ contract DeployOPChain is Script { ChainAssertions.checkL1CrossDomainMessenger(_o.l1CrossDomainMessengerProxy, vm, true); ChainAssertions.checkOptimismPortal2({ _contracts: proxies, - _superchainConfig: superchainConfig, + _superchainConfig: _i.superchainConfig, _opChainProxyAdminOwner: _i.opChainProxyAdminOwner, _isProxy: true }); diff --git a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol index 23deddc11e7..0836ca6ecc3 100644 --- a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol @@ -60,6 +60,7 @@ contract ReadImplementationAddresses is Script { output_.l1StandardBridge = IStaticL1ChugSplashProxy(_input.l1StandardBridgeProxy).getImplementation(); // Get implementations from OPCM + // TODO: Adapt to OPCMV2 interface when feature enabled IOPContractsManager opcm = IOPContractsManager(_input.opcm); output_.opcmGameTypeAdder = address(opcm.opcmGameTypeAdder()); output_.opcmDeployer = address(opcm.opcmDeployer()); diff --git a/packages/contracts-bedrock/scripts/libraries/Types.sol b/packages/contracts-bedrock/scripts/libraries/Types.sol index 7b90b7204e9..bec2f022e91 100644 --- a/packages/contracts-bedrock/scripts/libraries/Types.sol +++ b/packages/contracts-bedrock/scripts/libraries/Types.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import { Claim, Duration, GameType } from "src/dispute/lib/Types.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; library Types { /// @notice Represents a set of L1 contracts. Used to represent a set of proxies. @@ -49,6 +50,8 @@ library Types { // Fee params uint32 operatorFeeScalar; uint64 operatorFeeConstant; + // Superchain contracts + ISuperchainConfig superchainConfig; // Whether to use the custom gas token. bool useCustomGasToken; } diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol index 34d49b42497..bf09a614b5c 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol @@ -121,6 +121,8 @@ contract OPContractsManagerV2 is ISemver { IResourceMetering.ResourceConfig resourceConfig; // Dispute game configuration. DisputeGameConfig[] disputeGameConfigs; + // Feature flags. + bool useCustomGasToken; } /// @notice Partial input required for an upgrade. @@ -598,7 +600,16 @@ contract OPContractsManagerV2 is ISemver { ), (GameType) ), - disputeGameConfigs: _upgradeInput.disputeGameConfigs + disputeGameConfigs: _upgradeInput.disputeGameConfigs, + useCustomGasToken: abi.decode( + _loadBytes( + address(_chainContracts.systemConfig), + _chainContracts.systemConfig.isCustomGasToken.selector, + "overrides.cfg.useCustomGasToken", + _upgradeInput.extraInstructions + ), + (bool) + ) }); } @@ -802,6 +813,11 @@ contract OPContractsManagerV2 is ISemver { ); } + // If the custom gas token feature was requested, enable it in the SystemConfig. + if (_cfg.useCustomGasToken) { + _cts.systemConfig.setFeature(Features.CUSTOM_GAS_TOKEN, true); + } + // If critical transfer is allowed, tranfer ownership of the DisputeGameFactory and // ProxyAdmin to the PAO. During deployments, this means transferring ownership from the // OPCM contract to the target PAO. During upgrades, this would theoretically mean @@ -855,7 +871,7 @@ contract OPContractsManagerV2 is ISemver { _cfg.gasLimit, _cfg.unsafeBlockSigner, _cfg.resourceConfig, - _chainIdToBatchInboxAddress(_cfg.l2ChainId), + chainIdToBatchInboxAddress(_cfg.l2ChainId), addrs, _cfg.l2ChainId, _cfg.superchainConfig @@ -945,10 +961,6 @@ contract OPContractsManagerV2 is ISemver { return contractsContainer.isDevFeatureEnabled(_feature); } - /////////////////////////////////////////////////////////////////////////// - // INTERNAL UTILITY FUNCTIONS // - /////////////////////////////////////////////////////////////////////////// - /// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard /// configuration's convention. This convention is /// `versionByte || keccak256(bytes32(chainId))[:19]`, where || denotes concatenation, @@ -956,13 +968,17 @@ contract OPContractsManagerV2 is ISemver { /// https://specs.optimism.io/protocol/configurability.html#consensus-parameters /// @param _l2ChainId The L2 chain ID to map to an L1 batch inbox address. /// @return Chain ID mapped to an L1 batch inbox address. - function _chainIdToBatchInboxAddress(uint256 _l2ChainId) internal pure returns (address) { + function chainIdToBatchInboxAddress(uint256 _l2ChainId) public pure returns (address) { bytes1 versionByte = 0x00; bytes32 hashedChainId = keccak256(bytes.concat(bytes32(_l2ChainId))); bytes19 first19Bytes = bytes19(hashedChainId); return address(uint160(bytes20(bytes.concat(versionByte, first19Bytes)))); } + /////////////////////////////////////////////////////////////////////////// + // INTERNAL UTILITY FUNCTIONS // + /////////////////////////////////////////////////////////////////////////// + /// @notice Computes a unique salt for a contract deployment. /// @param _l2ChainId The L2 chain ID of the chain being deployed to. /// @param _saltMixer The salt mixer to use for the deployment. diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index b92298af39f..abdf89d534d 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -2377,7 +2377,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase, DisputeGames input.l2ChainId = 0; vm.expectRevert(IOPContractsManager.InvalidChainId.selector); - opcm.deploy(input); + IOPContractsManager(opcmAddr).deploy(input); } function test_deploy_l2ChainIdEqualsCurrentChainId_reverts() public { @@ -2385,13 +2385,13 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase, DisputeGames input.l2ChainId = block.chainid; vm.expectRevert(IOPContractsManager.InvalidChainId.selector); - opcm.deploy(input); + IOPContractsManager(opcmAddr).deploy(input); } function test_deploy_succeeds() public { vm.expectEmit(true, true, true, false); // TODO precompute the expected `deployOutput`. emit Deployed(deployOPChainInput.l2ChainId, address(this), bytes("")); - opcm.deploy(toOPCMDeployInput(deployOPChainInput)); + IOPContractsManager(opcmAddr).deploy(toOPCMDeployInput(deployOPChainInput)); } /// @notice Test that deploy sets the permissioned dispute game implementation @@ -2399,7 +2399,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase, DisputeGames bool isV2 = isDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES); // Sanity-check setup is consistent with devFeatures flag - IOPContractsManager.Implementations memory impls = opcm.implementations(); + IOPContractsManager.Implementations memory impls = IOPContractsManager(opcmAddr).implementations(); address pdgImpl = address(impls.permissionedDisputeGameV2Impl); address fdgImpl = address(impls.faultDisputeGameV2Impl); if (isV2) { @@ -2412,7 +2412,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase, DisputeGames // Run OPCM.deploy IOPContractsManager.DeployInput memory opcmInput = toOPCMDeployInput(deployOPChainInput); - IOPContractsManager.DeployOutput memory opcmOutput = opcm.deploy(opcmInput); + IOPContractsManager.DeployOutput memory opcmOutput = IOPContractsManager(opcmAddr).deploy(opcmInput); // Verify that the DisputeGameFactory has registered an implementation for the PERMISSIONED_CANNON game type address expectedPDGAddress = isV2 ? pdgImpl : address(opcmOutput.permissionedDisputeGame); diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index 3246d9de9b4..bdcad213e3c 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -15,6 +15,7 @@ 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 { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; contract DeployOPChain_TestBase is Test, FeatureFlags { DeploySuperchain deploySuperchain; @@ -58,7 +59,8 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { uint256 disputeSplitDepth = 30; Duration disputeClockExtension = Duration.wrap(3 hours); Duration disputeMaxClockDuration = Duration.wrap(3.5 days); - IOPContractsManager opcm; + address opcmAddr; + ISuperchainConfig superchainConfig; bool useCustomGasToken = false; event Deployed(uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); @@ -102,8 +104,13 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { devFeatureBitmap: devFeatureBitmap }) ); - opcm = dio.opcm; - vm.label(address(opcm), "opcm"); + // Select OPCM v1 or v2 based on feature flag + opcmAddr = isDevFeatureEnabled(DevFeatures.OPCM_V2) ? address(dio.opcmV2) : address(dio.opcm); + vm.label(address(dio.opcm), "opcm"); + vm.label(address(dio.opcmV2), "opcmV2"); + + // Set superchainConfig from deployment + superchainConfig = dso.superchainConfigProxy; // 3) Build DeployOPChainInput struct deployOPChainInput = Types.DeployOPChainInput({ @@ -116,7 +123,7 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { basefeeScalar: basefeeScalar, blobBaseFeeScalar: blobBaseFeeScalar, l2ChainId: l2ChainId, - opcm: address(opcm), + opcm: opcmAddr, saltMixer: saltMixer, gasLimit: gasLimit, disputeGameType: disputeGameType, @@ -128,6 +135,7 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { allowCustomDisputeParameters: false, operatorFeeScalar: 0, operatorFeeConstant: 0, + superchainConfig: superchainConfig, useCustomGasToken: useCustomGasToken }); } @@ -171,6 +179,34 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { useCustomGasToken, "SystemConfig CUSTOM_GAS_TOKEN feature" ); + + // Verify superchainConfig is set correctly + assertEq( + address(doo.systemConfigProxy.superchainConfig()), + address(deployOPChainInput.superchainConfig), + "superchainConfig mismatch" + ); + + // OPCM v2 specific assertions + if (isDevFeatureEnabled(DevFeatures.OPCM_V2)) { + // PERMISSIONED_CANNON must always be enabled with 0.08 ether init bond + assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.PERMISSIONED_CANNON), 0.08 ether); + assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)), address(0)); + + // CANNON is only enabled if it's the starting game type + bool cannonEnabled = deployOPChainInput.disputeGameType.raw() == GameTypes.CANNON.raw(); + assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), cannonEnabled ? 0.08 ether : 0); + if (cannonEnabled) { + assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON)), address(0)); + } + + // CANNON_KONA is only enabled if it's the starting game type + bool cannonKonaEnabled = deployOPChainInput.disputeGameType.raw() == GameTypes.CANNON_KONA.raw(); + assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON_KONA), cannonKonaEnabled ? 0.08 ether : 0); + if (cannonKonaEnabled) { + assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON_KONA)), address(0)); + } + } } function testFuzz_run_memory_succeeds(bytes32 _seed) public { @@ -187,30 +223,37 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput); - // Verify that the initial bonds are zero. - assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), 0, "2700"); - assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.PERMISSIONED_CANNON), 0, "2800"); + // Skip init bond checks for OPCM v2 (bonds are set during deployment, not zero) + if (!isDevFeatureEnabled(DevFeatures.OPCM_V2)) { + // Verify that the initial bonds are zero for OPCM v1. + assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), 0, "2700"); + assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.PERMISSIONED_CANNON), 0, "2800"); + } // Check dispute game deployments // Validate permissionedDisputeGame (PDG) address bool isDeployV2Games = isDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES); - IOPContractsManager.Implementations memory impls = opcm.implementations(); + IOPContractsManager.Implementations memory impls = IOPContractsManager(opcmAddr).implementations(); address expectedPDGAddress = isDeployV2Games ? impls.permissionedDisputeGameV2Impl : address(doo.permissionedDisputeGame); address actualPDGAddress = address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)); assertNotEq(actualPDGAddress, address(0), "PDG address should be non-zero"); assertEq(actualPDGAddress, expectedPDGAddress, "PDG address should match expected address"); - // Check PDG getters - IPermissionedDisputeGame pdg = IPermissionedDisputeGame(actualPDGAddress); - bytes32 expectedPrestate = - isDeployV2Games ? bytes32(0) : bytes32(0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c); - assertEq(pdg.l2BlockNumber(), 0, "3000"); - assertEq(Claim.unwrap(pdg.absolutePrestate()), expectedPrestate, "3100"); - assertEq(Duration.unwrap(pdg.clockExtension()), 10800, "3200"); - assertEq(Duration.unwrap(pdg.maxClockDuration()), 302400, "3300"); - assertEq(pdg.splitDepth(), 30, "3400"); - assertEq(pdg.maxGameDepth(), 73, "3500"); + // Skip PDG getter checks for OPCM v2 (game args are passed at creation time) + if (!isDevFeatureEnabled(DevFeatures.OPCM_V2)) { + // Check PDG getters + IPermissionedDisputeGame pdg = IPermissionedDisputeGame(actualPDGAddress); + bytes32 expectedPrestate = isDeployV2Games + ? bytes32(0) + : bytes32(0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c); + assertEq(pdg.l2BlockNumber(), 0, "3000"); + assertEq(Claim.unwrap(pdg.absolutePrestate()), expectedPrestate, "3100"); + assertEq(Duration.unwrap(pdg.clockExtension()), 10800, "3200"); + assertEq(Duration.unwrap(pdg.maxClockDuration()), 302400, "3300"); + assertEq(pdg.splitDepth(), 30, "3400"); + assertEq(pdg.maxGameDepth(), 73, "3500"); + } // Verify custom gas token feature is set as seeded assertEq(