Skip to content

feat(precompiles): read logs across a block range - #349

Open
monty-sei wants to merge 1 commit into
mainfrom
feat/precompiles-get-logs-in-range
Open

feat(precompiles): read logs across a block range#349
monty-sei wants to merge 1 commit into
mainfrom
feat/precompiles-get-logs-in-range

Conversation

@monty-sei

Copy link
Copy Markdown
Collaborator

Summary

Adds getLogsInRange, blockRanges and MAX_GET_LOGS_BLOCK_RANGE to
@sei-js/precompiles, for reading logs across a block range.

eth_getLogs is capped per call, so reading any history longer than the cap
means 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 as from + 2000
therefore asks for 2001 blocks and is rejected on every chunk with:

block range too large (2001), maximum allowed is 2000 blocks

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 confirmations
default — 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.
getLogsInRange reads to head, and a caller who wants to lag head passes an
explicit toBlock.

blockRanges exposes the same arithmetic as a generator without making any
requests, so a caller can plan a backfill or drive 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.

It takes a PublicClient rather than constructing one, so it works with
whatever transport and chain the caller already has. No dependency or peer
range changes
— this uses the viem peer the package already declares.

Related issue

None. Raised from writing this loop by hand and hitting the off-by-one.

Test plan

  • bun run check
  • bun run build@sei-js/precompiles builds clean
  • bun run test — 70 pass in @sei-js/precompiles

19 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
main before this branch, and neither touched by this change:

  • bun run build fails in @sei-js/registry on my machine due to local
    submodule drift.
  • bun run test reports 55 failures in @sei-js/mcp-server.

Checklist

  • I added or updated tests where needed.
  • I added a Changeset when this affects a published package.
  • I updated documentation when behavior or usage changed.

`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.
@cursor

cursor Bot commented Aug 31, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Additive library API with no dependency changes; risk is limited to incorrect chunking logic, which is heavily covered by tests.

Overview
Adds chunked eth_getLogs helpers to @sei-js/precompiles: getLogsInRange, blockRanges, and MAX_GET_LOGS_BLOCK_RANGE (2000n), exported from the package root and viem entry.

getLogsInRange takes an existing viem PublicClient and walks long history in inclusive ranges that satisfy toBlock - fromBlock + 1 <= chunkSize, avoiding the common off-by-one that rejects every request. It defaults to chain head (no Ethereum-style confirmation lag on Sei), supports optional toBlock, chunkSize, filter args, and onChunk for backfill progress. blockRanges exposes the same splitting logic as a generator for planning or parallel workers.

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-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.19%. Comparing base (66deb15) to head (596ba49).

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              
Flag Coverage Δ
mcp-server 96.13% <ø> (ø)
precompiles 100.00% <100.00%> (ø)
registry 100.00% <ø> (ø)
sei-global-wallet 100.00% <ø> (ø)

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

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

The automated review did not complete; see the failing AI Review check for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants