From 2922e3d29506b4aab8b49298527f60df2d468983 Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:51:59 -0300 Subject: [PATCH 01/15] fix: json default zero --- packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index 09dcdf23247..30b88cdd32e 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -126,7 +126,7 @@ contract DeployConfig is Script { useCustomGasToken = _readOr(_json, "$.useCustomGasToken", false); gasPayingTokenName = _readOr(_json, "$.gasPayingTokenName", ""); gasPayingTokenSymbol = _readOr(_json, "$.gasPayingTokenSymbol", ""); - nativeAssetLiquidityAmount = _readOr(_json, "$.nativeAssetLiquidityAmount", type(uint248).max); + nativeAssetLiquidityAmount = _readOr(_json, "$.nativeAssetLiquidityAmount", 0); enableGovernance = _readOr(_json, "$.enableGovernance", false); systemConfigStartBlock = stdJson.readUint(_json, "$.systemConfigStartBlock"); From 571216451d7a095f9f638f599b658d4691446e8f Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:53:14 -0300 Subject: [PATCH 02/15] feat(cgt): add native asset liquidity amount test & omitempty on cgt struct * test: check native asset liquidity amount is correctly configured * feat: add omitempty to name and symbol on cgt intent struct --- op-deployer/pkg/deployer/integration_test/apply_test.go | 7 +++++++ op-deployer/pkg/deployer/state/chain_intent.go | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 2e0fc48208f..854eb452cea 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -279,6 +279,13 @@ func TestEndToEndApply(t *testing.T) { err = fn.DecodeReturns(res, &response) require.NoError(t, err) require.Equal(t, true, response) + + // Check that the native asset liquidity predeploy has the configured amount in L2 genesis + nativeAssetLiquidityAddr := common.HexToAddress("0x4200000000000000000000000000000000000029") + l2Genesis := st.Chains[0].Allocs.Data.Accounts + account, exists := l2Genesis[nativeAssetLiquidityAddr] + 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") }) } diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index 7b4e6727ad7..9eaf5be1b4b 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -59,8 +59,8 @@ type L2DevGenesisParams struct { type CustomGasToken struct { Enabled bool `json:"enabled" toml:"enabled"` - Name string `json:"name" toml:"name"` - Symbol string `json:"symbol" toml:"symbol"` + Name string `json:"name,omitempty" toml:"name,omitempty"` + Symbol string `json:"symbol,omitempty" toml:"symbol,omitempty"` NativeAssetLiquidityAmount *hexutil.Big `json:"nativeAssetLiquidityAmount,omitempty" toml:"nativeAssetLiquidityAmount,omitempty"` } From 3732dc46ab1c978ddf4111f73b6246b71f05b1cb Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:06:48 -0300 Subject: [PATCH 03/15] fix: check value in cgt --- .../src/L1/OptimismPortal2.sol | 39 ++++++++++++------- .../test/L1/OptimismPortal2.t.sol | 20 ++++++++++ 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index f5c158285c3..055672388ff 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -349,14 +349,19 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Cannot prove withdrawal transactions while the system is paused. _assertNotPaused(); - // Fetch the dispute game proxy from the `DisputeGameFactory` contract. - (,, IDisputeGame disputeGameProxy) = disputeGameFactory().gameAtIndex(_disputeGameIndex); - // Make sure that the target address is safe. if (_isUnsafeTarget(_tx.target)) { revert OptimismPortal_BadTarget(); } + // Cannot prove withdrawal with value when custom gas token mode is enabled. + if (_isInvalidCGTWithdrawal(_tx.value)) { + revert OptimismPortal_NotAllowedOnCGTMode(); + } + + // Fetch the dispute game proxy from the `DisputeGameFactory` contract. + (,, IDisputeGame disputeGameProxy) = disputeGameFactory().gameAtIndex(_disputeGameIndex); + // Game must be a Proper Game. if (!anchorStateRegistry.isGameProper(disputeGameProxy)) { revert OptimismPortal_ImproperDisputeGame(); @@ -442,14 +447,19 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase ) public { - // Cannot finalize withdrawal with value when custom gas token mode is enabled. - if (_isUsingCustomGasToken()) { - if (_tx.value > 0) revert OptimismPortal_NotAllowedOnCGTMode(); - } - // Cannot finalize withdrawal transactions while the system is paused. _assertNotPaused(); + // Make sure that the target address is safe. + if (_isUnsafeTarget(_tx.target)) { + revert OptimismPortal_BadTarget(); + } + + // Cannot finalize withdrawal with value when custom gas token mode is enabled. + if (_isInvalidCGTWithdrawal(_tx.value)) { + revert OptimismPortal_NotAllowedOnCGTMode(); + } + // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other // than the default value when a withdrawal transaction is being finalized. This check is // a defacto reentrancy guard. @@ -457,11 +467,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase revert OptimismPortal_NoReentrancy(); } - // Make sure that the target address is safe. - if (_isUnsafeTarget(_tx.target)) { - revert OptimismPortal_BadTarget(); - } - // Grab the withdrawal. bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); @@ -659,6 +664,14 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase return _target == address(this) || _target == address(ethLockbox); } + /// @notice Checks if a withdrawal transaction is invalid due to CGT mode restrictions. + /// @param _value The value of the withdrawal transaction to validate. + /// @return Whether the transaction is invalid (has value when CGT mode is enabled). + function _isInvalidCGTWithdrawal(uint256 _value) internal view returns (bool) { + // Cannot process withdrawal with value when custom gas token mode is enabled. + return _isUsingCustomGasToken() && _value > 0; + } + /// @notice Getter for the resource config. Used internally by the ResourceMetering contract. /// The SystemConfig is the source of truth for the resource config. /// @return config_ ResourceMetering ResourceConfig diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index dadd779c8f5..e8e37f884c6 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -1353,6 +1353,26 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test _withdrawalProof: _withdrawalProof }); } + + /// @notice Tests that `proveWithdrawalTransaction` reverts when the custom gas token mode + /// is enabled and the withdrawal transaction has a value. + function test_proveWithdrawalTransaction_withValueAndCustomGasToken_reverts() external { + skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); + skipIfForkTest( + "OptimismPortal2_ProveWithdrawalTransaction_Test: isCustomGasToken() not available on forked networks" + ); + // Set the withdrawal transaction value to a non-zero value. + _defaultTx.value = bound(uint256(1), 1, type(uint256).max); + + // Prove the withdrawal transaction. This should revert. + vm.expectRevert(IOptimismPortal.OptimismPortal_NotAllowedOnCGTMode.selector); + optimismPortal2.proveWithdrawalTransaction({ + _tx: _defaultTx, + _disputeGameIndex: _proposedGameIndex, + _outputRootProof: _outputRootProof, + _withdrawalProof: _withdrawalProof + }); + } } /// @title OptimismPortal2_FinalizeWithdrawalTransaction_Test From d7b490545d0fc31a409e1cca6684c7a257116d4f Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:24:59 -0300 Subject: [PATCH 04/15] feat(cgt): add cgt dev feature * feat: add cgt dev feature * refactor: set custom gas token (#563) * feat: add cgt devFeatures and remove useCustomGasToken from DeployOPChainInput --------- Co-authored-by: Hex <165055168+hexshire@users.noreply.github.com> --- .circleci/config.yml | 3 ++- op-chain-ops/interopgen/deploy.go | 1 - .../deployer/integration_test/apply_test.go | 6 ++++++ op-deployer/pkg/deployer/opcm/opchain.go | 1 - op-deployer/pkg/deployer/pipeline/opchain.go | 1 - .../interfaces/L1/IOPContractsManager.sol | 1 - .../scripts/deploy/Deploy.s.sol | 1 - .../scripts/deploy/DeployOPChain.s.sol | 10 +-------- .../scripts/libraries/Config.sol | 5 +++++ .../snapshots/abi/OPContractsManager.json | 5 ----- .../abi/OPContractsManagerDeployer.json | 5 ----- .../snapshots/semver-lock.json | 8 +++---- .../src/L1/OPContractsManager.sol | 3 +-- .../src/libraries/DevFeatures.sol | 4 ++++ .../test/L1/OPContractsManager.t.sol | 6 ++---- .../test/L1/OptimismPortal2.t.sol | 12 +++++------ .../test/opcm/DeployOPChain.t.sol | 21 ------------------- .../test/setup/FeatureFlags.sol | 4 ++++ 18 files changed, 35 insertions(+), 62 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 383c258e596..956560b6f5f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2028,7 +2028,8 @@ workflows: dev_features: <> matrix: parameters: - dev_features: ["main", "OPTIMISM_PORTAL_INTEROP"] + dev_features: + ["main", "OPTIMISM_PORTAL_INTEROP", "CUSTOM_GAS_TOKEN"] # need this requires to ensure that all FFI JSONs exist requires: - contracts-bedrock-build diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index dd880890c5c..44952423f58 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -247,7 +247,6 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme AllowCustomDisputeParameters: true, OperatorFeeScalar: cfg.GasPriceOracleOperatorFeeScalar, OperatorFeeConstant: cfg.GasPriceOracleOperatorFeeConstant, - UseCustomGasToken: cfg.UseCustomGasToken, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 854eb452cea..211bae3cb44 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -243,6 +243,8 @@ func TestEndToEndApply(t *testing.T) { t.Run("with custom gas token", func(t *testing.T) { intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) + + // CGT config for L2 genesis amount := new(big.Int) amount.SetString("1000000000000000000000", 10) intent.Chains[0].CustomGasToken = &state.CustomGasToken{ @@ -251,6 +253,10 @@ func TestEndToEndApply(t *testing.T) { Symbol: "CGT", NativeAssetLiquidityAmount: (*hexutil.Big)(amount), } + // CGT config for OPCM + intent.GlobalDeployOverrides = map[string]interface{}{ + "devFeatureBitmap": common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000010"), + } require.NoError(t, deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{ DeploymentTarget: deployer.DeploymentTargetLive, diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 402d0eb0f4f..76afa75fca5 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -40,7 +40,6 @@ type DeployOPChainInput struct { DisputeClockExtension uint64 DisputeMaxClockDuration uint64 AllowCustomDisputeParameters bool - UseCustomGasToken bool OperatorFeeScalar uint32 OperatorFeeConstant uint64 diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index b82da6c8240..b1510a89a2d 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -110,7 +110,6 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common DisputeClockExtension: proofParams.DisputeClockExtension, // 3 hours (input in seconds) DisputeMaxClockDuration: proofParams.DisputeMaxClockDuration, // 3.5 days (input in seconds) AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters, - UseCustomGasToken: thisIntent.CustomGasToken != nil && thisIntent.CustomGasToken.Enabled, OperatorFeeScalar: thisIntent.OperatorFeeScalar, OperatorFeeConstant: thisIntent.OperatorFeeConstant, }, nil diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol index c99055e5099..adb75fe8c8d 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol @@ -145,7 +145,6 @@ interface IOPContractsManager { uint32 basefeeScalar; uint32 blobBasefeeScalar; uint256 l2ChainId; - bool useCustomGasToken; // The correct type is OutputRoot memory but OP Deployer does not yet support structs. bytes startingAnchorRoot; // The salt mixer is used as part of making the resulting salt unique. diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 74f64ba0462..b806e6c3a3b 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -423,7 +423,6 @@ contract Deploy is Deployer { basefeeScalar: cfg.basefeeScalar(), blobBasefeeScalar: cfg.blobbasefeeScalar(), l2ChainId: cfg.l2ChainID(), - useCustomGasToken: cfg.useCustomGasToken(), startingAnchorRoot: abi.encode( Proposal({ root: Hash.wrap(cfg.faultGameGenesisOutputRoot()), l2SequenceNumber: cfg.faultGameGenesisBlock() }) ), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 9db32e7c5b9..77326a62a66 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -55,7 +55,6 @@ contract DeployOPChainInput is BaseDeployIO { Duration internal _disputeClockExtension; Duration internal _disputeMaxClockDuration; bool internal _allowCustomDisputeParameters; - bool internal _useCustomGasToken; uint32 internal _operatorFeeScalar; uint64 internal _operatorFeeConstant; @@ -118,8 +117,6 @@ contract DeployOPChainInput is BaseDeployIO { function set(bytes4 _sel, bool _value) public { if (_sel == this.allowCustomDisputeParameters.selector) { _allowCustomDisputeParameters = _value; - } else if (_sel == this.useCustomGasToken.selector) { - _useCustomGasToken = _value; } else { revert("DeployOPChainInput: unknown selector"); } @@ -171,10 +168,6 @@ contract DeployOPChainInput is BaseDeployIO { return _l2ChainId; } - function useCustomGasToken() public view returns (bool) { - return _useCustomGasToken; - } - function startingAnchorRoot() public pure returns (bytes memory) { // WARNING: For now always hardcode the starting permissioned game anchor root to 0xdead, // and we do not set anything for the permissioned game. This is because we currently only @@ -394,8 +387,7 @@ contract DeployOPChain is Script { disputeMaxGameDepth: _doi.disputeMaxGameDepth(), disputeSplitDepth: _doi.disputeSplitDepth(), disputeClockExtension: _doi.disputeClockExtension(), - disputeMaxClockDuration: _doi.disputeMaxClockDuration(), - useCustomGasToken: _doi.useCustomGasToken() + disputeMaxClockDuration: _doi.disputeMaxClockDuration() }); vm.broadcast(msg.sender); diff --git a/packages/contracts-bedrock/scripts/libraries/Config.sol b/packages/contracts-bedrock/scripts/libraries/Config.sol index a9a4fb8bf82..d0412474074 100644 --- a/packages/contracts-bedrock/scripts/libraries/Config.sol +++ b/packages/contracts-bedrock/scripts/libraries/Config.sol @@ -240,4 +240,9 @@ library Config { function devFeatureInterop() internal view returns (bool) { return vm.envOr("DEV_FEATURE__OPTIMISM_PORTAL_INTEROP", false); } + + /// @notice Returns true if the development feature custom gas token is enabled. + function devFeatureCustomGasToken() internal view returns (bool) { + return vm.envOr("DEV_FEATURE__CUSTOM_GAS_TOKEN", false); + } } diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 28827b6841d..f4ef1718aa4 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -303,11 +303,6 @@ "name": "l2ChainId", "type": "uint256" }, - { - "internalType": "bool", - "name": "useCustomGasToken", - "type": "bool" - }, { "internalType": "bytes", "name": "startingAnchorRoot", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json index f5914713535..7cd7a44502c 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json @@ -191,11 +191,6 @@ "name": "l2ChainId", "type": "uint256" }, - { - "internalType": "bool", - "name": "useCustomGasToken", - "type": "bool" - }, { "internalType": "bytes", "name": "startingAnchorRoot", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 42b0b52cd16..df989daa5aa 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -20,16 +20,16 @@ "sourceCodeHash": "0xfca613b5d055ffc4c3cbccb0773ddb9030abedc1aa6508c9e2e7727cc0cd617b" }, "src/L1/OPContractsManager.sol:OPContractsManager": { - "initCodeHash": "0x7df9acb5abebfb2c619531785e4aa7fcca48f3e4e83b08faada53991a579a789", - "sourceCodeHash": "0x69b4d3684719d3b7f5f1f5e235be92673ca2b3ff7e7edd9b17e50e12499df466" + "initCodeHash": "0xd724cd39e215134f5101e64b46c6b72c692c07777a1db330cc32790b15fce05d", + "sourceCodeHash": "0x35bbbb65a855d7b4ba6338218e539d55b332c40ec7a85f01f222cdf92fa184eb" }, "src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": { "initCodeHash": "0x17a40747da8a9978f7294f071ea371b8c021504b898919431e6acf62623e8adc", "sourceCodeHash": "0xa80dcd2ebafc5a6a437f712d8073f8a48998807aa317ad7762b3fc9dc2caa133" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x1cc12939347474e26a57c89f6fe70bc5c82e456e7583c81da4b327a045c32f38", - "sourceCodeHash": "0x48ff518993541028ba2c299509dcb506a58808900496b4878aae4ec841fa8256" + "initCodeHash": "0xe1246739f9e47fcec4db88c1b7ad8cb658d3760879529b71e7d1c3229789b645", + "sourceCodeHash": "0xb1f34116284f02a89feb23029f2a096cd254338b9f4a5505a3e8948e968a324f" }, "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { "initCodeHash": "0x087281cd2a48e882648c09fa90bfcca7487d222e16300f9372deba6b2b8ccfad", diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 318cb5c1d92..75b98f8e4e0 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -1074,7 +1074,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { // If the custom gas token feature was requested, enable the custom gas token feature in the SystemConfig // contract. - if (_input.useCustomGasToken) { + if (isDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN)) { output.systemConfigProxy.setFeature(Features.CUSTOM_GAS_TOKEN, true); } @@ -1704,7 +1704,6 @@ contract OPContractsManager is ISemver { uint32 basefeeScalar; uint32 blobBasefeeScalar; uint256 l2ChainId; - bool useCustomGasToken; // The correct type is Proposal memory but OP Deployer does not yet support structs. bytes startingAnchorRoot; // The salt mixer is used as part of making the resulting salt unique. diff --git a/packages/contracts-bedrock/src/libraries/DevFeatures.sol b/packages/contracts-bedrock/src/libraries/DevFeatures.sol index 9ec47379bac..4b9c5ac2d1e 100644 --- a/packages/contracts-bedrock/src/libraries/DevFeatures.sol +++ b/packages/contracts-bedrock/src/libraries/DevFeatures.sol @@ -14,6 +14,10 @@ library DevFeatures { bytes32 public constant OPTIMISM_PORTAL_INTEROP = bytes32(0x0000000000000000000000000000000000000000000000000000000000000001); + /// @notice The feature that enables the custom gas token. + bytes32 public constant CUSTOM_GAS_TOKEN = + bytes32(0x0000000000000000000000000000000000000000000000000000000000000010); + /// @notice Checks if a feature is enabled in a bitmap. Note that this function does not check /// that the input feature represents a single feature and the bitwise AND operation /// allows for multiple features to be enabled at once. Users should generally check diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index cb610119e6c..4df6358cae1 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -362,8 +362,7 @@ contract OPContractsManager_TestInit is CommonTest { disputeMaxGameDepth: 73, disputeSplitDepth: 30, disputeClockExtension: Duration.wrap(10800), - disputeMaxClockDuration: Duration.wrap(302400), - useCustomGasToken: false + disputeMaxClockDuration: Duration.wrap(302400) }) ); } @@ -1638,8 +1637,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { disputeMaxGameDepth: _doi.disputeMaxGameDepth(), disputeSplitDepth: _doi.disputeSplitDepth(), disputeClockExtension: _doi.disputeClockExtension(), - disputeMaxClockDuration: _doi.disputeMaxClockDuration(), - useCustomGasToken: _doi.useCustomGasToken() + disputeMaxClockDuration: _doi.disputeMaxClockDuration() }); } diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index e8e37f884c6..a2047253f80 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -166,7 +166,7 @@ contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { /// @notice Checks if the Custom Gas Token feature is enabled. /// @return bool True if the Custom Gas Token feature is enabled. function isUsingCustomGasToken() public view returns (bool) { - return systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN); + return isDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); } /// @notice Enables the ETHLockbox feature if not enabled. @@ -590,7 +590,7 @@ contract OptimismPortal2_NumProofSubmitters_Test is OptimismPortal2_TestInit { contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { /// @notice Tests that `receive` successfully deposits ETH. function testFuzz_receive_succeeds(uint256 _value) external { - skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); // Prevent overflow on an upgrade context _value = bound(_value, 0, type(uint256).max - address(ethLockbox).balance); uint256 balanceBefore = address(optimismPortal2).balance; @@ -629,7 +629,7 @@ contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { } function testFuzz_receive_withLockbox_succeeds(uint256 _value) external { - skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); // Prevent overflow on an upgrade context. // We use a dummy lockbox here because the real one won't work for upgrade tests. address dummyLockbox = address(0xdeadbeef); @@ -668,7 +668,7 @@ contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { /// @notice Tests that `receive` reverts when custom gas token is enabled function testFuzz_receive_customGasToken_reverts(uint256 _value) external virtual { - skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureDisabled(DevFeatures.CUSTOM_GAS_TOKEN); _value = bound(_value, 1, type(uint128).max); vm.deal(alice, _value); @@ -1862,7 +1862,7 @@ contract OptimismPortal2_FinalizeWithdrawalTransaction_Test is OptimismPortal2_T /// @notice Tests that `finalizeWithdrawalTransaction` reverts when the custom gas token mode /// is enabled and the withdrawal transaction has a value. function test_finalizeWithdrawalTransaction_withValueAndCustomGasToken_reverts() external { - skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureDisabled(DevFeatures.CUSTOM_GAS_TOKEN); skipIfForkTest( "OptimismPortal2_FinalizeWithdrawalTransaction_Test: isCustomGasToken() not available on forked networks" ); @@ -2470,7 +2470,7 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { /// @notice Tests that `depositTransaction` reverts when the value is greater than 0 and the /// custom gas token is active. function test_depositTransaction_withCustomGasTokenAndValue_reverts(bytes memory _data, uint256 _value) external { - skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureDisabled(DevFeatures.CUSTOM_GAS_TOKEN); skipIfForkTest("OptimismPortal2_DepositTransaction_Test: isCustomGasToken() not available on forked networks"); // Prevent overflow on an upgrade context diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index a8534c1edbf..6f735c9a611 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -56,7 +56,6 @@ contract DeployOPChainInput_Test is Test { doi.set(doi.blobBaseFeeScalar.selector, blobBaseFeeScalar); doi.set(doi.l2ChainId.selector, l2ChainId); doi.set(doi.allowCustomDisputeParameters.selector, true); - doi.set(doi.useCustomGasToken.selector, false); doi.set(doi.opcm.selector, opcm); vm.etch(opcm, hex"01"); @@ -72,7 +71,6 @@ contract DeployOPChainInput_Test is Test { assertEq(l2ChainId, doi.l2ChainId(), "1000"); assertEq(opcm, address(doi.opcm()), "1100"); assertEq(true, doi.allowCustomDisputeParameters(), "1200"); - assertEq(false, doi.useCustomGasToken(), "1300"); } function test_getters_whenNotSet_reverts() public { @@ -330,7 +328,6 @@ contract DeployOPChain_TestBase is Test { IOPContractsManager opcm = IOPContractsManager(address(0)); string saltMixer = "defaultSaltMixer"; uint64 gasLimit = 60_000_000; - bool useCustomGasToken = false; // Configurable dispute game parameters. uint32 disputeGameType = GameType.unwrap(GameTypes.PERMISSIONED_CANNON); bytes32 disputeAbsolutePrestate = hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"; @@ -404,7 +401,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { basefeeScalar = uint32(uint256(hash(_seed, 6))); blobBaseFeeScalar = uint32(uint256(hash(_seed, 7))); l2ChainId = uint256(hash(_seed, 8)); - useCustomGasToken = bool(uint256(hash(_seed, 9)) % 2 == 0); doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); @@ -424,7 +420,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth); doi.set(doi.disputeClockExtension.selector, disputeClockExtension); doi.set(doi.disputeMaxClockDuration.selector, disputeMaxClockDuration); - doi.set(doi.useCustomGasToken.selector, useCustomGasToken); deployOPChain.run(doi, doo); @@ -448,7 +443,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { assertEq(disputeSplitDepth, doi.disputeSplitDepth(), "1500"); assertEq(disputeClockExtension, Duration.unwrap(doi.disputeClockExtension()), "1600"); assertEq(disputeMaxClockDuration, Duration.unwrap(doi.disputeMaxClockDuration()), "1700"); - assertEq(useCustomGasToken, doi.useCustomGasToken(), "1800"); // Assert inputs were properly passed through to the contract initializers. assertEq(address(doo.opChainProxyAdmin().owner()), opChainProxyAdminOwner, "2100"); @@ -493,20 +487,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { assertEq(doo.permissionedDisputeGame().splitDepth(), disputeSplitDepth + 1); } - function test_isCustomGasToken_whenTrue_succeeds() public { - setDOI(); - doi.set(doi.useCustomGasToken.selector, true); - deployOPChain.run(doi, doo); - assertEq(doi.useCustomGasToken(), true, "CGT-300"); - } - - function test_isCustomGasToken_whenFalse_succeeds() public { - setDOI(); - doi.set(doi.useCustomGasToken.selector, false); - deployOPChain.run(doi, doo); - assertEq(doi.useCustomGasToken(), false, "CGT-400"); - } - function setDOI() internal { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); @@ -526,6 +506,5 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { doi.set(doi.disputeSplitDepth.selector, disputeSplitDepth); doi.set(doi.disputeClockExtension.selector, disputeClockExtension); doi.set(doi.disputeMaxClockDuration.selector, disputeMaxClockDuration); - doi.set(doi.useCustomGasToken.selector, false); } } diff --git a/packages/contracts-bedrock/test/setup/FeatureFlags.sol b/packages/contracts-bedrock/test/setup/FeatureFlags.sol index 675a130e558..6b79cc58082 100644 --- a/packages/contracts-bedrock/test/setup/FeatureFlags.sol +++ b/packages/contracts-bedrock/test/setup/FeatureFlags.sol @@ -36,6 +36,10 @@ contract FeatureFlags { console.log("Setup: DEV_FEATURE__OPTIMISM_PORTAL_INTEROP is enabled"); devFeatureBitmap |= DevFeatures.OPTIMISM_PORTAL_INTEROP; } + if (Config.devFeatureCustomGasToken()) { + console.log("Setup: DEV_FEATURE__CUSTOM_GAS_TOKEN is enabled"); + devFeatureBitmap |= DevFeatures.CUSTOM_GAS_TOKEN; + } } /// @notice Enables a feature. From 8243537d4073f1f1206fe760ac534f332eb7e8b8 Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:59:33 -0300 Subject: [PATCH 05/15] fix: ir informational * fix: informational * fix: legacy tag --- .../contracts-bedrock/src/L1/SystemConfig.sol | 1 + .../src/L2/L2ToL1MessagePasser.sol | 2 +- .../test/scripts/L2Genesis.t.sol | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index eb6b54754ab..465232c9ab7 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -520,6 +520,7 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl /// @notice Returns whether the custom gas token feature is enabled. /// @return bool True if the custom gas token feature is enabled, false otherwise. + /// @custom:legacy function isCustomGasToken() public view returns (bool) { return isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]; } diff --git a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol index 3ad1522b4d2..d675fe4f94d 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol @@ -48,7 +48,7 @@ contract L2ToL1MessagePasser is ISemver { ); /// @notice Emitted when the balance of this contract is burned. - /// @param amount Amount of ETh that was burned. + /// @param amount Amount of ETH that was burned. event WithdrawerBalanceBurnt(uint256 indexed amount); /// @custom:semver 1.1.3 diff --git a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index 2426eae290f..2cd1d3180ab 100644 --- a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol @@ -30,7 +30,17 @@ contract L2Genesis_TestInit is Test { } function testProxyAdmin() internal view { + // Verify owner in the proxy assertEq(input.opChainProxyAdminOwner, IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + + // Verify owner in the implementation to catch storage shifting issues + // The implementation is stored in the code namespace + address proxyAdminImpl = Predeploys.predeployToCodeNamespace(Predeploys.PROXY_ADMIN); + assertEq( + input.opChainProxyAdminOwner, + IProxyAdmin(proxyAdminImpl).owner(), + "ProxyAdmin implementation owner should match expected" + ); } function testPredeploys() internal view { @@ -83,7 +93,14 @@ contract L2Genesis_TestInit is Test { function testGovernance() internal view { IGovernanceToken token = IGovernanceToken(payable(Predeploys.GOVERNANCE_TOKEN)); + + // Verify owner (existing check) assertEq(token.owner(), input.governanceTokenOwner); + + // Verify name and symbol to catch storage shifting issues + // These should match the values hardcoded in GovernanceToken constructor + assertEq(token.name(), "Optimism", "GovernanceToken name should be 'Optimism'"); + assertEq(token.symbol(), "OP", "GovernanceToken symbol should be 'OP'"); } function testFactories() internal view { From ee852abfa59d385afdef8d642e93a15d2775eaa1 Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:02:02 -0300 Subject: [PATCH 06/15] feat(cgt): add setNativeAssetLiquidityAmount & L2Genesis max amount check * feat: add setNativeAssetLiquidityAmount & L2Genesis max amount check * test: fix bound param in liquidity controller * fix: remove unused import * refactor: move max amount check inside setNativeAssetLiquidity --- packages/contracts-bedrock/scripts/L2Genesis.s.sol | 6 ++++++ .../contracts-bedrock/scripts/deploy/DeployConfig.s.sol | 5 +++++ .../contracts-bedrock/test/L2/LiquidityController.t.sol | 6 +++--- packages/contracts-bedrock/test/scripts/L2Genesis.t.sol | 9 +++++++++ packages/contracts-bedrock/test/setup/CommonTest.sol | 1 + 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 1ea3d989178..277e6101005 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -124,6 +124,7 @@ contract L2Genesis is Script { if (_input.fundDevAccounts) { fundDevAccounts(); } + vm.stopPrank(); vm.deal(deployer, 0); vm.resetNonce(deployer); @@ -600,6 +601,11 @@ contract L2Genesis is Script { function setNativeAssetLiquidity(Input memory _input) internal { _setImplementationCode(Predeploys.NATIVE_ASSET_LIQUIDITY); + require( + _input.nativeAssetLiquidityAmount <= type(uint248).max, + "Native asset liquidity amount must be less than or equal to type(uint248).max" + ); + // Pre-fund the liquidity contract with the specified amount vm.deal(Predeploys.NATIVE_ASSET_LIQUIDITY, _input.nativeAssetLiquidityAmount); } diff --git a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index 30b88cdd32e..4155b18bc62 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -236,6 +236,11 @@ contract DeployConfig is Script { useCustomGasToken = _useCustomGasToken; } + /// @notice Allow the `nativeAssetLiquidityAmount` config to be overridden in testing environments + function setNativeAssetLiquidityAmount(uint256 _nativeAssetLiquidityAmount) public { + nativeAssetLiquidityAmount = _nativeAssetLiquidityAmount; + } + /// @notice Allow the `baseFeeVaultWithdrawalNetwork` config to be overridden in testing environments function setBaseFeeVaultWithdrawalNetwork(uint256 _baseFeeVaultWithdrawalNetwork) public { baseFeeVaultWithdrawalNetwork = _baseFeeVaultWithdrawalNetwork; diff --git a/packages/contracts-bedrock/test/L2/LiquidityController.t.sol b/packages/contracts-bedrock/test/L2/LiquidityController.t.sol index 911b8d8c987..77cec8b5e3c 100644 --- a/packages/contracts-bedrock/test/L2/LiquidityController.t.sol +++ b/packages/contracts-bedrock/test/L2/LiquidityController.t.sol @@ -168,7 +168,7 @@ contract LiquidityController_Mint_Test is LiquidityController_TestInit { /// @notice Tests that the mint function reverts when called by unauthorized address. function testFuzz_mint_fromUnauthorizedCaller_fails(address _caller, address _to, uint256 _amount) public { - _amount = bound(_amount, 1, type(uint248).max); + _amount = bound(_amount, 1, address(nativeAssetLiquidity).balance); uint256 nativeAssetBalanceBefore = address(nativeAssetLiquidity).balance; uint256 toBalanceBefore = _to.balance; @@ -208,7 +208,7 @@ contract LiquidityController_Burn_Test is LiquidityController_TestInit { /// @notice Tests that the burn function can be called by an authorized minter. function testFuzz_burn_fromAuthorizedMinter_succeeds(uint256 _amount) public isAuthorizedMinter(authorizedMinter) { - _amount = bound(_amount, 0, type(uint248).max); + _amount = bound(_amount, 0, address(nativeAssetLiquidity).balance); // Deal the authorized minter with the amount to burn vm.deal(authorizedMinter, _amount); @@ -238,7 +238,7 @@ contract LiquidityController_Burn_Test is LiquidityController_TestInit { isAuthorizedMinter(authorizedMinter) { vm.assume(_caller != authorizedMinter); - _amount = bound(_amount, 0, type(uint248).max); + _amount = bound(_amount, 0, address(nativeAssetLiquidity).balance); // Deal the unauthorized caller with the amount to burn vm.deal(_caller, _amount); diff --git a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index 2cd1d3180ab..8fa2e2cae2c 100644 --- a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol @@ -244,5 +244,14 @@ contract L2Genesis_Run_Test is L2Genesis_TestInit { input.l1FeeVaultWithdrawalNetwork = 0; vm.expectRevert("L1FeeVault: withdrawalNetwork type cannot be L1 when custom gas token is enabled"); genesis.run(input); + // Reset l1FeeVaultWithdrawalNetwork input to L2 + input.l1FeeVaultWithdrawalNetwork = 1; + + // Expect revert when nativeAssetLiquidityAmount is greater than type(uint248).max + input.nativeAssetLiquidityAmount += 1; + vm.expectRevert("Native asset liquidity amount must be less than or equal to type(uint248).max"); + genesis.run(input); + // Reset nativeAssetLiquidityAmount input to type(uint248).max + input.nativeAssetLiquidityAmount = type(uint248).max; } } diff --git a/packages/contracts-bedrock/test/setup/CommonTest.sol b/packages/contracts-bedrock/test/setup/CommonTest.sol index 7fc22b467b0..1a09ae741f7 100644 --- a/packages/contracts-bedrock/test/setup/CommonTest.sol +++ b/packages/contracts-bedrock/test/setup/CommonTest.sol @@ -77,6 +77,7 @@ contract CommonTest is Test, Setup, Events { } if (useCustomGasToken) { deploy.cfg().setUseCustomGasToken(true); + deploy.cfg().setNativeAssetLiquidityAmount(type(uint248).max); deploy.cfg().setBaseFeeVaultWithdrawalNetwork(1); deploy.cfg().setL1FeeVaultWithdrawalNetwork(1); deploy.cfg().setSequencerFeeVaultWithdrawalNetwork(1); From 5cfe047c2b5d4973faa71781de5706eba4905cef Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:30:28 -0300 Subject: [PATCH 07/15] refactor: make chainIntent.CustomGasToken a non-pointer (#574) * refactor: make chainIntent.CustomGasToken a non-pointer * test: fix custom gas token text (#576) --------- Co-authored-by: Hex <165055168+hexshire@users.noreply.github.com> --- .../pkg/deployer/integration_test/apply_test.go | 4 ++-- op-deployer/pkg/deployer/pipeline/l2genesis.go | 5 +++-- op-deployer/pkg/deployer/state/chain_intent.go | 6 +++--- .../pkg/deployer/state/deploy_config_test.go | 10 ++++++---- op-deployer/pkg/deployer/state/intent.go | 14 +++++++++----- op-deployer/pkg/deployer/state/intent_test.go | 16 ++++++++-------- op-e2e/config/init.go | 4 ++-- op-e2e/e2eutils/intentbuilder/builder.go | 2 +- op-e2e/e2eutils/intentbuilder/builder_test.go | 2 +- 9 files changed, 35 insertions(+), 28 deletions(-) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 211bae3cb44..4161f9bec86 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -247,7 +247,7 @@ func TestEndToEndApply(t *testing.T) { // CGT config for L2 genesis amount := new(big.Int) amount.SetString("1000000000000000000000", 10) - intent.Chains[0].CustomGasToken = &state.CustomGasToken{ + intent.Chains[0].CustomGasToken = state.CustomGasToken{ Enabled: true, Name: "Custom Gas Token", Symbol: "CGT", @@ -766,7 +766,7 @@ func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.In Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)), Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)), }, - CustomGasToken: &state.CustomGasToken{ + CustomGasToken: state.CustomGasToken{ Enabled: false, Name: "", Symbol: "", diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis.go b/op-deployer/pkg/deployer/pipeline/l2genesis.go index cdafc262b42..08bce853eb9 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis.go @@ -154,8 +154,9 @@ func calculateL2GenesisOverrides(intent *state.Intent, thisIntent *state.ChainIn } } - if thisIntent.CustomGasToken == nil { - thisIntent.CustomGasToken = &state.CustomGasToken{ + // If CustomGasToken is not enabled, update it with override values + if !thisIntent.CustomGasToken.Enabled { + thisIntent.CustomGasToken = state.CustomGasToken{ Enabled: overrides.UseCustomGasToken, Name: overrides.GasPayingTokenName, Symbol: overrides.GasPayingTokenSymbol, diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index 9eaf5be1b4b..4286d718082 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -81,7 +81,7 @@ type ChainIntent struct { OperatorFeeConstant uint64 `json:"operatorFeeConstant,omitempty" toml:"operatorFeeConstant,omitempty"` L1StartBlockHash *common.Hash `json:"l1StartBlockHash,omitempty" toml:"l1StartBlockHash,omitempty"` MinBaseFee uint64 `json:"minBaseFee,omitempty" toml:"minBaseFee,omitempty"` - CustomGasToken *CustomGasToken `json:"customGasToken" toml:"customGasToken"` + CustomGasToken CustomGasToken `json:"customGasToken" toml:"customGasToken"` // Optional. For development purposes only. Only enabled if the operation mode targets a genesis-file output. L2DevGenesisParams *L2DevGenesisParams `json:"l2DevGenesisParams,omitempty" toml:"l2DevGenesisParams,omitempty"` } @@ -127,7 +127,7 @@ func (c *ChainIntent) Check() error { return fmt.Errorf("%w: chainId=%s", ErrFeeVaultZeroAddress, c.ID) } - if c.CustomGasToken != nil && c.CustomGasToken.Enabled { + if c.CustomGasToken.Enabled { if c.CustomGasToken.Name == "" { return fmt.Errorf("%w: CustomGasToken.Name cannot be empty when enabled, chainId=%s", ErrIncompatibleValue, c.ID) } @@ -150,7 +150,7 @@ func (c *ChainIntent) Check() error { // GetNativeAssetLiquidityAmount returns the native asset liquidity amount for the chain. // If not set, returns the default value of zero. func (c *ChainIntent) GetNativeAssetLiquidityAmount() *big.Int { - if c.CustomGasToken != nil && c.CustomGasToken.NativeAssetLiquidityAmount != nil { + if c.CustomGasToken.NativeAssetLiquidityAmount != nil { return c.CustomGasToken.NativeAssetLiquidityAmount.ToInt() } diff --git a/op-deployer/pkg/deployer/state/deploy_config_test.go b/op-deployer/pkg/deployer/state/deploy_config_test.go index 6e9bfd0d06d..324b6598335 100644 --- a/op-deployer/pkg/deployer/state/deploy_config_test.go +++ b/op-deployer/pkg/deployer/state/deploy_config_test.go @@ -1,6 +1,7 @@ package state import ( + "math/big" "testing" "github.com/ethereum-optimism/optimism/op-chain-ops/addresses" @@ -33,10 +34,11 @@ func TestCombineDeployConfig(t *testing.T) { UnsafeBlockSigner: common.HexToAddress("0xabc"), Batcher: common.HexToAddress("0xdef"), }, - CustomGasToken: &CustomGasToken{ - Enabled: false, - Name: "Test", - Symbol: "TEST", + CustomGasToken: CustomGasToken{ + Enabled: false, + Name: "", + Symbol: "", + NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(0)), }, } state := State{ diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 8879e5df237..b4edc275a37 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -158,8 +158,7 @@ func (c *Intent) validateStandardValues() error { if len(chain.AdditionalDisputeGames) > 0 { return fmt.Errorf("%w: chainId=%s additionalDisputeGames must be nil", ErrNonStandardValue, chain.ID) } - - if chain.CustomGasToken != nil { + if chain.CustomGasToken.Enabled { return fmt.Errorf("%w: chainId=%s custom gas token must be nil for standard chains", ErrNonStandardValue, chain.ID) } } @@ -303,11 +302,11 @@ func NewIntentCustom(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) intent.Chains = append(intent.Chains, &ChainIntent{ ID: l2ChainID, GasLimit: standard.GasLimit, - CustomGasToken: &CustomGasToken{ + CustomGasToken: CustomGasToken{ Enabled: false, Name: "", Symbol: "", - NativeAssetLiquidityAmount: nil, + NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(0)), }, }) } @@ -353,7 +352,12 @@ func NewIntentStandard(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, erro L1ProxyAdminOwner: l1ProxyAdminOwner, L2ProxyAdminOwner: l2ProxyAdminOwner, }, - CustomGasToken: nil, // Standard chains must have nil CustomGasToken + CustomGasToken: CustomGasToken{ + Enabled: false, + Name: "", + Symbol: "", + NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(0)), + }, }) } return intent, nil diff --git a/op-deployer/pkg/deployer/state/intent_test.go b/op-deployer/pkg/deployer/state/intent_test.go index 9507054f575..3fdd975c576 100644 --- a/op-deployer/pkg/deployer/state/intent_test.go +++ b/op-deployer/pkg/deployer/state/intent_test.go @@ -67,11 +67,11 @@ func TestValidateStandardValues(t *testing.T) { { "CustomGasToken", func(intent *Intent) { - intent.Chains[0].CustomGasToken = &CustomGasToken{ - Enabled: false, - Name: "", - Symbol: "", - NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(0)), + intent.Chains[0].CustomGasToken = CustomGasToken{ + Enabled: true, + Name: "Custom Gas Token", + Symbol: "CGT", + NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(1000)), } }, ErrNonStandardValue, @@ -176,7 +176,7 @@ func TestValidateCustomValues(t *testing.T) { { "empty custom gas token name when enabled", func(intent *Intent) { - intent.Chains[0].CustomGasToken = &CustomGasToken{ + intent.Chains[0].CustomGasToken = CustomGasToken{ Enabled: true, Name: "", Symbol: "CGT", @@ -187,7 +187,7 @@ func TestValidateCustomValues(t *testing.T) { { "empty custom gas token symbol when enabled", func(intent *Intent) { - intent.Chains[0].CustomGasToken = &CustomGasToken{ + intent.Chains[0].CustomGasToken = CustomGasToken{ Enabled: true, Name: "Custom Gas Token", Symbol: "", @@ -257,7 +257,7 @@ func setCustomGasToken(intent *Intent) { amount := new(big.Int) amount.SetString("1000000000000000000000", 10) - intent.Chains[0].CustomGasToken = &CustomGasToken{ + intent.Chains[0].CustomGasToken = CustomGasToken{ Enabled: true, Name: "Custom Gas Token", Symbol: "CGT", diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 37dcc27da3b..1c105a245aa 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -400,11 +400,11 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, Proposer: addrs.Proposer, Challenger: common.HexToAddress("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"), }, - CustomGasToken: &state.CustomGasToken{ + CustomGasToken: state.CustomGasToken{ Enabled: false, Name: "", Symbol: "", - NativeAssetLiquidityAmount: nil, + NativeAssetLiquidityAmount: (*hexutil.Big)(big.NewInt(0)), }, AdditionalDisputeGames: []state.AdditionalDisputeGame{ { diff --git a/op-e2e/e2eutils/intentbuilder/builder.go b/op-e2e/e2eutils/intentbuilder/builder.go index 47daff25d82..a3dfca9a144 100644 --- a/op-e2e/e2eutils/intentbuilder/builder.go +++ b/op-e2e/e2eutils/intentbuilder/builder.go @@ -390,7 +390,7 @@ func (c *l2Configurator) WithEIP1559Denominator(value uint64) { } func (c *l2Configurator) WithCustomGasToken(enabled bool, name, symbol string, nativeAssetLiquidityAmount *big.Int) { - c.builder.intent.Chains[c.chainIndex].CustomGasToken = &state.CustomGasToken{ + c.builder.intent.Chains[c.chainIndex].CustomGasToken = state.CustomGasToken{ Enabled: enabled, Name: name, Symbol: symbol, diff --git a/op-e2e/e2eutils/intentbuilder/builder_test.go b/op-e2e/e2eutils/intentbuilder/builder_test.go index 15b50d83f5f..9895f104b2f 100644 --- a/op-e2e/e2eutils/intentbuilder/builder_test.go +++ b/op-e2e/e2eutils/intentbuilder/builder_test.go @@ -163,7 +163,7 @@ func TestBuilder(t *testing.T) { GasLimit: standard.GasLimit, OperatorFeeScalar: 100, OperatorFeeConstant: 200, - CustomGasToken: &state.CustomGasToken{ + CustomGasToken: state.CustomGasToken{ Enabled: false, Name: "", Symbol: "", From 6287839ea028ba6936c5523a8e31476808ce314d Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Thu, 18 Sep 2025 21:01:28 -0300 Subject: [PATCH 08/15] fix: undo tests Co-authored-by: hexshire --- .../src/L1/OptimismPortal2.sol | 9 ++-- .../test/L1/OptimismPortal2.t.sol | 54 ++++++++++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 055672388ff..5e1cd684317 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -236,6 +236,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase systemConfig = _systemConfig; anchorStateRegistry = _anchorStateRegistry; + // Assert that the lockbox state is valid. + _assertValidLockboxState(); + // Set the l2Sender slot, only if it is currently empty. This signals the first // initialization of the contract. if (l2Sender == address(0)) { @@ -246,12 +249,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase __ResourceMetering_init(); } - /// @notice Returns whether the custom gas token feature is enabled. - /// @return bool True if the custom gas token feature is enabled, false otherwise. - function isCustomGasToken() public view returns (bool) { - return _isUsingCustomGasToken(); - } - /// @notice Getter for the current paused status. function paused() public view returns (bool) { return systemConfig.paused(); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index a2047253f80..fcf71c57444 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -240,9 +240,9 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { assertEq(address(optimismPortal2.ethLockbox()), address(0)); } if (isUsingCustomGasToken()) { - assertTrue(OptimismPortal2(payable(address(optimismPortal2))).isCustomGasToken()); + assertTrue(optimismPortal2.systemConfig().isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)); } else if (!isUsingLockbox()) { - assertFalse(OptimismPortal2(payable(address(optimismPortal2))).isCustomGasToken()); + assertFalse(optimismPortal2.systemConfig().isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)); } returnIfForkTest( @@ -271,6 +271,56 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { // Assert that the initializer value matches the expected value. assertEq(val, optimismPortal2.initVersion()); } + /// @notice Tests that the initialize function reverts if called by a non-proxy admin or owner. + /// @param _sender The address of the sender to test. + + function testFuzz_initialize_interopNotProxyAdminOrProxyAdminOwner_reverts(address _sender) public { + skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); + + // Prank as the not ProxyAdmin or ProxyAdmin owner. + vm.assume(_sender != address(proxyAdmin) && _sender != proxyAdminOwner); + + // Get the slot for _initialized. + StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); + + // Set the initialized slot to 0. + vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); + + // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner` selector. + vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner.selector); + + // Call the `initialize` function with the sender + vm.prank(_sender); + IOptimismPortalInterop(payable(optimismPortal2)).initialize(systemConfig, anchorStateRegistry, ethLockbox); + } + + /// @notice Tests that the initialize function reverts when lockbox state is invalid. + function test_initialize_invalidLockboxState_reverts() external { + skipIfDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); + + // Get the slot for _initialized. + StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); + + // Set the initialized slot to 0. + vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); + + // Enable ETH_LOCKBOX feature but clear the lockbox address to create invalid state. + if (!systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { + vm.prank(address(proxyAdmin)); + systemConfig.setFeature(Features.ETH_LOCKBOX, true); + } + + // Clear the lockbox address. + StorageSlot memory lockboxSlot = ForgeArtifacts.getSlot("OptimismPortal2", "ethLockbox"); + vm.store(address(optimismPortal2), bytes32(lockboxSlot.slot), bytes32(0)); + + // Expect the revert with `OptimismPortal_InvalidLockboxState` selector. + vm.expectRevert(IOptimismPortal.OptimismPortal_InvalidLockboxState.selector); + + // Call the `initialize` function + vm.prank(address(proxyAdmin)); + optimismPortal2.initialize(systemConfig, anchorStateRegistry); + } /// @notice Tests that the initialize function reverts if called by a non-proxy admin or owner. /// @param _sender The address of the sender to test. From 81796abb7b82c2b43cb9667cb3cde59e6c5b38bc Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:12:06 -0300 Subject: [PATCH 09/15] fix: cgt portal (#577) --- .../interfaces/L1/IOptimismPortal2.sol | 7 +------ .../contracts-bedrock/scripts/L2Genesis.s.sol | 2 +- .../snapshots/abi/OptimismPortal2.json | 13 ------------- .../contracts-bedrock/snapshots/semver-lock.json | 10 +++++----- .../contracts-bedrock/src/L1/OptimismPortal2.sol | 16 +++++++++------- .../contracts-bedrock/src/L1/SystemConfig.sol | 6 +++--- .../test/L1/OptimismPortal2.t.sol | 1 - 7 files changed, 19 insertions(+), 36 deletions(-) diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index ffa8ab35130..849249ab269 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -71,13 +71,8 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { external; function finalizedWithdrawals(bytes32) external view returns (bool); function guardian() external view returns (address); - function initialize( - ISystemConfig _systemConfig, - IAnchorStateRegistry _anchorStateRegistry - ) - external; + function initialize(ISystemConfig _systemConfig, IAnchorStateRegistry _anchorStateRegistry) external; function initVersion() external view returns (uint8); - function isCustomGasToken() external view returns (bool); function l2Sender() external view returns (address); function minimumGasLimit(uint64 _byteCount) external pure returns (uint64); function numProofSubmitters(bytes32 _withdrawalHash) external view returns (uint256); diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 277e6101005..244cdc4c409 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -603,7 +603,7 @@ contract L2Genesis is Script { require( _input.nativeAssetLiquidityAmount <= type(uint248).max, - "Native asset liquidity amount must be less than or equal to type(uint248).max" + "L2Genesis: native asset liquidity amount must be less than or equal to type(uint248).max" ); // Pre-fund the liquidity contract with the specified amount diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 8f6545baf8f..9d19d18dbe5 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -301,19 +301,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "isCustomGasToken", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "l2Sender", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index df989daa5aa..bbe858e6469 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -28,8 +28,8 @@ "sourceCodeHash": "0xa80dcd2ebafc5a6a437f712d8073f8a48998807aa317ad7762b3fc9dc2caa133" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0xe1246739f9e47fcec4db88c1b7ad8cb658d3760879529b71e7d1c3229789b645", - "sourceCodeHash": "0xb1f34116284f02a89feb23029f2a096cd254338b9f4a5505a3e8948e968a324f" + "initCodeHash": "0x17e83cec6174c046b8a1f0ac710b269c380f57e07eb33b62673b42ab2ebdeea5", + "sourceCodeHash": "0x41152ef88638d180591b843d75137cac5a28304b9e4a100e3f798a6b5a87ad2e" }, "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { "initCodeHash": "0x087281cd2a48e882648c09fa90bfcca7487d222e16300f9372deba6b2b8ccfad", @@ -44,8 +44,8 @@ "sourceCodeHash": "0xbf344c4369b8cb00ec7a3108f72795747f3bc59ab5b37ac18cf21e72e2979dbf" }, "src/L1/SystemConfig.sol:SystemConfig": { - "initCodeHash": "0xfb19d8929abb73b1678b280eab947847c3f5aed67d1a7773b713fb9911ed1839", - "sourceCodeHash": "0xf7bd98c11f7b35f5da4d73aa389e0d22c352b4246c3a2c199a5e8ce90da95520" + "initCodeHash": "0xfdeaf611cb031e9d53492cea8bcc62bc64686aea9e5c0ba258366de955b31668", + "sourceCodeHash": "0x7750ade92ba2910a39a623c808803b5dafd8a57baa1168c66d528a0d16d7498f" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x9b664e3d84ad510091337b4aacaa494b142512e2f6f7fbcdb6210ed62ca9b885", @@ -93,7 +93,7 @@ }, "src/L2/L2ToL1MessagePasser.sol:L2ToL1MessagePasser": { "initCodeHash": "0xff1caffe84cbae4b94285754885d00fa4e174efe7043258c56ba61825eedba7d", - "sourceCodeHash": "0x4a03734b3c2ab1403506de5c833e538b2cfa81bf6e880b5ca80852abfa34c3d9" + "sourceCodeHash": "0xffa514767f63fae7566c65817bcc5fc6a72490af6bf7144d1aea53d3bf93a9e6" }, "src/L2/L2ToL1MessagePasserCGT.sol:L2ToL1MessagePasserCGT": { "initCodeHash": "0x5e1e6b0fa69edb4e07e01dd4ae915a319bbba318c079fa0a6c9c4878d9c98b7c", diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 5e1cd684317..801fe0c0eff 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -208,9 +208,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase error OptimismPortal_InvalidLockboxState(); /// @notice Semantic version. - /// @custom:semver 5.1.1 + /// @custom:semver 5.2.0 function version() public pure virtual returns (string memory) { - return "5.1.1"; + return "5.2.0"; } /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. @@ -447,11 +447,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Cannot finalize withdrawal transactions while the system is paused. _assertNotPaused(); - // Make sure that the target address is safe. - if (_isUnsafeTarget(_tx.target)) { - revert OptimismPortal_BadTarget(); - } - // Cannot finalize withdrawal with value when custom gas token mode is enabled. if (_isInvalidCGTWithdrawal(_tx.value)) { revert OptimismPortal_NotAllowedOnCGTMode(); @@ -464,6 +459,11 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase revert OptimismPortal_NoReentrancy(); } + // Make sure that the target address is safe. + if (_isUnsafeTarget(_tx.target)) { + revert OptimismPortal_BadTarget(); + } + // Grab the withdrawal. bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); @@ -666,6 +666,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @return Whether the transaction is invalid (has value when CGT mode is enabled). function _isInvalidCGTWithdrawal(uint256 _value) internal view returns (bool) { // Cannot process withdrawal with value when custom gas token mode is enabled. + // NOTE: Chains are not supposed to enable Custom Gas Token (CGT) mode after initial deployment. + // Enabling CGT post-deployment is strongly discouraged and may lead to unexpected behavior. return _isUsingCustomGasToken() && _value > 0; } diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index 465232c9ab7..c975292a97d 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -154,9 +154,9 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl error SystemConfig_InvalidFeatureState(); /// @notice Semantic version. - /// @custom:semver 3.8.1 + /// @custom:semver 3.9.0 function version() public pure virtual returns (string memory) { - return "3.8.1"; + return "3.9.0"; } /// @notice Constructs the SystemConfig contract. @@ -518,9 +518,9 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl return superchainConfig.guardian(); } + /// @custom:legacy /// @notice Returns whether the custom gas token feature is enabled. /// @return bool True if the custom gas token feature is enabled, false otherwise. - /// @custom:legacy function isCustomGasToken() public view returns (bool) { return isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]; } diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index fcf71c57444..436f06f96ef 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -9,7 +9,6 @@ import { CommonTest } from "test/setup/CommonTest.sol"; import { NextImpl } from "test/mocks/NextImpl.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { DisputeGameFactory_TestInit } from "test/dispute/DisputeGameFactory.t.sol"; -import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; // Scripts import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.sol"; From df13abe041efd39855dba312189fb29c33947557 Mon Sep 17 00:00:00 2001 From: agusduha Date: Thu, 18 Sep 2025 23:20:55 -0300 Subject: [PATCH 10/15] fix: semver --- packages/contracts-bedrock/snapshots/semver-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index d72fe7d7c6a..bad1f662bf5 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -44,8 +44,8 @@ "sourceCodeHash": "0xbf344c4369b8cb00ec7a3108f72795747f3bc59ab5b37ac18cf21e72e2979dbf" }, "src/L1/SystemConfig.sol:SystemConfig": { - "initCodeHash": "0xfdeaf611cb031e9d53492cea8bcc62bc64686aea9e5c0ba258366de955b31668", - "sourceCodeHash": "0x7750ade92ba2910a39a623c808803b5dafd8a57baa1168c66d528a0d16d7498f" + "initCodeHash": "0x9632e6be2a3406ba4de82428a5e497027049ba13e1feea547516addaae2352fc", + "sourceCodeHash": "0xae64fefaf4f29f3c222d335efbdaee9379c644c4b9e22b1c7ee6914cac1efb5e" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x9b664e3d84ad510091337b4aacaa494b142512e2f6f7fbcdb6210ed62ca9b885", @@ -247,4 +247,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file From f8ff4e23255206dc2997f4b21fdf12623a2b5f44 Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:23:19 -0300 Subject: [PATCH 11/15] fix: cgt tests coverage (#582) * fix: l2genesis expectRevert amount test * test: improve l1block coverage * test: improve l1blockcgt coverage * test: add isCustomGasToken tests on SystemConfig * test: improve L2ToL1MessagePasserCGT coverage * test: improve L2ToL1MessagePasser coverage * fix: naming pre-pr --- .../test/L1/SystemConfig.t.sol | 17 ++++++++++++++ .../contracts-bedrock/test/L2/L1Block.t.sol | 23 +++++++++++++++++++ .../test/L2/L1BlockCGT.t.sol | 20 ++++++++++++++++ .../test/L2/L2ToL1MessagePasser.t.sol | 10 ++++++++ .../test/L2/L2ToL1MessagePasserCGT.t.sol | 11 +++++++++ .../test/scripts/L2Genesis.t.sol | 2 +- 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index 91a4e2eed16..f7428c3bf56 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -11,6 +11,7 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so import { Constants } from "src/libraries/Constants.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { Features } from "src/libraries/Features.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; // Interfaces import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; @@ -843,3 +844,19 @@ contract SystemConfig_SetMinBaseFee_Test is SystemConfig_TestInit { assertEq(systemConfig.minBaseFee(), newMinBaseFee); } } + +/// @title SystemConfig_IsCustomGasToken_Test +/// @notice Test contract for SystemConfig `isCustomGasToken` function. +contract SystemConfig_IsCustomGasToken_Test is SystemConfig_TestInit { + /// @notice Tests that `isCustomGasToken` returns the correct value. + function test_isCustomGasToken_enabled_succeeds() external { + skipIfDevFeatureDisabled(DevFeatures.CUSTOM_GAS_TOKEN); + assertTrue(systemConfig.isCustomGasToken()); + } + + /// @notice Tests that `isCustomGasToken` returns the correct value. + function test_isCustomGasToken_disabled_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); + assertFalse(systemConfig.isCustomGasToken()); + } +} diff --git a/packages/contracts-bedrock/test/L2/L1Block.t.sol b/packages/contracts-bedrock/test/L2/L1Block.t.sol index 166f0982baa..80c347a9f77 100644 --- a/packages/contracts-bedrock/test/L2/L1Block.t.sol +++ b/packages/contracts-bedrock/test/L2/L1Block.t.sol @@ -6,6 +6,7 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Encoding } from "src/libraries/Encoding.sol"; +import { Constants } from "src/libraries/Constants.sol"; import "src/libraries/L1BlockErrors.sol"; /// @title L1Block_ TestInit @@ -20,6 +21,28 @@ contract L1Block_TestInit is CommonTest { } } +/// @title L1Block_Version_Test +/// @notice Test contract for L1Block `version` function. +contract L1Block_Version_Test is L1Block_TestInit { + /// @notice Tests that the version function returns a valid string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() external view { + assert(bytes(l1Block.version()).length > 0); + } +} + +/// @title L1Block_GasPayingToken_Test +/// @notice Tests the `gasPayingToken` function of the `L1Block` contract. +contract L1Block_GasPayingToken_Test is L1Block_TestInit { + /// @notice Tests that the `gasPayingToken` function returns the correct token address and + /// decimals. + function test_gasPayingToken_succeeds() external view { + (address token, uint8 decimals) = l1Block.gasPayingToken(); + assertEq(token, Constants.ETHER); + assertEq(uint256(decimals), uint256(18)); + } +} + /// @title L1Block_GasPayingTokenName_Test /// @notice Tests the `gasPayingTokenName` function of the `L1Block` contract. contract L1Block_GasPayingTokenName_Test is L1Block_TestInit { diff --git a/packages/contracts-bedrock/test/L2/L1BlockCGT.t.sol b/packages/contracts-bedrock/test/L2/L1BlockCGT.t.sol index 7cb11a04c54..cd9e3e61853 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockCGT.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockCGT.t.sol @@ -31,6 +31,26 @@ contract L1BlockCGT_TestInit is CommonTest { } } +/// @title L1BlockCGT_Version_Test +/// @notice Test contract for L1BlockCGT `version` function. +contract L1BlockCGT_Version_Test is L1BlockCGT_TestInit { + /// @notice Tests that the version function returns a valid string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() external view { + assert(bytes(l1BlockCGT.version()).length > 0); + } +} + +/// @title L1BlockCGT_GasPayingToken_Test +/// @notice Tests the `gasPayingToken` function of the `L1BlockCGT` contract. +contract L1BlockCGT_GasPayingToken_Test is L1BlockCGT_TestInit { + /// @notice Tests that the `gasPayingToken` function reverts. + function test_gasPayingToken_deprecated_reverts() external { + vm.expectRevert("L1BlockCGT: deprecated"); + l1BlockCGT.gasPayingToken(); + } +} + /// @title L1BlockCGT_GasPayingTokenName_Test /// @notice Tests the `gasPayingTokenName` function of the `L1Block` contract with custom gas /// token enabled. diff --git a/packages/contracts-bedrock/test/L2/L2ToL1MessagePasser.t.sol b/packages/contracts-bedrock/test/L2/L2ToL1MessagePasser.t.sol index 8ce02b6aad7..b96c0316619 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL1MessagePasser.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL1MessagePasser.t.sol @@ -8,6 +8,16 @@ import { CommonTest } from "test/setup/CommonTest.sol"; import { Types } from "src/libraries/Types.sol"; import { Hashing } from "src/libraries/Hashing.sol"; +/// @title L2ToL1MessagePasser_Version_Test +/// @notice Tests the `version` function of the `L2ToL1MessagePasser` contract. +contract L2ToL1MessagePasser_Version_Test is CommonTest { + /// @notice Tests that the `version` function returns the correct string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() external view { + assert(bytes(l2ToL1MessagePasser.version()).length > 0); + } +} + /// @title L2ToL1MessagePasser_InitiateWithdrawal_Test /// @notice Tests the `initiateWithdrawal` function of the `L2ToL1MessagePasser` contract. contract L2ToL1MessagePasser_InitiateWithdrawal_Test is CommonTest { diff --git a/packages/contracts-bedrock/test/L2/L2ToL1MessagePasserCGT.t.sol b/packages/contracts-bedrock/test/L2/L2ToL1MessagePasserCGT.t.sol index 17ff93b7779..4b52ce85760 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL1MessagePasserCGT.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL1MessagePasserCGT.t.sol @@ -21,6 +21,17 @@ contract L2ToL1MessagePasserCGT_TestInit is CommonTest { } } +/// @title L2ToL1MessagePasserCGT_Version_Test +/// @notice Tests the `version` function of the `L2ToL1MessagePasser` contract with custom gas token +/// enabled. +contract L2ToL1MessagePasserCGT_Version_Test is L2ToL1MessagePasserCGT_TestInit { + /// @notice Tests that the `version` function returns the correct string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() external view { + assert(bytes(l2ToL1MessagePasser.version()).length > 0); + } +} + /// @title L2ToL1MessagePasserCGT_InitiateWithdrawal_Test /// @notice Tests the `initiateWithdrawal` function of the `L2ToL1MessagePasser` contract with /// custom gas token enabled. diff --git a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index 8fa2e2cae2c..dbdadf3f820 100644 --- a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol @@ -249,7 +249,7 @@ contract L2Genesis_Run_Test is L2Genesis_TestInit { // Expect revert when nativeAssetLiquidityAmount is greater than type(uint248).max input.nativeAssetLiquidityAmount += 1; - vm.expectRevert("Native asset liquidity amount must be less than or equal to type(uint248).max"); + vm.expectRevert("L2Genesis: native asset liquidity amount must be less than or equal to type(uint248).max"); genesis.run(input); // Reset nativeAssetLiquidityAmount input to type(uint248).max input.nativeAssetLiquidityAmount = type(uint248).max; From 3348c021977031700e7e48ef9e771fb6ecae8000 Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:02:49 -0300 Subject: [PATCH 12/15] fix(cgt): change L2ToL1MessagePasser & OPContractsManager semver (#584) * fix: change L2ToL1MessagePasser semver * fix: change L2ToL1MessagePasser & OPContractsManager semver --- packages/contracts-bedrock/snapshots/semver-lock.json | 10 +++++----- .../contracts-bedrock/src/L1/OPContractsManager.sol | 4 ++-- .../contracts-bedrock/src/L2/L2ToL1MessagePasser.sol | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index bad1f662bf5..6a95772f145 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": "0xd724cd39e215134f5101e64b46c6b72c692c07777a1db330cc32790b15fce05d", - "sourceCodeHash": "0x35bbbb65a855d7b4ba6338218e539d55b332c40ec7a85f01f222cdf92fa184eb" + "initCodeHash": "0xe94325cd292ec3131cae7525eb1487abfea94c16aa4f000d594c1fd5a5bbbadd", + "sourceCodeHash": "0xb42ef712bffcdd99f0ad33482fe74183a4205e4960a1aa7a24ddc9daa70faadc" }, "src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": { "initCodeHash": "0x17a40747da8a9978f7294f071ea371b8c021504b898919431e6acf62623e8adc", @@ -92,11 +92,11 @@ "sourceCodeHash": "0xde724da82ecf3c96b330c2876a7285b6e2b933ac599241eaa3174c443ebbe33a" }, "src/L2/L2ToL1MessagePasser.sol:L2ToL1MessagePasser": { - "initCodeHash": "0xff1caffe84cbae4b94285754885d00fa4e174efe7043258c56ba61825eedba7d", - "sourceCodeHash": "0xffa514767f63fae7566c65817bcc5fc6a72490af6bf7144d1aea53d3bf93a9e6" + "initCodeHash": "0xe30675ea6623cd7390dd2cd1e9a523c92c66956dfab86d06e318eb410cd1989b", + "sourceCodeHash": "0xdc7bd63134eeab163a635950f2afd16b59f40f9cf1306f2ed33ad661cc7b4962" }, "src/L2/L2ToL1MessagePasserCGT.sol:L2ToL1MessagePasserCGT": { - "initCodeHash": "0x5e1e6b0fa69edb4e07e01dd4ae915a319bbba318c079fa0a6c9c4878d9c98b7c", + "initCodeHash": "0xf36eab44c41249b4d8ea95a21b70f1eae55b1a555384870c2f4ca306fa9121b6", "sourceCodeHash": "0xec1736e67134e22ad9ceb0b8b6c116fd169637aa6729c05d9f0f4b02547aaac0" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 75b98f8e4e0..3b048fdb83c 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -1807,9 +1807,9 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 3.3.1 + /// @custom:semver 3.4.0 function version() public pure virtual returns (string memory) { - return "3.3.1"; + return "3.4.0"; } OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder; diff --git a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol index d675fe4f94d..682b5ac1b2c 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol @@ -51,9 +51,9 @@ contract L2ToL1MessagePasser is ISemver { /// @param amount Amount of ETH that was burned. event WithdrawerBalanceBurnt(uint256 indexed amount); - /// @custom:semver 1.1.3 + /// @custom:semver 1.2.0 function version() public pure virtual returns (string memory) { - return "1.1.3"; + return "1.2.0"; } /// @notice Allows users to withdraw ETH by sending directly to this contract. From f7ef9a4a5ab5e9d2a1e259170710bb43e8474e4c Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:49:52 -0300 Subject: [PATCH 13/15] fix: cgt feature tests (#585) * fix: cgt feature tests * fix: deposits with amount 0 when cgt enabled * Revert "fix: deposits with amount 0 when cgt enabled" This reverts commit 8bee464d45cf7d4040c4dd1459ffc78de2508024. --- .../contracts-bedrock/test/L1/L1StandardBridge.t.sol | 8 ++++++++ .../test/invariants/OptimismPortal2.t.sol | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol index 6acf740938e..1761a130415 100644 --- a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol @@ -15,6 +15,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { Features } from "src/libraries/Features.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; // Interfaces import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol"; @@ -332,6 +333,7 @@ contract L1StandardBridge_Paused_Test is CommonTest { contract L1StandardBridge_Receive_Test is CommonTest { /// @notice Tests receive bridges ETH successfully. function test_receive_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); uint256 portalBalanceBefore = address(optimismPortal2).balance; uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; @@ -386,6 +388,7 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit { /// Only EOA can call depositETH. /// ETH ends up in the optimismPortal. function test_depositETH_fromEOA_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); _preBridgeETH({ isLegacy: true, value: 500 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; @@ -401,6 +404,7 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit { /// @notice Tests that depositing ETH succeeds for an EOA using 7702 delegation. function test_depositETH_fromEOA7702_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); // Set alice to have 7702 code. vm.etch(alice, abi.encodePacked(hex"EF0100", address(0))); @@ -435,6 +439,7 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit { /// EOA or contract can call depositETHTo. /// ETH ends up in the optimismPortal. function test_depositETHTo_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); _preBridgeETHTo({ isLegacy: true, value: 600 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; @@ -452,6 +457,7 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit { /// @param _to Random recipient address /// @param _amount Random ETH amount to deposit function testFuzz_depositETHTo_randomRecipient_succeeds(address _to, uint256 _amount) external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); vm.assume(_to != address(0)); _amount = bound(_amount, 1, 10 ether); @@ -780,6 +786,7 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit { /// Only EOA can call bridgeETH. /// ETH ends up in the optimismPortal. function test_bridgeETH_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); _preBridgeETH({ isLegacy: false, value: 500 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; @@ -799,6 +806,7 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit { /// Only EOA can call bridgeETHTo. /// ETH ends up in the optimismPortal. function test_bridgeETHTo_succeeds() external { + skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN); _preBridgeETHTo({ isLegacy: false, value: 600 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; diff --git a/packages/contracts-bedrock/test/invariants/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/invariants/OptimismPortal2.t.sol index 63d05ad7a4f..f826e026291 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismPortal2.t.sol @@ -14,6 +14,7 @@ import { ResourceMetering } from "src/L1/ResourceMetering.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { Types } from "src/libraries/Types.sol"; +import { Features } from "src/libraries/Features.sol"; import "src/dispute/lib/Types.sol"; // Interfaces @@ -67,6 +68,11 @@ contract OptimismPortal2_Depositor is StdUtils, ResourceMetering { uint256 preDepositBalance = address(this).balance; uint256 value = bound(preDepositvalue, 0, preDepositBalance); + // If custom gas token is enabled, set deposit value to 0 + if (portal.systemConfig().isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)) { + value = 0; + } + (, uint64 cachedPrevBoughtGas,) = ResourceMetering(address(portal)).params(); ResourceMetering.ResourceConfig memory rcfg = resourceConfig(); uint256 maxResourceLimit = uint64(rcfg.maxResourceLimit); @@ -106,6 +112,12 @@ contract OptimismPortal2_Invariant_Harness is DisputeGameFactory_TestInit { gasLimit: 100_000, data: hex"" }); + + // If custom gas token is enabled, set deposit value to 0 + if (systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)) { + _defaultTx.value = 0; + } + // Get withdrawal proof data we can use for testing. (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) = ffi.getProveWithdrawalTransactionInputs(_defaultTx); From 397b34ad9a13bba5f749e6863ca913b4146d594a Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:50:44 -0300 Subject: [PATCH 14/15] fix: cgt portal (#587) --- .../snapshots/semver-lock.json | 4 ++-- .../src/L1/OptimismPortal2.sol | 20 ++++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 6a95772f145..2a02db7349f 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -28,8 +28,8 @@ "sourceCodeHash": "0xa80dcd2ebafc5a6a437f712d8073f8a48998807aa317ad7762b3fc9dc2caa133" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x17e83cec6174c046b8a1f0ac710b269c380f57e07eb33b62673b42ab2ebdeea5", - "sourceCodeHash": "0x41152ef88638d180591b843d75137cac5a28304b9e4a100e3f798a6b5a87ad2e" + "initCodeHash": "0x2c01bc6c0a55a1a27263224e05c1b28703ff85c61075bae7ab384b3043820ed2", + "sourceCodeHash": "0x697dd2b22925254fb716c9bdb666413c5653a62397007e8abe92834299319004" }, "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { "initCodeHash": "0x087281cd2a48e882648c09fa90bfcca7487d222e16300f9372deba6b2b8ccfad", diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 801fe0c0eff..173e33d9d7c 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -352,8 +352,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase } // Cannot prove withdrawal with value when custom gas token mode is enabled. - if (_isInvalidCGTWithdrawal(_tx.value)) { - revert OptimismPortal_NotAllowedOnCGTMode(); + if (_isUsingCustomGasToken()) { + if (_tx.value > 0) revert OptimismPortal_NotAllowedOnCGTMode(); } // Fetch the dispute game proxy from the `DisputeGameFactory` contract. @@ -448,8 +448,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase _assertNotPaused(); // Cannot finalize withdrawal with value when custom gas token mode is enabled. - if (_isInvalidCGTWithdrawal(_tx.value)) { - revert OptimismPortal_NotAllowedOnCGTMode(); + if (_isUsingCustomGasToken()) { + if (_tx.value > 0) revert OptimismPortal_NotAllowedOnCGTMode(); } // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other @@ -635,6 +635,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Checks if the Custom Gas Token feature is enabled. /// @return bool True if the Custom Gas Token feature is enabled. function _isUsingCustomGasToken() internal view returns (bool) { + // NOTE: Chains are not supposed to enable Custom Gas Token (CGT) mode after initial deployment. + // Enabling CGT post-deployment is strongly discouraged and may lead to unexpected behavior. return systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN); } @@ -661,16 +663,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase return _target == address(this) || _target == address(ethLockbox); } - /// @notice Checks if a withdrawal transaction is invalid due to CGT mode restrictions. - /// @param _value The value of the withdrawal transaction to validate. - /// @return Whether the transaction is invalid (has value when CGT mode is enabled). - function _isInvalidCGTWithdrawal(uint256 _value) internal view returns (bool) { - // Cannot process withdrawal with value when custom gas token mode is enabled. - // NOTE: Chains are not supposed to enable Custom Gas Token (CGT) mode after initial deployment. - // Enabling CGT post-deployment is strongly discouraged and may lead to unexpected behavior. - return _isUsingCustomGasToken() && _value > 0; - } - /// @notice Getter for the resource config. Used internally by the ResourceMetering contract. /// The SystemConfig is the source of truth for the resource config. /// @return config_ ResourceMetering ResourceConfig From 67e2452abf69f664b972cfd649149f9066c1890a Mon Sep 17 00:00:00 2001 From: niha <205694301+0xniha@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:43:22 -0300 Subject: [PATCH 15/15] fix: ci fixes (#588) --- packages/contracts-bedrock/snapshots/semver-lock.json | 4 ++-- packages/contracts-bedrock/src/L2/L2ToL1MessagePasserCGT.sol | 4 ++-- packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 2a02db7349f..f504bfeecc4 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -96,8 +96,8 @@ "sourceCodeHash": "0xdc7bd63134eeab163a635950f2afd16b59f40f9cf1306f2ed33ad661cc7b4962" }, "src/L2/L2ToL1MessagePasserCGT.sol:L2ToL1MessagePasserCGT": { - "initCodeHash": "0xf36eab44c41249b4d8ea95a21b70f1eae55b1a555384870c2f4ca306fa9121b6", - "sourceCodeHash": "0xec1736e67134e22ad9ceb0b8b6c116fd169637aa6729c05d9f0f4b02547aaac0" + "initCodeHash": "0xe09e6219f705bc120b604f25997610f3107daef880f33e380da7ed618b45bb6e", + "sourceCodeHash": "0x1ba0f62d5101f3541fe074a39fd67ab6a06b5b72678a4f08d08366fbe4bef764" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { "initCodeHash": "0x975fd33a3a386310d54dbb01b56f3a6a8350f55a3b6bd7781e5ccc2166ddf2e6", diff --git a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasserCGT.sol b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasserCGT.sol index efd00314559..e23cc30007a 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasserCGT.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasserCGT.sol @@ -18,9 +18,9 @@ contract L2ToL1MessagePasserCGT is L2ToL1MessagePasser { /// @notice The error thrown when a withdrawal is initiated with value and custom gas token is used. error L2ToL1MessagePasserCGT_NotAllowedOnCGTMode(); - /// @custom:semver +custom-gas-token + /// @custom:semver +custom-gas-token.1 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+custom-gas-token"); + return string.concat(super.version(), "+custom-gas-token.1"); } /// @notice Sends a message from L2 to L1. diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index 436f06f96ef..221a103760f 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -1406,7 +1406,7 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test /// @notice Tests that `proveWithdrawalTransaction` reverts when the custom gas token mode /// is enabled and the withdrawal transaction has a value. function test_proveWithdrawalTransaction_withValueAndCustomGasToken_reverts() external { - skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); + skipIfDevFeatureDisabled(DevFeatures.CUSTOM_GAS_TOKEN); skipIfForkTest( "OptimismPortal2_ProveWithdrawalTransaction_Test: isCustomGasToken() not available on forked networks" );