sendErrorPrecedesPersistence function
Whether error, thrown by the SDK's own send AFTER the spend closure was
entered, is a kind the core raises only BEFORE any transaction is persisted
(S7 U1 fold) — so classifySendFailure's "nothing was sent" / back-to-form
routing is still the truth. Anything else lands on SendOutcomeUnknown.
Each kind was checked against the core's send path (Wallet::send_by_id →
sign_proposal_upstream → broadcast_persisted):
WatchOnly—require_spend_capable, before the token is touched.WalletBusy—require_openat the top ofsign_proposal_upstream.NetworkUpgradeUnsupported,ConsensusGraceExpired,ConsensusNotEvaluated—signing_permit, before the seed pull.ProposalAlreadyUsed,ProposalStale— the registry peek/consume andassert_proposal_anchor_fresh, before the create;ProposalStaleis alsomap_create_err's anchor arm, a create that rolled back.SeedRequired,SeedMismatch—acquire_seedandensure_seed_controls_account, before the token is consumed.QueuedSendsFull— the two-step enrol'senqueue, before the create.TexSendLimitReached,SignFailed—map_create_erronly, a create the engine rolled back whole.
NOT listed, on purpose: StoreCorrupt (raw_tx_bytes in
broadcast_persisted reads the just-persisted bytes; the two-step's
mark_sent_multi runs after the create committed), and DiskFull,
StoreBusy, Io — the same mark_sent_multi write classifies a SQLite
fault into any of them after the transactions exist. InvalidState is
broadcast_persisted's answer for a wallet wiped mid-send, also after.
Implementation
bool sendErrorPrecedesPersistence(Object error) =>
error is WalletApiError &&
switch (error.kind) {
WalletErrorKind_WatchOnly() ||
WalletErrorKind_WalletBusy() ||
WalletErrorKind_NetworkUpgradeUnsupported() ||
WalletErrorKind_ConsensusGraceExpired() ||
WalletErrorKind_ConsensusNotEvaluated() ||
WalletErrorKind_ProposalAlreadyUsed() ||
WalletErrorKind_ProposalStale() ||
WalletErrorKind_SeedRequired() ||
WalletErrorKind_SeedMismatch() ||
WalletErrorKind_QueuedSendsFull() ||
WalletErrorKind_TexSendLimitReached() ||
WalletErrorKind_SignFailed() => true,
_ => false,
};