{"version":3,"sources":["../src/keys.ts"],"sourcesContent":["// Private Pool V2 — wallet-derived key tree (ADR-002).\n//\n//   root_sk      = blake2b256(walletSig)[:32]\n//   spending_sk  = blake2b256(root_sk || ASCII(\"relai-pool-v2/spend\"))\n//   viewing_sk   = blake2b256(root_sk || ASCII(\"relai-pool-v2/view\"))\n//   nullifier_sk = blake2b256(root_sk || ASCII(\"relai-pool-v2/null\"))\n//\n//   spending_pk  = Poseidon1(spending_sk_field) mod p   // field element used in commitments\n//   viewing_pk   = X25519.public_from(viewing_sk)        // 32-byte X25519 pubkey\n//\n//   PaymentAddress = base58( spending_pk_bytes_BE || viewing_pk_bytes )\n//\n// `*_sk` is the raw 32-byte blake2b output. `*_sk_field` is the same\n// bytes reduced mod the BN254 scalar field modulus, then re-emitted as\n// a bigint suitable for Poseidon / circuit witness inputs. The two\n// representations are kept side by side because:\n//   - the encryption layer (ADR-003) wants raw bytes for X25519\n//   - the circuit witness (Faza 1) wants field elements\n\nimport { blake2b } from '@noble/hashes/blake2b';\nimport { x25519 } from '@noble/curves/ed25519';\nimport { buildPoseidon } from 'circomlibjs';\nimport bs58 from 'bs58';\n\nconst FIELD_MODULUS =\n  21888242871839275222246405745257275088548364400416034343698204186575808495617n;\n\nconst TEXT_ENCODER = new TextEncoder();\nconst LABELS = {\n  spending: TEXT_ENCODER.encode('relai-pool-v2/spend'),\n  viewing: TEXT_ENCODER.encode('relai-pool-v2/view'),\n  nullifier: TEXT_ENCODER.encode('relai-pool-v2/null'),\n} as const;\n\nconst PAYMENT_ADDRESS_BYTES = 64; // spending_pk(32) + viewing_pk(32)\n\n// ── Hash helpers ─────────────────────────────────────────────────────────\n\nfunction concatBytes(...parts: Uint8Array[]): Uint8Array {\n  const total = parts.reduce((sum, p) => sum + p.length, 0);\n  const out = new Uint8Array(total);\n  let off = 0;\n  for (const p of parts) {\n    out.set(p, off);\n    off += p.length;\n  }\n  return out;\n}\n\nfunction ensureBytes(label: string, bytes: Uint8Array, expected: number): void {\n  if (bytes.length !== expected) {\n    throw new Error(`${label}: expected ${expected} bytes, got ${bytes.length}`);\n  }\n}\n\n/// 32-byte BE → bigint (reduced mod BN254 scalar field).\nexport function bytesToFieldBE(bytes: Uint8Array): bigint {\n  if (bytes.length === 0) return 0n;\n  let v = 0n;\n  for (const b of bytes) v = (v << 8n) + BigInt(b);\n  return ((v % FIELD_MODULUS) + FIELD_MODULUS) % FIELD_MODULUS;\n}\n\n/// bigint → 32-byte BE buffer. Throws if the bigint exceeds 256 bits\n/// (shouldn't happen for field elements, but worth being explicit).\nexport function fieldToBytes32BE(value: bigint): Uint8Array {\n  if (value < 0n) {\n    value = ((value % FIELD_MODULUS) + FIELD_MODULUS) % FIELD_MODULUS;\n  }\n  const hex = value.toString(16).padStart(64, '0');\n  if (hex.length > 64) {\n    throw new Error(`fieldToBytes32BE: value exceeds 32 bytes (${hex.length / 2})`);\n  }\n  return Uint8Array.from(Buffer.from(hex, 'hex'));\n}\n\n// ── Poseidon singleton ───────────────────────────────────────────────────\n\nlet poseidonInstance: ReturnType<typeof buildPoseidon> extends Promise<infer T> ? T | null : never = null;\nasync function getPoseidon() {\n  if (!poseidonInstance) {\n    poseidonInstance = await buildPoseidon();\n  }\n  return poseidonInstance!;\n}\n\n/// Poseidon-Bn254X5 of one field input → bigint mod p.\nexport async function poseidon1(input: bigint): Promise<bigint> {\n  // circomlibjs has no published .d.ts so the `buildPoseidon` return is\n  // typed as `unknown` upstream. The runtime shape is well-known and\n  // stable; cast to `any` for the call sites.\n  const p = (await getPoseidon()) as any;\n  const h = p([input]);\n  return ((BigInt(p.F.toString(h)) % FIELD_MODULUS) + FIELD_MODULUS) % FIELD_MODULUS;\n}\n\n// ── Sub-key derivation ───────────────────────────────────────────────────\n\n/// `root_sk = blake2b256(walletSig)[:32]`. The wallet signature is\n/// produced off-screen by signing the canonical challenge message\n/// (`PRIVATE_POOL_V2_KEY_CHALLENGE`) — the caller is expected to handle\n/// the wallet popup itself.\nexport function deriveRootSk(walletSignature: Uint8Array): Uint8Array {\n  if (!walletSignature || walletSignature.length === 0) {\n    throw new Error('deriveRootSk: walletSignature must be non-empty');\n  }\n  return blake2b(walletSignature, { dkLen: 32 });\n}\n\n/// `<sub_sk> = blake2b256(root_sk || label)`.\nexport function deriveSubkey(rootSk: Uint8Array, label: Uint8Array): Uint8Array {\n  ensureBytes('rootSk', rootSk, 32);\n  return blake2b(concatBytes(rootSk, label), { dkLen: 32 });\n}\n\nexport type PrivatePoolKeyMaterial = {\n  /// Raw 32-byte blake2b sub-keys.\n  rootSk: Uint8Array;\n  spendingSk: Uint8Array;\n  viewingSk: Uint8Array;\n  nullifierSk: Uint8Array;\n  /// Field-element views of the spending/nullifier secrets — what the\n  /// circuit witness consumes. spending_sk_field / nullifier_sk_field\n  /// are `bytesToFieldBE(spendingSk)` etc.\n  spendingSkField: bigint;\n  nullifierSkField: bigint;\n  /// Public counterparts.\n  spendingPkField: bigint;        // Poseidon1(spendingSkField)\n  viewingPk: Uint8Array;          // X25519.public(viewingSk)\n  /// Encoded payment address, base58( spending_pk_bytes || viewing_pk_bytes ).\n  paymentAddress: string;\n};\n\n/// One-shot key derivation. Returns the full sub-key tree + payment\n/// address. Idempotent; safe to re-run on every page load.\nexport async function derivePrivatePoolKeys(\n  walletSignature: Uint8Array,\n): Promise<PrivatePoolKeyMaterial> {\n  const rootSk = deriveRootSk(walletSignature);\n  const spendingSk = deriveSubkey(rootSk, LABELS.spending);\n  const viewingSk = deriveSubkey(rootSk, LABELS.viewing);\n  const nullifierSk = deriveSubkey(rootSk, LABELS.nullifier);\n\n  const spendingSkField = bytesToFieldBE(spendingSk);\n  const nullifierSkField = bytesToFieldBE(nullifierSk);\n  const spendingPkField = await poseidon1(spendingSkField);\n  const viewingPk = x25519.getPublicKey(viewingSk);\n\n  const paymentAddress = encodePaymentAddress(spendingPkField, viewingPk);\n\n  return {\n    rootSk,\n    spendingSk,\n    viewingSk,\n    nullifierSk,\n    spendingSkField,\n    nullifierSkField,\n    spendingPkField,\n    viewingPk,\n    paymentAddress,\n  };\n}\n\n// ── F2: code-derived key material (private payment codes) ──────────────\n//\n// A one-time `code_seed` (32 bytes) deterministically generates the\n// same key tree as a wallet signature would. The seed plays exactly\n// the role of `walletSignature` in `derivePrivatePoolKeys` — same\n// blake2b labels, same Poseidon derivation, same X25519 viewing key.\n//\n// Why we reuse the existing tree shape:\n//   - The credit-pool leaf binds `owner_pk = spending_pk_field`.\n//     Whatever generates `spending_sk_field` and lets us compute its\n//     Poseidon-1 image works as a \"recipient identity.\" The wallet\n//     case derives via signature; the bearer-code case derives via\n//     seed. Same circuit, same prover, same on-chain verifier.\n//   - The encrypted note blob is keyed by `viewing_pk` (X25519). The\n//     redeemer needs to derive the matching `viewing_sk` from the\n//     same seed at redeem time.\n//\n// Why this is safe:\n//   - The seed is generated client-side and never leaves the issuer's\n//     device (except via the URL/QR they hand to the redeemer).\n//   - Anyone with the seed can re-derive all keys and cash out the\n//     parked leaf. That's exactly the bearer-token semantics we want.\n//   - The seed is large enough (32 bytes = 256 bits) that brute-force\n//     guessing the seed of a specific leaf is not feasible.\n//\n// Important difference from `derivePrivatePoolKeys`:\n//   - No `paymentAddress` is computed — the seed itself encodes both\n//     halves of the addressing, and we don't want to leak a payment\n//     address that ties the code to a long-lived identity.\n\n/// Two seed formats, picked by the issuer per code:\n///\n///   • `long` (default; 32B / 256-bit)\n///     - Encoded as base58 in URL fragment (~44 chars).\n///     - Astronomically brute-force resistant. Industry-standard for\n///       \"fire and forget\" bearer tokens.\n///     - Hard to dictate by voice/SMS — must be shared as URL/QR.\n///\n///   • `short` (16-char Wi-Fi-style base32, 80-bit)\n///     - Encoded as `XXXX-XXXX-XXXX-XXXX` for easy reading.\n///     - ~80 bits of entropy. At 10⁹ hashes/s a single specific code\n///       takes ≈ 38,000 years to brute-force; the on-chain leaf set\n///       gates each guess by Poseidon-derived owner_pk lookup, so\n///       practical attack cost is much higher. Comfortable for typical\n///       consumer amounts; we'd recommend `long` for institutional /\n///       very high value.\n///     - Can be dictated, written on paper, typed into a separate\n///       device — same UX feel as a Wi-Fi password.\n///\n/// Both share the SAME key-derivation algorithm — we just hand\n/// `deriveRootSk` a different number of entropy bytes. The on-chain\n/// proof is identical for both formats; format choice is purely an\n/// issuer-side UX dial.\nconst LONG_SEED_BYTES = 32;\nconst SHORT_SEED_BYTES = 10; // 10 × 8 = 80 bits, encodes as 16 base32 chars\n\nexport type CodeSeedFormat = 'short' | 'long';\n\nexport type CodeKeyMaterial = {\n  /// Raw seed; the only thing the issuer must guard + later share.\n  /// 10 bytes for `short`, 32 bytes for `long`. The on-chain derivation\n  /// works identically either way (blake2b is variable-input).\n  codeSeed: Uint8Array;\n  /// Which format the seed was generated as. Lets callers re-encode\n  /// the same seed back to the right surface (base32 vs base58)\n  /// without recomputing entropy.\n  format: CodeSeedFormat;\n  /// Key tree, identical shape to wallet-derived material.\n  rootSk: Uint8Array;\n  spendingSk: Uint8Array;\n  viewingSk: Uint8Array;\n  nullifierSk: Uint8Array;\n  spendingSkField: bigint;\n  nullifierSkField: bigint;\n  spendingPkField: bigint;\n  viewingPk: Uint8Array;\n};\n\n/// Generate a fresh code seed using a CSPRNG. The issuer holds this\n/// locally (in IndexedDB if they want cancel capability) and shares\n/// it out-of-band — usually as part of a URL fragment so it never\n/// touches the server.\n///\n/// `format` defaults to `'long'` for safety. Callers wanting the\n/// dictatable Wi-Fi-style format pass `'short'` explicitly.\nexport function generateCodeSeed(format: CodeSeedFormat = 'long'): Uint8Array {\n  const bytes = format === 'short' ? SHORT_SEED_BYTES : LONG_SEED_BYTES;\n  const out = new Uint8Array(bytes);\n  // `crypto` is the same global the rest of the SDK uses for\n  // blinding randomness; available in both browser and Node ≥ 19.\n  crypto.getRandomValues(out);\n  return out;\n}\n\n/// Detect the format of an existing seed by its length. Useful when a\n/// caller has the bytes but not the format flag (e.g. after re-deriving\n/// from a decoded code).\nexport function detectCodeSeedFormat(codeSeed: Uint8Array): CodeSeedFormat {\n  if (codeSeed.length === SHORT_SEED_BYTES) return 'short';\n  if (codeSeed.length === LONG_SEED_BYTES) return 'long';\n  throw new Error(\n    `detectCodeSeedFormat: unsupported seed length ${codeSeed.length} (expected ${SHORT_SEED_BYTES} or ${LONG_SEED_BYTES})`,\n  );\n}\n\n/// Deterministic key derivation from a code seed. Same algorithm as\n/// the wallet-derived path; the only difference is the entropy source\n/// (seed vs wallet signature). Idempotent — calling it twice with the\n/// same seed yields exactly the same key material.\n///\n/// Accepts both short (10-byte) and long (32-byte) seeds; the\n/// `blake2b(seed)` rooting step doesn't care about input length, and\n/// downstream derivation is identical.\nexport async function deriveCodeKeys(\n  codeSeed: Uint8Array,\n): Promise<CodeKeyMaterial> {\n  if (!codeSeed) {\n    throw new Error('deriveCodeKeys: codeSeed required');\n  }\n  if (codeSeed.length !== SHORT_SEED_BYTES && codeSeed.length !== LONG_SEED_BYTES) {\n    throw new Error(\n      `deriveCodeKeys: codeSeed must be ${SHORT_SEED_BYTES} or ${LONG_SEED_BYTES} bytes (got ${codeSeed.length})`,\n    );\n  }\n  const format: CodeSeedFormat = codeSeed.length === SHORT_SEED_BYTES ? 'short' : 'long';\n  const rootSk = deriveRootSk(codeSeed);\n  const spendingSk = deriveSubkey(rootSk, LABELS.spending);\n  const viewingSk = deriveSubkey(rootSk, LABELS.viewing);\n  const nullifierSk = deriveSubkey(rootSk, LABELS.nullifier);\n\n  const spendingSkField = bytesToFieldBE(spendingSk);\n  const nullifierSkField = bytesToFieldBE(nullifierSk);\n  const spendingPkField = await poseidon1(spendingSkField);\n  const viewingPk = x25519.getPublicKey(viewingSk);\n\n  return {\n    codeSeed,\n    format,\n    rootSk,\n    spendingSk,\n    viewingSk,\n    nullifierSk,\n    spendingSkField,\n    nullifierSkField,\n    spendingPkField,\n    viewingPk,\n  };\n}\n\n// ── F2: code-seed codec for URLs (Bech32-style) ────────────────────────\n//\n// We encode the 32B seed as base58 — same alphabet the SDK already uses\n// for the payment address, no new dependencies, ~44 ASCII chars. The\n// short fixed length lets us validate decoded seeds with a single\n// length check. A Bech32 checksum is overkill at this size (a typo\n// would already fail the field-element derivation at park-redeem time,\n// just less helpfully) — kept simple to reduce dependency surface.\n\n/// Encode a 32B \"long\" seed for URL / QR transport. Result is base58\n/// of the raw seed bytes — no version byte, no checksum. The caller\n/// is expected to put this in the URL fragment (`#seed=…`) so it never\n/// hits server logs.\nexport function encodeCodeSeed(seed: Uint8Array): string {\n  if (!seed || seed.length !== LONG_SEED_BYTES) {\n    throw new Error(\n      `encodeCodeSeed: seed must be exactly ${LONG_SEED_BYTES} bytes (got ${seed?.length ?? 0})`,\n    );\n  }\n  const encoder = (bs58 as { encode?: (b: Uint8Array) => string }).encode\n    ?? (bs58 as { default?: { encode: (b: Uint8Array) => string } }).default?.encode;\n  if (!encoder) {\n    throw new Error('encodeCodeSeed: bs58 encoder missing — incompatible bs58 version');\n  }\n  return encoder(seed);\n}\n\n/// Inverse of `encodeCodeSeed`. Throws on malformed input (wrong\n/// length, non-base58 chars). The caller may want to wrap this in a\n/// try/catch to surface a user-friendly \"invalid code\" error.\nexport function decodeCodeSeed(encoded: string): Uint8Array {\n  if (typeof encoded !== 'string' || encoded.length === 0) {\n    throw new Error('decodeCodeSeed: input must be a non-empty string');\n  }\n  const decoder = (bs58 as { decode?: (s: string) => Uint8Array }).decode\n    ?? (bs58 as { default?: { decode: (s: string) => Uint8Array } }).default?.decode;\n  if (!decoder) {\n    throw new Error('decodeCodeSeed: bs58 decoder missing — incompatible bs58 version');\n  }\n  const out = decoder(encoded);\n  if (out.length !== LONG_SEED_BYTES) {\n    throw new Error(\n      `decodeCodeSeed: decoded ${out.length} bytes, expected ${LONG_SEED_BYTES}`,\n    );\n  }\n  return out;\n}\n\n// ── Short-code (Wi-Fi-style base32) codec ──────────────────────────────\n//\n// 10 bytes → 16 base32 characters → rendered as `XXXX-XXXX-XXXX-XXXX`.\n// RFC 4648 base32 alphabet (`A-Z2-7`) is deliberately confusion-free —\n// no 0/O, 1/I/l ambiguity. Issuer can dictate the code over the phone\n// without losing 25 minutes to \"is that an O or zero\" rounds. Dashes\n// are decorative; the decoder strips them.\n//\n// Why base32 over base58 here:\n//   • 5 bits per character → fixed 16-char output for 80 bits, with\n//     no awkward \"≈ 14 chars\" rounding.\n//   • Voice-friendly alphabet.\n//   • Dashes optional in the input — we tolerate either form.\n\nconst BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n\nfunction encodeBase32(bytes: Uint8Array): string {\n  let bits = 0;\n  let value = 0;\n  let out = '';\n  for (const b of bytes) {\n    value = (value << 8) | b;\n    bits += 8;\n    while (bits >= 5) {\n      out += BASE32_ALPHABET[(value >>> (bits - 5)) & 0x1f];\n      bits -= 5;\n    }\n  }\n  if (bits > 0) {\n    out += BASE32_ALPHABET[(value << (5 - bits)) & 0x1f];\n  }\n  return out;\n}\n\nfunction decodeBase32(s: string): Uint8Array {\n  // Normalise: uppercase, drop dashes/whitespace, validate alphabet.\n  const cleaned = s.toUpperCase().replace(/[\\s-]/g, '');\n  if (cleaned.length === 0) throw new Error('decodeBase32: empty input');\n  for (const ch of cleaned) {\n    if (BASE32_ALPHABET.indexOf(ch) === -1) {\n      throw new Error(`decodeBase32: illegal character '${ch}' (allowed: A-Z, 2-7)`);\n    }\n  }\n  let bits = 0;\n  let value = 0;\n  const out: number[] = [];\n  for (const ch of cleaned) {\n    value = (value << 5) | BASE32_ALPHABET.indexOf(ch);\n    bits += 5;\n    if (bits >= 8) {\n      out.push((value >>> (bits - 8)) & 0xff);\n      bits -= 8;\n    }\n  }\n  return Uint8Array.from(out);\n}\n\n/// Format a raw 10-byte seed into the user-facing\n/// `XXXX-XXXX-XXXX-XXXX` representation. Round-trip with\n/// `decodeShortCode` is exact.\nexport function encodeShortCode(seed: Uint8Array): string {\n  if (!seed || seed.length !== SHORT_SEED_BYTES) {\n    throw new Error(\n      `encodeShortCode: seed must be exactly ${SHORT_SEED_BYTES} bytes (got ${seed?.length ?? 0})`,\n    );\n  }\n  const flat = encodeBase32(seed); // 16 chars\n  // Insert a hyphen every 4 chars for readability. `XXXX-XXXX-XXXX-XXXX`.\n  return flat.match(/.{1,4}/g)!.join('-');\n}\n\n/// Inverse of `encodeShortCode`. Accepts the canonical\n/// `XXXX-XXXX-XXXX-XXXX` form, plain `XXXXXXXXXXXXXXXX`, lower-case,\n/// or any combination with extra whitespace — anything a user might\n/// dictate or paste with mild messiness. Throws on illegal characters\n/// or wrong length.\nexport function decodeShortCode(encoded: string): Uint8Array {\n  if (typeof encoded !== 'string') {\n    throw new Error('decodeShortCode: input must be a string');\n  }\n  const out = decodeBase32(encoded);\n  if (out.length !== SHORT_SEED_BYTES) {\n    throw new Error(\n      `decodeShortCode: decoded ${out.length} bytes, expected ${SHORT_SEED_BYTES} (16 base32 chars)`,\n    );\n  }\n  return out;\n}\n\n/// Auto-detect the format of an encoded code string and return the raw\n/// seed bytes. Convenience for redeem flows that accept either format\n/// from a single text input.\n///\n/// Detection rules:\n///   • Strip whitespace + dashes. If the result is exactly 16 chars in\n///     the base32 alphabet → short code, decoded as such.\n///   • Otherwise try base58 → 32-byte long seed.\n///   • Anything else → throw with a hint.\nexport function decodeCodeAuto(encoded: string): {\n  seed: Uint8Array;\n  format: CodeSeedFormat;\n} {\n  if (typeof encoded !== 'string' || encoded.trim().length === 0) {\n    throw new Error('decodeCodeAuto: empty input');\n  }\n  const cleaned = encoded.trim();\n  const base32Cleaned = cleaned.toUpperCase().replace(/[\\s-]/g, '');\n  // Short code: exactly 16 base32 chars.\n  if (\n    base32Cleaned.length === 16\n    && [...base32Cleaned].every((c) => BASE32_ALPHABET.indexOf(c) !== -1)\n  ) {\n    return { seed: decodeShortCode(cleaned), format: 'short' };\n  }\n  // Otherwise assume base58 long. The base58 decoder will throw if\n  // the alphabet doesn't match.\n  try {\n    return { seed: decodeCodeSeed(cleaned), format: 'long' };\n  } catch (err) {\n    throw new Error(\n      `decodeCodeAuto: not a valid short (16 char) or long (~44 char) code: ${err instanceof Error ? err.message : String(err)}`,\n    );\n  }\n}\n\n// ── Payment address codec ────────────────────────────────────────────────\n\n/// `base58(spending_pk_bytes_BE || viewing_pk_bytes)` — the human-shareable\n/// \"send notes to me\" handle. 64 bytes encoded → ~88 base58 chars.\nexport function encodePaymentAddress(spendingPkField: bigint, viewingPk: Uint8Array): string {\n  ensureBytes('viewingPk', viewingPk, 32);\n  const spendingPkBytes = fieldToBytes32BE(spendingPkField);\n  const concat = concatBytes(spendingPkBytes, viewingPk);\n  // bs58@4: bs58.encode(buffer)\n  // bs58@5+: bs58.default.encode(buffer)\n  const encoder = (bs58 as { encode?: (b: Uint8Array) => string }).encode\n    ?? (bs58 as { default?: { encode: (b: Uint8Array) => string } }).default?.encode;\n  if (!encoder) throw new Error('encodePaymentAddress: bs58.encode not found');\n  return encoder(concat);\n}\n\nexport function parsePaymentAddress(address: string): {\n  spendingPkField: bigint;\n  viewingPk: Uint8Array;\n} {\n  const decoder = (bs58 as { decode?: (s: string) => Uint8Array }).decode\n    ?? (bs58 as { default?: { decode: (s: string) => Uint8Array } }).default?.decode;\n  if (!decoder) throw new Error('parsePaymentAddress: bs58.decode not found');\n  const decoded = decoder(address);\n  if (decoded.length !== PAYMENT_ADDRESS_BYTES) {\n    throw new Error(\n      `parsePaymentAddress: decoded length ${decoded.length} != ${PAYMENT_ADDRESS_BYTES}`,\n    );\n  }\n  return {\n    spendingPkField: bytesToFieldBE(decoded.slice(0, 32)),\n    viewingPk: decoded.slice(32, 64),\n  };\n}\n\n// ── Wallet challenge ─────────────────────────────────────────────────────\n\n/// Canonical challenge message the wallet signs to derive the root_sk.\n/// Versioned + namespaced so a sig made for V2 can never be reused for\n/// any other RelAI feature (or any other app's \"sign this\" flow).\nexport const PRIVATE_POOL_V2_KEY_CHALLENGE =\n  'RelAI Private Pool V2 — derive private balance keys.\\n\\nThis signature unlocks your hidden-amount balance under the /app/private surface. It does NOT authorize any spend on its own.';\n\n/// Build the wallet challenge bytes for `signMessage` (UTF-8 of the\n/// human-readable challenge above; matches what the on-screen prompt\n/// will display).\nexport function buildKeyChallengeMessage(): Uint8Array {\n  return TEXT_ENCODER.encode(PRIVATE_POOL_V2_KEY_CHALLENGE);\n}\n"],"mappings":"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","names":["bs58"]}