Skip to content

feat(address): on-demand backfill for Calls-tab block-range gaps - #86

Merged
amanusk merged 2 commits into
masterfrom
feat/calls-tab-gap-backfill
Jun 29, 2026
Merged

feat(address): on-demand backfill for Calls-tab block-range gaps#86
amanusk merged 2 commits into
masterfrom
feat/calls-tab-gap-backfill

Conversation

@amanusk

@amanusk amanusk commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Problem

The address Calls tab shows old cached calls immediately on re-entry (good), but the fresh fetch only fills the recent window — Dune starknet.calls caps at 500, the pf event window at 200. An active address revisited after a few days ends up with recent calls on top and stale cached calls at the bottom, separated by an unfetched block range the UI never surfaced and the user couldn't fill. The Transactions tab already solves the equivalent problem for nonce gaps; this brings the same UX to Calls, keyed on block ranges.

What this does

  • Detect a gap wherever two consecutive (block-sorted) cached calls are more than CALL_GAP_SPAN_BLOCKS (10k) apart, and render an interactive row:
     ── calls gap (blocks 1005000..1200490) — press Enter to load ──
    
  • Fill lazily on Enter — one capped page over the gap range, newest-first, so the gap shrinks from its newer edge and a residual row remains to press again (mirrors the tx gap fill). Contracts use the bounded query_contract_calls_windowed; accounts use the pf event window's FillGap policy. Results flow through the existing AddressCallsMerged / cache-persist path.
  • Gap-aware navigation for the Calls list (j/k, Ctrl+D/U, g/G clamp on gap rows), reusing the existing static gap-index helpers.

Caching gaps as "closed" (so they don't re-appear)

  • Gaps filled with real calls self-close for free: the fetched calls persist to address_calls and bridge the hole on the next visit.
  • Gaps that turn out genuinely sparse (scanned, no calls to cache) are recorded in a new address_call_scanned_ranges table and loaded before first paint, so detection skips them across re-navigation and restarts. Interval coalescing is shared between the in-memory tracker and the SQLite layer (utils::merge_block_interval).

Verification

  • cargo build + cargo build --release clean.
  • cargo test — full suite green (263 lib + integration tests, 0 failed). New coverage: call-gap detection/threshold/nav, scanned-range suppression, and a cache.db round-trip/coalesce test.
  • cargo fmt applied; no new clippy warnings.
  • The bounded BETWEEN from AND to fill SQL was run against production Dune (public STRK token, a finite block window) — parses, runs, and returns newest-first rows as the lazy-chunk fill expects.

The live TUI gap-row interaction itself was not driven in a terminal (covered by unit tests + the SQL check).

🤖 Generated with Claude Code

The Calls tab showed recent calls and older cached calls with no
indication of the unfetched block range between them — the Dune
`starknet.calls` fetch caps at 500 and the pf event window at 200, so an
active address revisited after a few days has a hidden hole. Mirror the
Transactions tab's nonce-gap feature, keyed on block ranges:

- Detect block-span holes wider than CALL_GAP_SPAN_BLOCKS between cached
  calls and render an interactive gap row. Enter backfills that window
  newest-first (lazy chunking) via Dune `query_contract_calls_windowed`
  (contracts) or the pf event window `FillGap` (accounts), merging the
  result so the gap shrinks from its newer edge.
- Persist fully-scanned ranges (new `address_call_scanned_ranges` table)
  so a genuinely-sparse hole stays closed across re-navigation and
  restarts; ranges load before first paint. Gaps filled with real calls
  self-close via the existing call cache.

Adds gap-aware Calls-list navigation that reuses the existing static
gap-index helpers and the calls fetch/merge/persist plumbing, plus a
shared block-interval coalescing helper (utils::merge_block_interval).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Adds on-demand “gap rows” to the Address Calls tab to surface large block-range holes between cached calls and lazily backfill them (mirroring the Transactions nonce-gap UX). It also persists “scanned but sparse” call ranges in SQLite so gaps that prove empty don’t reappear across re-navigation/restarts.

Changes:

  • Detect block-range call gaps (threshold-based), render interactive gap rows, and make Calls list navigation gap-aware.
  • Implement a network task to fill a selected call gap and merge results into the existing calls/cache pipeline.
  • Persist fully-scanned call ranges (address_call_scanned_ranges) and coalesce intervals (shared in-memory + SQLite).

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils.rs Adds merge_block_interval interval coalescing helper used by scanned-range tracking.
src/ui/views/address_info.rs Renders Calls-tab gap rows and switches Calls list rendering to a gap-aware ListState.
src/network/mod.rs Wires the new FillAddressCallGap action into the network task runner/cancellation set.
src/network/event_window.rs Documents FillGap policy usage for Calls-gap filling.
src/network/address.rs Implements run_call_gap_fill and loads persisted scanned ranges before first paint.
src/data/mod.rs Extends DataSource with load/add APIs for persisted scanned call ranges.
src/data/cache.rs Adds address_call_scanned_ranges table + read/write/coalesce logic + a persistence test.
src/app/views/address_info.rs Adds call-gap state, detection, selection, and gap-aware navigation helpers + tests.
src/app/mod.rs Dispatches call-gap fills on Enter and refreshes call-gap detection after merges/scans.
src/app/input.rs On Enter in Calls tab, dispatches a call-gap fill when a gap row is selected.
src/app/actions.rs Adds actions for filling call gaps and for scanned-range load/record events.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/network/address.rs
Comment thread src/network/event_window.rs
Comment thread src/ui/views/address_info.rs
…ck-loading

Addresses three issues in the on-demand Calls-tab gap fill flagged in review:

- FillGap pf path ignored the upper bound: `fetch_address_activity` hardcoded
  `to_block = None`, so a `[lo, hi]` gap fill scanned `[lo, head]` newest-first
  and never converged on its range. Thread `to_block` through into
  `get_events_for_address`/`get_contract_events` (which already accept it).

- Contract-without-Dune used the keyed `transaction_executed` (`Account`)
  filter, which targets an address as a tx sender — wrong for a contract.
  Pick the EventQueryKind from `is_contract`.

- A failed/no-op fill left the gap row stuck on "loading": `r` (full refresh)
  preserves `fill_dispatched` for an unchanged range, so Enter could never
  re-dispatch. A drop guard now emits `AddressCallGapFillFinished` on every
  exit path of `run_call_gap_fill`; the reducer clears `fill_dispatched` so the
  row becomes re-dispatchable. Drop the misleading "press r to retry" text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread src/data/cache.rs
@amanusk
amanusk merged commit 76da11a into master Jun 29, 2026
3 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.

2 participants