Skip to content

feat(ui): shielded operations in the home transaction history#838

Merged
QuantumExplorer merged 4 commits into
swift-sdk-integrationfrom
feat/shielded-history-merge
Jul 17, 2026
Merged

feat(ui): shielded operations in the home transaction history#838
QuantumExplorer merged 4 commits into
swift-sdk-integrationfrom
feat/shielded-history-merge

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 16, 2026

Copy link
Copy Markdown
Member

Problem

The home history reads only the Core SwiftData rows, so purely shielded operations were invisible. Private receives, private sends, Platform → Shielded moves, and shielded identity fundings never appeared — the list showed at most the L1 legs of shielded flows.

What this does

Merges the SDK's PersistentShieldedActivity timeline (live-recorded per operation, scan-derived on restore) into the same day-grouped history list as the Core rows.

  • ShieldedActivityItem (ShieldedActivityHistory.swift): immutable projection of one activity row — credits→duffs, kind/direction/status, decoded 36-byte text memo — plus row display: kind title, route pill ("Platform → Shielded" / "Shielded → Platform" / "Shielded", memo wins when present), shield icon with a per-kind corner badge, signed amount, live fiat, Pending/Failed trailing status.
  • No double rows: kinds whose Core leg already renders are not projected — ShieldFromAssetLock (shows as the Core asset-lock row via Transaction.isShieldedTransfer), and Withdrawal only when its destination script is one of the active wallet's own Core addresses (the Core receipt via isShieldedWithdrawalReceipt). A withdrawal to an EXTERNAL Core address produces no wallet-side Core transaction, so it stays in the merged list as a signed Sent row with the destination address (shortened in the row pill, full in the detail sheet). Entries are also deduped by entryId across accounts (an intra-wallet transfer writes Sent + Received rows for one operation; the initiating side wins).
  • Pipeline: new TransactionListDataItem.shieldedActivity case flows through the existing filter → sort → day-group pipeline. The NSManagedObjectContextDidSave reload trigger already fires on the shielded persister's saves, so rows appear live.
  • Filters: the existing "Shielded sent" / "Shielded received" filter categories now match these rows (previously they only matched Core legs).
  • Detail: tapping opens ShieldedActivityDetailsView in the standard bottom sheet — type, status, block, exact fee when recorded, copyable created-identity id, date, and the honest restore-path footnote for unclassifiable ShieldedSpend residuals. Deliberately simpler than TXDetailVC: a shielded entry has no addresses or inputs/outputs to enumerate — that's the point of it.

Test plan

  • dashpay arm64-sim build green; installed on QA-iPhone16 (whose store holds 1 shielded receive, 2 Platform→Shielded moves, 3 private sends, and 2 asset-lock entries that correctly stay represented by their Core rows)
  • Home history shows the shielded rows interleaved by day; asset-lock shieldings/withdrawals appear once (as their Core rows), not twice
  • Filter → Shielded sent / Shielded received matches the new rows
  • Tap a shielded row → detail sheet; identity-funding entry shows the copyable identity id

🤖 Generated with Claude Code

QuantumExplorer and others added 4 commits July 17, 2026 05:54
The home history read only the Core SwiftData rows, so purely shielded
operations — private receives and sends, Platform↔Shielded moves,
shielded identity fundings — were invisible: the list showed at most
the L1 legs. Merge the SDK's PersistentShieldedActivity timeline into
the same day-grouped list.

- ShieldedActivityItem: immutable projection of one activity row
  (credits→duffs, kind/direction/status enums, decoded text memo),
  with row display (title, route pill, shield icon + kind badge,
  signed amount, live fiat, Pending/Failed trailing status).
- Kinds whose Core leg already renders are NOT projected —
  ShieldFromAssetLock (Transaction.isShieldedTransfer row) and
  Withdrawal (Transaction.isShieldedWithdrawalReceipt row) — so one
  user action never shows as two rows; entries are deduped by entryId
  across accounts (outgoing side wins).
- New TransactionListDataItem.shieldedActivity case flows through the
  existing sort/group pipeline; the NSManagedObjectContextDidSave
  reload trigger already covers the shielded persister's saves.
- The Shielded sent/received filter categories now match these rows
  (previously they only matched the Core legs).
- Tapping opens ShieldedActivityDetailsView in the standard bottom
  sheet — deliberately simpler than TXDetailVC since a shielded entry
  has no addresses/inputs/outputs to enumerate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Platform↔Shielded moves carry the shielded pool's perspective in
their recorded direction (a shield is "incoming"), but no funds were
gained or lost — show them unsigned like self-transfers, in the row
and the detail sheet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Platform↔Shielded row is self-originated by construction — the
shield/unshield kinds are only ever live-recorded for this wallet's
own operation; the restore-path scan can't prove origin and never
labels anything a shield (an old own-shield resurfaces as plain
Received). Surface that guarantee as explicit From/To rows ("Your
Platform balance" → "Your Shielded balance") in the detail sheet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The withdrawal kind was excluded from the merged history on the
assumption its Core receipt row always represents it — true only when
the destination is one of the active wallet's own addresses. A
withdrawal to an EXTERNAL Core address produces no wallet-side Core
transaction at all, so the operation vanished from the timeline
entirely (QA repro: a 2.45 DASH withdrawal to yQRyiz… was invisible).

Fetch now decodes the entry's counterparty script and keeps the entry
unless that address belongs to the active wallet's Core pools
(active-wallet scoped on purpose: another on-device wallet's address
is still external from this wallet's perspective). External
withdrawals render as signed Sent rows with the shortened destination
in the pill and the full address as the detail sheet's To row;
undecodable-script withdrawals are shown rather than hidden — losing
money from the timeline is the worse failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer merged commit 6c5e157 into swift-sdk-integration Jul 17, 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.

1 participant