fix(ui): keep Max transfer amount exact in fiat mode - #956
Conversation
📝 WalkthroughWalkthroughBoth payment view models preserve exact Max amounts in duffs while displaying DASH or fiat values. Unit changes reuse the stored amount, and invalid Max selections are cleared. ChangesMax amount handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@jeanpierreroma could you please review this fix? It keeps the exact Max amount in duffs when fiat display rounding would otherwise disable Continue. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift (1)
670-700: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftAdd regression tests for exact Max amount state.
The PR changes the executable amount independently from the visible fiat text. Add tests before merge.
DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift#L670-L700: test that fiat Max, unit changes, and manual edits retain or clearmaxAmountDuffsas required.DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swift#L836-L866: test the same Max-state contract for internal transfers.Use a Max value whose fiat representation rounds before conversion. Assert that
dashDuffsUnsignedremains the original duff amount until a manual edit occurs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift` around lines 670 - 700, Add regression tests covering the exact Max amount state in SendViewModel.swift (lines 670-700) and InternalTransferViewModel.swift (lines 836-866): use a Max value whose fiat display rounds, verify fiat Max selection and unit changes preserve the original maxDuffs and dashDuffsUnsigned, and verify manual edits clear that state while retaining the edited amount behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@DashWallet/Sources/UI/Payments/Pay/SendViewModel.swift`:
- Around line 670-700: Add regression tests covering the exact Max amount state
in SendViewModel.swift (lines 670-700) and InternalTransferViewModel.swift
(lines 836-866): use a Max value whose fiat display rounds, verify fiat Max
selection and unit changes preserve the original maxDuffs and dashDuffsUnsigned,
and verify manual edits clear that state while retaining the edited amount
behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f77f9c17-6f40-4525-b7d5-4f3dc76e5a1d
📒 Files selected for processing (2)
DashWallet/Sources/UI/Payments/InternalTransfer/InternalTransferViewModel.swiftDashWallet/Sources/UI/Payments/Pay/SendViewModel.swift
romchornyi
left a comment
There was a problem hiding this comment.
Approving — the approach is right and the change is minimal.
I pulled the branch and read both view models end to end. The key invariants hold:
- Manual edits clear Max.
amountText.didSetcallsclearMaxSelection(), andisApplyingMaxis only ever true insidefillMaxFromWalletand theunitdidSet, so the exact amount can't survive a user edit. - No stale Max across sources.
source/destinationchanges route throughrouteDidChange()→clearMaxSelection()(SendViewModel.swift:262,InternalTransferViewModel.swift:312), so a Max taken from Core can't leak into a Platform or Shielded send. - Balance gates are not bypassed.
canContinuestill checksdashDuffsUnsigned <= coreBalanceDuffsforcoreToCoreand the partial-withdrawal cap forplatformToCore. Exact duffs actually help here:isFullPlatformWithdrawalandcreditsPreviewno longer drift because of two-decimal fiat rounding. - Wider coverage than before. The old
preserveMaxonly applied whenisFullShieldedSweepwas set; exactness now holds for every route, which is the substance of the fix. - Both view models were changed symmetrically.
Two non-blocking notes:
-
SendViewModel.swift:693andInternalTransferViewModel.swift:859— in the fiat-conversion-failure branch onlymaxAmountDuffsis reset, whileisFullShieldedSweepandshieldedSweepAmountCreditsstay set from the.readypath infillMaxFromWallet. That leaves a "full sweep with amount 0" state. It isn't sendable (canContinuerequiresdashDuffsUnsigned > 0) and any manual edit clears it, so it's not a blocker — but the reset would be better made complete. Note that reusingclearMaxSelection()verbatim would also wipe the Max notice, so it likely needs a narrower reset. -
No regression tests. CodeRabbit's point stands: this PR deliberately decouples the displayed amount from the executable amount, and that's exactly the kind of invariant that breaks silently later.
DashWalletTestsalready ships arates.jsonfixture, so a "tap Max in fiat → toggle units →dashDuffsUnsignedunchanged" test is feasible, though the singletons (CurrencyExchanger.shared,SwiftDashSDKWalletState.shared) make it more than a one-liner. Not a condition of this approval.
One thing to be aware of: CI here only runs the PR-title check and CodeRabbit — there's no build job, so the xcodebuild result in the description isn't independently verified.
Summary
Verification
Summary by CodeRabbit