Repository navigation
Description
Summary
Suppose we send a DashPay contact request and the contact never sends one back. That contact never gets a DashpayReceivingFunds account in the SDK wallet. Our receiving chain for that contact is never watched, and any payment the contact sends us on it is invisible at every rescan depth.
DIP-15 puts our receiving xpub inside the contact request we send. A contact who has our request can therefore pay us whether or not they ever reciprocate. platform-wallet assumes the opposite in two places.
Where
- Account building.
collect_account_build_candidates(packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs) walksestablished_contacts()only. A contact that exists only insent_contact_requests()is never a candidate, soRegisterReceivingis never queued for it. - Rescan.
reconcile_dashpay_rescan(.../network/payments.rs) skips every receival account whose contact is not inestablished_contacts(). Even an account built some other way would get no backfill for the blocks before it was registered.
The live send path (send_contact_request_with_external_signer) registers the receiving account itself, so a wallet that sends the request in the current session is fine. The gap appears when the sent request is learned from Platform by the contact sweep (restore from seed, a second device, a migrated wallet), or when the live registration failed after the request had been saved.
A contact who only sent us a request is not affected: they never received our xpub, so there is no chain of ours for them to pay.
Example (testnet, topple wallet 7dc06ad3…)
- Tx
e5169bfc4989585abd4b0476188611b981e3c750539da5b8a39fe135e3bbb957, height 1,475,820: 0.001 DASH toyMNNc2UZz62V9N7SZfQnsk79G5okCP7eSN. That is our receiving chain15'/0'/(us)/(4193bbe6…)/0for contact5QzA7GnST…. - The SDK store holds one contact request for that contact: ours, at core height 1,475,801, 19 blocks before the payment. It has no incoming request and no DashPay account rows for the contact.
- The transaction is missing from every archived SDK store of this wallet (about 40 builds, int6 through int27). dashj's wallet dump has it and watches the same chain.
- Its later spend,
6ac8356c…, is stored withnetAmount = +99734instead of minus the fee, because the funding transaction is unknown. - The balance is correct today only because that coin has since been spent. An unspent payment on such a chain would be missing money.
- Two more contacts on the same wallet (
c7037829…,ce3cff2f…) are in the same one-way state.
The host's dark-contact diagnostic cannot see this case either: it lists established contacts that lack receival accounts, and these contacts are not established.
Expected
For every contact we have sent a request to, the wallet should register our DashpayReceivingFunds account (the external account still needs their request) and rescan from the height of our request.
Related
- platform-wallet: restored wallets miss historical DashPay contact payments — no registration-time rescan, and failed receival-account builds are never re-enqueued #4475: the same symptom for established contacts whose receiving build failed. Its last comment notes this case as a separate issue.
- fix(platform-wallet): rescan DashPay contact accounts from the contact request height #4740: makes the rescan cover sent-only receival accounts once they exist, but does not create them.
- fix(platform-wallet)!: persist DashPay coreHeight backfill coverage so a relaunch resumes instead of rewinding again #5026 / Persist DashPay coreHeight backfill completion (rescan_triggered is in-memory only) #4302: persists backfill coverage; its rescan still considers only established contacts.
- platform-wallet: restore the deferred DashPay contact-crypto queue on load #5091: the deferred contact-crypto queue is not restored on load, so the sweep's candidate list is the only thing that re-queues these builds after a restart.
Activity
HashEngineering commentedon Nov 4, 2026
Fixed by #4740 (merged into v5.1-dev on 2026-10-27), which builds a
DashpayReceivingFundsaccount for every contact holding our request, including one-way contacts, and backfills from our earliest sent request. #5256 was closed as superseded.Verified on device with a kotlin-sdk build (5.1.0-int33) based on v5.1-dev with #4740 and without #5256: on a Wallet A restore,
e5169bfc…appears at height 1,476,252 (+100000), the spend6ac8356c…shows −266 instead of +99734, and all three one-way contacts have receiving accounts.🤖 Posted by Claude Code