singleStepSendErrorPrecedesPersistence function
The SINGLE-STEP twin of sendErrorPrecedesPersistence, for the shield and
move-to-transparent send (R13 §4.1). Same path (Wallet::send_by_id →
sign_proposal_upstream → create_signed_core → broadcast_persisted),
but never a two-step: a shield is its own arm and a move pays the wallet's
own t-address, never a TEX — so mark_sent_multi is unreachable, and the
engine writes the transaction ONCE, in one transaction, on full success.
Listed, by the site that raises each:
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.InvalidSeedLength,KeyDerivation— the spending-key derivation from the seed (derivation.rs), before anything is built.ProposeFailed—create_signed_core's shape guard, before signing.TexSendLimitReached,SignFailed—map_create_erronly, a create the engine rolled back whole.DiskFull,StoreBusy,Io— the persist itself (a failed COMMIT,map_create_err→into_store_fault): the engine writes once, so a failed commit saved nothing. After the create,broadcast_persistedraises none of them — itsraw_tx_bytesread stays an unclassifiedStoreCorrupt, pinned by the floor inno_blind_store_corrupt.rs. Keeping them here keeps the "free up space" and "busy" routes (#373), which are true on this path.InvalidStatewithphase == closingONLY — the FFI'shandle_closed, a handle closed before the core was reached. Written as== closing, not "not wiped", so a future post-persistence site with another phase fails closed.
NOT listed, on purpose: StoreCorrupt (raw_tx_bytes reads the
just-persisted bytes), InvalidState with any other phase (wiped is
broadcast_persisted's answer for a wallet wiped mid-send, after the
persist), and QueuedSendsFull (only the two-step enrol raises it; this
path cannot, so the list does not claim it). An untyped throw is never
listed. Whatever is not here lands on the flow's "outcome unknown" state.
Implementation
bool singleStepSendErrorPrecedesPersistence(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_InvalidSeedLength() ||
WalletErrorKind_KeyDerivation() ||
WalletErrorKind_ProposeFailed() ||
WalletErrorKind_TexSendLimitReached() ||
WalletErrorKind_SignFailed() ||
WalletErrorKind_DiskFull() ||
WalletErrorKind_StoreBusy() ||
WalletErrorKind_Io() => true,
WalletErrorKind_InvalidState(:final phase) =>
phase == LifecyclePhase.closing,
_ => false,
};