Skip to content

fix(wallet): gate sends on what the funding pool can actually spend - #1107

Draft
romchornyi wants to merge 1 commit into
developfrom
fix/gate-sends-on-the-pooled-balance
Draft

fix(wallet): gate sends on what the funding pool can actually spend#1107
romchornyi wants to merge 1 commit into
developfrom
fix/gate-sends-on-the-pooled-balance

Conversation

@romchornyi

Copy link
Copy Markdown
Contributor

Part of the CoinJoin fix set for support ticket 32081. See the merge order at the bottom.

Issue being fixed or feature implemented

SendAmountModel.canShowInsufficientFunds compared against balance.spendable — the whole wallet's
confirmed balance, every funding account, CoinJoin included. A send draws on SEND_FUNDING_SOURCES:
BIP44@0, BIP32@0 and the DashPay receiving accounts, with CoinJoin deliberately excluded (spending
mixed outputs alongside transparent ones undoes the mixing).

So the screen accepted amounts the builder then refused, and the user found out only after committing:

insufficient unreserved core funds across the pooled funding sources
[BIP44, BIP32, AllDashpayReceivingFunds]: available Some(538503), required Some(100000000)

Support ticket 32081: 94 DASH on screen, 0.0054 actually spendable, the rest mixed. 538503 duffs
is exactly the transparent balance, unchanged across three days of the user trying.

What was done?

The gate and Max now read pooledSpendableDuffs, refreshed from the SDK on every balance event. The
SDK computes it with the same account resolution the builder uses (dashpay/platform#4582), so the app
does not mirror the pooling rule.

The mirror is what drifted here: the comment in SwiftDashSDKTransactionSender still claims
.allSpendable pools "the same set the home balance already totals" — it never did, and that belief
is why this shipped.

Max is included deliberately. Leaving it on the wallet-wide figure keeps a button that fills in an
amount guaranteed to fail.

Reservations are still not subtracted; the SDK cannot read them yet, and that part is transient.

How Has This Been Tested?

Builds and runs against the SDK carrying the new call. Not yet verified against a live CoinJoin-heavy
wallet
— the testnet wallet built for this work has since been swept, so the state must be rebuilt to
watch the ceiling change. The arithmetic is covered on the SDK side.

Breaking Changes

None in API terms, but user-visible: anyone holding CoinJoin funds will see a lower ceiling and a
smaller Max than today. That is the point — those funds were never spendable by a plain send — but it
is why the merge order below matters.

Merge order

This must not land before the sweep fix. On its own it lowers what the user is offered without
giving them a way to unlock the difference, which is strictly worse than today.

  1. feat(key-wallet): let a build fund from only the inputs it was given rust-dashcore#994 — merged
  2. feat(platform-wallet): let a Core build fund from only the inputs it was given platform#4548fix(coinjoin): make the sweep's input chunking actually bound the transaction #1094 — the sweep above 500 UTXOs
  3. fix(coinjoin): offer the shielded destination from every move-funds surface #1090 — the shielded destination
  4. feat(platform-wallet): report the balance a pooled build can actually spend platform#4582this PR

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

The amount screen compared against `balance.spendable`, which is the whole
wallet's confirmed balance — every funding account, CoinJoin included. A send
draws on `SEND_FUNDING_SOURCES`: BIP44@0, BIP32@0 and the DashPay receiving
accounts, with CoinJoin deliberately out. So the screen accepted amounts the
builder then refused, and the user learned it only after committing.

Support ticket 32081: 94 DASH on screen, 0.0054 actually spendable, the rest
mixed. Entering 1 DASH gave "insufficient unreserved core funds … available
Some(538503), required Some(100000000)" — 538503 duffs being exactly the
transparent balance.

Both the gate and Max now read `pooledSpendableDuffs`, which the SDK computes
with the same account resolution the builder uses (dashpay/platform#4582) rather
than the app mirroring the pooling rule. The mirror is what drifted: the comment
in SwiftDashSDKTransactionSender still claims `.allSpendable` pools "the same set
the home balance already totals", and it never did.

Max is included deliberately. Leaving it on the wallet-wide figure would keep a
button that fills in an amount guaranteed to fail.
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

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.

❤️ Share

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

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.

2 participants