{"version":3,"sources":["../src/browser.ts","../src/network.ts","../src/errors.ts","../src/scan-retry.ts","../src/units.ts","../src/lib/rpcClient.ts","../src/lib/verify.ts","../src/psbt.ts","../src/wallets/xverse.ts","../src/wallets/seed.ts","../src/derivation.ts","../src/migration.ts","../src/rpc/client.ts","../src/user-key.ts","../src/providers/xverse-provider.ts","../src/hooks/useTestnetWallet.ts"],"sourcesContent":["/**\n * Browser + React entry point.\n *\n * Imported via `@tachibtc/taurus-wallet-aggregator/browser` so the core (Node)\n * entry (`.`) stays free of the `react` and `sats-connect` peer dependencies.\n * Everything node-safe (networks, RPC adapter, verification, PSBT bridge) is\n * re-exported here too for one-stop browser imports.\n */\n\n// Pre-configured import-wallet networks (signet is the default chain).\nexport {\n  SIGNET,\n  REGTEST,\n  DEFAULT_WALLET_NETWORK,\n  SUPPORTED_WALLET_NETWORKS,\n  SUPPORTED_WALLET_CHAINS,\n  getWalletNetwork,\n} from \"./network.js\";\nexport type {\n  WalletChainName,\n  WalletNetworkConfig,\n  RpcEndpoints,\n} from \"./types.js\";\n\n// RPC adapter.\nexport { rpcCall, getUtxos, broadcastTx } from \"./lib/rpcClient.js\";\nexport type {\n  GetUtxosOptions,\n  BroadcastTxOptions,\n  RestUtxo,\n  UtxoStatus,\n} from \"./lib/rpcClient.js\";\n\n// BIP-322 signature verification.\nexport { verifyBip322Signature } from \"./lib/verify.js\";\nexport type { VerifyResult } from \"./lib/verify.js\";\n\n// PSBT → wallet-signing bridge.\nexport { prepareForWalletSigning } from \"./psbt.js\";\nexport type { WalletSigningRequest } from \"./psbt.js\";\n\n// Xverse connector. SIGNET ONLY — the extension has no regtest mode; use the\n// seed-phrase connector below for regtest.\nexport {\n  connectXverse,\n  signPsbtXverse,\n  signMessageXverse,\n  isXverseInstalled,\n  isXverseSupportedChain,\n  XVERSE_SUPPORTED_CHAINS,\n} from \"./wallets/xverse.js\";\nexport type { XverseConnection, XverseInputToSign } from \"./wallets/xverse.js\";\n\n// Seed-phrase (BIP39 mnemonic) connector — the other half of the import flow,\n// and the only path that covers BOTH signet and regtest. Keys are derived\n// locally; balances resolve over the same hosted JSON-RPC gateway as Xverse.\nexport {\n  connectSeed,\n  deriveSeedAddress,\n  signPsbtSeed,\n  seedAddressDescriptor,\n  isSeedImportAvailable,\n  isSeedSupportedChain,\n  SEED_SUPPORTED_CHAINS,\n} from \"./wallets/seed.js\";\nexport type {\n  SeedConnection,\n  ImportSeedOptions,\n  SeedInputToSign,\n  SignPsbtSeedOptions,\n} from \"./wallets/seed.js\";\n\n// Mnemonic + HD derivation primitives, so a browser consumer can generate,\n// validate, and derive from a seed phrase without pulling the Node entry.\nexport {\n  Keystore,\n  generateMnemonic,\n  validateMnemonic,\n  normalizeMnemonic,\n  buildAccountPath,\n  buildAddressPath,\n  deriveAddressFromAccountNode,\n  buildDescriptor,\n} from \"./derivation.js\";\n\n// Vault user-key derivation metadata — persist this at vault creation so a\n// record is never ambiguous about which derivation produced it.\nexport {\n  deriveUserKey,\n  resolveUserKey,\n  requireUserKey,\n  userKeyMatches,\n} from \"./user-key.js\";\nexport type { UserKeyDescriptor } from \"./user-key.js\";\n\n// Mnemonic/derivation errors a browser import flow must surface to the user.\nexport {\n  WalletError,\n  InvalidMnemonicError,\n  InvalidNetworkError,\n  InvalidAddressError,\n  WalletLockedError,\n  PsbtError,\n} from \"./errors.js\";\n\n// Aggregator adapter: bridge a connected Xverse wallet into WalletAggregator\n// via addProvider(). See the XverseProvider header for its capability boundary\n// (registration + balance/UTXO aggregation + external PSBT signing; not the\n// internal p2wpkh send() builder).\nexport { XverseProvider } from \"./providers/xverse-provider.js\";\nexport type { XverseProviderOptions } from \"./providers/xverse-provider.js\";\n\n// Unified React hook (covers both import paths and both chains).\nexport { useTestnetWallet, chainsFor } from \"./hooks/useTestnetWallet.js\";\nexport type {\n  UseTestnetWallet,\n  TestnetWalletState,\n  WalletType,\n  WalletStatus,\n  SignedMessage,\n} from \"./hooks/useTestnetWallet.js\";\n","import * as bitcoin from \"bitcoinjs-lib\";\nimport { InvalidAddressError, InvalidNetworkError } from \"./errors.js\";\nimport type {\n  NetworkConfig,\n  WalletChainName,\n  WalletNetworkConfig,\n} from \"./types.js\";\n\n// Built-in network names (used to guard unregisterNetwork). This SDK ships as a\n// testnet wallet: only signet and regtest are selectable networks. Mainnet and\n// public testnet are intentionally not registered.\nconst BUILT_IN_NETWORKS = new Set([\"signet\", \"regtest\"]);\n\nconst registry = new Map<string, NetworkConfig>();\n\n// signet and regtest are registered further down as full WalletNetworkConfigs\n// (with hosted rpc endpoints) once those configs are defined.\n\nexport function getNetwork(name: string): NetworkConfig {\n  const config = registry.get(name);\n  if (!config) {\n    throw new InvalidNetworkError(name);\n  }\n  return { ...config };\n}\n\n// Register a custom network\nexport function registerNetwork(name: string, config: NetworkConfig): void {\n  if (registry.has(name)) {\n    throw new Error(\n      `Network \"${name}\" is already registered. Unregister it first to replace.`,\n    );\n  }\n  registry.set(name, { ...config });\n}\n\n// Remove a custom registered network\nexport function unregisterNetwork(name: string): void {\n  if (BUILT_IN_NETWORKS.has(name)) {\n    throw new Error(`Cannot unregister built-in network \"${name}\".`);\n  }\n  if (!registry.has(name)) {\n    throw new InvalidNetworkError(name);\n  }\n  registry.delete(name);\n}\n\nexport function listNetworks(): string[] {\n  return [...registry.keys()];\n}\n\n// Check address is on the given network\nexport function isAddressOnNetwork(address: string, name: string): boolean {\n  const config = registry.get(name);\n  if (!config) {\n    throw new InvalidNetworkError(name);\n  }\n\n  try {\n    bitcoin.address.toOutputScript(address, config.network);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n// Check address is valid on the given network\nexport function validateAddress(address: string, name: string): void {\n  if (!isAddressOnNetwork(address, name)) {\n    throw new InvalidAddressError(address, `not valid for network ${name}`);\n  }\n}\n\n// Read an env var without assuming `process` exists. In the browser entry\n// there is no `process`, so a bare `process.env.X` would throw a ReferenceError;\n// bundlers (tsup/vite/webpack) statically inline `process.env.X` at build time,\n// so this resolves to the configured value for bundled consumers and falls back\n// to undefined in a raw browser context.\nfunction readEnv(name: string): string | undefined {\n  try {\n    return typeof process !== \"undefined\" ? process.env?.[name] : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n// Validate a gateway endpoint at config-construction time. RPC/REST base URLs\n// flow straight into fetch (see src/lib/rpcClient.ts), so an env override to an\n// attacker-controlled or cleartext host would silently redirect every balance\n// lookup, getUtxos, and broadcastTx call — enabling SSRF (CWE-918), address\n// harvesting, and cleartext exposure of addresses/raw transactions (CWE-319).\n// We require an absolute https:// URL, allowing http:// only for loopback hosts\n// (local regtest/dev gateways), and reject anything else loudly rather than\n// issuing the request.\nexport function requireSecureUrl(raw: string, label: string): string {\n  let u: URL;\n  try {\n    u = new URL(raw);\n  } catch {\n    throw new Error(`${label}: invalid URL \"${raw}\"`);\n  }\n  const isLoopback =\n    u.hostname === \"localhost\" ||\n    u.hostname === \"127.0.0.1\" ||\n    u.hostname === \"[::1]\" ||\n    u.hostname === \"::1\";\n  if (u.protocol !== \"https:\" && !(u.protocol === \"http:\" && isLoopback)) {\n    throw new Error(\n      `${label}: must be an https:// URL (got \"${u.protocol}//${u.hostname}\")`,\n    );\n  }\n  // Drop a single trailing slash so `${base}/rest` and `${rest}/address/...`\n  // derivations don't produce double slashes.\n  return u.toString().replace(/\\/$/, \"\");\n}\n\n// --- Signet (default import-wallet chain) ------------------------------------\n//\n// Hosted signet endpoints, defaulting to the tachibtc signet validator node and\n// overridable via env so consumers aren't pinned to this tenant (rate limits,\n// self-hosted gateways). Both the default and any override are validated by\n// requireSecureUrl.\n//\n//   jsonRpc  <- https://rpc-signet.tachibtc.com (Bitcoin Core JSON-RPC 1.0 proxy;\n//               also backs getUtxos()/broadcastTx() — see src/lib/rpcClient.ts)\n//   rest     <- mirrors jsonRpc (deprecated compat field; no env override)\n//   explorer <- https://explorer.bc-2.jp (human-facing links only)\n//\n// All methods (read and broadcast) are served without authentication, so no API\n// key is configured.\nconst SIGNET_JSONRPC = requireSecureUrl(\n  readEnv(\"TAURUS_SIGNET_RPC\") ?? \"https://rpc-signet.tachibtc.com\",\n  \"TAURUS_SIGNET_RPC\",\n);\n\n// Esplora/Electrs indexer for signet — Xverse's public signet infrastructure by\n// default. This is the SAME chain the Tachi signet node serves (verified: both\n// report identical UTXOs and tip height for the global/public signet), so the\n// two are interchangeable views of one chain, not two networks.\n//\n// It is preferred over `scantxoutset` for reads because it is ~35x faster\n// (~0.4s vs ~14s for a full signet UTXO-set scan), takes no node-global lock,\n// and reports genuine mempool/confirmation status. `jsonRpc` remains the\n// authoritative fallback — see the `esplora` field docs in types.ts.\n//\n// TAURUS_SIGNET_ELECTRS is honoured as the legacy name for this endpoint.\nconst SIGNET_ESPLORA = requireSecureUrl(\n  readEnv(\"TAURUS_SIGNET_ESPLORA\") ??\n    readEnv(\"TAURUS_SIGNET_ELECTRS\") ??\n    \"https://btc-signet.xverse.app\",\n  \"TAURUS_SIGNET_ESPLORA\",\n);\n\n// Block explorer for signet — human-facing links only, never network I/O (see\n// RpcEndpoints.explorer). Defaults to the public bc-2.jp signet explorer, which\n// indexes the same global/public signet the endpoints above serve, and is\n// overridable via TAURUS_SIGNET_EXPLORER. Validated by requireSecureUrl like\n// every other endpoint.\nconst SIGNET_EXPLORER = requireSecureUrl(\n  readEnv(\"TAURUS_SIGNET_EXPLORER\") ?? \"https://explorer.bc-2.jp\",\n  \"TAURUS_SIGNET_EXPLORER\",\n);\n\n// Pre-configured signet network wired to the hosted tachibtc gateway, exported\n// as a named constant so consumers can import and pass it directly into the\n// browser-wallet helpers (rpcClient, the Xverse connector, and\n// useTestnetWallet). Registered below so getNetwork(\"signet\") and\n// listNetworks() expose it as a built-in network.\n//\n// bitcoinjs-lib has no separate Network object for signet, so it reuses\n// networks.testnet for address and script encoding (shared \"tb\" HRP).\nexport const SIGNET: WalletNetworkConfig = {\n  name: \"signet\",\n  network: bitcoin.networks.testnet,\n  bitcoinjsNetwork: bitcoin.networks.testnet,\n  coinType: 1,\n  bech32: \"tb\",\n  rpc: {\n    jsonRpc: SIGNET_JSONRPC,\n    rest: SIGNET_JSONRPC,\n    esplora: SIGNET_ESPLORA,\n    explorer: SIGNET_EXPLORER,\n  },\n};\n\n// --- Regtest (import-wallet chain) -------------------------------------------\n//\n// Hosted regtest endpoints run by tachibtc, overridable via env. These are\n// non-loopback hosts, so requireSecureUrl requires https for the default and any\n// override (a local regtest node may still be pointed at via http://127.0.0.1).\n//\n//   jsonRpc  <- https://rpc-regtest.tachibtc.com  (Bitcoin Core JSON-RPC 1.0\n//               proxy; also backs getUtxos()/broadcastTx())\n//   rest     <- mirrors jsonRpc (deprecated compat field; no env override)\n//   explorer <- https://explorer-regtest.tachibtc.com (human-facing links only)\n//\n// All methods (read and broadcast) are served without authentication, so no API\n// key is configured.\nconst REGTEST_JSONRPC = requireSecureUrl(\n  readEnv(\"TAURUS_REGTEST_RPC\") ?? \"https://rpc-regtest.tachibtc.com\",\n  \"TAURUS_REGTEST_RPC\",\n);\nconst REGTEST_EXPLORER = requireSecureUrl(\n  readEnv(\"TAURUS_REGTEST_EXPLORER\") ?? \"https://explorer-regtest.tachibtc.com\",\n  \"TAURUS_REGTEST_EXPLORER\",\n);\n\nexport const REGTEST: WalletNetworkConfig = {\n  name: \"regtest\",\n  network: bitcoin.networks.regtest,\n  bitcoinjsNetwork: bitcoin.networks.regtest,\n  coinType: 1,\n  bech32: \"bcrt\",\n  rpc: {\n    jsonRpc: REGTEST_JSONRPC,\n    rest: REGTEST_JSONRPC,\n    explorer: REGTEST_EXPLORER,\n  },\n};\n\n// Register the full signet/regtest configs as built-in networks. A\n// WalletNetworkConfig is a structural superset of NetworkConfig, so\n// getNetwork() returns the full config (including rpc/bitcoinjsNetwork).\nregistry.set(\"signet\", SIGNET);\nregistry.set(\"regtest\", REGTEST);\n\n// The only chains the browser import-wallet flow supports, keyed by name for\n// easy chain switching. Signet is the default.\nexport const SUPPORTED_WALLET_NETWORKS: Record<\n  WalletChainName,\n  WalletNetworkConfig\n> = {\n  signet: SIGNET,\n  regtest: REGTEST,\n};\n\n// Ordered list of supported import-wallet chains (signet first / default).\nexport const SUPPORTED_WALLET_CHAINS: WalletChainName[] = [\"signet\", \"regtest\"];\n\n// The default chain used when the import-wallet flow is not told otherwise.\nexport const DEFAULT_WALLET_NETWORK: WalletNetworkConfig = SIGNET;\n\n// Resolve a supported import-wallet chain name to its full config. Throws for\n// any name outside the supported set (mainnet/testnet/custom are intentionally\n// rejected here — the import flow supports signet and regtest only).\nexport function getWalletNetwork(name: WalletChainName): WalletNetworkConfig {\n  const config = SUPPORTED_WALLET_NETWORKS[name];\n  if (!config) {\n    throw new InvalidNetworkError(\n      `${name} (import wallet supports only: ${SUPPORTED_WALLET_CHAINS.join(\", \")})`,\n    );\n  }\n  return { ...config };\n}\n","export class WalletError extends Error {\n  public readonly code: string;\n\n  constructor(code: string, message: string) {\n    super(message);\n    this.name = \"WalletError\";\n    this.code = code;\n    // Maintain a clean prototype chain across CJS/ESM bundlers.\n    Object.setPrototypeOf(this, new.target.prototype);\n  }\n}\n\nexport class InvalidMnemonicError extends WalletError {\n  constructor(message = \"Invalid mnemonic phrase\") {\n    super(\"INVALID_MNEMONIC\", message);\n    this.name = \"InvalidMnemonicError\";\n  }\n}\n\nexport class InvalidNetworkError extends WalletError {\n  constructor(network: string) {\n    super(\"INVALID_NETWORK\", `Invalid network: ${network}`);\n    this.name = \"InvalidNetworkError\";\n  }\n}\n\nexport class InvalidAddressError extends WalletError {\n  constructor(address: string, reason?: string) {\n    const msg = reason\n      ? `Invalid address \"${address}\": ${reason}`\n      : `Invalid address: ${address}`;\n    super(\"INVALID_ADDRESS\", msg);\n    this.name = \"InvalidAddressError\";\n  }\n}\n\nexport class InvalidAmountError extends WalletError {\n  constructor(message = \"Invalid amount\") {\n    super(\"INVALID_AMOUNT\", message);\n    this.name = \"InvalidAmountError\";\n  }\n}\n\nexport class InsufficientFundsError extends WalletError {\n  readonly available: bigint;\n  readonly required: bigint;\n\n  constructor(available: bigint, required: bigint) {\n    super(\n      \"INSUFFICIENT_FUNDS\",\n      `Insufficient funds: available ${available} sats, required ${required} sats`,\n    );\n    this.name = \"InsufficientFundsError\";\n    this.available = available;\n    this.required = required;\n  }\n}\n\nexport class WalletLockedError extends WalletError {\n  constructor() {\n    super(\n      \"WALLET_LOCKED\",\n      \"Wallet has been locked — re-derive from mnemonic before signing\",\n    );\n    this.name = \"WalletLockedError\";\n  }\n}\n\nexport class RpcError extends WalletError {\n  readonly method: string;\n  readonly rpcCode: number | undefined;\n\n  constructor(method: string, message: string, rpcCode?: number) {\n    super(\"RPC_ERROR\", `RPC ${method} failed: ${message}`);\n    this.name = \"RpcError\";\n    this.method = method;\n    this.rpcCode = rpcCode;\n  }\n}\n\nexport class RpcTransportError extends WalletError {\n  readonly httpStatus: number | undefined;\n\n  constructor(message: string, httpStatus?: number) {\n    super(\"RPC_TRANSPORT_ERROR\", message);\n    this.name = \"RpcTransportError\";\n    this.httpStatus = httpStatus;\n  }\n}\n\nexport class FeeRateError extends WalletError {\n  constructor(message = \"Invalid fee rate\") {\n    super(\"FEE_RATE_ERROR\", message);\n    this.name = \"FeeRateError\";\n  }\n}\n\nexport class PsbtError extends WalletError {\n  constructor(message: string) {\n    super(\"PSBT_ERROR\", message);\n    this.name = \"PsbtError\";\n  }\n}\n","/**\n * Shared backoff policy for `scantxoutset` lock contention.\n *\n * `scantxoutset` takes a single node-global, single-threaded lock: only ONE scan\n * runs on the whole node at a time, ACROSS every client of a shared hosted\n * gateway. An overlapping scan — another user of the same node, a retried\n * import, a double-clicked \"refresh balance\", a React StrictMode double-invoke —\n * fails immediately with Bitcoin Core error -8 \"Scan already in progress\". A long\n * scan (~14s on signet) can also exceed the caller's request timeout.\n *\n * Both are transient: the in-progress scan finishes and the slot frees. Failing\n * outright on them turns a shared node into a coin flip, so both scan paths retry\n * with the same bounded backoff.\n *\n * Two paths consume this, and they detect the retryable condition differently\n * because their transports report it differently — the Core RPC client raises\n * typed `RpcError`/`RpcTransportError` (matched on `rpcCode === -8`), while the\n * browser gateway adapter only has message text. What they MUST agree on is the\n * policy — attempt count, delays, jitter — which lives here so the two cannot\n * drift apart again:\n *\n *   - `src/rpc/index.ts`     — `scanForUtxos`, the Core RPC / `Wallet.sync()` path\n *   - `src/lib/rpcClient.ts` — `getUtxos`, the browser hosted-gateway path\n */\n\n/** Total attempts including the first; `1` disables retrying. */\nexport const SCAN_RETRY_ATTEMPTS = 4;\n/** Delay before the first retry, doubling thereafter. */\nexport const SCAN_RETRY_BASE_DELAY_MS = 2_000;\n/** Ceiling for the backoff delay. */\nexport const SCAN_RETRY_MAX_DELAY_MS = 8_000;\n\n/** Promise-based delay. Works in both Node and the browser. */\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Exponential backoff (2s, 4s, 8s, 8s, …) with ±20% jitter, so several clients\n * backing off the same shared node do not resynchronize and collide again on\n * every retry. `attempt` is zero-based.\n */\nexport function scanRetryDelay(\n  attempt: number,\n  baseDelayMs: number = SCAN_RETRY_BASE_DELAY_MS,\n  maxDelayMs: number = SCAN_RETRY_MAX_DELAY_MS,\n): number {\n  const base = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);\n  const jitter = base * 0.2 * (Math.random() * 2 - 1);\n  return Math.max(0, Math.round(base + jitter));\n}\n","import { InvalidAmountError } from \"./errors.js\";\n\nexport const SATS_PER_BTC = 100_000_000n;\nexport const DUST_THRESHOLD_SATS = 546n;\nexport const SAFE_DUST_THRESHOLD_SATS = 1000n;\n\nexport function btcToSats(btc: string | number): bigint {\n  // Number inputs: convert directly via float arithmetic. This avoids the\n  // trap where small values like 1e-8 stringify to \"1e-8\" and fail the\n  // decimal regex below.\n  if (typeof btc === \"number\") {\n    if (!Number.isFinite(btc)) {\n      throw new InvalidAmountError(`Invalid BTC amount: ${btc}`);\n    }\n    const sats = Math.round(btc * 1e8);\n    if (!Number.isSafeInteger(sats)) {\n      throw new InvalidAmountError(\n        `BTC amount out of safe integer range: ${btc}`,\n      );\n    }\n    return BigInt(sats);\n  }\n\n  const trimmed = btc.trim();\n  if (trimmed === \"\" || trimmed === \"-\") {\n    throw new InvalidAmountError(`Invalid BTC amount: \"${btc}\"`);\n  }\n\n  // Preferred path: plain decimal strings convert via bigint arithmetic for\n  // full precision, and the max-decimal-places check kicks in here.\n  const match = trimmed.match(/^-?(\\d+)(?:\\.(\\d+))?$/);\n  if (match) {\n    const negative = trimmed.startsWith(\"-\");\n    const wholePart = match[1];\n    const fracPart = (match[2] ?? \"\").padEnd(8, \"0\");\n\n    if (fracPart.length > 8) {\n      throw new InvalidAmountError(\n        `BTC amount has too many decimal places (max 8): \"${btc}\"`,\n      );\n    }\n\n    const sats = BigInt(wholePart) * SATS_PER_BTC + BigInt(fracPart);\n    return negative ? -sats : sats;\n  }\n\n  // Fallback for strings the decimal regex rejects (e.g. scientific notation\n  // like \"1e-8\"): validate via Number() then use the same float-based path.\n  const asNumber = Number(trimmed);\n  if (!Number.isFinite(asNumber)) {\n    throw new InvalidAmountError(`Invalid BTC amount: \"${btc}\"`);\n  }\n  const sats = Math.round(asNumber * 1e8);\n  if (!Number.isSafeInteger(sats)) {\n    throw new InvalidAmountError(\n      `BTC amount out of safe integer range: \"${btc}\"`,\n    );\n  }\n  return BigInt(sats);\n}\n\nexport function satsToBtc(sats: bigint): string {\n  const negative = sats < 0n;\n  const absolute = negative ? -sats : sats;\n\n  const whole = absolute / SATS_PER_BTC;\n  const frac = absolute % SATS_PER_BTC;\n\n  const fracStr = frac.toString().padStart(8, \"0\").replace(/0+$/, \"\");\n  const prefix = negative ? \"-\" : \"\";\n\n  if (fracStr === \"\") {\n    return `${prefix}${whole}.0`;\n  }\n\n  return `${prefix}${whole}.${fracStr}`;\n}\n\n// Coerce a `bigint | number` amount to a `bigint`, rejecting unsafe floating values\nexport function coerceSats(value: bigint | number | string): bigint {\n  if (typeof value === \"bigint\") {\n    return value;\n  }\n  if (typeof value === \"number\") {\n    if (!Number.isSafeInteger(value)) {\n      throw new InvalidAmountError(\n        `Satoshi value must be a safe integer, got ${value}`,\n      );\n    }\n    return BigInt(value);\n  }\n  if (typeof value === \"string\") {\n    const trimmed = value.trim();\n    if (!/^-?\\d+$/.test(trimmed)) {\n      throw new InvalidAmountError(`Invalid satoshi value: \"${value}\"`);\n    }\n    return BigInt(trimmed);\n  }\n  throw new InvalidAmountError(`Cannot coerce value to sats: ${value}`);\n}\n","/**\n * Thin, dependency-free RPC adapter for the hosted Tachi Bitcoin gateways.\n *\n * These helpers are intentionally decoupled from the existing\n * `src/rpc/` Bitcoin Core client: they target the gateway shape used by\n * {@link WalletNetworkConfig} and work with any network object exposing those\n * endpoints — SIGNET, REGTEST, or any future network defined the same way.\n *\n * TWO BACKENDS, one API\n * ---------------------\n * `getUtxos` / `broadcastTx` can talk to either of two very different servers,\n * and pick per network based on what that network configures:\n *\n *  1. **Esplora/Electrs indexer** (`rpc.esplora`) — PREFERRED when set. Signet\n *     points at Xverse's public signet indexer. An indexer answers an address\n *     query from a prebuilt index: ~0.4s vs ~14s, no node-global lock, and it\n *     sees the MEMPOOL, so `status.confirmed` carries real information.\n *\n *  2. **Bitcoin Core JSON-RPC 1.0 proxy** (`rpc.jsonRpc`) — the authoritative\n *     node, and the automatic fallback. The hosted Tachi daemons\n *     (rpc-signet / rpc-regtest.tachibtc.com) are NOT Esplora servers; they\n *     expose this proxy at `POST /`. Here `getUtxos` scans the on-chain UTXO\n *     set via `scantxoutset` and `broadcastTx` submits via\n *     `sendrawtransaction`. Regtest uses this path exclusively — its chain is\n *     small enough that a full scan is already fast.\n *\n * Both reflect the wallet's real L1 balance (not the daemon's L2/VTXO balance),\n * and for signet both serve the same public signet chain.\n *\n * FALLBACK BOUNDARY: only a TRANSPORT failure from the indexer (connection\n * error, timeout, HTTP status, non-JSON body) falls through to the node. A\n * malformed indexer *body* throws instead — falling back on bad data would let\n * a tampered or broken indexer hide behind the node rather than surface as a\n * wrong balance. A broadcast rejection (4xx) is likewise reported, not retried.\n *\n * All methods on both backends are unauthenticated — no API key is sent.\n *\n * Uses the global `fetch` (Node >= 18 and all browsers).\n */\n\nimport { SCAN_RETRY_ATTEMPTS, scanRetryDelay, sleep } from \"../scan-retry.js\";\nimport { btcToSats } from \"../units.js\";\nimport type { WalletNetworkConfig } from \"../types.js\";\n\n// Only the `rpc` endpoints are required, so callers may pass a full\n// WalletNetworkConfig or any structurally-compatible object.\ntype NetworkWithRpc = Pick<WalletNetworkConfig, \"rpc\">;\n\n// Abort hosted-gateway requests that hang so a stalled endpoint can't wedge the\n// browser flow indefinitely (mirrors the 30s cap on the Core RPC client).\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n// fetch wrapper that aborts after `timeoutMs` and reports the timeout clearly.\nasync function fetchWithTimeout(\n  input: string,\n  init: RequestInit = {},\n  timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<Response> {\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), timeoutMs);\n  try {\n    return await fetch(input, { ...init, signal: controller.signal });\n  } catch (err) {\n    if (err instanceof Error && err.name === \"AbortError\") {\n      throw new Error(`request to ${input} timed out after ${timeoutMs}ms`);\n    }\n    throw err;\n  } finally {\n    clearTimeout(timeout);\n  }\n}\n\n// A confirmation status. The Esplora-style field names are retained as the\n// stable return shape; `getUtxos` populates `confirmed`/`block_height` from the\n// node's `scantxoutset` result (which only sees confirmed outputs).\nexport interface UtxoStatus {\n  confirmed: boolean;\n  block_height?: number;\n  block_hash?: string;\n  block_time?: number;\n}\n\n// A single unspent output, in the Esplora-style shape `getUtxos` returns.\nexport interface RestUtxo {\n  txid: string;\n  vout: number;\n  value: number;\n  status: UtxoStatus;\n}\n\ninterface JsonRpcResponse<T> {\n  result: T;\n  error: { code: number; message: string } | null;\n  id: number | string;\n}\n\nlet rpcIdCounter = 0;\n\n// POST a Bitcoin Core JSON-RPC 1.0 call to the node proxy and return its\n// `result` (throws on RPC/HTTP error). The hosted Tachi daemon serves every\n// method — read and write — without authentication, so no auth header is sent.\nexport async function rpcCall<T = unknown>(\n  network: NetworkWithRpc,\n  method: string,\n  params: readonly unknown[] = [],\n): Promise<T> {\n  const headers: Record<string, string> = {\n    \"content-type\": \"application/json\",\n  };\n  const res = await fetchWithTimeout(network.rpc.jsonRpc, {\n    method: \"POST\",\n    headers,\n    body: JSON.stringify({\n      jsonrpc: \"1.0\",\n      id: `taurus-${++rpcIdCounter}`,\n      method,\n      params,\n    }),\n  });\n  if (!res.ok) {\n    // Surface the body for context — the Tachi proxy returns a plain-text\n    // reason (e.g. \"method not permitted\") that is far more actionable than the\n    // bare status line.\n    const detail = (await res.text().catch(() => \"\")).trim().slice(0, 512);\n    throw new Error(\n      `RPC ${method} failed: HTTP ${res.status} ${res.statusText}${\n        detail ? ` — ${detail}` : \"\"\n      }`,\n    );\n  }\n  const body = (await res.json()) as JsonRpcResponse<T>;\n  if (body.error) {\n    throw new Error(`RPC ${method} error: ${body.error.message}`);\n  }\n  return body.result;\n}\n\n// Validate that an arbitrary value from the gateway has the shape and ranges of\n// a RestUtxo. The hosted gateway is untrusted (it can be MITM'd, DNS-poisoned,\n// or compromised), so we never cast its JSON blindly: a malformed response must\n// fail loudly here rather than surface as a NaN balance or feed a garbage value\n// into a downstream consumer's coin selection.\nfunction assertRestUtxo(u: unknown, i: number): asserts u is RestUtxo {\n  const where = `getUtxos: utxo[${i}]`;\n  if (typeof u !== \"object\" || u === null) {\n    throw new Error(`${where} is not an object`);\n  }\n  const o = u as Record<string, unknown>;\n  if (typeof o.txid !== \"string\" || !/^[0-9a-f]{64}$/i.test(o.txid)) {\n    throw new Error(`${where}.txid is not a 32-byte hex string`);\n  }\n  if (!Number.isSafeInteger(o.vout) || (o.vout as number) < 0) {\n    throw new Error(`${where}.vout is not a non-negative integer`);\n  }\n  // A negative, fractional, or out-of-safe-range value can corrupt every sum\n  // and fee/coin-selection decision derived from it.\n  if (!Number.isSafeInteger(o.value) || (o.value as number) < 0) {\n    throw new Error(`${where}.value is not a non-negative safe integer`);\n  }\n  if (\n    typeof o.status !== \"object\" ||\n    o.status === null ||\n    typeof (o.status as Record<string, unknown>).confirmed !== \"boolean\"\n  ) {\n    throw new Error(`${where}.status.confirmed is not a boolean`);\n  }\n}\n\n// The subset of Bitcoin Core's `scantxoutset` result we consume.\ninterface ScantxoutsetResult {\n  success: boolean;\n  height: number;\n  unspents?: Array<{\n    txid: unknown;\n    vout: unknown;\n    amount: unknown; // BTC (float), per Bitcoin Core\n    height?: unknown;\n  }>;\n}\n\n// Bitcoin addresses are alphanumeric (bech32/bech32m) or base58; neither\n// contains the parentheses/commas/whitespace that would let a caller-supplied\n// string break out of the `addr(...)` output descriptor below. Reject anything\n// else loudly rather than forwarding a crafted descriptor to the node.\nfunction assertPlainAddress(address: string): void {\n  if (!/^[a-zA-Z0-9]+$/.test(address)) {\n    throw new Error(`getUtxos: refusing to scan malformed address`);\n  }\n}\n\n// Retrying a busy/timed-out `scantxoutset` rather than surfacing a hard error to\n// the balance UI. The attempt count and backoff curve are the shared policy in\n// src/scan-retry.ts, which the Core-RPC path (scanForUtxos) uses too.\n//\n// Detection differs from that path, though: this transport has no typed error\n// class, so error -8 arrives as text from rpcCall (\"RPC scantxoutset error:\n// bitcoin rpc error -8: Scan already in progress, …\") and the abort at\n// DEFAULT_TIMEOUT_MS as \"request to … timed out …\". Match either.\nfunction isRetryableScanError(err: unknown): boolean {\n  if (!(err instanceof Error)) return false;\n  return /scan already in progress|rpc error -8|\\btimed out\\b/i.test(\n    err.message,\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Esplora/Electrs indexer path (preferred when `network.rpc.esplora` is set)\n// ---------------------------------------------------------------------------\n//\n// An indexer answers an address query from a prebuilt index, so it returns in\n// milliseconds where `scantxoutset` walks the entire UTXO set (~14s on signet)\n// under a node-global lock. It also sees the mempool, so `status.confirmed`\n// carries real information instead of being hardcoded true.\n\n// A transport-level failure is one where the indexer did not deliver a usable\n// answer: DNS/connection error, timeout, or an HTTP status. These are the only\n// conditions that justify falling back to the authoritative JSON-RPC node —\n// a malformed *body* is a correctness problem and must surface loudly, since\n// silently falling back would let a tampered indexer hide behind the node.\nclass EsploraTransportError extends Error {}\n\n// GET the Esplora UTXO list for an address: `/address/:addr/utxo`, which\n// returns exactly the RestUtxo shape (value already in sats).\nasync function getUtxosEsplora(\n  base: string,\n  address: string,\n): Promise<RestUtxo[]> {\n  let res: Response;\n  try {\n    res = await fetchWithTimeout(\n      `${base}/address/${encodeURIComponent(address)}/utxo`,\n      { headers: { accept: \"application/json\" } },\n    );\n  } catch (err) {\n    throw new EsploraTransportError(\n      `esplora getUtxos: ${err instanceof Error ? err.message : String(err)}`,\n    );\n  }\n  if (!res.ok) {\n    const detail = (await res.text().catch(() => \"\")).trim().slice(0, 256);\n    throw new EsploraTransportError(\n      `esplora getUtxos: HTTP ${res.status} ${res.statusText}${\n        detail ? ` — ${detail}` : \"\"\n      }`,\n    );\n  }\n\n  let body: unknown;\n  try {\n    body = await res.json();\n  } catch (err) {\n    // A body that is not JSON at all is a transport/proxy problem (captive\n    // portal, HTML error page), not indexer data — worth falling back.\n    throw new EsploraTransportError(\n      `esplora getUtxos: response was not JSON: ${\n        err instanceof Error ? err.message : String(err)\n      }`,\n    );\n  }\n\n  // Past this point the indexer DID answer, so any shape violation is a real\n  // data problem and must throw rather than fall back. Same trust boundary as\n  // the scantxoutset path: shape and range only, never authenticity.\n  if (!Array.isArray(body)) {\n    throw new Error(\"getUtxos: esplora response is not an array\");\n  }\n  return body.map((u, i) => {\n    assertRestUtxo(u, i);\n    const status = u.status;\n    return {\n      txid: u.txid,\n      vout: u.vout,\n      value: u.value,\n      status: {\n        confirmed: status.confirmed,\n        ...(typeof status.block_height === \"number\"\n          ? { block_height: status.block_height }\n          : {}),\n        ...(typeof status.block_hash === \"string\"\n          ? { block_hash: status.block_hash }\n          : {}),\n        ...(typeof status.block_time === \"number\"\n          ? { block_time: status.block_time }\n          : {}),\n      },\n    };\n  });\n}\n\n// POST a raw transaction hex to the indexer's `/tx` endpoint; it returns the\n// txid as plain text.\nasync function broadcastTxEsplora(\n  base: string,\n  txHex: string,\n): Promise<string> {\n  let res: Response;\n  try {\n    res = await fetchWithTimeout(`${base}/tx`, {\n      method: \"POST\",\n      headers: { \"content-type\": \"text/plain\" },\n      body: txHex,\n    });\n  } catch (err) {\n    throw new EsploraTransportError(\n      `esplora broadcastTx: ${err instanceof Error ? err.message : String(err)}`,\n    );\n  }\n  const text = (await res.text().catch(() => \"\")).trim();\n  if (!res.ok) {\n    // Distinguish \"the node looked at this transaction and said no\" from \"this\n    // endpoint could not process the request at all\".\n    //\n    // 400/422 is bitcoind REJECTING the tx (bad signature, insufficient fee,\n    // double spend) — a real, deterministic answer. Retrying it against the\n    // JSON-RPC node would produce the same rejection, so surface it.\n    //\n    // Every other status (404 route missing on a mis-set esplora base, 405,\n    // 403 policy, 429 rate limit, 5xx) says nothing about the transaction's\n    // validity. Treating those as rejections would report a perfectly good\n    // transaction as invalid and skip the working fallback — so they fall\n    // through to the node instead.\n    if (res.status === 400 || res.status === 422) {\n      throw new Error(\n        `broadcastTx rejected by esplora: HTTP ${res.status}${\n          text ? ` — ${text.slice(0, 512)}` : \"\"\n        }`,\n      );\n    }\n    throw new EsploraTransportError(\n      `esplora broadcastTx: HTTP ${res.status} ${res.statusText}${\n        text ? ` — ${text.slice(0, 256)}` : \"\"\n      }`,\n    );\n  }\n  if (!/^[0-9a-f]{64}$/i.test(text)) {\n    throw new Error(\n      `broadcastTx: esplora did not return a txid (got \"${text.slice(0, 128)}\")`,\n    );\n  }\n  return text;\n}\n\nexport interface GetUtxosOptions {\n  // How many times to retry a transient scantxoutset failure (busy node /\n  // timeout) before giving up. Defaults to SCAN_RETRY_ATTEMPTS. Set to 0 to\n  // disable retries.\n  scanRetries?: number;\n  // Set false to skip the Esplora indexer and force the JSON-RPC\n  // `scantxoutset` path even when `network.rpc.esplora` is configured.\n  // Defaults to true.\n  preferEsplora?: boolean;\n}\n\n/**\n * Fetch the unspent outputs for an address.\n *\n * Two paths, in preference order:\n *\n *   1. `network.rpc.esplora`, when one is configured (signet is) and\n *      `options.preferEsplora !== false`. An indexer answers in ~0.4s, takes no\n *      node-global lock, and sees the MEMPOOL — so unconfirmed outputs come back\n *      with `status.confirmed === false`.\n *   2. The node's `scantxoutset` JSON-RPC (the Tachi daemon is not Esplora),\n *      which is authoritative and the automatic fallback when the indexer is\n *      unreachable. It does NOT see the mempool, so every UTXO it returns is\n *      reported as confirmed.\n *\n * Only a TRANSPORT failure from the indexer falls back. A malformed indexer\n * response throws, so a broken or tampered indexer surfaces rather than hiding\n * behind the node.\n *\n * Because `scantxoutset` holds a node-global lock (see the retry note above), a\n * concurrent scan on the shared gateway can make path 2 transiently fail; the\n * call retries such failures with backoff before throwing. Retries are bounded\n * by `options.scanRetries`.\n *\n * TRUST BOUNDARY: the returned values are only validated for *shape and range*,\n * not *authenticity*. The hosted gateway is trusted to report the correct UTXO\n * set. This is acceptable for the display-only balance path\n * ({@link useTestnetWallet}'s `refreshBalance`), but any caller that uses these\n * UTXOs to BUILD or SIGN a transaction MUST re-verify each input's value and\n * scriptPubKey against its parent transaction first — see `verifyParentOutput`\n * in `src/psbt.ts`, which defends the Core-RPC signing path against exactly this\n * tampering. Do not trust `value` from this function for fee or coin-selection\n * decisions without that check.\n */\nexport async function getUtxos(\n  network: NetworkWithRpc,\n  address: string,\n  options: GetUtxosOptions = {},\n): Promise<RestUtxo[]> {\n  assertPlainAddress(address);\n\n  // Prefer the indexer when one is configured: ~35x faster on signet, no\n  // node-global lock, and real mempool visibility. Only a transport failure\n  // falls through to the authoritative node below.\n  const esplora = network.rpc.esplora;\n  if (esplora && options.preferEsplora !== false) {\n    try {\n      return await getUtxosEsplora(esplora, address);\n    } catch (err) {\n      if (!(err instanceof EsploraTransportError)) throw err;\n      // Indexer unreachable — fall through to scantxoutset.\n    }\n  }\n\n  const maxRetries = options.scanRetries ?? SCAN_RETRY_ATTEMPTS;\n\n  // Retry only the RPC call itself — a transient busy/timeout failure is worth\n  // re-issuing, but a shape/range error from parsing below is deterministic and\n  // must surface immediately.\n  let result: ScantxoutsetResult;\n  for (let attempt = 0; ; attempt++) {\n    try {\n      result = await rpcCall<ScantxoutsetResult>(network, \"scantxoutset\", [\n        \"start\",\n        [{ desc: `addr(${address})` }],\n      ]);\n      break;\n    } catch (err) {\n      if (attempt < maxRetries && isRetryableScanError(err)) {\n        await sleep(scanRetryDelay(attempt));\n        continue;\n      }\n      throw err;\n    }\n  }\n  if (!result || result.success !== true) {\n    throw new Error(\"getUtxos: scantxoutset did not complete successfully\");\n  }\n  const utxos: RestUtxo[] = (result.unspents ?? []).map((u, i) => {\n    // Convert the BTC float amount to integer sats up front so the safe-integer\n    // guard in assertRestUtxo catches any absurd value the node reports.\n    let valueSats: number;\n    try {\n      valueSats = Number(btcToSats(u.amount as number));\n    } catch {\n      throw new Error(`getUtxos: utxo[${i}].amount is not a valid BTC amount`);\n    }\n    const blockHeight =\n      typeof u.height === \"number\" && Number.isSafeInteger(u.height)\n        ? u.height\n        : undefined;\n    const utxo: RestUtxo = {\n      txid: u.txid as string,\n      vout: u.vout as number,\n      value: valueSats,\n      status: {\n        confirmed: true,\n        ...(blockHeight !== undefined ? { block_height: blockHeight } : {}),\n      },\n    };\n    assertRestUtxo(utxo, i);\n    return utxo;\n  });\n  return utxos;\n}\n\nexport interface BroadcastTxOptions {\n  // Set false to skip the Esplora indexer and broadcast via JSON-RPC\n  // `sendrawtransaction` even when `network.rpc.esplora` is configured.\n  // Defaults to true.\n  preferEsplora?: boolean;\n}\n\n/**\n * Broadcast a raw transaction hex and return the txid.\n *\n * Uses the Esplora indexer's `POST /tx` when one is configured, falling back to\n * the node's `sendrawtransaction` JSON-RPC if the indexer is unreachable. Both\n * ultimately hand the transaction to the same bitcoind on the same chain.\n *\n * A REJECTION (bad signature, insufficient fee, double spend) is returned as a\n * hard error from whichever path saw it — it is not retried against the other,\n * since the second node would reject it identically and a spurious retry could\n * mask the real reason.\n *\n * The hosted Tachi daemon accepts `sendrawtransaction` without authentication,\n * so no API key is required.\n */\nexport async function broadcastTx(\n  network: NetworkWithRpc,\n  txHex: string,\n  options: BroadcastTxOptions = {},\n): Promise<string> {\n  const esplora = network.rpc.esplora;\n  if (esplora && options.preferEsplora !== false) {\n    try {\n      return await broadcastTxEsplora(esplora, txHex);\n    } catch (err) {\n      if (!(err instanceof EsploraTransportError)) throw err;\n      // Indexer unreachable — fall through to the node.\n    }\n  }\n  return rpcCall<string>(network, \"sendrawtransaction\", [txHex]);\n}\n","/**\n * BIP-322 \"simple\" signature verification for P2WPKH and P2TR addresses.\n *\n * NOTE: `bitcoinjs-message` only implements the legacy \"Bitcoin Signed Message\"\n * (ECDSA) scheme — it does NOT support BIP-322. Xverse returns BIP-322\n * signatures, so this module implements the BIP-322 *simple*\n * verification manually with bitcoinjs-lib, following the toSpend / toSign\n * virtual-transaction construction from the BIP:\n *\n *   1. message_hash = tagged_hash(\"BIP0322-signed-message\", message)\n *   2. toSpend: a 0-fee tx whose single input commits to message_hash and\n *      whose single output pays the address' scriptPubKey.\n *   3. toSign:  spends toSpend[0] carrying the provided witness, outputs\n *      an OP_RETURN. Its sighash is what the wallet actually signed.\n *\n * Verification then checks the witness signature against that sighash and\n * confirms the key commits to the address.\n */\n\nimport * as ecc from \"@bitcoinerlab/secp256k1\";\nimport * as bitcoin from \"bitcoinjs-lib\";\nimport type { NetworkName } from \"../types.js\";\n\n// Initialise the secp256k1 backend (idempotent across the SDK).\nbitcoin.initEccLib(ecc);\n\nexport interface VerifyResult {\n  valid: boolean;\n  reason?: string;\n  // The network the address was resolved on. Present whenever the address\n  // parsed (regardless of whether signature verification then succeeded), so\n  // callers using a signature as a proof-of-ownership can assert the\n  // address↔network binding. The testnet family (testnet/signet) shares an\n  // identical address encoding and is reported as \"testnet\" when no expected\n  // network was supplied; pass `expectedNetwork` to bind precisely.\n  network?: NetworkName;\n}\n\nconst BIP322_TAG = \"BIP0322-signed-message\";\n\n// tagged_hash(tag, msg) = SHA256(SHA256(tag) || SHA256(tag) || msg)\nfunction taggedHash(tag: string, msg: Uint8Array): Buffer {\n  const tagHash = bitcoin.crypto.sha256(Buffer.from(tag, \"utf8\"));\n  return Buffer.from(\n    bitcoin.crypto.sha256(Buffer.concat([tagHash, tagHash, msg])),\n  );\n}\n\n// Resolve an address to its scriptPubKey along with the network it parsed on.\n// The scriptPubKey is identical across networks (only the HRP/version differs),\n// so without a binding we try each distinct bitcoinjs network in turn. When\n// `expected` is supplied we resolve *only* against that network, so a signature\n// over an address on some other network is rejected rather than silently\n// accepted (see VerifyResult.network).\nfunction getOutputScript(\n  address: string,\n  expected?: NetworkName,\n): { scriptPubKey: Uint8Array; network: NetworkName } | null {\n  // Address decoding is purely about the encoding family (HRP/version bytes),\n  // independent of which networks the registry exposes as selectable — signet\n  // reuses testnet's encoding, so we map each NetworkName straight onto its\n  // bitcoinjs-lib Network object rather than going through getNetwork().\n  const family: Record<NetworkName, bitcoin.networks.Network> = {\n    mainnet: bitcoin.networks.bitcoin,\n    testnet: bitcoin.networks.testnet,\n    signet: bitcoin.networks.testnet,\n    regtest: bitcoin.networks.regtest,\n  };\n  const candidates: { name: NetworkName; network: bitcoin.networks.Network }[] =\n    expected\n      ? [{ name: expected, network: family[expected] }]\n      : [\n          { name: \"mainnet\", network: bitcoin.networks.bitcoin },\n          { name: \"testnet\", network: bitcoin.networks.testnet },\n          { name: \"regtest\", network: bitcoin.networks.regtest },\n        ];\n  for (const { name, network } of candidates) {\n    try {\n      return {\n        scriptPubKey: bitcoin.address.toOutputScript(address, network),\n        network: name,\n      };\n    } catch {\n      // Not valid on this network — try the next.\n    }\n  }\n  return null;\n}\n\nfunction buildToSpend(\n  scriptPubKey: Uint8Array,\n  messageHash: Buffer,\n): bitcoin.Transaction {\n  const tx = new bitcoin.Transaction();\n  tx.version = 0;\n  tx.locktime = 0;\n  // scriptSig = OP_0 PUSH32[message_hash]\n  const scriptSig = bitcoin.script.compile([bitcoin.opcodes.OP_0, messageHash]);\n  // prevout = (0x00 * 32 : 0xffffffff), nSequence = 0\n  tx.addInput(Buffer.alloc(32, 0), 0xffffffff, 0, scriptSig);\n  tx.addOutput(Buffer.from(scriptPubKey), 0n);\n  return tx;\n}\n\nfunction buildToSign(\n  toSpend: bitcoin.Transaction,\n  witness: Uint8Array[],\n): bitcoin.Transaction {\n  const tx = new bitcoin.Transaction();\n  tx.version = 0;\n  tx.locktime = 0;\n  // Spends toSpend[0] with an empty scriptSig and nSequence = 0.\n  tx.addInput(toSpend.getHash(false), 0, 0);\n  // Single OP_RETURN output, value 0.\n  tx.addOutput(bitcoin.script.compile([bitcoin.opcodes.OP_RETURN]), 0n);\n  tx.setWitness(0, witness);\n  return tx;\n}\n\n// Decode a serialized witness stack (compactSize count, then length-prefixed\n// items) as produced by a BIP-322 simple signature.\nfunction parseWitnessStack(buf: Buffer): Uint8Array[] {\n  let offset = 0;\n  const readCompactSize = (): number => {\n    if (offset >= buf.length) throw new Error(\"truncated witness\");\n    const first = buf[offset++];\n    if (first < 0xfd) return first;\n    if (first === 0xfd) {\n      const v = buf.readUInt16LE(offset);\n      offset += 2;\n      return v;\n    }\n    if (first === 0xfe) {\n      const v = buf.readUInt32LE(offset);\n      offset += 4;\n      return v;\n    }\n    throw new Error(\"witness item too large\");\n  };\n\n  // A BIP-322 simple signature (P2WPKH/P2TR) has at most a handful of small\n  // witness items, so cap count and per-item length to reject crafted inputs\n  // that declare huge sizes to force disproportionate work.\n  const MAX_WITNESS_ITEMS = 8;\n  const MAX_WITNESS_ITEM_LEN = 1024;\n\n  const count = readCompactSize();\n  if (count > MAX_WITNESS_ITEMS) throw new Error(\"witness stack too large\");\n  const items: Uint8Array[] = [];\n  for (let i = 0; i < count; i++) {\n    const len = readCompactSize();\n    if (len > MAX_WITNESS_ITEM_LEN) throw new Error(\"witness item too large\");\n    if (offset + len > buf.length) throw new Error(\"truncated witness item\");\n    items.push(buf.subarray(offset, offset + len));\n    offset += len;\n  }\n  return items;\n}\n\nfunction verifyP2wpkh(\n  scriptPubKey: Uint8Array,\n  toSign: bitcoin.Transaction,\n  witness: Uint8Array[],\n): VerifyResult {\n  if (witness.length < 2) {\n    return {\n      valid: false,\n      reason: \"P2WPKH witness must be [signature, pubkey]\",\n    };\n  }\n  const sig = Buffer.from(witness[0]);\n  const pubkey = Buffer.from(witness[1]);\n  const keyhash = scriptPubKey.subarray(2); // 20-byte program\n\n  if (!Buffer.from(bitcoin.crypto.hash160(pubkey)).equals(keyhash)) {\n    return { valid: false, reason: \"public key does not match address\" };\n  }\n\n  const decoded = bitcoin.script.signature.decode(sig);\n  // BIP143 scriptCode for P2WPKH: OP_DUP OP_HASH160 <kh> OP_EQUALVERIFY OP_CHECKSIG\n  const scriptCode = bitcoin.script.compile([\n    bitcoin.opcodes.OP_DUP,\n    bitcoin.opcodes.OP_HASH160,\n    keyhash,\n    bitcoin.opcodes.OP_EQUALVERIFY,\n    bitcoin.opcodes.OP_CHECKSIG,\n  ]);\n  const sighash = toSign.hashForWitnessV0(0, scriptCode, 0n, decoded.hashType);\n  const valid = ecc.verify(sighash, pubkey, decoded.signature);\n  return valid\n    ? { valid: true }\n    : { valid: false, reason: \"signature verification failed\" };\n}\n\nfunction verifyP2tr(\n  scriptPubKey: Uint8Array,\n  toSign: bitcoin.Transaction,\n  witness: Uint8Array[],\n): VerifyResult {\n  if (witness.length < 1) {\n    return { valid: false, reason: \"P2TR witness must contain a signature\" };\n  }\n  let sig = Buffer.from(witness[0]);\n  let hashType = bitcoin.Transaction.SIGHASH_DEFAULT;\n  if (sig.length === 65) {\n    hashType = sig[64];\n    sig = sig.subarray(0, 64);\n  } else if (sig.length !== 64) {\n    return {\n      valid: false,\n      reason: `unexpected schnorr signature length ${sig.length}`,\n    };\n  }\n  const outputKey = scriptPubKey.subarray(2); // 32-byte x-only key\n  const sighash = toSign.hashForWitnessV1(\n    0,\n    [Buffer.from(scriptPubKey)],\n    [0n],\n    hashType,\n  );\n  const valid = ecc.verifySchnorr(sighash, outputKey, sig);\n  return valid\n    ? { valid: true }\n    : { valid: false, reason: \"schnorr signature verification failed\" };\n}\n\n/**\n * Verify a BIP-322 simple signature for a P2WPKH or P2TR address.\n *\n * @param address         the signer's address (P2WPKH `bc1q…` / P2TR `bc1p…`)\n * @param message         the signed message (UTF-8)\n * @param signatureBase64 base64-encoded witness stack (as returned by wallets)\n * @param expectedNetwork when set, the address must parse on this network or\n *                        verification fails — bind this for proof-of-ownership\n *                        use cases so a signature over an address on a\n *                        different network is not silently accepted. The\n *                        resolved network is echoed back on `VerifyResult.network`.\n */\nexport function verifyBip322Signature(\n  address: string,\n  message: string,\n  signatureBase64: string,\n  expectedNetwork?: NetworkName,\n): VerifyResult {\n  try {\n    const resolved = getOutputScript(address, expectedNetwork);\n    if (!resolved) {\n      return {\n        valid: false,\n        reason: expectedNetwork\n          ? `address not valid on expected network ${expectedNetwork}: ${address}`\n          : `unrecognized address: ${address}`,\n      };\n    }\n    const { scriptPubKey, network } = resolved;\n\n    const messageHash = taggedHash(BIP322_TAG, Buffer.from(message, \"utf8\"));\n    const toSpend = buildToSpend(scriptPubKey, messageHash);\n    const witness = parseWitnessStack(Buffer.from(signatureBase64, \"base64\"));\n    const toSign = buildToSign(toSpend, witness);\n\n    // P2WPKH: OP_0 <20-byte keyhash>\n    if (\n      scriptPubKey.length === 22 &&\n      scriptPubKey[0] === 0x00 &&\n      scriptPubKey[1] === 0x14\n    ) {\n      return { ...verifyP2wpkh(scriptPubKey, toSign, witness), network };\n    }\n    // P2TR: OP_1 <32-byte x-only key>\n    if (\n      scriptPubKey.length === 34 &&\n      scriptPubKey[0] === 0x51 &&\n      scriptPubKey[1] === 0x20\n    ) {\n      return { ...verifyP2tr(scriptPubKey, toSign, witness), network };\n    }\n    return {\n      valid: false,\n      reason: \"only P2WPKH and P2TR addresses are supported\",\n      network,\n    };\n  } catch (err) {\n    return {\n      valid: false,\n      reason: err instanceof Error ? err.message : String(err),\n    };\n  }\n}\n","/**\n * PSBT construction, signing, and finalization (p2wpkh only).\n *\n * p2wpkh inputs are witness-based: the prevout (scriptPubKey + value) is\n * supplied as `witnessUtxo`. The full parent tx is still fetched via RPC and\n * verified, so a tampered scanner result cannot misstate an input's value or\n * script before it is signed over.\n *\n * Per address type (for the types returning in a future release):\n *   p2pkh       - non-witness; requires full parent tx as `nonWitnessUtxo`.\n *   p2sh-p2wpkh - witness; also needs redeem script.\n *   p2wpkh      - witness only.\n *   p2tr        - taproot key-path; signer tweaked per BIP86 before schnorr signing.\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport type { Keystore } from \"./derivation.js\";\nimport { PsbtError } from \"./errors.js\";\nimport type { RpcClient } from \"./rpc/types.js\";\nimport type { AddressType, Utxo } from \"./types.js\";\n\nexport type { RpcClient } from \"./rpc/types.js\";\n\n// BIP125 opt-in RBF sequence number.\nexport const RBF_SEQUENCE = 0xfffffffd;\n\ninterface RawTransaction {\n  hex?: string;\n  [key: string]: unknown;\n}\n\nexport interface BuildPsbtArgs {\n  network: bitcoin.networks.Network;\n  inputs: readonly Utxo[];\n  outputs: readonly { address: string; valueSats: bigint }[];\n  inputAddressType: AddressType;\n  rpc: RpcClient;\n}\n\nexport async function buildPsbt(args: BuildPsbtArgs): Promise<bitcoin.Psbt> {\n  const psbt = new bitcoin.Psbt({ network: args.network });\n\n  for (const input of args.inputs) {\n    switch (args.inputAddressType) {\n      case \"p2wpkh\": {\n        const parent = await args.rpc.call<RawTransaction>(\n          \"getrawtransaction\",\n          [input.txid, true],\n        );\n        if (!parent.hex || typeof parent.hex !== \"string\") {\n          throw new PsbtError(\n            `getrawtransaction did not return hex for parent ${input.txid}`,\n          );\n        }\n        const parentBuf = Buffer.from(parent.hex, \"hex\");\n        verifyParentOutput(parentBuf, input);\n        // Witness input: supply only the spent output (script + value), verified\n        // above against the real parent tx. No nonWitnessUtxo needed for segwit.\n        psbt.addInput({\n          hash: input.txid,\n          index: input.vout,\n          witnessUtxo: {\n            script: Buffer.from(input.scriptPubKey, \"hex\"),\n            value: input.valueSats,\n          },\n          sequence: RBF_SEQUENCE,\n        });\n        break;\n      }\n      default: {\n        const exhaustive: never = args.inputAddressType;\n        throw new PsbtError(`Unhandled input type: ${exhaustive as string}`);\n      }\n    }\n  }\n\n  for (const out of args.outputs) {\n    psbt.addOutput({\n      address: out.address,\n      value: out.valueSats,\n    });\n  }\n\n  return psbt;\n}\n\n// Parses the parent transaction and confirms the referenced prevout matches\n// the scanner-reported value and scriptPubKey. Any mismatch throws before\n// the input is added to the PSBT, so a tampered RPC cannot trick a signer\n// into producing a transaction that over-allocates value.\nfunction verifyParentOutput(parentBuf: Buffer, input: Utxo): void {\n  let parentTx: bitcoin.Transaction;\n  try {\n    parentTx = bitcoin.Transaction.fromBuffer(parentBuf);\n  } catch (err) {\n    throw new PsbtError(\n      `Failed to parse parent tx ${input.txid}: ${\n        err instanceof Error ? err.message : String(err)\n      }`,\n    );\n  }\n  if (parentTx.getId() !== input.txid) {\n    throw new PsbtError(\n      `Parent tx id mismatch: expected ${input.txid}, got ${parentTx.getId()}`,\n    );\n  }\n  const prevout = parentTx.outs[input.vout];\n  if (!prevout) {\n    throw new PsbtError(`Parent tx ${input.txid} has no vout ${input.vout}`);\n  }\n  const prevoutValue = BigInt(prevout.value);\n  if (prevoutValue !== input.valueSats) {\n    throw new PsbtError(\n      `UTXO amount mismatch for ${input.txid}:${input.vout} — ` +\n        `scan reported ${input.valueSats} sats, parent tx has ${prevoutValue}`,\n    );\n  }\n  const prevoutScriptHex = Buffer.from(prevout.script).toString(\"hex\");\n  if (prevoutScriptHex !== input.scriptPubKey) {\n    throw new PsbtError(\n      `UTXO scriptPubKey mismatch for ${input.txid}:${input.vout}`,\n    );\n  }\n}\n\nexport interface SignPsbtArgs {\n  psbt: bitcoin.Psbt;\n  inputs: readonly Utxo[];\n  keystore: Keystore;\n}\n\n// Signs every PSBT input via the keystore using each input's derivation path.\nexport function signPsbt(args: SignPsbtArgs): void {\n  const { psbt, inputs, keystore } = args;\n\n  inputs.forEach((utxo, i) => {\n    const signer = keystore.signerFor(utxo.change, utxo.addressIndex);\n    psbt.signInput(i, signer);\n  });\n\n  try {\n    psbt.finalizeAllInputs();\n  } catch (err) {\n    throw new PsbtError(\n      `Failed to finalize PSBT: ${err instanceof Error ? err.message : String(err)}`,\n    );\n  }\n}\n\nexport function extractTransaction(psbt: bitcoin.Psbt): {\n  txid: string;\n  hex: string;\n  vsize: number;\n} {\n  const tx = psbt.extractTransaction();\n  return {\n    txid: tx.getId(),\n    hex: tx.toHex(),\n    vsize: tx.virtualSize(),\n  };\n}\n\nexport interface WalletSigningRequest {\n  // Xverse / sats-connect consumes the PSBT as base64.\n  psbtBase64: string;\n  // Hex serialization for any wallet that consumes a PSBT as hex.\n  psbtHex: string;\n  // Which inputs the external wallet should sign. Defaults to every input,\n  // all attributed to `signerAddress`.\n  inputsToSign: { address: string; signingIndexes: number[] }[];\n}\n\n// Serialize a built (unsigned) PSBT into the shapes browser wallets expect.\n// This does NOT sign — the internal-signer path (signPsbt) stays untouched, so\n// the same builder output can be routed to either signer.\nexport function prepareForWalletSigning(\n  psbt: bitcoin.Psbt,\n  signerAddress: string,\n): WalletSigningRequest {\n  const signingIndexes = psbt.txInputs.map((_, index) => index);\n  return {\n    psbtBase64: psbt.toBase64(),\n    psbtHex: psbt.toHex(),\n    inputsToSign: [{ address: signerAddress, signingIndexes }],\n  };\n}\n","/**\n * Xverse browser-extension connector, wrapping sats-connect.\n *\n * sats-connect's `getAddress` / `signTransaction` / `signMessage` use a\n * callback (`onFinish` / `onCancel`) style; each helper here adapts them into\n * a Promise. Install peer dep: `sats-connect`.\n */\n\nimport {\n  AddressPurpose,\n  BitcoinNetworkType,\n  MessageSigningProtocols,\n  getAddress,\n  signMessage,\n  signTransaction,\n  type Address,\n} from \"sats-connect\";\n\nimport type { WalletChainName, WalletNetworkConfig } from \"../types.js\";\n\n// Only the network name is needed to pick the Xverse network enum.\ntype NamedNetwork = Pick<WalletNetworkConfig, \"name\">;\n\n// The import chains the Xverse connector can actually handshake on. Exported so\n// a consumer (and useTestnetWallet) can GATE the chain selector up front rather\n// than discovering the limitation by catching a thrown connect error. Regtest is\n// absent by design — Xverse has no regtest mode; import regtest via a seed\n// phrase (see ./seed.ts).\nexport const XVERSE_SUPPORTED_CHAINS: readonly WalletChainName[] = [\"signet\"];\n\n// True when the Xverse extension can be connected on `chain`.\nexport function isXverseSupportedChain(chain: string): boolean {\n  return (XVERSE_SUPPORTED_CHAINS as readonly string[]).includes(chain);\n}\n\nexport interface XverseConnection {\n  paymentAddress: string;\n  ordinalsAddress: string;\n  publicKey: string;\n  walletType: \"xverse\";\n}\n\nexport interface XverseInputToSign {\n  address: string;\n  signingIndexes: number[];\n}\n\nexport interface SignedMessage {\n  signature: string;\n  protocol: \"bip322\";\n}\n\n// Default to Signet — the import-wallet flow's default chain; the hook passes\n// the real network so signing matches the connected account.\nconst DEFAULT_NETWORK_TYPE = BitcoinNetworkType.Signet;\n\n// Map an SDK network name onto Xverse's BitcoinNetworkType.\n//\n// The Xverse connector currently supports signet only. Any other network name\n// (including regtest) is rejected rather than silently handshaking on the wrong\n// chain — regtest import must use a non-Xverse path.\nfunction toBitcoinNetworkType(name: string): BitcoinNetworkType {\n  switch (name) {\n    case \"signet\":\n      return BitcoinNetworkType.Signet;\n    default:\n      throw new Error(\n        `Xverse: unsupported network \"${name}\" (supported: ` +\n          `${XVERSE_SUPPORTED_CHAINS.join(\", \")}). Import this chain with a ` +\n          `seed phrase instead — see connectSeed() in wallets/seed.ts.`,\n      );\n  }\n}\n\nexport async function connectXverse(\n  network: NamedNetwork,\n): Promise<XverseConnection> {\n  const type = toBitcoinNetworkType(network.name);\n\n  const addresses = await new Promise<Address[]>((resolve, reject) => {\n    getAddress({\n      payload: {\n        purposes: [AddressPurpose.Payment, AddressPurpose.Ordinals],\n        message: \"Connect to Taurus Wallet Aggregator\",\n        network: { type },\n      },\n      onFinish: (response) => resolve(response.addresses),\n      onCancel: () => reject(new Error(\"Xverse connection cancelled\")),\n    }).catch(reject);\n  });\n\n  const payment = addresses.find((a) => a.purpose === AddressPurpose.Payment);\n  const ordinals = addresses.find((a) => a.purpose === AddressPurpose.Ordinals);\n  if (!payment) {\n    throw new Error(\"Xverse did not return a payment address\");\n  }\n\n  return {\n    paymentAddress: payment.address,\n    ordinalsAddress: ordinals?.address ?? payment.address,\n    publicKey: payment.publicKey,\n    walletType: \"xverse\",\n  };\n}\n\nexport async function signPsbtXverse(\n  psbtBase64: string,\n  inputsToSign: XverseInputToSign[],\n  network?: NamedNetwork,\n): Promise<string> {\n  const type = network\n    ? toBitcoinNetworkType(network.name)\n    : DEFAULT_NETWORK_TYPE;\n\n  return new Promise<string>((resolve, reject) => {\n    signTransaction({\n      payload: {\n        network: { type },\n        message: \"Sign transaction\",\n        psbtBase64,\n        broadcast: false,\n        inputsToSign,\n      },\n      onFinish: (response) => resolve(response.psbtBase64),\n      onCancel: () => reject(new Error(\"Xverse signing cancelled\")),\n    }).catch(reject);\n  });\n}\n\nexport async function signMessageXverse(\n  message: string,\n  address: string,\n  network?: NamedNetwork,\n): Promise<SignedMessage> {\n  const type = network\n    ? toBitcoinNetworkType(network.name)\n    : DEFAULT_NETWORK_TYPE;\n\n  const signature = await new Promise<string>((resolve, reject) => {\n    signMessage({\n      payload: {\n        network: { type },\n        address,\n        message,\n        protocol: MessageSigningProtocols.BIP322,\n      },\n      onFinish: (sig) => resolve(sig),\n      onCancel: () => reject(new Error(\"Xverse message signing cancelled\")),\n    }).catch(reject);\n  });\n\n  return { signature, protocol: \"bip322\" };\n}\n\n// Ambient declaration so we can probe for the injected provider without\n// pulling the whole DOM lib into the SDK's tsconfig.\ninterface XverseWindow {\n  XverseProviders?: { BitcoinProvider?: unknown };\n}\ndeclare const window: XverseWindow | undefined;\n\nexport function isXverseInstalled(): boolean {\n  return (\n    typeof window !== \"undefined\" &&\n    Boolean(window?.XverseProviders?.BitcoinProvider)\n  );\n}\n","/**\n * Seed-phrase (BIP39 mnemonic) import connector — the non-extension half of the\n * browser import-wallet flow, and the ONLY path that works on both supported\n * chains.\n *\n * Why this exists\n * ---------------\n * The import flow offers two chains, signet and regtest, but the Xverse\n * connector speaks signet only (`connectXverse` throws for regtest — see\n * `toBitcoinNetworkType` in ./xverse.ts). Without a local signer, selecting\n * regtest in the UI could only ever produce an error. This module closes that\n * gap: a mnemonic is derived locally, so it works identically on signet and\n * regtest, and both chains resolve their balance through the same hosted Tachi\n * JSON-RPC gateway (`scantxoutset`) that already backs regtest.\n *\n * Capability boundary vs. Xverse\n * ------------------------------\n *   • Key custody is LOCAL. The mnemonic never leaves the process, and the\n *     derived account node lives in a `Keystore` whose `lock()` zeros it. The\n *     caller owns the mnemonic string itself (JS strings are immutable and\n *     cannot be wiped — see the security note in the README).\n *   • Signing is SYNCHRONOUS and local: `signPsbtSeed` signs with the derived\n *     key rather than round-tripping to an extension.\n *   • BIP-322 message signing is NOT implemented here. The SDK ships a\n *     verifier (`verifyBip322Signature`) but no signer, so seed wallets have no\n *     message-signing path; use Xverse (signet) if a consumer needs one.\n *\n * Chain switching is deliberately mnemonic-free: signet and regtest share\n * BIP-44 coin type 1, so a keystore imported on one is byte-identical to the\n * other and only the address ENCODING differs (\"tb\" vs \"bcrt\" HRP). See\n * `deriveSeedAddress`, which re-encodes from the retained account xpub instead\n * of asking for the seed phrase a second time.\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { Keystore, deriveAddressFromAccountNode } from \"../derivation.js\";\nimport { PsbtError } from \"../errors.js\";\nimport type {\n  AddressType,\n  WalletChainName,\n  WalletNetworkConfig,\n} from \"../types.js\";\n\n// The SDK's AddressType union is p2wpkh-only today; named here so the intent is\n// explicit at each derivation call rather than a bare string literal.\nconst SEED_ADDRESS_TYPE: AddressType = \"p2wpkh\";\n\n// Both import chains are locally derivable, so unlike Xverse (signet only) the\n// seed path supports every supported wallet chain.\nexport const SEED_SUPPORTED_CHAINS: readonly WalletChainName[] = [\n  \"signet\",\n  \"regtest\",\n];\n\n/**\n * The result of importing a mnemonic: the connected account's public material,\n * shaped to parallel {@link XverseConnection} so the hook can treat both wallet\n * types uniformly.\n *\n * Public material only — the signer lives in the `Keystore` returned alongside\n * this by {@link connectSeed}, never on this object.\n */\nexport interface SeedConnection {\n  paymentAddress: string;\n  // Compressed 33-byte pubkey (hex) for the imported address.\n  publicKey: string;\n  walletType: \"seed\";\n  // Full derivation path of `paymentAddress`, e.g. `m/84'/1'/0'/0/0`.\n  path: string;\n  // scriptPubKey (hex) of `paymentAddress`.\n  scriptPubKey: string;\n  // BIP32 master fingerprint (hex) — identifies WHICH seed this came from.\n  fingerprint: string;\n  // Account-level xpub (`m/84'/1'/account'`), for watch-only descriptors.\n  accountXpub: string;\n  // Chain this address was encoded for.\n  chain: WalletChainName;\n  // BIP-44 account index, and the chain/index of `paymentAddress` within it.\n  account: number;\n  change: boolean;\n  index: number;\n}\n\nexport interface ImportSeedOptions {\n  // Optional BIP39 passphrase (\"25th word\"). Must match what the wallet was\n  // created with — a different passphrase silently derives a DIFFERENT wallet\n  // rather than failing, so surface this in the UI as its own field.\n  passphrase?: string;\n  // BIP-44 account index (default 0).\n  account?: number;\n  // Address index within the chain (default 0).\n  index?: number;\n  // Derive from the change chain instead of receive (default false).\n  change?: boolean;\n}\n\nfunction assertIndex(label: string, value: number): void {\n  if (!Number.isInteger(value) || value < 0) {\n    throw new RangeError(\n      `connectSeed: ${label} must be a non-negative integer, got ${value}`,\n    );\n  }\n}\n\n/**\n * Import a BIP39 mnemonic and derive the account's payment address on `network`.\n *\n * Throws `InvalidMnemonicError` for a mnemonic that fails BIP39 checksum\n * validation, so a mistyped word is rejected at import rather than silently\n * producing a valid-looking address for the wrong wallet.\n *\n * Returns the public connection details AND the live `Keystore`. The caller\n * owns the keystore's lifetime and MUST call `keystore.lock()` on disconnect —\n * {@link useTestnetWallet} does this for the hook-driven flow.\n */\nexport function connectSeed(\n  mnemonic: string,\n  network: WalletNetworkConfig,\n  options: ImportSeedOptions = {},\n): { connection: SeedConnection; keystore: Keystore } {\n  const account = options.account ?? 0;\n  const index = options.index ?? 0;\n  const change = options.change ?? false;\n  assertIndex(\"account\", account);\n  assertIndex(\"index\", index);\n\n  // Throws InvalidMnemonicError on a bad checksum, before any derivation.\n  const keystore = Keystore.fromMnemonic(\n    mnemonic,\n    options.passphrase ?? \"\",\n    network,\n    SEED_ADDRESS_TYPE,\n    account,\n  );\n\n  try {\n    const derived = keystore.deriveAddress(change, index);\n    return {\n      connection: {\n        paymentAddress: derived.address,\n        publicKey: derived.publicKey,\n        walletType: \"seed\",\n        path: derived.path,\n        scriptPubKey: derived.scriptPubKey,\n        fingerprint: keystore.fingerprint,\n        accountXpub: keystore.accountXpub,\n        chain: network.name as WalletChainName,\n        account,\n        change,\n        index,\n      },\n      keystore,\n    };\n  } catch (err) {\n    // Never leak a live keystore if derivation fails partway.\n    keystore.lock();\n    throw err;\n  }\n}\n\n/**\n * Re-encode an already-imported account's address for a different chain,\n * WITHOUT the mnemonic.\n *\n * signet and regtest both use BIP-44 coin type 1, so the account node — and\n * therefore the private key and pubkey — is identical on both; only the bech32\n * HRP differs (\"tb\" vs \"bcrt\"). This derives from the keystore's retained\n * account xpub, so switching chains never asks the user for their seed phrase\n * again and never re-materializes seed bytes.\n *\n * Throws if the target network's coin type differs from the imported one: that\n * would be a genuinely different BIP-44 account and MUST be re-imported rather\n * than silently re-encoded under the wrong path.\n */\nexport function deriveSeedAddress(\n  keystore: Keystore,\n  network: WalletNetworkConfig,\n  options: { change?: boolean; index?: number } = {},\n): SeedConnection {\n  const change = options.change ?? false;\n  const index = options.index ?? 0;\n  assertIndex(\"index\", index);\n\n  if (keystore.network.coinType !== network.coinType) {\n    throw new Error(\n      `deriveSeedAddress: cannot re-encode an account derived for coin type ` +\n        `${keystore.network.coinType} (${keystore.network.name}) onto ` +\n        `${network.name} (coin type ${network.coinType}) — that is a different ` +\n        `BIP-44 account. Re-import the mnemonic on ${network.name} instead.`,\n    );\n  }\n\n  const derived = deriveAddressFromAccountNode(\n    keystore.watchOnlyAccountNode(),\n    SEED_ADDRESS_TYPE,\n    network,\n    keystore.account,\n    change,\n    index,\n  );\n\n  return {\n    paymentAddress: derived.address,\n    publicKey: derived.publicKey,\n    walletType: \"seed\",\n    path: derived.path,\n    scriptPubKey: derived.scriptPubKey,\n    fingerprint: keystore.fingerprint,\n    accountXpub: keystore.accountXpub,\n    chain: network.name as WalletChainName,\n    account: keystore.account,\n    change,\n    index,\n  };\n}\n\n/**\n * Which PSBT inputs to sign, and with which derived key.\n *\n * `change`/`addressIndex` default to the imported address's own position, which\n * is the common case for the single-address import flow. Provide them\n * explicitly when a PSBT spends from several addresses of the same account.\n */\nexport interface SeedInputToSign {\n  // Index of the input within the PSBT.\n  index: number;\n  // Derivation chain of the key that owns this input (default: the imported\n  // address's chain).\n  change?: boolean;\n  // Address index of the key that owns this input (default: the imported\n  // address's index).\n  addressIndex?: number;\n}\n\n// PSBT base64 always begins with the magic \"cHNidP\"; 'H'/'N'/'s'/'P' are not\n// hex digits, so an even-length all-hex string is unambiguously the hex\n// serialization and never a base64 PSBT.\nfunction parsePsbt(serialized: string, network: bitcoin.Network): bitcoin.Psbt {\n  const trimmed = serialized.trim();\n  try {\n    if (/^[0-9a-fA-F]+$/.test(trimmed) && trimmed.length % 2 === 0) {\n      return bitcoin.Psbt.fromHex(trimmed, { network });\n    }\n    return bitcoin.Psbt.fromBase64(trimmed, { network });\n  } catch (err) {\n    throw new PsbtError(\n      `signPsbtSeed: could not parse PSBT as base64 or hex: ${\n        err instanceof Error ? err.message : String(err)\n      }`,\n    );\n  }\n}\n\nexport interface SignPsbtSeedOptions {\n  // Inputs to sign. Omit to sign EVERY input with the default signer below.\n  inputs?: readonly SeedInputToSign[];\n  // Derivation of the imported address, used for any input that does not name\n  // its own. Defaults to the receive chain at index 0.\n  defaultChange?: boolean;\n  defaultIndex?: number;\n  // Finalize the inputs this call signed (default true), mirroring the internal\n  // keystore path so `extractTransaction()` works immediately. Set false to\n  // keep a multi-signer PSBT open for other signers.\n  finalize?: boolean;\n}\n\n/**\n * Sign a PSBT locally with the imported seed's keys and return the updated PSBT\n * as base64.\n *\n * Accepts the PSBT as base64 or hex, matching the hook's `signPsbt` contract.\n * Only the named inputs are signed — an input this account does not own is left\n * untouched, so a multi-provider PSBT cannot be over-signed here.\n *\n * SECURITY: signing attests to whatever the PSBT says. This function does NOT\n * verify each input's value or scriptPubKey against its parent transaction —\n * a PSBT built from gateway-reported UTXOs must be checked with\n * `verifyParentOutput` (src/psbt.ts) first, or a tampered gateway can induce a\n * signature over an unintended fee. See the TRUST BOUNDARY note on `getUtxos`.\n */\nexport function signPsbtSeed(\n  psbtBase64OrHex: string,\n  keystore: Keystore,\n  network: WalletNetworkConfig,\n  options: SignPsbtSeedOptions = {},\n): string {\n  const psbt = parsePsbt(psbtBase64OrHex, network.network);\n  const defaultChange = options.defaultChange ?? false;\n  const defaultIndex = options.defaultIndex ?? 0;\n  const finalize = options.finalize ?? true;\n\n  const targets: SeedInputToSign[] =\n    options.inputs && options.inputs.length > 0\n      ? [...options.inputs]\n      : psbt.txInputs.map((_, index) => ({ index }));\n\n  if (targets.length === 0) {\n    throw new PsbtError(\"signPsbtSeed: PSBT has no inputs to sign\");\n  }\n\n  const signedIndexes: number[] = [];\n  for (const target of targets) {\n    if (\n      !Number.isInteger(target.index) ||\n      target.index < 0 ||\n      target.index >= psbt.inputCount\n    ) {\n      throw new PsbtError(\n        `signPsbtSeed: input index ${target.index} is out of range ` +\n          `(PSBT has ${psbt.inputCount} input(s))`,\n      );\n    }\n    // Throws WalletLockedError if the keystore was locked (disconnected).\n    const signer = keystore.signerFor(\n      target.change ?? defaultChange,\n      target.addressIndex ?? defaultIndex,\n    );\n    psbt.signInput(target.index, signer);\n    signedIndexes.push(target.index);\n  }\n\n  if (finalize) {\n    for (const index of signedIndexes) {\n      const input = psbt.data.inputs[index];\n      // Skip anything already finalized — re-finalizing throws.\n      if (input?.finalScriptWitness || input?.finalScriptSig) continue;\n      try {\n        psbt.finalizeInput(index);\n      } catch (err) {\n        throw new PsbtError(\n          `signPsbtSeed: failed to finalize input ${index}: ${\n            err instanceof Error ? err.message : String(err)\n          }`,\n        );\n      }\n    }\n  }\n\n  return psbt.toBase64();\n}\n\n/**\n * Watch-only output descriptor for a seed-imported single address, matching the\n * `addr(...)` form the gateway's `scantxoutset` consumes.\n */\nexport function seedAddressDescriptor(connection: SeedConnection): string {\n  return `addr(${connection.paymentAddress})`;\n}\n\n// Re-exported for symmetry with `isXverseInstalled()`: a seed import has no\n// environment prerequisite, so it is always available. Lets a consumer render\n// both import options through one uniform check.\nexport function isSeedImportAvailable(): boolean {\n  return true;\n}\n\n// Narrow a chain name to the wallet chains the seed path supports.\nexport function isSeedSupportedChain(name: string): name is WalletChainName {\n  return (SEED_SUPPORTED_CHAINS as readonly string[]).includes(name);\n}\n","/**\n * BIP39/32/84 HD key derivation (p2wpkh only).\n *\n * Security notes:\n *   - Seed bytes are held in a private field on `Keystore`, hidden from\n *     JSON.stringify, structured clones, and util.inspect.\n *   - `lock()` is best-effort memory hygiene: it zeros the seed and attempts\n *     to zero the private key bytes on the account node before dropping the reference.\n *   - Intermediate seed buffers created during construction are zeroed as\n *     soon as they are no longer needed.\n *   - Public derivations expose only pubkeys and scriptPubKeys;\n *     `signerFor()` is package-internal and used only by `psbt.ts`.\n */\n\nimport * as bip39 from \"bip39\";\nimport { BIP32Factory, type BIP32Interface } from \"bip32\";\nimport * as ecc from \"@bitcoinerlab/secp256k1\";\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { InvalidMnemonicError, WalletLockedError } from \"./errors.js\";\nimport type { AddressType, HdDerivedKey, NetworkConfig } from \"./types.js\";\n\nconst bip32 = BIP32Factory(ecc);\nbitcoin.initEccLib(ecc);\n\n// BIP43 purpose field for p2wpkh (BIP84).\nconst PURPOSE: Record<AddressType, number> = {\n  p2wpkh: 84,\n};\n\nexport function generateMnemonic(\n  strength: 128 | 160 | 192 | 224 | 256 = 256,\n): string {\n  return bip39.generateMnemonic(strength);\n}\n\nexport function validateMnemonic(mnemonic: string): boolean {\n  return bip39.validateMnemonic(normalizeMnemonic(mnemonic));\n}\n\nexport function normalizeMnemonic(mnemonic: string): string {\n  return mnemonic.trim().split(/\\s+/).join(\" \").toLowerCase();\n}\n\n// Builds a BIP84 account path `m/84'/coin'/account'`.\nexport function buildAccountPath(\n  addressType: AddressType,\n  coinType: number,\n  account: number,\n): string {\n  return `m/${PURPOSE[addressType]}'/${coinType}'/${account}'`;\n}\n\nexport function buildAddressPath(\n  addressType: AddressType,\n  coinType: number,\n  account: number,\n  change: boolean,\n  index: number,\n): string {\n  const accountPath = buildAccountPath(addressType, coinType, account);\n  return `${accountPath}/${change ? 1 : 0}/${index}`;\n}\n\nexport function deriveAddressFromAccountNode(\n  accountNode: BIP32Interface,\n  addressType: AddressType,\n  networkConfig: NetworkConfig,\n  account: number,\n  change: boolean,\n  index: number,\n): HdDerivedKey {\n  const child = accountNode.derive(change ? 1 : 0).derive(index);\n  if (!child.publicKey) {\n    throw new Error(\"Failed to derive child public key\");\n  }\n  const pubkey = Buffer.from(child.publicKey);\n  const payment = makePayment(addressType, pubkey, networkConfig);\n\n  if (!payment.address || !payment.output) {\n    throw new Error(`Failed to build ${addressType} payment`);\n  }\n\n  return {\n    index,\n    change,\n    path: buildAddressPath(\n      addressType,\n      networkConfig.coinType,\n      account,\n      change,\n      index,\n    ),\n    address: payment.address,\n    scriptPubKey: Buffer.from(payment.output).toString(\"hex\"),\n    publicKey: pubkey.toString(\"hex\"),\n  };\n}\n\nfunction makePayment(\n  addressType: AddressType,\n  pubkey: Buffer,\n  networkConfig: NetworkConfig,\n): bitcoin.Payment {\n  const net = networkConfig.network;\n  switch (addressType) {\n    case \"p2wpkh\":\n      return bitcoin.payments.p2wpkh({ pubkey, network: net });\n    default: {\n      const exhaustive: never = addressType;\n      throw new Error(`Unhandled address type: ${exhaustive as string}`);\n    }\n  }\n}\n\n// Best-effort wipe of BIP32 private key bytes; zeroes exposed buffer but not guaranteed to clear all internal copies\nfunction tryWipeNode(node: BIP32Interface | null): void {\n  if (!node) return;\n  const pk = node.privateKey;\n  if (pk && typeof (pk as Uint8Array).fill === \"function\") {\n    try {\n      (pk as Uint8Array).fill(0);\n    } catch {\n      // Some buffer-backed getters are read-only — ignore.\n    }\n  }\n}\n\nexport class Keystore {\n  readonly #network: NetworkConfig;\n  readonly #addressType: AddressType;\n  readonly #account: number;\n  readonly #fingerprint: string;\n  readonly #accountXpub: string;\n  readonly #accountPath: string;\n\n  // Account node holds the private key material used by `signerFor`.\n  // `lock()` zeros and drops it. No full seed is retained here.\n  #accountNode: BIP32Interface | null;\n  #locked: boolean;\n\n  private constructor(args: {\n    network: NetworkConfig;\n    addressType: AddressType;\n    account: number;\n    fingerprint: string;\n    accountNode: BIP32Interface;\n  }) {\n    this.#network = args.network;\n    this.#addressType = args.addressType;\n    this.#account = args.account;\n    this.#fingerprint = args.fingerprint;\n    this.#accountNode = args.accountNode;\n    this.#locked = false;\n\n    this.#accountPath = buildAccountPath(\n      args.addressType,\n      args.network.coinType,\n      args.account,\n    );\n    this.#accountXpub = args.accountNode.neutered().toBase58();\n  }\n\n  static fromMnemonic(\n    mnemonic: string,\n    passphrase: string,\n    network: NetworkConfig,\n    addressType: AddressType,\n    account: number,\n  ): Keystore {\n    const cleaned = normalizeMnemonic(mnemonic);\n    if (!bip39.validateMnemonic(cleaned)) {\n      throw new InvalidMnemonicError();\n    }\n    // mnemonicToSeedSync returns a Buffer; wipe it once we've finished\n    // deriving the account node.\n    const seedBuf = bip39.mnemonicToSeedSync(cleaned, passphrase);\n    try {\n      return Keystore.fromSeedBuffer(seedBuf, network, addressType, account);\n    } finally {\n      seedBuf.fill(0);\n    }\n  }\n\n  // Build a Keystore directly from raw seed bytes.\n  // The caller's `seed` argument is NOT zeroed — the caller owns that buffer.\n  // An internal copy is made, used for derivation, then zeroed.\n  static fromSeedBytes(\n    seed: Uint8Array,\n    network: NetworkConfig,\n    addressType: AddressType,\n    account: number,\n  ): Keystore {\n    const working = Buffer.from(seed);\n    try {\n      return Keystore.fromSeedBuffer(working, network, addressType, account);\n    } finally {\n      working.fill(0);\n    }\n  }\n\n  // Internal: derive the root and account node from a seed Buffer.\n  // Zeros the root's private material before returning so only the account\n  // node retains private bytes.\n  private static fromSeedBuffer(\n    seed: Buffer,\n    network: NetworkConfig,\n    addressType: AddressType,\n    account: number,\n  ): Keystore {\n    const root = bip32.fromSeed(seed, network.network);\n    const fingerprint = Buffer.from(root.fingerprint).toString(\"hex\");\n    const accountPath = buildAccountPath(\n      addressType,\n      network.coinType,\n      account,\n    );\n    const accountNode = root.derivePath(accountPath);\n    // Root has served its purpose — wipe its private key bytes.\n    tryWipeNode(root);\n    return new Keystore({\n      network,\n      addressType,\n      account,\n      fingerprint,\n      accountNode,\n    });\n  }\n\n  get fingerprint(): string {\n    return this.#fingerprint;\n  }\n\n  get accountPath(): string {\n    return this.#accountPath;\n  }\n\n  get accountXpub(): string {\n    return this.#accountXpub;\n  }\n\n  get addressType(): AddressType {\n    return this.#addressType;\n  }\n\n  get account(): number {\n    return this.#account;\n  }\n\n  get network(): NetworkConfig {\n    return this.#network;\n  }\n\n  get locked(): boolean {\n    return this.#locked;\n  }\n\n  watchOnlyAccountNode(): BIP32Interface {\n    return bip32.fromBase58(this.#accountXpub, this.#network.network);\n  }\n\n  deriveAddress(change: boolean, index: number): HdDerivedKey {\n    const node = this.watchOnlyAccountNode();\n    return deriveAddressFromAccountNode(\n      node,\n      this.#addressType,\n      this.#network,\n      this.#account,\n      change,\n      index,\n    );\n  }\n\n  signerFor(change: boolean, index: number): BIP32Interface {\n    if (this.#locked || !this.#accountNode) {\n      throw new WalletLockedError();\n    }\n    return this.#accountNode.derive(change ? 1 : 0).derive(index);\n  }\n\n  // Best-effort wipe: zeros the account node's private bytes, drops the\n  // reference, and marks the keystore locked. See file header for limits.\n  lock(): void {\n    tryWipeNode(this.#accountNode);\n    this.#accountNode = null;\n    this.#locked = true;\n  }\n\n  [Symbol.for(\"nodejs.util.inspect.custom\")](): string {\n    return `Keystore(${this.#addressType} ${this.#accountPath} fp=${this.#fingerprint} locked=${this.#locked})`;\n  }\n\n  toJSON(): Record<string, unknown> {\n    return {\n      addressType: this.#addressType,\n      accountPath: this.#accountPath,\n      accountXpub: this.#accountXpub,\n      fingerprint: this.#fingerprint,\n      locked: this.#locked,\n    };\n  }\n}\n\n// Builds a BDK-style output descriptor (e.g. `wpkh([fp/84'/1'/0']tpub.../0/*)`) for interop with bdk-cli or Sparrow.\nexport function buildDescriptor(\n  addressType: AddressType,\n  fingerprint: string,\n  accountPath: string,\n  accountXpub: string,\n  change: boolean,\n): string {\n  const origin = accountPath.replace(/^m\\//, \"\");\n  const inner = `[${fingerprint}/${origin}]${accountXpub}/${change ? 1 : 0}/*`;\n  switch (addressType) {\n    case \"p2wpkh\":\n      return `wpkh(${inner})`;\n    default: {\n      const exhaustive: never = addressType;\n      throw new Error(`Unhandled address type: ${exhaustive as string}`);\n    }\n  }\n}\n","/**\n * Legacy P2PKH (m/44') migration safeguards.\n *\n * 0.3.0 switched the built-in HD signer to native segwit (p2wpkh, BIP84) and\n * dropped P2PKH support. The same mnemonic now derives only m/84'/… addresses,\n * so any balance previously received on the legacy m/44'/… P2PKH chain becomes\n * invisible to scanning, balance, and send flows. Upgrading without first\n * sweeping those UTXOs makes the funds *look* gone.\n *\n * This module is an opt-in, temporary migration aid:\n *   - `scanLegacyP2pkh()`    — read-only detection of non-empty m/44' chains.\n *   - `assertNoStrandedLegacyFunds()` — hard abort when legacy funds exist.\n *   - `sweepLegacy()`        — moves all legacy UTXOs to a new p2wpkh address.\n *\n * It deliberately re-derives P2PKH *locally* (BIP44 purpose 44) without\n * re-adding `p2pkh` to the supported `AddressType` union, so the main wallet,\n * PSBT builder, and signer stay p2wpkh-only. Treat it as a one-time bridge to\n * run before cutting over, not as a general-purpose legacy wallet.\n *\n * Seed hygiene mirrors `derivation.ts`: intermediate seed buffers and derived\n * private-key bytes are zeroed on a best-effort basis once they are no longer\n * needed.\n */\n\nimport * as bip39 from \"bip39\";\nimport { BIP32Factory, type BIP32Interface } from \"bip32\";\nimport * as ecc from \"@bitcoinerlab/secp256k1\";\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { normalizeMnemonic } from \"./derivation.js\";\nimport {\n  InsufficientFundsError,\n  InvalidAddressError,\n  InvalidMnemonicError,\n  FeeRateError,\n  PsbtError,\n  WalletError,\n} from \"./errors.js\";\nimport { scanForUtxos } from \"./rpc/index.js\";\nimport type { RpcClient } from \"./rpc/types.js\";\nimport type { NetworkConfig } from \"./types.js\";\nimport { DUST_THRESHOLD_SATS } from \"./units.js\";\n\nconst bip32 = BIP32Factory(ecc);\nbitcoin.initEccLib(ecc);\n\n// BIP43 purpose field for legacy P2PKH.\nconst LEGACY_PURPOSE = 44;\n\nconst DEFAULT_LEGACY_GAP_LIMIT = 20;\n// Caps adaptive rescans so a malicious RPC that always reports \"one more used\n// address\" cannot drive an unbounded derivation loop (same defense as\n// Wallet.sync()).\nconst DEFAULT_MAX_SCAN_ITERATIONS = 32;\n\n// Conservative vsize estimates (vBytes) for the single-output legacy sweep tx.\nconst LEGACY_P2PKH_INPUT_VBYTES = 148;\nconst P2WPKH_OUTPUT_VBYTES = 31;\nconst TX_OVERHEAD_VBYTES = 11;\n\nexport interface LegacyScanOptions {\n  // BIP44 account index to scan (default 0).\n  account?: number;\n  // Optional BIP39 passphrase; must match what the legacy wallet used.\n  passphrase?: string;\n  // Unused-address gap limit per chain (default 20).\n  gapLimit?: number;\n  // Bound on adaptive rescans (default 32).\n  maxScanIterations?: number;\n}\n\nexport interface LegacyUserKeyOptions {\n  // BIP44 account index (default 0).\n  account?: number;\n  // Optional BIP39 passphrase; must match what the legacy wallet used.\n  passphrase?: string;\n  // How many indices to derive on the chain (default 20).\n  count?: number;\n  // Derive the change chain (`…/1/i`) instead of the receive chain (`…/0/i`).\n  // Legacy vault creation used the receive chain, so this defaults to false;\n  // it exists so a recovery search can cover both.\n  change?: boolean;\n}\n\nexport interface LegacyUserKey {\n  readonly index: number;\n  readonly change: boolean;\n  // Full BIP44 path, e.g. `m/44'/1'/0'/0/4`.\n  readonly path: string;\n  // Hex-encoded 33-byte compressed secp256k1 pubkey.\n  readonly compressedHex: string;\n  // The P2PKH address this pubkey encodes to. Informational — the p2wpkh-only\n  // signer cannot spend from it; use `sweepLegacy()` for that.\n  readonly address: string;\n}\n\nexport interface LegacyUtxo {\n  readonly txid: string;\n  readonly vout: number;\n  readonly valueSats: bigint;\n  readonly address: string;\n  readonly scriptPubKey: string;\n  readonly path: string;\n  readonly change: boolean;\n  readonly addressIndex: number;\n}\n\nexport interface LegacyScanResult {\n  // Sum of all discovered legacy UTXO values.\n  readonly totalSats: bigint;\n  readonly utxos: LegacyUtxo[];\n  // Number of addresses derived/scanned across both chains.\n  readonly scannedAddresses: number;\n  // The m/44' account path that was scanned.\n  readonly accountPath: string;\n}\n\ninterface DerivedLegacyAddress {\n  address: string;\n  scriptPubKey: string;\n  path: string;\n  change: boolean;\n  index: number;\n}\n\n// Best-effort wipe of BIP32 private-key bytes (see derivation.ts for limits).\nfunction tryWipeNode(node: BIP32Interface | null): void {\n  if (!node) return;\n  const pk = node.privateKey;\n  if (pk && typeof (pk as Uint8Array).fill === \"function\") {\n    try {\n      (pk as Uint8Array).fill(0);\n    } catch {\n      // Read-only buffer-backed getters — ignore.\n    }\n  }\n}\n\nfunction legacyAccountPath(network: NetworkConfig, account: number): string {\n  return `m/${LEGACY_PURPOSE}'/${network.coinType}'/${account}'`;\n}\n\n// Derive the legacy m/44'/coin'/account' node (retains private material so the\n// same node can both scan and sign a sweep). The caller MUST `tryWipeNode()` it\n// when finished.\nfunction deriveLegacyAccountNode(\n  mnemonic: string,\n  network: NetworkConfig,\n  account: number,\n  passphrase: string,\n): BIP32Interface {\n  const cleaned = normalizeMnemonic(mnemonic);\n  if (!bip39.validateMnemonic(cleaned)) {\n    throw new InvalidMnemonicError();\n  }\n  const seed = bip39.mnemonicToSeedSync(cleaned, passphrase);\n  try {\n    const root = bip32.fromSeed(seed, network.network);\n    const node = root.derivePath(legacyAccountPath(network, account));\n    // Root has served its purpose — wipe its private bytes.\n    tryWipeNode(root);\n    return node;\n  } finally {\n    seed.fill(0);\n  }\n}\n\nfunction deriveLegacyAddresses(\n  accountNode: BIP32Interface,\n  network: NetworkConfig,\n  account: number,\n  change: boolean,\n  count: number,\n): DerivedLegacyAddress[] {\n  const branch = accountNode.derive(change ? 1 : 0);\n  const out: DerivedLegacyAddress[] = [];\n  for (let index = 0; index < count; index++) {\n    const child = branch.derive(index);\n    if (!child.publicKey) {\n      throw new Error(\"Failed to derive legacy child public key\");\n    }\n    const pubkey = Buffer.from(child.publicKey);\n    const payment = bitcoin.payments.p2pkh({\n      pubkey,\n      network: network.network,\n    });\n    if (!payment.address || !payment.output) {\n      throw new Error(\"Failed to build legacy p2pkh payment\");\n    }\n    out.push({\n      address: payment.address,\n      scriptPubKey: Buffer.from(payment.output).toString(\"hex\"),\n      path: `${legacyAccountPath(network, account)}/${change ? 1 : 0}/${index}`,\n      change,\n      index,\n    });\n  }\n  return out;\n}\n\n/**\n * Derive the PUBLIC keys of one chain of the legacy m/44' account.\n *\n * The 0.3.0 cutover stranded more than balances. Anything that reused the\n * account's secp256k1 pubkey as an identity — most notably a vault's user key\n * or a Taproot internal key — silently changed with it, because\n * m/44'/coin'/0'/0/i and m/84'/coin'/0'/0/i are different keys from the same\n * mnemonic. A consumer that committed the pre-cutover pubkey into a vault or an\n * on-chain script cannot reproduce it from a post-cutover wallet at ANY index,\n * and has no way to derive the old key either: `AddressType` is p2wpkh-only, so\n * `Keystore.fromMnemonic` can no longer reach purpose 44. This is the escape\n * hatch for exactly that case.\n *\n * Prefer `resolveUserKey()` (src/user-key.ts) when you hold the recorded pubkey\n * and want to know which scheme and path produced it; this is the lower-level\n * enumeration it is built on.\n *\n * Public keys only — no signing capability is returned, and the private material\n * derived along the way is wiped before returning. Sweeping legacy funds is\n * still `sweepLegacy()`; this is for reconstructing a legacy IDENTITY.\n */\nexport function deriveLegacyUserKeys(\n  mnemonic: string,\n  network: NetworkConfig,\n  opts: LegacyUserKeyOptions = {},\n): LegacyUserKey[] {\n  const account = opts.account ?? 0;\n  if (!Number.isInteger(account) || account < 0) {\n    throw new RangeError(\n      `deriveLegacyUserKeys: account must be a non-negative integer, got ${account}`,\n    );\n  }\n  const count = opts.count ?? DEFAULT_LEGACY_GAP_LIMIT;\n  if (!Number.isInteger(count) || count < 1) {\n    throw new RangeError(\n      `deriveLegacyUserKeys: count must be a positive integer, got ${count}`,\n    );\n  }\n  const change = opts.change ?? false;\n\n  const accountNode = deriveLegacyAccountNode(\n    mnemonic,\n    network,\n    account,\n    opts.passphrase ?? \"\",\n  );\n  try {\n    const branch = accountNode.derive(change ? 1 : 0);\n    const accountPath = legacyAccountPath(network, account);\n    const out: LegacyUserKey[] = [];\n    for (let index = 0; index < count; index++) {\n      const child = branch.derive(index);\n      if (!child.publicKey) {\n        throw new Error(\n          `Failed to derive legacy child public key at index ${index}`,\n        );\n      }\n      const pubkey = Buffer.from(child.publicKey);\n      const payment = bitcoin.payments.p2pkh({\n        pubkey,\n        network: network.network,\n      });\n      if (!payment.address) {\n        throw new Error(\n          `Failed to build legacy p2pkh address at index ${index}`,\n        );\n      }\n      out.push({\n        index,\n        change,\n        path: `${accountPath}/${change ? 1 : 0}/${index}`,\n        compressedHex: pubkey.toString(\"hex\"),\n        address: payment.address,\n      });\n      tryWipeNode(child);\n    }\n    tryWipeNode(branch);\n    return out;\n  } finally {\n    tryWipeNode(accountNode);\n  }\n}\n\n// Adaptive gap-limit scan over the legacy receive + change chains using the\n// supplied (private-bearing) account node. Shared by scanLegacyP2pkh() and\n// sweepLegacy() so the node is derived once.\nasync function scanWithNode(\n  accountNode: BIP32Interface,\n  network: NetworkConfig,\n  account: number,\n  rpc: RpcClient,\n  gapLimit: number,\n  maxScanIterations: number,\n): Promise<LegacyScanResult> {\n  const accountPath = legacyAccountPath(network, account);\n  let receive = deriveLegacyAddresses(\n    accountNode,\n    network,\n    account,\n    false,\n    gapLimit,\n  );\n  let change = deriveLegacyAddresses(\n    accountNode,\n    network,\n    account,\n    true,\n    gapLimit,\n  );\n\n  let iterations = 0;\n  while (true) {\n    const scriptMap = new Map<string, DerivedLegacyAddress>();\n    for (const d of receive) scriptMap.set(d.scriptPubKey, d);\n    for (const d of change) scriptMap.set(d.scriptPubKey, d);\n\n    const descriptors = [...receive, ...change].map(\n      (d) => `addr(${d.address})`,\n    );\n    const { utxos: scanned } = await scanForUtxos(rpc, descriptors);\n\n    let highestUsedReceive = -1;\n    let highestUsedChange = -1;\n    const matched: LegacyUtxo[] = [];\n    let totalSats = 0n;\n\n    for (const u of scanned) {\n      const derived = scriptMap.get(u.scriptPubKey);\n      if (!derived) continue;\n      if (derived.change) {\n        if (derived.index > highestUsedChange)\n          highestUsedChange = derived.index;\n      } else {\n        if (derived.index > highestUsedReceive)\n          highestUsedReceive = derived.index;\n      }\n      matched.push({\n        txid: u.txid,\n        vout: u.vout,\n        valueSats: u.valueSats,\n        address: derived.address,\n        scriptPubKey: u.scriptPubKey,\n        path: derived.path,\n        change: derived.change,\n        addressIndex: derived.index,\n      });\n      totalSats += u.valueSats;\n    }\n\n    const wantReceive = highestUsedReceive + 1 + gapLimit;\n    const wantChange = highestUsedChange + 1 + gapLimit;\n    const needExtend =\n      wantReceive > receive.length || wantChange > change.length;\n\n    if (needExtend && iterations < maxScanIterations) {\n      iterations++;\n      if (wantReceive > receive.length) {\n        receive = deriveLegacyAddresses(\n          accountNode,\n          network,\n          account,\n          false,\n          wantReceive,\n        );\n      }\n      if (wantChange > change.length) {\n        change = deriveLegacyAddresses(\n          accountNode,\n          network,\n          account,\n          true,\n          wantChange,\n        );\n      }\n      continue;\n    }\n\n    return {\n      totalSats,\n      utxos: matched,\n      scannedAddresses: receive.length + change.length,\n      accountPath,\n    };\n  }\n}\n\nfunction sanitizeGapLimit(value: number | undefined): number {\n  const n = Number(value);\n  if (!Number.isFinite(n) || n < 1) return DEFAULT_LEGACY_GAP_LIMIT;\n  return Math.floor(n);\n}\n\nfunction sanitizeMaxScanIterations(value: number | undefined): number {\n  const n = Number(value);\n  const int = Number.isFinite(n) ? Math.floor(n) : DEFAULT_MAX_SCAN_ITERATIONS;\n  return Math.max(0, Math.min(DEFAULT_MAX_SCAN_ITERATIONS, int));\n}\n\n/**\n * Detect funds stranded on the legacy m/44' (P2PKH) chain for `mnemonic`.\n * Read-only: derives legacy addresses for a gap-limit window, scans the UTXO\n * set, and reports the total. The derived private material is wiped before\n * returning.\n */\nexport async function scanLegacyP2pkh(\n  mnemonic: string,\n  network: NetworkConfig,\n  rpc: RpcClient,\n  opts: LegacyScanOptions = {},\n): Promise<LegacyScanResult> {\n  const account = opts.account ?? 0;\n  const gapLimit = sanitizeGapLimit(opts.gapLimit);\n  const maxScanIterations = sanitizeMaxScanIterations(opts.maxScanIterations);\n\n  const accountNode = deriveLegacyAccountNode(\n    mnemonic,\n    network,\n    account,\n    opts.passphrase ?? \"\",\n  );\n  try {\n    return await scanWithNode(\n      accountNode,\n      network,\n      account,\n      rpc,\n      gapLimit,\n      maxScanIterations,\n    );\n  } finally {\n    tryWipeNode(accountNode);\n  }\n}\n\n/**\n * Guard intended for first-run / upgrade paths: throws if any legacy P2PKH\n * funds are discovered so they cannot silently strand under the p2wpkh-only\n * signer. Resolves quietly when the legacy chain is empty.\n */\nexport async function assertNoStrandedLegacyFunds(\n  mnemonic: string,\n  network: NetworkConfig,\n  rpc: RpcClient,\n  opts: LegacyScanOptions = {},\n): Promise<void> {\n  const legacy = await scanLegacyP2pkh(mnemonic, network, rpc, opts);\n  if (legacy.totalSats > 0n) {\n    throw new WalletError(\n      \"LEGACY_FUNDS_DETECTED\",\n      `Legacy P2PKH funds detected (${legacy.totalSats} sats on ${legacy.accountPath}). ` +\n        `Sweep with sweepLegacy() before using the P2WPKH-only signer.`,\n    );\n  }\n}\n\nexport interface SweepLegacyArgs extends LegacyScanOptions {\n  mnemonic: string;\n  network: NetworkConfig;\n  rpc: RpcClient;\n  // Destination address (typically the new p2wpkh receive address). Must be\n  // valid on `network`.\n  destination: string;\n  // Fee rate in sat/vB.\n  feeRate: number;\n  // Broadcast the signed transaction (default true). When false, the caller\n  // gets the raw hex back to broadcast/inspect themselves.\n  broadcast?: boolean;\n}\n\nexport interface SweepLegacyResult {\n  // null when `broadcast` is false (tx built/signed but not sent).\n  readonly txid: string | null;\n  readonly hex: string;\n  readonly sweptSats: bigint;\n  readonly feeSats: bigint;\n  readonly inputCount: number;\n  readonly destination: string;\n}\n\n// Parse the parent tx and confirm the referenced prevout matches the\n// scanner-reported value and scriptPubKey before it is signed over (mirrors the\n// p2wpkh builder's prevout verification so a tampered RPC cannot misstate an\n// input).\nfunction verifyLegacyParent(parentBuf: Buffer, utxo: LegacyUtxo): void {\n  let parentTx: bitcoin.Transaction;\n  try {\n    parentTx = bitcoin.Transaction.fromBuffer(parentBuf);\n  } catch (err) {\n    throw new PsbtError(\n      `Failed to parse parent tx ${utxo.txid}: ${\n        err instanceof Error ? err.message : String(err)\n      }`,\n    );\n  }\n  if (parentTx.getId() !== utxo.txid) {\n    throw new PsbtError(\n      `Parent tx id mismatch: expected ${utxo.txid}, got ${parentTx.getId()}`,\n    );\n  }\n  const prevout = parentTx.outs[utxo.vout];\n  if (!prevout) {\n    throw new PsbtError(`Parent tx ${utxo.txid} has no vout ${utxo.vout}`);\n  }\n  if (BigInt(prevout.value) !== utxo.valueSats) {\n    throw new PsbtError(\n      `UTXO amount mismatch for ${utxo.txid}:${utxo.vout} — ` +\n        `scan reported ${utxo.valueSats} sats, parent tx has ${BigInt(\n          prevout.value,\n        )}`,\n    );\n  }\n  if (Buffer.from(prevout.script).toString(\"hex\") !== utxo.scriptPubKey) {\n    throw new PsbtError(\n      `UTXO scriptPubKey mismatch for ${utxo.txid}:${utxo.vout}`,\n    );\n  }\n}\n\n/**\n * Sweep every discovered legacy m/44' (P2PKH) UTXO into a single output at\n * `destination` (your new p2wpkh address). Builds, signs, and — unless\n * `broadcast: false` — broadcasts the transaction.\n *\n * Legacy inputs are non-witness, so each parent transaction is fetched in full,\n * verified against the scan result, and attached as `nonWitnessUtxo`. The\n * derived private material is wiped before returning.\n */\nexport async function sweepLegacy(\n  args: SweepLegacyArgs,\n): Promise<SweepLegacyResult> {\n  const { mnemonic, network, rpc, destination } = args;\n  const account = args.account ?? 0;\n  const gapLimit = sanitizeGapLimit(args.gapLimit);\n  const maxScanIterations = sanitizeMaxScanIterations(args.maxScanIterations);\n  const broadcast = args.broadcast ?? true;\n\n  if (!Number.isFinite(args.feeRate) || args.feeRate <= 0) {\n    throw new FeeRateError(\n      `Fee rate must be a positive number: ${args.feeRate}`,\n    );\n  }\n\n  // Reject destinations that aren't encodable on this network before we touch\n  // any key material.\n  let destinationScript: Buffer;\n  try {\n    destinationScript = Buffer.from(\n      bitcoin.address.toOutputScript(destination, network.network),\n    );\n  } catch {\n    throw new InvalidAddressError(\n      destination,\n      `not valid on network ${network.name}`,\n    );\n  }\n\n  const accountNode = deriveLegacyAccountNode(\n    mnemonic,\n    network,\n    account,\n    args.passphrase ?? \"\",\n  );\n\n  try {\n    const scan = await scanWithNode(\n      accountNode,\n      network,\n      account,\n      rpc,\n      gapLimit,\n      maxScanIterations,\n    );\n\n    if (scan.utxos.length === 0 || scan.totalSats === 0n) {\n      throw new WalletError(\n        \"NO_LEGACY_FUNDS\",\n        `No legacy P2PKH funds found on ${scan.accountPath} to sweep.`,\n      );\n    }\n\n    const vbytes =\n      TX_OVERHEAD_VBYTES +\n      scan.utxos.length * LEGACY_P2PKH_INPUT_VBYTES +\n      P2WPKH_OUTPUT_VBYTES;\n    const feeSats = BigInt(Math.ceil(vbytes * args.feeRate));\n    const sweepSats = scan.totalSats - feeSats;\n\n    if (sweepSats < DUST_THRESHOLD_SATS) {\n      // Not enough to cover the fee and leave a non-dust output.\n      throw new InsufficientFundsError(\n        scan.totalSats,\n        feeSats + DUST_THRESHOLD_SATS,\n      );\n    }\n\n    const psbt = new bitcoin.Psbt({ network: network.network });\n\n    for (const utxo of scan.utxos) {\n      const parent = await rpc.call<{ hex?: string }>(\"getrawtransaction\", [\n        utxo.txid,\n        true,\n      ]);\n      if (!parent.hex || typeof parent.hex !== \"string\") {\n        throw new PsbtError(\n          `getrawtransaction did not return hex for parent ${utxo.txid}`,\n        );\n      }\n      const parentBuf = Buffer.from(parent.hex, \"hex\");\n      verifyLegacyParent(parentBuf, utxo);\n      psbt.addInput({\n        hash: utxo.txid,\n        index: utxo.vout,\n        nonWitnessUtxo: parentBuf,\n      });\n    }\n\n    psbt.addOutput({ script: destinationScript, value: sweepSats });\n\n    scan.utxos.forEach((utxo, i) => {\n      const signer = accountNode\n        .derive(utxo.change ? 1 : 0)\n        .derive(utxo.addressIndex);\n      psbt.signInput(i, signer);\n    });\n\n    try {\n      psbt.finalizeAllInputs();\n    } catch (err) {\n      throw new PsbtError(\n        `Failed to finalize legacy sweep PSBT: ${\n          err instanceof Error ? err.message : String(err)\n        }`,\n      );\n    }\n\n    const tx = psbt.extractTransaction();\n    const hex = tx.toHex();\n    let txid: string | null = null;\n    if (broadcast) {\n      txid = await rpc.call<string>(\"sendrawtransaction\", [hex]);\n    }\n\n    return {\n      txid,\n      hex,\n      sweptSats: sweepSats,\n      feeSats,\n      inputCount: scan.utxos.length,\n      destination,\n    };\n  } finally {\n    tryWipeNode(accountNode);\n  }\n}\n","import { RpcError, RpcTransportError, WalletError } from \"../errors.js\";\n\nimport type { RpcClient, RpcConfig } from \"./types.js\";\n\ninterface JsonRpcRequest {\n  jsonrpc: \"1.0\";\n  id: string;\n  method: string;\n  params: readonly unknown[];\n}\n\ninterface JsonRpcResponse<T> {\n  result: T | null;\n  error: { code: number; message: string } | null;\n  id: string;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class BitcoinCoreRpcClient implements RpcClient {\n  readonly #url: string;\n  readonly #authHeader: string | null;\n  readonly #fetchImpl: typeof fetch;\n  readonly #timeoutMs: number;\n  readonly #requestInit: Omit<RequestInit, \"method\" | \"body\" | \"signal\">;\n\n  #idCounter = 0;\n\n  constructor(config: RpcConfig) {\n    if (!config.url || typeof config.url !== \"string\") {\n      throw new WalletError(\n        \"INVALID_RPC_CONFIG\",\n        \"rpc.url is required and must be a string\",\n      );\n    }\n\n    // Extract inlined user:pass from the URL into the auth header so credentials never appear in the request line.\n    const { url, embeddedAuth } = stripEmbeddedAuth(config.url);\n    this.#url = url;\n\n    if (config.authHeader) {\n      this.#authHeader = config.authHeader;\n    } else if (config.username !== undefined && config.password !== undefined) {\n      this.#authHeader = buildBasicAuth(config.username, config.password);\n    } else if (embeddedAuth) {\n      this.#authHeader = embeddedAuth;\n    } else {\n      this.#authHeader = null;\n    }\n\n    this.#fetchImpl =\n      config.fetchImpl ??\n      (globalThis.fetch as typeof fetch | undefined) ??\n      unsupportedFetch;\n    this.#timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n    this.#requestInit = config.requestInit ?? {};\n  }\n\n  async call<T>(method: string, params: readonly unknown[] = []): Promise<T> {\n    const id = `taurus-${Date.now()}-${++this.#idCounter}`;\n    const body: JsonRpcRequest = {\n      jsonrpc: \"1.0\",\n      id,\n      method,\n      params,\n    };\n\n    const headers = new Headers(this.#requestInit.headers);\n    headers.set(\"Content-Type\", \"application/json\");\n    if (this.#authHeader) {\n      headers.set(\"Authorization\", this.#authHeader);\n    }\n\n    const controller = new AbortController();\n    const timeout = setTimeout(() => controller.abort(), this.#timeoutMs);\n\n    let response: Response;\n    try {\n      response = await this.#fetchImpl(this.#url, {\n        ...this.#requestInit,\n        method: \"POST\",\n        body: JSON.stringify(body),\n        headers,\n        signal: controller.signal,\n      });\n    } catch (err) {\n      const message =\n        err instanceof Error\n          ? err.name === \"AbortError\"\n            ? `RPC ${method} timed out after ${this.#timeoutMs}ms`\n            : err.message\n          : \"unknown fetch error\";\n      throw new RpcTransportError(message);\n    } finally {\n      clearTimeout(timeout);\n    }\n\n    if (!response.ok) {\n      // Read the body for error context\n      const text = (await safeText(response)).slice(0, 512);\n      throw new RpcTransportError(\n        `RPC ${method} HTTP ${response.status}: ${text}`,\n        response.status,\n      );\n    }\n\n    let json: JsonRpcResponse<T>;\n    try {\n      json = (await response.json()) as JsonRpcResponse<T>;\n    } catch (err) {\n      throw new RpcTransportError(\n        `RPC ${method} returned non-JSON response: ${\n          err instanceof Error ? err.message : String(err)\n        }`,\n      );\n    }\n\n    if (json.error) {\n      throw new RpcError(method, json.error.message, json.error.code);\n    }\n    // `result` may legitimately be `null` for some void RPCs\n    return json.result as T;\n  }\n\n  /** Hide the auth header from console.log / util.inspect. */\n  [Symbol.for(\"nodejs.util.inspect.custom\")](): string {\n    return `BitcoinCoreRpcClient(${this.#url} auth=${\n      this.#authHeader ? \"set\" : \"none\"\n    })`;\n  }\n\n  toJSON(): Record<string, unknown> {\n    return { url: this.#url, authConfigured: this.#authHeader !== null };\n  }\n}\n\nfunction buildBasicAuth(username: string, password: string): string {\n  // Use Buffer in Node, btoa in the browser. Both produce the same base64\n  const raw = `${username}:${password}`;\n  const encoded =\n    typeof Buffer !== \"undefined\"\n      ? Buffer.from(raw, \"utf8\").toString(\"base64\")\n      : btoa(raw);\n  return `Basic ${encoded}`;\n}\n\nfunction stripEmbeddedAuth(url: string): {\n  url: string;\n  embeddedAuth: string | null;\n} {\n  try {\n    const parsed = new URL(url);\n    if (parsed.username || parsed.password) {\n      const auth = buildBasicAuth(\n        decodeURIComponent(parsed.username),\n        decodeURIComponent(parsed.password),\n      );\n      parsed.username = \"\";\n      parsed.password = \"\";\n      return { url: parsed.toString(), embeddedAuth: auth };\n    }\n    return { url, embeddedAuth: null };\n  } catch {\n    // On URL parse failure, return the input and let fetch surface a clearer error.\n    return { url, embeddedAuth: null };\n  }\n}\n\nasync function safeText(response: Response): Promise<string> {\n  try {\n    return await response.text();\n  } catch {\n    return \"<unreadable response body>\";\n  }\n}\n\nfunction unsupportedFetch(): never {\n  throw new RpcTransportError(\n    \"No global fetch available — pass `rpc.fetchImpl` (e.g. undici.fetch on Node <18)\",\n  );\n}\n","/**\n * Vault user keys: derivation metadata that makes a recorded pubkey\n * self-describing, plus recovery for records created before that metadata\n * existed.\n *\n * ## The problem this closes\n *\n * `m/84'` (p2wpkh, BIP84) is the only supported scheme — the daemon's contract\n * is correct as written and does not need to accept `m/44'`. But an earlier\n * client flow created vaults from `m/44'` (p2pkh, BIP44) derived keys, before\n * the SDK enforced the scheme. Those vault records are still on file, and the\n * current SDK cannot reconstruct them: `AddressType` is p2wpkh-only, so\n * `Keystore.fromMnemonic()` can no longer reach purpose 44, and nothing in the\n * record says which purpose produced its user key. That is a legacy-data\n * problem, not a daemon-acceptance one.\n *\n * ## The two halves of the fix\n *\n *  1. **Recovery** — `resolveUserKey()` takes the pubkey a vault already\n *     recorded and searches the current `m/84'` chain first, then the legacy\n *     `m/44'` chain, and reports which scheme, path, and index produced it. The\n *     legacy key is still derivable locally from the mnemonic, so a stranded\n *     vault can be identified and reconstructed even though the signer is\n *     p2wpkh-only.\n *\n *  2. **Prevention** — `deriveUserKey()` returns a `UserKeyDescriptor`:\n *     scheme, purpose, coin type, account, chain, index, full path, master\n *     fingerprint, and the pubkey itself. Vault creation should persist this\n *     whole record alongside the vault, so no future record is ambiguous about\n *     the derivation that produced it. `assertSupportedUserKeyScheme()` is the\n *     fail-closed guard for creation paths: it rejects a legacy-scheme\n *     descriptor outright.\n *\n * No signing capability is returned anywhere in this module — descriptors carry\n * public material only, and private bytes derived along the way are wiped\n * before returning. Stranded *funds* are a separate concern: see\n * `scanLegacyP2pkh()` / `sweepLegacy()` in migration.ts.\n */\n\nimport { Keystore, deriveAddressFromAccountNode } from \"./derivation.js\";\nimport { WalletError } from \"./errors.js\";\nimport { deriveLegacyUserKeys } from \"./migration.js\";\nimport type { AddressType, NetworkConfig } from \"./types.js\";\n\n/**\n * Schema version of a persisted `UserKeyDescriptor`. Bump when the record shape\n * changes so consumers can migrate stored vault metadata deterministically.\n */\nexport const USER_KEY_RECORD_VERSION = 1;\n\n/**\n * Derivation schemes a vault user key can have been created under.\n *   - `bip84-p2wpkh` — the only supported scheme; what new vaults must use.\n *   - `bip44-p2pkh`  — legacy, recovery-only. Vaults created before the SDK\n *                      enforced BIP84. Never valid for new vaults.\n */\nexport type UserKeyScheme = \"bip84-p2wpkh\" | \"bip44-p2pkh\";\n\nexport const CURRENT_USER_KEY_SCHEME = \"bip84-p2wpkh\" as const;\nexport const LEGACY_USER_KEY_SCHEME = \"bip44-p2pkh\" as const;\n\n// BIP43 purpose per scheme.\nconst SCHEME_PURPOSE: Record<UserKeyScheme, number> = {\n  \"bip84-p2wpkh\": 84,\n  \"bip44-p2pkh\": 44,\n};\n\n// Address type each scheme encodes to. Only the current scheme's value is a\n// member of `AddressType` — that union is deliberately p2wpkh-only, and the\n// legacy scheme is reported as a plain string rather than widening it.\nconst SCHEME_ADDRESS_TYPE: Record<UserKeyScheme, AddressType | \"p2pkh\"> = {\n  \"bip84-p2wpkh\": \"p2wpkh\",\n  \"bip44-p2pkh\": \"p2pkh\",\n};\n\n/**\n * The full derivation record for a vault's user key. Persist ALL of it at vault\n * creation — the pubkey alone is what made the legacy records unrecoverable.\n */\nexport interface UserKeyDescriptor {\n  // Schema version of this record (see USER_KEY_RECORD_VERSION).\n  readonly version: number;\n  readonly scheme: UserKeyScheme;\n  // BIP43 purpose: 84 for the supported scheme, 44 for legacy records.\n  readonly purpose: number;\n  // BIP44 coin type of the network the key was derived on (0 mainnet, 1 test).\n  readonly coinType: number;\n  // Network name, e.g. \"signet\" — coinType alone does not disambiguate\n  // signet/testnet/regtest.\n  readonly network: string;\n  readonly account: number;\n  // false = receive chain (`…/0/i`), true = change chain (`…/1/i`).\n  readonly change: boolean;\n  readonly index: number;\n  // Full derivation path, e.g. `m/84'/1'/0'/0/0`.\n  readonly path: string;\n  // Hex-encoded 33-byte compressed secp256k1 pubkey — the user key itself.\n  readonly publicKey: string;\n  // Master (root) key fingerprint, hex. Identifies WHICH seed this came from\n  // when a consumer holds more than one.\n  readonly masterFingerprint: string;\n  // Address this pubkey encodes to under `scheme`. Informational: for a legacy\n  // descriptor this is a p2pkh address the current signer cannot spend from.\n  readonly address: string;\n  // Address type for `scheme`. \"p2pkh\" only ever appears on legacy records.\n  readonly addressType: AddressType | \"p2pkh\";\n}\n\nexport interface DeriveUserKeyOptions {\n  // BIP44 account index (default 0).\n  account?: number;\n  // Optional BIP39 passphrase.\n  passphrase?: string;\n  // Chain to derive on (default false — the receive chain).\n  change?: boolean;\n  // Address index within the chain (default 0).\n  index?: number;\n}\n\nexport interface ResolveUserKeyOptions {\n  // BIP44 account index to search (default 0).\n  account?: number;\n  // Optional BIP39 passphrase; must match what the vault was created with.\n  passphrase?: string;\n  // Indices to search per chain (default 20).\n  count?: number;\n  // Also search the change chain (default true). Vault creation used the\n  // receive chain, but a mis-specified legacy flow may not have.\n  includeChange?: boolean;\n  // Also search the legacy m/44' chain when the current scheme misses\n  // (default true). Set false to check only that a record is well-formed under\n  // the supported scheme.\n  includeLegacy?: boolean;\n}\n\nconst DEFAULT_SEARCH_COUNT = 20;\n\n// Compressed secp256k1 pubkey: 0x02/0x03 prefix + 32-byte X coordinate.\nconst COMPRESSED_RE = /^0[23][0-9a-f]{64}$/;\n// BIP340 x-only pubkey (a Taproot internal key committed as an identity).\nconst X_ONLY_RE = /^[0-9a-f]{64}$/;\n\ninterface NormalizedKey {\n  hex: string;\n  xOnly: boolean;\n}\n\n/**\n * Accepts a compressed (66 hex chars) or x-only/BIP340 (64 hex chars) pubkey,\n * with or without a `0x` prefix, in any case. X-only input matches on the X\n * coordinate alone, so a Taproot internal key recorded without its parity byte\n * still resolves.\n */\nfunction normalizeUserKey(publicKey: string): NormalizedKey {\n  if (typeof publicKey !== \"string\") {\n    throw new WalletError(\n      \"INVALID_USER_KEY\",\n      `User key must be a hex string, got ${typeof publicKey}`,\n    );\n  }\n  const hex = publicKey.trim().replace(/^0x/i, \"\").toLowerCase();\n  if (COMPRESSED_RE.test(hex)) return { hex, xOnly: false };\n  if (X_ONLY_RE.test(hex)) return { hex, xOnly: true };\n  throw new WalletError(\n    \"INVALID_USER_KEY\",\n    `User key must be a 33-byte compressed (66 hex chars) or x-only ` +\n      `(64 hex chars) secp256k1 pubkey, got ${publicKey.length} chars`,\n  );\n}\n\n// Compare a derived compressed pubkey against normalized search input.\nfunction keyEquals(\n  candidateCompressed: string,\n  target: NormalizedKey,\n): boolean {\n  const candidate = candidateCompressed.toLowerCase();\n  return target.xOnly\n    ? candidate.slice(2) === target.hex\n    : candidate === target.hex;\n}\n\nfunction assertSearchOpts(account: number, count: number): void {\n  if (!Number.isInteger(account) || account < 0) {\n    throw new RangeError(\n      `account must be a non-negative integer, got ${account}`,\n    );\n  }\n  if (!Number.isInteger(count) || count < 1) {\n    throw new RangeError(`count must be a positive integer, got ${count}`);\n  }\n}\n\n/**\n * Derive a vault user key under the supported scheme (`m/84'`, p2wpkh) and\n * return the full derivation record to persist with the vault.\n *\n * This is what vault creation should call. Store the returned descriptor —\n * every field of it — so the vault is never ambiguous about which key and which\n * derivation produced it. Public material only; the keystore is locked before\n * returning.\n */\nexport function deriveUserKey(\n  mnemonic: string,\n  network: NetworkConfig,\n  opts: DeriveUserKeyOptions = {},\n): UserKeyDescriptor {\n  const account = opts.account ?? 0;\n  const index = opts.index ?? 0;\n  const change = opts.change ?? false;\n  if (!Number.isInteger(account) || account < 0) {\n    throw new RangeError(\n      `deriveUserKey: account must be a non-negative integer, got ${account}`,\n    );\n  }\n  if (!Number.isInteger(index) || index < 0) {\n    throw new RangeError(\n      `deriveUserKey: index must be a non-negative integer, got ${index}`,\n    );\n  }\n\n  const keystore = Keystore.fromMnemonic(\n    mnemonic,\n    opts.passphrase ?? \"\",\n    network,\n    \"p2wpkh\",\n    account,\n  );\n  try {\n    const derived = keystore.deriveAddress(change, index);\n    return {\n      version: USER_KEY_RECORD_VERSION,\n      scheme: CURRENT_USER_KEY_SCHEME,\n      purpose: SCHEME_PURPOSE[CURRENT_USER_KEY_SCHEME],\n      coinType: network.coinType,\n      network: network.name,\n      account,\n      change,\n      index,\n      path: derived.path,\n      publicKey: derived.publicKey,\n      masterFingerprint: keystore.fingerprint,\n      address: derived.address,\n      addressType: SCHEME_ADDRESS_TYPE[CURRENT_USER_KEY_SCHEME],\n    };\n  } finally {\n    keystore.lock();\n  }\n}\n\n/**\n * Identify which derivation produced a user key a vault already recorded.\n *\n * Searches the supported `m/84'` chain first, then — unless `includeLegacy` is\n * false — the legacy `m/44'` chain, over `count` indices of the receive chain\n * and (by default) the change chain. Returns the reconstructed descriptor, or\n * null when no derivation in the searched window matches.\n *\n * A non-null result with `scheme === \"bip44-p2pkh\"` is the diagnosis for a\n * stranded legacy vault: the record is real and reproducible from this\n * mnemonic, but was created outside the supported scheme. Use it to reconstruct\n * the vault; do NOT feed it to a creation path (see\n * `assertSupportedUserKeyScheme()`).\n */\nexport function resolveUserKey(\n  mnemonic: string,\n  network: NetworkConfig,\n  publicKey: string,\n  opts: ResolveUserKeyOptions = {},\n): UserKeyDescriptor | null {\n  const target = normalizeUserKey(publicKey);\n  const account = opts.account ?? 0;\n  const count = opts.count ?? DEFAULT_SEARCH_COUNT;\n  assertSearchOpts(account, count);\n  const passphrase = opts.passphrase ?? \"\";\n  const includeChange = opts.includeChange ?? true;\n  const includeLegacy = opts.includeLegacy ?? true;\n  const chains = includeChange ? [false, true] : [false];\n\n  // Current scheme first — a correctly created vault resolves without ever\n  // touching the legacy derivation.\n  const keystore = Keystore.fromMnemonic(\n    mnemonic,\n    passphrase,\n    network,\n    \"p2wpkh\",\n    account,\n  );\n  const masterFingerprint = keystore.fingerprint;\n  try {\n    const accountNode = keystore.watchOnlyAccountNode();\n    for (const change of chains) {\n      for (let index = 0; index < count; index++) {\n        const derived = deriveAddressFromAccountNode(\n          accountNode,\n          \"p2wpkh\",\n          network,\n          account,\n          change,\n          index,\n        );\n        if (!keyEquals(derived.publicKey, target)) continue;\n        return {\n          version: USER_KEY_RECORD_VERSION,\n          scheme: CURRENT_USER_KEY_SCHEME,\n          purpose: SCHEME_PURPOSE[CURRENT_USER_KEY_SCHEME],\n          coinType: network.coinType,\n          network: network.name,\n          account,\n          change,\n          index,\n          path: derived.path,\n          publicKey: derived.publicKey,\n          masterFingerprint,\n          address: derived.address,\n          addressType: SCHEME_ADDRESS_TYPE[CURRENT_USER_KEY_SCHEME],\n        };\n      }\n    }\n  } finally {\n    keystore.lock();\n  }\n\n  if (!includeLegacy) return null;\n\n  for (const change of chains) {\n    const candidates = deriveLegacyUserKeys(mnemonic, network, {\n      account,\n      passphrase,\n      count,\n      change,\n    });\n    for (const candidate of candidates) {\n      if (!keyEquals(candidate.compressedHex, target)) continue;\n      return {\n        version: USER_KEY_RECORD_VERSION,\n        scheme: LEGACY_USER_KEY_SCHEME,\n        purpose: SCHEME_PURPOSE[LEGACY_USER_KEY_SCHEME],\n        coinType: network.coinType,\n        network: network.name,\n        account,\n        change: candidate.change,\n        index: candidate.index,\n        path: candidate.path,\n        publicKey: candidate.compressedHex,\n        masterFingerprint,\n        address: candidate.address,\n        addressType: SCHEME_ADDRESS_TYPE[LEGACY_USER_KEY_SCHEME],\n      };\n    }\n  }\n\n  return null;\n}\n\n/**\n * `resolveUserKey()` that throws instead of returning null. Use when a vault\n * record is known to belong to this mnemonic and a miss is a genuine error\n * (wrong mnemonic, wrong passphrase, wrong network, or an index beyond the\n * search window).\n */\nexport function requireUserKey(\n  mnemonic: string,\n  network: NetworkConfig,\n  publicKey: string,\n  opts: ResolveUserKeyOptions = {},\n): UserKeyDescriptor {\n  const resolved = resolveUserKey(mnemonic, network, publicKey, opts);\n  if (resolved) return resolved;\n  const count = opts.count ?? DEFAULT_SEARCH_COUNT;\n  throw new WalletError(\n    \"USER_KEY_NOT_FOUND\",\n    `No derivation of this mnemonic produced user key ${publicKey} on ` +\n      `${network.name} (searched account ${opts.account ?? 0}, ${count} ` +\n      `indices per chain, ` +\n      `${opts.includeLegacy === false ? \"m/84' only\" : \"m/84' and legacy m/44'\"}). ` +\n      `Check the passphrase, network, and account, or widen \\`count\\`.`,\n  );\n}\n\n/** True when the descriptor came from the legacy, unsupported m/44' scheme. */\nexport function isLegacyUserKey(descriptor: UserKeyDescriptor): boolean {\n  return descriptor.scheme === LEGACY_USER_KEY_SCHEME;\n}\n\n/**\n * Fail-closed guard for vault CREATION paths: throws unless the descriptor was\n * produced by the supported `m/84'` scheme. Recovery paths should not call this\n * — a legacy descriptor is exactly what they expect to handle.\n */\nexport function assertSupportedUserKeyScheme(\n  descriptor: UserKeyDescriptor,\n): void {\n  if (descriptor.scheme === CURRENT_USER_KEY_SCHEME) return;\n  throw new WalletError(\n    \"UNSUPPORTED_USER_KEY_SCHEME\",\n    `User key at ${descriptor.path} uses the unsupported \"${descriptor.scheme}\" ` +\n      `scheme; only \"${CURRENT_USER_KEY_SCHEME}\" (m/84' p2wpkh) may be used to ` +\n      `create a vault. Legacy records are recovery-only — see resolveUserKey().`,\n  );\n}\n\n/**\n * Check a recorded pubkey against a descriptor without re-deriving. Accepts the\n * same compressed or x-only forms as `resolveUserKey()`.\n */\nexport function userKeyMatches(\n  descriptor: UserKeyDescriptor,\n  publicKey: string,\n): boolean {\n  return keyEquals(descriptor.publicKey, normalizeUserKey(publicKey));\n}\n","/**\n * XverseProvider — a {@link WalletProvider} adapter that lets an Xverse\n * browser-extension connection plug into {@link WalletAggregator.addProvider}\n * and sit alongside imported (seed) keystore accounts under one aggregator.\n *\n * IMPORTANT — capability boundary\n * --------------------------------\n * Xverse is fundamentally different from the built-in HD `KeystoreProvider`:\n *\n *   • It exposes ONE externally-controlled payment address, not an HD chain.\n *     There is no xpub and no way to derive fresh receive/change addresses, so\n *     `deriveReceiveAddresses`/`deriveChangeAddresses` return that single\n *     address repeated to satisfy the consuming `Wallet`'s gap-limit loop.\n *     `scantxoutset` dedupes outputs across descriptors, so the repetition does\n *     not affect balances.\n *   • The SDK's `AddressType` union is p2wpkh-only today. Rather than assert\n *     `\"p2wpkh\"` nominally, the constructor verifies the connected account's\n *     resolved scriptPubKey really is a native witness-v0 key-hash and rejects\n *     anything else (nested segwit / taproot) until the union is widened — so a\n *     reported `addressType` is always the account's real on-chain script.\n *   • Signing is asynchronous and delegated to the extension. `signPsbt`\n *     forwards to {@link signPsbtXverse} and merges the returned signatures.\n *\n * As a result this provider supports REGISTRATION, BALANCE / UTXO AGGREGATION,\n * and EXTERNAL PSBT SIGNING. It does NOT support the aggregator's internal\n * `Wallet.send()` flow: the aggregator holds no local signer for Xverse's\n * externally-controlled key, so spending an Xverse wallet must go through the browser flow\n * (`prepareForWalletSigning()` + `signPsbtXverse()` / the `useTestnetWallet`\n * hook), not `wallet.send()`.\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport type {\n  AddressType,\n  DerivedKey,\n  NetworkConfig,\n  SignerInfo,\n  Utxo,\n  WalletNetworkConfig,\n} from \"../types.js\";\nimport { InvalidAddressError } from \"../errors.js\";\nimport { signPsbtXverse, type XverseConnection } from \"../wallets/xverse.js\";\nimport type { WalletProvider } from \"./types.js\";\n\nexport interface XverseProviderOptions {\n  // Provider name used as the aggregator key. Must not be a reserved name\n  // (\"keystore\"). Defaults to \"xverse\".\n  name?: string;\n  // BIP-44 account index used only for the aggregator wallet key. Defaults to 0.\n  account?: number;\n}\n\n// The only address type the SDK's AddressType union admits today. The\n// constructor verifies the connected account's script matches before this is\n// reported, so it is the account's real type, not a nominal placeholder.\nconst SUPPORTED_ADDRESS_TYPE: AddressType = \"p2wpkh\";\n\n// A native P2WPKH scriptPubKey is OP_0 <20-byte push> = \"0014\" + 20 bytes\n// (40 hex chars). Anything else (nested segwit \"a914…87\", taproot \"5120…\",\n// legacy \"76a914…88ac\") is not representable as a supported AddressType.\nfunction isP2wpkhScript(scriptHex: string): boolean {\n  return /^0014[0-9a-f]{40}$/i.test(scriptHex);\n}\n\nexport class XverseProvider implements WalletProvider {\n  readonly name: string;\n  readonly account: number;\n\n  // Xverse is segwit/taproot and only signs externally-built PSBTs; the\n  // aggregator's internal send() builder is unsupported (see file header).\n  readonly supportsInternalSend = false;\n\n  readonly #network: WalletNetworkConfig;\n  readonly #address: string;\n  readonly #publicKey: string;\n  readonly #scriptPubKey: string;\n  readonly #fingerprint: string;\n  #locked = false;\n\n  constructor(\n    connection: XverseConnection,\n    network: WalletNetworkConfig,\n    options: XverseProviderOptions = {},\n  ) {\n    this.name = options.name ?? \"xverse\";\n    this.account = options.account ?? 0;\n    this.#network = network;\n    this.#address = connection.paymentAddress;\n    this.#publicKey = connection.publicKey;\n\n    // Resolve the address' scriptPubKey so sync()'s scan reverse-lookup matches.\n    const script = bitcoin.address.toOutputScript(\n      this.#address,\n      network.network,\n    );\n    this.#scriptPubKey = Buffer.from(script).toString(\"hex\");\n\n    // Fail closed if the connected account is not native segwit: reporting a\n    // verified-wrong addressType (nested segwit / taproot) would let a consumer\n    // encode the wrong PSBT input type or descriptor. Reject until the SDK's\n    // AddressType union can represent the account's real script.\n    if (!isP2wpkhScript(this.#scriptPubKey)) {\n      throw new InvalidAddressError(\n        this.#address,\n        \"Xverse payment account is not native segwit (p2wpkh); nested segwit \" +\n          \"and taproot are not yet representable by the SDK's AddressType union\",\n      );\n    }\n\n    // Synthesize a stable fingerprint from the payment public key (Xverse does\n    // not expose a BIP32 master fingerprint).\n    const pubkeyBuf = Buffer.from(this.#publicKey, \"hex\");\n    this.#fingerprint = Buffer.from(bitcoin.crypto.hash160(pubkeyBuf))\n      .subarray(0, 4)\n      .toString(\"hex\");\n  }\n\n  get network(): NetworkConfig {\n    return this.#network;\n  }\n\n  get addressType(): AddressType {\n    // Verified native p2wpkh in the constructor — not a nominal placeholder.\n    return SUPPORTED_ADDRESS_TYPE;\n  }\n\n  get fingerprint(): string {\n    return this.#fingerprint;\n  }\n\n  // Xverse does not expose a BIP32 derivation path or xpub, so both are null.\n  //\n  // These were previously reported as the sentinel `xverse:<network>` and `\"\"`.\n  // The sentinel was worse than useless: `accountPath` is typed and documented\n  // as a derivation path, so a consumer recording where a key came from (vault\n  // user-key metadata, a PSBT bip32Derivation, a descriptor) received a\n  // non-empty string that passed every truthiness guard and only failed once it\n  // was parsed — surfacing as `derived path \"xverse:signet\" is not a\n  // BIP-44-style path` deep inside the consumer rather than at the boundary.\n  // Null makes \"there is no derivation\" a value the type system carries.\n  get accountPath(): string | null {\n    return null;\n  }\n\n  get accountXpub(): string | null {\n    return null;\n  }\n\n  get locked(): boolean {\n    return this.#locked;\n  }\n\n  // Single-address wallet: return the one payment address repeated `count`\n  // times so the consuming Wallet's gap-limit bookkeeping converges. Every\n  // entry is identical (index 0), so the next-unused pointer never advances off\n  // the real address and scantxoutset dedupes the repeated descriptor.\n  deriveReceiveAddresses(count: number): DerivedKey[] {\n    return this.#repeat(count, false);\n  }\n\n  // Xverse manages its own change internally and exposes no change chain; the\n  // same payment address is returned so sync() converges. (The internal\n  // send()/change path is unsupported — see the file header.)\n  deriveChangeAddresses(count: number): DerivedKey[] {\n    return this.#repeat(count, true);\n  }\n\n  // Delegate signing to the Xverse extension and merge the returned signatures\n  // back into the caller's PSBT. Used for externally-built PSBTs; the internal\n  // send() builder never reaches this for an externally-signed Xverse wallet.\n  async signPsbt(psbt: bitcoin.Psbt, inputs: readonly Utxo[]): Promise<void> {\n    if (this.#locked) {\n      throw new Error(\"XverseProvider is locked\");\n    }\n    // Only ask Xverse to sign inputs this provider actually owns. Signing is\n    // matched by scriptPubKey so a multi-provider PSBT cannot route another\n    // wallet's inputs through this extension.\n    const signingIndexes = inputs.reduce<number[]>((acc, input, index) => {\n      if (input.scriptPubKey === this.#scriptPubKey) acc.push(index);\n      return acc;\n    }, []);\n    const signedBase64 = await signPsbtXverse(\n      psbt.toBase64(),\n      [{ address: this.#address, signingIndexes }],\n      this.#network,\n    );\n    const signed = bitcoin.Psbt.fromBase64(signedBase64, {\n      network: this.#network.network,\n    });\n    psbt.combine(signed);\n\n    // sats-connect's `signTransaction` (called with `broadcast: false`) returns\n    // the PSBT with partial signatures attached but does NOT finalize the\n    // inputs. Callers (`depositToVault`) extract the transaction immediately\n    // after signing, and `Psbt.extractTransaction()` throws \"Not finalized\"\n    // unless every input carries a `finalScriptWitness`. Mirror the internal\n    // keystore path (which finalizes after signing) by finalizing the inputs\n    // this provider signed — but only those, so a multi-provider PSBT's other\n    // inputs stay untouched, and skip any input a future Xverse build already\n    // finalized to avoid a redundant-finalization throw.\n    for (const index of signingIndexes) {\n      const input = psbt.data.inputs[index];\n      if (input?.finalScriptWitness || input?.finalScriptSig) continue;\n      psbt.finalizeInput(index);\n    }\n  }\n\n  // Watch-only address descriptor for the single payment address.\n  buildDescriptor(_change: boolean): string {\n    return `addr(${this.#address})`;\n  }\n\n  getSignerInfo(): SignerInfo {\n    return {\n      fingerprint: this.#fingerprint,\n      accountPath: this.accountPath,\n      accountXpub: this.accountXpub,\n      addressType: SUPPORTED_ADDRESS_TYPE,\n      canSign: !this.#locked,\n    };\n  }\n\n  // Disconnecting the extension; subsequent signPsbt calls throw.\n  lock(): void {\n    this.#locked = true;\n  }\n\n  #repeat(count: number, change: boolean): DerivedKey[] {\n    const entry: DerivedKey = {\n      index: 0,\n      change,\n      // No derivation behind this address — see the accountPath note above.\n      path: null,\n      address: this.#address,\n      scriptPubKey: this.#scriptPubKey,\n      publicKey: this.#publicKey,\n    };\n    return Array.from({ length: Math.max(0, count) }, () => entry);\n  }\n\n  [Symbol.for(\"nodejs.util.inspect.custom\")](): string {\n    return `XverseProvider(name=${this.name} address=${this.#address} network=${this.#network.name} locked=${this.#locked})`;\n  }\n\n  toJSON(): Record<string, unknown> {\n    return {\n      name: this.name,\n      address: this.#address,\n      network: this.#network.name,\n      fingerprint: this.#fingerprint,\n      locked: this.#locked,\n    };\n  }\n}\n","/**\n * Unified React hook wrapping BOTH import paths — the Xverse extension and a\n * locally-derived seed phrase — behind one connect / disconnect / sign /\n * balance API.\n *\n * Chain coverage\n * --------------\n * The import flow offers two chains, and they are NOT equally reachable by both\n * wallet types:\n *\n *   • `\"xverse\"` → signet only. The extension has no regtest mode.\n *   • `\"seed\"`   → signet AND regtest. Keys are derived locally, so both chains\n *                  work identically.\n *\n * Balances for both wallet types resolve through whatever the SELECTED CHAIN\n * configures — the import method never changes which node is queried. Signet\n * reads from its Esplora indexer with the Tachi JSON-RPC node as fallback;\n * regtest uses `scantxoutset` on its node directly (see src/lib/rpcClient.ts).\n * `chainsFor()` reports the per-wallet-type support so a chain selector can be\n * gated up front instead of surfacing a failed connect.\n *\n * Concurrency: every action that changes which wallet/chain the state describes\n * bumps a generation counter, and async results are dropped if the generation\n * moved on. Without that, a signet balance scan (which can take ~14s on the\n * fallback path) could resolve after a chain switch and paint the old chain's\n * balance onto the new one.\n *\n * Seed custody: the mnemonic is used once, at import, to derive an account\n * `Keystore`; the hook retains the keystore (not the mnemonic) and `lock()`s it\n * on disconnect and on unmount. Chain switching re-encodes the address from the\n * retained account xpub, so the user is never asked for the seed phrase twice.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { Keystore } from \"../derivation.js\";\nimport { getUtxos } from \"../lib/rpcClient.js\";\nimport {\n  DEFAULT_WALLET_NETWORK,\n  SUPPORTED_WALLET_CHAINS,\n  getWalletNetwork,\n} from \"../network.js\";\nimport type { WalletChainName, WalletNetworkConfig } from \"../types.js\";\nimport {\n  SEED_SUPPORTED_CHAINS,\n  connectSeed,\n  deriveSeedAddress,\n  signPsbtSeed,\n  type ImportSeedOptions,\n  type SeedInputToSign,\n} from \"../wallets/seed.js\";\nimport {\n  XVERSE_SUPPORTED_CHAINS,\n  connectXverse,\n  signMessageXverse,\n  signPsbtXverse,\n  type SignedMessage,\n  type XverseInputToSign,\n} from \"../wallets/xverse.js\";\n\nexport type WalletType = \"xverse\" | \"seed\";\nexport type WalletStatus = \"idle\" | \"connecting\" | \"connected\" | \"error\";\n\n// Which chains each wallet type can actually be connected on. Xverse is\n// signet-only (no regtest mode in the extension); a seed phrase is derived\n// locally and therefore works on both.\nconst CHAINS_BY_WALLET_TYPE: Record<WalletType, readonly WalletChainName[]> = {\n  xverse: XVERSE_SUPPORTED_CHAINS,\n  seed: SEED_SUPPORTED_CHAINS,\n};\n\n// Chains a given wallet type can connect on. Use this to gate a chain selector\n// so an unsupported combination is never offered.\nexport function chainsFor(walletType: WalletType): WalletChainName[] {\n  return [...CHAINS_BY_WALLET_TYPE[walletType]];\n}\n\n// One copy of the chain list, made once at module load and handed to every\n// caller of the hook.\n//\n// It must be a COPY, because SUPPORTED_WALLET_CHAINS is the network registry's\n// own mutable array — a consumer that sorts or splices the list it was given\n// would otherwise reorder chains for every other consumer in the app.\n//\n// And it must be made ONCE, not per render: the hook returns this by reference,\n// so a fresh array each render would change identity every time and re-fire any\n// consumer `useEffect`/`useMemo` that lists it as a dependency.\nconst SUPPORTED_CHAINS_SNAPSHOT: WalletChainName[] = [\n  ...SUPPORTED_WALLET_CHAINS,\n];\n\n// Re-exported for convenience so consumers can type their chain selector\n// against the same union the hook accepts.\nexport type { WalletChainName } from \"../types.js\";\n\nexport interface WalletBalance {\n  confirmed: number;\n  total: number;\n}\n\n// The connector owns this shape (it is what sats-connect returns); re-exported\n// rather than redeclared so the two cannot drift apart.\nexport type { SignedMessage } from \"../wallets/xverse.js\";\n\nexport interface TestnetWalletState {\n  walletType: WalletType | null;\n  // The currently selected chain. Defaults to signet and is never null so a\n  // consumer's chain selector always has a value to render.\n  chain: WalletChainName;\n  network: WalletNetworkConfig;\n  address: string | null;\n  publicKey: string | null;\n  balance: WalletBalance | null;\n  status: WalletStatus;\n  error: string | null;\n  // Derivation path of `address` for a seed import (e.g. `m/84'/1'/0'/0/0`);\n  // null for Xverse, which exposes no BIP32 path.\n  derivationPath: string | null;\n}\n\nexport interface UseTestnetWallet extends TestnetWalletState {\n  // Every chain the import-wallet flow supports (signet, regtest). Signet\n  // first. NOTE: not every wallet type can connect on every one of these —\n  // gate the selector with `chainsFor(walletType)` instead when a wallet type\n  // has already been chosen.\n  supportedChains: WalletChainName[];\n  // Chains the given wallet type can connect on: signet only for \"xverse\",\n  // signet + regtest for \"seed\".\n  chainsFor: (walletType: WalletType) => WalletChainName[];\n  // Connect the Xverse extension on `chain` (defaults to signet). Rejects\n  // regtest up front — use `importSeedPhrase` for that chain.\n  connect: (walletType: WalletType, chain?: WalletChainName) => Promise<void>;\n  // Import a BIP39 mnemonic and derive the account locally. Works on BOTH\n  // signet and regtest. The mnemonic is used only to derive the keystore and\n  // is not retained by the hook.\n  importSeedPhrase: (\n    mnemonic: string,\n    chain?: WalletChainName,\n    options?: ImportSeedOptions,\n  ) => Promise<void>;\n  // Switch the active chain. A seed wallet re-encodes its address locally (no\n  // re-import); an Xverse wallet re-handshakes. With nothing connected this\n  // just updates the selection for the next connect/import.\n  switchNetwork: (chain: WalletChainName) => Promise<void>;\n  disconnect: () => void;\n  refreshBalance: () => Promise<void>;\n  // Sign a PSBT (base64 or hex) with the connected wallet. Xverse requires\n  // `XverseInputToSign[]`; a seed wallet accepts `SeedInputToSign[]` or, if\n  // omitted, signs every input with the imported key.\n  signPsbt: (\n    psbtBase64OrHex: string,\n    inputsToSign?: XverseInputToSign[] | SeedInputToSign[],\n  ) => Promise<string>;\n  // BIP-322 message signing. Xverse only — the SDK ships a BIP-322 verifier\n  // but no signer, so this throws for a seed wallet.\n  signMessage: (message: string) => Promise<SignedMessage>;\n}\n\nconst INITIAL_STATE: TestnetWalletState = {\n  walletType: null,\n  chain: DEFAULT_WALLET_NETWORK.name as WalletChainName,\n  network: DEFAULT_WALLET_NETWORK,\n  address: null,\n  publicKey: null,\n  balance: null,\n  status: \"idle\",\n  error: null,\n  derivationPath: null,\n};\n\nfunction errorMessage(err: unknown): string {\n  return err instanceof Error ? err.message : String(err);\n}\n\nexport function useTestnetWallet(): UseTestnetWallet {\n  const [state, setState] = useState<TestnetWalletState>(INITIAL_STATE);\n\n  // Mirror state into a ref so the action callbacks (with stable identities)\n  // always read the latest connection without re-creating on every change.\n  const stateRef = useRef(state);\n  useEffect(() => {\n    stateRef.current = state;\n  }, [state]);\n\n  // Monotonic id of \"which wallet on which chain\" the state currently\n  // describes. Every action that changes that bumps it. An async action\n  // captures the value it started under and DROPS its state write if the id\n  // moved on — otherwise a slow request that resolves after the user switched\n  // chain or disconnected would write a stale result over the new one (e.g.\n  // painting a regtest balance onto a signet wallet).\n  const generationRef = useRef(0);\n\n  // Single-flight guard for refreshBalance. `scantxoutset` takes a node-global\n  // lock (see getUtxos), so two overlapping refreshes on the same node fail with\n  // \"Scan already in progress\". This dedupes same-component overlaps (React\n  // StrictMode double-invoke, a double-clicked refresh button) onto one scan;\n  // cross-client collisions are handled by getUtxos's retry.\n  //\n  // Tagged with the generation it belongs to: a refresh issued AFTER a chain\n  // switch must not be coalesced onto the previous chain's in-flight scan, or\n  // it would resolve to a result that is then discarded as stale, leaving the\n  // caller with no balance at all.\n  const refreshInFlight = useRef<{\n    gen: number;\n    promise: Promise<void>;\n  } | null>(null);\n\n  // Live signer for a seed import. Held in a ref (never in state) so it is\n  // never serialized into React DevTools or a state snapshot. Locked — zeroing\n  // its private bytes — on disconnect, on re-import, and on unmount.\n  const keystoreRef = useRef<Keystore | null>(null);\n  // Derivation of the imported address, so signPsbt can pick the right signer\n  // and switchNetwork can re-encode the same position on the new chain.\n  const seedPositionRef = useRef<{ change: boolean; index: number }>({\n    change: false,\n    index: 0,\n  });\n\n  const releaseKeystore = useCallback(() => {\n    keystoreRef.current?.lock();\n    keystoreRef.current = null;\n  }, []);\n\n  // Begin an action that changes which wallet/chain the state describes.\n  // Returns a token to check before writing state asynchronously.\n  const beginAction = useCallback((): number => ++generationRef.current, []);\n  const isCurrent = useCallback(\n    (gen: number) => generationRef.current === gen,\n    [],\n  );\n\n  // Resolve a chain name to its config, reporting an unknown name through the\n  // hook's own error state instead of rejecting the returned promise. Consumers\n  // read failures from `w.error`; a JS caller passing a bad chain string should\n  // not get an unhandled rejection on top of that.\n  const resolveChain = useCallback(\n    (chain: WalletChainName): WalletNetworkConfig | null => {\n      try {\n        return getWalletNetwork(chain);\n      } catch (err) {\n        setState((s) => ({ ...s, status: \"error\", error: errorMessage(err) }));\n        return null;\n      }\n    },\n    [],\n  );\n\n  // Best-effort wipe if the component unmounts while a seed wallet is imported.\n  useEffect(() => releaseKeystore, [releaseKeystore]);\n\n  const connect = useCallback(\n    async (walletType: WalletType, chain: WalletChainName = \"signet\") => {\n      if (walletType === \"seed\") {\n        // A seed wallet cannot be connected without its mnemonic; route the\n        // caller to the right entry point rather than failing obscurely.\n        setState((s) => ({\n          ...s,\n          status: \"error\",\n          error:\n            'connect(\"seed\") is not supported — call importSeedPhrase(mnemonic, chain) instead.',\n        }));\n        return;\n      }\n\n      const network = resolveChain(chain);\n      if (!network) return;\n\n      // Gate before touching the extension so an unsupported chain reports the\n      // real reason (and the actual alternative) rather than a connect failure.\n      if (!XVERSE_SUPPORTED_CHAINS.includes(chain)) {\n        setState((s) => ({\n          ...s,\n          chain,\n          network,\n          status: \"error\",\n          error:\n            `Xverse cannot connect on \"${chain}\" (supported: ` +\n            `${XVERSE_SUPPORTED_CHAINS.join(\", \")}). Import ${chain} with a ` +\n            `seed phrase via importSeedPhrase() instead.`,\n        }));\n        return;\n      }\n\n      const gen = beginAction();\n      setState((s) => ({\n        ...s,\n        chain,\n        network,\n        status: \"connecting\",\n        error: null,\n      }));\n      try {\n        const conn = await connectXverse(network);\n        // The user switched chain / disconnected / imported a seed while the\n        // extension prompt was open — that newer intent wins.\n        if (!isCurrent(gen)) return;\n        // Switching away from a seed import to the extension: drop the old signer.\n        releaseKeystore();\n        setState({\n          walletType,\n          chain,\n          network,\n          address: conn.paymentAddress,\n          publicKey: conn.publicKey,\n          balance: null,\n          status: \"connected\",\n          error: null,\n          derivationPath: null,\n        });\n      } catch (err) {\n        if (!isCurrent(gen)) return;\n        setState((s) => ({\n          ...s,\n          status: \"error\",\n          error: errorMessage(err),\n        }));\n      }\n    },\n    [beginAction, isCurrent, releaseKeystore, resolveChain],\n  );\n\n  // Import a BIP39 mnemonic and derive the account locally. This is the path\n  // that makes REGTEST usable — Xverse has no regtest mode — and it works\n  // identically on signet.\n  const importSeedPhrase = useCallback(\n    async (\n      mnemonic: string,\n      chain: WalletChainName = \"signet\",\n      options: ImportSeedOptions = {},\n    ) => {\n      const network = resolveChain(chain);\n      if (!network) return;\n\n      const gen = beginAction();\n      setState((s) => ({\n        ...s,\n        chain,\n        network,\n        status: \"connecting\",\n        error: null,\n      }));\n      try {\n        // Synchronous, but kept async so both entry points share one contract\n        // and a consumer can await either without branching.\n        const { connection, keystore } = connectSeed(\n          mnemonic,\n          network,\n          options,\n        );\n        if (!isCurrent(gen)) {\n          // Superseded mid-import: this keystore would otherwise be an\n          // unreachable live signer holding private key bytes.\n          keystore.lock();\n          return;\n        }\n        // Replace any prior signer (re-import, or switching from Xverse).\n        releaseKeystore();\n        keystoreRef.current = keystore;\n        seedPositionRef.current = {\n          change: connection.change,\n          index: connection.index,\n        };\n        setState({\n          walletType: \"seed\",\n          chain,\n          network,\n          address: connection.paymentAddress,\n          publicKey: connection.publicKey,\n          balance: null,\n          status: \"connected\",\n          error: null,\n          derivationPath: connection.path,\n        });\n      } catch (err) {\n        if (!isCurrent(gen)) return;\n        // An invalid mnemonic must not leave a half-connected wallet behind.\n        // The state below reports \"disconnected\", so the previous signer has to\n        // go with it — otherwise live key material would outlive the wallet the\n        // UI says is gone.\n        releaseKeystore();\n        setState((s) => ({\n          ...s,\n          walletType: null,\n          address: null,\n          publicKey: null,\n          derivationPath: null,\n          balance: null,\n          status: \"error\",\n          error: errorMessage(err),\n        }));\n      }\n    },\n    [beginAction, isCurrent, releaseKeystore, resolveChain],\n  );\n\n  // Switch the active chain.\n  //   • seed   — re-encode the SAME account for the new chain from the retained\n  //              account xpub. signet and regtest share coin type 1, so no\n  //              re-import (and no second seed-phrase prompt) is needed.\n  //   • xverse — re-handshake; the extension must be asked for an address on\n  //              the new network.\n  //   • neither connected — just move the selection for the next connect/import.\n  const switchNetwork = useCallback(\n    async (chain: WalletChainName) => {\n      const { walletType } = stateRef.current;\n\n      if (walletType === \"seed\") {\n        const keystore = keystoreRef.current;\n        if (!keystore) {\n          setState((s) => ({\n            ...s,\n            status: \"error\",\n            error:\n              \"Seed wallet signer is unavailable — re-import the mnemonic.\",\n          }));\n          return;\n        }\n        const network = resolveChain(chain);\n        if (!network) return;\n        // Bump even though the re-encode is synchronous: it invalidates any\n        // balance refresh already in flight for the previous chain.\n        beginAction();\n        try {\n          const connection = deriveSeedAddress(keystore, network, {\n            ...seedPositionRef.current,\n          });\n          setState((s) => ({\n            ...s,\n            chain,\n            network,\n            address: connection.paymentAddress,\n            publicKey: connection.publicKey,\n            derivationPath: connection.path,\n            // The prior chain's balance is meaningless on the new chain.\n            balance: null,\n            status: \"connected\",\n            error: null,\n          }));\n        } catch (err) {\n          setState((s) => ({\n            ...s,\n            status: \"error\",\n            error: errorMessage(err),\n          }));\n        }\n        return;\n      }\n\n      if (walletType) {\n        await connect(walletType, chain);\n        return;\n      }\n\n      const network = resolveChain(chain);\n      if (!network) return;\n      beginAction();\n      setState((s) => ({\n        ...s,\n        chain,\n        network,\n        balance: null,\n      }));\n    },\n    [beginAction, connect, resolveChain],\n  );\n\n  const disconnect = useCallback(() => {\n    // Invalidate anything in flight so a late-resolving refresh cannot write a\n    // balance back onto the disconnected state.\n    beginAction();\n    releaseKeystore();\n    refreshInFlight.current = null;\n    setState(INITIAL_STATE);\n  }, [beginAction, releaseKeystore]);\n\n  const refreshBalance = useCallback(async () => {\n    const gen = generationRef.current;\n\n    // Coalesce a concurrent refresh onto the in-flight scan instead of firing a\n    // second scantxoutset that would collide on the node-global lock — but only\n    // if it belongs to the SAME wallet/chain. A refresh issued after a chain\n    // switch must start its own scan, or it would await a result that is then\n    // discarded as stale and silently leave the balance unset.\n    const inFlight = refreshInFlight.current;\n    if (inFlight && inFlight.gen === gen) return inFlight.promise;\n\n    const { walletType, network, address } = stateRef.current;\n    if (!walletType || !address) return;\n\n    const run = (async () => {\n      try {\n        // TRUST BOUNDARY: this is a DISPLAY-ONLY balance. getUtxos() validates\n        // the gateway response for shape and range, but not authenticity — the\n        // hosted gateway is trusted to report the correct UTXO set (it can be\n        // MITM'd, DNS-poisoned, or compromised). Do NOT reuse these UTXOs to\n        // build/sign a transaction without re-verifying each input's value and\n        // scriptPubKey against its parent tx first (see verifyParentOutput in\n        // src/psbt.ts).\n        const utxos = await getUtxos(network, address);\n        // The wallet or chain changed while this scan was running (a signet\n        // scan can take ~14s on the fallback path) — this balance belongs to a\n        // wallet the UI has moved on from.\n        if (!isCurrent(gen)) return;\n        const total = utxos.reduce((sum, u) => sum + u.value, 0);\n        const confirmed = utxos\n          .filter((u) => u.status.confirmed)\n          .reduce((sum, u) => sum + u.value, 0);\n        const balance: WalletBalance = { confirmed, total };\n        // Clear any stale error from a prior failed refresh on success.\n        setState((s) => ({ ...s, balance, status: \"connected\", error: null }));\n      } catch (err) {\n        if (!isCurrent(gen)) return;\n        setState((s) => ({ ...s, status: \"error\", error: errorMessage(err) }));\n      }\n    })();\n\n    refreshInFlight.current = { gen, promise: run };\n    try {\n      await run;\n    } finally {\n      // Only clear if no newer refresh replaced this slot meanwhile.\n      if (refreshInFlight.current?.promise === run) {\n        refreshInFlight.current = null;\n      }\n    }\n  }, [isCurrent]);\n\n  const signPsbt = useCallback(\n    async (\n      psbtBase64OrHex: string,\n      inputsToSign?: XverseInputToSign[] | SeedInputToSign[],\n    ): Promise<string> => {\n      const { walletType, network } = stateRef.current;\n      if (!walletType) throw new Error(\"Wallet not connected\");\n\n      if (walletType === \"seed\") {\n        const keystore = keystoreRef.current;\n        if (!keystore) {\n          throw new Error(\n            \"Seed wallet signer is unavailable — re-import the mnemonic.\",\n          );\n        }\n        // The two wallet types take DIFFERENT input shapes. Passing the Xverse\n        // form here (`{address, signingIndexes}`) would otherwise surface as a\n        // baffling \"input index undefined is out of range\", so name the real\n        // problem.\n        const wrongShape = (\n          inputsToSign as XverseInputToSign[] | undefined\n        )?.find?.((i) => i && typeof i === \"object\" && \"signingIndexes\" in i);\n        if (wrongShape) {\n          throw new Error(\n            \"A seed wallet takes SeedInputToSign[] ({ index, change?, \" +\n              \"addressIndex? }), not the Xverse { address, signingIndexes } \" +\n              \"form. Omit the argument to sign every input with the imported key.\",\n          );\n        }\n        const { change, index } = seedPositionRef.current;\n        return signPsbtSeed(psbtBase64OrHex, keystore, network, {\n          // Omitted → sign every input with the imported key.\n          inputs: inputsToSign as SeedInputToSign[] | undefined,\n          defaultChange: change,\n          defaultIndex: index,\n        });\n      }\n\n      if (!inputsToSign) {\n        throw new Error(\n          \"Xverse requires inputsToSign — use prepareForWalletSigning()\",\n        );\n      }\n      return signPsbtXverse(\n        psbtBase64OrHex,\n        inputsToSign as XverseInputToSign[],\n        network,\n      );\n    },\n    [],\n  );\n\n  const signMessage = useCallback(\n    async (message: string): Promise<SignedMessage> => {\n      const { walletType, network, address } = stateRef.current;\n      if (!walletType) throw new Error(\"Wallet not connected\");\n      if (!address) throw new Error(\"No address available to sign with\");\n      if (walletType === \"seed\") {\n        // The SDK ships verifyBip322Signature but no BIP-322 signer, so there\n        // is no correct local implementation to route to. Fail loudly rather\n        // than emit a signature in some other format that the verifier — and\n        // any consumer checking it — would reject.\n        throw new Error(\n          \"BIP-322 message signing is not supported for seed-imported wallets \" +\n            \"(no local BIP-322 signer). Connect Xverse on signet to sign messages.\",\n        );\n      }\n      return signMessageXverse(message, address, network);\n    },\n    [],\n  );\n\n  return {\n    ...state,\n    supportedChains: SUPPORTED_CHAINS_SNAPSHOT,\n    chainsFor,\n    connect,\n    importSeedPhrase,\n    switchNetwork,\n    disconnect,\n    refreshBalance,\n    signPsbt,\n    signMessage,\n  };\n}\n"],"mappings":"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","names":["generateMnemonic","validateMnemonic","address","sats","address","bitcoin","address","bitcoin","address","bitcoin","ecc","bitcoin","ecc","generateMnemonic","validateMnemonic","bip39","import_bip32","ecc","bitcoin","DEFAULT_TIMEOUT_MS","bip32","ecc","tryWipeNode","bip32","tryWipeNode","bitcoin","script","network","address","signMessage"]}