diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index c8fe6d50792e7..151849df2dccb 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -275,13 +275,22 @@ func (d *GasPriceOracleDeployConfig) OperatorFeeParams() [32]byte { type GasTokenDeployConfig struct { // IsCustomGasToken is a flag to indicate that a custom gas token should be used IsCustomGasToken bool `json:"isCustomGasToken"` + // GasPayingTokenName represents the custom gas token name. + GasPayingTokenName string `json:"gasPayingTokenName"` + // GasPayingTokenSymbol represents the custom gas token symbol. + GasPayingTokenSymbol string `json:"gasPayingTokenSymbol"` } var _ ConfigChecker = (*GasTokenDeployConfig)(nil) func (d *GasTokenDeployConfig) Check(log log.Logger) error { if d.IsCustomGasToken { - log.Info("Using custom gas token") + if d.GasPayingTokenName == "" { + return fmt.Errorf("%w: GasPayingTokenName cannot be empty", ErrInvalidDeployConfig) + } + if d.GasPayingTokenSymbol == "" { + return fmt.Errorf("%w: GasPayingTokenSymbol cannot be empty", ErrInvalidDeployConfig) + } } return nil } diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index b5c1aab406872..6028da8c89d54 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -242,6 +242,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme AllowCustomDisputeParameters: true, OperatorFeeScalar: cfg.GasPriceOracleOperatorFeeScalar, OperatorFeeConstant: cfg.GasPriceOracleOperatorFeeConstant, + IsCustomGasToken: cfg.IsCustomGasToken, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) @@ -324,6 +325,9 @@ func GenesisL2(l2Host *script.Host, cfg *L2Config, deployment *L2Deployment, mul DeployCrossL2Inbox: multichainDepSet, EnableGovernance: cfg.EnableGovernance, FundDevAccounts: cfg.FundDevAccounts, + IsCustomGasToken: cfg.IsCustomGasToken, + GasPayingTokenName: cfg.GasPayingTokenName, + GasPayingTokenSymbol: cfg.GasPayingTokenSymbol, }); err != nil { return fmt.Errorf("failed L2 genesis: %w", err) } diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index 03e1927c95b5c..87bf2921181a1 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -243,7 +243,9 @@ func (r *InteropDevL2Recipe) build(l1ChainID uint64, addrs devkeys.Addresses) (* GasPriceOracleBlobBaseFeeScalar: 810949, }, GasTokenDeployConfig: genesis.GasTokenDeployConfig{ - IsCustomGasToken: false, + IsCustomGasToken: false, + GasPayingTokenName: "Custom Gas Token", + GasPayingTokenSymbol: "CGT", }, OperatorDeployConfig: genesis.OperatorDeployConfig{ P2PSequencerAddress: sequencerP2P, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 39d29ed6d8865..1c8c1d6c8fc2c 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -713,6 +713,11 @@ 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{ + Enabled: standard.CustomGasTokenEnabled, + Name: standard.CustomGasTokenName, + Symbol: standard.CustomGasTokenSymbol, + }, } } diff --git a/op-deployer/pkg/deployer/opcm/l2genesis.go b/op-deployer/pkg/deployer/opcm/l2genesis.go index 3f91196cc85fc..ff52f208b504d 100644 --- a/op-deployer/pkg/deployer/opcm/l2genesis.go +++ b/op-deployer/pkg/deployer/opcm/l2genesis.go @@ -28,6 +28,9 @@ type L2GenesisInput struct { DeployCrossL2Inbox bool EnableGovernance bool FundDevAccounts bool + IsCustomGasToken bool + GasPayingTokenName string + GasPayingTokenSymbol string } type L2GenesisScript script.DeployScriptWithoutOutput[L2GenesisInput] diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 9d87fb4979a4e..af40a7ea71623 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -40,6 +40,7 @@ type DeployOPChainInput struct { DisputeClockExtension uint64 DisputeMaxClockDuration uint64 AllowCustomDisputeParameters bool + IsCustomGasToken bool OperatorFeeScalar uint32 OperatorFeeConstant uint64 diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis.go b/op-deployer/pkg/deployer/pipeline/l2genesis.go index bb3f514358bce..566b6532451f5 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis.go @@ -22,6 +22,9 @@ import ( ) type l2GenesisOverrides struct { + IsCustomGasToken bool `json:"isCustomGasToken"` + GasPayingTokenName string `json:"gasPayingTokenName"` + GasPayingTokenSymbol string `json:"gasPayingTokenSymbol"` FundDevAccounts bool `json:"fundDevAccounts"` BaseFeeVaultMinimumWithdrawalAmount *hexutil.Big `json:"baseFeeVaultMinimumWithdrawalAmount"` L1FeeVaultMinimumWithdrawalAmount *hexutil.Big `json:"l1FeeVaultMinimumWithdrawalAmount"` @@ -94,6 +97,9 @@ func GenerateL2Genesis(pEnv *Env, intent *state.Intent, bundle ArtifactsBundle, DeployCrossL2Inbox: len(intent.Chains) > 1, EnableGovernance: overrides.EnableGovernance, FundDevAccounts: overrides.FundDevAccounts, + IsCustomGasToken: thisIntent.CustomGasToken.Enabled, + GasPayingTokenName: thisIntent.CustomGasToken.Name, + GasPayingTokenSymbol: thisIntent.CustomGasToken.Symbol, }); err != nil { return fmt.Errorf("failed to call L2Genesis script: %w", err) } @@ -156,6 +162,9 @@ func wdNetworkToBig(wd genesis.WithdrawalNetwork) *big.Int { func defaultOverrides() l2GenesisOverrides { return l2GenesisOverrides{ + IsCustomGasToken: false, + GasPayingTokenName: "Custom Gas Token", + GasPayingTokenSymbol: "CGT", FundDevAccounts: false, BaseFeeVaultMinimumWithdrawalAmount: standard.VaultMinWithdrawalAmount, L1FeeVaultMinimumWithdrawalAmount: standard.VaultMinWithdrawalAmount, diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis_test.go b/op-deployer/pkg/deployer/pipeline/l2genesis_test.go index aed2a1e782390..19493e51cba5b 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis_test.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis_test.go @@ -53,6 +53,9 @@ func TestCalculateL2GenesisOverrides(t *testing.T) { SequencerFeeVaultWithdrawalNetwork: "local", EnableGovernance: false, GovernanceTokenOwner: standard.GovernanceTokenOwner, + IsCustomGasToken: false, + GasPayingTokenName: "Custom Gas Token", + GasPayingTokenSymbol: "CGT", }, expectedSchedule: func() *genesis.UpgradeScheduleDeployConfig { return standard.DefaultHardforkScheduleForTag("") @@ -73,6 +76,9 @@ func TestCalculateL2GenesisOverrides(t *testing.T) { "enableGovernance": true, "governanceTokenOwner": "0x1111111111111111111111111111111111111111", "l2GenesisInteropTimeOffset": "0x1234", + "isCustomGasToken": false, + "gasPayingTokenName": "Custom Gas Token", + "gasPayingTokenSymbol": "CGT", }, }, chainIntent: &state.ChainIntent{}, @@ -87,6 +93,9 @@ func TestCalculateL2GenesisOverrides(t *testing.T) { SequencerFeeVaultWithdrawalNetwork: "remote", EnableGovernance: true, GovernanceTokenOwner: common.HexToAddress("0x1111111111111111111111111111111111111111"), + IsCustomGasToken: false, + GasPayingTokenName: "Custom Gas Token", + GasPayingTokenSymbol: "CGT", }, expectedSchedule: func() *genesis.UpgradeScheduleDeployConfig { sched := standard.DefaultHardforkScheduleForTag("") @@ -114,6 +123,9 @@ func TestCalculateL2GenesisOverrides(t *testing.T) { "enableGovernance": true, "governanceTokenOwner": "0x1111111111111111111111111111111111111111", "l2GenesisInteropTimeOffset": "0x1234", + "isCustomGasToken": false, + "gasPayingTokenName": "Custom Gas Token", + "gasPayingTokenSymbol": "CGT", }, }, expectError: false, @@ -127,6 +139,9 @@ func TestCalculateL2GenesisOverrides(t *testing.T) { SequencerFeeVaultWithdrawalNetwork: "remote", EnableGovernance: true, GovernanceTokenOwner: common.HexToAddress("0x1111111111111111111111111111111111111111"), + IsCustomGasToken: false, + GasPayingTokenName: "Custom Gas Token", + GasPayingTokenSymbol: "CGT", }, expectedSchedule: func() *genesis.UpgradeScheduleDeployConfig { sched := standard.DefaultHardforkScheduleForTag("") diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index f44721dfd5711..e9657a7a3d7d6 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -109,6 +109,7 @@ 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, + IsCustomGasToken: thisIntent.CustomGasToken.Enabled, OperatorFeeScalar: thisIntent.OperatorFeeScalar, OperatorFeeConstant: thisIntent.OperatorFeeConstant, }, nil diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 4b3379979593b..7306a7cdd9183 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -34,6 +34,9 @@ const ( Eip1559DenominatorCanyon uint64 = 250 Eip1559Denominator uint64 = 50 Eip1559Elasticity uint64 = 6 + CustomGasTokenEnabled bool = false + CustomGasTokenName string = "" + CustomGasTokenSymbol string = "" ContractsV160Tag = "op-contracts/v1.6.0" ContractsV180Tag = "op-contracts/v1.8.0-rc.4" diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index 3ad1a3dea3b00..c69a11a532718 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -56,6 +56,12 @@ type L2DevGenesisParams struct { Prefund map[common.Address]*hexutil.U256 `json:"prefund" toml:"prefund"` } +type CustomGasToken struct { + Enabled bool `json:"enabled" toml:"enabled"` + Name string `json:"name" toml:"name"` + Symbol string `json:"symbol" toml:"symbol"` +} + type ChainIntent struct { ID common.Hash `json:"id" toml:"id"` BaseFeeVaultRecipient common.Address `json:"baseFeeVaultRecipient" toml:"baseFeeVaultRecipient"` @@ -70,8 +76,8 @@ type ChainIntent struct { AdditionalDisputeGames []AdditionalDisputeGame `json:"dangerousAdditionalDisputeGames" toml:"dangerousAdditionalDisputeGames,omitempty"` OperatorFeeScalar uint32 `json:"operatorFeeScalar,omitempty" toml:"operatorFeeScalar,omitempty"` OperatorFeeConstant uint64 `json:"operatorFeeConstant,omitempty" toml:"operatorFeeConstant,omitempty"` - L1StartBlockHash *common.Hash `json:"l1StartBlockHash,omitempty" toml:"l1StartBlockHash,omitempty"` - + L1StartBlockHash *common.Hash `json:"l1StartBlockHash,omitempty" toml:"l1StartBlockHash,omitempty"` + 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"` } @@ -111,6 +117,15 @@ func (c *ChainIntent) Check() error { return fmt.Errorf("%w: chainId=%s", ErrFeeVaultZeroAddress, c.ID) } + if c.CustomGasToken != nil && c.CustomGasToken.Enabled { + if c.CustomGasToken.Name == "" { + return fmt.Errorf("%w: CustomGasToken.Name cannot be empty when enabled, chainId=%s", ErrIncompatibleValue, c.ID) + } + if c.CustomGasToken.Symbol == "" { + return fmt.Errorf("%w: CustomGasToken.Symbol cannot be empty when enabled, chainId=%s", ErrIncompatibleValue, c.ID) + } + } + if c.DangerousAltDAConfig.UseAltDA { return c.DangerousAltDAConfig.Check(nil) } diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 05123b143e323..943d9df8c9e0e 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -71,6 +71,12 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, EIP1559Elasticity: chainIntent.Eip1559Elasticity, }, + GasTokenDeployConfig: genesis.GasTokenDeployConfig{ + IsCustomGasToken: chainIntent.CustomGasToken.Enabled, + GasPayingTokenName: chainIntent.CustomGasToken.Name, + GasPayingTokenSymbol: chainIntent.CustomGasToken.Symbol, + }, + // STOP! This struct sets the _default_ upgrade schedule for all chains. // Any upgrades you enable here will be enabled for all new deployments. // In-development hardforks should never be activated here. Instead, they diff --git a/op-deployer/pkg/deployer/state/deploy_config_test.go b/op-deployer/pkg/deployer/state/deploy_config_test.go index 53d33d5874f2b..d8b7247df252c 100644 --- a/op-deployer/pkg/deployer/state/deploy_config_test.go +++ b/op-deployer/pkg/deployer/state/deploy_config_test.go @@ -31,6 +31,11 @@ func TestCombineDeployConfig(t *testing.T) { UnsafeBlockSigner: common.HexToAddress("0xabc"), Batcher: common.HexToAddress("0xdef"), }, + CustomGasToken: &CustomGasToken{ + Enabled: false, + Name: "Test", + Symbol: "TEST", + }, } state := State{ SuperchainDeployment: &addresses.SuperchainContracts{ProtocolVersionsProxy: common.HexToAddress("0x123")}, diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 410f0bd4369db..70fe84abd78c4 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -154,6 +154,9 @@ 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 && (chain.CustomGasToken.Enabled != standard.CustomGasTokenEnabled) { + return fmt.Errorf("%w: chainId=%s custom gas token not allowed in standard configuration", ErrNonStandardValue, chain.ID) + } } challenger, _ := standard.ChallengerAddressFor(c.L1ChainID) @@ -294,6 +297,11 @@ func NewIntentCustom(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) for _, l2ChainID := range l2ChainIds { intent.Chains = append(intent.Chains, &ChainIntent{ ID: l2ChainID, + CustomGasToken: &CustomGasToken{ + Enabled: standard.CustomGasTokenEnabled, + Name: standard.CustomGasTokenName, + Symbol: standard.CustomGasTokenSymbol, + }, }) } return intent, nil @@ -337,6 +345,11 @@ func NewIntentStandard(l1ChainId uint64, l2ChainIds []common.Hash) (Intent, erro L1ProxyAdminOwner: l1ProxyAdminOwner, L2ProxyAdminOwner: l2ProxyAdminOwner, }, + CustomGasToken: &CustomGasToken{ + Enabled: standard.CustomGasTokenEnabled, + Name: standard.CustomGasTokenName, + Symbol: standard.CustomGasTokenSymbol, + }, }) } return intent, nil diff --git a/op-deployer/pkg/deployer/state/intent_test.go b/op-deployer/pkg/deployer/state/intent_test.go index 8ffb0060db3c5..ca0ec71735fa2 100644 --- a/op-deployer/pkg/deployer/state/intent_test.go +++ b/op-deployer/pkg/deployer/state/intent_test.go @@ -87,6 +87,17 @@ func TestValidateStandardValues(t *testing.T) { }, ErrIncompatibleValue, }, + { + "CustomGasToken", + func(intent *Intent) { + intent.Chains[0].CustomGasToken = &CustomGasToken{ + Enabled: true, + Name: "Custom Gas Token", + Symbol: "CGT", + } + }, + ErrNonStandardValue, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -131,6 +142,10 @@ func TestValidateCustomValues(t *testing.T) { err = intent.Check() require.NoError(t, err) + setCustomGasToken(&intent) + err = intent.Check() + require.NoError(t, err) + tests := []struct { name string mutator func(intent *Intent) @@ -155,6 +170,28 @@ func TestValidateCustomValues(t *testing.T) { }, ErrIncompatibleValue, }, + { + "empty custom gas token name when enabled", + func(intent *Intent) { + intent.Chains[0].CustomGasToken = &CustomGasToken{ + Enabled: true, + Name: "", + Symbol: "CGT", + } + }, + ErrIncompatibleValue, + }, + { + "empty custom gas token symbol when enabled", + func(intent *Intent) { + intent.Chains[0].CustomGasToken = &CustomGasToken{ + Enabled: true, + Name: "Custom Gas Token", + Symbol: "", + } + }, + ErrIncompatibleValue, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -211,3 +248,11 @@ func setFeeAddresses(intent *Intent) { intent.Chains[0].L1FeeVaultRecipient = common.HexToAddress("0x09") intent.Chains[0].SequencerFeeVaultRecipient = common.HexToAddress("0x0A") } + +func setCustomGasToken(intent *Intent) { + intent.Chains[0].CustomGasToken = &CustomGasToken{ + Enabled: true, + Name: "Custom Gas Token", + Symbol: "CGT", + } +} diff --git a/op-devstack/sysgo/deployer.go b/op-devstack/sysgo/deployer.go index f01bb2240ed2d..aca99b2e81c1b 100644 --- a/op-devstack/sysgo/deployer.go +++ b/op-devstack/sysgo/deployer.go @@ -304,6 +304,14 @@ func WithDisputeGameFinalityDelaySeconds(seconds uint64) DeployerOption { } } +func WithCustomGasToken(enabled bool, name, symbol string) DeployerOption { + return func(p devtest.P, keys devkeys.Keys, builder intentbuilder.Builder) { + for _, l2Cfg := range builder.L2s() { + l2Cfg.WithCustomGasToken(enabled, name, symbol) + } + } +} + func (wb *worldBuilder) buildL1Genesis() { wb.require.NotNil(wb.output.L1DevGenesis, "must have L1 genesis outer config") wb.require.NotNil(wb.output.L1StateDump, "must have L1 genesis alloc") diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 04aa0fd620925..962a24f389a13 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -396,6 +396,11 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, Proposer: addrs.Proposer, Challenger: common.HexToAddress("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"), }, + CustomGasToken: &state.CustomGasToken{ + Enabled: false, + Name: "", + Symbol: "", + }, AdditionalDisputeGames: []state.AdditionalDisputeGame{ { ChainProofParams: state.ChainProofParams{ diff --git a/op-e2e/e2eutils/intentbuilder/builder.go b/op-e2e/e2eutils/intentbuilder/builder.go index 88482bbc11ad0..8b8598b9ed773 100644 --- a/op-e2e/e2eutils/intentbuilder/builder.go +++ b/op-e2e/e2eutils/intentbuilder/builder.go @@ -48,6 +48,7 @@ type L2Configurator interface { WithL1StartBlockHash(hash common.Hash) WithAdditionalDisputeGames(games []state.AdditionalDisputeGame) WithFinalizationPeriodSeconds(value uint64) + WithCustomGasToken(enabled bool, name, symbol string) ContractsConfigurator L2VaultsConfigurator L2RolesConfigurator @@ -386,6 +387,14 @@ func (c *l2Configurator) WithEIP1559Denominator(value uint64) { c.builder.intent.Chains[c.chainIndex].Eip1559Denominator = value } +func (c *l2Configurator) WithCustomGasToken(enabled bool, name, symbol string) { + c.builder.intent.Chains[c.chainIndex].CustomGasToken = &state.CustomGasToken{ + Enabled: enabled, + Name: name, + Symbol: symbol, + } +} + func (c *l2Configurator) WithEIP1559Elasticity(value uint64) { c.builder.intent.Chains[c.chainIndex].Eip1559Elasticity = value } diff --git a/op-e2e/e2eutils/intentbuilder/builder_test.go b/op-e2e/e2eutils/intentbuilder/builder_test.go index eeffb15a14d9e..39abaf38d6098 100644 --- a/op-e2e/e2eutils/intentbuilder/builder_test.go +++ b/op-e2e/e2eutils/intentbuilder/builder_test.go @@ -68,6 +68,7 @@ func TestBuilder(t *testing.T) { require.Equal(t, eth.ChainIDFromUInt64(420), l2Config.ChainID()) l2Config.WithBlockTime(2) l2Config.WithL1StartBlockHash(common.HexToHash("0x5678")) + l2Config.WithCustomGasToken(false, "Custom Gas Token", "CGT") // Test ContractsConfigurator methods l2Config.WithL1ContractsLocator("http://l1.example.com") @@ -159,6 +160,11 @@ func TestBuilder(t *testing.T) { Eip1559Elasticity: 10, OperatorFeeScalar: 100, OperatorFeeConstant: 200, + CustomGasToken: &state.CustomGasToken{ + Enabled: false, + Name: "Custom Gas Token", + Symbol: "CGT", + }, DeployOverrides: map[string]any{ "l2BlockTime": uint64(2), "l2GenesisRegolithTimeOffset": hexutil.Uint64(0), diff --git a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol index 8c693f0ca8871..c5f5fa5e1277b 100644 --- a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol +++ b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol @@ -91,6 +91,8 @@ library ChainAssertions { require(config.l1StandardBridge() == _contracts.L1StandardBridge, "CHECK-SCFG-180"); require(config.optimismPortal() == _contracts.OptimismPortal, "CHECK-SCFG-200"); require(config.optimismMintableERC20Factory() == _contracts.OptimismMintableERC20Factory, "CHECK-SCFG-210"); + // Check custom gas token + require(config.isCustomGasToken() == _doi.isCustomGasToken(), "CHECK-SCFG-220"); } else { require(config.owner() == address(0), "CHECK-SCFG-220"); require(config.overhead() == 0, "CHECK-SCFG-230"); diff --git a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol index faa3f466f4085..cc82f7518d6a5 100644 --- a/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol @@ -56,6 +56,7 @@ 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.isCustomGasToken.selector, false); doi.set(doi.opcm.selector, opcm); vm.etch(opcm, hex"01"); @@ -71,6 +72,7 @@ contract DeployOPChainInput_Test is Test { assertEq(l2ChainId, doi.l2ChainId(), "1000"); assertEq(opcm, address(doi.opcm()), "1100"); assertEq(true, doi.allowCustomDisputeParameters(), "1200"); + assertEq(false, doi.isCustomGasToken(), "1300"); } function test_getters_whenNotSet_reverts() public { @@ -328,6 +330,7 @@ contract DeployOPChain_TestBase is Test { IOPContractsManager opcm = IOPContractsManager(address(0)); string saltMixer = "defaultSaltMixer"; uint64 gasLimit = 60_000_000; + bool isCustomGasToken = false; // Configurable dispute game parameters. uint32 disputeGameType = GameType.unwrap(GameTypes.PERMISSIONED_CANNON); bytes32 disputeAbsolutePrestate = hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"; @@ -400,6 +403,7 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { basefeeScalar = uint32(uint256(hash(_seed, 6))); blobBaseFeeScalar = uint32(uint256(hash(_seed, 7))); l2ChainId = uint256(hash(_seed, 8)); + isCustomGasToken = bool(uint256(hash(_seed, 9)) % 2 == 0); doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); @@ -419,6 +423,7 @@ 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.isCustomGasToken.selector, isCustomGasToken); deployOPChain.run(doi, doo); @@ -442,6 +447,7 @@ 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(isCustomGasToken, doi.isCustomGasToken(), "1800"); // Assert inputs were properly passed through to the contract initializers. assertEq(address(doo.opChainProxyAdmin().owner()), opChainProxyAdminOwner, "2100"); @@ -486,6 +492,20 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { assertEq(doo.permissionedDisputeGame().splitDepth(), disputeSplitDepth + 1); } + function test_isCustomGasToken_whenTrue_succeeds() public { + setDOI(); + doi.set(doi.isCustomGasToken.selector, true); + deployOPChain.run(doi, doo); + assertEq(doi.isCustomGasToken(), true, "CGT-300"); + } + + function test_isCustomGasToken_whenFalse_succeeds() public { + setDOI(); + doi.set(doi.isCustomGasToken.selector, false); + deployOPChain.run(doi, doo); + assertEq(doi.isCustomGasToken(), false, "CGT-400"); + } + function setDOI() internal { doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner); @@ -505,5 +525,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.isCustomGasToken.selector, false); } }