Skip to content

fix(wallet): full 8-decimal Dash amounts, and accept & as a URI query separator - #914

Merged
romchornyi merged 2 commits into
swift-sdk-integrationfrom
fix/bug-26-dash-amount-precision
Aug 3, 2026
Merged

fix(wallet): full 8-decimal Dash amounts, and accept & as a URI query separator#914
romchornyi merged 2 commits into
swift-sdk-integrationfrom
fix/bug-26-dash-amount-precision

Conversation

@romchornyi

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

Two carry-over bugs from 2026-08-03 testing, both about amount precision on the send path.

BUG-26NumberFormatter.dashFormatter capped maximumFractionDigits at 5, so
0.23243214 DASH rendered as 0.23243: 214 duffs dropped on screen.

BUG-25 — a scanned payment request misbehaved. Retested with four generated QRs:

QR Encoded Before
1 dash:<addr>?amount=0.23243214 glitched
2 dash:<addr>&amount=0.23243214 rejected with an error
3 dash:<addr>?amount=0.25 ✅ pre-filled 0.25 DASH
4 <addr> (bare) ✅ accepted, amount 0

3 and 4 passing means the prefill path was never broken — the original "amount isn't
auto-populated" framing was wrong. That leaves two separate defects, one per commit here.

What was done?

fix(amount) — 5 → 8 decimal places. Five was an oversight, not a display cap:

  • the paste path already clamps Dash input to 8 (BaseAmountModel.updateFromPasteboard);
  • formattedDashAmountWithoutCurrencySymbol already goes through the 8-digit
    _dashDecimalFormatter, so the same amount rendered differently with and without the symbol;
  • DashAmountFormatterTests already asserts dashFormatter.maximumFractionDigits == 8 and has
    been failing against this;
  • Numbers+Dash.swift's own doc comment advertises 12345678 -> "1.2345678".

cryptoFormatter leaves minimumFractionDigits at 0, so whole amounts still render as "1", not
"1.00000000" — only amounts that actually carry sub-5-decimal precision get longer.

This also decouples dashFormat.maximum from the display precision. It was
kMaxDashSupplyDuffs / pow(10, maximumFractionDigits), which at 5 evaluated to 21,000,000,000
instead of the 21,000,000 DASH max supply; it now divides by kOneDash, the way
BaseAmountModel already does.

fix(payments) — accept & as the query separator. BIP21 introduces the query with ? and
the parser split only on that, so for QR 2 the entire tail stayed in addressPart; the address
then failed validation and the whole request was refused. It now splits on whichever of ? or &
comes first — & is already the pair separator for the rest of the query, so following pairs keep
parsing unchanged.

That accounts for QR 2. QR 1 is the display side: the internal value already kept full precision
(AmountObject.dashInputString uses the 8-digit dashDecimalFormatter) while mainFormatted was
rendered through the 5-digit dashFormatter, falling back to the literal "Invalid Input" on
failure — which the first commit fixes.

How Has This Been Tested?

xcodebuild … -scheme dashpay … ARCHS=arm64 buildBUILD SUCCEEDED (verified with #913
applied on top, since that hotfix is what currently unblocks the branch's build).

Added to PaymentProtocolTests: the & form parses address and amount, following pairs still
parse, and all 8 decimals survive the parse. The unit-test target remains unrunnable per
CLAUDE.md, so these are compile-ready rather than executed.

Still owed — a device retest of QR 1 to confirm the 8-decimal send screen is clean. If it is,
BUG-25 closes entirely.

Breaking Changes

None. Amounts with more than 5 decimals now display in full; nothing that was previously shown
changes.

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

🤖 Generated with Claude Code

jeanpierreroma and others added 2 commits August 3, 2026 20:42
`NumberFormatter.dashFormatter` capped `maximumFractionDigits` at 5, so a
scanned `dash:…?amount=0.23243214` rendered as "0.23243" — 214 duffs dropped
on screen. It backs the amount-entry screen (`BaseAmountModel`,
`AmountObject`) and `BalanceView`, so both entry and display were affected.

Five was an oversight rather than a deliberate display cap:

- the paste path already clamps Dash input to 8
  (`BaseAmountModel.updateFromPasteboard`);
- `formattedDashAmountWithoutCurrencySymbol` already goes through
  `_dashDecimalFormatter`, which uses 8;
- `DashAmountFormatterTests.testGeneralDashFormatterSupportsEightFractionDigits\
WithoutTrailingZeros` already asserts `dashFormatter.maximumFractionDigits == 8`
  and has been failing against this;
- `Numbers+Dash.swift`'s own doc comment advertises `12345678 -> "1.2345678"`.

`cryptoFormatter` leaves `minimumFractionDigits` at 0, so whole amounts still
render as "1" rather than "1.00000000" — only amounts that actually carry
sub-5-decimal precision get longer.

Also decouples `dashFormat.maximum` from the display precision. It was
`kMaxDashSupplyDuffs / pow(10, maximumFractionDigits)`, which at 5 evaluated to
21,000,000,000 instead of the 21,000,000 DASH max supply; it now divides by
`kOneDash` the way `BaseAmountModel` already does. The two expressions only
agree once the precision reaches 8, so leaving the coupling would have hidden
the next change.

Note for follow-up (not changed here): `cryptoFormatter` caches by currency
code alone and returns the cached instance without honouring a different
`exponent`, so the first caller for a code wins. Harmless today — the only
other DASH-capable callers pass the Coinbase account exponent, which is also 8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Scanning `dash:<address>&amount=0.23243214` was rejected outright. BIP21
introduces the query with `?`, and the parser split only on that, so for an
`&`-only URI the entire tail stayed in `addressPart` — the address then failed
validation and the whole request was refused, rather than merely losing the
amount as the bug was originally reported.

Split on whichever of `?` or `&` comes first. `&` is already the pair separator
for the rest of the query, so a leading one yields the same key/value list and
every following pair keeps parsing.

Verified against generated QRs (four variants in `~/Desktop/bug-25-qr/`): the
`?` form with 2 decimals pre-filled correctly before this change, the `&` form
was rejected, and a bare address was accepted with no amount. Tests cover the
`&` form, that following pairs still parse, and that all 8 decimals survive the
parse.

The remaining half of BUG-25 — an 8-decimal amount misbehaving on the send
screen where a 2-decimal one works — is the display side, fixed by the
5→8 formatter change in this branch's other commit: the internal value already
kept full precision (`AmountObject.dashInputString` uses the 8-digit
`dashDecimalFormatter`), while `mainFormatted` was rendered through the 5-digit
`dashFormatter`. Needs a device retest to confirm.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fa5fbec7-10e0-44ff-a130-c1a875862071

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@romchornyi romchornyi changed the title fix(payments): full 8-decimal Dash amounts, and accept & as a URI query separator fix(wallet): full 8-decimal Dash amounts, and accept & as a URI query separator Aug 3, 2026
@romchornyi
romchornyi merged commit ad7e346 into swift-sdk-integration Aug 3, 2026
2 of 3 checks passed
@romchornyi
romchornyi deleted the fix/bug-26-dash-amount-precision branch August 3, 2026 19:52
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