diff --git a/.circleci/config.yml b/.circleci/config.yml index 8484806f83a..4cdf35a09de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2038,7 +2038,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 2e0fc48208f..4161f9bec86 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -243,14 +243,20 @@ 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{ + intent.Chains[0].CustomGasToken = state.CustomGasToken{ Enabled: true, Name: "Custom Gas Token", 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, @@ -279,6 +285,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") }) } @@ -753,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/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/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/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/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index 7b4e6727ad7..4286d718082 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"` } @@ -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: "", 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/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 1ea3d989178..244cdc4c409 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, + "L2Genesis: 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/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/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol index 43acaba9b29..51609a69324 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployConfig.s.sol @@ -128,7 +128,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"); @@ -238,6 +238,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/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/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 ad5b3212979..f504bfeecc4 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": "0xe94325cd292ec3131cae7525eb1487abfea94c16aa4f000d594c1fd5a5bbbadd", + "sourceCodeHash": "0xb42ef712bffcdd99f0ad33482fe74183a4205e4960a1aa7a24ddc9daa70faadc" }, "src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": { "initCodeHash": "0x17a40747da8a9978f7294f071ea371b8c021504b898919431e6acf62623e8adc", "sourceCodeHash": "0xa80dcd2ebafc5a6a437f712d8073f8a48998807aa317ad7762b3fc9dc2caa133" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x1cc12939347474e26a57c89f6fe70bc5c82e456e7583c81da4b327a045c32f38", - "sourceCodeHash": "0x48ff518993541028ba2c299509dcb506a58808900496b4878aae4ec841fa8256" + "initCodeHash": "0x2c01bc6c0a55a1a27263224e05c1b28703ff85c61075bae7ab384b3043820ed2", + "sourceCodeHash": "0x697dd2b22925254fb716c9bdb666413c5653a62397007e8abe92834299319004" }, "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { "initCodeHash": "0x087281cd2a48e882648c09fa90bfcca7487d222e16300f9372deba6b2b8ccfad", @@ -44,8 +44,8 @@ "sourceCodeHash": "0xbf344c4369b8cb00ec7a3108f72795747f3bc59ab5b37ac18cf21e72e2979dbf" }, "src/L1/SystemConfig.sol:SystemConfig": { - "initCodeHash": "0x76edba72bf742e7729568f28547535cd69d88e533d96b80f7f32ad7c299b9efa", - "sourceCodeHash": "0x13b49aee27d9d7c90ef0bf2949c0c505bcb00c2377ac53a473d7759a44270419" + "initCodeHash": "0x9632e6be2a3406ba4de82428a5e497027049ba13e1feea547516addaae2352fc", + "sourceCodeHash": "0xae64fefaf4f29f3c222d335efbdaee9379c644c4b9e22b1c7ee6914cac1efb5e" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x9b664e3d84ad510091337b4aacaa494b142512e2f6f7fbcdb6210ed62ca9b885", @@ -92,12 +92,12 @@ "sourceCodeHash": "0xde724da82ecf3c96b330c2876a7285b6e2b933ac599241eaa3174c443ebbe33a" }, "src/L2/L2ToL1MessagePasser.sol:L2ToL1MessagePasser": { - "initCodeHash": "0xff1caffe84cbae4b94285754885d00fa4e174efe7043258c56ba61825eedba7d", - "sourceCodeHash": "0x4a03734b3c2ab1403506de5c833e538b2cfa81bf6e880b5ca80852abfa34c3d9" + "initCodeHash": "0xe30675ea6623cd7390dd2cd1e9a523c92c66956dfab86d06e318eb410cd1989b", + "sourceCodeHash": "0xdc7bd63134eeab163a635950f2afd16b59f40f9cf1306f2ed33ad661cc7b4962" }, "src/L2/L2ToL1MessagePasserCGT.sol:L2ToL1MessagePasserCGT": { - "initCodeHash": "0x5e1e6b0fa69edb4e07e01dd4ae915a319bbba318c079fa0a6c9c4878d9c98b7c", - "sourceCodeHash": "0xec1736e67134e22ad9ceb0b8b6c116fd169637aa6729c05d9f0f4b02547aaac0" + "initCodeHash": "0xe09e6219f705bc120b604f25997610f3107daef880f33e380da7ed618b45bb6e", + "sourceCodeHash": "0x1ba0f62d5101f3541fe074a39fd67ab6a06b5b72678a4f08d08366fbe4bef764" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { "initCodeHash": "0x975fd33a3a386310d54dbb01b56f3a6a8350f55a3b6bd7781e5ccc2166ddf2e6", diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 318cb5c1d92..3b048fdb83c 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. @@ -1808,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/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index f5c158285c3..173e33d9d7c 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. @@ -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(); @@ -349,14 +346,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 (_isUsingCustomGasToken()) { + if (_tx.value > 0) 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 +444,14 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase ) public { + // Cannot finalize withdrawal transactions while the system is paused. + _assertNotPaused(); + // 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 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. @@ -633,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); } diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index 45b9480b781..837b481998b 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -161,9 +161,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. @@ -540,6 +540,7 @@ 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. function isCustomGasToken() public view returns (bool) { diff --git a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol index 3ad1522b4d2..682b5ac1b2c 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol @@ -48,12 +48,12 @@ 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 + /// @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. 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/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/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/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 dadd779c8f5..221a103760f 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"; @@ -166,7 +165,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. @@ -240,9 +239,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 +270,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. @@ -590,7 +639,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 +678,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 +717,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); @@ -1353,6 +1402,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 { + skipIfDevFeatureDisabled(DevFeatures.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 @@ -1842,7 +1911,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" ); @@ -2450,7 +2519,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/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/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/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); 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/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index 2426eae290f..dbdadf3f820 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 { @@ -227,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("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; } } 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); 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.