Skip to Content
Security

Security

Key handling

  • Private keys and mnemonics live in memory only. They are never written to disk except through the storage adapter’s encryption (native: hardware-backed expo-secure-store; web: AES-GCM over localStorage).
  • The mnemonic is never placed in React stateuseStacksWallet keeps only the derived addresses in state and reads the mnemonic from secure storage on demand.
  • Key material, mnemonics, and private hex are never included in any error, log, or SbtcError.context.

withAuthGuard

Sensitive operations are wrapped with withAuthGuard, which calls the adapter’s auth.prompt() (biometrics / WebAuthn / passphrase) before running and rejects if the user cancels or fails. It guards:

  • useStacksWallet().exportMnemonic()
  • useStacksWallet().clearWallet()
  • transaction signing (signPsbt, signStacksTx)

These imperative actions reject with an SbtcError on auth failure (unlike the hooks’ loading actions, which surface errors via an error field).

const { exportMnemonic } = useStacksWallet(); try { const mnemonic = await exportMnemonic(); // prompts biometrics/WebAuthn first } catch (e) { // e.code === 'AUTH_FAILED' if the user cancelled }

HTTPS only

Every endpoint must be HTTPS. An HTTP URL (in defaults or apiConfig) throws NETWORK_SECURITY_ERROR.

SSR is a no-op

During server-side rendering (typeof window === 'undefined'), the SDK performs zero storage, auth, or network operations — hooks return their loading/empty state immediately. There is no key access on the server.

Errors

Hooks never throw — they surface an SbtcError via their error field. Imperative, auth-guarded actions reject with one. Every SbtcError has:

class SbtcError extends Error { code: SbtcErrorCode; // stable, machine-readable message: string; // human-readable; never contains secrets originalError?: unknown; // the underlying cause, if any context?: Record<string, unknown>; // non-sensitive (txids, addresses, amounts) platform?: 'native' | 'web'; }

Branch on code, not message:

CodeMeaning
POLYFILL_NOT_INITIALIZEDNative: SbtcProvider mounted before @baoku26/sbtc-sdk/polyfills was imported.
WALLET_NOT_FOUNDA hook was used before a wallet was generated/restored.
WALLET_LOCKEDA sensitive op was attempted on a locked wallet.
AUTH_FAILEDBiometric / WebAuthn auth failed or was rejected.
AUTH_UNAVAILABLENo auth mechanism is available on this platform.
INVALID_MNEMONICrestoreWallet got an invalid BIP39 phrase.
INVALID_BTC_ADDRESSA withdrawal targeted a malformed BTC address.
INSUFFICIENT_BALANCERequested amount exceeds available balance.
PSBT_SIGNING_FAILEDThe PSBT signer rejected or returned an invalid PSBT.
TX_SIGNING_FAILEDStacks transaction signing failed or was rejected.
EMILY_API_ERRORThe Emily / sBTC pipeline returned an error.
DEPOSIT_BELOW_DUSTDeposit below the 546-sat dust minimum.
NETWORK_TIMEOUTA network request exceeded its timeout.
STORAGE_ERRORA storage adapter read/write failed.
WALLET_CONNECT_UNAVAILABLENo supported wallet is installed/available.
NETWORK_SECURITY_ERRORAn HTTP (non-HTTPS) endpoint was attempted.
SSR_NOT_SUPPORTEDA wallet operation was attempted during SSR.

Import the enum for exhaustive handling:

import { SbtcErrorCode } from '@baoku26/sbtc-sdk'; if (error?.code === SbtcErrorCode.AUTH_FAILED) { /* ... */ }
Last updated on