diff --git a/op-node/rollup/chain_spec.go b/op-node/rollup/chain_spec.go index b152c2ef2ff08..55a4b1d5326a2 100644 --- a/op-node/rollup/chain_spec.go +++ b/op-node/rollup/chain_spec.go @@ -33,17 +33,18 @@ const maxSequencerDriftFjord = 1800 type ForkName string const ( - Bedrock ForkName = "bedrock" - Regolith ForkName = "regolith" - Canyon ForkName = "canyon" - Delta ForkName = "delta" - Ecotone ForkName = "ecotone" - Fjord ForkName = "fjord" - Granite ForkName = "granite" - Holocene ForkName = "holocene" - Isthmus ForkName = "isthmus" - Jovian ForkName = "jovian" - Interop ForkName = "interop" + Bedrock ForkName = "bedrock" + Regolith ForkName = "regolith" + Canyon ForkName = "canyon" + Delta ForkName = "delta" + Ecotone ForkName = "ecotone" + Fjord ForkName = "fjord" + Granite ForkName = "granite" + Holocene ForkName = "holocene" + Isthmus ForkName = "isthmus" + Jovian ForkName = "jovian" + Interop ForkName = "interop" + CustomGasToken ForkName = "custom_gas_token" // ADD NEW FORKS TO AllForks BELOW! None ForkName = "" ) @@ -60,6 +61,7 @@ var AllForks = []ForkName{ Isthmus, Jovian, Interop, + CustomGasToken, // ADD NEW FORKS HERE! } diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 54fa6a4570309..40b55470d4a7b 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -158,6 +158,14 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex } } + if ba.rollupCfg.IsCustomGasToken(nextL2Time) { + customGasToken, err := CustomGasTokenNetworkUpgradeTransactions() + if err != nil { + return nil, NewCriticalError(fmt.Errorf("failed to build custom gas token network upgrade txs: %w", err)) + } + upgradeTxs = append(upgradeTxs, customGasToken...) + } + l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, sysConfig, seqNumber, l1Info, nextL2Time) if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) diff --git a/op-node/rollup/derive/custom_gas_token_upgrade_transactions.go b/op-node/rollup/derive/custom_gas_token_upgrade_transactions.go new file mode 100644 index 0000000000000..d57135771b616 --- /dev/null +++ b/op-node/rollup/derive/custom_gas_token_upgrade_transactions.go @@ -0,0 +1,14 @@ +package derive + +import ( + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// CustomGasTokenNetworkUpgradeTransactions returns the transactions required to upgrade to use a custom gas token. +// For now, this function returns an empty slice of transactions as requested. +func CustomGasTokenNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { + // TODO: Implement custom gas token upgrade transactions + // Deploy controller, liquidity, set implementations, mint ETH to liquidity + upgradeTxns := make([]hexutil.Bytes, 0) + return upgradeTxns, nil +} diff --git a/op-node/rollup/derive/custom_gas_token_upgrade_transactions_test.go b/op-node/rollup/derive/custom_gas_token_upgrade_transactions_test.go new file mode 100644 index 0000000000000..ace0648555807 --- /dev/null +++ b/op-node/rollup/derive/custom_gas_token_upgrade_transactions_test.go @@ -0,0 +1,14 @@ +package derive + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCustomGasTokenNetworkTransactions(t *testing.T) { + upgradeTxns, err := CustomGasTokenNetworkUpgradeTransactions() + require.NoError(t, err) + // For now, the function returns an empty slice as requested + require.Len(t, upgradeTxns, 0) +} diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 28f9e19564445..2ac005c4052d3 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -131,6 +131,11 @@ type Config struct { // Active if InteropTime != nil && L2 block timestamp >= *InteropTime, inactive otherwise. InteropTime *uint64 `json:"interop_time,omitempty"` + // CustomGasTokenTime sets the activation time of the Custom Gas Token network upgrade. + // Active if CustomGasTokenTime != nil && L2 block timestamp >= *CustomGasTokenTime, inactive otherwise. + // TODO: replace with custom gas token fork name + CustomGasTokenTime *uint64 `json:"custom_gas_token_time,omitempty"` + // Note: below addresses are part of the block-derivation process, // and required to be the same network-wide to stay in consensus. @@ -162,6 +167,14 @@ type Config struct { // This feature (de)activates by L1 origin timestamp, to keep a consistent L1 block info per L2 // epoch. PectraBlobScheduleTime *uint64 `json:"pectra_blob_schedule_time,omitempty"` + + // Custom Gas Token Configuration + // IsCustomGasToken indicates whether this chain uses a custom gas token instead of ETH + CustomGasToken bool `json:"is_custom_gas_token,omitempty"` + // GasTokenName is the name of the custom gas token + GasTokenName string `json:"gas_token_name,omitempty"` + // GasTokenSymbol is the symbol of the custom gas token + GasTokenSymbol string `json:"gas_token_symbol,omitempty"` } // ValidateL1Config checks L1 config variables for errors. @@ -470,6 +483,12 @@ func (c *Config) IsJovian(timestamp uint64) bool { return c.JovianTime != nil && timestamp >= *c.JovianTime } +// TODO: use correct name +// IsCustomGasToken returns true if the Custom Gas Token hardfork is active at or past the given timestamp. +func (c *Config) IsCustomGasToken(timestamp uint64) bool { + return c.CustomGasToken && c.CustomGasTokenTime != nil && timestamp >= *c.CustomGasTokenTime +} + // IsInterop returns true if the Interop hardfork is active at or past the given timestamp. func (c *Config) IsInterop(timestamp uint64) bool { return c.InteropTime != nil && timestamp >= *c.InteropTime @@ -547,6 +566,14 @@ func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool { !c.IsInterop(l2BlockTime-c.BlockTime) } +// IsCustomGasTokenActivationBlock returns whether the specified block is the first block subject to the +// Custom Gas Token upgrade. +func (c *Config) IsCustomGasTokenActivationBlock(l2BlockTime uint64) bool { + return c.IsCustomGasToken(l2BlockTime) && + l2BlockTime >= c.BlockTime && + !c.IsCustomGasToken(l2BlockTime-c.BlockTime) +} + // IsActivationBlock returns the fork which activates at the block with time newTime if the previous // block's time is oldTime. It return an empty ForkName if no fork activation takes place between // those timestamps. It can be used for both, L1 and L2 blocks. @@ -555,6 +582,9 @@ func (c *Config) IsActivationBlock(oldTime, newTime uint64) ForkName { if c.IsInterop(newTime) && !c.IsInterop(oldTime) { return Interop } + if c.IsCustomGasToken(newTime) && !c.IsCustomGasToken(oldTime) { + return CustomGasToken + } if c.IsIsthmus(newTime) && !c.IsIsthmus(oldTime) { return Isthmus } @@ -586,6 +616,9 @@ func (c *Config) IsActivationBlockForFork(l2BlockTime uint64, forkName ForkName) func (c *Config) ActivateAtGenesis(hardfork ForkName) { // IMPORTANT! ordered from newest to oldest switch hardfork { + case CustomGasToken: + c.CustomGasTokenTime = new(uint64) + fallthrough case Jovian: c.JovianTime = new(uint64) fallthrough @@ -739,6 +772,9 @@ func (c *Config) Description(l2Chains map[string]string) string { if c.AltDAConfig != nil { banner += fmt.Sprintf("Node supports Alt-DA Mode with CommitmentType %v\n", c.AltDAConfig.CommitmentType) } + if c.CustomGasToken { + banner += fmt.Sprintf("Node supports Custom Gas Token: %s (%s)\n", c.GasTokenName, c.GasTokenSymbol) + } return banner } @@ -776,6 +812,9 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) { if c.AltDAConfig != nil { ctx = append(ctx, "alt_da", *c.AltDAConfig) } + if c.CustomGasToken { + ctx = append(ctx, "custom_gas_token", true, "gas_token_name", c.GasTokenName, "gas_token_symbol", c.GasTokenSymbol) + } log.Info("Rollup Config", ctx...) } @@ -794,6 +833,7 @@ func (c *Config) forEachFork(callback func(name string, logName string, time *ui callback("Isthmus", "isthmus_time", c.IsthmusTime) callback("Jovian", "jovian_time", c.JovianTime) callback("Interop", "interop_time", c.InteropTime) + callback("Custom Gas Token", "custom_gas_token_time", c.CustomGasTokenTime) } func (c *Config) ParseRollupConfig(in io.Reader) error { diff --git a/op-node/rollup/types_test.go b/op-node/rollup/types_test.go index aad48c1757ebe..ea827652b3cb9 100644 --- a/op-node/rollup/types_test.go +++ b/op-node/rollup/types_test.go @@ -919,3 +919,33 @@ func TestConfig_ProbablyMissingPectraBlobSchedule(t *testing.T) { }) } } + +func TestConfig_CustomGasToken(t *testing.T) { + activationTime := uint64(1002) // First block after genesis + cfg := &Config{ + BlockTime: 2, + CustomGasToken: true, + CustomGasTokenTime: &activationTime, + GasTokenName: "Test Token", + GasTokenSymbol: "TEST", + } + + // Test IsCustomGasTokenActivationBlock + // Should return true for the first block after genesis when custom gas token is enabled + genesisTime := uint64(1000) + firstBlockTime := genesisTime + cfg.BlockTime + + // First block should be activation block + require.True(t, cfg.IsCustomGasTokenActivationBlock(firstBlockTime)) + + // Second block should not be activation block + secondBlockTime := firstBlockTime + cfg.BlockTime + require.False(t, cfg.IsCustomGasTokenActivationBlock(secondBlockTime)) + + // Test with custom gas token disabled + cfgDisabled := &Config{ + BlockTime: 2, + CustomGasToken: false, + } + require.False(t, cfgDisabled.IsCustomGasTokenActivationBlock(firstBlockTime)) +}