fix: classify wallet errors by code, not by prose
Round 6 replaced one string-match with another and made things worse in the opposite direction. `userRefused` used \b after the code alternation, and \b does not match between D and _, so it missed USER_REFUSED_OP - the actual Wallet API code, number 113, declared in @starknet-io/starknet-types-0103 in this repo's own node_modules. An ordinary Reject therefore left the payer's invoice branded maybe-paid, behind a ten-minute probe, with the double-spend button as the only remaining control. Round 6 fixed the bug only for phrasings no wallet emits. The same labelling made every wallet-side error "may have reached the network", including INSUFFICIENT_PRIVATE_BALANCE (119), so the shield-then-pay branch was dead code with the shipped adapter: the payer was refused and then locked out. The one test covering it passed for a wallet that does not exist. Errors are now classified on the JSON-RPC code, which is a fact the wallet asserts rather than prose it happens to write. Six of the seven documented codes are pre-submission; UNKNOWN_ERROR is not, because unknown means unknown. The message remains a narrow fallback for wallets that send no code. Also: - A settled invoice no longer mints a receipt. `confirm` answers "is this invoice paid?" and ignores the hash, so on a shared link one payer's money minted a receipt for another who had broadcast nothing, and fired onPaid so the merchant handed over goods. It now ends with a distinct error saying the merchant records the invoice as paid and this page cannot attribute that payment to this wallet. - The unreconciled hold was applied by passing a doctored copy of the invoice, and evaluateInvoice returns a spread of what it is given - so the copy was written back and persisted with expiresAt gone. The deadline was destroyed permanently and later payments reported paid instead of paid_late. It is a parameter now, and the row is never edited. - expiresAt is out of the idempotency fingerprint: a dashboard recomputes it on every click, so a retry 409'd forever for a create that had succeeded. - A known token label must carry that token's address, the mirror of the check that already refused a known address with the wrong label. - persist() is serialised and writes temp-then-rename, and a corrupt store is quarantined and logged rather than refusing to boot - refusing took down the only routes that could repair it. - New write-off route: a stranger sending 1 wei to a published invoice address made it underpaid, which is neither deletable nor cancellable, pinning the invoice and its address forever. Write-off retires the row and keeps the address claimed, which is the part that must not change. 159 tests. All seven guards above verified by breaking them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
B
bongbongcrypto committed
5e353b2eac37be48e486ef5a481ddb6ccd7badb3
Parent: 20ebfb3