Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
036f4c0
Update L2 base fee calculation to account for Feynman
ranchalp Jun 9, 2025
aa2d6f5
Merge branch 'develop' into l2-fee-feynman
ranchalp Jun 9, 2025
43a698e
Update consensus/misc/eip1559.go
ranchalp Jun 10, 2025
a954f2d
Use dedicated system parameter provingBaseFee
ranchalp Jun 10, 2025
9b715c8
Merge and change initialBaseFee
ranchalp Jun 10, 2025
e2987e3
Merge branch 'develop' into l2-fee-feynman
ranchalp Jun 11, 2025
7ec3f2b
Check on parent not to fail tests
ranchalp Jun 11, 2025
7d70159
fix test
ranchalp Jun 11, 2025
6c5e44d
Merge branch 'develop' into l2-fee-feynman
colinlyguo Jun 17, 2025
e8f5a91
fix golint
Jun 17, 2025
b2bace1
fix goimport
Jun 17, 2025
d74395b
fix unit tests
Jun 17, 2025
1ca62bf
check config.IsFeynman(parent.Time)
Jun 17, 2025
e8f7257
Update consensus/misc/eip1559.go
colinlyguo Jun 24, 2025
dbf1a2f
Merge branch 'develop' into l2-fee-feynman
colinlyguo Jun 24, 2025
b0fa39f
fix fee transition
Jun 24, 2025
dc0accd
Merge branch 'develop' into l2-fee-feynman
colinlyguo Jun 25, 2025
13b13f9
remove an incorrect comment
Jun 25, 2025
e2f18ee
reuse baseFeeOverhead as proving base fee
Jun 25, 2025
d318278
update da-codec commit
Jun 26, 2025
88bab35
Merge branch 'develop' into l2-fee-feynman
colinlyguo Jun 26, 2025
ab13d1b
Merge branch 'develop' into l2-fee-feynman
colinlyguo Jun 26, 2025
4370957
address comments
Jun 26, 2025
8f9f962
add a comment
Jun 26, 2025
1cc13a6
add base fee max check
Thegaram Jun 26, 2025
52ee2b3
add upstream 1559 tests
Thegaram Jun 26, 2025
e294b40
Merge branch 'develop' into l2-fee-feynman
Thegaram Jun 26, 2025
67f8949
bump version
Thegaram Jun 26, 2025
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
70 changes: 68 additions & 2 deletions consensus/misc/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,79 @@ func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Heade
}

// CalcBaseFee calculates the basefee of the header.
func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseFee *big.Int) *big.Int {
func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseFee *big.Int, currentHeaderTime uint64) *big.Int {
if config.Clique != nil && config.Clique.ShadowForkHeight != 0 && parent.Number.Uint64() >= config.Clique.ShadowForkHeight {
return big.NewInt(10000000) // 0.01 Gwei
}

scalar, overhead := ReadL2BaseFeeCoefficients()
return calcBaseFee(scalar, overhead, parentL1BaseFee)

if parent == nil || parent.Number == nil || !config.IsFeynman(currentHeaderTime) {
return calcBaseFee(scalar, overhead, parentL1BaseFee)
}
// In Feynman base fee calculation, we reuse the contract's baseFeeOverhead slot as the proving base fee.
return calcBaseFeeFeynman(config, parent, overhead)
}

// calcBaseFeeFeynman calculates the basefee of the header for Feynman fork.
func calcBaseFeeFeynman(config *params.ChainConfig, parent *types.Header, overhead *big.Int) *big.Int {
baseFeeEIP1559 := calcBaseFeeEIP1559(config, parent)
baseFee := new(big.Int).Set(baseFeeEIP1559)
baseFee.Add(baseFee, overhead)

return baseFee
}

// CalcBaseFee calculates the basefee of the header.
func calcBaseFeeEIP1559(config *params.ChainConfig, parent *types.Header) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsFeynman(parent.Time) {
return new(big.Int).SetUint64(params.InitialBaseFee)
}

parentBaseFeeEIP1559 := extractBaseFeeEIP1559(config, parent.BaseFee)
parentGasTarget := parent.GasLimit / config.ElasticityMultiplier()
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return new(big.Int).Set(parentBaseFeeEIP1559)
}

var (
num = new(big.Int)
denom = new(big.Int)
)

if parent.GasUsed > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
// max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parent.GasUsed - parentGasTarget)
num.Mul(num, parentBaseFeeEIP1559)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
if num.Cmp(common.Big1) < 0 {
return num.Add(parentBaseFeeEIP1559, common.Big1)
}
return num.Add(parentBaseFeeEIP1559, num)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
// max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parentGasTarget - parent.GasUsed)
num.Mul(num, parentBaseFeeEIP1559)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))

baseFee := num.Sub(parentBaseFeeEIP1559, num)
if baseFee.Cmp(common.Big0) < 0 {
baseFee = common.Big0
}
return baseFee
}
}

func extractBaseFeeEIP1559(config *params.ChainConfig, baseFee *big.Int) *big.Int {
_, overhead := ReadL2BaseFeeCoefficients()
// In Feynman base fee calculation, we reuse the contract's baseFeeOverhead slot as the proving base fee.
return new(big.Int).Sub(baseFee, overhead)
}

// MinBaseFee calculates the minimum L2 base fee based on the current coefficients.
Expand Down
4 changes: 2 additions & 2 deletions consensus/misc/eip1559_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func TestCalcBaseFee(t *testing.T) {
config := config()
UpdateL2BaseFeeScalar(big.NewInt(10000000))
UpdateL2BaseFeeOverhead(big.NewInt(1))
if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee), 0), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
Expand All @@ -144,7 +144,7 @@ func TestCalcBaseFee(t *testing.T) {
for i, test := range testsWithDefaults {
UpdateL2BaseFeeScalar(big.NewInt(34000000000000))
UpdateL2BaseFeeOverhead(big.NewInt(15680000))
if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee), 0), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
}
if chain.Config().IsCurie(header.Number) {
parentL1BaseFee := fees.GetL1BaseFee(state)
header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header(), parentL1BaseFee)
header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header(), parentL1BaseFee, header.Time)
}
return header
}
Expand Down
2 changes: 1 addition & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
if g.BaseFee != nil {
head.BaseFee = g.BaseFee
} else {
head.BaseFee = misc.CalcBaseFee(g.Config, nil, big.NewInt(0))
head.BaseFee = misc.CalcBaseFee(g.Config, nil, big.NewInt(0), head.Time)
}
}
statedb.Commit(false)
Expand Down
8 changes: 4 additions & 4 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestStateProcessorErrors(t *testing.T) {
txs: []*types.Transaction{
mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil),
},
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
want: "could not apply tx 0 [0xa230ea82ab24a8e60aca9edfe901bab53c92d0d8850f13c3f72513608229e2f3]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
},
// ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`.
// The EstimateGas API tests test this case.
Expand Down Expand Up @@ -391,13 +391,13 @@ func TestStateProcessorErrors(t *testing.T) {
}{
{ // ErrMaxInitCodeSizeExceeded
txs: []*types.Transaction{
mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]),
mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee, 0), tooBigInitCode[:]),
},
want: "could not apply tx 0 [0xe0d03426cecc04467410064cb4de02012fc069d2462282735d7dfcb9dea9f63b]: max initcode size exceeded: code size 49153 limit 49152",
},
{ // ErrIntrinsicGas: Not enough gas to cover init code
txs: []*types.Transaction{
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), smallInitCode[:]),
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee, 0), smallInitCode[:]),
},
want: "could not apply tx 0 [0x98e54c5ecfa7986a66480d65ba32f2c6a2a6aedc3a67abb91b1e118b0717ed2d]: intrinsic gas too low: have 54299, want 54300",
},
Expand Down Expand Up @@ -436,7 +436,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr

if config.IsCurie(header.Number) {
parentL1BaseFee := big.NewInt(1000000000) // 1 gwei
header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee)
header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee, header.Time)
}
var receipts []*types.Receipt
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
Expand Down
3 changes: 2 additions & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1458,7 +1458,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
pool.demoteUnexecutables()
if reset.newHead != nil && pool.chainconfig.IsCurie(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) {
l1BaseFee := fees.GetL1BaseFee(pool.currentState)
pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead, l1BaseFee)
// Use time.Now() as the current block time to calculate the pending base fee.
pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead, l1BaseFee, uint64(time.Now().Unix()))
pool.priced.SetBaseFee(pendingBaseFee)
}
// Update all accounts to the latest known pending nonce
Expand Down
4 changes: 2 additions & 2 deletions eth/catalyst/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
return nil, err
}
parentL1BaseFee := fees.GetL1BaseFee(stateDb)
header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee)
header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee, header.Time)
}
err = api.eth.Engine().Prepare(bc, header, nil)
if err != nil {
Expand Down Expand Up @@ -274,7 +274,7 @@ func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea
Time: params.Timestamp,
}
if config.IsCurie(number) {
header.BaseFee = misc.CalcBaseFee(config, parent, parentL1BaseFee)
header.BaseFee = misc.CalcBaseFee(config, parent, parentL1BaseFee, header.Time)
}
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
return block, nil
Expand Down
3 changes: 2 additions & 1 deletion eth/gasprice/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64, nonCong
return
}
l1BaseFee := fees.GetL1BaseFee(state)
bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header, l1BaseFee)
// Use bf.header.Time as the current block time to calculate the next base fee because there are cases to query historical block fees
bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header, l1BaseFee, bf.header.Time)
} else {
bf.results.nextBaseFee = new(big.Int)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,8 @@ func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, conf
blockNumber := uint64(0)
blockTime := uint64(0)
if current != nil {
baseFee = misc.CalcBaseFee(config, current, l1BaseFee)
// Use time.Now() as the current block time to calculate the pending base fee.
baseFee = misc.CalcBaseFee(config, current, l1BaseFee, uint64(time.Now().Unix()))
blockNumber = current.Number.Uint64()
blockTime = current.Time
}
Expand Down
2 changes: 1 addition & 1 deletion miner/scroll_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ func (w *worker) newWork(now time.Time, parent *types.Block, reorging bool, reor
// Set baseFee if we are on an EIP-1559 chain
if w.chainConfig.IsCurie(header.Number) {
parentL1BaseFee := fees.GetL1BaseFee(parentState)
header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header(), parentL1BaseFee)
header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header(), parentL1BaseFee, header.Time)
}
// Only set the coinbase if our consensus engine is running (avoid spurious block rewards)
if w.isRunning() {
Expand Down
10 changes: 10 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,16 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi
return lasterr
}

// BaseFeeChangeDenominator bounds the amount the base fee can change between blocks.
func (c *ChainConfig) BaseFeeChangeDenominator() uint64 {
return DefaultBaseFeeChangeDenominator
}

// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have.
func (c *ChainConfig) ElasticityMultiplier() uint64 {
return DefaultElasticityMultiplier
}

// CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough
// to guarantee that forks can be implemented in a different order than on official networks
func (c *ChainConfig) CheckConfigForkOrder() error {
Expand Down
6 changes: 3 additions & 3 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ const (
// Introduced in Tangerine Whistle (Eip 150)
CreateBySelfdestructGas uint64 = 25000

BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
DefaultBaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 10000000 // Initial base fee for EIP-1559 blocks.

MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
Expand Down
Loading