{"version":3,"sources":["../src/witness.ts","../src/types.ts"],"sourcesContent":["// Private Pool V2 — circuit witness builders + crypto helpers.\n//\n// Off-circuit mirrors of the in-circuit primitives + assembly logic\n// that converts (user intent, on-chain state, owned notes) into the\n// witness shape that snarkjs.fullProve() expects.\n//\n// Every primitive here MUST produce the same field element as its\n// circom counterpart in `contracts/circuits/private-pool-v2/lib.circom`.\n// Conformance is checked by Node-side smoke tests in\n// `contracts/scripts/private-pool-v2/`.\n//\n// Poseidon implementation: `circomlibjs` (already a dep, used by V4).\n// Async-init via `loadPoseidonBuilder()` — first call awaits the\n// builder, subsequent calls reuse the cached instance.\n//\n// Reference: ADR-001 (commitment), ADR-002 (key derivation), ADR-003\n// (encryption — separate file `private-pool-notes.ts`).\n\nimport {\n  type DepositWitness,\n  type WithdrawWitness,\n  type JoinSplitWitness,\n  type DecryptedNote,\n  type OwnedNote,\n  type MerklePath,\n  type PaymentAddress,\n  POOL_DEPTH,\n  ASP_DEPTH,\n} from './types'\n\n// ─────────────────────────────────────────────────────────────────────\n// Poseidon loader — same pattern as `shielded-private-links.ts`.\n// `circomlibjs.buildPoseidon()` returns a callable that takes an array\n// of field elements (bigint) and returns a Uint8Array which we lift\n// back to bigint via `F.toObject`. We cache the builder promise so\n// every call after the first is sync-ish.\n// ─────────────────────────────────────────────────────────────────────\n\ninterface PoseidonField {\n  toObject: (value: unknown) => bigint | string | number\n}\ninterface PoseidonBuilder {\n  (inputs: bigint[]): unknown\n  F: PoseidonField\n}\n\nlet poseidonBuilderPromise: Promise<PoseidonBuilder> | null = null\n\nasync function loadPoseidonBuilder(): Promise<PoseidonBuilder> {\n  if (!poseidonBuilderPromise) {\n    poseidonBuilderPromise = import('circomlibjs').then(\n      (mod) => (mod as { buildPoseidon: () => Promise<PoseidonBuilder> }).buildPoseidon(),\n    )\n  }\n  return await poseidonBuilderPromise\n}\n\nfunction liftPoseidonResult(builder: PoseidonBuilder, value: unknown): bigint {\n  const lifted = builder.F && typeof builder.F.toObject === 'function'\n    ? builder.F.toObject(value)\n    : value\n  const big = BigInt(String(lifted))\n  return ((big % FIELD_MODULUS) + FIELD_MODULUS) % FIELD_MODULUS\n}\n\nasync function poseidonHash(inputs: bigint[]): Promise<bigint> {\n  const builder = await loadPoseidonBuilder()\n  return liftPoseidonResult(builder, builder(inputs))\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Field-element helpers — all circuit signals are BN254 scalar field\n// elements. snarkjs accepts them as decimal strings; the bigint API\n// keeps the rest of our code reasonable.\n// ─────────────────────────────────────────────────────────────────────\n\n/** BN254 scalar field modulus. */\nexport const FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n\n\n/** Convert 32 little-endian bytes (or fewer; right-padded) to a field element. */\nexport function bytesToField(bytes: Uint8Array): bigint {\n  if (bytes.length > 32) throw new Error('bytesToField: input > 32 bytes')\n  let n = 0n\n  for (let i = bytes.length - 1; i >= 0; i--) {\n    n = (n << 8n) | BigInt(bytes[i] ?? 0)\n  }\n  return n % FIELD_MODULUS\n}\n\n/** Convert a field element to 32 little-endian bytes. */\nexport function fieldToBytes(n: bigint): Uint8Array {\n  const out = new Uint8Array(32)\n  let x = ((n % FIELD_MODULUS) + FIELD_MODULUS) % FIELD_MODULUS\n  for (let i = 0; i < 32; i++) {\n    out[i] = Number(x & 0xffn)\n    x >>= 8n\n  }\n  return out\n}\n\n/** Field element → snarkjs witness string (decimal). */\nexport function fieldStr(n: bigint): string {\n  return n.toString(10)\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Commitment & nullifier — off-circuit mirrors of lib.circom.\n// ─────────────────────────────────────────────────────────────────────\n\n/**\n * Commitment per ADR-001:\n *   commitment = Poseidon4(value, owner_pk, blinding, memo_or_zero)\n */\nexport async function deriveCommitment(args: {\n  value: bigint\n  ownerPk: bigint\n  blinding: bigint\n  memo: bigint | null\n}): Promise<bigint> {\n  const memo = args.memo ?? 0n\n  return poseidonHash([args.value, args.ownerPk, args.blinding, memo])\n}\n\n/**\n * Spending pubkey per ADR-002:\n *   spending_pk = Poseidon1(spending_sk)\n */\nexport async function deriveSpendingPk(spendingSk: bigint): Promise<bigint> {\n  return poseidonHash([spendingSk])\n}\n\n/**\n * Nullifier per ADR-002:\n *   nullifier = Poseidon2(commitment, nullifier_sk)\n */\nexport async function deriveNullifier(args: {\n  commitment: bigint\n  nullifierSk: bigint\n}): Promise<bigint> {\n  return poseidonHash([args.commitment, args.nullifierSk])\n}\n\n/**\n * Merkle node hash per lib.circom MerkleRoot template:\n *   parent = Poseidon2(left, right)\n *\n * Used by witness builders that need to verify their own merkle path\n * against a published root before submitting a proof (catches\n * stale-witness bugs early).\n */\nexport async function merkleParent(left: bigint, right: bigint): Promise<bigint> {\n  return poseidonHash([left, right])\n}\n\n/**\n * Recompute a merkle root from a leaf + path. Useful for client-side\n * sanity check (\"does my path actually verify against the published\n * root?\") before burning proof time.\n */\nexport async function recomputeMerkleRoot(args: {\n  leaf: bigint\n  pathElements: bigint[]\n  pathIndices: number[]\n}): Promise<bigint> {\n  if (args.pathElements.length !== args.pathIndices.length) {\n    throw new Error('recomputeMerkleRoot: pathElements / pathIndices length mismatch')\n  }\n  let hash = args.leaf\n  for (let i = 0; i < args.pathElements.length; i++) {\n    const sibling = args.pathElements[i]!\n    const idx = args.pathIndices[i]!\n    if (idx !== 0 && idx !== 1) {\n      throw new Error(`recomputeMerkleRoot: pathIndices[${i}] must be 0 or 1, got ${idx}`)\n    }\n    if (idx === 0) {\n      hash = await merkleParent(hash, sibling)\n    } else {\n      hash = await merkleParent(sibling, hash)\n    }\n  }\n  return hash\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Owned note enrichment — given a DecryptedNote, fill in the cached\n// commitment + nullifier so the rest of the pipeline can reuse them.\n// ─────────────────────────────────────────────────────────────────────\n\nexport async function enrichOwnedNote(args: {\n  note: DecryptedNote\n  leafIndex: number\n  receivedAtSlot: number\n  spent: boolean\n  nullifierSk: bigint\n}): Promise<OwnedNote> {\n  const { note, leafIndex, receivedAtSlot, spent, nullifierSk } = args\n  const commitment = await deriveCommitment({\n    value: note.value,\n    ownerPk: bytesToField(note.ownerPk),\n    blinding: bytesToField(note.blinding),\n    memo: note.memo ? bytesToField(note.memo) : null,\n  })\n  const nullifier = await deriveNullifier({ commitment, nullifierSk })\n  return {\n    ...note,\n    leafIndex,\n    receivedAtSlot,\n    spent,\n    commitment: fieldToBytes(commitment),\n    nullifier: fieldToBytes(nullifier),\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Deposit witness builder.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface DepositInputs {\n  /** Output note being created. */\n  recipient: PaymentAddress\n  /** Random 32-byte blinding for the new note. */\n  blinding: Uint8Array\n  /** Optional 32-byte memo. */\n  memo: Uint8Array | null\n  /** USDC value entering the pool, in micro-USDC. */\n  publicValue: bigint\n  /** Hash of the SPL source account (binds proof to caller). */\n  depositorAddrHash: bigint\n  /** Relayer fee, micro-USDC. */\n  fee: bigint\n}\n\nexport function buildDepositWitness(inputs: DepositInputs): DepositWitness {\n  // Note: Deposit witness needs no Poseidon hashing — the circuit\n  // computes the commitment from inputs. So this builder stays sync.\n\n  if (inputs.publicValue < 0n) throw new Error('publicValue must be non-negative')\n  if (inputs.fee < 0n) throw new Error('fee must be non-negative')\n  if (inputs.fee > inputs.publicValue) throw new Error('fee exceeds publicValue')\n\n  return {\n    ownerPk: fieldStr(bytesToField(inputs.recipient.spendingPk)),\n    blinding: fieldStr(bytesToField(inputs.blinding)),\n    memo: fieldStr(inputs.memo ? bytesToField(inputs.memo) : 0n),\n    publicValue: fieldStr(inputs.publicValue),\n    depositorAddrHash: fieldStr(inputs.depositorAddrHash),\n    fee: fieldStr(inputs.fee),\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Withdraw witness builder.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface WithdrawInputs {\n  /** The note being spent (must have spent: false). */\n  note: OwnedNote\n  /** Caller's spending key (to prove authority). */\n  spendingSk: bigint\n  /** Caller's nullifier key (to derive `note.nullifier`). */\n  nullifierSk: bigint\n  /** Pool merkle inclusion proof for `note.commitment`. */\n  poolPath: MerklePath\n  /** ASP merkle inclusion proof for `note.commitment`. */\n  aspPath: MerklePath\n  /** Recipient address hash (binds the proof). */\n  publicAddress: bigint\n  /** USDC paid out to recipient (== note.value − fee). */\n  publicValue: bigint\n  /** Relayer fee, micro-USDC. */\n  fee: bigint\n}\n\nexport async function buildWithdrawWitness(inputs: WithdrawInputs): Promise<WithdrawWitness> {\n  // Sanity checks — fail fast before the prover spends 30s on garbage.\n  if (inputs.note.spent) throw new Error('cannot withdraw an already-spent note')\n  if (inputs.publicValue + inputs.fee !== inputs.note.value) {\n    throw new Error(\n      `balance mismatch: publicValue (${inputs.publicValue}) + fee (${inputs.fee}) ` +\n      `≠ note.value (${inputs.note.value})`,\n    )\n  }\n  if (inputs.poolPath.pathElements.length !== POOL_DEPTH) {\n    throw new Error(`pool path depth ${inputs.poolPath.pathElements.length} ≠ ${POOL_DEPTH}`)\n  }\n  if (inputs.aspPath.pathElements.length !== ASP_DEPTH) {\n    throw new Error(`asp path depth ${inputs.aspPath.pathElements.length} ≠ ${ASP_DEPTH}`)\n  }\n\n  // Verify the witness reconstructs to the published root before\n  // submitting — catches stale paths or bad indexer data early.\n  const leaf = bytesToField(inputs.note.commitment)\n  const recomputedPool = await recomputeMerkleRoot({\n    leaf,\n    pathElements: inputs.poolPath.pathElements.map(bytesToField),\n    pathIndices: inputs.poolPath.pathIndices,\n  })\n  if (recomputedPool !== bytesToField(inputs.poolPath.root)) {\n    throw new Error('pool path does not verify against published root')\n  }\n\n  return {\n    value: fieldStr(inputs.note.value),\n    ownerPk: fieldStr(bytesToField(inputs.note.ownerPk)),\n    blinding: fieldStr(bytesToField(inputs.note.blinding)),\n    memo: fieldStr(inputs.note.memo ? bytesToField(inputs.note.memo) : 0n),\n    spendingSk: fieldStr(inputs.spendingSk),\n    nullifierSk: fieldStr(inputs.nullifierSk),\n    poolPathElements: inputs.poolPath.pathElements.map((b) => fieldStr(bytesToField(b))),\n    poolPathIndices: inputs.poolPath.pathIndices,\n    aspPathElements: inputs.aspPath.pathElements.map((b) => fieldStr(bytesToField(b))),\n    aspPathIndices: inputs.aspPath.pathIndices,\n    publicValue: fieldStr(inputs.publicValue),\n    publicAddress: fieldStr(inputs.publicAddress),\n    fee: fieldStr(inputs.fee),\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// JoinSplit2x2 witness builder.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface JoinSplitInputs {\n  /** Two input notes being spent. */\n  inputs: [OwnedNote, OwnedNote]\n  /** Merkle paths for each input (pool + ASP). */\n  inputPoolPaths: [MerklePath, MerklePath]\n  inputAspPaths: [MerklePath, MerklePath]\n\n  /** Caller's spending key (must derive to both inputs' ownerPk). */\n  spendingSk: bigint\n  /** Caller's nullifier key. */\n  nullifierSk: bigint\n\n  /** Two output notes being created. */\n  outputs: [DecryptedNote, DecryptedNote]\n\n  /** Public-side flow. Set ONE of in/out non-zero, the other to 0n. */\n  publicValueIn: bigint\n  publicValueOut: bigint\n  /** Counterparty hash if publicValueIn or publicValueOut > 0; else 0n. */\n  publicAddress: bigint\n  /** Relayer fee. */\n  fee: bigint\n}\n\nexport async function buildJoinSplitWitness(inputs: JoinSplitInputs): Promise<JoinSplitWitness> {\n  // Hard assertions — fail fast.\n  if (inputs.inputs[0].spent || inputs.inputs[1].spent) {\n    throw new Error('cannot spend an already-spent note')\n  }\n  if (\n    inputs.inputs[0].commitment.every((b, i) => b === inputs.inputs[1].commitment[i])\n  ) {\n    throw new Error(\n      'both inputs share the same commitment — would produce identical nullifiers; ' +\n      'use distinct dummy notes (DUMMY_A vs DUMMY_B from pool config) for padding',\n    )\n  }\n  if (inputs.publicValueIn > 0n && inputs.publicValueOut > 0n) {\n    throw new Error('only one of publicValueIn / publicValueOut may be non-zero')\n  }\n\n  // Balance equation:\n  //   sum(inputValues) + publicValueIn === sum(outputValues) + publicValueOut + fee\n  const inputSum = inputs.inputs[0].value + inputs.inputs[1].value\n  const outputSum = inputs.outputs[0].value + inputs.outputs[1].value\n  const lhs = inputSum + inputs.publicValueIn\n  const rhs = outputSum + inputs.publicValueOut + inputs.fee\n  if (lhs !== rhs) {\n    throw new Error(\n      `balance mismatch: ${inputSum} + ${inputs.publicValueIn} (in) ≠ ${outputSum} + ` +\n      `${inputs.publicValueOut} (out) + ${inputs.fee} (fee)`,\n    )\n  }\n\n  // Verify both pool paths against same root (the circuit asserts this).\n  const root0 = bytesToField(inputs.inputPoolPaths[0].root)\n  const root1 = bytesToField(inputs.inputPoolPaths[1].root)\n  if (root0 !== root1) {\n    throw new Error('both inputs must verify against the same pool root snapshot')\n  }\n\n  // ASP roots same check.\n  const asp0 = bytesToField(inputs.inputAspPaths[0].root)\n  const asp1 = bytesToField(inputs.inputAspPaths[1].root)\n  if (asp0 !== asp1) {\n    throw new Error('both inputs must verify against the same ASP root snapshot')\n  }\n\n  // Verify each input's witness path against its claimed root —\n  // rejects stale or wrong indexer data before we burn 30-60s of CPU.\n  for (let i = 0; i < 2; i++) {\n    const note = inputs.inputs[i]!\n    const poolPath = inputs.inputPoolPaths[i]!\n    const aspPath = inputs.inputAspPaths[i]!\n    const leaf = bytesToField(note.commitment)\n    const recomputedPool = await recomputeMerkleRoot({\n      leaf,\n      pathElements: poolPath.pathElements.map(bytesToField),\n      pathIndices: poolPath.pathIndices,\n    })\n    if (recomputedPool !== bytesToField(poolPath.root)) {\n      throw new Error(`input ${i} pool path does not verify against root`)\n    }\n    const recomputedAsp = await recomputeMerkleRoot({\n      leaf,\n      pathElements: aspPath.pathElements.map(bytesToField),\n      pathIndices: aspPath.pathIndices,\n    })\n    if (recomputedAsp !== bytesToField(aspPath.root)) {\n      throw new Error(`input ${i} ASP path does not verify against root`)\n    }\n  }\n\n  // Spend authority sanity — derived spending_pk should match BOTH\n  // input notes' ownerPk. Circuit constraint G enforces this; we\n  // catch wallet-mixup bugs before the prover.\n  const spendingPk = await deriveSpendingPk(inputs.spendingSk)\n  for (let i = 0; i < 2; i++) {\n    const note = inputs.inputs[i]!\n    const ownerField = bytesToField(note.ownerPk)\n    if (spendingPk !== ownerField) {\n      throw new Error(\n        `input ${i} ownerPk does not match the provided spending key — ` +\n        `did you switch wallets? expected ${spendingPk}, got ${ownerField}`,\n      )\n    }\n  }\n\n  return {\n    inputValue1:       fieldStr(inputs.inputs[0].value),\n    inputOwnerPk1:     fieldStr(bytesToField(inputs.inputs[0].ownerPk)),\n    inputBlinding1:    fieldStr(bytesToField(inputs.inputs[0].blinding)),\n    inputMemo1:        fieldStr(inputs.inputs[0].memo ? bytesToField(inputs.inputs[0].memo) : 0n),\n    inputPoolPath1:    inputs.inputPoolPaths[0].pathElements.map((b) => fieldStr(bytesToField(b))),\n    inputPoolIndices1: inputs.inputPoolPaths[0].pathIndices,\n    inputAspPath1:     inputs.inputAspPaths[0].pathElements.map((b) => fieldStr(bytesToField(b))),\n    inputAspIndices1:  inputs.inputAspPaths[0].pathIndices,\n\n    inputValue2:       fieldStr(inputs.inputs[1].value),\n    inputOwnerPk2:     fieldStr(bytesToField(inputs.inputs[1].ownerPk)),\n    inputBlinding2:    fieldStr(bytesToField(inputs.inputs[1].blinding)),\n    inputMemo2:        fieldStr(inputs.inputs[1].memo ? bytesToField(inputs.inputs[1].memo) : 0n),\n    inputPoolPath2:    inputs.inputPoolPaths[1].pathElements.map((b) => fieldStr(bytesToField(b))),\n    inputPoolIndices2: inputs.inputPoolPaths[1].pathIndices,\n    inputAspPath2:     inputs.inputAspPaths[1].pathElements.map((b) => fieldStr(bytesToField(b))),\n    inputAspIndices2:  inputs.inputAspPaths[1].pathIndices,\n\n    spendingSk:  fieldStr(inputs.spendingSk),\n    nullifierSk: fieldStr(inputs.nullifierSk),\n\n    outputValue1:    fieldStr(inputs.outputs[0].value),\n    outputOwnerPk1:  fieldStr(bytesToField(inputs.outputs[0].ownerPk)),\n    outputBlinding1: fieldStr(bytesToField(inputs.outputs[0].blinding)),\n    outputMemo1:     fieldStr(inputs.outputs[0].memo ? bytesToField(inputs.outputs[0].memo) : 0n),\n\n    outputValue2:    fieldStr(inputs.outputs[1].value),\n    outputOwnerPk2:  fieldStr(bytesToField(inputs.outputs[1].ownerPk)),\n    outputBlinding2: fieldStr(bytesToField(inputs.outputs[1].blinding)),\n    outputMemo2:     fieldStr(inputs.outputs[1].memo ? bytesToField(inputs.outputs[1].memo) : 0n),\n\n    publicValueIn:  fieldStr(inputs.publicValueIn),\n    publicValueOut: fieldStr(inputs.publicValueOut),\n    publicAddress:  fieldStr(inputs.publicAddress),\n    fee:            fieldStr(inputs.fee),\n  }\n}\n","// Private Pool V2 — concrete TypeScript types.\n//\n// Single source of truth for shapes flowing between:\n//   - Browser key derivation (lib/private-pool-keys.ts)\n//   - Note encryption (lib/private-pool-notes.ts)\n//   - Witness builder (lib/private-pool-witness.ts)\n//   - Browser prover (lib/private-pool-prover.ts)\n//   - On-chain submission (api/private-pool/v2/* routes)\n//   - Node-side circuit smoke tests (contracts/scripts/private-pool-v2/)\n//\n// Reference: docs/PRIVATE_POOL_V2_DESIGN.md §17 (Appendix), ADR-001\n// (commitment), ADR-002 (key derivation), ADR-003 (encryption).\n\n// ─────────────────────────────────────────────────────────────────────\n// Constants — kept in code (not env) because changing them changes\n// the circuit + on-chain program; not a runtime tunable.\n// ─────────────────────────────────────────────────────────────────────\n\n/** Pool merkle tree depth — 4B notes capacity. */\nexport const POOL_DEPTH = 32\n\n/** ASP merkle tree depth — same as pool for symmetric witness costs. */\nexport const ASP_DEPTH = 32\n\n/** USDC base unit (6 decimals → micro-USDC). */\nexport const USDC_DECIMALS = 6\n\n// ─────────────────────────────────────────────────────────────────────\n// Payment address — share-friendly identifier the recipient hands to\n// senders. Encodes both spending and viewing public keys (ADR-002).\n// ─────────────────────────────────────────────────────────────────────\nexport interface PaymentAddress {\n  /** 32-byte spending public key (Poseidon1(spending_sk)) — used in commitments. */\n  spendingPk: Uint8Array\n  /** 32-byte X25519 viewing public key — used to encrypt notes for the recipient. */\n  viewingPk: Uint8Array\n}\n\n/** Base58Check encoding of `spendingPk || viewingPk`. ~88-char string. */\nexport type PaymentAddressString = string\n\n// ─────────────────────────────────────────────────────────────────────\n// Note material — the contents of a single private balance entry.\n//\n// `commitment` is computed from (value, ownerPk, blinding, memo) per\n// ADR-001. `spent` and `leafIndex` are off-chain tracking fields the\n// browser maintains; on-chain data only includes the commitment.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface DecryptedNote {\n  /** Micro-USDC value stored in the note. */\n  value: bigint\n  /** 32-byte recipient pubkey (== owner spending_pk). */\n  ownerPk: Uint8Array\n  /** 32-byte random blinding factor. */\n  blinding: Uint8Array\n  /** Optional 32-byte memo, or null if no memo. */\n  memo: Uint8Array | null\n}\n\nexport interface OwnedNote extends DecryptedNote {\n  /** Position in the pool's commitment tree, set when the note was added. */\n  leafIndex: number\n  /** Slot at which the note was added on chain. */\n  receivedAtSlot: number\n  /** True iff we've observed a tx that spent this note's nullifier. */\n  spent: boolean\n  /** Cached commitment (= Poseidon4 of the note fields). */\n  commitment: Uint8Array\n  /** Cached nullifier (= Poseidon2(commitment, nullifier_sk)). */\n  nullifier: Uint8Array\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Encrypted note blob — the on-chain (or indexer-served) wire format.\n// Per ADR-003: ChaCha20-Poly1305 + X25519 ephemeral.\n// ─────────────────────────────────────────────────────────────────────\nexport interface EncryptedNoteBlob {\n  /** 32-byte commitment of the note this blob describes. */\n  commitment: Uint8Array\n  /** 32-byte X25519 ephemeral public key the sender used. */\n  ephemeralPk: Uint8Array\n  /** ChaCha20-Poly1305 ciphertext + 16-byte tag (96 bytes for memo-less, 128 with memo). */\n  ciphertext: Uint8Array\n  /** Slot at which the blob was added on chain. */\n  slot: number\n  /** Position in the commitment tree (matches OwnedNote.leafIndex). */\n  leafIndex: number\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Merkle inclusion witness — pool or ASP path proving membership.\n// ─────────────────────────────────────────────────────────────────────\nexport interface MerklePath {\n  /** 32-byte hash siblings, leaf-to-root order (length == depth). */\n  pathElements: Uint8Array[]\n  /** Bit per level: 0 = leaf is left child, 1 = leaf is right child (length == depth). */\n  pathIndices: number[]\n  /** 32-byte root that this path verifies under. */\n  root: Uint8Array\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Circuit witnesses — what we feed to snarkjs.fullProve().\n//\n// Field elements are passed as decimal strings (snarkjs's preferred\n// shape). Arrays match circuit signal array sizes (32-deep paths).\n// ─────────────────────────────────────────────────────────────────────\n\n/** Witness for `Deposit.circom`. */\nexport interface DepositWitness {\n  // private\n  ownerPk: string\n  blinding: string\n  memo: string\n\n  // public\n  publicValue: string\n  depositorAddrHash: string\n  fee: string\n}\n\n/** Witness for `Withdraw.circom`. */\nexport interface WithdrawWitness {\n  // private\n  value: string\n  ownerPk: string\n  blinding: string\n  memo: string\n  spendingSk: string\n  nullifierSk: string\n\n  poolPathElements: string[]   // length POOL_DEPTH\n  poolPathIndices: number[]    // length POOL_DEPTH\n  aspPathElements: string[]    // length ASP_DEPTH\n  aspPathIndices: number[]     // length ASP_DEPTH\n\n  // public\n  publicValue: string\n  publicAddress: string\n  fee: string\n}\n\n/** Witness for `CashoutProof.circom` (ADR-012 V3). */\nexport interface CashoutProofWitness {\n  // private\n  value: string\n  ownerPk: string\n  blinding: string\n  memo: string\n  spendingSk: string\n  nullifierSk: string\n\n  // Credit-pool merkle inclusion (depth 24).\n  creditPoolPathElements: string[]\n  creditPoolPathIndices: number[]\n\n  // public\n  publicValue: string\n  publicAddress: string\n  fee: string\n}\n\n/** Witness for `JoinSplit2x2.circom`. */\nexport interface JoinSplitWitness {\n  // ── input note 1 ──\n  inputValue1: string\n  inputOwnerPk1: string\n  inputBlinding1: string\n  inputMemo1: string\n  inputPoolPath1: string[]\n  inputPoolIndices1: number[]\n  inputAspPath1: string[]\n  inputAspIndices1: number[]\n\n  // ── input note 2 ──\n  inputValue2: string\n  inputOwnerPk2: string\n  inputBlinding2: string\n  inputMemo2: string\n  inputPoolPath2: string[]\n  inputPoolIndices2: number[]\n  inputAspPath2: string[]\n  inputAspIndices2: number[]\n\n  // ── spending authority ──\n  spendingSk: string\n  nullifierSk: string\n\n  // ── output note 1 ──\n  outputValue1: string\n  outputOwnerPk1: string\n  outputBlinding1: string\n  outputMemo1: string\n\n  // ── output note 2 ──\n  outputValue2: string\n  outputOwnerPk2: string\n  outputBlinding2: string\n  outputMemo2: string\n\n  // ── public inputs ──\n  publicValueIn: string\n  publicValueOut: string\n  publicAddress: string\n  fee: string\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Public signals — what the verifier consumes after fullProve().\n//\n// Order matches circom's \"outputs in source order, then public inputs\n// alphabetical\" — see circuit headers for canonical layout. The\n// on-chain program reads these in the same order.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface DepositPublicSignals {\n  /** outputCommitment */\n  outputCommitment: string\n  /** depositorAddrHash */\n  depositorAddrHash: string\n  /** fee */\n  fee: string\n  /** publicValue */\n  publicValue: string\n}\n\nexport interface WithdrawPublicSignals {\n  /** nullifier */\n  nullifier: string\n  /** root */\n  root: string\n  /** aspRoot */\n  aspRoot: string\n  /** fee */\n  fee: string\n  /** publicAddress */\n  publicAddress: string\n  /** publicValue */\n  publicValue: string\n}\n\nexport interface JoinSplitPublicSignals {\n  /** inputNullifier1 */\n  inputNullifier1: string\n  /** inputNullifier2 */\n  inputNullifier2: string\n  /** outputCommitment1 */\n  outputCommitment1: string\n  /** outputCommitment2 */\n  outputCommitment2: string\n  /** root */\n  root: string\n  /** aspRoot */\n  aspRoot: string\n  /** fee */\n  fee: string\n  /** publicAddress */\n  publicAddress: string\n  /** publicValueIn */\n  publicValueIn: string\n  /** publicValueOut */\n  publicValueOut: string\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Proof + tx envelopes — what the client sends to the server route.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface Groth16Proof {\n  pi_a: string[]\n  pi_b: string[][]\n  pi_c: string[]\n  protocol: 'groth16'\n  curve: 'bn128'\n}\n\nexport interface DepositTx {\n  proof: Groth16Proof\n  publicSignals: DepositPublicSignals\n  encryptedNote: EncryptedNoteBlob\n}\n\nexport interface WithdrawTx {\n  proof: Groth16Proof\n  publicSignals: WithdrawPublicSignals\n}\n\nexport interface JoinSplitTx {\n  proof: Groth16Proof\n  publicSignals: JoinSplitPublicSignals\n  /** Length 2; one blob per output commitment. */\n  encryptedNotes: [EncryptedNoteBlob, EncryptedNoteBlob]\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Pool config — fetched at /api/private-pool/v2/config.\n// ─────────────────────────────────────────────────────────────────────\n\nexport interface PoolConfig {\n  /** Solana program ID hosting the pool (or EVM contract address). */\n  programId: string\n  /** USDC mint / token contract for this pool. */\n  usdcMint: string\n  /** Relayer pubkey (sponsored fee payer). */\n  relayerAddress: string\n  /** Network identifier — `solana-devnet`, `solana`, etc. */\n  network: string\n  /** Current pool merkle root (hex). */\n  currentRoot: string\n  /** Most recent ASP root (hex). */\n  currentAspRoot: string\n  /** Snapshot age — seconds since the ASP root was published. */\n  aspAgeSeconds: number\n  /** 32-byte commitment of `DUMMY_A` — used to pad single-input JoinSplits. */\n  dummyACommitment: string\n  /** 32-byte commitment of `DUMMY_B` — used in 0-input edge cases. */\n  dummyBCommitment: string\n}\n"],"mappings":"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","names":[]}