Skip to content

feat: update base fee via cli #1183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ var (
utils.DARecoverySignBlocksFlag,
utils.DARecoveryL2EndBlockFlag,
utils.DARecoveryProduceBlocksFlag,
utils.L2BaseFeeScalarFlag,
utils.L2BaseFeeOverheadFlag,
}

rpcFlags = []cli.Flag{
Expand Down
25 changes: 25 additions & 0 deletions cmd/geth/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.RPCGlobalEVMTimeoutFlag,
utils.RPCGlobalTxFeeCapFlag,
utils.AllowUnprotectedTxs,
utils.MaxBlockRangeFlag,
utils.JSpathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
Expand Down Expand Up @@ -227,6 +228,30 @@ var AppHelpFlagGroups = []flags.FlagGroup{
Name: "METRICS AND STATS",
Flags: metricsFlags,
},
{
Name: "ROLLUP",
Flags: []cli.Flag{
utils.L1EndpointFlag,
utils.L1ConfirmationsFlag,
utils.L1DeploymentBlockFlag,
utils.L1DisableMessageQueueV2Flag,
utils.RollupVerifyEnabledFlag,
utils.L2BaseFeeScalarFlag,
utils.L2BaseFeeOverheadFlag,
utils.DASyncEnabledFlag,
utils.DABlobScanAPIEndpointFlag,
utils.DABlockNativeAPIEndpointFlag,
utils.DABeaconNodeAPIEndpointFlag,
utils.DARecoveryModeFlag,
utils.DARecoveryInitialL1BlockFlag,
utils.DARecoveryInitialBatchFlag,
utils.DARecoverySignBlocksFlag,
utils.DARecoveryL2EndBlockFlag,
utils.DARecoveryProduceBlocksFlag,
utils.CircuitCapacityCheckEnabledFlag,
utils.CircuitCapacityCheckWorkersFlag,
},
},
{
Name: "ALIASED (deprecated)",
Flags: []cli.Flag{
Expand Down
37 changes: 37 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/scroll-tech/go-ethereum/consensus"
"github.com/scroll-tech/go-ethereum/consensus/clique"
"github.com/scroll-tech/go-ethereum/consensus/ethash"
"github.com/scroll-tech/go-ethereum/consensus/misc"
"github.com/scroll-tech/go-ethereum/core"
"github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/vm"
Expand Down Expand Up @@ -934,6 +935,18 @@ var (
Name: "da.recovery.produceblocks",
Usage: "Produce unsigned blocks after L1 recovery for permissionless batch submission",
}

// L2 base fee settings
L2BaseFeeScalarFlag = BigFlag{
Name: "basefee.scalar",
Usage: "Scalar used in the l2 base fee formula. Signer nodes will use this for computing the next block's base fee. Follower nodes will use this in RPC.",
Value: misc.DefaultBaseFeeScalar,
}
L2BaseFeeOverheadFlag = BigFlag{
Name: "basefee.overhead",
Usage: "Overhead used in the l2 base fee formula. Signer nodes will use this for computing the next block's base fee. Follower nodes will use this in RPC.",
Value: misc.DefaultBaseFeeOverhead,
}
)

// MakeDataDir retrieves the currently requested data directory, terminating
Expand Down Expand Up @@ -1709,6 +1722,29 @@ func setDA(ctx *cli.Context, cfg *ethconfig.Config) {
}
}

func setBaseFee(ctx *cli.Context, cfg *ethconfig.Config) {
cfg.BaseFeeScalar = misc.DefaultBaseFeeScalar
if ctx.GlobalIsSet(L2BaseFeeScalarFlag.Name) {
cfg.BaseFeeScalar = GlobalBig(ctx, L2BaseFeeScalarFlag.Name)
}
cfg.BaseFeeOverhead = misc.DefaultBaseFeeOverhead
if ctx.GlobalIsSet(L2BaseFeeOverheadFlag.Name) {
cfg.BaseFeeOverhead = GlobalBig(ctx, L2BaseFeeOverheadFlag.Name)
}

log.Info("L2 base fee coefficients", "scalar", cfg.BaseFeeScalar, "overhead", cfg.BaseFeeOverhead)

var minBaseFee uint64
if fee := misc.MinBaseFee(cfg.BaseFeeScalar, cfg.BaseFeeOverhead); fee.IsUint64() {
minBaseFee = fee.Uint64()
}

if cfg.TxPool.PriceLimit < minBaseFee {
log.Warn("Updating txpool price limit to min L2 base fee", "provided", cfg.TxPool.PriceLimit, "updated", minBaseFee)
cfg.TxPool.PriceLimit = minBaseFee
}
}

func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) {
if ctx.GlobalIsSet(MaxBlockRangeFlag.Name) {
cfg.MaxBlockRange = ctx.GlobalInt64(MaxBlockRangeFlag.Name)
Expand Down Expand Up @@ -1785,6 +1821,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
setCircuitCapacityCheck(ctx, cfg)
setEnableRollupVerify(ctx, cfg)
setDA(ctx, cfg)
setBaseFee(ctx, cfg)
setMaxBlockRange(ctx, cfg)
if ctx.GlobalIsSet(ShadowforkPeersFlag.Name) {
cfg.ShadowForkPeerIDs = ctx.GlobalStringSlice(ShadowforkPeersFlag.Name)
Expand Down
41 changes: 31 additions & 10 deletions consensus/misc/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ import (
// We would only go above this if L1 base fee hits 2931 Gwei.
const MaximumL2BaseFee = 10000000000

// L2 base fee formula constants and defaults.
// l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead.
// `scalar` accounts for finalization costs. `overhead` accounts for sequencing and proving costs.
// we use 1e18 for precision to match the contract implementation.
var (
BaseFeePrecision = new(big.Int).SetUint64(1e18)
DefaultBaseFeeScalar = new(big.Int).SetUint64(34000000000000)
DefaultBaseFeeOverhead = new(big.Int).SetUint64(15680000)
)

// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
// - gas limit check
// - basefee check
Expand All @@ -54,18 +64,29 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseF
if config.Clique != nil && config.Clique.ShadowForkHeight != 0 && parent.Number.Uint64() >= config.Clique.ShadowForkHeight {
return big.NewInt(10000000) // 0.01 Gwei
}
l2SequencerFee := big.NewInt(1000000) // 0.001 Gwei
provingFee := big.NewInt(14680000) // 0.01468 Gwei

// L1_base_fee * 0.000034
verificationFee := parentL1BaseFee
verificationFee = new(big.Int).Mul(verificationFee, big.NewInt(34))
verificationFee = new(big.Int).Div(verificationFee, big.NewInt(1000000))
scalar := config.Scroll.BaseFeeScalar
if scalar == nil {
scalar = DefaultBaseFeeScalar
}
overhead := config.Scroll.BaseFeeOverhead
if overhead == nil {
overhead = DefaultBaseFeeOverhead
}

return calcBaseFee(scalar, overhead, parentL1BaseFee)
}

// MinBaseFee calculates the minimum L2 base fee based on the configured coefficients.
func MinBaseFee(scalar, overhead *big.Int) *big.Int {
return calcBaseFee(scalar, overhead, big.NewInt(0))
}

baseFee := big.NewInt(0)
baseFee.Add(baseFee, l2SequencerFee)
baseFee.Add(baseFee, provingFee)
baseFee.Add(baseFee, verificationFee)
func calcBaseFee(scalar, overhead, parentL1BaseFee *big.Int) *big.Int {
baseFee := new(big.Int).Set(parentL1BaseFee)
baseFee.Mul(baseFee, scalar)
baseFee.Div(baseFee, BaseFeePrecision)
baseFee.Add(baseFee, overhead)

if baseFee.Cmp(big.NewInt(MaximumL2BaseFee)) > 0 {
baseFee = big.NewInt(MaximumL2BaseFee)
Expand Down
34 changes: 33 additions & 1 deletion consensus/misc/eip1559_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,27 @@ func TestCalcBaseFee(t *testing.T) {
tests := []struct {
parentL1BaseFee int64
expectedL2BaseFee int64
}{
{0, 1},
{1000000000, 1},
{2000000000, 1},
{100000000000, 2},
{111111111111, 2},
{2164000000000, 22},
{644149677419355, 6442},
}
for i, test := range tests {
config := config()
config.Scroll.BaseFeeScalar = big.NewInt(10000000)
config.Scroll.BaseFeeOverhead = big.NewInt(1)
if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}

testsWithDefaults := []struct {
parentL1BaseFee int64
expectedL2BaseFee int64
}{
{0, 15680000},
{1000000000, 15714000},
Expand All @@ -120,9 +141,20 @@ func TestCalcBaseFee(t *testing.T) {
{2164000000000, 89256000},
{644149677419355, 10000000000}, // cap at max L2 base fee
}
for i, test := range tests {
for i, test := range testsWithDefaults {
if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
}

// TestMinBaseFee assumes all blocks are 1559-blocks
func TestMinBaseFee(t *testing.T) {
if have, want := MinBaseFee(DefaultBaseFeeScalar, DefaultBaseFeeOverhead), big.NewInt(15680000); have.Cmp(want) != 0 {
t.Errorf("have %d want %d, ", have, want)
}

if have, want := MinBaseFee(big.NewInt(10000000), big.NewInt(1)), big.NewInt(1); have.Cmp(want) != 0 {
t.Errorf("have %d want %d, ", have, want)
}
}
7 changes: 7 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}

// Hacky workaround:
// It's hard to pass these fields to `CalcBaseFee`, etc.
// So pass them as part of the genesis config instead.
chainConfig.Scroll.BaseFeeScalar = config.BaseFeeScalar
chainConfig.Scroll.BaseFeeOverhead = config.BaseFeeOverhead

log.Info("Initialised chain configuration", "config", chainConfig)

if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ type Config struct {

// DA syncer options
DA da_syncer.Config

// L2 base fee coefficients for the formula: `l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead`.
BaseFeeScalar *big.Int
BaseFeeOverhead *big.Int
}

// CreateConsensusEngine creates a consensus engine for the given chain configuration.
Expand Down
19 changes: 17 additions & 2 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,11 @@ type ScrollConfig struct {

// Genesis State Root for MPT clients
GenesisStateRoot *common.Hash `json:"genesisStateRoot,omitempty"`

// L2 base fee coefficients for the formula: `l2BaseFee = (l1BaseFee * scalar) / PRECISION + overhead`.
// These fields are populated from configuration flags, and passed to `CalcBaseFee`.
BaseFeeScalar *big.Int `json:"-"`
BaseFeeOverhead *big.Int `json:"-"`
}

// L1Config contains the l1 parameters needed to sync l1 contract events (e.g., l1 messages, commit/revert/finalize batches) in the sequencer
Expand Down Expand Up @@ -753,8 +758,18 @@ func (s ScrollConfig) String() string {
genesisStateRoot = fmt.Sprintf("%v", *s.GenesisStateRoot)
}

return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v}",
s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot)
baseFeeScalar := "<nil>"
if s.BaseFeeScalar != nil {
baseFeeScalar = s.BaseFeeScalar.String()
}

baseFeeOverhead := "<nil>"
if s.BaseFeeOverhead != nil {
baseFeeOverhead = s.BaseFeeOverhead.String()
}

return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v, baseFeeScalar: %v, baseFeeOverhead: %v}",
s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot, baseFeeScalar, baseFeeOverhead)
}

// IsValidTxCount returns whether the given block's transaction count is below the limit.
Expand Down
2 changes: 1 addition & 1 deletion params/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
const (
VersionMajor = 5 // Major version component of the current release
VersionMinor = 8 // Minor version component of the current release
VersionPatch = 44 // Patch version component of the current release
VersionPatch = 45 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)

Expand Down
Loading