Repository navigation
feat(wallet): shield the whole Core balance with Max - #1106
Claudius-Maginificent wants to merge 4 commits into
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
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. Comment |
|
🕓 Review not started yet because this PR is a draft.
Commit e473f30. Normal review starts when eligible; priority review starts as soon as a slot is available. |
|
Blocked on dashpay/platform#5528 (open draft), the head of the stack dashpay/platform#5150, dashpay/platform#5207, dashpay/platform#5210, then #5528 (all open). The temporary pin 🤖 Co-authored by Claudius the Magnificent AI Agent |
Max on a Core-to-shielded send no longer shields an estimated amount. It runs the upstream pooled wallet drain, which moves everything minus fees with the amount fixed only once the funding transaction is built, so Max can no longer come up short of its own fees or leave dust behind. - The drain's operation id is stored in the DET k/v sidecar, scoped to the wallet and network, before the transfer starts. A crash or restart at any point leaves the id to look the transfer up by; it is removed only once the transfer has an outcome, and a second Max transfer waits for the first to settle. - A read-only drain preview refuses a wallet that cannot cover the fees or holds too many coins for one transfer before anything is stored, and sets the lock floor so a drain too small to pay the pool fee is abandoned before broadcast. - The Shielded tab looks a stored transfer up on arrival and, while it is unfinished, says so and offers "Finish now", which resumes the same drain and never builds a second funding lock. The status read moves no funds and stays available when shielded sending is not. - The send form and confirmation say Max moves the whole balance; any edit of the amount returns to an exact-amount send. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 967f947)
A status read that landed between storing the Max transfer's id and registering its drain found nothing upstream, reported "not sent" and dropped the id of a drain about to move the whole balance. The drain is now registered first and the registration is held until its outcome is read, so such a read sees a running transfer. - Finish no longer previews the wallet: upstream sets the drain's lock floor from the pool fee and ignores the caller's, so DET passes none and drops the dead floor helper. - A new read-only QuoteShieldMaxFromCore task returns the drain's own balance, fees and expected shielded amount for the confirmation. - The drain-run state derivation is a pure function, and the orchestration (status before the run, an unrun transfer, a second transfer, nothing to finish, an unreadable record) is tested on an offline context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Max confirmation showed the form's estimate, which covers BIP44 account 0 only, while the drain spends BIP32 and DashPay receiving funds too, and it showed no fee. Sending a Max transfer now fetches the drain's read-only quote first and opens the confirmation with the balance it spends, the fees and the expected shielded amount. A quote that arrives after the amount changed opens nothing. The Max choice is also forgotten on a wallet or network switch and on a form reset, so a value chosen for one wallet can no longer drain another. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the Shield flow the destination is the own-pool marker, which has no address, so the Max confirmation read "to the shielded address ." and then asked the user to check that destination. The exact-amount confirmation had the same gap. Both now say "into your own shielded balance" for the marker, and keep the address wording for a typed one. The Max quote could also go stale while the dialog stayed open: the drain spends whatever is spendable when it is built. ShieldMaxFromCore now carries the balance the user confirmed, and the pre-run preview refuses a different one with ShieldedMaxBalanceChanged. The figure is worded as the spendable Core balance, since CoinJoin and unconfirmed funds are not moved. - The registration docs say what the code does: the registration is held until the drain returns and is dropped before the outcome is read. - New tests: - the literal confirmation sentences; - a late quote after a wallet or network change; - a drain that fails before its lock, run with a registered handle; - a refused second transfer leaving no registration behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a9228c9 to
e473f30
Compare
|
Rebuilt on top of #1028 ( Still blocked: it depends on #1028, which waits for the platform stack #5150 → #5207 → #5210 → #5528 to merge into v5.1-dev. 🤖 Co-authored by Claudius the Magnificent AI Agent |
TL;DR: "Max" on a send from Core to the shielded pool moves the whole Core balance minus fees. An interrupted transfer can be finished from the Shielded tab. Stacked atop #1028 (
fix/orphaned-tx). Blocked on dashpay/platform#5528, which #1028 pins.User story
As a wallet user, I want Max on a Core-to-shielded send to shield everything I have, so that no dust is left behind and the send never fails for being a few duffs short of its own fees.
Scenario
Actual behavior
Max shields an estimated amount. The estimate can come up short of the real fees, or leave a small remainder in Core.
Expected behavior
Max shields the whole balance minus fees. The amount is fixed only when the funding transaction is built. If the app closes mid-transfer, the Shielded tab says the transfer is unfinished and offers "Finish now". That resumes the same transfer without paying twice.
Detailed discussion
Stacked atop #1028, which pins all Platform dependencies to
344e110a6, the head of dashpay/platform#5528 (stack #5150 → #5207 → #5210 → #5528). #5528 is the first point whereSqlitePersisterattests the wallet-drain persistence contract. This PR changes no dependency. It merges after #1028, once #5528 is onv5.1-dev.docs/user-stories.md.What moved to #1028: two of this PR's former commits.
85077af5b, the adaptation to the drain-capable stack: the conflicted asset-lock status and theAssetLockFundingConflictederror. fix(wallet): surface unresolved Platform funding transfers #1028 carries it as a cherry-pick, because it does not compile at the pin without it. The boxedWalletStorageErroris already on the base (c1f1a5ae71).fef5d6e0b, which ran the newer wallet migrations over bridged profiles. It is dropped: fix(wallet): surface unresolved Platform funding transfers #1028's own bridge stages the V019 reference schema and runs V020/V021 over the translated rows.This PR now carries only the feature.
TODO(max-drain-1107): #1107 (
feat/wallet-max-send-limits) makes the Core→Shielded Max a backend-quoted asset-lock ceiling, while this PR makes an untouched Max a whole-balance drain. Whichever lands second must merge them, so that one component owns the button: #1107's quote and limits, with the drain where Max is untouched.Testing
--all-features --lib --tests -- -D warningsis clean.🤖 Co-authored by Claudius the Magnificent AI Agent
🤖 Generated with Claude Code
https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q