LightningPayScreen becomes PayScreen: it can no longer call itself Lightning
now that it also takes Bitcoin addresses. Paste, auto-paste and scan all route
through the same parser.
New OnchainTransferScreen rather than a third mode inside TransferScreen. The
lightning screen is built around a payment request that already carries its
amount and a quote it can fetch as soon as a mint is chosen; onchain has neither
(the user types the amount, and the quote returns a LIST of fee tiers). Folding
that in would have meant re-auditing every `!encodedInvoice &&` render gate on a
screen where a mistake breaks lightning payments. The SERVICE layer is still a
single shared TransferOperationApi - this is a screen, not a second
implementation.
Address handling (src/services/bitcoin/bitcoinUtils.ts):
- Checksums verified locally, so a mistyped address fails on the screen the user
is looking at rather than one round-trip later. Witness version selects the
checksum constant - v0 must be bech32, v1+ bech32m - and accepting either for
both would let a corrupted address through whenever it satisfied the other.
- MAINNET ONLY, and this is the point rather than a detail. The CDK fakewallet
hands out REGTEST deposit addresses for onchain topup quotes, so anyone testing
this wallet has a bcrt1q... in their clipboard - the single most likely wrong
thing to be pasted into Pay. It is decoded (so we can say "wrong network"
rather than "unknown data") and then refused. Pinned in tests with the exact
address the fakewallet produced.
- Unified QR: LIGHTNING WINS. A bitcoin: URI carrying a lightning= invoice
offers both rails and lightning is cheaper and faster for everyone, so the
invoice is taken and the address goes unused.
Fee tiers default to the MIDDLE one, rounding to the cheaper side: the user can
always choose to pay more, but a wallet must never round a fee up on their
behalf. The picker collapses to a read-only row when the mint returns one tier -
which is the common case, not the edge case. Tiers are sorted before selection
because fee_index is the mint's IDENTIFIER for a tier, not its rank.
PENDING is styled as the SUCCESS state here, not a warning: NUT-30 requires the
mint to answer PENDING and broadcast in the background, so every onchain payment
lands there. The money has left; only confirmation is outstanding. The
transaction detail surfaces the outpoint (txid:vout) once broadcast - the only
handle on the payment that does not depend on the mint - and offers a manual
status check, since the sweep only runs on the ~60s pending cadence.
87 tsc errors (unchanged baseline), 318/318 tests, i18n clean.