features/wallet/send_authorization library

The host send-authorization seam (#327 — security review F1).

Some custody models authorize each SPEND individually: the host holds no standing signing capability and unlocks it per transaction (e.g. an app-passphrase-gated staged credential, or a biometric-per-send policy). The package's controllers route EVERY money-committing bridge call through WalletSendAuthorizer.authorizeSpend, so such a host plugs its prompt → unlock → sign → re-lock cycle in by overriding walletSendAuthorizerProvider — no session decorator required. The default WalletPassthroughSendAuthorizer runs the call unmodified, which is correct for sealed-keychain custody (the SDK signs whenever asked).

Classes

WalletPassthroughSendAuthorizer
The default: no host authorization step — run the signing call directly. Correct for sealed-keychain custody, where the SDK holds the signing capability for the wallet's whole open lifetime.
WalletSendAuthorizer
The seam itself. The package invokes authorizeSpend around every money-committing bridge call, exactly once per user-confirmed action:
WalletSpendIntent
The display-facts of the spend being authorized — enough for the host's prompt copy, nothing more. §5.4 never-log values: render them in the prompt, never write them to a log.

Enums

WalletSpendKind
What kind of money-committing action is being authorized — drives the host's prompt copy ("Authorize this payment" vs "Authorize recovering funds"). Every arm the package can emit; a host switch stays exhaustive.
WalletSpendOrigin
Who initiated the spend being authorized — a host POLICY input (#328). automatic marks a spend NO user gesture triggered; the only automatic spend the package emits today is the auto-shield loop's self-transfer (WalletSpendKind.shield — funds stay inside the wallet).

Functions

abbreviateWalletAddress(String address) → String
Elide an address for prompt display (#383 R3): keeps enough of both ends to visually match against a copied address, short enough that a prompt stays one line. Deterministic, display-only — §5.4: never log the result.

Exceptions / Errors

WalletSpendAuthorizationDenied
Thrown by a WalletSendAuthorizer when the user (or a host policy) declines the spend. The controllers catch it BEFORE any classification and silently restore the pre-confirm state (review stays reviewable, the one-shot proposal token is unconsumed, ZERO bridge calls were made). The host's own prompt is the user-facing communication channel for a denial — the package deliberately shows no additional fault for it.
WalletSpendSessionChanged
Thrown by the SDK from INSIDE an authorized action when the wallet session changed (a host identity switch / wallet re-open) between the prompt opening and the approval landing — the spend is REFUSED: running it would move the DEAD identity's money while every visible surface already shows the new one, so the user could never see what they just approved. Nothing was signed or broadcast.