Skip to content

feat(evmonly): add eth_gasPrice and eth_feeHistory to the EVM-only executor - #4220

Merged
shemnon merged 5 commits into
giga-1from
shemnon/giga-evmonly-fee-history
Sep 17, 2026
Merged

shemnon merged 5 commits into
giga-1from
shemnon/giga-evmonly-fee-history

Conversation

@shemnon

@shemnon shemnon commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Describe your changes and provide context

Adds eth_gasPrice and eth_feeHistory to the Autobahn EVM-only executor's RPC server, on top of eth_getBlockByNumber/eth_getBlockByHash (#4208). Reviewed locally (Cursor) and fixed before opening.

  • eth_gasPrice returns this application's fixed admission floor (1 gwei) plus 10%, via a new EvmMinGasPrice exposing the previously-private evmOnlyMinGasPrice constant, plumbed through the same EvmBaseFee/EvmGasLimit optional-interface pattern
  • eth_feeHistory walks the requested block range reusing block.go's resolveBlockByNumber; gasUsedRatio comes from the last transaction's receipt CumulativeGasUsed (equivalent to summing every tx, cheaper); reward, when requested, is the same suggested gas price for every percentile — a documented simplification, since the receipt store only supports point lookups (GetReceipt by hash), not the bulk/range read a real per-transaction percentile would need (same constraint noted in block.go's fullTx encoding)
  • eth_gasPrice and eth_feeHistory's reward are computed through one shared suggestedGasPrice helper, so they can't drift apart
  • an ending block that doesn't resolve (future height, or one the node has since pruned) is an error, not a silently-empty success result — a caller shouldn't mistake "no data" for "zero fees"; earliest resolves to height 1, this executor's first committed height
  • walkFeeHistoryRange checks context cancellation each iteration
  • lastTxGasUsedRatio ignores a receipt whose stored block number doesn't match the queried height, guarding against a resubmitted tx hash overwriting an earlier receipt (same defense evmrpc's v2 path has)
  • documents both methods, a cast gas-price/cast rpc eth_feeHistory example, and the reward limitation in the Autobahn README

Testing performed to validate your change

  • go build ./...
  • go test -count=1 ./giga/evmonly/... ./sei-tendermint/internal/proxy/... ./sei-tendermint/internal/evmonlyapp/... ./sei-tendermint/internal/rpc/core/... ./sei-tendermint/node/...
  • Coverage: gas-price-floor suggestion, block-count validation/capping, percentile validation, block-tag/historical-height/future-height/earliest resolution, per-block gas-used ratio (single and multi-block), reward-cell independence, mismatched-receipt handling, context cancellation, full JSON-RPC round trips
  • golangci-lint run v2.13.2 scoped to all touched packages: 0 issues
  • golangci-lint fmt --diff: clean

🤖 Generated with Claude Code

…ecutor

Exposes the application's fixed admission gas-price floor through the same
optional-interface plumbing as EvmBaseFee/EvmGasLimit (Proxy -> Environment
-> Backend), and reuses eth_getBlockByNumber's height resolution to walk a
range of blocks for fee history.

- eth_gasPrice suggests 10% above the admission floor
- eth_feeHistory reports real per-block gasUsedRatio and always-zero
  baseFeePerGas; reward is a documented simplification (fixed at the
  floor for every requested percentile), since this chain has no
  congestion-based fee market
- extracts blockAPI.resolveBlockByNumber into a package-level helper shared
  with the new range walk
…ry end, harden edges

- Must: eth_feeHistory's reward was the raw admission floor while eth_gasPrice
  suggested floor*1.1; an EIP-1559 wallet computing maxFeePerGas from reward
  would construct a transaction at exactly the rejection boundary. Both now
  share one suggestedGasPrice helper.
- Should: eth_feeHistory's lastBlock now errors on a future or pruned height
  instead of silently returning an empty success result, and resolves
  "earliest" to height 1 (this executor's first committed height) instead of
  the literal 0 this go-ethereum fork uses for the sentinel, which this
  executor never commits. eth_getBlockByNumber's null-for-unavailable
  semantics are unchanged; only feeHistory's own resolution changed.
- Should: walkFeeHistoryRange now checks ctx cancellation each iteration.
  Clamping the walk to the store's earliest retained height is left for when
  a lower-bound accessor exists (same gap already tracked in
  sei-tendermint/internal/rpc/core/blocks.go's autobahnCheckAndGetHeight) --
  building that here would be new infrastructure, not a review fix.
- Nit: fixedReward now copies the suggested price per cell instead of
  aliasing one *big.Int across every entry.
- Nit: lastTxGasUsedRatio now ignores a receipt whose stored block number
  doesn't match the queried height, the same defense v2 has against a
  resubmitted tx hash overwriting an earlier receipt.
- Nit: EvmMinGasPrice gets the same Proxy success/failure tests EvmBaseFee
  and EvmGasLimit already have.
Rationale for the deferred pruning-floor clamp belongs in the prior commit
message, not a code comment.
fixedReward and block.go's fullTx receipt-per-transaction loop are deferred
for the same reason: no bulk/range receipt read to build a real
per-transaction value from.
@cursor

cursor Bot commented Sep 17, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New public RPC surface and fee/gas semantics affect wallets and tooling; simplified fee history rewards and shared receipt heuristics could mislead clients that expect mainnet-accurate percentiles, though behavior is documented and guarded for stale receipts.

Overview
Adds eth_gasPrice and eth_feeHistory to the Autobahn EVM-only JSON-RPC server, with EvmMinGasPrice exposed from the app through proxy/core and the RPC Backend (same pattern as gas limit/base fee).

eth_gasPrice returns the admission floor scaled by 10% via shared suggestedGasPrice, which eth_feeHistory also uses for simplified per-percentile reward rows (documented limitation until bulk receipt reads exist).

eth_feeHistory walks a capped block range (max 1024), resolves the end block with shared resolveBlockByNumber (refactored out of blockAPI), errors on unresolved end blocks (unlike eth_getBlockBy*), validates percentiles, honors context cancellation, and sets gasUsedRatio from the last tx receipt’s cumulative gas vs current gas limit; baseFeePerGas is always zero.

infoAPI now takes the receipt store for fee-history reads. encodeBlock only applies gasUsed when the last receipt’s BlockNumber matches the block height, matching lastTxGasUsedRatio against stale/overwritten receipts.

Autobahn README documents cast gas-price / eth_feeHistory behavior and limitations.

Reviewed by Cursor Bugbot for commit d086104. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedSep 17, 2026, 6:19 AM

@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.83838% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.57%. Comparing base (7e3ca24) to head (d086104).
⚠️ Report is 19 commits behind head on giga-1.

Files with missing lines Patch % Lines
giga/evmonly/rpc/info.go 84.09% 14 Missing ⚠️
sei-tendermint/internal/evmonlyapp/app.go 0.00% 1 Missing ⚠️
sei-tendermint/internal/rpc/core/mempool.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           giga-1    #4220      +/-   ##
==========================================
+ Coverage   65.55%   65.57%   +0.01%     
==========================================
  Files        2081     2083       +2     
  Lines      157460   157472      +12     
==========================================
+ Hits       103222   103256      +34     
+ Misses      54097    54075      -22     
  Partials      141      141              
Flag Coverage Δ
sei-chain-pr 57.27% <83.83%> (?)
sei-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
giga/evmonly/rpc/block.go 87.00% <100.00%> (ø)
giga/evmonly/rpc/server.go 23.25% <100.00%> (-18.85%) ⬇️
sei-tendermint/internal/proxy/proxy.go 91.66% <100.00%> (+0.55%) ⬆️
sei-tendermint/internal/evmonlyapp/app.go 81.27% <0.00%> (-0.50%) ⬇️
sei-tendermint/internal/rpc/core/mempool.go 48.14% <0.00%> (-3.27%) ⬇️
giga/evmonly/rpc/info.go 84.44% <84.09%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eth_gasPrice and eth_feeHistory are implemented cleanly on the existing optional-provider plumbing pattern, with good test coverage and no blocking defects. Four non-blocking findings: a hardcoded earliest height that breaks on a chain with InitialHeight > 1, a null wire shape for the empty result, request amplification on the public RPC port, and two undocumented gasUsedRatio approximations.

Findings: 0 blocking | 5 non-blocking | 3 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] The README documents the baseFeePerGas and reward simplifications but not two gasUsedRatio ones: the denominator is EvmGasLimit() — the current committed block's limit, derived from consensus block.max_gas — applied to every historical block in the range, so ratios skew if that parameter ever changes; and a block whose last-transaction receipt is missing or block-number-mismatched reports ratio 0, indistinguishable from an empty block. The second is a deliberate guard, but it makes "no data" look like "no gas used" — the exact conflation the ending-block error path was added to avoid. Worth a sentence each alongside the existing limitation notes.
  • 3 suggestion(s)/nit(s) flagged inline on specific lines.
  • 1 non-blocking pre-existing issue(s) listed below under pre-existing issues.

Pre-existing issues

  • [suggestion] blockAPI.receiptFor (giga/evmonly/rpc/block.go:190) has no stored.BlockNumber != block height check, so eth_getBlockByNumber's gasUsed can be taken from a resubmitted transaction hash's receipt written by a different block. This PR's lastTxGasUsedRatio adds exactly that guard for eth_feeHistory, leaving the two paths inconsistent; the block path predates this change.

Comment thread giga/evmonly/rpc/info.go
Comment thread giga/evmonly/rpc/info.go Outdated
Comment thread giga/evmonly/rpc/info.go

// maxFeeHistoryBlockCount caps a single eth_feeHistory request, matching
// go-ethereum's own default block-count cap.
const maxFeeHistoryBlockCount = 1024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The 1024 cap matches go-ethereum's number, but not its cost: geth serves the range from an LRU header/block cache, whereas walkFeeHistoryRange issues one backend.Block full-block load per height — each through Proxy's timing-instrumented path — plus a receipt read for every non-empty block. One unauthenticated request on 0.0.0.0:8545 therefore fans out to ~1024 store reads sequentially, with nothing else bounding concurrent requests. The ctx.Err() check per iteration limits the damage from a client that disconnects, but not from one that waits. Consider a lower cap for this executor until a bulk/header-only read exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disagreeing, not lowering the cap: 1024 already matches go-ethereum's own default, and this repo's own production evmrpc config (MaxBlocksForLog, fed into the identical maxBlocks parameter) defaults to 2000 — higher, not lower. The real gap is the missing cache, not the cap number; picking an arbitrary smaller value wouldn't close that, and would make this devnet-facing surface stricter than both stock geth and this repo's own production default for the same knob. A cache or the bulk/header-only read (already tracked elsewhere as deferred) is the real fix.

…ocument ratio caveats

- eth_feeHistory's empty result now matches go-ethereum's shape (oldestBlock
  0x0, empty gasUsedRatio) instead of a zero-value struct with nil fields,
  which a strict client's unconditional BigInt(oldestBlock) would reject.
- walkFeeHistoryRange's post-loop nil-OldestBlock fallback is now an error,
  not silent empty success; it was already unreachable except a store
  eviction racing the just-completed end-height resolution.
- block.go's GetBlockByNumber gasUsed now ignores a receipt whose stored
  block number doesn't match the queried height, the same guard
  eth_feeHistory's lastTxGasUsedRatio already has, closing an inconsistency
  the reviewer flagged as pre-existing.
- Documented two gasUsedRatio approximations in the README and inline: the
  current gas limit is applied to every historical block in the range, and a
  missing/stale receipt reads as 0, indistinguishable from an empty block.
- earliestCommittedHeight's doc comment now states its height-1 assumption
  and why nothing currently exposes a real InitialHeight after restart,
  rather than silently asserting a fact that's false for a chain configured
  with InitialHeight > 1.
Comment thread giga/evmonly/rpc/block.go
// now since a block today is exactly one lane's transactions.
var gasUsed hexutil.Uint64
if lastReceipt != nil {
if lastReceipt != nil && lastReceipt.BlockNumber == uint64(number) { //nolint:gosec // G115: number is a validated block height.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@shemnon
shemnon added this pull request to the merge queue Sep 17, 2026
Merged via the queue into giga-1 with commit 4c07f52 Sep 17, 2026
86 checks passed
@shemnon
shemnon deleted the shemnon/giga-evmonly-fee-history branch September 17, 2026 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants