Repository navigation
feat(web): transaction history + dashboard activity (#403) - #501
Merged
davedumto merged 5 commits intoSep 30, 2026
Merged
Conversation
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`.
|
@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. |
…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.
|
@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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
Newest-first ordering is produced by sorting the merged two-rail stream, not by the RPC, which ignores
order.