Skip to content

feat(address): optimize tx search with eth_getTransactionBySenderAndNonce#219

Merged
AugustoL merged 1 commit intoopenscan-explorer:release/v1.2.1-afrom
AugustoL:feat/nonce-tx-lookup
Feb 12, 2026
Merged

feat(address): optimize tx search with eth_getTransactionBySenderAndNonce#219
AugustoL merged 1 commit intoopenscan-explorer:release/v1.2.1-afrom
AugustoL:feat/nonce-tx-lookup

Conversation

@AugustoL
Copy link
Collaborator

Description

Optimize address transaction search on Ethereum mainnet using reth's eth_getTransactionBySenderAndNonce RPC method. This reduces sent-tx discovery from ~50+ binary search RPC calls to exactly N calls (where N = address nonce). Received/internal transactions are found via gap-based binary search between sent tx blocks.

Related Issue

Closes #211

Type of Change

  • New feature
  • Bug fix
  • Documentation update
  • Refactoring
  • Performance improvement
  • Other (please describe):

Changes Made

  • src/config/rethProviders.ts (new): 3 hardcoded reth RPC provider URLs with race strategy, NONCE_LOOKUP_CHAIN_ID = 1, singleton getRethClient() factory
  • src/services/NonceLookupService.ts (new): Service wrapping eth_getTransactionBySenderAndNonce with isAvailable(), fetchSentTransactions(), and fetchRecentSentTransactions()
  • src/services/AddressTransactionSearch.ts:
    • Optional NonceLookupService injection in constructor
    • searchWithNonceLookup() with 2-phase approach: nonce lookups → gap search
    • Interleaved progressive delivery (newest-to-oldest) for correct table ordering
    • Exponential narrowing for large gaps to avoid slow archival RPC queries
    • Fixed address case normalization for consistent cache hits across all search paths
  • src/services/adapters/NetworkAdapter.ts: initTxSearch() accepts optional NonceLookupService
  • src/services/adapters/EVMAdapter/EVMAdapter.ts: Creates NonceLookupService with reth client for chain ID 1 only
  • src/locales/en/address.json + src/locales/es/address.json: 2 new i18n progress message keys

Key design decisions

  • Separate reth client: Nonce lookups go to dedicated reth providers (race strategy); receipts/blocks go to user's configured RPC
  • Graceful fallback: If eth_getTransactionBySenderAndNonce is unavailable or fails, binary search runs unchanged
  • No behavior change for non-mainnet: NonceLookupService is only injected for chain ID 1
  • Interleaved delivery: Segments are processed newest-to-oldest (gap → sent → gap → sent → ...) so each onTransactionsFound call appends in correct chronological order
  • Gap search skips pre-first-sent-tx range: Avoids searching the massive [0, firstSentBlock] gap that would hit non-archival RPCs for ancient blocks

Checklist

  • I have run npm run format:fix and npm run lint:fix
  • I have run npm run typecheck with no errors
  • I have run tests with npm run test:run
  • I have tested my changes locally
  • I have updated documentation if needed
  • My code follows the project's architecture patterns

Additional Notes

Tested manually on Ethereum mainnet addresses with mixed sent/received transactions. Verified correct chronological ordering, progressive table population, and graceful fallback when reth providers are unavailable.

🤖 Generated with Claude Code

…once

Use reth's eth_getTransactionBySenderAndNonce RPC method for O(N) sent
transaction discovery on Ethereum mainnet (chain ID 1), where N is the
address nonce. Falls back gracefully to existing binary search if the
method is unavailable.

- Add dedicated reth provider config with 3 public reth RPCs (race strategy)
- Add NonceLookupService for batched nonce-to-tx lookups
- Search gaps between sent tx blocks for received/internal txs using
  adaptive segmentation with exponential narrowing for large gaps
- Progressive interleaved delivery: walk newest-to-oldest alternating
  gap searches and sent tx delivery for correct chronological order
- Fix address case normalization for consistent cache hits

Closes openscan-explorer#211

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

🚀 Preview: https://pr-219--openscan.netlify.app
📝 Commit: 7adef3ca91a20801ac6f95ea158faeedb60b0190

@josealoha666
Copy link

Performance Report — Clean Cache Test

Address tested: 0x072f41AE99F250bD48835a88529309cbc16f59Ef

Method: Cleared localStorage, sessionStorage, and IndexedDB on both versions before testing. Used in-app hash navigation from a different route to ensure no cached transactions. Polled DOM every 500ms to track when transactions appeared.

Transaction Loading Timeline

PR-219 (7adef3c):

0.0s → Searching...
11.0s → 1st tx visible
15.0s → 3 txs
16.5s → 5/5 txs ✅ search complete
ETH.link (11d8240):

0.0s → Searching...
13.5s → 1st tx visible
18.0s → 2 txs
19.5s → 3 txs
22.5s → 4 txs
24.5s → 5/5 txs ✅ search complete
Summary

Milestone PR-219 ETH.link Delta
First tx visible ~11s ~13.5s 2.5s faster
All 5 txs found ~16.5s ~24.5s 8s faster
Total search time 16.5s 24.5s 33% faster
Observations
  1. PR-219 is ~33% faster overall for transaction discovery on a clean cache.
  2. First result appears 2.5s earlier on PR-219, improving perceived performance.
  3. Discovery pattern differs: PR-219 finds transactions in larger batches (1→3→5), while ETH.link discovers them one by one (1→2→3→4→5). This suggests PR-219 has a more efficient block scanning strategy.
  4. Both versions are slow without cache (16-25s for 5 txs). With warm cache both load in ~2s.
  5. Minor bug on both versions: Footer text shows "Showing 5 transactionShowing 5 transactionss" (duplicated/malformed string).
    — Jose 🤖

@AugustoL AugustoL requested a review from MatiasOS February 12, 2026 14:55
Copy link
Member

@MatiasOS MatiasOS left a comment

Choose a reason for hiding this comment

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

Tested some directions and it improved the times and UX

@AugustoL AugustoL merged commit b91534d into openscan-explorer:release/v1.2.1-a Feb 12, 2026
2 checks passed
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.

3 participants