feat(precompiles): read logs across a block range - #349
Conversation
`eth_getLogs` is capped per call, so reading any history longer than the cap means walking it in chunks. That loop is short but has two failure modes that both look like a working indexer, and every project needing logs writes it again. The inclusive boundary is the first. The public endpoints allow 2000 blocks and apply the check as `toBlock - fromBlock + 1 <= 2000`, so a range built as `from + 2000` asks for 2001 and is rejected on every chunk with "block range too large (2001), maximum allowed is 2000 blocks". Measured on both networks: 2000 succeeds, 2001 does not. Halving the chunk to be safe works but doubles the round trips a backfill needs. The confirmation depth is the second. Sei finalises a block as it is produced, so there is no reorg window to wait out, and a default lag copied from an Ethereum-shaped library is latency with nothing behind it. `getLogsInRange` reads to head; a caller wanting to lag passes an explicit `toBlock`. `blockRanges` exposes the same arithmetic as a generator without making requests, so a backfill can be planned or driven by a bounded worker pool rather than one sequential loop. `onChunk` reports progress, because a backfill over long history is thousands of requests and is otherwise indistinguishable from a hang. Takes a `PublicClient` rather than constructing one, so it works with whatever transport and chain the caller has configured. No dependency or peer range changes -- this uses the `viem` peer already declared. Verified against a live endpoint as well as the unit tests: a 4501-block span issued three requests of 2000, 2000 and 501 blocks, none over the cap.
PR SummaryLow Risk Overview
Documentation and a minor changeset accompany a focused unit test suite (recording client) for chunk bounds, gap-free coverage, and RPC arg shaping. Reviewed by Cursor Bugbot for commit 596ba49. Bugbot is set up for automated code reviews on this repo. Configure here. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #349 +/- ##
==========================================
+ Coverage 97.17% 97.19% +0.02%
==========================================
Files 80 81 +1
Lines 5410 5449 +39
==========================================
+ Hits 5257 5296 +39
Misses 153 153
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Summary
Adds
getLogsInRange,blockRangesandMAX_GET_LOGS_BLOCK_RANGEto@sei-js/precompiles, for reading logs across a block range.eth_getLogsis capped per call, so reading any history longer than the capmeans walking it in chunks. That loop is short, but it has two failure modes
that both look like a working indexer, and every project that needs logs ends
up writing it again.
The inclusive boundary. The public endpoints allow 2000 blocks and apply
the check as
toBlock - fromBlock + 1 <= 2000. A range built asfrom + 2000therefore asks for 2001 blocks and is rejected on every chunk with:
Measured against both public endpoints: a 2000-block span succeeds, 2001 does
not. Writing the loop conservatively at half the cap works but doubles the
round trips a backfill needs.
The confirmation depth. Most EVM indexing code carries a
confirmationsdefault — often around 12 — because Ethereum needs a reorg buffer. Sei finalises
a block as it is produced, so that default is latency with nothing behind it.
getLogsInRangereads to head, and a caller who wants to lag head passes anexplicit
toBlock.blockRangesexposes the same arithmetic as a generator without making anyrequests, so a caller can plan a backfill or drive a bounded worker pool rather
than one sequential loop.
onChunkreports progress, because a backfill overlong history is thousands of requests and is otherwise indistinguishable from a
hang.
It takes a
PublicClientrather than constructing one, so it works withwhatever transport and chain the caller already has. No dependency or peer
range changes — this uses the
viempeer the package already declares.Related issue
None. Raised from writing this loop by hand and hitting the off-by-one.
Test plan
bun run checkbun run build—@sei-js/precompilesbuilds cleanbun run test— 70 pass in@sei-js/precompiles19 unit tests covering the chunk arithmetic against a recording client: spans
never exceed the cap, a full chunk is exactly the maximum, coverage has no
gaps and no overlaps (a gap silently drops logs, an overlap silently duplicates
them), single-block and empty ranges, and a chunk size below one is rejected
rather than looping forever.
Also exercised against a live endpoint: a 4501-block span issued three requests
of 2000, 2000 and 501 blocks, none over the cap, returning 3116 logs.
Two notes on the repo-wide scripts, both reproduced on a clean checkout of
mainbefore this branch, and neither touched by this change:bun run buildfails in@sei-js/registryon my machine due to localsubmodule drift.
bun run testreports 55 failures in@sei-js/mcp-server.Checklist