From c0d89415da01d365e72d8c4a6108ad8dc5136a34 Mon Sep 17 00:00:00 2001 From: Alejandro Ranchal-Pedrosa Date: Thu, 26 Jun 2025 12:08:40 +0200 Subject: [PATCH] feat(feynman): add new compression-aware rollup fee formula (#1196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change l1DataFee to rollupFee for Feynman * Update CommitScalarSlot to ExecScalarSlot * Rename l1DataFee to rollupFee * Scale compression ratio to match scalars precision * Use l1DataFee and commitScalar instead of rollupFee and execScalar * Use blocktime not blocknumber * Add unit test * Clarify test comment * feat(feynman): upgrade gas oracle predeploy * address comments * revert renaming CalculateL1DataFee to CalculateRollupFee * simplify EstimateL1DataFeeForMessage * address comments * add a comment based on pr reviews * update formula * tweaks * tweak comments * Estimate compression ratio at rollupFee for Feynman (#1197) * Estimate compression ratio * Update rollup/fees/rollup_fee.go Co-authored-by: Péter Garamvölgyi * Check compressed size smaller and remove unnecessary checks/changes * goimports locally run * rollback format changes * update da-codec's zstd function * apply zstd * tweaks * tweak unit test * make sure compression ratio >= 1 * nit --------- Co-authored-by: Péter Garamvölgyi Co-authored-by: colinlyguo Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> * refactoring * remove incorrect merge artifact * error handling * update da-codec commit * update da-codec commit --------- Co-authored-by: Péter Garamvölgyi Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: colinlyguo --- accounts/abi/bind/backends/simulated.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 2 +- core/state_prefetcher.go | 2 +- core/state_processor.go | 9 +- core/tx_list.go | 4 +- core/tx_list_test.go | 4 +- core/tx_pool.go | 12 +- eth/state_accessor.go | 2 +- eth/tracers/api.go | 14 +- eth/tracers/api_test.go | 2 +- .../internal/tracetest/calltrace_test.go | 6 +- eth/tracers/tracers_test.go | 2 +- go.mod | 2 +- go.sum | 4 +- internal/ethapi/api.go | 4 +- les/odr_test.go | 4 +- les/state_accessor.go | 2 +- light/odr_test.go | 2 +- light/txpool.go | 4 +- rollup/fees/rollup_fee.go | 152 +++++++++++++++--- rollup/fees/rollup_fee_test.go | 108 +++++++++++++ rollup/tracing/tracing.go | 4 +- tests/state_test_util.go | 2 +- 23 files changed, 282 insertions(+), 67 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 0721676991..a4f51d425b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -640,7 +640,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(b.blockchain.Config(), head.Number, head.Time) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number, head.Time) if err != nil { return nil, err } diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2f237110dd..ff4d50810b 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -184,7 +184,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, snapshot := statedb.Snapshot() evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number)) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) if err != nil { log.Info("rejected tx due to fees.CalculateL1DataFee", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 7cdbf1dde6..5f31c02185 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -71,7 +71,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c } statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, p.config, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, p.config, block.Number(), block.Time()) if err != nil { return } diff --git a/core/state_processor.go b/core/state_processor.go index d031bc848a..aea19a4e04 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -80,6 +80,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg header = block.Header() blockHash = block.Hash() blockNumber = block.Number() + blockTime = block.Time() allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) @@ -110,7 +111,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.SetTxContext(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockTime, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -125,7 +126,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockTime uint64, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { defer func(t0 time.Time) { applyTransactionTimer.Update(time.Since(t0)) }(time.Now()) @@ -134,7 +135,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, config, blockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, config, blockNumber, blockTime) if err != nil { return nil, err } @@ -206,7 +207,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, config, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Time, header.Hash(), tx, usedGas, vmenv) } // ProcessParentBlockHash stores the parent block hash in the history storage contract diff --git a/core/tx_list.go b/core/tx_list.go index 73d646a870..fba3b57f0b 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -282,7 +282,7 @@ func (l *txList) Overlaps(tx *types.Transaction) bool { // // If the new transaction is accepted into the list, the lists' cost and gas // thresholds are also potentially updated. -func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64, chainconfig *params.ChainConfig, blockNumber *big.Int) (bool, *types.Transaction) { +func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64, chainconfig *params.ChainConfig, blockNumber *big.Int, blockTime uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) if old != nil { @@ -310,7 +310,7 @@ func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint l1DataFee := big.NewInt(0) if state != nil && chainconfig != nil { var err error - l1DataFee, err = fees.CalculateL1DataFee(tx, state, chainconfig, blockNumber) + l1DataFee, err = fees.CalculateL1DataFee(tx, state, chainconfig, blockNumber, blockTime) if err != nil { log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) return false, nil diff --git a/core/tx_list_test.go b/core/tx_list_test.go index 9b1cab4c40..f0f0d9dcc0 100644 --- a/core/tx_list_test.go +++ b/core/tx_list_test.go @@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) { // Insert the transactions in a random order list := newTxList(true) for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil) + list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil, 0) } // Verify internal state if len(list.txs.items) != len(txs) { @@ -65,7 +65,7 @@ func BenchmarkTxListAdd(b *testing.B) { for i := 0; i < b.N; i++ { list := newTxList(true) for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil) + list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil, 0) list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump) } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 3d49800e01..52f4b040d4 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -301,6 +301,7 @@ type TxPool struct { currentState *state.StateDB // Current state in the blockchain head currentHead *big.Int // Current blockchain head + currentTime uint64 // Current blockchain head time pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps @@ -838,7 +839,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // 2. If FeeVault is enabled, perform an additional check for L1 data fees. if pool.chainconfig.Scroll.FeeVaultEnabled() { // Get L1 data fee in current state - l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead, pool.currentTime) if err != nil { return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) } @@ -947,7 +948,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e from, _ := types.Sender(pool.signer, tx) // already validated if list := pool.pending[from]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met - inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { log.Debug("Discarding underpriced pending transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) pendingDiscardMeter.Mark(1) @@ -1001,7 +1002,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo pool.queue[from] = newTxList(false) } - inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { // An older transaction was better, discard this log.Debug("Discarding underpriced queued transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) @@ -1068,7 +1069,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T return false } - inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { // An older transaction was better, discard this log.Debug("Discarding underpriced pending transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) @@ -1603,6 +1604,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // Update current head pool.currentHead = next + pool.currentTime = newHead.Time } // promoteExecutables moves transactions that have become processable from the @@ -1690,7 +1692,7 @@ func (pool *TxPool) executableTxFilter(costLimit *big.Int) func(tx *types.Transa if pool.chainconfig.Scroll.FeeVaultEnabled() { // recheck L1 data fee, as the oracle price may have changed - l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead, pool.currentTime) if err != nil { log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) return false diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 13b80ded03..f4a75fc594 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -198,7 +198,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.SetTxContext(tx.Hash(), idx) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, eth.blockchain.Config(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, eth.blockchain.Config(), block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, err } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7b66326c09..cdfa276cba 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -292,7 +292,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config TxHash: tx.Hash(), } - l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb, api.backend.ChainConfig(), task.block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb, api.backend.ChainConfig(), task.block.Number(), task.block.Time()) if err != nil { // though it's not a "tracing error", we still need to put it here task.results[i] = &txTraceResult{Error: err.Error()} @@ -557,7 +557,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config ) statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { log.Warn("Tracing intermediate roots did not complete due to fees.CalculateL1DataFee", "txindex", i, "txhash", tx.Hash(), "err", err) return nil, err @@ -645,7 +645,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxHash: txs[task.index].Hash(), } - l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { // though it's not a "tracing error", we still need to put it here results[task.index] = &txTraceResult{ @@ -679,7 +679,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac msg, _ := tx.AsMessage(signer, block.BaseFee()) statedb.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { failed = err break @@ -804,7 +804,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err == nil { _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee) } @@ -869,7 +869,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { return nil, err } @@ -929,7 +929,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } signer := types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number()) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number(), block.Time()) if err != nil { return nil, err } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index d49e7df80a..47b8b4c6ac 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -173,7 +173,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block return msg, context, statedb, nil } vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, b.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, b.chainConfig, block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, err } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index c3d8bfff39..5ee8905fa4 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -193,7 +193,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber, context.Time.Uint64()) if err != nil { t.Fatalf("failed to calculate l1DataFee: %v", err) } @@ -308,7 +308,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { } evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) snap := statedb.Snapshot() - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber, context.Time.Uint64()) if err != nil { b.Fatalf("failed to calculate l1DataFee: %v", err) } @@ -381,7 +381,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.MainnetChainConfig, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.MainnetChainConfig, context.BlockNumber, context.Time.Uint64()) if err != nil { t.Fatalf("failed to calculate l1DataFee: %v", err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 4a18a9245c..addd2b8cdb 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -112,7 +112,7 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := statedb.Snapshot() - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.AllEthashProtocolChanges, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.AllEthashProtocolChanges, context.BlockNumber, context.Time.Uint64()) if err != nil { b.Fatal(err) } diff --git a/go.mod b/go.mod index ca5088a456..9c432ada8f 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 + github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 github.com/scroll-tech/zktrie v0.8.4 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sourcegraph/conc v0.3.0 diff --git a/go.sum b/go.sum index d13a6b3db1..1bfb050837 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 h1:6aKqJSal+QVdB5HMWMs0JTbAIZ6/iAHJx9qizz0w9dU= -github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= +github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 h1:vb2XLvQwCf+F/ifP6P/lfeiQrHY6+Yb/E3R4KHXLqSE= +github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d9978b7ac5..f41090a8c4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -999,7 +999,7 @@ func EstimateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, bloc }() signer := types.MakeSigner(config, header.Number, header.Time) - return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number) + return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number, header.Time) } func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { @@ -1595,7 +1595,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH return nil, 0, nil, err } signer := types.MakeSigner(b.ChainConfig(), header.Number, header.Time) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number, header.Time) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } diff --git a/les/odr_test.go b/les/odr_test.go index e5ab5410f3..a8cee131b7 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -145,7 +145,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) res = append(res, result.Return()...) } @@ -159,7 +159,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) if state.Error() == nil { res = append(res, result.Return()...) diff --git a/les/state_accessor.go b/les/state_accessor.go index c0db2715f4..b2afa28938 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -65,7 +65,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, leth.blockchain.Config(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, leth.blockchain.Config(), block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/light/odr_test.go b/light/odr_test.go index a122207d22..ce5fae9146 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -201,7 +201,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) res = append(res, result.Return()...) if st.Error() != nil { diff --git a/light/txpool.go b/light/txpool.go index ec311d927e..e366fdc963 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -74,6 +74,7 @@ type TxPool struct { shanghai bool // Fork indicator whether we are in the shanghai stage. currentHead *big.Int // Current blockchain head + currentTime uint64 // Current blockchain head time } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -326,6 +327,7 @@ func (pool *TxPool) setNewHead(head *types.Header) { pool.shanghai = pool.config.IsShanghai(next) pool.currentHead = next + pool.currentTime = head.Time } // Stop stops the light transaction pool @@ -408,7 +410,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // 2. If FeeVault is enabled, perform an additional check for L1 data fees. if pool.config.Scroll.FeeVaultEnabled() { // Get L1 data fee in current state - l1DataFee, err := fees.CalculateL1DataFee(tx, currentState, pool.config, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, currentState, pool.config, pool.currentHead, pool.currentTime) if err != nil { return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) } diff --git a/rollup/fees/rollup_fee.go b/rollup/fees/rollup_fee.go index ac5fa9fa81..e6540bad19 100644 --- a/rollup/fees/rollup_fee.go +++ b/rollup/fees/rollup_fee.go @@ -2,14 +2,17 @@ package fees import ( "bytes" + "fmt" "math" "math/big" "github.com/holiman/uint256" + "github.com/scroll-tech/da-codec/encoding" "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/rollup/rcfg" ) @@ -54,15 +57,17 @@ type StateDB interface { } type gpoState struct { - l1BaseFee *big.Int - overhead *big.Int - scalar *big.Int - l1BlobBaseFee *big.Int - commitScalar *big.Int - blobScalar *big.Int + l1BaseFee *big.Int + overhead *big.Int + scalar *big.Int + l1BlobBaseFee *big.Int + commitScalar *big.Int + blobScalar *big.Int + penaltyThreshold *big.Int + penaltyFactor *big.Int } -func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.ChainConfig, signer types.Signer, state StateDB, blockNumber *big.Int) (*big.Int, error) { +func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.ChainConfig, signer types.Signer, state StateDB, blockNumber *big.Int, blockTime uint64) (*big.Int, error) { if msg.IsL1MessageTx() { return big.NewInt(0), nil } @@ -74,22 +79,7 @@ func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.C return nil, err } - raw, err := tx.MarshalBinary() - if err != nil { - return nil, err - } - - gpoState := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) - - var l1DataFee *big.Int - - if !config.IsCurie(blockNumber) { - l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar) - } else { - l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar) - } - - return l1DataFee, nil + return CalculateL1DataFee(tx, state, config, blockNumber, blockTime) } // asUnsignedTx turns a Message into a types.Transaction @@ -173,9 +163,58 @@ func readGPOStorageSlots(addr common.Address, state StateDB) gpoState { gpoState.l1BlobBaseFee = state.GetState(addr, rcfg.L1BlobBaseFeeSlot).Big() gpoState.commitScalar = state.GetState(addr, rcfg.CommitScalarSlot).Big() gpoState.blobScalar = state.GetState(addr, rcfg.BlobScalarSlot).Big() + gpoState.penaltyThreshold = state.GetState(addr, rcfg.PenaltyThresholdSlot).Big() + gpoState.penaltyFactor = state.GetState(addr, rcfg.PenaltyFactorSlot).Big() return gpoState } +// estimateTxCompressionRatio estimates the compression ratio for transaction data using da-codec +// compression_ratio(tx) = size(tx) * PRECISION / size(zstd(tx)) +func estimateTxCompressionRatio(data []byte, blockNumber uint64, blockTime uint64, config *params.ChainConfig) (*big.Int, error) { + if len(data) == 0 { + return nil, fmt.Errorf("raw data is empty") + } + + // Compress data using da-codec + compressed, err := encoding.CompressScrollBatchBytes(data, blockNumber, blockTime, config) + if err != nil { + log.Error("Batch compression failed, using 1.0 compression ratio", "error", err, "data size", len(data), "data", common.Bytes2Hex(data)) + return nil, fmt.Errorf("batch compression failed: %w", err) + } + + if len(compressed) == 0 { + log.Error("Compressed data is empty, using 1.0 compression ratio", "data size", len(data), "data", common.Bytes2Hex(data)) + return nil, fmt.Errorf("compressed data is empty") + } + + // compression_ratio = size(tx) * PRECISION / size(zstd(tx)) + originalSize := new(big.Int).SetUint64(uint64(len(data))) + compressedSize := new(big.Int).SetUint64(uint64(len(compressed))) + + // Make sure compression ratio >= 1 by checking if compressed data is bigger or equal to original data + // This behavior is consistent with DA Batch compression in codecv7 and later versions + if len(compressed) >= len(data) { + log.Debug("Compressed data is bigger or equal to the original data, using 1.0 compression ratio", "original size", len(data), "compressed size", len(compressed)) + return rcfg.Precision, nil + } + + ratio := new(big.Int).Mul(originalSize, rcfg.Precision) + ratio.Div(ratio, compressedSize) + + return ratio, nil +} + +// calculatePenalty computes the penalty multiplier based on compression ratio +// penalty(tx) = compression_ratio(tx) >= penalty_threshold ? 1 * PRECISION : penalty_factor +func calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor *big.Int) *big.Int { + if compressionRatio.Cmp(penaltyThreshold) >= 0 { + // No penalty + return rcfg.Precision + } + // Apply penalty + return penaltyFactor +} + // calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx func calculateEncodedL1DataFee(data []byte, overhead, l1BaseFee *big.Int, scalar *big.Int) *big.Int { l1GasUsed := calculateL1GasUsed(data, overhead) @@ -200,6 +239,51 @@ func calculateEncodedL1DataFeeCurie(data []byte, l1BaseFee *big.Int, l1BlobBaseF return l1DataFee } +// calculateEncodedL1DataFeeFeynman computes the L1 fee for an RLP-encoded tx, post Feynman +// +// Post Feynman formula: +// rollup_fee(tx) = (execScalar * l1BaseFee + blobScalar * l1BlobBaseFee) * size(tx) * penalty(tx) / PRECISION / PRECISION +// +// Where: +// penalty(tx) = compression_ratio(tx) >= penalty_threshold ? 1 * PRECISION : penalty_factor +// +// compression_ratio(tx) = size(tx) * PRECISION / size(zstd(tx)) +// exec_scalar = compression_scalar * (commit_scalar + verification_scalar) +// blob_scalar = compression_scalar * blob_scalar +func calculateEncodedL1DataFeeFeynman( + data []byte, + l1BaseFee *big.Int, + l1BlobBaseFee *big.Int, + execScalar *big.Int, + blobScalar *big.Int, + penaltyThreshold *big.Int, + penaltyFactor *big.Int, + compressionRatio *big.Int, +) *big.Int { + // Calculate penalty multiplier + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + + // Transaction size (RLP-encoded) + txSize := big.NewInt(int64(len(data))) + + // Compute gas components + execGas := new(big.Int).Mul(execScalar, l1BaseFee) + blobGas := new(big.Int).Mul(blobScalar, l1BlobBaseFee) + + // fee per byte = execGas + blobGas + feePerByte := new(big.Int).Add(execGas, blobGas) + + // l1DataFee = feePerByte * txSize * penalty + l1DataFee := new(big.Int).Mul(feePerByte, txSize) + l1DataFee.Mul(l1DataFee, penalty) + + // Divide by rcfg.Precision (once for scalars, once for penalty) + l1DataFee.Div(l1DataFee, rcfg.Precision) // account for scalars + l1DataFee.Div(l1DataFee, rcfg.Precision) // accounts for penalty + + return l1DataFee +} + // calculateL1GasUsed computes the L1 gas used based on the calldata and // constant sized overhead. The overhead can be decreased as the cost of the // batch submission goes down via contract optimizations. This will not overflow @@ -233,7 +317,7 @@ func mulAndScale(x *big.Int, y *big.Int, precision *big.Int) *big.Int { return new(big.Int).Quo(z, precision) } -func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.ChainConfig, blockNumber *big.Int) (*big.Int, error) { +func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) (*big.Int, error) { if tx.IsL1MessageTx() { return big.NewInt(0), nil } @@ -249,8 +333,26 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.Cha if !config.IsCurie(blockNumber) { l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar) - } else { + } else if !config.IsFeynman(blockTime) { l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar) + } else { + // Calculate compression ratio for Feynman + compressionRatio, err := estimateTxCompressionRatio(raw, blockNumber.Uint64(), blockTime, config) + if err != nil { + return nil, fmt.Errorf("failed to estimate compression ratio: tx hash=%s: %w", tx.Hash().Hex(), err) + } + + // The contract slot for commitScalar is changed to execScalar in Feynman + l1DataFee = calculateEncodedL1DataFeeFeynman( + raw, + gpoState.l1BaseFee, + gpoState.l1BlobBaseFee, + gpoState.commitScalar, // now represents execScalar + gpoState.blobScalar, + gpoState.penaltyThreshold, + gpoState.penaltyFactor, + compressionRatio, + ) } // ensure l1DataFee fits into uint64 for circuit compatibility diff --git a/rollup/fees/rollup_fee_test.go b/rollup/fees/rollup_fee_test.go index b35d8cd9bf..a4197321cf 100644 --- a/rollup/fees/rollup_fee_test.go +++ b/rollup/fees/rollup_fee_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/scroll-tech/go-ethereum/params" ) func TestL1DataFeeBeforeCurie(t *testing.T) { @@ -31,3 +33,109 @@ func TestL1DataFeeAfterCurie(t *testing.T) { actual := calculateEncodedL1DataFeeCurie(data, l1BaseFee, l1BlobBaseFee, commitScalar, blobScalar) assert.Equal(t, expected, actual) } + +func TestL1DataFeeFeynman(t *testing.T) { + l1BaseFee := new(big.Int).SetInt64(1_000_000_000) + l1BlobBaseFee := new(big.Int).SetInt64(1_000_000_000) + execScalar := new(big.Int).SetInt64(10) + blobScalar := new(big.Int).SetInt64(20) + penaltyThreshold := new(big.Int).SetInt64(6_000_000_000) // 6 * PRECISION + penaltyFactor := new(big.Int).SetInt64(2_000_000_000) // 2 * PRECISION (200% penalty) + + // Test case 1: No penalty (compression ratio >= threshold) + t.Run("no penalty case", func(t *testing.T) { + data := make([]byte, 100) // txSize = 100 + compressionRatio := new(big.Int).SetInt64(6_000_000_000) // exactly at threshold + + // Since compression ratio >= penaltyThreshold, penalty = 1 * PRECISION + // feePerByte = execScalar * l1BaseFee + blobScalar * l1BlobBaseFee = 10 * 1_000_000_000 + 20 * 1_000_000_000 = 30_000_000_000 + // l1DataFee = feePerByte * txSize * penalty / PRECISION / PRECISION + // = 30_000_000_000 * 100 * 1_000_000_000 / 1_000_000_000 / 1_000_000_000 = 3000 + + expected := new(big.Int).SetInt64(3000) + + actual := calculateEncodedL1DataFeeFeynman( + data, + l1BaseFee, + l1BlobBaseFee, + execScalar, + blobScalar, + penaltyThreshold, + penaltyFactor, + compressionRatio, + ) + + assert.Equal(t, expected, actual) + }) + + // Test case 2: With penalty (compression ratio < threshold) + t.Run("with penalty case", func(t *testing.T) { + data := make([]byte, 100) // txSize = 100 + compressionRatio := new(big.Int).SetInt64(5_000_000_000) // below threshold + + // Since compression ratio < penaltyThreshold, penalty = penaltyFactor + // feePerByte = execScalar * l1BaseFee + blobScalar * l1BlobBaseFee = 30_000_000_000 + // l1DataFee = feePerByte * txSize * penaltyFactor / PRECISION / PRECISION + // = 30_000_000_000 * 100 * 2_000_000_000 / 1_000_000_000 / 1_000_000_000 = 6000 + + expected := new(big.Int).SetInt64(6000) + + actual := calculateEncodedL1DataFeeFeynman( + data, + l1BaseFee, + l1BlobBaseFee, + execScalar, + blobScalar, + penaltyThreshold, + penaltyFactor, + compressionRatio, + ) + + assert.Equal(t, expected, actual) + }) +} + +func TestEstimateTxCompressionRatio(t *testing.T) { + // Mock config that would select a specific codec version + // Note: You'll need to adjust this based on your actual params.ChainConfig structure + + t.Run("empty data", func(t *testing.T) { + data := []byte{} + // Should return 1.0 ratio (PRECISION) + ratio, err := estimateTxCompressionRatio(data, 1000000, 1700000000, params.TestChainConfig) + assert.Error(t, err, "raw data is empty") + assert.Nil(t, ratio) + // The exact value depends on rcfg.Precision, but should be the "1.0" equivalent + }) + + t.Run("non-empty data", func(t *testing.T) { + // Create some compressible data + data := make([]byte, 1000) + for i := range data { + data[i] = byte(i % 10) // Create patterns for better compression + } + + ratio, err := estimateTxCompressionRatio(data, 1000000, 1700000000, params.TestChainConfig) + assert.NoError(t, err) + assert.NotNil(t, ratio) + // Should return a ratio > 1.0 (since compressed size < original size) + }) +} + +func TestCalculatePenalty(t *testing.T) { + precision := new(big.Int).SetInt64(1_000_000_000) // PRECISION + penaltyThreshold := new(big.Int).SetInt64(6_000_000_000) // 6 * PRECISION + penaltyFactor := new(big.Int).SetInt64(2_000_000_000) // 2 * PRECISION + + t.Run("no penalty when ratio >= threshold", func(t *testing.T) { + compressionRatio := new(big.Int).SetInt64(6_000_000_000) // exactly at threshold + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + assert.Equal(t, precision, penalty) + }) + + t.Run("penalty when ratio < threshold", func(t *testing.T) { + compressionRatio := new(big.Int).SetInt64(5_000_000_000) // below threshold + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + assert.Equal(t, penaltyFactor, penalty) + }) +} diff --git a/rollup/tracing/tracing.go b/rollup/tracing/tracing.go index 86fe1381c0..0569a99bc4 100644 --- a/rollup/tracing/tracing.go +++ b/rollup/tracing/tracing.go @@ -239,7 +239,7 @@ func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error msg, _ := tx.AsMessage(env.signer, block.BaseFee()) env.state.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number(), block.Time()) if err != nil { failed = err break @@ -342,7 +342,7 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B state.SetTxContext(txctx.TxHash, txctx.TxIndex) // Computes the new state by applying the given message. - l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number(), block.Time()) if err != nil { return err } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6cb06777e5..9f3a5da75a 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -251,7 +251,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh gaspool := new(core.GasPool) gaspool.AddGas(block.GasLimit()) - l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb, config, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb, config, block.Number(), block.Time()) if err != nil { return nil, nil, common.Hash{}, err }