From 1481f9855427955eab4c05908be3fd88a04d7754 Mon Sep 17 00:00:00 2001 From: rayoo Date: Tue, 28 Apr 2026 10:43:45 +0800 Subject: [PATCH] internal/ethapi: apply block overrides to header in eth_call When BlockOverrides specifies BaseFeePerGas, doCall applies the override to the derived EVM block context, but the original header is still passed down to applyMessage. applyMessage then computes the message's gasPrice via args.ToMessage(header.BaseFee, ...) which ignores the override, so subsequent GASPRICE queries and effectiveTip calculations use the pre-override basefee. As a result, eth_call with a 1559 transaction (MaxFeePerGas + MaxPriorityFeePerGas) and a BaseFeePerGas block override returns a GASPRICE derived from the real block's basefee instead of the override. tracers/api.go and simulate.go already read BaseFee from the overridden block context, so only eth_call was affected. Mirror the fix applied to DoEstimateGas in #34081: after applying the overrides to blockCtx, rebuild the header via blockOverrides.MakeHeader so downstream code sees the overridden basefee. Add a TestCall case that asserts GASPRICE returns tip + overridden basefee. It fails on master and passes with this change. --- internal/ethapi/api.go | 4 ++++ internal/ethapi/api_test.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 149e12c5b8..520688fb47 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -734,6 +734,10 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S if err := blockOverrides.Apply(&blockCtx); err != nil { return nil, err } + // Override the header so callers that compute gas price from 1559 fee + // fields see the overridden basefee. Otherwise GASPRICE/effectiveTip + // would be derived from the pre-override basefee. + header = blockOverrides.MakeHeader(header) } rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) precompiles := vm.ActivePrecompiledContracts(rules) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 6cf52d636a..161d97b4eb 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -1315,6 +1315,27 @@ func TestCall(t *testing.T) { }, expectErr: errors.New(`block override "withdrawals" is not supported for this RPC method`), }, + // Verify that an overridden basefee is honored when computing gasPrice + // from the 1559 fee fields. Returning GASPRICE opcode; expected value + // is min(MaxFeePerGas, MaxPriorityFeePerGas + overridden BaseFee). + // + // BaseFee override = 0xa (10); MaxFeePerGas = 0x64 (100); + // MaxPriorityFeePerGas = 0x2 (2); expected GASPRICE = 12. + { + name: "basefee-override-used-in-gasprice", + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + // Contract: GASPRICE; PUSH1 0; MSTORE; PUSH1 32; PUSH1 0; RETURN + Input: hex2Bytes("3a60005260206000f3"), + MaxFeePerGas: (*hexutil.Big)(big.NewInt(100)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(2)), + }, + blockOverrides: override.BlockOverrides{ + BaseFeePerGas: (*hexutil.Big)(big.NewInt(10)), + }, + want: "0x000000000000000000000000000000000000000000000000000000000000000c", + }, } for _, tc := range testSuite { result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)