{"version":3,"file":"buildAndBroadcastRefund-CPPGvzXb.cjs","sources":["../src/tbv/core/services/activation/activateVault.ts","../src/tbv/core/services/deposit/peginRegistrationDepth.ts","../src/tbv/core/services/deposit/signDepositorGraph.ts","../src/tbv/core/services/deposit/waitForPeginStatus.ts","../src/tbv/core/services/deposit/runDepositorPresignFlow.ts","../src/tbv/core/services/deposit/submitWotsPublicKey.ts","../src/tbv/core/services/participants/indexerKeyHint.ts","../src/tbv/core/services/participants/resolveParticipantKeys.ts","../src/tbv/core/services/deposit/validateOnChainParticipantKeys.ts","../src/tbv/core/services/deposit/validation.ts","../src/tbv/core/services/deposit/verifyRegisteredParticipantKeys.ts","../src/tbv/core/services/deposit/verifyRegisteredVaultVersions.ts","../src/tbv/core/services/pegout/state.ts","../src/tbv/core/services/refund/errors.ts","../src/tbv/core/services/refund/buildAndBroadcastRefund.ts"],"sourcesContent":["/**\n * Vault activation — reveal HTLC secret on Ethereum to move the vault from\n * Verified to Active. The on-chain contract validates `sha256(s) == hashlock`\n * and the activation deadline; this function pre-validates inputs (including\n * an optional hashlock check) and delegates the actual contract write to an\n * injected callback so the SDK stays transport-agnostic.\n *\n * @module services/activation\n */\n\nimport type { Abi, Address, Hash, Hex } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { ensureHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport { validateSecretAgainstHashlock } from \"../htlc\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\nconst ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;\n// ETH calldata convention: 0x prefix REQUIRED, even number of hex chars, may\n// be empty (\"0x\"). Named distinctly from the BTC-hex regex in\n// buildAndBroadcastRefund.ts (which allows an optional prefix and requires\n// non-empty) to make the convention explicit at the call site.\nconst ETH_HEX_BYTES_RE = /^0x([0-9a-fA-F]{2})*$/;\n\nfunction assertBytes32(value: string, label: string): void {\n  if (value.length !== 66) {\n    throw new Error(\n      `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n    );\n  }\n  if (!BYTES32_HEX_RE.test(value)) {\n    throw new Error(\n      `${label} must contain only hex characters after the 0x prefix`,\n    );\n  }\n}\n\nfunction assertAddress(value: string, label: string): void {\n  if (!ADDRESS_HEX_RE.test(value)) {\n    throw new Error(\n      `${label} must be a 20-byte 0x-prefixed hex address (42 chars)`,\n    );\n  }\n}\n\nfunction assertHexBytes(value: string, label: string): void {\n  if (!ETH_HEX_BYTES_RE.test(value)) {\n    throw new Error(\n      `${label} must be a 0x-prefixed hex string with an even number of hex chars`,\n    );\n  }\n}\n\n/**\n * A single ETH contract-write call. The SDK assembles these; the caller\n * executes them via viem, wagmi, a wallet provider, or any other transport.\n */\nexport interface EthContractWriteCall {\n  address: Address;\n  abi: Abi;\n  functionName: string;\n  args: readonly unknown[];\n}\n\n/**\n * Minimum shape the SDK requires from any contract-write result. Callers may\n * return richer objects (e.g. including the receipt) — the SDK propagates\n * them unchanged via the generic parameter on {@link EthContractWriter}.\n */\nexport interface EthContractWriteResult {\n  transactionHash: Hash;\n}\n\n/**\n * Caller-provided contract writer. The generic `R` lets callers return any\n * transport-specific result shape (e.g. `{ transactionHash, receipt }`);\n * the SDK forwards that shape back through `activateVault`.\n */\nexport type EthContractWriter<R extends EthContractWriteResult = EthContractWriteResult> = (\n  call: EthContractWriteCall,\n) => Promise<R>;\n\nexport interface ActivateVaultInput<\n  R extends EthContractWriteResult = EthContractWriteResult,\n> {\n  /** BTCVaultRegistry contract address (env-specific). */\n  btcVaultRegistryAddress: Address;\n  /** Vault ID (bytes32, 0x-prefixed). */\n  vaultId: Hex;\n  /**\n   * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n   * `0X` prefix is normalised before validation.\n   */\n  secret: string;\n  /**\n   * Optional hashlock for client-side pre-validation. When provided, the SDK\n   * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n   */\n  hashlock?: Hex;\n  /**\n   * Activation metadata passed through to the contract. Required to keep\n   * the \"empty metadata\" convention explicit at the call site — pass `\"0x\"`\n   * (empty bytes) when no metadata is needed. Must be a 0x-prefixed hex\n   * string with an even number of hex chars.\n   */\n  activationMetadata: Hex;\n  /** Caller-provided write callback — see {@link EthContractWriter}. */\n  writeContract: EthContractWriter<R>;\n  /**\n   * Optional abort signal. Checked before validation runs; since validation\n   * is fully synchronous, cancellation between validation and the write is\n   * not observable and callers should rely on the transport's own\n   * cancellation support for that window.\n   */\n  signal?: AbortSignal;\n}\n\n/**\n * Shared pre-write validation for both activation entry points: address and\n * bytes32 shapes, plus the optional `sha256(secret) == hashlock` pre-check —\n * the last gate before the secret would enter calldata.\n *\n * @returns the 0x-normalised secret to place in calldata\n */\nfunction validateActivationInputs(input: {\n  btcVaultRegistryAddress: Address;\n  vaultId: Hex;\n  secret: string;\n  hashlock?: Hex;\n}): Hex {\n  assertAddress(input.btcVaultRegistryAddress, \"btcVaultRegistryAddress\");\n  assertBytes32(input.vaultId, \"vaultId\");\n\n  const normalizedSecret = ensureHexPrefix(input.secret);\n  assertBytes32(normalizedSecret, \"secret\");\n\n  if (input.hashlock !== undefined) {\n    assertBytes32(input.hashlock, \"hashlock\");\n    if (!validateSecretAgainstHashlock(normalizedSecret, input.hashlock)) {\n      throw new Error(\n        \"Invalid secret: SHA256(secret) does not match the provided hashlock\",\n      );\n    }\n  }\n\n  return normalizedSecret;\n}\n\n/**\n * Reveal the HTLC secret on Ethereum and activate the vault.\n *\n * Validates inputs, optionally pre-checks the secret against the expected\n * hashlock, and delegates the contract write to `writeContract`. Returns\n * whatever the writer returns so callers can keep richer transport-specific\n * metadata (e.g. viem receipts) end-to-end.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n *         or if `sha256(secret) != hashlock`\n * @throws `Error` if `activationMetadata` is not a 0x-prefixed hex byte\n *         string (must have an even number of hex chars). Pass `\"0x\"` for\n *         empty metadata.\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVault<\n  R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultInput<R>): Promise<R> {\n  const {\n    btcVaultRegistryAddress,\n    vaultId,\n    hashlock,\n    activationMetadata,\n    writeContract,\n    signal,\n  } = input;\n\n  signal?.throwIfAborted();\n\n  const normalizedSecret = validateActivationInputs({\n    btcVaultRegistryAddress,\n    vaultId,\n    secret: input.secret,\n    hashlock,\n  });\n\n  assertHexBytes(activationMetadata, \"activationMetadata\");\n\n  return writeContract({\n    address: btcVaultRegistryAddress,\n    abi: BTCVaultRegistryABI,\n    functionName: \"activateVaultWithSecret\",\n    args: [vaultId, normalizedSecret, activationMetadata],\n  });\n}\n\nexport interface ActivateVaultAndRedeemInput<\n  R extends EthContractWriteResult = EthContractWriteResult,\n> {\n  /** BTCVaultRegistry contract address (env-specific). */\n  btcVaultRegistryAddress: Address;\n  /** Vault ID (bytes32, 0x-prefixed). */\n  vaultId: Hex;\n  /**\n   * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n   * `0X` prefix is normalised before validation.\n   */\n  secret: string;\n  /**\n   * Optional hashlock for client-side pre-validation. When provided, the SDK\n   * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n   */\n  hashlock?: Hex;\n  /** Caller-provided write callback — see {@link EthContractWriter}. */\n  writeContract: EthContractWriter<R>;\n  /**\n   * Optional abort signal. Checked before validation runs; since validation\n   * is fully synchronous, cancellation between validation and the write is\n   * not observable and callers should rely on the transport's own\n   * cancellation support for that window.\n   */\n  signal?: AbortSignal;\n}\n\n/**\n * Depositor escape hatch: reveal the HTLC secret and immediately redeem the\n * vault for the depositor, without any application activation. The contract\n * (`activateVaultWithSecretAndRedeem`) runs the same activation preconditions\n * (Verified status, activation deadline, `sha256(s) == hashlock`) and then\n * marks the vault Redeemed so the vault provider pays the BTC out to the\n * depositor's committed payout address. Used when the normal activation is\n * unavailable (e.g. the application adapter is paused or its activation\n * reverts) but the secret must still be revealed to recover the swept peg-in.\n *\n * Takes no activation metadata — the application entry point is never called.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n *         or if `sha256(secret) != hashlock`\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVaultAndRedeem<\n  R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultAndRedeemInput<R>): Promise<R> {\n  const { btcVaultRegistryAddress, vaultId, hashlock, writeContract, signal } =\n    input;\n\n  signal?.throwIfAborted();\n\n  const normalizedSecret = validateActivationInputs({\n    btcVaultRegistryAddress,\n    vaultId,\n    secret: input.secret,\n    hashlock,\n  });\n\n  return writeContract({\n    address: btcVaultRegistryAddress,\n    abi: BTCVaultRegistryABI,\n    functionName: \"activateVaultWithSecretAndRedeem\",\n    args: [vaultId, normalizedSecret],\n  });\n}\n","/**\n * Ethereum confirmation-depth gate for a peg-in registration.\n *\n * Between `submitPeginRequestBatch` landing on Ethereum and the Pre-PegIn\n * BTC transaction being broadcast there is a reorg window. If the block\n * carrying the registration is orphaned *after* the BTC broadcast, the vault\n * record is gone from the chain while the depositor's BTC is already locked\n * in the HTLC — the deposit is stuck, recoverable only via the HTLC refund\n * leaf after `T_refund` (~3 days). This module closes that window by proving\n * the registration is buried before any BTC is committed.\n *\n * Depth is measured from `BTCVaultBasicInfo.createdAt`, which is the Ethereum\n * **block number** the registration was mined at (see\n * {@link isActivationDeadlinePassedOnChain}, which mirrors the contract's own\n * `block.number > createdAt + pegInActivationTimeout` check). Reading it needs\n * no transaction hash, so the same primitive serves the inline deposit flow\n * and the cross-device resume flow, and every poll re-reads live contract\n * state — an orphaned registration reads back as `depositor === zeroAddress`.\n *\n * Deliberately NOT built on viem's `waitForTransactionReceipt({confirmations})`.\n * That helper fetches the receipt once and thereafter only does block-number\n * arithmetic against the cached copy; it never re-checks that the receipt's\n * block is still canonical, so it resolves happily for a transaction that has\n * been reorged out. It buys a delay, not a finality guarantee.\n *\n * @module services/deposit\n */\n\nimport { type Hex, zeroAddress } from \"viem\";\n\nimport type { VaultBasicInfo, VaultRegistryReader } from \"../../clients/eth/types\";\n\n/**\n * Ethereum block confirmations required before the Pre-PegIn BTC transaction\n * may be broadcast.\n *\n * 8 exceeds the deepest reorg ever observed on Ethereum (7, pre-merge, caused\n * by a client bug), and costs ~1.6 min at 12s slots. Deliberately not the\n * `safe` block tag: a full epoch (~12.8 min) was rejected as too slow for the\n * benefit. This is a liveness guard against an orphaned registration, not a\n * theft mitigation — every Pre-PegIn HTLC spend path requires the depositor's\n * own BTC key regardless.\n */\nexport const PEGIN_ETH_CONFIRMATIONS = 8;\n\n/** Poll cadence — half an Ethereum slot, so a new block is never missed by long. */\nconst REGISTRATION_DEPTH_POLL_INTERVAL_MS = 6_000;\n\n/**\n * Overall budget for reaching depth. Nominally ~96s (8 × 12s); the rest\n * absorbs missed slots and RPC lag. Generous on purpose: timing out discards\n * a valid registration that only needed a few more seconds, and the failure is\n * recoverable through the resume flow, so over-waiting is cheaper than\n * under-waiting.\n */\nconst REGISTRATION_DEPTH_TIMEOUT_MS = 10 * 60_000;\n\n/**\n * Consecutive absent reads tolerated before concluding the registration is\n * genuinely not on-chain. At the 6s cadence this is ~60s — five Ethereum\n * blocks — which is what absorbs a load-balanced pool member still serving\n * pre-block state moments after the receipt. Such a node answers HTTP 200\n * with a validly encoded zero struct, so no transport-level retry can see it;\n * only re-reading later can. Expressed in polls rather than milliseconds so\n * the tolerance scales with the cadence.\n */\nconst REGISTRATION_ABSENT_GRACE_POLLS = 10;\n\n/**\n * The vault is not registered on-chain, and stayed that way past the grace\n * window — long enough that a lagging backend has been ruled out. Retrying\n * will not help.\n */\nexport class PeginRegistrationMissingError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"PeginRegistrationMissingError\";\n  }\n}\n\n/** The registration did not reach the required depth within the budget. */\nexport class PeginRegistrationNotFinalError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"PeginRegistrationNotFinalError\";\n  }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field, as the other deposit-service\n// errors in this directory do.\nexport function isPeginRegistrationMissingError(\n  err: unknown,\n): err is PeginRegistrationMissingError {\n  return (\n    err instanceof PeginRegistrationMissingError ||\n    (err instanceof Error && err.name === \"PeginRegistrationMissingError\")\n  );\n}\n\nexport function isPeginRegistrationNotFinalError(\n  err: unknown,\n): err is PeginRegistrationNotFinalError {\n  return (\n    err instanceof PeginRegistrationNotFinalError ||\n    (err instanceof Error && err.name === \"PeginRegistrationNotFinalError\")\n  );\n}\n\nexport interface RegistrationDepthParams {\n  /** Current chain tip block number. */\n  currentBlock: bigint;\n  /** Block number the registration was mined at (`VaultBasicInfo.createdAt`). */\n  createdAtBlock: bigint;\n}\n\n/**\n * Confirmations accrued by a registration: the mining block counts as the\n * first confirmation, so `tip === createdAt` is 1.\n *\n * Clamped at 0. A reorg between the tip read and the vault read can surface a\n * `createdAt` above the tip already in hand; a negative depth must never\n * propagate into a comparison.\n */\nexport function computeRegistrationConfirmations(\n  params: RegistrationDepthParams,\n): number {\n  const { currentBlock, createdAtBlock } = params;\n  const depth = currentBlock - createdAtBlock + 1n;\n  return depth < 0n ? 0 : Number(depth);\n}\n\nexport interface RegistrationDepthProgress {\n  /** Shallowest depth across every vault being waited on. */\n  confirmations: number;\n  required: number;\n}\n\nexport interface WaitForPeginRegistrationDepthParams {\n  vaultRegistryReader: VaultRegistryReader;\n  /**\n   * Chain-tip reader. A thunk rather than a `PublicClient` so this module has\n   * no viem-client dependency and stays testable with two plain fakes — the\n   * same shape `verifyRegisteredVaultVersions` uses for its reader.\n   */\n  getBlockNumber: () => Promise<bigint>;\n  /** Vaults registered by the same transaction; the shallowest one gates. */\n  vaultIds: readonly Hex[];\n  required?: number;\n  pollIntervalMs?: number;\n  timeoutMs?: number;\n  signal?: AbortSignal;\n  onProgress?: (progress: RegistrationDepthProgress) => void;\n}\n\nexport interface PeginRegistrationDepthResult {\n  confirmations: number;\n  /**\n   * The final observation for the shallowest vault. Callers that gated on\n   * `status` before the wait should re-assert it against this — the wait can\n   * span minutes, and a vault can leave PENDING in that time.\n   */\n  basicInfo: VaultBasicInfo;\n}\n\n/**\n * Poll until every vault's registration is at least `required` blocks deep.\n *\n * A read that comes back empty is treated as \"not visible yet\", not as\n * \"absent\": both callers reach this having already proven the registration\n * exists, so an empty read means a lagging RPC backend or a reorg, and both\n * resolve on their own.\n *\n * @throws {PeginRegistrationMissingError} if no vault has ever been observed and the grace window is spent.\n * @throws {PeginRegistrationNotFinalError} on timeout.\n * @throws if aborted.\n */\nexport async function waitForPeginRegistrationDepth(\n  params: WaitForPeginRegistrationDepthParams,\n): Promise<PeginRegistrationDepthResult> {\n  const {\n    vaultRegistryReader,\n    getBlockNumber,\n    vaultIds,\n    required = PEGIN_ETH_CONFIRMATIONS,\n    pollIntervalMs = REGISTRATION_DEPTH_POLL_INTERVAL_MS,\n    timeoutMs = REGISTRATION_DEPTH_TIMEOUT_MS,\n    signal,\n    onProgress,\n  } = params;\n\n  if (vaultIds.length === 0) {\n    throw new Error(\n      \"waitForPeginRegistrationDepth requires at least one vault ID\",\n    );\n  }\n\n  const startTime = Date.now();\n  // Once any poll has seen the registration, a later disappearance is a reorg,\n  // and re-inclusion is the expected outcome — that case polls to the full\n  // budget. Before the first sighting, absence is far more likely to be a\n  // lagging backend than a missing registration, so it gets a grace window.\n  let hasObservedRegistration = false;\n  let absentPolls = 0;\n  let lastError: unknown;\n\n  while (true) {\n    if (signal?.aborted) {\n      throw new Error(\n        `Aborted while waiting for peg-in registration depth (${vaultIds.length} vault(s))`,\n      );\n    }\n\n    if (Date.now() - startTime >= timeoutMs) {\n      // Deliberately avoids the word \"broadcast\". Callers classify this by\n      // type, but any surface that stringifies it would otherwise land in the\n      // message-matching \"broadcast failed\" bucket and tell the user the exact\n      // opposite of what happened.\n      throw new PeginRegistrationNotFinalError(\n        `Peg-in registration did not reach ${required} Ethereum confirmations within ${timeoutMs}ms. ` +\n          `The registration transaction was submitted but is not yet final, so the Pre-PegIn ` +\n          `Bitcoin transaction was never sent and no funds are at risk. ` +\n          `Resume this deposit from the dashboard once the network settles.` +\n          (lastError instanceof Error ? ` Last read error: ${lastError.message}` : \"\"),\n      );\n    }\n\n    try {\n      // Tip FIRST, vault state SECOND. Both read `latest`, but they are two\n      // round-trips: reading the tip first means it can only be at or behind\n      // the state the vault read observes, so the computed depth is an\n      // under-estimate. Reversing the order could over-count by a block and\n      // release the broadcast at 7 confirmations.\n      //\n      // That ordering only guarantees the under-count when both land on the\n      // same backend. Against a load-balanced pool the tip can come from an\n      // ahead node and the vault from a behind one — the same root cause as\n      // the empty-read handling below, and why this is a best-effort skew\n      // guard rather than a proof.\n      const currentBlock = await getBlockNumber();\n      const infos = await Promise.all(\n        vaultIds.map((vaultId) => vaultRegistryReader.getVaultBasicInfo(vaultId)),\n      );\n\n      // A zero record is what an orphaned (or never-included) registration\n      // reads back as. `createdAt === 0n` is checked alongside the zero\n      // depositor because a partially-decoded record must never be treated as\n      // block 0 — that would compute as infinitely deep and pass the gate.\n      const missingIndex = infos.findIndex(\n        (info) => info.depositor === zeroAddress || info.createdAt === 0n,\n      );\n\n      if (missingIndex !== -1) {\n        absentPolls += 1;\n\n        // Never seen it, and the grace window is spent: a lagging backend has\n        // been ruled out, so the registration really is not there. Only ever\n        // reachable for a caller that supplied a vault ID which was never\n        // registered — both in-repo callers arrive here having already proven\n        // the vault visible (a receipt inline, a throwing `getVaultFromChain`\n        // on resume).\n        if (\n          !hasObservedRegistration &&\n          absentPolls > REGISTRATION_ABSENT_GRACE_POLLS\n        ) {\n          throw new PeginRegistrationMissingError(\n            `Vault ${vaultIds[missingIndex]} is still not visible on-chain after ` +\n              `${absentPolls} reads — the registration does not exist.`,\n          );\n        }\n\n        // Otherwise keep polling. Before the first sighting this is almost\n        // always an RPC backend that has not caught up to the registration\n        // block; after one, it is the reorg this gate exists for, where\n        // re-inclusion within a block or two is the expected outcome. Both\n        // resolve themselves, and both would be made worse by aborting a\n        // deposit that is about to be fine.\n        console.warn(\n          `Peg-in registration for vault ${vaultIds[missingIndex]} is not visible in chain state ` +\n            `at block ${currentBlock} — a lagging RPC backend or an Ethereum reorg. Still polling.`,\n        );\n        onProgress?.({ confirmations: 0, required });\n      } else {\n        hasObservedRegistration = true;\n        absentPolls = 0;\n        lastError = undefined;\n\n        // The shallowest vault gates the batch: they share one registration\n        // transaction, so in practice the depths are equal, but taking the\n        // minimum is the fail-safe reading if they ever diverge.\n        let shallowest = infos[0];\n        let shallowestDepth = computeRegistrationConfirmations({\n          currentBlock,\n          createdAtBlock: infos[0].createdAt,\n        });\n        for (const info of infos.slice(1)) {\n          const depth = computeRegistrationConfirmations({\n            currentBlock,\n            createdAtBlock: info.createdAt,\n          });\n          if (depth < shallowestDepth) {\n            shallowestDepth = depth;\n            shallowest = info;\n          }\n        }\n\n        onProgress?.({ confirmations: shallowestDepth, required });\n\n        if (shallowestDepth >= required) {\n          return { confirmations: shallowestDepth, basicInfo: shallowest };\n        }\n      }\n    } catch (error) {\n      if (isPeginRegistrationMissingError(error)) {\n        throw error;\n      }\n      // Transient RPC failure. Retry on the next tick; the outer timeout owns\n      // the overall budget, so one blip must not consume it.\n      console.warn(\n        `Registration-depth read failed (retrying in ${pollIntervalMs}ms): ` +\n          (error instanceof Error ? error.message : String(error)),\n      );\n      lastError = error;\n    }\n\n    await new Promise<void>((resolve, reject) => {\n      const onAbort = () => {\n        clearTimeout(timeoutId);\n        reject(\n          new Error(\n            `Aborted while waiting for peg-in registration depth (${vaultIds.length} vault(s))`,\n          ),\n        );\n      };\n      const timeoutId = setTimeout(() => {\n        signal?.removeEventListener(\"abort\", onAbort);\n        resolve();\n      }, pollIntervalMs);\n      signal?.addEventListener(\"abort\", onAbort, { once: true });\n    });\n  }\n}\n","/**\n * Depositor Graph Signing Service\n *\n * Signs the depositor's own graph transactions (Payout, NoPayout per challenger)\n * for the depositor-as-claimer flow.\n *\n * Both PSBTs are constructed locally from authoritative on-chain connector\n * parameters and the VP-advertised transaction hexes (which are themselves\n * cross-checked against on-chain or protocol-defined sinks). Building PSBTs\n * locally is essential: every field that enters the Taproot sighash\n * (witnessUtxo, tapLeafScript, controlBlock, tapInternalKey) must come from\n * trusted sources, otherwise a malicious VP could substitute metadata that\n * makes the depositor's signature valid for a different spend.\n *\n * Transaction counts: 1 Payout + N NoPayout = 1 + N total PSBTs.\n *\n * @see btc-vault docs/pegin.md - \"Automatic Graph Creation & Presigning\"\n * @see btc-vault crates/vault/src/transactions/nopayout.rs - NoPayout structure\n */\n\nimport { type Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport type {\n  BitcoinWallet,\n  SignPsbtOptions,\n} from \"../../../../shared/wallets/interfaces\";\nimport type {\n  DepositorAsClaimerPresignatures,\n  DepositorGraphTransactions,\n  DepositorPreSigsPerChallenger,\n  PresignDataPerChallenger,\n} from \"../../clients/vault-provider/types\";\nimport { signPsbtsWithFallback } from \"../../managers/pegin/signPsbtsWithFallback\";\nimport { deriveLocalChallengers } from \"../../primitives/challengers\";\nimport {\n  assertPsbtUnsignedTxMatches,\n  type AssertPsbtUnsignedTxMatchesParams,\n} from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport {\n  assertNoPayoutOutputMatchesChallenger,\n  buildNoPayoutPsbt,\n} from \"../../primitives/psbt/noPayout\";\nimport {\n  buildPayoutPsbt,\n  extractPayoutSignature,\n} from \"../../primitives/psbt/payout\";\nimport { assertScriptPathSchnorrSignature } from \"../../primitives/psbt/verifyScriptPathSchnorrSignature\";\nimport {\n  stripHexPrefix,\n  uint8ArrayToHex,\n  validateWalletPubkey,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\n/**\n * The depositor signs exactly one input (index 0) per payout/nopayout PSBT.\n * Used to construct SignPsbtOptions for wallet.signPsbt(). PSBTs may carry\n * additional inputs (the payout PSBT includes the assert prevout; the nopayout\n * PSBT includes the two ChallengeAssert prevouts) so the Taproot SIGHASH_DEFAULT\n * sighash commits to all prevouts, but those inputs are not signed by the\n * depositor.\n */\nconst DEPOSITOR_SIGNED_INPUT_COUNT = 1;\n\n/**\n * commissionBps placeholder for the depositor-as-claimer path — `buildPayoutPsbt`\n * only consults it under the VP-claimer role, so any in-range value is inert.\n */\nconst DEPOSITOR_PATH_UNUSED_COMMISSION_BPS = 1;\n\n/** Tracks which indices in the flat PSBT array belong to which challenger */\ninterface ChallengerEntry {\n  challengerPubkey: string;\n  noPayoutIdx: number;\n}\n\n/** Result of the collect phase - flat PSBT array with index mapping */\ninterface CollectedDepositorGraphPsbts {\n  psbtHexes: string[];\n  signOptions: SignPsbtOptions[];\n  challengerEntries: ChallengerEntry[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Reject VP-supplied `challenger_presign_data` whose pubkey set does not\n * exactly equal `localChallengers ∪ universalChallengers`.\n *\n * The daemon's `challenger_presign_data` contains one entry per challenger\n * in `Challengers::all_sorted() = local + universal` (per\n * btc-vault `crates/vault/src/tx_graph/graph.rs:438-458`). For the\n * depositor-as-claimer flow this is `VKs + UCs`.\n *\n * Threat model: a malicious or buggy VP could omit, duplicate, or inject\n * unrelated entries. Missing entries → depositor activates with incomplete\n * recovery material (omitted challenger later becomes unenforceable).\n * Duplicates or extras → wallet signs PSBTs for challengers the protocol\n * doesn't recognize, handing the VP signatures it shouldn't have.\n */\nfunction assertChallengerSetMatchesExpected(\n  challengerPresignData: PresignDataPerChallenger[],\n  localChallengers: string[],\n  universalChallengerBtcPubkeys: string[],\n): void {\n  const universal = universalChallengerBtcPubkeys.map((k) =>\n    stripHexPrefix(k).toLowerCase(),\n  );\n  // Protocol guarantee: local and universal sets are disjoint. Reject\n  // overlap so the depositor doesn't sign for an ambiguous challenger role.\n  const overlap = localChallengers.filter((k) => universal.includes(k));\n  if (overlap.length > 0) {\n    throw new Error(\n      `Cannot validate challenger set: vault keepers and universal challengers overlap (${overlap.join(\", \")})`,\n    );\n  }\n  const expected = [...localChallengers, ...universal];\n\n  const suppliedList = challengerPresignData.map((c) =>\n    stripHexPrefix(c.challenger_pubkey).toLowerCase(),\n  );\n  const suppliedSet = new Set(suppliedList);\n  if (suppliedSet.size !== suppliedList.length) {\n    throw new Error(\n      \"Depositor graph contains duplicate challenger entries in challenger_presign_data\",\n    );\n  }\n  const expectedSet = new Set(expected);\n  const missing = expected.filter((c) => !suppliedSet.has(c));\n  const extra = suppliedList.filter((c) => !expectedSet.has(c));\n  if (missing.length > 0 || extra.length > 0) {\n    throw new Error(\n      `Depositor graph challenger set does not match expected (local ∪ universal)` +\n        (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n        (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n    );\n  }\n}\n\n/**\n * Read the txid that the given input references in the unsigned tx, in display\n * (big-endian) hex order. bitcoinjs-lib stores `input.hash` in internal\n * little-endian byte order, which is the reverse of how txids are normally\n * displayed.\n */\nfunction readInputTxid(tx: Transaction, inputIndex: number): string {\n  const input = tx.ins[inputIndex];\n  return uint8ArrayToHex(new Uint8Array(input.hash).slice().reverse());\n}\n\n/**\n * Verify the noPayout transaction's input at `inputIndex` references the\n * given parent transaction at vout 0 (per nopayout.rs the layout is fixed:\n * Assert:0, ChallengeAssertX:0, ChallengeAssertY:0).\n */\nfunction assertInputReferencesParent(\n  noPayoutTx: Transaction,\n  inputIndex: number,\n  parentTx: Transaction,\n  parentLabel: string,\n  challengerPubkey: string,\n): void {\n  const input = noPayoutTx.ins[inputIndex];\n  if (input.index !== 0) {\n    throw new Error(\n      `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} expected to spend ${parentLabel} vout 0, got vout ${input.index}`,\n    );\n  }\n  const parentTxid = parentTx.getId();\n  const inputTxid = readInputTxid(noPayoutTx, inputIndex);\n  if (inputTxid !== parentTxid) {\n    throw new Error(\n      `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} does not reference ${parentLabel} (expected txid ${parentTxid}, got ${inputTxid})`,\n    );\n  }\n}\n\n// ============================================================================\n// Collect phase\n// ============================================================================\n\n/**\n * Build the depositor's payout PSBT and per-challenger NoPayout PSBTs locally\n * from authoritative connector params.\n *\n * Layout of returned arrays: [Payout, NoPayout_0, NoPayout_1, ...]\n */\nasync function collectDepositorGraphPsbts(\n  depositorGraph: DepositorGraphTransactions,\n  walletPublicKey: string,\n  ctx: DepositorGraphSigningContext,\n): Promise<CollectedDepositorGraphPsbts> {\n  const psbtHexes: string[] = [];\n  const signOptions: SignPsbtOptions[] = [];\n  const challengerEntries: ChallengerEntry[] = [];\n\n  // 1. Fail-fast on a malformed VP response BEFORE doing any PSBT-build\n  //    work that would be wasted if the challenger set is wrong.\n  const localChallengers = deriveLocalChallengers({\n    claimerBtcPubkey: ctx.depositorBtcPubkey,\n    depositorBtcPubkey: ctx.depositorBtcPubkey,\n    vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n    vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n  });\n  assertChallengerSetMatchesExpected(\n    depositorGraph.challenger_presign_data,\n    localChallengers,\n    ctx.universalChallengerBtcPubkeys,\n  );\n\n  // 2. Build the payout PSBT locally — every sighash-relevant field is\n  //    derived from trusted on-chain connector params, not from the VP.\n  //    buildPayoutPsbt also runs the per-role output validation.\n  const builtPayout = await buildPayoutPsbt({\n    vaultCoreVersion: ctx.vaultCoreVersion,\n    vkClaimerPayoutScriptPubKeys: ctx.vkClaimerPayoutScriptPubKeys,\n    vpCommissionScriptPubKey: ctx.vpCommissionScriptPubKey,\n    payoutTxHex: depositorGraph.payout_tx.tx_hex,\n    peginTxHex: ctx.peginTxHex,\n    assertTxHex: depositorGraph.assert_tx.tx_hex,\n    timelockAssert: ctx.timelockAssert,\n    depositorBtcPubkey: ctx.depositorBtcPubkey,\n    vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n    vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n    universalChallengerBtcPubkeys: ctx.universalChallengerBtcPubkeys,\n    timelockPegin: ctx.timelockPegin,\n    network: ctx.network,\n    claimerBtcPubkey: ctx.depositorBtcPubkey,\n    registeredPayoutScriptPubKey: ctx.registeredPayoutScriptPubKey,\n    commissionBps: DEPOSITOR_PATH_UNUSED_COMMISSION_BPS,\n    protocolFeeRate: ctx.protocolFeeRate,\n    councilMembers: ctx.councilMembers,\n    councilQuorum: ctx.councilQuorum,\n  });\n  psbtHexes.push(builtPayout.psbtHex);\n  signOptions.push(\n    createTaprootScriptPathSignOptions(\n      walletPublicKey,\n      DEPOSITOR_SIGNED_INPUT_COUNT,\n    ),\n  );\n\n  // 3. Per-challenger: build the NoPayout PSBT locally too.\n  const claimerPubkey = stripHexPrefix(ctx.depositorBtcPubkey);\n  const assertTxParsed = Transaction.fromHex(\n    stripHexPrefix(depositorGraph.assert_tx.tx_hex),\n  );\n\n  for (const challenger of depositorGraph.challenger_presign_data) {\n    const challengerPubkey = stripHexPrefix(challenger.challenger_pubkey);\n\n    const noPayoutIdx = psbtHexes.length;\n    const noPayoutHex = await buildLocalNoPayoutPsbt({\n      challenger,\n      challengerPubkey,\n      claimerPubkey,\n      localChallengers,\n      assertTxParsed,\n      ctx,\n    });\n    psbtHexes.push(noPayoutHex);\n    signOptions.push(\n      createTaprootScriptPathSignOptions(\n        walletPublicKey,\n        DEPOSITOR_SIGNED_INPUT_COUNT,\n      ),\n    );\n\n    challengerEntries.push({\n      challengerPubkey,\n      noPayoutIdx,\n    });\n  }\n\n  return { psbtHexes, signOptions, challengerEntries };\n}\n\ninterface BuildLocalNoPayoutPsbtParams {\n  challenger: PresignDataPerChallenger;\n  challengerPubkey: string;\n  claimerPubkey: string;\n  localChallengers: string[];\n  assertTxParsed: Transaction;\n  ctx: DepositorGraphSigningContext;\n}\n\n/**\n * Build a single NoPayout PSBT for one challenger from authoritative\n * inputs. Validates the VP-supplied parent transactions match what the\n * NoPayout transaction commits to via input txids, and asserts the output\n * pays to the protocol-defined challenger sink before returning.\n *\n * NoPayout transaction layout (per\n * btc-vault crates/vault/src/transactions/nopayout.rs):\n * - 3 inputs (fixed order):\n *   - Input 0: Assert tx output 0 (depositor signs - NoPayout path)\n *   - Input 1: ChallengeAssertX tx output 0 (with timelock)\n *   - Input 2: ChallengeAssertY tx output 0 (with timelock)\n * - 1 output: BIP-86 P2TR to the challenger\n */\nasync function buildLocalNoPayoutPsbt(\n  params: BuildLocalNoPayoutPsbtParams,\n): Promise<string> {\n  const {\n    challenger,\n    challengerPubkey,\n    claimerPubkey,\n    localChallengers,\n    assertTxParsed,\n    ctx,\n  } = params;\n\n  // Pin the output sink before doing any sighash-relevant work.\n  assertNoPayoutOutputMatchesChallenger(\n    challenger.nopayout_tx.tx_hex,\n    challengerPubkey,\n    ctx.network,\n  );\n\n  // Parse the NoPayout tx and the two ChallengeAssert parents.\n  const noPayoutTx = Transaction.fromHex(\n    stripHexPrefix(challenger.nopayout_tx.tx_hex),\n  );\n  const challengeAssertXTx = Transaction.fromHex(\n    stripHexPrefix(challenger.challenge_assert_x_tx.tx_hex),\n  );\n  const challengeAssertYTx = Transaction.fromHex(\n    stripHexPrefix(challenger.challenge_assert_y_tx.tx_hex),\n  );\n\n  if (noPayoutTx.ins.length !== 3) {\n    throw new Error(\n      `NoPayout (challenger ${challengerPubkey}) must have exactly 3 inputs, got ${noPayoutTx.ins.length}`,\n    );\n  }\n\n  // Pin every input's parent. Each parent's outs[0] is the authoritative\n  // prevout - because we verified the parent's txid matches what the NoPayout\n  // tx commits to, the parent cannot be substituted without changing the\n  // NoPayout txid.\n  assertInputReferencesParent(\n    noPayoutTx,\n    0,\n    assertTxParsed,\n    \"Assert\",\n    challengerPubkey,\n  );\n  assertInputReferencesParent(\n    noPayoutTx,\n    1,\n    challengeAssertXTx,\n    \"ChallengeAssertX\",\n    challengerPubkey,\n  );\n  assertInputReferencesParent(\n    noPayoutTx,\n    2,\n    challengeAssertYTx,\n    \"ChallengeAssertY\",\n    challengerPubkey,\n  );\n\n  const prevouts = [\n    assertTxParsed.outs[0],\n    challengeAssertXTx.outs[0],\n    challengeAssertYTx.outs[0],\n  ].map((out) => ({\n    script_pubkey: uint8ArrayToHex(new Uint8Array(out.script)),\n    value: out.value,\n  }));\n\n  return buildNoPayoutPsbt({\n    noPayoutTxHex: challenger.nopayout_tx.tx_hex,\n    challengerPubkey,\n    prevouts,\n    connectorParams: {\n      txGraphVersion: ctx.vaultCoreVersion,\n      claimer: claimerPubkey,\n      localChallengers,\n      universalChallengers: ctx.universalChallengerBtcPubkeys,\n      timelockAssert: ctx.timelockAssert,\n      councilMembers: ctx.councilMembers,\n      councilQuorum: ctx.councilQuorum,\n    },\n  });\n}\n\n// ============================================================================\n// Extract phase\n// ============================================================================\n\n/** A pair of a locally-built PSBT and the wallet-returned PSBT for it. */\ntype PsbtPair = AssertPsbtUnsignedTxMatchesParams;\n\n/**\n * Extract all signatures from signed PSBTs and assemble into presignatures.\n * Each pair is asserted to encode the same unsigned tx before its signature\n * is extracted — defends against a wallet that returns a signature for a\n * substituted transaction.\n */\nfunction extractDepositorGraphSignatures(\n  psbtPairs: PsbtPair[],\n  challengerEntries: ChallengerEntry[],\n  depositorPubkey: string,\n): DepositorAsClaimerPresignatures {\n  // Positional invariant: psbtPairs[0] is the payout PSBT; per-challenger\n  // nopayouts live at indices recorded in `challengerEntries[].noPayoutIdx`.\n  // Set up by `collectDepositorGraphPsbts` (payout pushed first, then each\n  // nopayout). A future refactor that reorders the array would silently\n  // extract the wrong signature for the wrong slot — Critical Path #3.\n  // Payout and every NoPayout PSBT are signed on input 0 (depositor script-path).\n  const DEPOSITOR_SIGNED_INPUT_INDEX = 0;\n\n  assertPsbtUnsignedTxMatches(psbtPairs[0]);\n  const payoutSignature = extractPayoutSignature(\n    psbtPairs[0].returnedPsbtHex,\n    depositorPubkey,\n  );\n  // Critical Path #7: verify the wallet's signature against a sighash recomputed\n  // from the PSBT we built (psbtPairs[0].requestedPsbtHex), not the returned one.\n  assertScriptPathSchnorrSignature({\n    requestedPsbtHex: psbtPairs[0].requestedPsbtHex,\n    signatureHex: payoutSignature,\n    signerXOnlyPubkeyHex: depositorPubkey,\n    inputIndex: DEPOSITOR_SIGNED_INPUT_INDEX,\n  });\n\n  const perChallenger: Record<string, DepositorPreSigsPerChallenger> = {};\n  for (const entry of challengerEntries) {\n    assertPsbtUnsignedTxMatches(psbtPairs[entry.noPayoutIdx]);\n    const nopayoutSignature = extractPayoutSignature(\n      psbtPairs[entry.noPayoutIdx].returnedPsbtHex,\n      depositorPubkey,\n    );\n    assertScriptPathSchnorrSignature({\n      requestedPsbtHex: psbtPairs[entry.noPayoutIdx].requestedPsbtHex,\n      signatureHex: nopayoutSignature,\n      signerXOnlyPubkeyHex: depositorPubkey,\n      inputIndex: DEPOSITOR_SIGNED_INPUT_INDEX,\n    });\n    perChallenger[entry.challengerPubkey] = {\n      nopayout_signature: nopayoutSignature,\n    };\n  }\n\n  return {\n    payout_signatures: {\n      payout_signature: payoutSignature,\n    },\n    per_challenger: perChallenger,\n  };\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Authoritative inputs required to construct the depositor's Payout AND every\n * per-challenger NoPayout PSBT locally. Every field here must come from\n * trusted on-chain sources, not from the vault provider response. They feed\n * directly into the Taproot sighash.\n */\nexport interface DepositorGraphSigningContext {\n  /**\n   * Vault core (tx-graph) version the vault was registered under — the\n   * vault's stamped on-chain `vaultCoreVersion` from `BTCVaultRegistry`.\n   * Selects which graph's connector scripts every PSBT is rebuilt with.\n   */\n  vaultCoreVersion: number;\n  /** Raw pegin BTC transaction hex (provides the depositor's signed prevout) */\n  peginTxHex: string;\n  /** Depositor's BTC public key (x-only, 64-char hex, no 0x prefix) */\n  depositorBtcPubkey: string;\n  /** Vault provider's BTC public key (x-only hex, no prefix) */\n  vaultProviderBtcPubkey: string;\n  /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n  vaultKeeperBtcPubkeys: string[];\n  /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n  universalChallengerBtcPubkeys: string[];\n  /** Pegin CSV timelock from the locked offchain params version (blocks) */\n  timelockPegin: number;\n  /**\n   * Tx-graph fee rate (sat/vB) from the locked offchain params version —\n   * bounds the depositor-claimer payout's implicit fee (payout fee band).\n   */\n  protocolFeeRate: bigint;\n  /**\n   * Assert CSV timelock from the locked offchain params version (blocks).\n   * Sourced from the on-chain ProtocolParams contract via\n   * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n   */\n  timelockAssert: number;\n  /**\n   * Security council member x-only public keys (hex, no prefix). Sourced from\n   * the on-chain ProtocolParams contract via\n   * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).securityCouncilKeys`.\n   */\n  councilMembers: string[];\n  /**\n   * M-of-N council quorum threshold. Sourced from the on-chain ProtocolParams\n   * contract via `ViemProtocolParamsReader.getOffchainParamsByVersion(...).councilQuorum`.\n   */\n  councilQuorum: number;\n  /** BTC network (Mainnet, Testnet, etc.) */\n  network: Network;\n  /**\n   * On-chain registered depositor payout scriptPubKey (hex, with or without\n   * 0x prefix). Used to assert the VP-advertised payout transaction pays to\n   * the depositor's registered address before the wallet produces a signature.\n   */\n  registeredPayoutScriptPubKey: string;\n  /**\n   * RFC-006 operator payout destinations. Forwarded to `buildPayoutPsbt` for\n   * shape completeness only: this graph is signed under the\n   * `depositor-as-claimer` role, whose payout has two outputs and reads\n   * neither the keeper map nor the VP commission destination.\n   */\n  vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n  /** See {@link vkClaimerPayoutScriptPubKeys} — unused for this role. */\n  vpCommissionScriptPubKey: string;\n}\n\nexport interface SignDepositorGraphParams {\n  /** The depositor graph from VP response */\n  depositorGraph: DepositorGraphTransactions;\n  /** Bitcoin wallet for signing */\n  btcWallet: BitcoinWallet;\n  /** Authoritative inputs used to rebuild every PSBT locally */\n  signingContext: DepositorGraphSigningContext;\n}\n\n/**\n * Sign all depositor graph transactions and assemble into presignatures.\n *\n * Flow:\n * 1. Build payout + per-challenger nopayout PSBTs locally\n * 2. Batch sign via wallet.signPsbts() if available, else sequential signPsbt()\n * 3. Extract Schnorr signatures from each signed PSBT\n * 4. Assemble into DepositorAsClaimerPresignatures\n */\nexport async function signDepositorGraph(\n  params: SignDepositorGraphParams,\n): Promise<DepositorAsClaimerPresignatures> {\n  const { depositorGraph, btcWallet, signingContext } = params;\n\n  const walletPublicKey = await btcWallet.getPublicKeyHex();\n  // Fail fast if the connected wallet doesn't match the on-chain registered\n  // depositor key — otherwise extractPayoutSignature later fails after\n  // multiple wallet popups with an opaque \"no signature found\" error.\n  const { depositorPubkey } = validateWalletPubkey(\n    walletPublicKey,\n    stripHexPrefix(signingContext.depositorBtcPubkey),\n  );\n\n  // 1. Build all PSBTs locally\n  const { psbtHexes, signOptions, challengerEntries } =\n    await collectDepositorGraphPsbts(\n      depositorGraph,\n      walletPublicKey,\n      signingContext,\n    );\n\n  // 2. Sign all PSBTs (batch when supported, sequential fallback for mobile)\n  // signPsbtsWithFallback guarantees one signed PSBT per input (or throws), so\n  // no separate arity check is needed here.\n  const signedPsbtHexes = await signPsbtsWithFallback(\n    btcWallet,\n    psbtHexes,\n    signOptions,\n  );\n\n  // 3. Pair requested with signed and extract signatures\n  const psbtPairs: PsbtPair[] = psbtHexes.map((requestedPsbtHex, i) => ({\n    requestedPsbtHex,\n    returnedPsbtHex: signedPsbtHexes[i],\n  }));\n  return extractDepositorGraphSignatures(\n    psbtPairs,\n    challengerEntries,\n    depositorPubkey,\n  );\n}\n","/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * Pure polling utility with no framework dependencies (no localStorage, no React).\n * Handles \"PegIn not found\" as transient (VP hasn't ingested yet).\n */\n\nimport { JsonRpcError } from \"../../clients/vault-provider/json-rpc-client\";\nimport {\n  DaemonStatus,\n  RpcErrorCode,\n  VP_TERMINAL_FAILURE_STATUSES,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader } from \"./interfaces\";\n\n/** Default polling interval (10 seconds). */\nconst DEFAULT_POLL_INTERVAL_MS = 10_000;\n\nexport interface WaitForPeginStatusParams {\n  /** VP client implementing the status reader interface */\n  statusReader: PeginStatusReader;\n  /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n  peginTxid: string;\n  /** Set of acceptable statuses — polling stops when the VP reports one of these */\n  targetStatuses: ReadonlySet<DaemonStatus>;\n  /** Maximum time to wait in milliseconds */\n  timeoutMs: number;\n  /** Polling interval in milliseconds (default: 10s) */\n  pollIntervalMs?: number;\n  /** AbortSignal for cancellation */\n  signal?: AbortSignal;\n}\n\n/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * @returns The DaemonStatus that matched one of the targets, OR\n *   `DaemonStatus.ACTIVATED` if the VP raced past the requested target into the\n *   happy-path terminal (success-via-overshoot — the goal is satisfied).\n * @throws Error on timeout, abort, non-transient RPC error, or any terminal status (`Expired` + `VP_TERMINAL_FAILURE_STATUSES`) not in `targetStatuses`.\n */\nexport async function waitForPeginStatus(\n  params: WaitForPeginStatusParams,\n): Promise<DaemonStatus> {\n  const {\n    statusReader,\n    peginTxid,\n    targetStatuses,\n    timeoutMs,\n    pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n    signal,\n  } = params;\n\n  const startTime = Date.now();\n\n  while (true) {\n    if (signal?.aborted) {\n      throw new Error(\n        `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n      );\n    }\n\n    if (Date.now() - startTime >= timeoutMs) {\n      throw new Error(\n        `Polling timeout after ${timeoutMs}ms for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n      );\n    }\n\n    try {\n      const response = await statusReader.getPeginStatus(\n        { pegin_txid: peginTxid },\n        signal,\n      );\n\n      // Reject responses echoing a different pegin txid.\n      if (response.pegin_txid.toLowerCase() !== peginTxid.toLowerCase()) {\n        throw new Error(\n          `getPeginStatus returned status for pegin ${response.pegin_txid.slice(0, 8)}…, requested ${peginTxid.slice(0, 8)}…`,\n        );\n      }\n\n      const status = response.status as DaemonStatus;\n      if (targetStatuses.has(status)) {\n        return status;\n      }\n      // Happy-path overshoot: VP raced past the requested target to ACTIVATED.\n      // The caller's goal (reach some earlier state) is satisfied — return\n      // success rather than time out waiting for a state the VP already left.\n      if (status === DaemonStatus.ACTIVATED) {\n        return status;\n      }\n      // EXPIRED is included — depositor has no path forward once VP marks the pegin Expired.\n      if (\n        status === DaemonStatus.EXPIRED ||\n        VP_TERMINAL_FAILURE_STATUSES.has(status)\n      ) {\n        throw new Error(\n          `Pegin ${peginTxid.slice(0, 8)}… reached terminal status \"${status}\" while waiting for ${[...targetStatuses].join(\", \")}`,\n        );\n      }\n    } catch (error) {\n      // \"PegIn not found\" is transient — VP hasn't ingested the pegin yet.\n      const isNotFound =\n        error instanceof JsonRpcError &&\n        error.code === RpcErrorCode.PEGIN_NOT_FOUND;\n      if (!isNotFound) {\n        throw error;\n      }\n    }\n\n    // Wait before next poll, with abort support\n    await new Promise<void>((resolve, reject) => {\n      const onAbort = () => {\n        clearTimeout(timeoutId);\n        reject(\n          new Error(\n            `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n          ),\n        );\n      };\n      const timeoutId = setTimeout(() => {\n        signal?.removeEventListener(\"abort\", onAbort);\n        resolve();\n      }, pollIntervalMs);\n      signal?.addEventListener(\"abort\", onAbort, { once: true });\n    });\n  }\n}\n","/**\n * Payout Signing Orchestration\n *\n * Polls VP for `PendingDepositorSignatures`, fetches presign transactions,\n * signs payouts via PayoutManager, signs the depositor graph, and submits\n * all signatures back to the VP.\n *\n * This is the main deposit protocol step between registration and activation.\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport type {\n  ClaimerSignatures,\n  ClaimerTransactions,\n} from \"../../clients/vault-provider/types\";\nimport { DaemonStatus } from \"../../clients/vault-provider/types\";\nimport {\n  supportsDepositApproval,\n  type DepositTerms,\n} from \"../../deposit-terms\";\nimport { PayoutManager } from \"../../managers/PayoutManager\";\nimport {\n  processPublicKeyToXOnly,\n  stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport type { PeginStatusReader, PresignClient } from \"./interfaces\";\nimport { signDepositorGraph } from \"./signDepositorGraph\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context required for signing payout transactions.\n * Caller builds this from on-chain data (contract queries, GraphQL, config).\n */\nexport interface PayoutSigningContext {\n  /**\n   * Vault core (tx-graph) version the vault was registered under — the\n   * vault's stamped on-chain `vaultCoreVersion` from `BTCVaultRegistry`.\n   * Selects which graph's connector scripts every payout/nopayout PSBT is\n   * rebuilt with.\n   */\n  vaultCoreVersion: number;\n  /** Raw pegin BTC transaction hex (for PSBT construction) */\n  peginTxHex: string;\n  /** Vault provider's BTC public key (x-only hex, no prefix) */\n  vaultProviderBtcPubkey: string;\n  /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n  vaultKeeperBtcPubkeys: string[];\n  /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n  universalChallengerBtcPubkeys: string[];\n  /** Depositor's BTC public key (x-only hex, no prefix) */\n  depositorBtcPubkey: string;\n  /** Pegin timelock from the locked offchain params version */\n  timelockPegin: number;\n  /**\n   * Assert CSV timelock from the locked offchain params version (blocks).\n   * Source: ProtocolParams contract via\n   * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n   * Required for the depositor-graph NoPayout local rebuild.\n   */\n  timelockAssert: number;\n  /**\n   * Security council member x-only public keys (hex, no prefix).\n   * Source: ProtocolParams contract via\n   * `getOffchainParamsByVersion(...).securityCouncilKeys`.\n   * Required to rebuild every Assert:0 leaf (payout and NoPayout) locally.\n   */\n  councilMembers: string[];\n  /**\n   * M-of-N council quorum threshold.\n   * Source: ProtocolParams contract via\n   * `getOffchainParamsByVersion(...).councilQuorum`.\n   * Required to rebuild every Assert:0 leaf (payout and NoPayout) locally.\n   */\n  councilQuorum: number;\n  /** BTC network (Mainnet, Testnet, etc.) */\n  network: Network;\n  /** On-chain registered depositor payout scriptPubKey (hex) */\n  registeredPayoutScriptPubKey: string;\n  /** VP commission (bps) from `BTCVaultRegistry`; caps the VP-claimer payout commission output. */\n  commissionBps: number;\n  /**\n   * Tx-graph fee rate (sat/vB) from the locked offchain params version —\n   * `getOffchainParamsByVersion(...).feeRate`, the rate the VP built the\n   * graph with. Bounds every payout's implicit fee (payout fee band).\n   */\n  protocolFeeRate: bigint;\n\n  /**\n   * RFC-006 resolved keeper payout destinations at the vault's frozen\n   * `appKeeperKeyEpoch`, keyed by lowercased x-only operation pubkey.\n   */\n  vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n  /**\n   * RFC-006 resolved VP commission destination at the vault's frozen\n   * `vpKeyEpoch`.\n   */\n  vpCommissionScriptPubKey: string;\n}\n\nexport interface RunDepositorPresignFlowParams {\n  /** VP client implementing the status reader interface */\n  statusReader: PeginStatusReader;\n  /** VP client implementing the presign transaction flow interface */\n  presignClient: PresignClient;\n  /** Bitcoin wallet for signing */\n  btcWallet: BitcoinWallet;\n  /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n  peginTxid: string;\n  /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n  depositorPk: string;\n  /** Signing context built from on-chain data */\n  signingContext: PayoutSigningContext;\n  /**\n   * Required for approval-capable wallets. Fresh flows pass\n   * PreparePeginResult.depositTerms; resume flows rebuild them from\n   * on-chain state (the vault app's rebuildDepositTerms).\n   */\n  depositTerms?: DepositTerms;\n  /** Maximum polling timeout in milliseconds (default: 20 min) */\n  timeoutMs?: number;\n  /** AbortSignal for cancellation */\n  signal?: AbortSignal;\n  /** Optional progress callback (completed claimers, total claimers) */\n  onProgress?: (completed: number, total: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum polling timeout (20 minutes) — VP may take 15-20 min to prepare. */\nconst MAX_POLLING_TIMEOUT_MS = 20 * 60 * 1000;\n\n/** Statuses after payout signatures are submitted — if VP is already here, skip. */\nconst POST_PAYOUT_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n  DaemonStatus.PENDING_ACKS,\n  DaemonStatus.PENDING_ACTIVATION,\n  DaemonStatus.ACTIVATED_PENDING_BROADCAST,\n  DaemonStatus.ACTIVATED,\n]);\n\nconst TARGET_STATUS: ReadonlySet<DaemonStatus> = new Set([\n  DaemonStatus.PENDING_DEPOSITOR_SIGNATURES,\n  ...POST_PAYOUT_STATUSES,\n]);\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\ninterface PreparedTransaction {\n  claimerPubkeyXOnly: string;\n  payoutTxHex: string;\n  assertTxHex: string;\n}\n\nfunction prepareTransactionsForSigning(\n  claimerTransactions: ClaimerTransactions[],\n): PreparedTransaction[] {\n  return claimerTransactions.map((tx) => ({\n    claimerPubkeyXOnly: processPublicKeyToXOnly(tx.claimer_pubkey),\n    payoutTxHex: tx.payout_tx.tx_hex,\n    assertTxHex: tx.assert_tx.tx_hex,\n  }));\n}\n\n/**\n * Canonical x-only lowercase form, used for all claimer pubkey set-equality\n * comparisons in this module. `processPublicKeyToXOnly` already strips any\n * `0x` prefix; the lowercase here removes case-sensitivity (the VP-response\n * schema validator accepts uppercase hex, and `processPublicKeyToXOnly`\n * preserves the case of already-x-only 64-char input).\n */\nfunction normalizeClaimerPubkey(pubkey: string): string {\n  return processPublicKeyToXOnly(pubkey).toLowerCase();\n}\n\n/**\n * Assert the approved terms describe the graph we will actually sign. An\n * RFC-006 key rotation bumps only a key epoch, so no version comparison can\n * catch it; `verifyRegisteredParticipantKeys` is the app-side pin, and this\n * keeps the seam self-contained for external providers (#2109).\n *\n * @throws If the terms and the signing context disagree\n */\nfunction assertDepositTermsMatchSigningContext(\n  terms: DepositTerms,\n  context: PayoutSigningContext,\n): void {\n  const refuse = (field: string, a: unknown, b: unknown): never => {\n    throw new Error(\n      `Deposit terms ${field} (${String(a)}) does not match the vault's ` +\n        `version-locked signing context (${String(b)}); refusing to sign ` +\n        `payouts against terms that describe a different graph.`,\n    );\n  };\n\n  // Every scalar the two types share: each one shapes the graph, and the\n  // timelocks are what payout.ts pins the input sequences to.\n  const scalars = [\n    [\"protocolFeeRate\", terms.protocolFeeRate, context.protocolFeeRate],\n    [\"vaultCoreVersion\", terms.vaultCoreVersion, context.vaultCoreVersion],\n    [\"timelockPegin\", terms.timelockPegin, context.timelockPegin],\n    [\"timelockAssert\", terms.timelockAssert, context.timelockAssert],\n  ] as const;\n  for (const [field, fromTerms, fromContext] of scalars) {\n    if (fromTerms !== fromContext) {\n      refuse(field, fromTerms, fromContext);\n    }\n  }\n\n  // Set, not sequence: btc-vault sorts every roster and rejects duplicates\n  // (crates/vault/src/lib.rs:249, :339), so a permutation is not drift.\n  const canonical = (keys: readonly string[]) =>\n    keys.map(normalizeClaimerPubkey).sort();\n  const sameSet = (a: readonly string[], b: readonly string[]) => {\n    const x = canonical(a);\n    const y = canonical(b);\n    return x.length === y.length && x.every((k, i) => k === y[i]);\n  };\n\n  if (!sameSet(terms.vaultKeeperBtcPubkeys, context.vaultKeeperBtcPubkeys)) {\n    refuse(\n      \"vaultKeeperBtcPubkeys\",\n      terms.vaultKeeperBtcPubkeys.join(\",\"),\n      context.vaultKeeperBtcPubkeys.join(\",\"),\n    );\n  }\n  if (\n    !sameSet(\n      terms.universalChallengerBtcPubkeys,\n      context.universalChallengerBtcPubkeys,\n    )\n  ) {\n    refuse(\n      \"universalChallengerBtcPubkeys\",\n      terms.universalChallengerBtcPubkeys.join(\",\"),\n      context.universalChallengerBtcPubkeys.join(\",\"),\n    );\n  }\n\n  // Membership, not equality: `DepositTermsVaultGroup` carries a per-vault VP\n  // key, so a batch may legitimately span providers. What matters is that the\n  // vault this flow signs for was covered by what the depositor approved.\n  const contextVp = normalizeClaimerPubkey(context.vaultProviderBtcPubkey);\n  const approvedVps = terms.vaults.map((v) =>\n    normalizeClaimerPubkey(v.vaultProviderBtcPubkey),\n  );\n  if (!approvedVps.includes(contextVp)) {\n    refuse(\n      \"vaults[].vaultProviderBtcPubkey\",\n      approvedVps.join(\",\") || \"<no vaults>\",\n      context.vaultProviderBtcPubkey,\n    );\n  }\n}\n\n/**\n * Reject VP-supplied `response.txs` whose non-depositor claimer set does not\n * exactly equal `{vaultProviderBtcPubkey} ∪ vaultKeeperBtcPubkeys`.\n *\n * The expected set is derived from on-chain context (sourced by the caller\n * from the registry/contract reads that populate PayoutSigningContext). A\n * malicious or buggy VP could otherwise omit registered vault keepers from\n * the response; the depositor would sign only the supplied subset and submit\n * a partial presignature map. If the VP later disappears, the omitted\n * keepers cannot exercise their payout recovery branch and BTC can lock.\n *\n * The depositor's own claimer entry (if present in `response.txs`) is\n * filtered out before diffing — its Payout PSBT is built locally and signed\n * separately via signDepositorGraph, so its presence in `response.txs` is\n * permitted but not required. Duplicate detection runs on the full supplied\n * list *before* the depositor filter, so a response containing\n * `[VP, VK, depositor, depositor]` is rejected as malformed.\n */\nfunction assertNonDepositorClaimerSetMatches(\n  suppliedTxs: ClaimerTransactions[],\n  expectedVpPubkey: string,\n  expectedVkPubkeys: string[],\n  depositorPubkeyXOnly: string,\n): void {\n  const depositor = normalizeClaimerPubkey(depositorPubkeyXOnly);\n  const expectedList = [\n    normalizeClaimerPubkey(expectedVpPubkey),\n    ...expectedVkPubkeys.map(normalizeClaimerPubkey),\n  ];\n  const expected = new Set(expectedList);\n  if (expected.size !== expectedList.length) {\n    throw new Error(\n      \"Cannot validate claimer set: signing context contains duplicate vault provider or vault keeper key\",\n    );\n  }\n  if (expected.has(depositor)) {\n    throw new Error(\n      \"Cannot validate claimer set: depositor key overlaps with vault provider or vault keeper set\",\n    );\n  }\n\n  const suppliedAll = suppliedTxs.map((tx) =>\n    normalizeClaimerPubkey(tx.claimer_pubkey),\n  );\n  if (new Set(suppliedAll).size !== suppliedAll.length) {\n    throw new Error(\"Presign response contains duplicate claimer entries\");\n  }\n\n  const suppliedNonDepositor = suppliedAll.filter((k) => k !== depositor);\n  const suppliedSet = new Set(suppliedNonDepositor);\n  const missing = expectedList.filter((c) => !suppliedSet.has(c));\n  const extra = suppliedNonDepositor.filter((c) => !expected.has(c));\n  if (missing.length > 0 || extra.length > 0) {\n    throw new Error(\n      `Presign response claimer set does not match expected (vault provider ∪ vault keepers)` +\n        (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n        (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n    );\n  }\n}\n\n/**\n * Build the `SignPayoutParams` for a single claimer. Role/script resolution\n * happens inside `buildPayoutPsbt`; here we only forward the claimer pubkey\n * and the per-vault context fields.\n */\nfunction buildPayoutSigningInput(\n  tx: PreparedTransaction,\n  context: PayoutSigningContext,\n) {\n  return {\n    vaultCoreVersion: context.vaultCoreVersion,\n    payoutTxHex: tx.payoutTxHex,\n    peginTxHex: context.peginTxHex,\n    assertTxHex: tx.assertTxHex,\n    vaultProviderBtcPubkey: context.vaultProviderBtcPubkey,\n    vaultKeeperBtcPubkeys: context.vaultKeeperBtcPubkeys,\n    universalChallengerBtcPubkeys: context.universalChallengerBtcPubkeys,\n    depositorBtcPubkey: context.depositorBtcPubkey,\n    timelockPegin: context.timelockPegin,\n    timelockAssert: context.timelockAssert,\n    registeredPayoutScriptPubKey: context.registeredPayoutScriptPubKey,\n    claimerBtcPubkey: tx.claimerPubkeyXOnly,\n    commissionBps: context.commissionBps,\n    protocolFeeRate: context.protocolFeeRate,\n    councilMembers: context.councilMembers,\n    councilQuorum: context.councilQuorum,\n    vkClaimerPayoutScriptPubKeys: context.vkClaimerPayoutScriptPubKeys,\n    vpCommissionScriptPubKey: context.vpCommissionScriptPubKey,\n  };\n}\n\n/**\n * Sign all payout transactions using PayoutManager.\n * Uses batch signing when wallet supports it, sequential otherwise.\n */\nasync function signPayoutTransactions(\n  btcWallet: BitcoinWallet,\n  context: PayoutSigningContext,\n  transactions: PreparedTransaction[],\n  onProgress?: (completed: number, total: number) => void,\n): Promise<Record<string, ClaimerSignatures>> {\n  const payoutManager = new PayoutManager({\n    network: context.network,\n    btcWallet,\n  });\n\n  const totalClaimers = transactions.length;\n  onProgress?.(0, totalClaimers);\n\n  let payoutSignatures: string[];\n\n  if (payoutManager.supportsBatchSigning()) {\n    const results = await payoutManager.signPayoutTransactionsBatch(\n      transactions.map((tx) => buildPayoutSigningInput(tx, context)),\n    );\n    payoutSignatures = results.map((r) => r.payoutSignature);\n  } else {\n    payoutSignatures = [];\n    for (let i = 0; i < transactions.length; i++) {\n      onProgress?.(i, totalClaimers);\n      const result = await payoutManager.signPayoutTransaction(\n        buildPayoutSigningInput(transactions[i], context),\n      );\n      payoutSignatures.push(result.signature);\n    }\n  }\n\n  const signatures: Record<string, ClaimerSignatures> = {};\n  for (let i = 0; i < transactions.length; i++) {\n    signatures[transactions[i].claimerPubkeyXOnly] = {\n      payout_signature: payoutSignatures[i],\n    };\n  }\n\n  onProgress?.(totalClaimers, totalClaimers);\n  return signatures;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Poll for payout transactions, sign them, sign the depositor graph,\n * and submit all signatures to the vault provider.\n *\n * This is the main deposit protocol step between registration and activation.\n *\n * @throws Error on timeout, abort, signing failure, or RPC error\n */\nexport async function runDepositorPresignFlow(\n  params: RunDepositorPresignFlowParams,\n): Promise<void> {\n  const {\n    statusReader,\n    presignClient,\n    btcWallet,\n    peginTxid,\n    depositorPk,\n    signingContext,\n    depositTerms,\n    timeoutMs = MAX_POLLING_TIMEOUT_MS,\n    signal,\n    onProgress,\n  } = params;\n\n  // Phase 1: Poll until VP is ready for depositor signatures (or already past)\n  const status = await waitForPeginStatus({\n    statusReader,\n    peginTxid,\n    targetStatuses: TARGET_STATUS,\n    timeoutMs,\n    signal,\n  });\n\n  // Resume-safe: if VP already moved past payout signing, nothing to do\n  if (POST_PAYOUT_STATUSES.has(status)) {\n    return;\n  }\n\n  signal?.throwIfAborted();\n\n  // Approval-capable wallets must approve before any signing call they\n  // authorize, and the terms must match what we sign. Conditional because\n  // non-approval wallets pass no terms.\n  if (depositTerms !== undefined) {\n    assertDepositTermsMatchSigningContext(depositTerms, signingContext);\n  }\n\n  if (supportsDepositApproval(btcWallet)) {\n    if (!depositTerms) {\n      throw new Error(\n        \"runDepositorPresignFlow: this wallet requires approved deposit terms but none were \" +\n          \"provided. Fresh deposits must pass PreparePeginResult.depositTerms; resume flows \" +\n          \"must rebuild them from on-chain state (the vault app's rebuildDepositTerms).\",\n      );\n    }\n    // #2110 T4: providers exposing the validate-only pre-check fail an\n    // envelope violation here, before the approval ceremony starts.\n    if (typeof btcWallet.validateDepositTerms === \"function\") {\n      await btcWallet.validateDepositTerms(depositTerms);\n    }\n    // The provider validates its own device envelope inside\n    // approveDepositTerms (DepositTermsApprover contract, #2109).\n    await btcWallet.approveDepositTerms(depositTerms);\n  }\n\n  // Phase 2: Fetch presign transactions\n  const response = await presignClient.requestDepositorPresignTransactions(\n    {\n      pegin_txid: peginTxid,\n      depositor_pk: depositorPk,\n    },\n    signal,\n  );\n\n  signal?.throwIfAborted();\n\n  // Phase 3: Sign VP/VK claimer payout transactions\n  // Fail-fast: assert the supplied non-depositor claimer set exactly equals\n  // the on-chain-derived {VP} ∪ {VKs} before any wallet prompts run. The\n  // depositor's own entry is permitted but not required (its payout is\n  // signed separately via signDepositorGraph in Phase 4).\n  const depositorPkNormalized = normalizeClaimerPubkey(depositorPk);\n  assertNonDepositorClaimerSetMatches(\n    response.txs,\n    signingContext.vaultProviderBtcPubkey,\n    signingContext.vaultKeeperBtcPubkeys,\n    depositorPk,\n  );\n  // Filter out the depositor's own claimer entry — its payout is signed\n  // separately via signDepositorGraph (Phase 4) using VP-provided PSBTs.\n  // Including it here would cause a redundant wallet signing prompt whose\n  // result is discarded when the depositor graph signature overwrites it.\n  // Compare on the normalized form so an uppercase-hex depositor entry in\n  // the VP response is still filtered out consistently with the assertion.\n  const nonDepositorTxs = response.txs.filter(\n    (tx) => normalizeClaimerPubkey(tx.claimer_pubkey) !== depositorPkNormalized,\n  );\n  const preparedTransactions = prepareTransactionsForSigning(nonDepositorTxs);\n  const claimerSignatures = await signPayoutTransactions(\n    btcWallet,\n    signingContext,\n    preparedTransactions,\n    onProgress,\n  );\n\n  signal?.throwIfAborted();\n\n  // Phase 4: Sign depositor-as-claimer graph. Both Payout and per-challenger\n  // NoPayout PSBTs are rebuilt locally inside signDepositorGraph from these\n  // authoritative connector params and the on-chain protocol parameters.\n  const depositorClaimerPresignatures = await signDepositorGraph({\n    depositorGraph: response.depositor_graph,\n    btcWallet,\n    signingContext: {\n      vaultCoreVersion: signingContext.vaultCoreVersion,\n      peginTxHex: signingContext.peginTxHex,\n      depositorBtcPubkey: depositorPk,\n      vaultProviderBtcPubkey: signingContext.vaultProviderBtcPubkey,\n      vaultKeeperBtcPubkeys: signingContext.vaultKeeperBtcPubkeys,\n      universalChallengerBtcPubkeys:\n        signingContext.universalChallengerBtcPubkeys,\n      timelockPegin: signingContext.timelockPegin,\n      timelockAssert: signingContext.timelockAssert,\n      councilMembers: signingContext.councilMembers,\n      councilQuorum: signingContext.councilQuorum,\n      network: signingContext.network,\n      registeredPayoutScriptPubKey: signingContext.registeredPayoutScriptPubKey,\n      protocolFeeRate: signingContext.protocolFeeRate,\n      vkClaimerPayoutScriptPubKeys: signingContext.vkClaimerPayoutScriptPubKeys,\n      vpCommissionScriptPubKey: signingContext.vpCommissionScriptPubKey,\n    },\n  });\n\n  signal?.throwIfAborted();\n\n  // Phase 5: Submit all signatures to VP\n  // Include depositor's own payout signature in the signatures map\n  const allSignatures = { ...claimerSignatures };\n  allSignatures[stripHexPrefix(depositorPk)] =\n    depositorClaimerPresignatures.payout_signatures;\n\n  await presignClient.submitDepositorPresignatures(\n    {\n      pegin_txid: peginTxid,\n      depositor_pk: depositorPk,\n      signatures: allSignatures,\n      depositor_claimer_presignatures: depositorClaimerPresignatures,\n    },\n    signal,\n  );\n}\n","/**\n * Submit pre-derived WOTS public keys to the vault provider.\n *\n * Polls `getPeginStatus` until the VP reaches `PendingDepositorWotsPK`,\n * then submits the keys. If the VP has already moved past WOTS step\n * (e.g., resume flow), submission is skipped.\n *\n * The caller is responsible for deriving WOTS keys externally using\n * `expandWotsSeed` + `deriveWotsBlocksFromSeed` from the SDK's\n * `tbv/core/vault-secrets` and `tbv/core/wots` modules respectively.\n */\n\nimport {\n  DaemonStatus,\n  POST_WOTS_STATUSES,\n  type WotsBlockPublicKey,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader, WotsKeySubmitter } from \"./interfaces\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n/** Maximum time to wait for VP to reach PendingDepositorWotsPK (5 min). */\nconst STATUS_POLL_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** All statuses we accept — either ready for submission or already past it. */\nconst TARGET_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n  DaemonStatus.PENDING_DEPOSITOR_WOTS_PK,\n  ...POST_WOTS_STATUSES,\n]);\n\nexport interface SubmitWotsPublicKeyParams {\n  /** VP client implementing the status reader interface */\n  statusReader: PeginStatusReader;\n  /** VP client implementing the WOTS key submission interface */\n  wotsSubmitter: WotsKeySubmitter;\n  /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n  peginTxid: string;\n  /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n  depositorPk: string;\n  /** Pre-derived WOTS block public keys (one per assert block) */\n  wotsPublicKeys: WotsBlockPublicKey[];\n  /** Maximum time to wait for VP to be ready (default: 5 min) */\n  timeoutMs?: number;\n  /** AbortSignal for cancellation */\n  signal?: AbortSignal;\n}\n\n/**\n * Submit WOTS public keys to the vault provider.\n *\n * @throws Error on timeout, abort, or RPC error\n */\nexport async function submitWotsPublicKey(\n  params: SubmitWotsPublicKeyParams,\n): Promise<void> {\n  const {\n    statusReader,\n    wotsSubmitter,\n    peginTxid,\n    depositorPk,\n    wotsPublicKeys,\n    timeoutMs = STATUS_POLL_TIMEOUT_MS,\n    signal,\n  } = params;\n\n  signal?.throwIfAborted();\n\n  // Wait until VP has ingested the pegin and is ready for the WOTS key.\n  const status = await waitForPeginStatus({\n    statusReader,\n    peginTxid,\n    targetStatuses: TARGET_STATUSES,\n    timeoutMs,\n    signal,\n  });\n\n  // Key was already submitted in a previous session (e.g. resume flow)\n  if (POST_WOTS_STATUSES.has(status)) {\n    return;\n  }\n\n  signal?.throwIfAborted();\n\n  await wotsSubmitter.submitDepositorWotsKey(\n    {\n      pegin_txid: peginTxid,\n      depositor_pk: depositorPk,\n      wots_public_keys: wotsPublicKeys,\n    },\n    signal,\n  );\n}\n","/**\n * RFC-006 indexer-hint acceptance policy — the single definition.\n *\n * Every path that builds against a vault takes participant keys from chain and\n * treats the indexer's copy as an untrusted *hint*. The hint never supplies key\n * material; its job is to catch a wrong vault provider address, a wrong\n * application entry point, or a stale roster version.\n *\n * Under RFC-006 an operator's BTC key is an append-only history of operation\n * keys, so there are two values the indexer may legitimately be serving at any\n * moment: the **registration** key (it has not caught up to a rotation) or the\n * **current operation** key (it has). Accepting only the first hard-fails every\n * depositor of a rotated provider the day the indexer catches up — an\n * indexer-side deploy, with nothing to deploy on our side and no user\n * workaround. Hence: accept either.\n *\n * For an operator that never rotated the two candidates are identical, so this\n * is exactly the old strict check until someone rotates.\n *\n * This module exists because the policy had already been written three times —\n * deposit, payout, and (missing) refund — and drifted each time. Callers differ\n * in shape and cannot share one entry point:\n *\n * - the deposit path compares three roles as whole sets and resolves operation\n *   keys **eagerly**, because it builds the Bitcoin lock with them;\n * - payout and refund compare one scalar key and must read the operation key\n *   **lazily**, because on the happy path it is a second RPC for nothing.\n *\n * So what is shared is the predicate — {@link isHintAccepted} and the two\n * matchers — plus {@link assertVaultProviderHintAccepted} for the two scalar\n * callers. Anything that needs a different acceptance rule does not belong\n * here; see `vpAuthPinnedPubkey`, which is deliberately operation-key-only\n * because accepting either would hollow out the pin.\n *\n * @module services/participants/indexerKeyHint\n */\n\nimport type { Address } from \"viem\";\n\nimport { canonicalizeBtcPubkey } from \"../../primitives/utils/bitcoin\";\n\n/**\n * Which of the two legitimate on-chain candidates a role's hint matched.\n *\n * Both true means the role never rotated, so the hint constrains nothing.\n * Both false means the hint is not explainable by any state the chain is in.\n */\nexport interface HintMatch {\n  registration: boolean;\n  operation: boolean;\n}\n\n/**\n * The accept-either policy itself.\n *\n * Kept as a named function rather than inlined at each call site so that\n * changing the policy is a one-line change in one file, and so a reader can\n * find every path governed by it.\n */\nexport function isHintAccepted(match: HintMatch): boolean {\n  return match.registration || match.operation;\n}\n\n/** Match a single hinted key against both candidates. */\nexport function matchKeyHint(\n  hint: string,\n  registrationKey: string,\n  operationKey: string,\n): HintMatch {\n  const canonicalHint = canonicalizeBtcPubkey(hint);\n  return {\n    registration: canonicalHint === canonicalizeBtcPubkey(registrationKey),\n    operation: canonicalHint === canonicalizeBtcPubkey(operationKey),\n  };\n}\n\n/**\n * Match a hinted key *set* against both candidate sets.\n *\n * Compared as whole sets, never as per-element membership of the union: a\n * roster holding one registration key and one operation key is an indexer that\n * is halfway through applying a rotation, and union membership would wave that\n * through. Order is normalized, so this is set equality and not list equality.\n */\nexport function matchKeySetHint(\n  hints: readonly string[],\n  registrationKeys: readonly string[],\n  operationKeys: readonly string[],\n): HintMatch {\n  const canonicalHints = sortedCanonical(hints);\n  return {\n    registration: setsEqual(canonicalHints, sortedCanonical(registrationKeys)),\n    operation: setsEqual(canonicalHints, sortedCanonical(operationKeys)),\n  };\n}\n\nfunction sortedCanonical(keys: readonly string[]): string[] {\n  return keys.map(canonicalizeBtcPubkey).sort();\n}\n\nfunction setsEqual(a: readonly string[], b: readonly string[]): boolean {\n  return a.length === b.length && a.every((key, i) => key === b[i]);\n}\n\nexport interface AssertVaultProviderHintAcceptedParams {\n  /** Vault provider's admin address, named in the error. */\n  vaultProviderEthAddress: Address;\n  /** The untrusted hint. Absent means there is nothing to cross-check. */\n  hintBtcPubkey?: string;\n  /** The vault provider's registration key, already read from chain. */\n  registrationBtcPubkey: string;\n  /**\n   * Reads the vault provider's *current* operation key.\n   *\n   * Invoked only when the hint fails against the registration key, so a\n   * provider that never rotated — and an indexer that has not caught up — cost\n   * no extra RPC. Callers must not pre-read this.\n   */\n  readCurrentOperationBtcPubkey: () => Promise<string>;\n  /**\n   * Sentence appended to the error naming what was aborted, e.g.\n   * `\"Aborting refund.\"`. The shared half of the message says which keys\n   * failed to match; this says which operation the user just lost.\n   */\n  context?: string;\n}\n\n/**\n * Assert an indexer-hinted vault provider key is one the chain can explain.\n *\n * Resolves silently when there is no hint, or when the hint matches either\n * candidate. Throws otherwise — the caller's key material is unaffected either\n * way, since resolution is chain-only.\n */\nexport async function assertVaultProviderHintAccepted(\n  params: AssertVaultProviderHintAcceptedParams,\n): Promise<void> {\n  const {\n    vaultProviderEthAddress,\n    hintBtcPubkey,\n    registrationBtcPubkey,\n    readCurrentOperationBtcPubkey,\n    context,\n  } = params;\n\n  if (!hintBtcPubkey) return;\n\n  const canonicalHint = canonicalizeBtcPubkey(hintBtcPubkey);\n  if (canonicalHint === canonicalizeBtcPubkey(registrationBtcPubkey)) return;\n\n  const canonicalOperation = canonicalizeBtcPubkey(\n    await readCurrentOperationBtcPubkey(),\n  );\n  if (canonicalHint === canonicalOperation) return;\n\n  throw new Error(\n    `Vault provider BTC pubkey mismatch for ${vaultProviderEthAddress}: ` +\n      `indexer hint matches neither the registration key nor the current ` +\n      `operation key on-chain.${context ? ` ${context}` : \"\"}`,\n  );\n}\n","/**\n * RFC-006 participant operation-key resolution.\n *\n * Under RFC-006 an operator's BTC key is no longer fixed at registration: it\n * is an append-only history of *operation* keys, rotated by the operator's\n * cold ETH admin key. Every consumer that needs \"the key this participant\n * signs with\" must resolve it, in one of two modes:\n *\n * - **current** — for a peg-in being built now, and for the VP auth pin.\n *   Needs no epoch read; each registry resolves its own genesis fallback, so\n *   an operator that never rotated yields its registration key.\n * - **at epochs** — for any vault that already exists. The vault froze its\n *   epochs at `submitPeginRequest`, and resolving against them is what keeps\n *   an old vault signable after a later rotation.\n *\n * Both modes go through the same `finalize` pass, which is where the\n * rotation-specific safety checks live.\n *\n * @module services/participants/resolveParticipantKeys\n */\n\nimport { assertOnChainBtcPubkey } from \"../../clients/eth/onChainBtcPubkey\";\nimport type {\n  KeyEpochs,\n  OperationKeyQuery,\n  OperationKeyReader,\n  RawOperationKeys,\n} from \"../../clients/eth/types\";\nimport type {\n  KeyResolutionMode,\n  ParticipantKeySet,\n  ResolvedParticipant,\n} from \"./types\";\n\n/** Role label used in validation error messages. */\ntype Role = \"vault provider\" | \"vault keeper\" | \"universal challenger\";\n\nfunction resolveOne(\n  role: Role,\n  rosterEntry: { ethAddress: `0x${string}`; btcPubKey: `0x${string}` },\n  rawOperationKey: `0x${string}`,\n): ResolvedParticipant {\n  const label = `${role} operation key (admin=${rosterEntry.ethAddress})`;\n  const genesisBtcPubkey = assertOnChainBtcPubkey(\n    rosterEntry.btcPubKey,\n    `${role} roster key (admin=${rosterEntry.ethAddress})`,\n  );\n  const operationBtcPubkey = assertOnChainBtcPubkey(rawOperationKey, label);\n\n  return {\n    adminAddress: rosterEntry.ethAddress,\n    genesisBtcPubkey,\n    operationBtcPubkey,\n    rotated: operationBtcPubkey !== genesisBtcPubkey,\n  };\n}\n\n/**\n * Assert no two participants resolved to the same operation key.\n *\n * The on-chain guarantees are narrower than \"registration keys are distinct\",\n * and narrower than an earlier revision of this comment claimed. Each registry\n * enforces uniqueness *within its own role*, on rotation as well as\n * registration, and a VP rotation additionally rejects any key held in the\n * current vault-keeper or universal-challenger rosters.\n *\n * What no contract checks is the cross-role direction those rules leave open:\n * a keeper or challenger rotating **onto** the VP's key (the VP-side check runs\n * at VP rotation time and does not run again when a keeper moves), a keeper and\n * a challenger colliding with each other, and keepers of different applications\n * colliding. Any of those would collapse two entries in the sorted script key\n * set and silently build a lock with the wrong participant count, so they are\n * rejected here — before any script is constructed — rather than surfacing as a\n * confusing signature mismatch much later.\n */\nfunction assertDistinctOperationKeys(\n  participants: ResolvedParticipant[],\n): void {\n  const seen = new Map<string, string>();\n  for (const participant of participants) {\n    const previousOwner = seen.get(participant.operationBtcPubkey);\n    if (previousOwner) {\n      throw new Error(\n        `Participant operation key collision: ${previousOwner} and ` +\n          `${participant.adminAddress} both resolve to operation key ` +\n          `${participant.operationBtcPubkey}`,\n      );\n    }\n    seen.set(participant.operationBtcPubkey, participant.adminAddress);\n  }\n}\n\nfunction finalize(\n  query: OperationKeyQuery,\n  raw: RawOperationKeys,\n  resolvedAt: KeyResolutionMode,\n): ParticipantKeySet {\n  if (\n    raw.vaultKeepers.length !== query.vaultKeepers.length ||\n    raw.universalChallengers.length !== query.universalChallengers.length\n  ) {\n    throw new Error(\n      `Operation-key resolution returned ${raw.vaultKeepers.length} keeper and ` +\n        `${raw.universalChallengers.length} challenger keys for a roster of ` +\n        `${query.vaultKeepers.length} keepers and ` +\n        `${query.universalChallengers.length} challengers`,\n    );\n  }\n\n  const vaultProvider = resolveOne(\n    \"vault provider\",\n    {\n      ethAddress: query.vaultProviderEthAddress,\n      btcPubKey: query.vaultProviderGenesisBtcPubkey,\n    },\n    raw.vaultProvider,\n  );\n\n  const vaultKeepers = query.vaultKeepers.map((keeper, i) =>\n    resolveOne(\"vault keeper\", keeper, raw.vaultKeepers[i]),\n  );\n  const universalChallengers = query.universalChallengers.map((challenger, i) =>\n    resolveOne(\"universal challenger\", challenger, raw.universalChallengers[i]),\n  );\n\n  assertDistinctOperationKeys([\n    vaultProvider,\n    ...vaultKeepers,\n    ...universalChallengers,\n  ]);\n\n  return {\n    vaultProvider,\n    vaultKeepers,\n    universalChallengers,\n    // Derived from the pairs, never the reverse — see ParticipantKeySet.\n    vaultKeeperOperationKeysSorted: vaultKeepers\n      .map((p) => p.operationBtcPubkey as string)\n      .sort(),\n    universalChallengerOperationKeysSorted: universalChallengers\n      .map((p) => p.operationBtcPubkey as string)\n      .sort(),\n    resolvedAt,\n    query,\n  };\n}\n\n/**\n * Resolve every participant's *current* operation key.\n *\n * Use for a peg-in being built now. Issues no epoch read, so it never touches\n * the extended `getBtcVaultProtocolInfo` ABI.\n */\nexport async function resolveCurrentParticipantKeys(params: {\n  operationKeyReader: OperationKeyReader;\n  query: OperationKeyQuery;\n}): Promise<ParticipantKeySet> {\n  const raw = await params.operationKeyReader.getCurrentOperationKeys(\n    params.query,\n  );\n  return finalize(params.query, raw, { mode: \"current\" });\n}\n\n/**\n * Resolve every participant's operation key bonded at a vault's frozen epochs.\n *\n * Use for every existing-vault path: resume, payout signing, refund. The\n * rosters in `query` must be read at the vault's frozen *membership* versions,\n * because those roster keys are the genesis the keeper/challenger getters fall\n * back to.\n */\nexport async function resolveParticipantKeysAtEpochs(params: {\n  operationKeyReader: OperationKeyReader;\n  query: OperationKeyQuery;\n  epochs: KeyEpochs;\n}): Promise<ParticipantKeySet> {\n  const raw = await params.operationKeyReader.getOperationKeysAtEpochs(\n    params.query,\n    params.epochs,\n  );\n  return finalize(params.query, raw, {\n    mode: \"epochs\",\n    epochs: params.epochs,\n  });\n}\n","import type { Address, Hex } from \"viem\";\n\nimport type {\n  OperationKeyReader,\n  UniversalChallengerReader,\n  VaultKeeperReader,\n  VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { canonicalizeBtcPubkey } from \"../../primitives/utils/bitcoin\";\nimport {\n  type HintMatch,\n  isHintAccepted,\n  matchKeyHint,\n  matchKeySetHint,\n} from \"../participants/indexerKeyHint\";\nimport { resolveCurrentParticipantKeys } from \"../participants/resolveParticipantKeys\";\nimport type { ParticipantKeySet } from \"../participants/types\";\n\nexport interface ValidateOnChainParticipantKeysParams {\n  vaultRegistryReader: VaultRegistryReader;\n  vaultKeeperReader: VaultKeeperReader;\n  universalChallengerReader: UniversalChallengerReader;\n  vaultProviderEthAddress: Address;\n  applicationEntryPoint: Address;\n  expectedVaultProviderBtcPubkey: string;\n  expectedVaultKeeperBtcPubkeys: string[];\n  expectedUniversalChallengerBtcPubkeys: string[];\n  /**\n   * RFC-006. Participant keys are resolved to their *current operation* keys,\n   * and those are what the returned key fields carry.\n   */\n  operationKeyReader: OperationKeyReader;\n  /**\n   * Optional observer for the case where the indexer hint matched the\n   * operation keys rather than the registration keys — i.e. the indexer is\n   * ahead of us, not wrong. Called at most once.\n   */\n  onIndexerServingOperationKeys?: (message: string) => void;\n  /**\n   * Optional observer for the case where the indexer is serving a half-applied\n   * view — one role explainable only by the registration keys, another only by\n   * the operation keys. That blocks every deposit for the provider until the\n   * indexer converges, and \"Refresh and try again\" cannot help, so the block\n   * needs to be visible rather than showing up only as user reports. Called\n   * immediately before the throw.\n   */\n  onIndexerHintsInconsistent?: (message: string) => void;\n}\n\nexport interface ValidatedOnChainParticipantKeys {\n  /** The VP key to build with: its current operation key. */\n  vaultProviderBtcPubkeyXOnly: string;\n  vaultKeeperBtcPubkeysSorted: string[];\n  universalChallengerBtcPubkeysSorted: string[];\n  expectedAppVaultKeepersVersion: number;\n  expectedUniversalChallengersVersion: number;\n  /**\n   * The registration / roster keys, sorted. These are what indexer hints are\n   * compared against first, and they stay available for diagnostics after\n   * resolution.\n   */\n  registrationKeys: {\n    vaultProvider: string;\n    vaultKeepers: string[];\n    universalChallengers: string[];\n  };\n  /**\n   * The full resolution, including the admin↔key pairing. Feeds the\n   * post-registration read-after-mine verification.\n   */\n  participantKeys: ParticipantKeySet;\n}\n\nconst sortedSet = (keys: string[]) => keys.map(canonicalizeBtcPubkey).sort();\n\nexport async function validateOnChainParticipantKeys(\n  params: ValidateOnChainParticipantKeysParams,\n): Promise<ValidatedOnChainParticipantKeys> {\n  const {\n    vaultRegistryReader,\n    vaultKeeperReader,\n    universalChallengerReader,\n    vaultProviderEthAddress,\n    applicationEntryPoint,\n    expectedVaultProviderBtcPubkey,\n    expectedVaultKeeperBtcPubkeys,\n    expectedUniversalChallengerBtcPubkeys,\n    operationKeyReader,\n    onIndexerServingOperationKeys,\n    onIndexerHintsInconsistent,\n  } = params;\n\n  const [\n    onChainVpKey,\n    expectedAppVaultKeepersVersion,\n    expectedUniversalChallengersVersion,\n  ] = await Promise.all([\n    vaultRegistryReader.getVaultProviderGenesisBtcPubKey(\n      vaultProviderEthAddress,\n    ),\n    vaultKeeperReader.getCurrentVaultKeepersVersion(applicationEntryPoint),\n    universalChallengerReader.getLatestUniversalChallengersVersion(),\n  ]);\n\n  const [onChainKeepers, onChainChallengers] = await Promise.all([\n    vaultKeeperReader.getVaultKeepersByVersion(\n      applicationEntryPoint,\n      expectedAppVaultKeepersVersion,\n    ),\n    universalChallengerReader.getUniversalChallengersByVersion(\n      expectedUniversalChallengersVersion,\n    ),\n  ]);\n\n  const registrationKeys = {\n    vaultProvider: canonicalizeBtcPubkey(onChainVpKey),\n    vaultKeepers: sortedSet(onChainKeepers.map((p) => p.btcPubKey)),\n    universalChallengers: sortedSet(onChainChallengers.map((p) => p.btcPubKey)),\n  };\n\n  // Resolve the current operation keys. This is what we build the Bitcoin lock\n  // with; the registration keys above stay only as the primary comparison\n  // target for indexer hints.\n  //\n  // Read unconditionally, which is why this path cannot use\n  // `assertVaultProviderHintAccepted` — that helper reads the operation key\n  // lazily, for callers that only need it to explain a hint mismatch.\n  const participantKeys: ParticipantKeySet =\n    await resolveCurrentParticipantKeys({\n      operationKeyReader,\n      query: {\n        vaultProviderEthAddress,\n        vaultProviderGenesisBtcPubkey: `0x${onChainVpKey}` as Hex,\n        applicationEntryPoint,\n        vaultKeepers: onChainKeepers,\n        universalChallengers: onChainChallengers,\n      },\n    });\n\n  const operationKeys = {\n    vaultProvider: participantKeys.vaultProvider.operationBtcPubkey as string,\n    vaultKeepers: [...participantKeys.vaultKeeperOperationKeysSorted],\n    universalChallengers: [\n      ...participantKeys.universalChallengerOperationKeysSorted,\n    ],\n  };\n\n  // --- Indexer hint cross-check -----------------------------------------\n  //\n  // The hint never influences the resolved key — resolution is chain-only.\n  // Its job is to catch a wrong VP address, a wrong application entry point,\n  // or a stale roster version, and it still does that.\n  //\n  // Once rotation is possible the indexer may legitimately serve either the\n  // registration keys (it has not caught up) or the operation keys (it has),\n  // so each role is accepted against both candidates — see `indexerKeyHint`,\n  // which owns that policy and the whole-set matching rule. The cross-role\n  // check below closes the same hole one level up.\n  const vpMatch: HintMatch = matchKeyHint(\n    expectedVaultProviderBtcPubkey,\n    registrationKeys.vaultProvider,\n    operationKeys.vaultProvider,\n  );\n  const keeperMatch: HintMatch = matchKeySetHint(\n    expectedVaultKeeperBtcPubkeys,\n    registrationKeys.vaultKeepers,\n    operationKeys.vaultKeepers,\n  );\n  const challengerMatch: HintMatch = matchKeySetHint(\n    expectedUniversalChallengerBtcPubkeys,\n    registrationKeys.universalChallengers,\n    operationKeys.universalChallengers,\n  );\n\n  if (!isHintAccepted(vpMatch)) {\n    throw new Error(\n      `Vault provider BTC pubkey indexer hint does not match BTCVaultRegistry for ${vaultProviderEthAddress}. Refresh and try again.`,\n    );\n  }\n  if (!isHintAccepted(keeperMatch)) {\n    throw new Error(\n      `Vault keeper BTC pubkeys (v${expectedAppVaultKeepersVersion}) indexer set does not match ApplicationRegistry on-chain set. Refresh and try again.`,\n    );\n  }\n  if (!isHintAccepted(challengerMatch)) {\n    throw new Error(\n      `Universal challenger BTC pubkeys (v${expectedUniversalChallengersVersion}) indexer set does not match ProtocolParams on-chain set. Refresh and try again.`,\n    );\n  }\n\n  // Cross-role consistency. A role whose two candidates are identical (nobody\n  // in it rotated) matches both and constrains nothing. But if one role can\n  // *only* be explained by the registration keys while another can *only* be\n  // explained by the operation keys, the indexer is serving a half-applied\n  // view — accepting that would mean building with a key set no single\n  // snapshot of the indexer ever held.\n  const roles = [vpMatch, keeperMatch, challengerMatch];\n  const pinsRegistration = roles.some((m) => m.registration && !m.operation);\n  const pinsOperation = roles.some((m) => m.operation && !m.registration);\n\n  if (pinsRegistration && pinsOperation) {\n    const message =\n      `Indexer participant hints are internally inconsistent for vault provider ` +\n      `${vaultProviderEthAddress}: some roles match the registration keys while ` +\n      `others match the rotated operation keys.`;\n    // Report before throwing. This state blocks every deposit for the provider\n    // and clears only when the indexer converges across all three registries —\n    // nothing the user does resolves it, so it has to be observable to us.\n    onIndexerHintsInconsistent?.(message);\n    throw new Error(`${message} Refresh and try again.`);\n  }\n\n  if (pinsOperation) {\n    onIndexerServingOperationKeys?.(\n      `Indexer is serving rotated operation keys for vault provider ${vaultProviderEthAddress}`,\n    );\n  }\n\n  return {\n    vaultProviderBtcPubkeyXOnly: operationKeys.vaultProvider,\n    vaultKeeperBtcPubkeysSorted: operationKeys.vaultKeepers,\n    universalChallengerBtcPubkeysSorted: operationKeys.universalChallengers,\n    expectedAppVaultKeepersVersion,\n    expectedUniversalChallengersVersion,\n    registrationKeys,\n    participantKeys,\n  };\n}\n","/**\n * Pure validation functions for deposit operations.\n *\n * All validations return a consistent {@link ValidationResult} format or throw\n * on critical failures (e.g. missing protocol participants).\n *\n * Business rules (single-provider limit, max vault count) and form-flow\n * checks (wallet connected) belong in the consumer layer.\n *\n * @module tbv/core/services/deposit/validation\n */\n\nimport {\n  formatSatoshisToBtc,\n  stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ValidationResult {\n  valid: boolean;\n  error?: string;\n  warnings?: string[];\n}\n\n/**\n * Parameters for checking if a deposit form is valid.\n */\nexport interface DepositFormValidityParams {\n  /** Deposit amount in satoshis */\n  amountSats: bigint;\n  /** Minimum deposit from protocol params */\n  minDeposit: bigint;\n  /** Maximum deposit from protocol params (optional) */\n  maxDeposit?: bigint;\n  /** User's available BTC balance in satoshis */\n  btcBalance: bigint;\n  /** Estimated transaction fee in satoshis */\n  estimatedFeeSats?: bigint;\n  /** Depositor claim value in satoshis (required output for challenge transactions) */\n  depositorClaimValue?: bigint;\n}\n\nexport interface RemainingCapacityParams {\n  /** Requested deposit amount in satoshis */\n  amount: bigint;\n  /**\n   * Effective remaining capacity in satoshis (min of protocol-total and\n   * per-address remaining). `null` means no cap applies.\n   */\n  effectiveRemaining: bigint | null;\n}\n\n/** Narrow structural type for UTXO — avoids importing vault-specific types. */\ninterface UtxoLike {\n  txid: string;\n  vout: number;\n  value: number;\n}\n\n/**\n * Parameters for validating multi-vault deposit flow inputs.\n *\n * Callers must resolve any async loading states before calling — the SDK\n * validates resolved data, not React hook state.\n *\n * Form-flow checks (wallet connected, provider selected) are the caller's\n * responsibility and are NOT performed here.\n */\nexport interface MultiVaultDepositFlowInputs {\n  vaultAmounts: bigint[];\n  confirmedUTXOs: UtxoLike[];\n  vaultProviderBtcPubkey: string;\n  vaultKeeperBtcPubkeys: string[];\n  universalChallengerBtcPubkeys: string[];\n  /** Protocol minimum deposit per vault (satoshis) */\n  minDeposit: bigint;\n  /** Protocol maximum deposit per vault (satoshis) */\n  maxDeposit?: bigint;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction isValidXOnlyHex(hex: string): boolean {\n  return /^[0-9a-fA-F]{64}$/.test(hex);\n}\n\n// ---------------------------------------------------------------------------\n// Validation functions\n// ---------------------------------------------------------------------------\n\n/**\n * Check if deposit amount is within valid range and affordable.\n *\n * Returns false when fees/claim value are not yet known (still loading),\n * and includes them in the balance check once available.\n */\nexport function isDepositAmountValid(\n  params: DepositFormValidityParams,\n): boolean {\n  const {\n    amountSats,\n    minDeposit,\n    maxDeposit,\n    btcBalance,\n    estimatedFeeSats,\n    depositorClaimValue,\n  } = params;\n\n  if (amountSats <= 0n) return false;\n  if (amountSats < minDeposit) return false;\n  if (maxDeposit && maxDeposit > 0n && amountSats > maxDeposit) return false;\n\n  if (estimatedFeeSats == null || depositorClaimValue == null) return false;\n\n  const totalRequired = amountSats + estimatedFeeSats + depositorClaimValue;\n  if (totalRequired > btcBalance) return false;\n\n  return true;\n}\n\n/**\n * Validate deposit amount against minimum and maximum constraints.\n */\nexport function validateDepositAmount(\n  amount: bigint,\n  minDeposit: bigint,\n  maxDeposit?: bigint,\n): ValidationResult {\n  if (amount <= 0n) {\n    return {\n      valid: false,\n      error: \"Deposit amount must be greater than zero\",\n    };\n  }\n\n  if (amount < minDeposit) {\n    return {\n      valid: false,\n      error: `Minimum deposit is ${formatSatoshisToBtc(minDeposit)} BTC`,\n    };\n  }\n\n  if (maxDeposit && maxDeposit > 0n && amount > maxDeposit) {\n    return {\n      valid: false,\n      error: `Maximum deposit is ${formatSatoshisToBtc(maxDeposit)} BTC`,\n    };\n  }\n\n  return { valid: true };\n}\n\n/**\n * Validate that the requested deposit fits within the effective remaining cap.\n */\nexport function validateRemainingCapacity(\n  params: RemainingCapacityParams,\n): ValidationResult {\n  const { amount, effectiveRemaining } = params;\n  if (effectiveRemaining === null) return { valid: true };\n\n  if (effectiveRemaining === 0n) {\n    return {\n      valid: false,\n      error: \"Supply cap reached — deposits temporarily paused\",\n    };\n  }\n\n  if (amount > effectiveRemaining) {\n    return {\n      valid: false,\n      error: `Vault size exceeds remaining capacity (${formatSatoshisToBtc(effectiveRemaining)} BTC)`,\n    };\n  }\n\n  return { valid: true };\n}\n\n/**\n * Validate that selected providers exist in the available set.\n *\n * Business rules (e.g. single-provider limit) are the caller's responsibility.\n */\nexport function validateProviderSelection(\n  selectedProviders: string[],\n  availableProviders: string[],\n): ValidationResult {\n  if (!selectedProviders || selectedProviders.length === 0) {\n    return {\n      valid: false,\n      error: \"At least one vault provider must be selected\",\n    };\n  }\n\n  const availableProvidersLower = availableProviders.map((p) =>\n    p.toLowerCase(),\n  );\n  const invalidProviders = selectedProviders.filter(\n    (p) => !availableProvidersLower.includes(p.toLowerCase()),\n  );\n\n  if (invalidProviders.length > 0) {\n    return {\n      valid: false,\n      error: \"Invalid vault provider selected\",\n    };\n  }\n\n  return { valid: true };\n}\n\n/**\n * Validate vault amounts array for multi-vault deposits.\n * Checks count, positivity, and per-vault min/max protocol limits.\n *\n * Max vault count limits are the caller's responsibility.\n */\nexport function validateVaultAmounts(\n  amounts: bigint[],\n  minDeposit?: bigint,\n  maxDeposit?: bigint,\n): ValidationResult {\n  if (!amounts || amounts.length === 0) {\n    return {\n      valid: false,\n      error: \"At least one vault amount required\",\n    };\n  }\n\n  for (let i = 0; i < amounts.length; i++) {\n    const amount = amounts[i];\n    if (amount <= 0n) {\n      return {\n        valid: false,\n        error: `Vault ${i + 1} amount must be positive`,\n      };\n    }\n    if (minDeposit && amount < minDeposit) {\n      return {\n        valid: false,\n        error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC is below minimum deposit ${formatSatoshisToBtc(minDeposit)} BTC`,\n      };\n    }\n    if (maxDeposit && amount > maxDeposit) {\n      return {\n        valid: false,\n        error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC exceeds maximum deposit ${formatSatoshisToBtc(maxDeposit)} BTC`,\n      };\n    }\n  }\n\n  return { valid: true };\n}\n\n/**\n * Validate vault provider BTC public key format.\n */\nexport function validateVaultProviderPubkey(pubkey: string): ValidationResult {\n  const stripped = stripHexPrefix(pubkey);\n  if (!isValidXOnlyHex(stripped)) {\n    return {\n      valid: false,\n      error:\n        \"Invalid pubkey format: must be 64 hex characters (32-byte x-only public key, no 0x prefix)\",\n    };\n  }\n  return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers for multi-vault validation\n// ---------------------------------------------------------------------------\n\nfunction validateVaultKeepers(vaultKeeperBtcPubkeys: string[]): void {\n  if (!vaultKeeperBtcPubkeys || vaultKeeperBtcPubkeys.length === 0) {\n    throw new Error(\n      \"No vault keepers available. The system requires at least one vault keeper to create a deposit.\",\n    );\n  }\n}\n\nfunction validateUniversalChallengers(\n  universalChallengerBtcPubkeys: string[],\n): void {\n  if (\n    !universalChallengerBtcPubkeys ||\n    universalChallengerBtcPubkeys.length === 0\n  ) {\n    throw new Error(\n      \"No universal challengers available. The system requires at least one universal challenger to create a deposit.\",\n    );\n  }\n}\n\nfunction validateUTXOState(confirmedUTXOs: UtxoLike[]): void {\n  if (confirmedUTXOs.length === 0) {\n    throw new Error(\"No spendable UTXOs available\");\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Multi-vault composite validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validate protocol-level multi-vault deposit inputs.\n * Throws an error if any validation fails.\n *\n * Form-flow checks (wallet connections, provider selection) must be\n * performed by the caller before invoking this function.\n */\nexport function validateMultiVaultDepositInputs(\n  params: MultiVaultDepositFlowInputs,\n): void {\n  const {\n    vaultAmounts,\n    confirmedUTXOs,\n    vaultProviderBtcPubkey,\n    vaultKeeperBtcPubkeys,\n    universalChallengerBtcPubkeys,\n    minDeposit,\n    maxDeposit,\n  } = params;\n\n  const amountsValidation = validateVaultAmounts(\n    vaultAmounts,\n    minDeposit,\n    maxDeposit,\n  );\n  if (!amountsValidation.valid) {\n    throw new Error(amountsValidation.error);\n  }\n\n  // Vault provider pubkey\n  const pubkeyValidation = validateVaultProviderPubkey(vaultProviderBtcPubkey);\n  if (!pubkeyValidation.valid) {\n    throw new Error(pubkeyValidation.error);\n  }\n\n  validateVaultKeepers(vaultKeeperBtcPubkeys);\n  validateUniversalChallengers(universalChallengerBtcPubkeys);\n  validateUTXOState(confirmedUTXOs);\n}\n","/**\n * RFC-006 read-after-mine verification for a freshly registered vault.\n *\n * A new peg-in is built with each participant's *current* operation key, but\n * the vault does not freeze its key epochs until `submitPeginRequest` executes.\n * An operator rotating in that window would leave the registered vault bonded\n * to keys other than the ones baked into the Bitcoin lock we are about to\n * broadcast — BTC locked into a script no counterparty will presign.\n *\n * So after the registration mines and before the BTC broadcast, re-read the\n * vault's frozen epochs, re-resolve every participant against them, and assert\n * the result is byte-identical to what we built with.\n *\n * Deliberately a sibling of `verifyRegisteredVaultVersions` rather than an\n * extension of it: that function reads `getProtocolInfoBatch` through the\n * shared 13-field ABI and must stay there, while this one needs the extended\n * key-epoch ABI.\n *\n * @module services/deposit/verifyRegisteredParticipantKeys\n */\n\nimport type { Hex } from \"viem\";\n\nimport type {\n  OperationKeyReader,\n  VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { resolveParticipantKeysAtEpochs } from \"../participants/resolveParticipantKeys\";\nimport type { ParticipantKeySet } from \"../participants/types\";\n\n/**\n * Participant operation keys drifted between building the Bitcoin artifacts\n * and the vault freezing its epochs.\n *\n * A *sibling* of `RegisteredVaultVersionMismatchError`, never a subclass, and\n * the distinction is load-bearing. On a version mismatch the orchestrator drops\n * the local pending-pegin record, because the on-chain `prePeginTxHash` is\n * still the authoritative copy of the transaction and a later resume can safely\n * broadcast it from the indexer.\n *\n * Key drift breaks exactly that assumption. The registered hash commits to a\n * transaction whose scripts embed the *pre-rotation* keys, while the vault\n * froze the *post-rotation* epoch — so every counterparty resolves a different\n * funding output and the deposit can never activate. Dropping the record would\n * discard `buildParticipantOperationKeys`, the only thing that lets the resume\n * path re-detect the drift; the next attempt would fall back to the indexer's\n * copy, pass the hash check, and broadcast the very transaction this refused,\n * locking BTC until the refund timelock.\n *\n * So: callers must keep the pending record when they catch this.\n */\nexport class ParticipantKeyDriftError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"ParticipantKeyDriftError\";\n  }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field, as the version guard does.\nexport function isParticipantKeyDriftError(\n  err: unknown,\n): err is ParticipantKeyDriftError {\n  return (\n    err instanceof ParticipantKeyDriftError ||\n    (err instanceof Error && err.name === \"ParticipantKeyDriftError\")\n  );\n}\n\nexport interface VerifyRegisteredParticipantKeysParams {\n  vaultRegistryReader: VaultRegistryReader;\n  operationKeyReader: OperationKeyReader;\n  vaultIds: readonly Hex[];\n  /**\n   * The exact key set the BTC artifacts were built with. Its `query` supplies\n   * the rosters to re-resolve against — deliberately reused rather than\n   * accepted as a separate argument, so the two can never disagree and a\n   * roster that moved since the build cannot be misreported as a key drift.\n   */\n  expected: ParticipantKeySet;\n}\n\nfunction diffKeys(\n  label: string,\n  expected: readonly string[],\n  actual: readonly string[],\n): string | null {\n  if (\n    expected.length === actual.length &&\n    expected.every((k, i) => k === actual[i])\n  ) {\n    return null;\n  }\n  return `${label} expected [${expected.join(\", \")}], got [${actual.join(\", \")}]`;\n}\n\nexport async function verifyRegisteredParticipantKeys(\n  params: VerifyRegisteredParticipantKeysParams,\n): Promise<void> {\n  const { vaultRegistryReader, operationKeyReader, vaultIds, expected } =\n    params;\n  const query = expected.query;\n\n  if (vaultIds.length === 0) return;\n\n  // Batch registration gives every sibling vault the same epochs, so these\n  // reads are redundant across siblings. Kept per-vault anyway: it is one\n  // multicall, and asserting each vault individually is what makes the error\n  // name the vault that drifted.\n  const epochsPerVault =\n    await vaultRegistryReader.getVaultKeyEpochsBatch(vaultIds);\n\n  const mismatches: string[] = [];\n\n  for (const [i, epochs] of epochsPerVault.entries()) {\n    const vaultId = vaultIds[i];\n\n    let resolved: ParticipantKeySet;\n    try {\n      resolved = await resolveParticipantKeysAtEpochs({\n        operationKeyReader,\n        query,\n        epochs,\n      });\n    } catch (error) {\n      // A resolution failure here is itself a mismatch signal: the frozen\n      // epochs point at a key set we cannot reconstruct, so broadcasting\n      // would be worse than aborting.\n      mismatches.push(\n        `vault ${vaultId}: could not resolve participants at frozen epochs ` +\n          `(vp=${epochs.vpKeyEpoch}, keeper=${epochs.appKeeperKeyEpoch}, ` +\n          `uc=${epochs.ucKeyEpoch}): ${(error as Error).message}`,\n      );\n      continue;\n    }\n\n    if (\n      resolved.vaultProvider.operationBtcPubkey !==\n      expected.vaultProvider.operationBtcPubkey\n    ) {\n      mismatches.push(\n        `vault ${vaultId}: vault provider key expected ` +\n          `${expected.vaultProvider.operationBtcPubkey}, got ` +\n          `${resolved.vaultProvider.operationBtcPubkey}`,\n      );\n    }\n\n    const keeperDiff = diffKeys(\n      `vault ${vaultId}: vault keeper keys`,\n      expected.vaultKeeperOperationKeysSorted,\n      resolved.vaultKeeperOperationKeysSorted,\n    );\n    if (keeperDiff) mismatches.push(keeperDiff);\n\n    const challengerDiff = diffKeys(\n      `vault ${vaultId}: universal challenger keys`,\n      expected.universalChallengerOperationKeysSorted,\n      resolved.universalChallengerOperationKeysSorted,\n    );\n    if (challengerDiff) mismatches.push(challengerDiff);\n  }\n\n  if (mismatches.length > 0) {\n    throw new ParticipantKeyDriftError(\n      `Aborting BTC broadcast: participant operation keys changed during registration ` +\n        `(${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ` +\n        `ETH vault will time out per protocol rules.`,\n    );\n  }\n}\n","import type { Hex } from \"viem\";\n\nimport type { VaultRegistryReader } from \"../../clients/eth/types\";\n\nexport interface VerifyRegisteredVaultVersionsParams {\n  vaultRegistryReader: VaultRegistryReader;\n  vaultIds: readonly Hex[];\n  expectedOffchainParamsVersion: number;\n  expectedAppVaultKeepersVersion: number;\n  expectedUniversalChallengersVersion: number;\n  /**\n   * Vault core (tx-graph) version the BTC artifacts were BUILT with. The\n   * contract stamps `activeVaultCoreVersion` at registration-tx execution\n   * time, so a governance flip between build and registration stamps a\n   * different graph than the one the depositor signed — broadcasting would\n   * lock BTC into a graph no resume path can rebuild.\n   */\n  expectedVaultCoreVersion: number;\n}\n\n// Distinct from a transient RPC failure: the orchestrator removes pending\n// pegin entries only when a real mismatch is confirmed on-chain.\nexport class RegisteredVaultVersionMismatchError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"RegisteredVaultVersionMismatchError\";\n  }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field so the cleanup path stays reliable.\nexport function isRegisteredVaultVersionMismatchError(\n  err: unknown,\n): err is RegisteredVaultVersionMismatchError {\n  return (\n    err instanceof RegisteredVaultVersionMismatchError ||\n    (err instanceof Error && err.name === \"RegisteredVaultVersionMismatchError\")\n  );\n}\n\nexport async function verifyRegisteredVaultVersions(\n  params: VerifyRegisteredVaultVersionsParams,\n): Promise<void> {\n  const {\n    vaultRegistryReader,\n    vaultIds,\n    expectedOffchainParamsVersion,\n    expectedAppVaultKeepersVersion,\n    expectedUniversalChallengersVersion,\n    expectedVaultCoreVersion,\n  } = params;\n\n  const infos = await vaultRegistryReader.getProtocolInfoBatch(vaultIds);\n\n  const mismatches: string[] = [];\n  infos.forEach((v, i) => {\n    const id = vaultIds[i];\n    if (v.offchainParamsVersion !== expectedOffchainParamsVersion) {\n      mismatches.push(\n        `vault ${id}: offchainParams expected v${expectedOffchainParamsVersion}, got v${v.offchainParamsVersion}`,\n      );\n    }\n    if (v.appVaultKeepersVersion !== expectedAppVaultKeepersVersion) {\n      mismatches.push(\n        `vault ${id}: appVaultKeepers expected v${expectedAppVaultKeepersVersion}, got v${v.appVaultKeepersVersion}`,\n      );\n    }\n    if (v.universalChallengersVersion !== expectedUniversalChallengersVersion) {\n      mismatches.push(\n        `vault ${id}: universalChallengers expected v${expectedUniversalChallengersVersion}, got v${v.universalChallengersVersion}`,\n      );\n    }\n    if (v.vaultCoreVersion !== expectedVaultCoreVersion) {\n      mismatches.push(\n        `vault ${id}: vaultCoreVersion expected v${expectedVaultCoreVersion} (build-time active), got v${v.vaultCoreVersion}`,\n      );\n    }\n  });\n\n  if (mismatches.length > 0) {\n    throw new RegisteredVaultVersionMismatchError(\n      `Aborting BTC broadcast: signer-set or offchain-params versions changed during registration (${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ETH vault will time out per protocol rules.`,\n    );\n  }\n}\n","/**\n * Pegout state definitions and protocol-level terminal checks.\n *\n * Maps VP-reported pegout statuses from `vaultProvider_batchGetPegoutStatus`\n * to protocol lifecycle states.\n *\n * Lifecycle (pegin-level, see btc-vault mod.rs PegoutStatus):\n *   ClaimEventReceived -> ClaimBroadcast -> AssertBroadcast ->\n *     PayoutBroadcast (success) | PayoutBlocked (NoPayout / CouncilNoPayout)\n */\n\n/** Claimer-side pegout statuses reported by the VP. */\nexport enum ClaimerPegoutStatusValue {\n  CLAIM_EVENT_RECEIVED = \"ClaimEventReceived\",\n  CLAIM_BROADCAST = \"ClaimBroadcast\",\n  ASSERT_BROADCAST = \"AssertBroadcast\",\n  PAYOUT_BROADCAST = \"PayoutBroadcast\",\n  PAYOUT_BLOCKED = \"PayoutBlocked\",\n}\n\nconst PEGOUT_TERMINAL_STATUSES = new Set<string>([\n  ClaimerPegoutStatusValue.PAYOUT_BROADCAST,\n  ClaimerPegoutStatusValue.PAYOUT_BLOCKED,\n]);\n\n/** Whether a claimer status string maps to a known pegout state. */\nexport function isRecognizedPegoutStatus(status: string): boolean {\n  return Object.values(ClaimerPegoutStatusValue).includes(\n    status as ClaimerPegoutStatusValue,\n  );\n}\n\n/**\n * Whether a claimer status is a hard-terminal pegout status\n * (PayoutBroadcast or PayoutBlocked). Soft-terminal conditions (polling\n * thresholds) are a consumer-side concern.\n */\nexport function isPegoutTerminalStatus(\n  claimerStatus: string | undefined,\n): boolean {\n  return !!claimerStatus && PEGOUT_TERMINAL_STATUSES.has(claimerStatus);\n}\n","/**\n * Domain errors thrown by the refund service.\n *\n * @module services/refund/errors\n */\n\nimport type { Hex } from \"viem\";\n\n/**\n * Thrown when the broadcast transport rejects the refund tx because the CSV\n * timelock has not yet matured (BIP68 non-final). Callers can surface a\n * friendly \"wait until block N\" message; the original transport error is\n * available via {@link cause}.\n */\nexport class BIP68NotMatureError extends Error {\n  public readonly vaultId: Hex;\n  public override readonly cause: Error;\n\n  constructor(vaultId: Hex, cause: Error) {\n    super(`Refund not yet mature (BIP68 not final): ${cause.message}`);\n    this.name = \"BIP68NotMatureError\";\n    this.vaultId = vaultId;\n    this.cause = cause;\n  }\n}\n","/**\n * Vault refund orchestration — reclaim BTC from an expired Pre-PegIn HTLC via\n * the CSV-timelocked refund script (leaf 1). SDK owns the sequence of:\n * fetch → fee calc → PSBT build → sign → finalize → broadcast. Pre-fetched\n * data (fee rate) is passed by value; the data-flow-dependent reads\n * (`readVault`, `readPrePeginContext(vault)`) and the interactive transports\n * (`signPsbt`, `broadcastTx`) stay as injected callbacks so the caller keeps\n * its transport choice (viem, wagmi, mempool client, etc.) and error decoding.\n *\n * @module services/refund\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\nimport type { Address, Hex } from \"viem\";\n\nimport type { SignPsbtOptions } from \"../../../../shared/wallets/interfaces/BitcoinWallet\";\nimport { findAuthAnchorOpReturn } from \"../../managers/pegin\";\nimport { assertPsbtUnsignedTxMatches } from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport { extractPayoutSignature } from \"../../primitives/psbt/payout\";\nimport { buildRefundPsbt } from \"../../primitives/psbt/refund\";\nimport { assertScriptPathSchnorrSignature } from \"../../primitives/psbt/verifyScriptPathSchnorrSignature\";\nimport { assertValidVaultCoreVersion } from \"../../primitives/vaultCoreVersion\";\nimport {\n  processPublicKeyToXOnly,\n  stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\nimport { BIP68NotMatureError } from \"./errors\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\n// BTC raw-hex convention: 0x prefix optional, even number of hex chars, must\n// be non-empty. Named distinctly from the ETH-hex regex in activateVault.ts\n// (which requires a 0x prefix and allows empty \"0x\") to make the convention\n// explicit at the call site.\nconst BTC_HEX_BYTES_RE = /^(?:0x)?(?:[0-9a-fA-F]{2})+$/;\n// Pubkeys are either 32 bytes (x-only, 64 hex chars) or 33 bytes (compressed,\n// 66 hex chars). 65 hex chars is not a valid byte length — reject it here\n// rather than letting the malformed value surface as an opaque PSBT/signing\n// failure later.\nconst PUBKEY_HEX_RE = /^(?:0x)?(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{66})$/;\n// Conservative upper bound for the fixed-shape refund tx (1 P2TR script-path\n// input spending the HTLC refund leaf → 1 P2TR/P2WPKH output). Taproot\n// script-path witness: 64-byte Schnorr sig + refund script + control block.\n// This is protocol-owned knowledge; callers don't parameterise it.\nexport const REFUND_VSIZE = 160;\n\n// Hard upper bound on the per-vbyte fee rate the SDK will sign a refund at.\n// Defense-in-depth: a compromised mempool endpoint can legally return up to\n// 10_000 sat/vB (see mempoolApi.ts `MAX_FEE_RATE`), which on a 160-vbyte\n// refund would burn up to 1.6M sats in miner fees.\n//\n// Sizing: during the April 2024 halving / Runes launch `fastestFee` peaked\n// around 1,800 sat/vB, and `halfHourFee` tracked close to it during the\n// worst of the congestion (~1,000–1,500 sat/vB range — the half-hour\n// bucket converges with the fastest bucket when the queue is deep enough).\n// 2000 leaves ~1.3× margin over that historical extreme so the cap doesn't\n// gate legitimate refunds during a comparable event, while still blocking\n// the obvious malicious case (10_000) by 5×. Small-vault burn is bounded\n// separately by REFUND_MAX_FEE_FRACTION_* below, so the rate cap is free\n// to be set generously here.\nexport const REFUND_MAX_FEE_RATE_SATS_VB = 2000;\n\n// Hard upper bound on the absolute refund fee as a fraction of the vault\n// amount. Protects small vaults where even a moderate fee rate burns a\n// disproportionate share (e.g. on a 100k-sat vault, 500 sat/vB would burn\n// 80%). The fraction cap binds before the rate cap whenever the vault is\n// small. Expressed as numerator/denominator to keep arithmetic in bigint\n// and avoid float-precision drift in the comparison.\nexport const REFUND_MAX_FEE_FRACTION_NUMERATOR = 10n;\nexport const REFUND_MAX_FEE_FRACTION_DENOMINATOR = 100n;\n\n/**\n * Network fee (sats) the SDK will charge for a refund tx at the given\n * sat/vB rate. Mirrors the internal computation in\n * {@link buildAndBroadcastRefund} so callers (e.g. UI fee previews) don't\n * have to duplicate the constant.\n */\nexport function estimateRefundFeeSats(feeRateSatsVb: number): bigint {\n  if (!Number.isFinite(feeRateSatsVb) || feeRateSatsVb <= 0) {\n    throw new Error(\n      `feeRateSatsVb must be a positive finite number, got ${feeRateSatsVb}`,\n    );\n  }\n  return BigInt(Math.ceil(feeRateSatsVb * REFUND_VSIZE));\n}\n// Refund tx has exactly one input — the HTLC output at htlcVout from the\n// Pre-PegIn tx. Used to tell the signer how many sign entries to generate.\n// (Not the taproot leaf index; the leaf is encoded into the PSBT by the\n// WASM PSBT builder based on the refund script path.)\nconst REFUND_INPUT_COUNT = 1;\nconst BIP68_ERROR_RE = /non-BIP68-final/i;\n\nfunction assertBytes32(value: string, label: string): void {\n  if (value.length !== 66) {\n    throw new Error(\n      `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n    );\n  }\n  if (!BYTES32_HEX_RE.test(value)) {\n    throw new Error(\n      `${label} must contain only hex characters after the 0x prefix`,\n    );\n  }\n}\n\n/**\n * One vault's per-HTLC binding in a Pre-PegIn batch. Carries the fields\n * needed to reconstruct the WASM `WasmPrePeginTx` template byte-for-byte\n * against the funded transaction.\n */\nexport interface VaultBatchEntry {\n  /** SHA-256 hashlock commitment for this vault (bytes32, 0x-prefixed). */\n  hashlock: Hex;\n  /**\n   * Vault deposit (peg-in) amount in satoshis — the on-chain contract's\n   * `amount` field. This is the peg-in amount WASM expects in `pegInAmounts`,\n   * NOT the funded HTLC output value (which is `amount + depositorClaimValue +\n   * minPeginFee`). WASM re-adds that reserve internally when it sizes the HTLC\n   * output, so this value is passed straight through.\n   */\n  amount: bigint;\n  /** Index of this vault's HTLC output in the funded Pre-PegIn tx. */\n  htlcVout: number;\n}\n\n/**\n * Authoritative vault fields needed to build a refund. Versioning fields,\n * the hashlock, and htlcVout must come from the on-chain contract (never the\n * indexer). The amount + `unsignedPrePeginTxHex` + `depositorBtcPubkey` can\n * come from the indexer since they are not security-critical for signing\n * (the PSBT builder re-derives the HTLC script from on-chain params).\n *\n * `batch` is the full, vout-ordered HTLC vector for the Pre-PegIn (one\n * entry per sibling vault that shares this funded transaction). For a\n * single-vault deposit this is a length-1 array. For batched deposits\n * (e.g. the Aave split) the orchestrator passes every sibling through\n * so the WASM template matches the funded tx's shape.\n */\nexport interface VaultRefundData {\n  /**\n   * Vault core (tx-graph) version stamped on-chain at registration\n   * (`BTCVaultProtocolInfo.vaultCoreVersion`). The refund template must be\n   * reconstructed under the same graph version the Pre-PegIn was built with.\n   */\n  vaultCoreVersion: number;\n  hashlock: Hex;\n  htlcVout: number;\n  offchainParamsVersion: number;\n  appVaultKeepersVersion: number;\n  universalChallengersVersion: number;\n  vaultProvider: Address;\n  applicationEntryPoint: Address;\n  /** Vault deposit (peg-in) amount in satoshis — the on-chain `amount` field. */\n  amount: bigint;\n  /**\n   * Funded, pre-witness Pre-PegIn transaction hex. 0x prefix optional.\n   * The name mirrors the contract/indexer schema; the bytes are the\n   * funded form (refund construction needs real outpoints).\n   */\n  unsignedPrePeginTxHex: string;\n  /** Depositor's BTC public key (x-only or compressed hex; 0x prefix optional). */\n  depositorBtcPubkey: string;\n  /**\n   * Full vout-ordered HTLC vector for the funded Pre-PegIn (one entry\n   * per sibling vault, including the target vault). Must satisfy\n   * `batch[i].htlcVout === i` for all i, and the target's `htlcVout` /\n   * `hashlock` / `amount` must equal `batch[vault.htlcVout]`.\n   */\n  batch: ReadonlyArray<VaultBatchEntry>;\n}\n\n/**\n * Version-resolved protocol context that parameterises the HTLC's taproot\n * scripts. The *signer-set* fields (`vaultKeeperPubkeys`,\n * `universalChallengerPubkeys`) and the version-locked numeric protocol\n * params **must** be sourced from the on-chain contract at the version\n * pinned in {@link VaultRefundData} — this is the trust boundary.\n * `vaultProviderPubkey` today is sourced from the GraphQL indexer via\n * `fetchVaultProviderById`; the caller is responsible for any additional\n * cross-check it requires. Keeper and challenger pubkey arrays must be\n * pre-sorted the same way the Rust protocol sorts them (canonical for\n * script derivation).\n */\nexport interface RefundPrePeginContext {\n  vaultProviderPubkey: string;\n  vaultKeeperPubkeys: readonly string[];\n  universalChallengerPubkeys: readonly string[];\n  timelockRefund: number;\n  feeRate: bigint;\n  minPeginFeeRate: bigint;\n  numLocalChallengers: number;\n  councilQuorum: number;\n  councilSize: number;\n  network: Network;\n}\n\n/** Minimum shape required from a broadcast result. */\nexport interface BtcBroadcastResult {\n  txId: string;\n}\n\nexport type BtcBroadcaster<R extends BtcBroadcastResult = BtcBroadcastResult> =\n  (signedTxHex: string) => Promise<R>;\n\nexport type RefundPsbtSigner = (\n  psbtHex: string,\n  opts: SignPsbtOptions,\n) => Promise<string>;\n\nexport interface RefundInput<\n  R extends BtcBroadcastResult = BtcBroadcastResult,\n> {\n  vaultId: Hex;\n  /**\n   * Fetch authoritative on-chain + indexer vault data. The SDK passes no\n   * arguments — the caller closes over `vaultId` (or any other context it\n   * needs).\n   */\n  readVault: () => Promise<VaultRefundData>;\n  /**\n   * Fetch the version-pinned refund context (sorted pubkeys, timelock, etc.)\n   * derived from the vault's locked versions.\n   */\n  readPrePeginContext: (\n    vault: VaultRefundData,\n  ) => Promise<RefundPrePeginContext>;\n  /**\n   * Mempool-derived sat/vB fee rate to use for the refund tx (positive\n   * number). Caller fetches this before invoking — it does not depend on\n   * any value the SDK computes, and folding it into the call keeps the\n   * orchestration honest.\n   */\n  feeRate: number;\n  /** BTC wallet signer; receives a PSBT hex + taproot script-path options. */\n  signPsbt: RefundPsbtSigner;\n  /** Broadcast callback — returns whatever shape the caller needs. */\n  broadcastTx: BtcBroadcaster<R>;\n  /** Checked at every async boundary. */\n  signal?: AbortSignal;\n}\n\nfunction assertNonNegativeInteger(value: number, label: string): void {\n  if (!Number.isInteger(value) || value < 0) {\n    throw new Error(`${label} must be a non-negative integer, got ${value}`);\n  }\n}\n\nfunction validateVaultRefundData(v: VaultRefundData): void {\n  assertBytes32(v.hashlock, \"hashlock\");\n  if (!Number.isInteger(v.htlcVout) || v.htlcVout < 0) {\n    throw new Error(\n      `htlcVout must be a non-negative integer, got ${v.htlcVout}`,\n    );\n  }\n  // Batch shape — one entry per sibling HTLC, vout-ordered and\n  // contiguous from 0. The reconstructed WASM template uses these\n  // arrays directly: any gap, duplicate, or mis-ordering against the\n  // funded tx would produce an unspendable refund. The target's\n  // (hashlock, amount, htlcVout) must equal the corresponding batch\n  // entry so the orchestrator and the caller can't disagree about\n  // which output is being refunded.\n  if (!Array.isArray(v.batch) || v.batch.length === 0) {\n    throw new Error(\"batch must be a non-empty array of HTLC entries\");\n  }\n  if (v.htlcVout >= v.batch.length) {\n    throw new Error(\n      `htlcVout ${v.htlcVout} is out of range for batch of size ${v.batch.length}`,\n    );\n  }\n  for (let i = 0; i < v.batch.length; i++) {\n    const entry = v.batch[i];\n    assertBytes32(entry.hashlock, `batch[${i}].hashlock`);\n    if (!Number.isInteger(entry.htlcVout) || entry.htlcVout !== i) {\n      throw new Error(\n        `batch[${i}].htlcVout must equal ${i} (contiguous vout-ordered vector), got ${entry.htlcVout}`,\n      );\n    }\n    if (typeof entry.amount !== \"bigint\" || entry.amount <= 0n) {\n      throw new Error(\n        `batch[${i}].amount must be a positive bigint, got ${entry.amount}`,\n      );\n    }\n  }\n  const targetEntry = v.batch[v.htlcVout];\n  if (targetEntry.hashlock.toLowerCase() !== v.hashlock.toLowerCase()) {\n    throw new Error(\n      `batch[${v.htlcVout}].hashlock (${targetEntry.hashlock}) does not match target hashlock (${v.hashlock})`,\n    );\n  }\n  if (targetEntry.amount !== v.amount) {\n    throw new Error(\n      `batch[${v.htlcVout}].amount (${targetEntry.amount}) does not match target amount (${v.amount})`,\n    );\n  }\n  // Version fields flow directly into on-chain script derivation via\n  // `readPrePeginContext` — NaN, negative, or non-integer values would\n  // silently produce wrong scripts. Guard here as defence in depth even\n  // though the caller sources these from bigint on-chain reads.\n  assertNonNegativeInteger(v.offchainParamsVersion, \"offchainParamsVersion\");\n  assertNonNegativeInteger(v.appVaultKeepersVersion, \"appVaultKeepersVersion\");\n  assertNonNegativeInteger(\n    v.universalChallengersVersion,\n    \"universalChallengersVersion\",\n  );\n  assertValidVaultCoreVersion(\n    v.vaultCoreVersion,\n    \"VaultRefundData.vaultCoreVersion\",\n  );\n  if (\n    typeof v.unsignedPrePeginTxHex !== \"string\" ||\n    v.unsignedPrePeginTxHex.length === 0\n  ) {\n    throw new Error(\"unsignedPrePeginTxHex must be a non-empty hex string\");\n  }\n  if (!BTC_HEX_BYTES_RE.test(v.unsignedPrePeginTxHex)) {\n    throw new Error(\n      \"unsignedPrePeginTxHex must be a hex byte string (optional 0x prefix, even length)\",\n    );\n  }\n  if (!v.depositorBtcPubkey || !PUBKEY_HEX_RE.test(v.depositorBtcPubkey)) {\n    throw new Error(\n      \"depositorBtcPubkey must be 32 or 33 bytes of hex (optional 0x prefix)\",\n    );\n  }\n  if (typeof v.amount !== \"bigint\" || v.amount <= 0n) {\n    throw new Error(`amount must be a positive bigint, got ${v.amount}`);\n  }\n}\n\nfunction validateRefundPrePeginContext(c: RefundPrePeginContext): void {\n  if (!c.vaultProviderPubkey || !PUBKEY_HEX_RE.test(c.vaultProviderPubkey)) {\n    throw new Error(\"vaultProviderPubkey must be 32 or 33 bytes of hex\");\n  }\n  if (c.vaultKeeperPubkeys.length === 0) {\n    throw new Error(\"vaultKeeperPubkeys must be non-empty\");\n  }\n  if (c.universalChallengerPubkeys.length === 0) {\n    throw new Error(\"universalChallengerPubkeys must be non-empty\");\n  }\n  if (!Number.isInteger(c.timelockRefund) || c.timelockRefund <= 0) {\n    throw new Error(\n      `timelockRefund must be a positive integer, got ${c.timelockRefund}`,\n    );\n  }\n  if (typeof c.feeRate !== \"bigint\" || c.feeRate <= 0n) {\n    throw new Error(\n      `protocol feeRate must be a positive bigint, got ${c.feeRate}`,\n    );\n  }\n  if (typeof c.minPeginFeeRate !== \"bigint\" || c.minPeginFeeRate <= 0n) {\n    throw new Error(\n      `minPeginFeeRate must be a positive bigint, got ${c.minPeginFeeRate}`,\n    );\n  }\n  if (!Number.isInteger(c.numLocalChallengers) || c.numLocalChallengers < 0) {\n    throw new Error(\"numLocalChallengers must be a non-negative integer\");\n  }\n  if (\n    !Number.isInteger(c.councilQuorum) ||\n    !Number.isInteger(c.councilSize) ||\n    c.councilQuorum <= 0 ||\n    c.councilSize <= 0 ||\n    c.councilQuorum > c.councilSize\n  ) {\n    throw new Error(\n      `councilQuorum (${c.councilQuorum}) must be in [1, councilSize=${c.councilSize}]`,\n    );\n  }\n}\n\nfunction finalizeAndExtract(signedPsbtHex: string): string {\n  const psbt = Psbt.fromHex(signedPsbtHex);\n  try {\n    psbt.finalizeAllInputs();\n  } catch (e: unknown) {\n    // Some wallets (e.g. Keystone) finalize during signPsbt; bitcoinjs then\n    // throws \"Input is already finalized\". Treat that case as a no-op.\n    const message = e instanceof Error ? e.message : String(e);\n    if (!message.includes(\"already finalized\")) {\n      throw new Error(`Failed to finalize refund PSBT: ${message}`);\n    }\n  }\n  return psbt.extractTransaction().toHex();\n}\n\n/**\n * Build, sign, and broadcast a refund transaction for an expired vault.\n *\n * Trust boundary: `readVault` must source the hashlock, htlcVout, and\n * versioning fields from the on-chain contract — an indexer-only path\n * leaves the refund flow open to signer-set substitution. The SDK does\n * not enforce this; it is the caller's responsibility.\n *\n * The broadcast transport is expected to surface Bitcoin's `non-BIP68-final`\n * policy rejection as an `Error` whose message contains that string; when\n * it does, the SDK wraps it in {@link BIP68NotMatureError}. All other\n * transport errors propagate unchanged.\n *\n * @returns whatever the injected `broadcastTx` returns (generic pass-through)\n * @throws `Error` if any validation fails\n * @throws {@link BIP68NotMatureError} if the broadcast is rejected because\n *         the refund CSV timelock has not yet matured\n * @throws anything `readVault`, `readPrePeginContext`,\n *         `signPsbt`, or `broadcastTx` throws\n */\nexport async function buildAndBroadcastRefund<\n  R extends BtcBroadcastResult = BtcBroadcastResult,\n>(input: RefundInput<R>): Promise<R> {\n  const {\n    vaultId,\n    readVault,\n    readPrePeginContext,\n    feeRate,\n    signPsbt,\n    broadcastTx,\n    signal,\n  } = input;\n\n  signal?.throwIfAborted();\n  assertBytes32(vaultId, \"vaultId\");\n\n  const vault = await readVault();\n  validateVaultRefundData(vault);\n  signal?.throwIfAborted();\n\n  const ctx = await readPrePeginContext(vault);\n  validateRefundPrePeginContext(ctx);\n  signal?.throwIfAborted();\n\n  if (!Number.isFinite(feeRate) || feeRate <= 0) {\n    throw new Error(`feeRate must be a positive number, got ${feeRate}`);\n  }\n  // Rate cap: fail closed before PSBT construction if the seeded value\n  // exceeds the safety ceiling. A compromised mempool API (or upstream\n  // proxy / BGP hijack) can otherwise drive `halfHourFee` to the API's\n  // 10_000 sat/vB ceiling and burn the refund as miner fee.\n  if (feeRate > REFUND_MAX_FEE_RATE_SATS_VB) {\n    throw new Error(\n      `feeRate ${feeRate} sat/vB exceeds refund safety cap ` +\n        `${REFUND_MAX_FEE_RATE_SATS_VB} sat/vB; refusing to sign refund.`,\n    );\n  }\n  const refundFee = BigInt(Math.ceil(feeRate * REFUND_VSIZE));\n  // Fraction cap: even within the rate ceiling, refuse to sign if the\n  // absolute fee would consume more than the configured percentage of the\n  // vault amount. Protects small vaults from disproportionate burn.\n  const maxFeeByFraction =\n    (vault.amount * REFUND_MAX_FEE_FRACTION_NUMERATOR) /\n    REFUND_MAX_FEE_FRACTION_DENOMINATOR;\n  if (refundFee > maxFeeByFraction) {\n    throw new Error(\n      `Refund fee ${refundFee} sats exceeds the per-vault safety cap ` +\n        `of ${maxFeeByFraction} sats ` +\n        `(${REFUND_MAX_FEE_FRACTION_NUMERATOR}/${REFUND_MAX_FEE_FRACTION_DENOMINATOR} ` +\n        `of vault.amount=${vault.amount}); refusing to sign refund.`,\n    );\n  }\n  signal?.throwIfAborted();\n\n  // `vault.depositorBtcPubkey` may arrive as wallet-native compressed sec1\n  // (33 bytes) because the caller fetches it live from the wallet for\n  // signing. WASM script derivation wants x-only (32 bytes), so normalize\n  // here; the raw form is kept for the wallet sign call below.\n  const xOnlyDepositorPubkey = processPublicKeyToXOnly(\n    vault.depositorBtcPubkey,\n  );\n\n  const cleanFundedPrePeginTxHex = stripHexPrefix(vault.unsignedPrePeginTxHex);\n\n  // Production peg-ins (PeginManager) commit an OP_RETURN <PUSH32\n  // SHA256(authAnchor)> output at `vout = hashlocks.length`. The\n  // reconstructed unfunded template carries `batch.length` HTLC outputs,\n  // so the OP_RETURN — when present — must sit at exactly that vout.\n  // Legacy non-auth-anchored Pre-PegIns return `undefined` from the\n  // finder; the template then has no OP_RETURN either, which is a\n  // matching configuration.\n  const found = findAuthAnchorOpReturn(cleanFundedPrePeginTxHex);\n  if (found !== undefined && found.vout !== vault.batch.length) {\n    throw new Error(\n      `Auth-anchor OP_RETURN at vout ${found.vout} does not match batch size ` +\n        `(${vault.batch.length} HTLC outputs expect the anchor at vout ${vault.batch.length}). ` +\n        `Refund refused — sibling HTLC vector is incomplete.`,\n    );\n  }\n  const authAnchorHash = found?.hash;\n\n  // Independent structural check on the funded tx: it must carry at\n  // least N HTLC outputs (one per batch entry). If the anchor is\n  // present we've already pinned its position above, which transitively\n  // proves the tx has ≥ N+1 outputs; if the anchor is absent (legacy)\n  // we still need ≥ N to spend `htlcVout = N-1`.\n  let parsedFundedTx: Transaction;\n  try {\n    parsedFundedTx = Transaction.fromHex(cleanFundedPrePeginTxHex);\n  } catch (e) {\n    throw new Error(\n      `Failed to parse funded Pre-PegIn transaction hex: ${e instanceof Error ? e.message : String(e)}`,\n    );\n  }\n  if (parsedFundedTx.outs.length < vault.batch.length) {\n    throw new Error(\n      `Funded Pre-PegIn tx has ${parsedFundedTx.outs.length} outputs but batch ` +\n        `requires at least ${vault.batch.length} HTLC outputs. ` +\n        `Refund refused — funded tx shape disagrees with sibling vector.`,\n    );\n  }\n\n  const { psbtHex } = await buildRefundPsbt({\n    prePeginParams: {\n      vaultCoreVersion: vault.vaultCoreVersion,\n      depositorPubkey: xOnlyDepositorPubkey,\n      vaultProviderPubkey: stripHexPrefix(ctx.vaultProviderPubkey),\n      vaultKeeperPubkeys: ctx.vaultKeeperPubkeys.map(stripHexPrefix),\n      universalChallengerPubkeys:\n        ctx.universalChallengerPubkeys.map(stripHexPrefix),\n      hashlocks: vault.batch.map((b) => stripHexPrefix(b.hashlock)),\n      timelockRefund: ctx.timelockRefund,\n      // `batch[i].amount` is the on-chain vault deposit (peg-in) amount, which\n      // is exactly what WASM's `pegInAmounts` expects — it re-adds the protocol\n      // reserve (`depositorClaimValue + minPeginFee`) internally when sizing the\n      // HTLC output. `buildRefundPsbt`'s value cross-check then binds the result\n      // to the funded tx bytes, refusing the refund if the template's HTLC value\n      // disagrees with the on-chain commitment.\n      pegInAmounts: vault.batch.map((b) => b.amount),\n      feeRate: ctx.feeRate,\n      minPeginFeeRate: ctx.minPeginFeeRate,\n      numLocalChallengers: ctx.numLocalChallengers,\n      councilQuorum: ctx.councilQuorum,\n      councilSize: ctx.councilSize,\n      network: ctx.network,\n      authAnchorHash,\n    },\n    fundedPrePeginTxHex: cleanFundedPrePeginTxHex,\n    htlcVout: vault.htlcVout,\n    refundFee,\n    // buildRefundPsbt's top-level `hashlock` param is documented as \"no 0x\n    // prefix\" and flows into the WASM HTLC connector derivation; a prefixed\n    // value would derive the wrong refund script leaf and yield an\n    // unspendable PSBT. Match the `hashlocks` array handling above.\n    hashlock: stripHexPrefix(vault.hashlock),\n  });\n  signal?.throwIfAborted();\n\n  const signOptions = createTaprootScriptPathSignOptions(\n    vault.depositorBtcPubkey,\n    REFUND_INPUT_COUNT,\n  );\n  const signedPsbtHex = await signPsbt(psbtHex, signOptions);\n\n  assertPsbtUnsignedTxMatches({\n    requestedPsbtHex: psbtHex,\n    returnedPsbtHex: signedPsbtHex,\n  });\n\n  // Critical Path #7: verify the depositor's script-path signature against a\n  // sighash recomputed from the PSBT we built before finalizing and broadcasting.\n  // The refund spends a single input (the HTLC output) on input 0.\n  const REFUND_SIGNED_INPUT_INDEX = 0;\n  const refundSignature = extractPayoutSignature(\n    signedPsbtHex,\n    xOnlyDepositorPubkey,\n    REFUND_SIGNED_INPUT_INDEX,\n  );\n  assertScriptPathSchnorrSignature({\n    requestedPsbtHex: psbtHex,\n    signatureHex: refundSignature,\n    signerXOnlyPubkeyHex: xOnlyDepositorPubkey,\n    inputIndex: REFUND_SIGNED_INPUT_INDEX,\n  });\n\n  const signedTxHex = finalizeAndExtract(signedPsbtHex);\n  signal?.throwIfAborted();\n\n  try {\n    return await broadcastTx(signedTxHex);\n  } catch (error) {\n    if (error instanceof Error && BIP68_ERROR_RE.test(error.message)) {\n      throw new BIP68NotMatureError(vaultId, error);\n    }\n    throw error;\n  }\n}\n"],"names":["BYTES32_HEX_RE","ADDRESS_HEX_RE","ETH_HEX_BYTES_RE","assertBytes32","value","label","assertAddress","assertHexBytes","validateActivationInputs","input","normalizedSecret","ensureHexPrefix","validateSecretAgainstHashlock","activateVault","btcVaultRegistryAddress","vaultId","hashlock","activationMetadata","writeContract","signal","BTCVaultRegistryABI","activateVaultAndRedeem","PEGIN_ETH_CONFIRMATIONS","REGISTRATION_DEPTH_POLL_INTERVAL_MS","REGISTRATION_DEPTH_TIMEOUT_MS","REGISTRATION_ABSENT_GRACE_POLLS","PeginRegistrationMissingError","message","PeginRegistrationNotFinalError","isPeginRegistrationMissingError","err","isPeginRegistrationNotFinalError","computeRegistrationConfirmations","params","currentBlock","createdAtBlock","depth","waitForPeginRegistrationDepth","vaultRegistryReader","getBlockNumber","vaultIds","required","pollIntervalMs","timeoutMs","onProgress","startTime","hasObservedRegistration","absentPolls","lastError","infos","missingIndex","info","zeroAddress","shallowest","shallowestDepth","error","resolve","reject","onAbort","timeoutId","DEPOSITOR_SIGNED_INPUT_COUNT","DEPOSITOR_PATH_UNUSED_COMMISSION_BPS","assertChallengerSetMatchesExpected","challengerPresignData","localChallengers","universalChallengerBtcPubkeys","universal","k","stripHexPrefix","overlap","expected","suppliedList","c","suppliedSet","expectedSet","missing","extra","readInputTxid","tx","inputIndex","uint8ArrayToHex","assertInputReferencesParent","noPayoutTx","parentTx","parentLabel","challengerPubkey","parentTxid","inputTxid","collectDepositorGraphPsbts","depositorGraph","walletPublicKey","ctx","psbtHexes","signOptions","challengerEntries","deriveLocalChallengers","builtPayout","buildPayoutPsbt","createTaprootScriptPathSignOptions","claimerPubkey","assertTxParsed","Transaction","challenger","noPayoutIdx","noPayoutHex","buildLocalNoPayoutPsbt","assertNoPayoutOutputMatchesChallenger","challengeAssertXTx","challengeAssertYTx","prevouts","out","buildNoPayoutPsbt","extractDepositorGraphSignatures","psbtPairs","depositorPubkey","assertPsbtUnsignedTxMatches","payoutSignature","extractPayoutSignature","assertScriptPathSchnorrSignature","perChallenger","entry","nopayoutSignature","signDepositorGraph","btcWallet","signingContext","validateWalletPubkey","signedPsbtHexes","signPsbtsWithFallback","requestedPsbtHex","i","DEFAULT_POLL_INTERVAL_MS","waitForPeginStatus","statusReader","peginTxid","targetStatuses","response","status","DaemonStatus","VP_TERMINAL_FAILURE_STATUSES","JsonRpcError","RpcErrorCode","MAX_POLLING_TIMEOUT_MS","POST_PAYOUT_STATUSES","TARGET_STATUS","prepareTransactionsForSigning","claimerTransactions","processPublicKeyToXOnly","normalizeClaimerPubkey","pubkey","assertDepositTermsMatchSigningContext","terms","context","refuse","field","a","b","scalars","fromTerms","fromContext","canonical","keys","sameSet","x","y","contextVp","approvedVps","v","assertNonDepositorClaimerSetMatches","suppliedTxs","expectedVpPubkey","expectedVkPubkeys","depositorPubkeyXOnly","depositor","expectedList","suppliedAll","suppliedNonDepositor","buildPayoutSigningInput","signPayoutTransactions","transactions","payoutManager","PayoutManager","totalClaimers","payoutSignatures","r","result","signatures","runDepositorPresignFlow","presignClient","depositorPk","depositTerms","supportsDepositApproval","depositorPkNormalized","nonDepositorTxs","preparedTransactions","claimerSignatures","depositorClaimerPresignatures","allSignatures","STATUS_POLL_TIMEOUT_MS","TARGET_STATUSES","POST_WOTS_STATUSES","submitWotsPublicKey","wotsSubmitter","wotsPublicKeys","isHintAccepted","match","matchKeyHint","hint","registrationKey","operationKey","canonicalHint","canonicalizeBtcPubkey","matchKeySetHint","hints","registrationKeys","operationKeys","canonicalHints","sortedCanonical","setsEqual","key","assertVaultProviderHintAccepted","vaultProviderEthAddress","hintBtcPubkey","registrationBtcPubkey","readCurrentOperationBtcPubkey","canonicalOperation","resolveOne","role","rosterEntry","rawOperationKey","genesisBtcPubkey","assertOnChainBtcPubkey","operationBtcPubkey","assertDistinctOperationKeys","participants","seen","participant","previousOwner","finalize","query","raw","resolvedAt","vaultProvider","vaultKeepers","keeper","universalChallengers","p","resolveCurrentParticipantKeys","resolveParticipantKeysAtEpochs","sortedSet","validateOnChainParticipantKeys","vaultKeeperReader","universalChallengerReader","applicationEntryPoint","expectedVaultProviderBtcPubkey","expectedVaultKeeperBtcPubkeys","expectedUniversalChallengerBtcPubkeys","operationKeyReader","onIndexerServingOperationKeys","onIndexerHintsInconsistent","onChainVpKey","expectedAppVaultKeepersVersion","expectedUniversalChallengersVersion","onChainKeepers","onChainChallengers","participantKeys","vpMatch","keeperMatch","challengerMatch","roles","pinsRegistration","m","pinsOperation","isValidXOnlyHex","hex","isDepositAmountValid","amountSats","minDeposit","maxDeposit","btcBalance","estimatedFeeSats","depositorClaimValue","validateDepositAmount","amount","formatSatoshisToBtc","validateRemainingCapacity","effectiveRemaining","validateProviderSelection","selectedProviders","availableProviders","availableProvidersLower","validateVaultAmounts","amounts","validateVaultProviderPubkey","stripped","validateVaultKeepers","vaultKeeperBtcPubkeys","validateUniversalChallengers","validateUTXOState","confirmedUTXOs","validateMultiVaultDepositInputs","vaultAmounts","vaultProviderBtcPubkey","amountsValidation","pubkeyValidation","ParticipantKeyDriftError","isParticipantKeyDriftError","diffKeys","actual","verifyRegisteredParticipantKeys","epochsPerVault","mismatches","epochs","resolved","keeperDiff","challengerDiff","RegisteredVaultVersionMismatchError","isRegisteredVaultVersionMismatchError","verifyRegisteredVaultVersions","expectedOffchainParamsVersion","expectedVaultCoreVersion","id","ClaimerPegoutStatusValue","PEGOUT_TERMINAL_STATUSES","isRecognizedPegoutStatus","isPegoutTerminalStatus","claimerStatus","BIP68NotMatureError","cause","__publicField","BTC_HEX_BYTES_RE","PUBKEY_HEX_RE","REFUND_VSIZE","REFUND_MAX_FEE_RATE_SATS_VB","REFUND_MAX_FEE_FRACTION_NUMERATOR","REFUND_MAX_FEE_FRACTION_DENOMINATOR","estimateRefundFeeSats","feeRateSatsVb","REFUND_INPUT_COUNT","BIP68_ERROR_RE","assertNonNegativeInteger","validateVaultRefundData","targetEntry","assertValidVaultCoreVersion","validateRefundPrePeginContext","finalizeAndExtract","signedPsbtHex","psbt","Psbt","e","buildAndBroadcastRefund","readVault","readPrePeginContext","feeRate","signPsbt","broadcastTx","vault","refundFee","maxFeeByFraction","xOnlyDepositorPubkey","cleanFundedPrePeginTxHex","found","findAuthAnchorOpReturn","authAnchorHash","parsedFundedTx","psbtHex","buildRefundPsbt","REFUND_SIGNED_INPUT_INDEX","refundSignature","signedTxHex"],"mappings":"unBAgBMA,GAAiB,sBACjBC,GAAiB,sBAKjBC,GAAmB,wBAEzB,SAASC,EAAcC,EAAeC,EAAqB,CACzD,GAAID,EAAM,SAAW,GACnB,MAAM,IAAI,MACR,GAAGC,CAAK,+DAA+DD,EAAM,MAAM,EAAA,EAGvF,GAAI,CAACJ,GAAe,KAAKI,CAAK,EAC5B,MAAM,IAAI,MACR,GAAGC,CAAK,uDAAA,CAGd,CAEA,SAASC,GAAcF,EAAeC,EAAqB,CACzD,GAAI,CAACJ,GAAe,KAAKG,CAAK,EAC5B,MAAM,IAAI,MACR,GAAGC,CAAK,uDAAA,CAGd,CAEA,SAASE,GAAeH,EAAeC,EAAqB,CAC1D,GAAI,CAACH,GAAiB,KAAKE,CAAK,EAC9B,MAAM,IAAI,MACR,GAAGC,CAAK,oEAAA,CAGd,CAyEA,SAASG,GAAyBC,EAK1B,CACNH,GAAcG,EAAM,wBAAyB,yBAAyB,EACtEN,EAAcM,EAAM,QAAS,SAAS,EAEtC,MAAMC,EAAmBC,EAAAA,gBAAgBF,EAAM,MAAM,EAGrD,GAFAN,EAAcO,EAAkB,QAAQ,EAEpCD,EAAM,WAAa,SACrBN,EAAcM,EAAM,SAAU,UAAU,EACpC,CAACG,EAAAA,8BAA8BF,EAAkBD,EAAM,QAAQ,GACjE,MAAM,IAAI,MACR,qEAAA,EAKN,OAAOC,CACT,CAoBA,eAAsBG,GAEpBJ,EAA0C,CAC1C,KAAM,CACJ,wBAAAK,EACA,QAAAC,EACA,SAAAC,EACA,mBAAAC,EACA,cAAAC,EACA,OAAAC,CAAA,EACEV,EAEJU,GAAA,MAAAA,EAAQ,iBAER,MAAMT,EAAmBF,GAAyB,CAChD,wBAAAM,EACA,QAAAC,EACA,OAAQN,EAAM,OACd,SAAAO,CAAA,CACD,EAED,OAAAT,GAAeU,EAAoB,oBAAoB,EAEhDC,EAAc,CACnB,QAASJ,EACT,IAAKM,GAAAA,oBACL,aAAc,0BACd,KAAM,CAACL,EAASL,EAAkBO,CAAkB,CAAA,CACrD,CACH,CAiDA,eAAsBI,GAEpBZ,EAAmD,CACnD,KAAM,CAAE,wBAAAK,EAAyB,QAAAC,EAAS,SAAAC,EAAU,cAAAE,EAAe,OAAAC,GACjEV,EAEFU,GAAA,MAAAA,EAAQ,iBAER,MAAMT,EAAmBF,GAAyB,CAChD,wBAAAM,EACA,QAAAC,EACA,OAAQN,EAAM,OACd,SAAAO,CAAA,CACD,EAED,OAAOE,EAAc,CACnB,QAASJ,EACT,IAAKM,GAAAA,oBACL,aAAc,mCACd,KAAM,CAACL,EAASL,CAAgB,CAAA,CACjC,CACH,CC9NO,MAAMY,GAA0B,EAGjCC,GAAsC,IAStCC,GAAgC,GAAK,IAWrCC,GAAkC,GAOjC,MAAMC,UAAsC,KAAM,CACvD,YAAYC,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,+BACd,CACF,CAGO,MAAMC,UAAuC,KAAM,CACxD,YAAYD,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,gCACd,CACF,CAKO,SAASE,GACdC,EACsC,CACtC,OACEA,aAAeJ,GACdI,aAAe,OAASA,EAAI,OAAS,+BAE1C,CAEO,SAASC,GACdD,EACuC,CACvC,OACEA,aAAeF,GACdE,aAAe,OAASA,EAAI,OAAS,gCAE1C,CAiBO,SAASE,GACdC,EACQ,CACR,KAAM,CAAE,aAAAC,EAAc,eAAAC,CAAA,EAAmBF,EACnCG,EAAQF,EAAeC,EAAiB,GAC9C,OAAOC,EAAQ,GAAK,EAAI,OAAOA,CAAK,CACtC,CA+CA,eAAsBC,GACpBJ,EACuC,CACvC,KAAM,CACJ,oBAAAK,EACA,eAAAC,EACA,SAAAC,EACA,SAAAC,EAAWnB,GACX,eAAAoB,EAAiBnB,GACjB,UAAAoB,EAAYnB,GACZ,OAAAL,EACA,WAAAyB,CAAA,EACEX,EAEJ,GAAIO,EAAS,SAAW,EACtB,MAAM,IAAI,MACR,8DAAA,EAIJ,MAAMK,EAAY,KAAK,IAAA,EAKvB,IAAIC,EAA0B,GAC1BC,EAAc,EACdC,EAEJ,OAAa,CACX,GAAI7B,GAAA,MAAAA,EAAQ,QACV,MAAM,IAAI,MACR,wDAAwDqB,EAAS,MAAM,YAAA,EAI3E,GAAI,KAAK,MAAQK,GAAaF,EAK5B,MAAM,IAAIf,EACR,qCAAqCa,CAAQ,kCAAkCE,CAAS,uNAIrFK,aAAqB,MAAQ,qBAAqBA,EAAU,OAAO,GAAK,GAAA,EAI/E,GAAI,CAYF,MAAMd,EAAe,MAAMK,EAAA,EACrBU,EAAQ,MAAM,QAAQ,IAC1BT,EAAS,IAAKzB,GAAYuB,EAAoB,kBAAkBvB,CAAO,CAAC,CAAA,EAOpEmC,EAAeD,EAAM,UACxBE,GAASA,EAAK,YAAcC,GAAAA,aAAeD,EAAK,YAAc,EAAA,EAGjE,GAAID,IAAiB,GAAI,CASvB,GARAH,GAAe,EASb,CAACD,GACDC,EAActB,GAEd,MAAM,IAAIC,EACR,SAASc,EAASU,CAAY,CAAC,wCAC1BH,CAAW,2CAAA,EAUpB,QAAQ,KACN,iCAAiCP,EAASU,CAAY,CAAC,2CACzChB,CAAY,+DAAA,EAE5BU,GAAA,MAAAA,EAAa,CAAE,cAAe,EAAG,SAAAH,CAAA,EACnC,KAAO,CACLK,EAA0B,GAC1BC,EAAc,EACdC,EAAY,OAKZ,IAAIK,EAAaJ,EAAM,CAAC,EACpBK,EAAkBtB,GAAiC,CACrD,aAAAE,EACA,eAAgBe,EAAM,CAAC,EAAE,SAAA,CAC1B,EACD,UAAWE,KAAQF,EAAM,MAAM,CAAC,EAAG,CACjC,MAAMb,EAAQJ,GAAiC,CAC7C,aAAAE,EACA,eAAgBiB,EAAK,SAAA,CACtB,EACGf,EAAQkB,IACVA,EAAkBlB,EAClBiB,EAAaF,EAEjB,CAIA,GAFAP,GAAA,MAAAA,EAAa,CAAE,cAAeU,EAAiB,SAAAb,CAAA,GAE3Ca,GAAmBb,EACrB,MAAO,CAAE,cAAea,EAAiB,UAAWD,CAAA,CAExD,CACF,OAASE,EAAO,CACd,GAAI1B,GAAgC0B,CAAK,EACvC,MAAMA,EAIR,QAAQ,KACN,+CAA+Cb,CAAc,SAC1Da,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EAAA,EAE1DP,EAAYO,CACd,CAEA,MAAM,IAAI,QAAc,CAACC,EAASC,IAAW,CAC3C,MAAMC,EAAU,IAAM,CACpB,aAAaC,CAAS,EACtBF,EACE,IAAI,MACF,wDAAwDjB,EAAS,MAAM,YAAA,CACzE,CAEJ,EACMmB,EAAY,WAAW,IAAM,CACjCxC,GAAA,MAAAA,EAAQ,oBAAoB,QAASuC,GACrCF,EAAA,CACF,EAAGd,CAAc,EACjBvB,GAAA,MAAAA,EAAQ,iBAAiB,QAASuC,EAAS,CAAE,KAAM,IACrD,CAAC,CACH,CACF,CCtRA,MAAME,GAA+B,EAM/BC,GAAuC,EAkC7C,SAASC,GACPC,EACAC,EACAC,EACM,CACN,MAAMC,EAAYD,EAA8B,IAAKE,GACnDC,EAAAA,eAAeD,CAAC,EAAE,YAAA,CAAY,EAI1BE,EAAUL,EAAiB,OAAQG,GAAMD,EAAU,SAASC,CAAC,CAAC,EACpE,GAAIE,EAAQ,OAAS,EACnB,MAAM,IAAI,MACR,oFAAoFA,EAAQ,KAAK,IAAI,CAAC,GAAA,EAG1G,MAAMC,EAAW,CAAC,GAAGN,EAAkB,GAAGE,CAAS,EAE7CK,EAAeR,EAAsB,IAAKS,GAC9CJ,EAAAA,eAAeI,EAAE,iBAAiB,EAAE,YAAA,CAAY,EAE5CC,EAAc,IAAI,IAAIF,CAAY,EACxC,GAAIE,EAAY,OAASF,EAAa,OACpC,MAAM,IAAI,MACR,kFAAA,EAGJ,MAAMG,EAAc,IAAI,IAAIJ,CAAQ,EAC9BK,EAAUL,EAAS,OAAQE,GAAM,CAACC,EAAY,IAAID,CAAC,CAAC,EACpDI,EAAQL,EAAa,OAAQC,GAAM,CAACE,EAAY,IAAIF,CAAC,CAAC,EAC5D,GAAIG,EAAQ,OAAS,GAAKC,EAAM,OAAS,EACvC,MAAM,IAAI,MACR,8EACGD,EAAQ,OAAS,EAAI,cAAcA,EAAQ,KAAK,IAAI,CAAC,IAAM,KAC3DC,EAAM,OAAS,EAAI,iBAAiBA,EAAM,KAAK,IAAI,CAAC,IAAM,GAAA,CAGnE,CAQA,SAASC,GAAcC,EAAiBC,EAA4B,CAClE,MAAMtE,EAAQqE,EAAG,IAAIC,CAAU,EAC/B,OAAOC,EAAAA,gBAAgB,IAAI,WAAWvE,EAAM,IAAI,EAAE,MAAA,EAAQ,SAAS,CACrE,CAOA,SAASwE,EACPC,EACAH,EACAI,EACAC,EACAC,EACM,CACN,MAAM5E,EAAQyE,EAAW,IAAIH,CAAU,EACvC,GAAItE,EAAM,QAAU,EAClB,MAAM,IAAI,MACR,wBAAwB4E,CAAgB,WAAWN,CAAU,sBAAsBK,CAAW,qBAAqB3E,EAAM,KAAK,EAAA,EAGlI,MAAM6E,EAAaH,EAAS,MAAA,EACtBI,EAAYV,GAAcK,EAAYH,CAAU,EACtD,GAAIQ,IAAcD,EAChB,MAAM,IAAI,MACR,wBAAwBD,CAAgB,WAAWN,CAAU,uBAAuBK,CAAW,mBAAmBE,CAAU,SAASC,CAAS,GAAA,CAGpJ,CAYA,eAAeC,GACbC,EACAC,EACAC,EACuC,CACvC,MAAMC,EAAsB,CAAA,EACtBC,EAAiC,CAAA,EACjCC,EAAuC,CAAA,EAIvC9B,EAAmB+B,EAAAA,uBAAuB,CAC9C,iBAAkBJ,EAAI,mBACtB,mBAAoBA,EAAI,mBACxB,uBAAwBA,EAAI,uBAC5B,sBAAuBA,EAAI,qBAAA,CAC5B,EACD7B,GACE2B,EAAe,wBACfzB,EACA2B,EAAI,6BAAA,EAMN,MAAMK,EAAc,MAAMC,kBAAgB,CACxC,iBAAkBN,EAAI,iBACtB,6BAA8BA,EAAI,6BAClC,yBAA0BA,EAAI,yBAC9B,YAAaF,EAAe,UAAU,OACtC,WAAYE,EAAI,WAChB,YAAaF,EAAe,UAAU,OACtC,eAAgBE,EAAI,eACpB,mBAAoBA,EAAI,mBACxB,uBAAwBA,EAAI,uBAC5B,sBAAuBA,EAAI,sBAC3B,8BAA+BA,EAAI,8BACnC,cAAeA,EAAI,cACnB,QAASA,EAAI,QACb,iBAAkBA,EAAI,mBACtB,6BAA8BA,EAAI,6BAClC,cAAe9B,GACf,gBAAiB8B,EAAI,gBACrB,eAAgBA,EAAI,eACpB,cAAeA,EAAI,aAAA,CACpB,EACDC,EAAU,KAAKI,EAAY,OAAO,EAClCH,EAAY,KACVK,EAAAA,mCACER,EACA9B,EAAA,CACF,EAIF,MAAMuC,EAAgB/B,EAAAA,eAAeuB,EAAI,kBAAkB,EACrDS,EAAiBC,EAAAA,YAAY,QACjCjC,iBAAeqB,EAAe,UAAU,MAAM,CAAA,EAGhD,UAAWa,KAAcb,EAAe,wBAAyB,CAC/D,MAAMJ,EAAmBjB,EAAAA,eAAekC,EAAW,iBAAiB,EAE9DC,EAAcX,EAAU,OACxBY,EAAc,MAAMC,GAAuB,CAC/C,WAAAH,EACA,iBAAAjB,EACA,cAAAc,EACA,iBAAAnC,EACA,eAAAoC,EACA,IAAAT,CAAA,CACD,EACDC,EAAU,KAAKY,CAAW,EAC1BX,EAAY,KACVK,EAAAA,mCACER,EACA9B,EAAA,CACF,EAGFkC,EAAkB,KAAK,CACrB,iBAAAT,EACA,YAAAkB,CAAA,CACD,CACH,CAEA,MAAO,CAAE,UAAAX,EAAW,YAAAC,EAAa,kBAAAC,CAAA,CACnC,CAyBA,eAAeW,GACbxE,EACiB,CACjB,KAAM,CACJ,WAAAqE,EACA,iBAAAjB,EACA,cAAAc,EACA,iBAAAnC,EACA,eAAAoC,EACA,IAAAT,CAAA,EACE1D,EAGJyE,EAAAA,sCACEJ,EAAW,YAAY,OACvBjB,EACAM,EAAI,OAAA,EAIN,MAAMT,EAAamB,EAAAA,YAAY,QAC7BjC,iBAAekC,EAAW,YAAY,MAAM,CAAA,EAExCK,EAAqBN,EAAAA,YAAY,QACrCjC,iBAAekC,EAAW,sBAAsB,MAAM,CAAA,EAElDM,EAAqBP,EAAAA,YAAY,QACrCjC,iBAAekC,EAAW,sBAAsB,MAAM,CAAA,EAGxD,GAAIpB,EAAW,IAAI,SAAW,EAC5B,MAAM,IAAI,MACR,wBAAwBG,CAAgB,qCAAqCH,EAAW,IAAI,MAAM,EAAA,EAQtGD,EACEC,EACA,EACAkB,EACA,SACAf,CAAA,EAEFJ,EACEC,EACA,EACAyB,EACA,mBACAtB,CAAA,EAEFJ,EACEC,EACA,EACA0B,EACA,mBACAvB,CAAA,EAGF,MAAMwB,EAAW,CACfT,EAAe,KAAK,CAAC,EACrBO,EAAmB,KAAK,CAAC,EACzBC,EAAmB,KAAK,CAAC,CAAA,EACzB,IAAKE,IAAS,CACd,cAAe9B,EAAAA,gBAAgB,IAAI,WAAW8B,EAAI,MAAM,CAAC,EACzD,MAAOA,EAAI,KAAA,EACX,EAEF,OAAOC,oBAAkB,CACvB,cAAeT,EAAW,YAAY,OACtC,iBAAAjB,EACA,SAAAwB,EACA,gBAAiB,CACf,eAAgBlB,EAAI,iBACpB,QAASQ,EACT,iBAAAnC,EACA,qBAAsB2B,EAAI,8BAC1B,eAAgBA,EAAI,eACpB,eAAgBA,EAAI,eACpB,cAAeA,EAAI,aAAA,CACrB,CACD,CACH,CAeA,SAASqB,GACPC,EACAnB,EACAoB,EACiC,CASjCC,8BAA4BF,EAAU,CAAC,CAAC,EACxC,MAAMG,EAAkBC,EAAAA,uBACtBJ,EAAU,CAAC,EAAE,gBACbC,CAAA,EAIFI,mCAAiC,CAC/B,iBAAkBL,EAAU,CAAC,EAAE,iBAC/B,aAAcG,EACd,qBAAsBF,EACtB,WAAY,CAAA,CACb,EAED,MAAMK,EAA+D,CAAA,EACrE,UAAWC,KAAS1B,EAAmB,CACrCqB,EAAAA,4BAA4BF,EAAUO,EAAM,WAAW,CAAC,EACxD,MAAMC,EAAoBJ,EAAAA,uBACxBJ,EAAUO,EAAM,WAAW,EAAE,gBAC7BN,CAAA,EAEFI,mCAAiC,CAC/B,iBAAkBL,EAAUO,EAAM,WAAW,EAAE,iBAC/C,aAAcC,EACd,qBAAsBP,EACtB,WAAY,CAAA,CACb,EACDK,EAAcC,EAAM,gBAAgB,EAAI,CACtC,mBAAoBC,CAAA,CAExB,CAEA,MAAO,CACL,kBAAmB,CACjB,iBAAkBL,CAAA,EAEpB,eAAgBG,CAAA,CAEpB,CA0FA,eAAsBG,GACpBzF,EAC0C,CAC1C,KAAM,CAAE,eAAAwD,EAAgB,UAAAkC,EAAW,eAAAC,CAAA,EAAmB3F,EAEhDyD,EAAkB,MAAMiC,EAAU,gBAAA,EAIlC,CAAE,gBAAAT,GAAoBW,EAAAA,qBAC1BnC,EACAtB,EAAAA,eAAewD,EAAe,kBAAkB,CAAA,EAI5C,CAAE,UAAAhC,EAAW,YAAAC,EAAa,kBAAAC,CAAA,EAC9B,MAAMN,GACJC,EACAC,EACAkC,CAAA,EAMEE,EAAkB,MAAMC,EAAAA,sBAC5BJ,EACA/B,EACAC,CAAA,EAIIoB,EAAwBrB,EAAU,IAAI,CAACoC,EAAkBC,KAAO,CACpE,iBAAAD,EACA,gBAAiBF,EAAgBG,CAAC,CAAA,EAClC,EACF,OAAOjB,GACLC,EACAnB,EACAoB,CAAA,CAEJ,CCzjBA,MAAMgB,GAA2B,IAyBjC,eAAsBC,EACpBlG,EACuB,CACvB,KAAM,CACJ,aAAAmG,EACA,UAAAC,EACA,eAAAC,EACA,UAAA3F,EACA,eAAAD,EAAiBwF,GACjB,OAAA/G,CAAA,EACEc,EAEEY,EAAY,KAAK,IAAA,EAEvB,OAAa,CACX,GAAI1B,GAAA,MAAAA,EAAQ,QACV,MAAM,IAAI,MACR,6BAA6BkH,EAAU,MAAM,EAAG,CAAC,CAAC,cAAc,CAAC,GAAGC,CAAc,EAAE,KAAK,IAAI,CAAC,GAAA,EAIlG,GAAI,KAAK,MAAQzF,GAAaF,EAC5B,MAAM,IAAI,MACR,yBAAyBA,CAAS,gBAAgB0F,EAAU,MAAM,EAAG,CAAC,CAAC,cAAc,CAAC,GAAGC,CAAc,EAAE,KAAK,IAAI,CAAC,GAAA,EAIvH,GAAI,CACF,MAAMC,EAAW,MAAMH,EAAa,eAClC,CAAE,WAAYC,CAAA,EACdlH,CAAA,EAIF,GAAIoH,EAAS,WAAW,YAAA,IAAkBF,EAAU,cAClD,MAAM,IAAI,MACR,4CAA4CE,EAAS,WAAW,MAAM,EAAG,CAAC,CAAC,gBAAgBF,EAAU,MAAM,EAAG,CAAC,CAAC,GAAA,EAIpH,MAAMG,EAASD,EAAS,OAOxB,GANID,EAAe,IAAIE,CAAM,GAMzBA,IAAWC,EAAAA,aAAa,UAC1B,OAAOD,EAGT,GACEA,IAAWC,EAAAA,aAAa,SACxBC,EAAAA,6BAA6B,IAAIF,CAAM,EAEvC,MAAM,IAAI,MACR,SAASH,EAAU,MAAM,EAAG,CAAC,CAAC,8BAA8BG,CAAM,uBAAuB,CAAC,GAAGF,CAAc,EAAE,KAAK,IAAI,CAAC,EAAA,CAG7H,OAAS/E,EAAO,CAKd,GAAI,EAFFA,aAAiBoF,EAAAA,cACjBpF,EAAM,OAASqF,EAAAA,aAAa,iBAE5B,MAAMrF,CAEV,CAGA,MAAM,IAAI,QAAc,CAACC,EAASC,IAAW,CAC3C,MAAMC,EAAU,IAAM,CACpB,aAAaC,CAAS,EACtBF,EACE,IAAI,MACF,6BAA6B4E,EAAU,MAAM,EAAG,CAAC,CAAC,cAAc,CAAC,GAAGC,CAAc,EAAE,KAAK,IAAI,CAAC,GAAA,CAChG,CAEJ,EACM3E,EAAY,WAAW,IAAM,CACjCxC,GAAA,MAAAA,EAAQ,oBAAoB,QAASuC,GACrCF,EAAA,CACF,EAAGd,CAAc,EACjBvB,GAAA,MAAAA,EAAQ,iBAAiB,QAASuC,EAAS,CAAE,KAAM,IACrD,CAAC,CACH,CACF,CCUA,MAAMmF,GAAyB,KAAU,IAGnCC,OAAsD,IAAI,CAC9DL,EAAAA,aAAa,aACbA,EAAAA,aAAa,mBACbA,EAAAA,aAAa,4BACbA,eAAa,SACf,CAAC,EAEKM,OAA+C,IAAI,CACvDN,EAAAA,aAAa,6BACb,GAAGK,EACL,CAAC,EAYD,SAASE,GACPC,EACuB,CACvB,OAAOA,EAAoB,IAAKnE,IAAQ,CACtC,mBAAoBoE,EAAAA,wBAAwBpE,EAAG,cAAc,EAC7D,YAAaA,EAAG,UAAU,OAC1B,YAAaA,EAAG,UAAU,MAAA,EAC1B,CACJ,CASA,SAASqE,EAAuBC,EAAwB,CACtD,OAAOF,EAAAA,wBAAwBE,CAAM,EAAE,YAAA,CACzC,CAUA,SAASC,GACPC,EACAC,EACM,CACN,MAAMC,EAAS,CAACC,EAAeC,EAAYC,IAAsB,CAC/D,MAAM,IAAI,MACR,iBAAiBF,CAAK,KAAK,OAAOC,CAAC,CAAC,gEACC,OAAOC,CAAC,CAAC,4EAAA,CAGlD,EAIMC,EAAU,CACd,CAAC,kBAAmBN,EAAM,gBAAiBC,EAAQ,eAAe,EAClE,CAAC,mBAAoBD,EAAM,iBAAkBC,EAAQ,gBAAgB,EACrE,CAAC,gBAAiBD,EAAM,cAAeC,EAAQ,aAAa,EAC5D,CAAC,iBAAkBD,EAAM,eAAgBC,EAAQ,cAAc,CAAA,EAEjE,SAAW,CAACE,EAAOI,EAAWC,CAAW,IAAKF,EACxCC,IAAcC,GAChBN,EAAOC,EAAOI,EAAWC,CAAW,EAMxC,MAAMC,EAAaC,GACjBA,EAAK,IAAIb,CAAsB,EAAE,KAAA,EAC7Bc,EAAU,CAACP,EAAsBC,IAAyB,CAC9D,MAAMO,EAAIH,EAAUL,CAAC,EACfS,EAAIJ,EAAUJ,CAAC,EACrB,OAAOO,EAAE,SAAWC,EAAE,QAAUD,EAAE,MAAM,CAAC/F,EAAG8D,IAAM9D,IAAMgG,EAAElC,CAAC,CAAC,CAC9D,EAEKgC,EAAQX,EAAM,sBAAuBC,EAAQ,qBAAqB,GACrEC,EACE,wBACAF,EAAM,sBAAsB,KAAK,GAAG,EACpCC,EAAQ,sBAAsB,KAAK,GAAG,CAAA,EAIvCU,EACCX,EAAM,8BACNC,EAAQ,6BAAA,GAGVC,EACE,gCACAF,EAAM,8BAA8B,KAAK,GAAG,EAC5CC,EAAQ,8BAA8B,KAAK,GAAG,CAAA,EAOlD,MAAMa,EAAYjB,EAAuBI,EAAQ,sBAAsB,EACjEc,EAAcf,EAAM,OAAO,IAAKgB,GACpCnB,EAAuBmB,EAAE,sBAAsB,CAAA,EAE5CD,EAAY,SAASD,CAAS,GACjCZ,EACE,kCACAa,EAAY,KAAK,GAAG,GAAK,cACzBd,EAAQ,sBAAA,CAGd,CAoBA,SAASgB,GACPC,EACAC,EACAC,EACAC,EACM,CACN,MAAMC,EAAYzB,EAAuBwB,CAAoB,EACvDE,EAAe,CACnB1B,EAAuBsB,CAAgB,EACvC,GAAGC,EAAkB,IAAIvB,CAAsB,CAAA,EAE3C7E,EAAW,IAAI,IAAIuG,CAAY,EACrC,GAAIvG,EAAS,OAASuG,EAAa,OACjC,MAAM,IAAI,MACR,oGAAA,EAGJ,GAAIvG,EAAS,IAAIsG,CAAS,EACxB,MAAM,IAAI,MACR,6FAAA,EAIJ,MAAME,EAAcN,EAAY,IAAK1F,GACnCqE,EAAuBrE,EAAG,cAAc,CAAA,EAE1C,GAAI,IAAI,IAAIgG,CAAW,EAAE,OAASA,EAAY,OAC5C,MAAM,IAAI,MAAM,qDAAqD,EAGvE,MAAMC,EAAuBD,EAAY,OAAQ3G,GAAMA,IAAMyG,CAAS,EAChEnG,EAAc,IAAI,IAAIsG,CAAoB,EAC1CpG,EAAUkG,EAAa,OAAQrG,GAAM,CAACC,EAAY,IAAID,CAAC,CAAC,EACxDI,EAAQmG,EAAqB,OAAQvG,GAAM,CAACF,EAAS,IAAIE,CAAC,CAAC,EACjE,GAAIG,EAAQ,OAAS,GAAKC,EAAM,OAAS,EACvC,MAAM,IAAI,MACR,yFACGD,EAAQ,OAAS,EAAI,cAAcA,EAAQ,KAAK,IAAI,CAAC,IAAM,KAC3DC,EAAM,OAAS,EAAI,iBAAiBA,EAAM,KAAK,IAAI,CAAC,IAAM,GAAA,CAGnE,CAOA,SAASoG,GACPlG,EACAyE,EACA,CACA,MAAO,CACL,iBAAkBA,EAAQ,iBAC1B,YAAazE,EAAG,YAChB,WAAYyE,EAAQ,WACpB,YAAazE,EAAG,YAChB,uBAAwByE,EAAQ,uBAChC,sBAAuBA,EAAQ,sBAC/B,8BAA+BA,EAAQ,8BACvC,mBAAoBA,EAAQ,mBAC5B,cAAeA,EAAQ,cACvB,eAAgBA,EAAQ,eACxB,6BAA8BA,EAAQ,6BACtC,iBAAkBzE,EAAG,mBACrB,cAAeyE,EAAQ,cACvB,gBAAiBA,EAAQ,gBACzB,eAAgBA,EAAQ,eACxB,cAAeA,EAAQ,cACvB,6BAA8BA,EAAQ,6BACtC,yBAA0BA,EAAQ,wBAAA,CAEtC,CAMA,eAAe0B,GACbtD,EACA4B,EACA2B,EACAtI,EAC4C,CAC5C,MAAMuI,EAAgB,IAAIC,gBAAc,CACtC,QAAS7B,EAAQ,QACjB,UAAA5B,CAAA,CACD,EAEK0D,EAAgBH,EAAa,OACnCtI,GAAA,MAAAA,EAAa,EAAGyI,GAEhB,IAAIC,EAEJ,GAAIH,EAAc,uBAIhBG,GAHgB,MAAMH,EAAc,4BAClCD,EAAa,IAAKpG,GAAOkG,GAAwBlG,EAAIyE,CAAO,CAAC,CAAA,GAEpC,IAAKgC,GAAMA,EAAE,eAAe,MAClD,CACLD,EAAmB,CAAA,EACnB,QAASrD,EAAI,EAAGA,EAAIiD,EAAa,OAAQjD,IAAK,CAC5CrF,GAAA,MAAAA,EAAaqF,EAAGoD,GAChB,MAAMG,EAAS,MAAML,EAAc,sBACjCH,GAAwBE,EAAajD,CAAC,EAAGsB,CAAO,CAAA,EAElD+B,EAAiB,KAAKE,EAAO,SAAS,CACxC,CACF,CAEA,MAAMC,EAAgD,CAAA,EACtD,QAASxD,EAAI,EAAGA,EAAIiD,EAAa,OAAQjD,IACvCwD,EAAWP,EAAajD,CAAC,EAAE,kBAAkB,EAAI,CAC/C,iBAAkBqD,EAAiBrD,CAAC,CAAA,EAIxC,OAAArF,GAAA,MAAAA,EAAayI,EAAeA,GACrBI,CACT,CAcA,eAAsBC,GACpBzJ,EACe,CACf,KAAM,CACJ,aAAAmG,EACA,cAAAuD,EACA,UAAAhE,EACA,UAAAU,EACA,YAAAuD,EACA,eAAAhE,EACA,aAAAiE,EACA,UAAAlJ,EAAYkG,GACZ,OAAA1H,EACA,WAAAyB,CAAA,EACEX,EAGEuG,EAAS,MAAML,EAAmB,CACtC,aAAAC,EACA,UAAAC,EACA,eAAgBU,GAChB,UAAApG,EACA,OAAAxB,CAAA,CACD,EAGD,GAAI2H,GAAqB,IAAIN,CAAM,EACjC,OAYF,GATArH,GAAA,MAAAA,EAAQ,iBAKJ0K,IAAiB,QACnBxC,GAAsCwC,EAAcjE,CAAc,EAGhEkE,EAAAA,wBAAwBnE,CAAS,EAAG,CACtC,GAAI,CAACkE,EACH,MAAM,IAAI,MACR,kPAAA,EAOA,OAAOlE,EAAU,sBAAyB,YAC5C,MAAMA,EAAU,qBAAqBkE,CAAY,EAInD,MAAMlE,EAAU,oBAAoBkE,CAAY,CAClD,CAGA,MAAMtD,EAAW,MAAMoD,EAAc,oCACnC,CACE,WAAYtD,EACZ,aAAcuD,CAAA,EAEhBzK,CAAA,EAGFA,GAAA,MAAAA,EAAQ,iBAOR,MAAM4K,EAAwB5C,EAAuByC,CAAW,EAChErB,GACEhC,EAAS,IACTX,EAAe,uBACfA,EAAe,sBACfgE,CAAA,EAQF,MAAMI,EAAkBzD,EAAS,IAAI,OAClCzD,GAAOqE,EAAuBrE,EAAG,cAAc,IAAMiH,CAAA,EAElDE,EAAuBjD,GAA8BgD,CAAe,EACpEE,EAAoB,MAAMjB,GAC9BtD,EACAC,EACAqE,EACArJ,CAAA,EAGFzB,GAAA,MAAAA,EAAQ,iBAKR,MAAMgL,EAAgC,MAAMzE,GAAmB,CAC7D,eAAgBa,EAAS,gBACzB,UAAAZ,EACA,eAAgB,CACd,iBAAkBC,EAAe,iBACjC,WAAYA,EAAe,WAC3B,mBAAoBgE,EACpB,uBAAwBhE,EAAe,uBACvC,sBAAuBA,EAAe,sBACtC,8BACEA,EAAe,8BACjB,cAAeA,EAAe,cAC9B,eAAgBA,EAAe,eAC/B,eAAgBA,EAAe,eAC/B,cAAeA,EAAe,cAC9B,QAASA,EAAe,QACxB,6BAA8BA,EAAe,6BAC7C,gBAAiBA,EAAe,gBAChC,6BAA8BA,EAAe,6BAC7C,yBAA0BA,EAAe,wBAAA,CAC3C,CACD,EAEDzG,GAAA,MAAAA,EAAQ,iBAIR,MAAMiL,EAAgB,CAAE,GAAGF,CAAA,EAC3BE,EAAchI,EAAAA,eAAewH,CAAW,CAAC,EACvCO,EAA8B,kBAEhC,MAAMR,EAAc,6BAClB,CACE,WAAYtD,EACZ,aAAcuD,EACd,WAAYQ,EACZ,gCAAiCD,CAAA,EAEnChL,CAAA,CAEJ,CCvhBA,MAAMkL,GAAyB,IAAS,IAGlCC,OAAiD,IAAI,CACzD7D,EAAAA,aAAa,0BACb,GAAG8D,EAAAA,kBACL,CAAC,EAwBD,eAAsBC,GACpBvK,EACe,CACf,KAAM,CACJ,aAAAmG,EACA,cAAAqE,EACA,UAAApE,EACA,YAAAuD,EACA,eAAAc,EACA,UAAA/J,EAAY0J,GACZ,OAAAlL,CAAA,EACEc,EAEJd,GAAA,MAAAA,EAAQ,iBAGR,MAAMqH,EAAS,MAAML,EAAmB,CACtC,aAAAC,EACA,UAAAC,EACA,eAAgBiE,GAChB,UAAA3J,EACA,OAAAxB,CAAA,CACD,EAGGoL,EAAAA,mBAAmB,IAAI/D,CAAM,IAIjCrH,GAAA,MAAAA,EAAQ,iBAER,MAAMsL,EAAc,uBAClB,CACE,WAAYpE,EACZ,aAAcuD,EACd,iBAAkBc,CAAA,EAEpBvL,CAAA,EAEJ,CC/BO,SAASwL,EAAeC,EAA2B,CACxD,OAAOA,EAAM,cAAgBA,EAAM,SACrC,CAGO,SAASC,GACdC,EACAC,EACAC,EACW,CACX,MAAMC,EAAgBC,EAAAA,sBAAsBJ,CAAI,EAChD,MAAO,CACL,aAAcG,IAAkBC,EAAAA,sBAAsBH,CAAe,EACrE,UAAWE,IAAkBC,EAAAA,sBAAsBF,CAAY,CAAA,CAEnE,CAUO,SAASG,EACdC,EACAC,EACAC,EACW,CACX,MAAMC,EAAiBC,EAAgBJ,CAAK,EAC5C,MAAO,CACL,aAAcK,GAAUF,EAAgBC,EAAgBH,CAAgB,CAAC,EACzE,UAAWI,GAAUF,EAAgBC,EAAgBF,CAAa,CAAC,CAAA,CAEvE,CAEA,SAASE,EAAgBxD,EAAmC,CAC1D,OAAOA,EAAK,IAAIkD,EAAAA,qBAAqB,EAAE,KAAA,CACzC,CAEA,SAASO,GAAU/D,EAAsBC,EAA+B,CACtE,OAAOD,EAAE,SAAWC,EAAE,QAAUD,EAAE,MAAM,CAACgE,EAAKzF,IAAMyF,IAAQ/D,EAAE1B,CAAC,CAAC,CAClE,CAgCA,eAAsB0F,GACpB1L,EACe,CACf,KAAM,CACJ,wBAAA2L,EACA,cAAAC,EACA,sBAAAC,EACA,8BAAAC,EACA,QAAAxE,CAAA,EACEtH,EAEJ,GAAI,CAAC4L,EAAe,OAEpB,MAAMZ,EAAgBC,EAAAA,sBAAsBW,CAAa,EACzD,GAAIZ,IAAkBC,wBAAsBY,CAAqB,EAAG,OAEpE,MAAME,EAAqBd,EAAAA,sBACzB,MAAMa,EAAA,CAA8B,EAEtC,GAAId,IAAkBe,EAEtB,MAAM,IAAI,MACR,0CAA0CJ,CAAuB,8FAErCrE,EAAU,IAAIA,CAAO,GAAK,EAAE,EAAA,CAE5D,CC3HA,SAAS0E,EACPC,EACAC,EACAC,EACqB,CACrB,MAAM/N,EAAQ,GAAG6N,CAAI,yBAAyBC,EAAY,UAAU,IAC9DE,EAAmBC,EAAAA,uBACvBH,EAAY,UACZ,GAAGD,CAAI,sBAAsBC,EAAY,UAAU,GAAA,EAE/CI,EAAqBD,EAAAA,uBAAuBF,EAAiB/N,CAAK,EAExE,MAAO,CACL,aAAc8N,EAAY,WAC1B,iBAAAE,EACA,mBAAAE,EACA,QAASA,IAAuBF,CAAA,CAEpC,CAoBA,SAASG,GACPC,EACM,CACN,MAAMC,MAAW,IACjB,UAAWC,KAAeF,EAAc,CACtC,MAAMG,EAAgBF,EAAK,IAAIC,EAAY,kBAAkB,EAC7D,GAAIC,EACF,MAAM,IAAI,MACR,wCAAwCA,CAAa,QAChDD,EAAY,YAAY,kCACxBA,EAAY,kBAAkB,EAAA,EAGvCD,EAAK,IAAIC,EAAY,mBAAoBA,EAAY,YAAY,CACnE,CACF,CAEA,SAASE,GACPC,EACAC,EACAC,EACmB,CACnB,GACED,EAAI,aAAa,SAAWD,EAAM,aAAa,QAC/CC,EAAI,qBAAqB,SAAWD,EAAM,qBAAqB,OAE/D,MAAM,IAAI,MACR,qCAAqCC,EAAI,aAAa,MAAM,eACvDA,EAAI,qBAAqB,MAAM,oCAC/BD,EAAM,aAAa,MAAM,gBACzBA,EAAM,qBAAqB,MAAM,cAAA,EAI1C,MAAMG,EAAgBhB,EACpB,iBACA,CACE,WAAYa,EAAM,wBAClB,UAAWA,EAAM,6BAAA,EAEnBC,EAAI,aAAA,EAGAG,EAAeJ,EAAM,aAAa,IAAI,CAACK,EAAQ,IACnDlB,EAAW,eAAgBkB,EAAQJ,EAAI,aAAa,CAAC,CAAC,CAAA,EAElDK,EAAuBN,EAAM,qBAAqB,IAAI,CAACxI,EAAY,IACvE2H,EAAW,uBAAwB3H,EAAYyI,EAAI,qBAAqB,CAAC,CAAC,CAAA,EAG5E,OAAAP,GAA4B,CAC1BS,EACA,GAAGC,EACH,GAAGE,CAAA,CACJ,EAEM,CACL,cAAAH,EACA,aAAAC,EACA,qBAAAE,EAEA,+BAAgCF,EAC7B,IAAKG,GAAMA,EAAE,kBAA4B,EACzC,KAAA,EACH,uCAAwCD,EACrC,IAAKC,GAAMA,EAAE,kBAA4B,EACzC,KAAA,EACH,WAAAL,EACA,MAAAF,CAAA,CAEJ,CAQA,eAAsBQ,GAA8BrN,EAGrB,CAC7B,MAAM8M,EAAM,MAAM9M,EAAO,mBAAmB,wBAC1CA,EAAO,KAAA,EAET,OAAO4M,GAAS5M,EAAO,MAAO8M,EAAK,CAAE,KAAM,UAAW,CACxD,CAUA,eAAsBQ,GAA+BtN,EAItB,CAC7B,MAAM8M,EAAM,MAAM9M,EAAO,mBAAmB,yBAC1CA,EAAO,MACPA,EAAO,MAAA,EAET,OAAO4M,GAAS5M,EAAO,MAAO8M,EAAK,CACjC,KAAM,SACN,OAAQ9M,EAAO,MAAA,CAChB,CACH,CC/GA,MAAMuN,GAAaxF,GAAmBA,EAAK,IAAIkD,EAAAA,qBAAqB,EAAE,KAAA,EAEtE,eAAsBuC,GACpBxN,EAC0C,CAC1C,KAAM,CACJ,oBAAAK,EACA,kBAAAoN,EACA,0BAAAC,EACA,wBAAA/B,EACA,sBAAAgC,EACA,+BAAAC,EACA,8BAAAC,EACA,sCAAAC,EACA,mBAAAC,EACA,8BAAAC,EACA,2BAAAC,CAAA,EACEjO,EAEE,CACJkO,EACAC,EACAC,CAAA,EACE,MAAM,QAAQ,IAAI,CACpB/N,EAAoB,iCAClBsL,CAAA,EAEF8B,EAAkB,8BAA8BE,CAAqB,EACrED,EAA0B,qCAAA,CAAqC,CAChE,EAEK,CAACW,EAAgBC,CAAkB,EAAI,MAAM,QAAQ,IAAI,CAC7Db,EAAkB,yBAChBE,EACAQ,CAAA,EAEFT,EAA0B,iCACxBU,CAAA,CACF,CACD,EAEKhD,EAAmB,CACvB,cAAeH,EAAAA,sBAAsBiD,CAAY,EACjD,aAAcX,GAAUc,EAAe,IAAKjB,GAAMA,EAAE,SAAS,CAAC,EAC9D,qBAAsBG,GAAUe,EAAmB,IAAKlB,GAAMA,EAAE,SAAS,CAAC,CAAA,EAUtEmB,EACJ,MAAMlB,GAA8B,CAClC,mBAAAU,EACA,MAAO,CACL,wBAAApC,EACA,8BAA+B,KAAKuC,CAAY,GAChD,sBAAAP,EACA,aAAcU,EACd,qBAAsBC,CAAA,CACxB,CACD,EAEGjD,EAAgB,CACpB,cAAekD,EAAgB,cAAc,mBAC7C,aAAc,CAAC,GAAGA,EAAgB,8BAA8B,EAChE,qBAAsB,CACpB,GAAGA,EAAgB,sCAAA,CACrB,EAcIC,EAAqB5D,GACzBgD,EACAxC,EAAiB,cACjBC,EAAc,aAAA,EAEVoD,EAAyBvD,EAC7B2C,EACAzC,EAAiB,aACjBC,EAAc,YAAA,EAEVqD,EAA6BxD,EACjC4C,EACA1C,EAAiB,qBACjBC,EAAc,oBAAA,EAGhB,GAAI,CAACX,EAAe8D,CAAO,EACzB,MAAM,IAAI,MACR,8EAA8E7C,CAAuB,0BAAA,EAGzG,GAAI,CAACjB,EAAe+D,CAAW,EAC7B,MAAM,IAAI,MACR,8BAA8BN,CAA8B,uFAAA,EAGhE,GAAI,CAACzD,EAAegE,CAAe,EACjC,MAAM,IAAI,MACR,sCAAsCN,CAAmC,kFAAA,EAU7E,MAAMO,EAAQ,CAACH,EAASC,EAAaC,CAAe,EAC9CE,GAAmBD,EAAM,KAAME,GAAMA,EAAE,cAAgB,CAACA,EAAE,SAAS,EACnEC,EAAgBH,EAAM,KAAME,GAAMA,EAAE,WAAa,CAACA,EAAE,YAAY,EAEtE,GAAID,IAAoBE,EAAe,CACrC,MAAMpP,EACJ,4EACGiM,CAAuB,0FAK5B,MAAAsC,GAAA,MAAAA,EAA6BvO,GACvB,IAAI,MAAM,GAAGA,CAAO,yBAAyB,CACrD,CAEA,OAAIoP,IACFd,GAAA,MAAAA,EACE,gEAAgErC,CAAuB,KAIpF,CACL,4BAA6BN,EAAc,cAC3C,4BAA6BA,EAAc,aAC3C,oCAAqCA,EAAc,qBACnD,+BAAA8C,EACA,oCAAAC,EACA,iBAAAhD,EACA,gBAAAmD,CAAA,CAEJ,CC5IA,SAASQ,GAAgBC,EAAsB,CAC7C,MAAO,oBAAoB,KAAKA,CAAG,CACrC,CAYO,SAASC,GACdjP,EACS,CACT,KAAM,CACJ,WAAAkP,EACA,WAAAC,EACA,WAAAC,EACA,WAAAC,EACA,iBAAAC,EACA,oBAAAC,CAAA,EACEvP,EASJ,MAPI,EAAAkP,GAAc,IACdA,EAAaC,GACbC,GAAcA,EAAa,IAAMF,EAAaE,GAE9CE,GAAoB,MAAQC,GAAuB,MAEjCL,EAAaI,EAAmBC,EAClCF,EAGtB,CAKO,SAASG,GACdC,EACAN,EACAC,EACkB,CAClB,OAAIK,GAAU,GACL,CACL,MAAO,GACP,MAAO,0CAAA,EAIPA,EAASN,EACJ,CACL,MAAO,GACP,MAAO,sBAAsBO,sBAAoBP,CAAU,CAAC,MAAA,EAI5DC,GAAcA,EAAa,IAAMK,EAASL,EACrC,CACL,MAAO,GACP,MAAO,sBAAsBM,sBAAoBN,CAAU,CAAC,MAAA,EAIzD,CAAE,MAAO,EAAA,CAClB,CAKO,SAASO,GACd3P,EACkB,CAClB,KAAM,CAAE,OAAAyP,EAAQ,mBAAAG,CAAA,EAAuB5P,EACvC,OAAI4P,IAAuB,KAAa,CAAE,MAAO,EAAA,EAE7CA,IAAuB,GAClB,CACL,MAAO,GACP,MAAO,kDAAA,EAIPH,EAASG,EACJ,CACL,MAAO,GACP,MAAO,0CAA0CF,sBAAoBE,CAAkB,CAAC,OAAA,EAIrF,CAAE,MAAO,EAAA,CAClB,CAOO,SAASC,GACdC,EACAC,EACkB,CAClB,GAAI,CAACD,GAAqBA,EAAkB,SAAW,EACrD,MAAO,CACL,MAAO,GACP,MAAO,8CAAA,EAIX,MAAME,EAA0BD,EAAmB,IAAK3C,GACtDA,EAAE,YAAA,CAAY,EAMhB,OAJyB0C,EAAkB,OACxC1C,GAAM,CAAC4C,EAAwB,SAAS5C,EAAE,aAAa,CAAA,EAGrC,OAAS,EACrB,CACL,MAAO,GACP,MAAO,iCAAA,EAIJ,CAAE,MAAO,EAAA,CAClB,CAQO,SAAS6C,GACdC,EACAf,EACAC,EACkB,CAClB,GAAI,CAACc,GAAWA,EAAQ,SAAW,EACjC,MAAO,CACL,MAAO,GACP,MAAO,oCAAA,EAIX,QAASlK,EAAI,EAAGA,EAAIkK,EAAQ,OAAQlK,IAAK,CACvC,MAAMyJ,EAASS,EAAQlK,CAAC,EACxB,GAAIyJ,GAAU,GACZ,MAAO,CACL,MAAO,GACP,MAAO,SAASzJ,EAAI,CAAC,0BAAA,EAGzB,GAAImJ,GAAcM,EAASN,EACzB,MAAO,CACL,MAAO,GACP,MAAO,SAASnJ,EAAI,CAAC,WAAW0J,sBAAoBD,CAAM,CAAC,iCAAiCC,sBAAoBP,CAAU,CAAC,MAAA,EAG/H,GAAIC,GAAcK,EAASL,EACzB,MAAO,CACL,MAAO,GACP,MAAO,SAASpJ,EAAI,CAAC,WAAW0J,sBAAoBD,CAAM,CAAC,gCAAgCC,sBAAoBN,CAAU,CAAC,MAAA,CAGhI,CAEA,MAAO,CAAE,MAAO,EAAA,CAClB,CAKO,SAASe,GAA4BhJ,EAAkC,CAC5E,MAAMiJ,EAAWjO,EAAAA,eAAegF,CAAM,EACtC,OAAK4H,GAAgBqB,CAAQ,EAOtB,CAAE,MAAO,EAAA,EANP,CACL,MAAO,GACP,MACE,4FAAA,CAIR,CAMA,SAASC,GAAqBC,EAAuC,CACnE,GAAI,CAACA,GAAyBA,EAAsB,SAAW,EAC7D,MAAM,IAAI,MACR,gGAAA,CAGN,CAEA,SAASC,GACPvO,EACM,CACN,GACE,CAACA,GACDA,EAA8B,SAAW,EAEzC,MAAM,IAAI,MACR,gHAAA,CAGN,CAEA,SAASwO,GAAkBC,EAAkC,CAC3D,GAAIA,EAAe,SAAW,EAC5B,MAAM,IAAI,MAAM,8BAA8B,CAElD,CAaO,SAASC,GACd1Q,EACM,CACN,KAAM,CACJ,aAAA2Q,EACA,eAAAF,EACA,uBAAAG,EACA,sBAAAN,EACA,8BAAAtO,EACA,WAAAmN,EACA,WAAAC,CAAA,EACEpP,EAEE6Q,EAAoBZ,GACxBU,EACAxB,EACAC,CAAA,EAEF,GAAI,CAACyB,EAAkB,MACrB,MAAM,IAAI,MAAMA,EAAkB,KAAK,EAIzC,MAAMC,EAAmBX,GAA4BS,CAAsB,EAC3E,GAAI,CAACE,EAAiB,MACpB,MAAM,IAAI,MAAMA,EAAiB,KAAK,EAGxCT,GAAqBC,CAAqB,EAC1CC,GAA6BvO,CAA6B,EAC1DwO,GAAkBC,CAAc,CAClC,CCxSO,MAAMM,UAAiC,KAAM,CAClD,YAAYrR,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,0BACd,CACF,CAIO,SAASsR,GACdnR,EACiC,CACjC,OACEA,aAAekR,GACdlR,aAAe,OAASA,EAAI,OAAS,0BAE1C,CAeA,SAASoR,GACP7S,EACAiE,EACA6O,EACe,CACf,OACE7O,EAAS,SAAW6O,EAAO,QAC3B7O,EAAS,MAAM,CAACH,EAAG8D,IAAM9D,IAAMgP,EAAOlL,CAAC,CAAC,EAEjC,KAEF,GAAG5H,CAAK,cAAciE,EAAS,KAAK,IAAI,CAAC,WAAW6O,EAAO,KAAK,IAAI,CAAC,GAC9E,CAEA,eAAsBC,GACpBnR,EACe,CACf,KAAM,CAAE,oBAAAK,EAAqB,mBAAA0N,EAAoB,SAAAxN,EAAU,SAAA8B,GACzDrC,EACI6M,EAAQxK,EAAS,MAEvB,GAAI9B,EAAS,SAAW,EAAG,OAM3B,MAAM6Q,EACJ,MAAM/Q,EAAoB,uBAAuBE,CAAQ,EAErD8Q,EAAuB,CAAA,EAE7B,SAAW,CAACrL,EAAGsL,CAAM,IAAKF,EAAe,UAAW,CAClD,MAAMtS,EAAUyB,EAASyF,CAAC,EAE1B,IAAIuL,EACJ,GAAI,CACFA,EAAW,MAAMjE,GAA+B,CAC9C,mBAAAS,EACA,MAAAlB,EACA,OAAAyE,CAAA,CACD,CACH,OAAShQ,EAAO,CAId+P,EAAW,KACT,SAASvS,CAAO,yDACPwS,EAAO,UAAU,YAAYA,EAAO,iBAAiB,QACtDA,EAAO,UAAU,MAAOhQ,EAAgB,OAAO,EAAA,EAEzD,QACF,CAGEiQ,EAAS,cAAc,qBACvBlP,EAAS,cAAc,oBAEvBgP,EAAW,KACT,SAASvS,CAAO,iCACXuD,EAAS,cAAc,kBAAkB,SACzCkP,EAAS,cAAc,kBAAkB,EAAA,EAIlD,MAAMC,EAAaP,GACjB,SAASnS,CAAO,sBAChBuD,EAAS,+BACTkP,EAAS,8BAAA,EAEPC,GAAYH,EAAW,KAAKG,CAAU,EAE1C,MAAMC,EAAiBR,GACrB,SAASnS,CAAO,8BAChBuD,EAAS,uCACTkP,EAAS,sCAAA,EAEPE,GAAgBJ,EAAW,KAAKI,CAAc,CACpD,CAEA,GAAIJ,EAAW,OAAS,EACtB,MAAM,IAAIN,EACR,mFACMM,EAAW,KAAK,IAAI,CAAC,gGAAA,CAIjC,CCnJO,MAAMK,UAA4C,KAAM,CAC7D,YAAYhS,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,qCACd,CACF,CAIO,SAASiS,GACd9R,EAC4C,CAC5C,OACEA,aAAe6R,GACd7R,aAAe,OAASA,EAAI,OAAS,qCAE1C,CAEA,eAAsB+R,GACpB5R,EACe,CACf,KAAM,CACJ,oBAAAK,EACA,SAAAE,EACA,8BAAAsR,EACA,+BAAA1D,EACA,oCAAAC,EACA,yBAAA0D,CAAA,EACE9R,EAEEgB,EAAQ,MAAMX,EAAoB,qBAAqBE,CAAQ,EAE/D8Q,EAAuB,CAAA,EAyB7B,GAxBArQ,EAAM,QAAQ,CAACqH,EAAGrC,IAAM,CACtB,MAAM+L,EAAKxR,EAASyF,CAAC,EACjBqC,EAAE,wBAA0BwJ,GAC9BR,EAAW,KACT,SAASU,CAAE,8BAA8BF,CAA6B,UAAUxJ,EAAE,qBAAqB,EAAA,EAGvGA,EAAE,yBAA2B8F,GAC/BkD,EAAW,KACT,SAASU,CAAE,+BAA+B5D,CAA8B,UAAU9F,EAAE,sBAAsB,EAAA,EAG1GA,EAAE,8BAAgC+F,GACpCiD,EAAW,KACT,SAASU,CAAE,oCAAoC3D,CAAmC,UAAU/F,EAAE,2BAA2B,EAAA,EAGzHA,EAAE,mBAAqByJ,GACzBT,EAAW,KACT,SAASU,CAAE,gCAAgCD,CAAwB,8BAA8BzJ,EAAE,gBAAgB,EAAA,CAGzH,CAAC,EAEGgJ,EAAW,OAAS,EACtB,MAAM,IAAIK,EACR,+FAA+FL,EAAW,KAAK,IAAI,CAAC,gGAAA,CAG1H,CCxEO,IAAKW,GAAAA,IACVA,EAAA,qBAAuB,qBACvBA,EAAA,gBAAkB,iBAClBA,EAAA,iBAAmB,kBACnBA,EAAA,iBAAmB,kBACnBA,EAAA,eAAiB,gBALPA,IAAAA,GAAA,CAAA,CAAA,EAQZ,MAAMC,OAA+B,IAAY,CAC/C,kBACA,eACF,CAAC,EAGM,SAASC,GAAyB3L,EAAyB,CAChE,OAAO,OAAO,OAAOyL,CAAwB,EAAE,SAC7CzL,CAAA,CAEJ,CAOO,SAAS4L,GACdC,EACS,CACT,MAAO,CAAC,CAACA,GAAiBH,GAAyB,IAAIG,CAAa,CACtE,CC3BO,MAAMC,WAA4B,KAAM,CAI7C,YAAYvT,EAAcwT,EAAc,CACtC,MAAM,4CAA4CA,EAAM,OAAO,EAAE,EAJnDC,EAAA,gBACSA,EAAA,cAIvB,KAAK,KAAO,sBACZ,KAAK,QAAUzT,EACf,KAAK,MAAQwT,CACf,CACF,CCOA,MAAMvU,GAAiB,sBAKjByU,GAAmB,+BAKnBC,GAAgB,+CAKTC,EAAe,IAgBfC,EAA8B,IAQ9BC,EAAoC,IACpCC,EAAsC,KAQ5C,SAASC,GAAsBC,EAA+B,CACnE,GAAI,CAAC,OAAO,SAASA,CAAa,GAAKA,GAAiB,EACtD,MAAM,IAAI,MACR,uDAAuDA,CAAa,EAAA,EAGxE,OAAO,OAAO,KAAK,KAAKA,EAAgBL,CAAY,CAAC,CACvD,CAKA,MAAMM,GAAqB,EACrBC,GAAiB,mBAEvB,SAAS/U,EAAcC,EAAeC,EAAqB,CACzD,GAAID,EAAM,SAAW,GACnB,MAAM,IAAI,MACR,GAAGC,CAAK,+DAA+DD,EAAM,MAAM,EAAA,EAGvF,GAAI,CAACJ,GAAe,KAAKI,CAAK,EAC5B,MAAM,IAAI,MACR,GAAGC,CAAK,uDAAA,CAGd,CA0IA,SAAS8U,EAAyB/U,EAAeC,EAAqB,CACpE,GAAI,CAAC,OAAO,UAAUD,CAAK,GAAKA,EAAQ,EACtC,MAAM,IAAI,MAAM,GAAGC,CAAK,wCAAwCD,CAAK,EAAE,CAE3E,CAEA,SAASgV,GAAwB9K,EAA0B,CAEzD,GADAnK,EAAcmK,EAAE,SAAU,UAAU,EAChC,CAAC,OAAO,UAAUA,EAAE,QAAQ,GAAKA,EAAE,SAAW,EAChD,MAAM,IAAI,MACR,gDAAgDA,EAAE,QAAQ,EAAA,EAU9D,GAAI,CAAC,MAAM,QAAQA,EAAE,KAAK,GAAKA,EAAE,MAAM,SAAW,EAChD,MAAM,IAAI,MAAM,iDAAiD,EAEnE,GAAIA,EAAE,UAAYA,EAAE,MAAM,OACxB,MAAM,IAAI,MACR,YAAYA,EAAE,QAAQ,sCAAsCA,EAAE,MAAM,MAAM,EAAA,EAG9E,QAASrC,EAAI,EAAGA,EAAIqC,EAAE,MAAM,OAAQrC,IAAK,CACvC,MAAMT,EAAQ8C,EAAE,MAAMrC,CAAC,EAEvB,GADA9H,EAAcqH,EAAM,SAAU,SAASS,CAAC,YAAY,EAChD,CAAC,OAAO,UAAUT,EAAM,QAAQ,GAAKA,EAAM,WAAaS,EAC1D,MAAM,IAAI,MACR,SAASA,CAAC,yBAAyBA,CAAC,0CAA0CT,EAAM,QAAQ,EAAA,EAGhG,GAAI,OAAOA,EAAM,QAAW,UAAYA,EAAM,QAAU,GACtD,MAAM,IAAI,MACR,SAASS,CAAC,2CAA2CT,EAAM,MAAM,EAAA,CAGvE,CACA,MAAM6N,EAAc/K,EAAE,MAAMA,EAAE,QAAQ,EACtC,GAAI+K,EAAY,SAAS,YAAA,IAAkB/K,EAAE,SAAS,cACpD,MAAM,IAAI,MACR,SAASA,EAAE,QAAQ,eAAe+K,EAAY,QAAQ,qCAAqC/K,EAAE,QAAQ,GAAA,EAGzG,GAAI+K,EAAY,SAAW/K,EAAE,OAC3B,MAAM,IAAI,MACR,SAASA,EAAE,QAAQ,aAAa+K,EAAY,MAAM,mCAAmC/K,EAAE,MAAM,GAAA,EAiBjG,GAVA6K,EAAyB7K,EAAE,sBAAuB,uBAAuB,EACzE6K,EAAyB7K,EAAE,uBAAwB,wBAAwB,EAC3E6K,EACE7K,EAAE,4BACF,6BAAA,EAEFgL,GAAAA,4BACEhL,EAAE,iBACF,kCAAA,EAGA,OAAOA,EAAE,uBAA0B,UACnCA,EAAE,sBAAsB,SAAW,EAEnC,MAAM,IAAI,MAAM,sDAAsD,EAExE,GAAI,CAACmK,GAAiB,KAAKnK,EAAE,qBAAqB,EAChD,MAAM,IAAI,MACR,mFAAA,EAGJ,GAAI,CAACA,EAAE,oBAAsB,CAACoK,GAAc,KAAKpK,EAAE,kBAAkB,EACnE,MAAM,IAAI,MACR,uEAAA,EAGJ,GAAI,OAAOA,EAAE,QAAW,UAAYA,EAAE,QAAU,GAC9C,MAAM,IAAI,MAAM,yCAAyCA,EAAE,MAAM,EAAE,CAEvE,CAEA,SAASiL,GAA8B/Q,EAAgC,CACrE,GAAI,CAACA,EAAE,qBAAuB,CAACkQ,GAAc,KAAKlQ,EAAE,mBAAmB,EACrE,MAAM,IAAI,MAAM,mDAAmD,EAErE,GAAIA,EAAE,mBAAmB,SAAW,EAClC,MAAM,IAAI,MAAM,sCAAsC,EAExD,GAAIA,EAAE,2BAA2B,SAAW,EAC1C,MAAM,IAAI,MAAM,8CAA8C,EAEhE,GAAI,CAAC,OAAO,UAAUA,EAAE,cAAc,GAAKA,EAAE,gBAAkB,EAC7D,MAAM,IAAI,MACR,kDAAkDA,EAAE,cAAc,EAAA,EAGtE,GAAI,OAAOA,EAAE,SAAY,UAAYA,EAAE,SAAW,GAChD,MAAM,IAAI,MACR,mDAAmDA,EAAE,OAAO,EAAA,EAGhE,GAAI,OAAOA,EAAE,iBAAoB,UAAYA,EAAE,iBAAmB,GAChE,MAAM,IAAI,MACR,kDAAkDA,EAAE,eAAe,EAAA,EAGvE,GAAI,CAAC,OAAO,UAAUA,EAAE,mBAAmB,GAAKA,EAAE,oBAAsB,EACtE,MAAM,IAAI,MAAM,oDAAoD,EAEtE,GACE,CAAC,OAAO,UAAUA,EAAE,aAAa,GACjC,CAAC,OAAO,UAAUA,EAAE,WAAW,GAC/BA,EAAE,eAAiB,GACnBA,EAAE,aAAe,GACjBA,EAAE,cAAgBA,EAAE,YAEpB,MAAM,IAAI,MACR,kBAAkBA,EAAE,aAAa,gCAAgCA,EAAE,WAAW,GAAA,CAGpF,CAEA,SAASgR,GAAmBC,EAA+B,CACzD,MAAMC,EAAOC,EAAAA,KAAK,QAAQF,CAAa,EACvC,GAAI,CACFC,EAAK,kBAAA,CACP,OAASE,EAAY,CAGnB,MAAMjU,EAAUiU,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,EACzD,GAAI,CAACjU,EAAQ,SAAS,mBAAmB,EACvC,MAAM,IAAI,MAAM,mCAAmCA,CAAO,EAAE,CAEhE,CACA,OAAO+T,EAAK,mBAAA,EAAqB,MAAA,CACnC,CAsBA,eAAsBG,GAEpBpV,EAAmC,CACnC,KAAM,CACJ,QAAAM,EACA,UAAA+U,EACA,oBAAAC,EACA,QAAAC,EACA,SAAAC,EACA,YAAAC,EACA,OAAA/U,CAAA,EACEV,EAEJU,GAAA,MAAAA,EAAQ,iBACRhB,EAAcY,EAAS,SAAS,EAEhC,MAAMoV,EAAQ,MAAML,EAAA,EACpBV,GAAwBe,CAAK,EAC7BhV,GAAA,MAAAA,EAAQ,iBAER,MAAMwE,EAAM,MAAMoQ,EAAoBI,CAAK,EAI3C,GAHAZ,GAA8B5P,CAAG,EACjCxE,GAAA,MAAAA,EAAQ,iBAEJ,CAAC,OAAO,SAAS6U,CAAO,GAAKA,GAAW,EAC1C,MAAM,IAAI,MAAM,0CAA0CA,CAAO,EAAE,EAMrE,GAAIA,EAAUpB,EACZ,MAAM,IAAI,MACR,WAAWoB,CAAO,qCACbpB,CAA2B,mCAAA,EAGpC,MAAMwB,EAAY,OAAO,KAAK,KAAKJ,EAAUrB,CAAY,CAAC,EAIpD0B,EACHF,EAAM,OAAStB,EAChBC,EACF,GAAIsB,EAAYC,EACd,MAAM,IAAI,MACR,cAAcD,CAAS,6CACfC,CAAgB,UAClBxB,CAAiC,IAAIC,CAAmC,oBACzDqB,EAAM,MAAM,6BAAA,EAGrChV,GAAA,MAAAA,EAAQ,iBAMR,MAAMmV,EAAuBpN,EAAAA,wBAC3BiN,EAAM,kBAAA,EAGFI,EAA2BnS,EAAAA,eAAe+R,EAAM,qBAAqB,EASrEK,EAAQC,EAAAA,uBAAuBF,CAAwB,EAC7D,GAAIC,IAAU,QAAaA,EAAM,OAASL,EAAM,MAAM,OACpD,MAAM,IAAI,MACR,iCAAiCK,EAAM,IAAI,+BACrCL,EAAM,MAAM,MAAM,2CAA2CA,EAAM,MAAM,MAAM,wDAAA,EAIzF,MAAMO,EAAiBF,GAAA,YAAAA,EAAO,KAO9B,IAAIG,EACJ,GAAI,CACFA,EAAiBtQ,EAAAA,YAAY,QAAQkQ,CAAwB,CAC/D,OAASX,EAAG,CACV,MAAM,IAAI,MACR,qDAAqDA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAA,CAEnG,CACA,GAAIe,EAAe,KAAK,OAASR,EAAM,MAAM,OAC3C,MAAM,IAAI,MACR,2BAA2BQ,EAAe,KAAK,MAAM,wCAC9BR,EAAM,MAAM,MAAM,gFAAA,EAK7C,KAAM,CAAE,QAAAS,GAAY,MAAMC,kBAAgB,CACxC,eAAgB,CACd,iBAAkBV,EAAM,iBACxB,gBAAiBG,EACjB,oBAAqBlS,EAAAA,eAAeuB,EAAI,mBAAmB,EAC3D,mBAAoBA,EAAI,mBAAmB,IAAIvB,EAAAA,cAAc,EAC7D,2BACEuB,EAAI,2BAA2B,IAAIvB,EAAAA,cAAc,EACnD,UAAW+R,EAAM,MAAM,IAAKxM,GAAMvF,EAAAA,eAAeuF,EAAE,QAAQ,CAAC,EAC5D,eAAgBhE,EAAI,eAOpB,aAAcwQ,EAAM,MAAM,IAAKxM,GAAMA,EAAE,MAAM,EAC7C,QAAShE,EAAI,QACb,gBAAiBA,EAAI,gBACrB,oBAAqBA,EAAI,oBACzB,cAAeA,EAAI,cACnB,YAAaA,EAAI,YACjB,QAASA,EAAI,QACb,eAAA+Q,CAAA,EAEF,oBAAqBH,EACrB,SAAUJ,EAAM,SAChB,UAAAC,EAKA,SAAUhS,EAAAA,eAAe+R,EAAM,QAAQ,CAAA,CACxC,EACDhV,GAAA,MAAAA,EAAQ,iBAER,MAAM0E,EAAcK,EAAAA,mCAClBiQ,EAAM,mBACNlB,EAAA,EAEIQ,EAAgB,MAAMQ,EAASW,EAAS/Q,CAAW,EAEzDsB,8BAA4B,CAC1B,iBAAkByP,EAClB,gBAAiBnB,CAAA,CAClB,EAKD,MAAMqB,EAA4B,EAC5BC,EAAkB1P,EAAAA,uBACtBoO,EACAa,EACAQ,CAAA,EAEFxP,mCAAiC,CAC/B,iBAAkBsP,EAClB,aAAcG,EACd,qBAAsBT,EACtB,WAAYQ,CAAA,CACb,EAED,MAAME,EAAcxB,GAAmBC,CAAa,EACpDtU,GAAA,MAAAA,EAAQ,iBAER,GAAI,CACF,OAAO,MAAM+U,EAAYc,CAAW,CACtC,OAASzT,EAAO,CACd,MAAIA,aAAiB,OAAS2R,GAAe,KAAK3R,EAAM,OAAO,EACvD,IAAI+Q,GAAoBvT,EAASwC,CAAK,EAExCA,CACR,CACF"}