Skip to content

fix: open review for requests with an amount - #1763

Merged
ovitrif merged 26 commits into
masterfrom
fix/scan-amount-to-confirm
Nov 5, 2026
Merged

ovitrif merged 26 commits into
masterfrom
fix/scan-amount-to-confirm

Conversation

@jvsena42

@jvsena42 jvsena42 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Twin: synonymdev/bitkit-ios#1198
Companion: synonymdev/bitkit-e2e-tests#290 — updates send.e2e.ts for this route; the e2e gate needs it, so merge them together.

This PR:

  1. Opens Confirm directly when an on-chain BIP21 request with an amount is scanned, pasted or opened as a link and the Savings balance covers it, and never opens Amount for it: the payee set the amount, so it cannot be edited
  2. Opens the USDT review directly when a USDT payment request with an amount is opened, matching iOS, with the same fixed amount
  3. Updates the journeys whose route these change, adds two Send journeys, and corrects the fixed-price Paykit journey that still described the old Amount detour

Motivation, from the Slack report:

A lock priced at $2.50, scanned with USDT not enabled and paid in BTC, showed a loading spinner on the swipe slider for a few seconds, then landed on the Amount screen with most of the UI disabled, showing the BTC amount for the dollar value. Requests priced in BTC go to Confirm as expected.

Description

  • On-chain BIP21 with an amount now opens Confirm instead of Amount, because the amount is already known and Amount only made the user press Continue. The balance and dust checks run first; the fee and auto coin selection are prepared as Amount's Continue would, and a hardware wallet's fee is estimated before Confirm opens.
  • The scanned amount is fixed. Tapping it on Confirm does nothing, and Back (toolbar arrow or system Back) goes to the screen before Confirm, or closes the sheet when Confirm is its first screen.
  • Amount does not open for a scanned amount in any case: with manual coin selection the link opens Confirm and the swipe opens the coin picker; an amount below the 547 sat minimum shows an "Amount Too Low" toast and opens nothing; a failed hardware fee estimate shows its error and leaves the user where the scan started.
  • When Savings cannot cover the amount but a paired hardware wallet can, Confirm opens on that hardware wallet. Switching back to Savings restores the scanned amount, and a speed change does not rewrite it.
  • Zero-amount BIP21, contact payments and requests with a usable Lightning invoice keep their existing routes.
  • A USDT payment link or URI with a positive amount runs the quote on entry and opens on review, with the amount fixed. The fixed amount belongs to the recipient whose request set it; another recipient or a new send gets the editable Amount page. A failed quote, such as an insufficient USDT balance, shows its error and Try Again on that review instead of Amount, and Back from review returns to the recipient step. uint256=0 counts as no amount.
  • The Slack report predates fix: confirm fixed Paykit requests directly #1581, which merged a few hours after the report and shipped in v3.1.0. On this branch a fixed 2.50 USD request with USDT off goes from Preparing straight to Confirm, with Bitcoin only and with Bitcoin plus USDT. No further code change was needed; this PR adds a unit test for the Bitcoin plus USDT case and updates fixed-price-bitcoin.xml and the payment-requests README, which still described the Amount detour.

Out of Scope

Design

N/A — no design available.

Preview

On-chain BIP21 with an amount, opened as a link, goes straight to Confirm; tapping the amount stays on Confirm and Back closes the sheet. A below-minimum link shows Amount Too Low:

r6-dust-link.mp4

The same URI entered through Send → Enter Manually opens Confirm:

onchain-amount-manual-entry.mp4

BIP21 without an amount still opens Amount:

onchain-no-amount-to-amount.mp4

Manual coin selection: the link opens Confirm and the swipe opens the coin picker:

r6-manual.mp4

USDT link with an amount opens the fixed review; with no USDT the quote error and Try Again show there:

r6-usdt.mp4

Fixed 2.50 USD Paykit request with USDT off goes from Preparing straight to Confirm at 2 500 sats:

paykit-usd-request-to-confirm.mp4

QA Notes

Setup

Dev regtest build from this branch on an emulator, against the staging regtest backend the dev flavor targets, with a wallet holding about 700 000 sats in Savings and a Lightning channel. Coin Selection on Autopilot. The Paykit cases used the paykit-fixture-sender from bitkit-e2e-tests main, linked to a Pubky profile created on the payer. USDT cases enable USDT through the link's prompt; the wallet has no USDT balance.

Journeys

  • J1: new scan-onchain-amount-to-confirm.xml — BIP21 with an amount opens Confirm directly, tapping the amount stays on Confirm, Back leaves Confirm without opening Amount, over-balance and below-minimum amounts show their toasts
  • J2: new scan-onchain-no-amount-to-amount.xml — BIP21 without an amount, or with amount 0, still opens Amount
  • J3: updated usdt-payment-links-and-scans.xml — a USDT link or copied URI with an amount opens review without tapping Review, or shows the quote error on that review; Back returns to the recipient step
  • J4: updated fixed-quoted-confirmation.xml — a fixed USD request accepting Bitcoin and USDT opens Bitcoin Confirm with USDT off
  • J5: updated fixed-price-bitcoin.xml — fixed-price requests open confirmation directly with 21 000 and 50 000 sats
  • J6: updated onchain-accepted-result.xml — the fixed-amount send starts on Confirm
  • J7: updated rbf-own-send.xml — the boosted send starts on Confirm
  • J8: updated pin-lock-on-resume.xml — the payment URI opened while locked opens Confirm after unlock; Back closes the sheet
  • J9: updated signin-with-second-link.xml — the payment link opens Confirm once the sign-in sheet closes
  • J10: updated manual-coin-selection.xml and manual-coin-selection-load.xml — with manual coin selection the link opens Confirm and the swipe opens the coin picker

Manual Tests

N/A

Automated Checks

  • AppViewModelSendFlowTest.kt — on-chain BIP21 with an amount opens Confirm from the main scanner and from Send, including with manual coin selection; no amount opens Amount; a dust amount shows the minimum without opening Send; a hardware wallet's fee is estimated before Confirm and a failed estimate keeps the user on the address; a hardware wallet that covers what Savings cannot is selected; switching to Savings restores the scanned amount; a speed change keeps a scanned hardware amount; a fixed USD request accepting USDT opens Bitcoin Confirm while USDT is off
  • UsdtWalletScreenTest.kt — the initial quote runs only with a recipient and a positive amount; a positive requested amount is fixed and zero is not; the fixed amount stays with the recipient that set it
  • ./gradlew testDevDebugUnitTest, ./gradlew detekt and ./gradlew lintDevDebug passed locally; CI green on 6a85db2

Evidence

Verified on a Pixel 9 emulator (API 37) with the dev debug build of this branch:

  • Fixed scanned amount (22913e7): below-minimum link shows Amount Too Low; a link-opened Confirm ignores the amount tap and Back closes the sheet; manual coin selection opens Confirm and the swipe opens the coin picker; a USDT link with an amount shows its quote error on the review. Videos: fix: open review for requests with an amount #1763 (comment)
  • Review round 7 (d00e77c): hardware wallet covering what Savings cannot, Savings switch restoring the amount, uint256=0 as no amount, Back then Continue keeping a USDT amount fixed: fix: open review for requests with an amount #1763 (comment)
  • Review round 8 (156f029): the fixed USDT amount is cleared for another recipient or a new send; the review error has 16 dp above Try Again: fix: open review for requests with an amount #1763 (comment)
  • J2: bitcoin:<addr> and ?amount=0 open Amount at 0; entering 10 000 continues to Confirm.
  • J4, J5: 2.50 USD at 0.00001 opens Confirm at 2 500 sats with Bitcoin only and with Bitcoin plus USDT; the usd and btc presets open Confirm at 21 000 and 50 000 sats.
  • J6: amount=0.00001 opens Confirm at 1 000 sats with Savings and the recipient.
  • J8: with PIN on, the URI opened in the background shows the PIN pad, then Confirm at 10 000 sats.
  • Rounds 1–5 let Confirm return to Amount to edit the amount; 22913e7 removed that, so those comments' videos show the old flow.

Gaps

  • J3's success branch, opening on review after a successful quote, was not verified: the emulator wallet has no USDT, and the funded USDT fixture is not available locally.
  • J6 and J7 were driven only up to Confirm; nothing was broadcast or boosted.
  • J9 was not driven: it needs a requesting app that issues fresh signin_grant URLs. Its Confirm step follows the same PIN-gated link route verified in J8.
  • Hardware-wallet funding (fee estimate before Confirm, fallback when Savings is short) is covered by unit tests; it was not driven with the Trezor emulator.
  • The Slack case was a Pubky Market lock, which can carry a Lightning endpoint. The fixture sender only publishes on-chain and USDT endpoints, so a fixed USD request paid over Lightning was not reproduced.
  • bitkit-e2e-tests: @send_1 and @send_2 are updated in test: expect review screen for bip21 with an amount bitkit-e2e-tests#290 on the same-named branch the e2e gate picks up; all E2E shards passed against it on 6a85db2.

Models used

  • Planning/scoping: claude-opus-5-5 (reasoning: Not exposed)
  • Implementation: claude-opus-5-5 (reasoning: Not exposed)
  • Review: Not performed

The reasoning setting is not exposed in this session's metadata.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The latest changes appear safe to merge and address both earlier USDT findings.

Summary

Amount-bearing Bitcoin and USDT requests open review directly and keep the requested amount fixed.

  • Bitcoin links with a positive amount open Confirm directly.
  • USDT links with a positive amount open review after quoting.
  • Fixed-price Paykit Bitcoin requests are documented as opening Confirm directly.

Reviews (7) · Last reviewed commit: "fix: space the usdt review error above t..." · Reviewed by Greptile

@ovitrif ovitrif added this to the 3.2.0 milestone Nov 3, 2026
@ovitrif

ovitrif commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Added to the 3.2.0 milestone: this fix was opened after the 3.1.0 cut and closes no issue, so this PR carries the milestone itself.

@talosmachina talosmachina left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No findings. On-chain BIP21 with a payable amount now opens Confirm (Back swaps it for Amount), and a USDT payment link with an amount quotes once on entry. Reviewed 5e95204, full tier, static: the branch was not built or run for this review, so the evidence is the code and CI.

What I checked, and 7 candidates I ruled out

Read in full: the diff; onScanOnchain, onAmountContinue, prepareCoinSelection, prepareHardwareSendFee, validateAmount, refreshOnchainSendIfNeeded, navigateToSendRoute, resetSendState in AppViewModel.kt; the Confirm and Amount routes and navigateToSendAmount in SendSheet.kt; UsdtWalletScreen.kt, UsdtViewModel.quote, UsdtRepo.quote, PaykitAmount.parse.
Call sites traced: openOnchainConfirmForInvoiceAmount (one, after the balance and dust checks), navigateToAmountFromConfirm, Routes.UsdtPayment (built only by handleUsdtPaymentInput).
CI: build-local and Greptile green; build-and-test, lint, compose-ui and the local e2e shards still running when this was posted.

Ruled out

  • Confirm skips a step Amount's Continue does: for auto coin selection prepareCoinSelection returns true with no side effect, selectedUtxos is already null after the reset, the hardware fee is prepared before navigating, and refreshOnchainSendIfNeeded follows; Lightning routing estimation does not apply to an on-chain pay method.
  • Manual coin selection or a contact payment reaching Confirm without the picker: both return false before any state change and fall through to Amount, covered by the manual-coin-selection test.
  • Back loops or strands the sheet: from the main scanner the stack becomes [Amount] and Amount's Back hides the sheet; from the send sheet it becomes [Recipient, ..., Amount]; after Continue, Back pops to Amount through the existing popBackStack(SendRoute.Amount) branch. canReturnToAmount lives in SendUiState, so every new send clears it through resetSendState.
  • Amount loses the invoice amount on the way back: SendAmountScreen seeds its input from uiState.amount when it is positive.
  • A hardware fee estimate failure: it toasts and returns false, then the scan falls through to Amount, the same place it went before.
  • USDT quote repeats on rotation or process death: isRequested is saveable and the effect is keyed on Unit, so Back, Edit and a restore never quote again; canQuote guards a busy state.
  • USDT amount string rejected by the quote: the route amount is PaykitAmount.value (a plain dot decimal), which both PaykitAmount.parseInput and UsdtRepo.quote accept, the same string the Review button sends.

Merge confidence: 4/5, not built here and CI still running; the e2e send suite expects Amount for this route until the bitkit-e2e-tests change named in the PR lands.

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Regtest APK

Built from 6a85db2 (run).

Download bitkit-dev-debug universal APK (expires in 30 days).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jvsena42

jvsena42 commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

System Back on a Confirm opened straight from a bitcoin:…?amount=0.0001 link, after 1d280b6: Confirm (10 000 sats) → system Back → Amount with 10 000 sats kept.

system-back-from-confirm.mp4

@jvsena42
jvsena42 marked this pull request as ready for review November 3, 2026 13:51
@jvsena42 jvsena42 self-assigned this Nov 3, 2026
@jvsena42
jvsena42 requested review from a team, coreyphillips and pwltr and removed request for a team November 3, 2026 13:55

@coreyphillips coreyphillips left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two independent reviews.

needs changing before merge

  • E2E send shard breaks: e2e main still expects Amount for BIP21 with amount (app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt:5402)

worth doing, does not block

  • USDT journey clipboard step still says the amount screen opens (journeys/usdt-payment-links-and-scans.xml:6)
  • Hardware-wallet direct-Confirm path has no test (app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt:5426)
  • Tapping the amount on direct Confirm does nothing (app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt:5434)
  • The PIN journey expects Home one Back press too early (journeys/security/pin-lock-on-resume.xml:45-46)

nits

  • KDoc on a private function (app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt:5415-5418)

@pwltr pwltr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit 1d280b6c59a51ede602149b453981acf160656b7 through general, security and cross-platform source review, followed by targeted finding verification.

[P2] Tapping the amount cannot edit it after opening confirmation directly

For a valid amount-bearing Bitcoin request with Autopilot coin selection, the new flow opens Confirm without adding Amount to the navigation stack. Tapping the amount still emits BackToAmount, which becomes PopBack(Amount). SendSheet.kt only calls popBackStack for that effect, so the tap silently does nothing when Amount is absent.

The new fallback is wired to toolbar/system Back, which remains a workaround, but the amount-edit control does not use it. Route that control through the same fallback, or explicitly open Amount when it is missing. This is an introduced navigation regression confirmed by source tracing; it does not itself submit a payment.

Review details

General review
Model: Claude Opus 5.5
Reasoning effort: xhigh

Security review
Model: Kimi K3
Reasoning effort: max

Cross-platform review and verification
Model: GPT-6.1-sol
Reasoning effort: xhigh

@jvsena42
jvsena42 marked this pull request as draft November 4, 2026 08:44
jvsena42 and others added 4 commits November 4, 2026 06:02
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jvsena42

jvsena42 commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Amount tap on a Confirm opened straight from bitcoin:…?amount=0.0001.

Before, on 1d280b6: two taps on the 10 000 amount and nothing happens.

amount-tap-before.mp4

After, on 5a8cda7: the tap opens Amount at 10 000, and Continue returns to Confirm at 10 000.

amount-tap-after.mp4

@jvsena42

jvsena42 commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Replying to the review on 1d280b6:

@jvsena42
jvsena42 marked this pull request as ready for review November 4, 2026 09:04
jvsena42 and others added 5 commits November 4, 2026 17:33
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jvsena42

jvsena42 commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Review round 7 (d00e77c). Pixel 9 emulator, dev build of d00e77c, wallet with no USDT.

  • Back then Continue: a link with uint256=10001 opens the fixed review at $0.010001. Back opens the recipient step with the address. Continue on it, without changes, opens the fixed review again at 0.010001, not an editable Amount page.
  • uint256=0: treated as no amount. The link opens the editable Amount page at 0, and typing 5 works.
r7-usdt.mp4

@jvsena42
jvsena42 marked this pull request as ready for review November 4, 2026 20:43
@jvsena42
jvsena42 marked this pull request as draft November 4, 2026 21:29
jvsena42 and others added 2 commits November 4, 2026 18:34
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jvsena42

jvsena42 commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Review round 8 (156f029). Pixel 9 emulator, dev build of 156f029, wallet with no USDT. USDT was switched off again afterwards.

  • Changed recipient (01fb2a6): a uint256=10001 link opens the fixed review. After Back, I typed a different address (0x…dEaD) and tapped Continue. That opens the editable Amount page, with the number pad shown, not the fixed review.
  • New send (01fb2a6): I closed that send, started a new USDT Send, chose Enter Manually and typed the original payee's address. Amount opens editable at 0.
r8-usdt.mp4
  • Error spacing (156f029): on the fixed review, the quote error now uses the Amount page's style (BodyS, tag UsdtError) with 16 dp above Try Again. Before, it sat directly on the button.

Tests: UsdtWalletScreenTest 6/6 and detekt pass.

@jvsena42
jvsena42 marked this pull request as ready for review November 4, 2026 21:48
@jvsena42
jvsena42 enabled auto-merge November 4, 2026 21:48

@pwltr pwltr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit 156f029.

Medium candidate — fixed-amount contact payment may become stuck on Amount. If the Paykit SDK delivers an on-chain-only contact endpoint containing a positive-amount Bitcoin URI, the app locks that amount before initializing its validity. For a Savings-only user, Continue can then remain disabled; switching to a funded hardware wallet provides a recovery path. Initialize validation before locking the amount or preparing the Amount screen. The application-side path is supported statically, but whether the pinned SDK delivers this endpoint shape remains unverified; this is a conditional candidate, not a reproduced production failure.

Review details

General review: GPT-6.1 Sol / high (Codex).

Finding verification: GPT-6.1 Sol / high (Codex).

@pwltr pwltr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: my request for changes on this point was premature. The scenario depends on the Paykit SDK returning an amount-bearing BIP21 URI for a contact endpoint, which I haven’t verified.

Please treat this as a non-blocking question pending confirmation that the SDK supports that input. I’m withdrawing it as a reason to block this PR.

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

@ovitrif ovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the head. @pwltr approved 156f029b after his device checks, and the only commit since drops a compose test in SendConfirmNavigationTest.kt, with no app code changed.

@ovitrif
ovitrif dismissed coreyphillips’s stale review November 5, 2026 13:11

Dismissing: the one point still open was the e2e send shard on bitkit-e2e-tests main. synonymdev/bitkit-e2e-tests#290 now provides the same-name fix/scan-amount-to-confirm branch, and the e2e send shard passed on head 6a85db25.

@ovitrif

ovitrif commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

@jvsena42 Merging: approved on the head, CI green including the e2e send shard, and every review thread resolved. synonymdev/bitkit-e2e-tests#290 can merge right after, so e2e main matches both apps; the iOS twin synonymdev/bitkit-ios#1198 is already merged.

@ovitrif
ovitrif merged commit edca388 into master Nov 5, 2026
32 of 34 checks passed
@ovitrif
ovitrif deleted the fix/scan-amount-to-confirm branch November 5, 2026 13:12
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.

5 participants