singleStepSendErrorPrecedesPersistence function

bool singleStepSendErrorPrecedesPersistence(
  1. Object error
)

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_open at the top of sign_proposal_upstream.
  • NetworkUpgradeUnsupported, ConsensusGraceExpired, ConsensusNotEvaluated — signing_permit, before the seed pull.
  • ProposalAlreadyUsed, ProposalStale — the registry peek/consume and assert_proposal_anchor_fresh, before the create; ProposalStale is also map_create_err's anchor arm, a create that rolled back.
  • SeedRequired, SeedMismatch — acquire_seed and ensure_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_err only, 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_persisted raises none of them — its raw_tx_bytes read stays an unclassified StoreCorrupt, pinned by the floor in no_blind_store_corrupt.rs. Keeping them here keeps the "free up space" and "busy" routes (#373), which are true on this path.
  • InvalidState with phase == closing ONLY — the FFI's handle_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,
    };