Description
GasEstimator.cs uses a single constant for two different purposes:
- The optimistic-guess ceiling — what gas value to try first to short-circuit the binary search.
- The binary-search-stop threshold — how tight the (left, right) bracket must get before accepting the result.
Both come from marginMultiplier = errorMargin / 10000 + 1 = 1.015. Geth uses two separate constants:
- Optimistic guess:
(MaxUsedGas + CallStipend) * 64/63 — compensates for EIP-150's 1/64 child-call reservation (eth/gasestimator/gasestimator.go:154).
- Binary-search stop:
ErrorRatio = 0.015 (= 150 bps).
NM's optimistic multiplier (1.015) is smaller than Geth's (≈1.01587), so NM's binary search starts from a lower upper bound and settles 2-4 gas under Geth on typical contract calls.
This is not a correctness bug — NM's estimates are still safely above intrinsic — but it causes constant low-grade divergence on every cross-client comparison involving contract execution.
Steps to Reproduce
Any contract call that exercises EIP-150's child-call budget reproduces the divergence:
{
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [{"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "to": "0x2525680000000000000000000000000000000000", "data": "0x..."}, "latest"],
"id": 1
}
Expected (Geth)
{"jsonrpc":"2.0","id":1,"result":"0x5309"}
Actual (Nethermind v1.39.0-rc+c6739713)
{"jsonrpc":"2.0","id":1,"result":"0x5307"}
Consistently 2-4 gas lower than Geth across all affected fixtures.
EIP-7702 SetCodeTx variant (test_32)
The same multiplier gap surfaces on SetCodeTx transactions. A type 0x4 transaction with
an authorizationList but no calldata:
{
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0xd3CdA913deB6f4967b2Ef3aa68f5A843aFB83557",
"type": "0x4",
"authorizationList": [
{
"chainId": "0x1",
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"nonce": "0x1",
"yParity": "0x1",
"r": "0xa8f2b4c1d3e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
"s": "0x1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
]
},
"latest"
],
"id": 1
}
Expected (Geth): "0xb52e" (46382) — Actual (NM): "0xb3b0" (46000). The 382-gas gap is the optimistic-multiplier delta applied to the 46000 floor (21000 intrinsic + 25000 for one authorization).
Fix Location
src/Nethermind/Nethermind.Blockchain/Tracing/GasEstimator.cs:
- Add
private const double OptimisticMultiplier = 64d / 63d; (mirrors Geth's EIP-150 budget compensation).
- In
BinarySearchEstimate (around line 148): rename marginMultiplier to errorRatio = errorMargin / BasisPointsDivisor and use it directly as the ShouldContinueSearch threshold.
- In the call to
TryOptimisticEstimate (around line 150): pass OptimisticMultiplier instead of the merged marginMultiplier.
- Rename the parameter on
TryOptimisticEstimate (around line 170) to optimisticMultiplier for clarity.
Three-line semantic change; existing GasEstimator unit tests still pass.
Notes
- Cross-client triangulation: Erigon, Reth, and Besu all follow Geth's 64/63 + separate-error-ratio pattern. NM is the outlier.
- Covers 9 fixtures in
integration/mainnet/eth_estimateGas/:
test_13, test_15, test_16, test_18, test_20, test_24, test_25, test_26 — contract calls, 2-4 gas under Geth.
test_32 — EIP-7702 SetCodeTx, 382 gas under Geth ("0xb52e" → "0xb3b0").
- All 9 flip to PASS when the
OptimisticMultiplier = 64d/63d fix lands.
Description
GasEstimator.csuses a single constant for two different purposes:Both come from
marginMultiplier = errorMargin / 10000 + 1 = 1.015. Geth uses two separate constants:(MaxUsedGas + CallStipend) * 64/63— compensates for EIP-150's 1/64 child-call reservation (eth/gasestimator/gasestimator.go:154).ErrorRatio = 0.015(= 150 bps).NM's optimistic multiplier (1.015) is smaller than Geth's (≈1.01587), so NM's binary search starts from a lower upper bound and settles 2-4 gas under Geth on typical contract calls.
This is not a correctness bug — NM's estimates are still safely above intrinsic — but it causes constant low-grade divergence on every cross-client comparison involving contract execution.
Steps to Reproduce
Any contract call that exercises EIP-150's child-call budget reproduces the divergence:
{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "to": "0x2525680000000000000000000000000000000000", "data": "0x..."}, "latest"], "id": 1 }Expected (Geth)
{"jsonrpc":"2.0","id":1,"result":"0x5309"}Actual (Nethermind v1.39.0-rc+c6739713)
{"jsonrpc":"2.0","id":1,"result":"0x5307"}Consistently 2-4 gas lower than Geth across all affected fixtures.
EIP-7702 SetCodeTx variant (
test_32)The same multiplier gap surfaces on SetCodeTx transactions. A
type 0x4transaction withan
authorizationListbut no calldata:{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [ { "from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "to": "0xd3CdA913deB6f4967b2Ef3aa68f5A843aFB83557", "type": "0x4", "authorizationList": [ { "chainId": "0x1", "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "nonce": "0x1", "yParity": "0x1", "r": "0xa8f2b4c1d3e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1", "s": "0x1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" } ] }, "latest" ], "id": 1 }Expected (Geth):
"0xb52e"(46382) — Actual (NM):"0xb3b0"(46000). The 382-gas gap is the optimistic-multiplier delta applied to the 46000 floor (21000 intrinsic + 25000 for one authorization).Fix Location
src/Nethermind/Nethermind.Blockchain/Tracing/GasEstimator.cs:private const double OptimisticMultiplier = 64d / 63d;(mirrors Geth's EIP-150 budget compensation).BinarySearchEstimate(around line 148): renamemarginMultipliertoerrorRatio = errorMargin / BasisPointsDivisorand use it directly as theShouldContinueSearchthreshold.TryOptimisticEstimate(around line 150): passOptimisticMultiplierinstead of the mergedmarginMultiplier.TryOptimisticEstimate(around line 170) tooptimisticMultiplierfor clarity.Three-line semantic change; existing GasEstimator unit tests still pass.
Notes
integration/mainnet/eth_estimateGas/:test_13,test_15,test_16,test_18,test_20,test_24,test_25,test_26— contract calls, 2-4 gas under Geth.test_32— EIP-7702 SetCodeTx, 382 gas under Geth ("0xb52e"→"0xb3b0").OptimisticMultiplier = 64d/63dfix lands.