Skip to content

feat(web): transaction history + dashboard activity (#403) - #501

Merged
davedumto merged 5 commits into
Vellar-Wallet:devfrom
Orah-dev:feat/403-transaction-history
Sep 30, 2026
Merged

davedumto merged 5 commits into
Vellar-Wallet:devfrom
Orah-dev:feat/403-transaction-history

Conversation

@Orah-dev

@Orah-dev Orah-dev commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Closes #403

History is read from Soroban RPC getEvents as the primary rail, with Horizon's operations endpoint as a secondary rail for classic G-accounts. Soroban/SAC transfers are contract events that Horizon does not serve — GET /accounts/{C...}/operations answers 400 for a contract address, which is exactly the account type this wallet creates — so a Horizon-only view would be empty for every real user. Verified live against testnet rather than assumed; the rationale and evidence are in the ADR.

Also adds the §5.2 account metadata rows (wallet since / last active).

Notable corrections found while verifying against testnet:

  • Topic filters on the public RPC return 0 events where matching events exist, so events are discriminated client-side by topic[0] and contract.
  • topic.length is matched as >= 3, not === 4, so transfers are not silently dropped on contracts whose topic shape differs.
  • The classic Horizon rail is gated on classic addresses; calling it for a C-address failed the whole page.
  • getEvents cursors are clamped to the node's retained ledger window.
  • The page cursor no longer uses Buffer, which is absent in the browser.
  • Failed and reverted transfers are surfaced as "failed" rows rather than dropped, since the question being asked is whether it went through.
  • An amount whose asset cannot be resolved drops the row rather than rendering a bare number.

Newest-first ordering is produced by sorting the merged two-rail stream, not by the RPC, which ignores order.

Closes the last open BUILD-PLAN Phase 2 item: the dashboard showed
balances but no history, so a user could not answer "did my payment go
through?" without leaving for an external explorer.

History is read from Soroban RPC getEvents as the primary rail, with
Horizon's operations endpoint as a secondary rail for classic G-accounts.
Soroban/SAC transfers are contract events that Horizon does not serve —
GET /accounts/{C...}/operations answers 400 for a contract address, which
is exactly the account type this wallet creates — so a Horizon-only view
would be empty for every real user. Verified live against testnet rather
than assumed; the rationale and evidence are in the ADR.

Also adds the §5.2 account metadata rows (wallet since / last active).

Notable corrections found while verifying against testnet:
- Topic filters on the public RPC return 0 events where matching events
  exist, so events are discriminated client-side by topic[0] and contract.
- topic.length is matched as >= 3, not === 4, so transfers are not
  silently dropped on contracts whose topic shape differs.
- The classic Horizon rail is gated on classic addresses; calling it for
  a C-address failed the whole page.
- getEvents cursors are clamped to the node's retained ledger window.
- The page cursor no longer uses Buffer, which is absent in the browser.
- Failed and reverted transfers are surfaced as "failed" rows rather
  than dropped, since the question being asked is whether it went through.
- An amount whose asset cannot be resolved drops the row rather than
  rendering a bare number.

Newest-first ordering is produced by sorting the merged two-rail stream,
not by the RPC, which ignores `order`.
@vercel

vercel Bot commented Sep 28, 2026

Copy link
Copy Markdown

@Orah-dev is attempting to deploy a commit to the david's projects Team on Vercel.

A member of the Team first needs to authorize it.

Orah-dev and others added 4 commits September 28, 2026 16:01
…iring

The upstream dev merge dropped the ActivityPanel import and the 5.2 metadata
rows from the dashboard, so the history panel no longer compiled. Restored
against the merged file, keeping upstream's own dashboard changes.

Three defects in the history client, all found by driving the real client
against live testnet rather than by reading it:

1. A page could render no history for a wallet that plainly had transfers.
   Server-side topic filters are unreliable on the public node, so a raw page
   is mostly `fee`/`approve` events that are discarded client-side; taking
   exactly one page meant the first page was sometimes empty.

2. Pages were labelled newest-first while showing the OLDEST history.
   getEvents({startLedger}) anchors the old end and its cursor walks forward;
   `order: "desc"` is ignored by the node. A two-day lookback window paged
   forward therefore showed the oldest transfers of that window.

3. Consecutive pages re-served rows. The resume point was derived from
   ledgerClosedAt, which does not always agree with the event stream order -
   observed a window where a newer ledger sat at the end of the stream. That
   is the security-audit L6 defect class, and it reproduced intermittently
   because it depends on where a ledger boundary lands inside a scan.

Pages now read an explicit ledger window, render it newest-first by reversing
the ascending stream, and resume from the ledger before the oldest event the
previous window consumed - so windows are disjoint and nothing is skipped. The
Soroban rail no longer trims (a trimmed row would be unreachable, since the
resume is by ledger); the classic rail trims and resumes at the paging token of
the last emitted row. Restore the ActivityPanel import and metadata rows lost
in the dev merge. ADR updated with the verification.
A history page is a ledger window, not "the next 20 transactions" — the node
offers no reverse cursor, so a page reads a whole time-boxed window and a busy
one carries hundreds of rows. The panel rendered all of them, burying the
dashboard. Bound one screen to the newest 50; "Load older" still walks back,
and hasMore now also reports when the cap is hiding rows.
…ar-Wallet#403

The live integration test already pins "page 2 does not re-serve page 1", but
only through the client. This checks it through the rendered panel too, and
exercises "Load older" when an older window exists.

The closing keyword is here because the pull request body could not be edited:
GitHub's linked-PR relationship (and the "Development" entry the wave
dashboard reads) is built from closing keywords, and this PR's body was
written without one.
@drips-wave

drips-wave Bot commented Sep 29, 2026

Copy link
Copy Markdown

@Orah-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@davedumto
davedumto merged commit 9c59ba0 into Vellar-Wallet:dev Sep 30, 2026
1 check failed
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.

Transaction history + dashboard account metadata/activity (last open Phase 2 item)

2 participants