fix(convert): auto-correct the entry to the affordable max instead of prompting - #1302
Merged
Merged
Conversation
… prompting Amount entry is capped at the raw balance, but the fee comes out of that same balance — charged on top when the funding side is Dollars (no launchpad sale to skim), grossed up out of the sale otherwise. Entering the maximum therefore always overruns the balance by exactly the fee, and never by more. Both flows handled that badly. A Get funded by a token the fee pushed over balance priced the typed number and then popped an "Insufficient Balance After Fees" modal asking the user to confirm a correction. Converting the whole Dollars balance hard-failed at submit with "Insufficient Funds". Trim the entry itself to what the balance can actually fund, before anything is priced. The correction band is only ever the fee percentage, so there is nothing to confirm. Writing through the delegate (rather than only the receipt, as the modal did) keeps the amount screen agreeing with what the flow priced when the user navigates back. The maximum is floored to the currency's smallest unit — rounding it up would put the debit straight back over the balance ($10.11 at 1% corrects to $10.00, not $10.01). Buy now derives its fee shape once, feeding both the correction and the receipt's fee, so the two can't disagree.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Amount entry is capped at the raw balance, but the fee comes out of that same balance:
entered × (1 + f)entered / (1 - f)Either way, entering the maximum always overruns the balance by exactly the fee — and never by more.
Both flows handled that badly:
Fix
Trim the entry itself to what the balance can actually fund, before anything is priced. The correction band is only ever the fee percentage (
checkFundingAmount/checkBalanceLimitalready block anything above the raw balance), so there is nothing meaningful to put to the user.New pure helper
entryAffordableAfterFeeinverts both conventions:entered × (1 + f)balance / (1 + f)entered / (1 - f)balance × (1 - f)The result is floored to the currency's smallest unit via a new
Fiat.flooredToSmallestUnit(). Rounding it up would put the debit straight back over the balance —$10.11at 1% corrects to$10.00, not$10.01, because$10.01plus its own fee is$10.1101.The correction writes through a new
AmountEntryDelegate.setAmount(a replace-setter —prefilltypes on top of the existing entry), so the amount screen and the receipt show the same number when the user navigates back.Buy now derives its fee shape once through
buyFeeFor(token), feeding both the correction and the receipt'sexchangeFee, so the two can't drift apart. The dead modal branch and its three strings are removed.Tests
16 new tests, written test-first:
FeeAffordableEntryTest(8) — entries with room left alone, zero-fee no-op, whole balance under each fee convention, the flooring case, idempotence, over-balance entries, and a JPY case confirming the correction is denominated in the balance's currencyLaunchpadSellFeeTest(4) — the two new inverses round-trip back to the balanceFiatTest(3) — flooringAmountEntryDelegateTest(1) —setAmountreplaces rather than appends