Skip to content

fix(dashpay): classify DIP-15 contact payments by the payment record, not net change#830

Merged
QuantumExplorer merged 3 commits into
swift-sdk-integrationfrom
fix/dashpay-payment-tx-direction
Jul 16, 2026
Merged

fix(dashpay): classify DIP-15 contact payments by the payment record, not net change#830
QuantumExplorer merged 3 commits into
swift-sdk-integrationfrom
fix/dashpay-payment-tx-direction

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 16, 2026

Copy link
Copy Markdown
Member

Summary

Paying a contact rendered on the home transaction list as "Received +1.2" (QA repro: two payments to a contact showed as incoming for exactly the paid amounts, while the contact profile sheet correctly showed them as sent).

Root cause: dash-spv's net-change view watches the jointly-derived DIP-15 payment address, so the outgoing payment output counts as wallet-owned while the spent inputs aren't attributed — an outgoing contact payment reads as a phantom incoming +amount. On-chain the txs are normal sends (verified via testnet explorer: payment output + our change). The contact sheet was already correct because it reads the Rust DashPay payment history (PersistentDashpayPayment).

Fix: give the tx list the same source of truth. New DashPayPaymentTxLookup (same overlay shape as ShieldedTxLookup): txid → recorded direction/amount/counterparty, mirrored from PersistentDashpayPayment, refreshed on every contacts snapshot rebuild (covers launch) and payments projection. Transaction prefers it for direction, dashAmount, and live fiat — so a recorded contact payment always renders as Sent −amount.

Not fixed here (upstream): the SPV-side classification itself — and whether the watched DIP-15 payment address inflates the transparent balance — needs a dash-spv / rs-platform-wallet look. Filed as follow-up.

Row identity (second commit)

Contact-payment rows also get the counterparty's identity: title reads "Sent to " / "Received from " (profile displayName from the cached PersistentDashpayContactProfile, joined into the lookup at refresh) and the row icon becomes their avatar via the shared ContactAvatarView (profile image, or the deterministic initial placeholder the contacts screens use). No cached profile → generic Sent/Received + "?" placeholder.

Test plan

  • Clean dashpay simulator build (arm64); installed on QA-iPhone16
  • Home list shows the two QA contact payments as Sent −1.0 / −1.2 (matching the contact sheet)
  • Incoming DashPay payment still renders as Received (direction 1 record)

🤖 Generated with Claude Code

… not net change

Paying a contact showed up on the home list as "Received +amount":
dash-spv's net-change view watches the jointly-derived DIP-15 payment
address (so the outgoing payment output reads as wallet-owned) while
the spent inputs aren't attributed, flipping an outgoing payment into a
phantom incoming one. The contact profile sheet was already right — it
reads the Rust payment history (PersistentDashpayPayment), which
records the true direction and amount at send/sync time.

Give the tx list the same source of truth: a DashPayPaymentTxLookup
overlay (txid → direction/amount/counterparty, same shape as
ShieldedTxLookup) mirrored from PersistentDashpayPayment, refreshed on
every contacts snapshot rebuild and payments projection. Transaction
prefers it for direction, on-screen amount, and live fiat.

The SPV-side misclassification (and any balance effect of the watched
payment address) still deserves an upstream dash-spv/rs-platform-wallet
look — this makes every app surface agree with the recorded payment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 323e5374-fafa-445a-aaa2-2136f9599328

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dashpay-payment-tx-direction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

QuantumExplorer and others added 2 commits July 17, 2026 02:33
The home list's DashPay payment rows now read "Sent to <name>" /
"Received from <name>" and show the counterparty's avatar (profile
image, or the contacts screens' deterministic initial placeholder via
the shared ContactAvatarView). The lookup joins the counterparty's
cached PersistentDashpayContactProfile (displayName + avatarUrl) at
refresh time; rows without a cached profile fall back to the generic
Sent/Received title and a "?" placeholder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…titles

Setting a contact's alias (or note / hidden) died with the bogus
"Contact request not found": mutateEstablishedContact resolves the live
EstablishedContact from the SDK's per-session in-memory state, while
the contact the sheet renders comes from SwiftData — the same trap
acceptContactRequest had. Same cure: on a miss, run a DashPay sync pass
and retry the lookup once (the setters and the profile sheet's save
paths go async for it).

The payment rows then honor the alias: title reads "Sent to <alias>"
(alias joined from the contact-request rows, winning over the profile
display name — matching ContactItem.displayTitle), with the contact's
actual name underneath on the gray details line, and the avatar
placeholder keyed by the same title.

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

1 participant