mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
Field report (BrollyZapper, 2026-08-25): a QUOTA_EXCEEDED on pay_invoice and a RESTRICTED on list_transactions both reached the phone and showed nothing at all — no toast, no dialog, no error state. The action simply looked like it had not happened. Three separate defects produce that symptom. 1. The zap path had no user-visible timeout. NwcSignerState's 60s safety net only dropped the relay subscription: it never cleaned the tracker entry and never told anyone. A response lost in transit (the same trip measured relay.damus.io refusing 40% of websocket upgrades) was therefore permanent silence. The timeout now retires the request and fires an onTimeout callback that every interactive caller renders. NwcPaymentTracker.cleanup returns whether it was the one to remove the entry, so a timeout racing a real response stays quiet rather than overwriting the wallet's own answer. 2. WalletTransactionsScreen never read walletViewModel.error. The ViewModel set it correctly on both the refusal and the timeout paths; the view branched on isLoading/isEmpty only and rendered "No transactions yet" over the top of it. 3. Consumers matched on PayInvoiceErrorResponse, which the deserializer only produces when result_type == "pay_invoice". NIP-47 does not require a wallet to echo result_type on an error, and an error for any other method takes the generic NwcErrorResponse branch — so those refusals were dropped without a word, and the DVM screen went as far as thanking the user for a payment that had just been refused. All of them now match IErrorResponseLike, and the remaining else branches report an unreadable response instead of nothing. Also: errorMessage() falls back to the code name when a wallet sends `code` without `message` (message is optional in NIP-47), and stale wallet errors are cleared when a transaction fetch or page load succeeds.