@babylonlabs-io/ts-sdk 0.62.1 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/{BTCVaultRegistry.abi-B3BLhpJ4.js → BTCVaultRegistry.abi-CJwWI2EO.js} +5 -1
  2. package/dist/{BTCVaultRegistry.abi-B3BLhpJ4.js.map → BTCVaultRegistry.abi-CJwWI2EO.js.map} +1 -1
  3. package/dist/{BTCVaultRegistry.abi-3VLWva2x.cjs → BTCVaultRegistry.abi-z7d_eP22.cjs} +2 -2
  4. package/dist/{BTCVaultRegistry.abi-3VLWva2x.cjs.map → BTCVaultRegistry.abi-z7d_eP22.cjs.map} +1 -1
  5. package/dist/{PeginManager-C8kIl0gw.js → PeginManager-BBl4xkWz.js} +7 -6
  6. package/dist/{PeginManager-C8kIl0gw.js.map → PeginManager-BBl4xkWz.js.map} +1 -1
  7. package/dist/{PeginManager-BgBt4hqp.cjs → PeginManager-p-ajwSxG.cjs} +2 -2
  8. package/dist/{PeginManager-BgBt4hqp.cjs.map → PeginManager-p-ajwSxG.cjs.map} +1 -1
  9. package/dist/{ProtocolParams.abi-CV938AYZ.js → ProtocolParams.abi-C0TSRaHV.js} +17 -1
  10. package/dist/{ProtocolParams.abi-CV938AYZ.js.map → ProtocolParams.abi-C0TSRaHV.js.map} +1 -1
  11. package/dist/{ProtocolParams.abi-C8U7BHCS.cjs → ProtocolParams.abi-CYZoV2EI.cjs} +2 -2
  12. package/dist/{ProtocolParams.abi-C8U7BHCS.cjs.map → ProtocolParams.abi-CYZoV2EI.cjs.map} +1 -1
  13. package/dist/batchPoll-6L5oToGr.cjs +2 -0
  14. package/dist/batchPoll-6L5oToGr.cjs.map +1 -0
  15. package/dist/{batchPoll-9w-Gv5dt.js → batchPoll-cIxrSLLJ.js} +43 -21
  16. package/dist/batchPoll-cIxrSLLJ.js.map +1 -0
  17. package/dist/{buildAndBroadcastRefund-CH2awK6h.js → buildAndBroadcastRefund-C2fP3Rva.js} +5 -5
  18. package/dist/{buildAndBroadcastRefund-CH2awK6h.js.map → buildAndBroadcastRefund-C2fP3Rva.js.map} +1 -1
  19. package/dist/{buildAndBroadcastRefund-CYzhA_B8.cjs → buildAndBroadcastRefund-CYtnT0D_.cjs} +2 -2
  20. package/dist/{buildAndBroadcastRefund-CYzhA_B8.cjs.map → buildAndBroadcastRefund-CYtnT0D_.cjs.map} +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.js +157 -146
  23. package/dist/{mempoolApi-BXaOZL_I.js → mempoolApi-BxSINXIB.js} +2 -2
  24. package/dist/{mempoolApi-BXaOZL_I.js.map → mempoolApi-BxSINXIB.js.map} +1 -1
  25. package/dist/{mempoolApi-Bsurcl43.cjs → mempoolApi-CXfr-L6x.cjs} +2 -2
  26. package/dist/{mempoolApi-Bsurcl43.cjs.map → mempoolApi-CXfr-L6x.cjs.map} +1 -1
  27. package/dist/{noPayout-oaUVsEjk.js → noPayout-BaZGRLD2.js} +2 -2
  28. package/dist/{noPayout-oaUVsEjk.js.map → noPayout-BaZGRLD2.js.map} +1 -1
  29. package/dist/{noPayout-CbxQqBw1.cjs → noPayout-CnQ1WLtr.cjs} +2 -2
  30. package/dist/{noPayout-CbxQqBw1.cjs.map → noPayout-CnQ1WLtr.cjs.map} +1 -1
  31. package/dist/reconstructPeginParams-BVA67Iv8.js +400 -0
  32. package/dist/reconstructPeginParams-BVA67Iv8.js.map +1 -0
  33. package/dist/reconstructPeginParams-BmUVK7F_.cjs +2 -0
  34. package/dist/reconstructPeginParams-BmUVK7F_.cjs.map +1 -0
  35. package/dist/{signPsbtsWithFallback-DC4rUCsy.js → signPsbtsWithFallback-Bb66DfY5.js} +2 -2
  36. package/dist/{signPsbtsWithFallback-DC4rUCsy.js.map → signPsbtsWithFallback-Bb66DfY5.js.map} +1 -1
  37. package/dist/{signPsbtsWithFallback-DbOmw8Ou.cjs → signPsbtsWithFallback-PTgWxsG3.cjs} +2 -2
  38. package/dist/{signPsbtsWithFallback-DbOmw8Ou.cjs.map → signPsbtsWithFallback-PTgWxsG3.cjs.map} +1 -1
  39. package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts +11 -0
  40. package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts.map +1 -1
  41. package/dist/tbv/core/clients/eth/types.d.ts +18 -0
  42. package/dist/tbv/core/clients/eth/types.d.ts.map +1 -1
  43. package/dist/tbv/core/clients/index.cjs +1 -1
  44. package/dist/tbv/core/clients/index.js +2 -2
  45. package/dist/tbv/core/contracts/abis/BTCVaultRegistry.abi.d.ts +4 -0
  46. package/dist/tbv/core/contracts/abis/BTCVaultRegistry.abi.d.ts.map +1 -1
  47. package/dist/tbv/core/contracts/abis/ProtocolParams.abi.d.ts +10 -0
  48. package/dist/tbv/core/contracts/abis/ProtocolParams.abi.d.ts.map +1 -1
  49. package/dist/tbv/core/contracts/index.cjs +1 -1
  50. package/dist/tbv/core/contracts/index.js +2 -2
  51. package/dist/tbv/core/index.cjs +1 -1
  52. package/dist/tbv/core/index.d.ts +3 -0
  53. package/dist/tbv/core/index.d.ts.map +1 -1
  54. package/dist/tbv/core/index.js +151 -140
  55. package/dist/tbv/core/managers/index.cjs +1 -1
  56. package/dist/tbv/core/managers/index.js +2 -2
  57. package/dist/tbv/core/primitives/index.cjs +1 -1
  58. package/dist/tbv/core/primitives/index.js +2 -2
  59. package/dist/tbv/core/primitives/psbt/assertWasmPeginSizing.d.ts +16 -2
  60. package/dist/tbv/core/primitives/psbt/assertWasmPeginSizing.d.ts.map +1 -1
  61. package/dist/tbv/core/recovery/__tests__/deriveHashlocksFromPrePegin.test.d.ts +10 -0
  62. package/dist/tbv/core/recovery/__tests__/deriveHashlocksFromPrePegin.test.d.ts.map +1 -0
  63. package/dist/tbv/core/recovery/__tests__/peginSizingIntegrity.test.d.ts +16 -0
  64. package/dist/tbv/core/recovery/__tests__/peginSizingIntegrity.test.d.ts.map +1 -0
  65. package/dist/tbv/core/recovery/__tests__/reconstructPeginParams.test.d.ts +14 -0
  66. package/dist/tbv/core/recovery/__tests__/reconstructPeginParams.test.d.ts.map +1 -0
  67. package/dist/tbv/core/recovery/deriveHashlocksFromPrePegin.d.ts +37 -0
  68. package/dist/tbv/core/recovery/deriveHashlocksFromPrePegin.d.ts.map +1 -0
  69. package/dist/tbv/core/recovery/index.d.ts +18 -0
  70. package/dist/tbv/core/recovery/index.d.ts.map +1 -0
  71. package/dist/tbv/core/recovery/peginParamsCandidates.d.ts +98 -0
  72. package/dist/tbv/core/recovery/peginParamsCandidates.d.ts.map +1 -0
  73. package/dist/tbv/core/recovery/reconstructPeginParams.d.ts +112 -0
  74. package/dist/tbv/core/recovery/reconstructPeginParams.d.ts.map +1 -0
  75. package/dist/tbv/core/recovery/recoveryErrors.d.ts +97 -0
  76. package/dist/tbv/core/recovery/recoveryErrors.d.ts.map +1 -0
  77. package/dist/tbv/core/services/index.cjs +1 -1
  78. package/dist/tbv/core/services/index.js +2 -2
  79. package/dist/tbv/index.cjs +1 -1
  80. package/dist/tbv/index.js +151 -140
  81. package/dist/verifyScriptPathSchnorrSignature-DPAd_qke.cjs +2 -0
  82. package/dist/verifyScriptPathSchnorrSignature-DPAd_qke.cjs.map +1 -0
  83. package/dist/{verifyScriptPathSchnorrSignature-XsICweGG.js → verifyScriptPathSchnorrSignature-i-koT139.js} +176 -170
  84. package/dist/verifyScriptPathSchnorrSignature-i-koT139.js.map +1 -0
  85. package/package.json +1 -1
  86. package/dist/batchPoll-9w-Gv5dt.js.map +0 -1
  87. package/dist/batchPoll-WMXweT_O.cjs +0 -2
  88. package/dist/batchPoll-WMXweT_O.cjs.map +0 -1
  89. package/dist/errors-BxL9QObN.js +0 -137
  90. package/dist/errors-BxL9QObN.js.map +0 -1
  91. package/dist/errors-DwjbI9h1.cjs +0 -2
  92. package/dist/errors-DwjbI9h1.cjs.map +0 -1
  93. package/dist/verifyScriptPathSchnorrSignature-B8zK1ctE.cjs +0 -2
  94. package/dist/verifyScriptPathSchnorrSignature-B8zK1ctE.cjs.map +0 -1
  95. package/dist/verifyScriptPathSchnorrSignature-XsICweGG.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconstructPeginParams-BmUVK7F_.cjs","sources":["../src/tbv/core/deposit-terms/depositTermsErrors.ts","../src/tbv/core/deposit-terms/rebuildDepositTermsCore.ts","../src/tbv/core/wots/errors.ts","../src/tbv/core/recovery/recoveryErrors.ts","../src/tbv/core/recovery/deriveHashlocksFromPrePegin.ts","../src/tbv/core/recovery/peginParamsCandidates.ts","../src/tbv/core/recovery/reconstructPeginParams.ts"],"sourcesContent":["/**\n * Typed rejection contract for deposit-terms approval. Providers cannot import\n * this class (no dependency edge between the packages), so the cross-package\n * contract is the error SHAPE on {@link DepositTermsApprover}. Consumers land\n * with the first provider (#2109) and its app error mapping (#2110).\n *\n * @module deposit-terms/depositTermsErrors\n */\n\n/** The `name` value providers must set on envelope rejections (the wire contract). */\nexport const DEPOSIT_TERMS_REJECTED_ERROR_NAME = \"DepositTermsRejectedError\";\n\n/** Why the terms were rejected before approval. */\nexport type DepositTermsRejectionReason = \"device-envelope\";\n\n/** SDK-owned typed rejection thrown when deposit terms fail pre-approval validation. */\nexport class DepositTermsRejectedError extends Error {\n readonly reason: DepositTermsRejectionReason;\n\n constructor(\n message: string,\n reason: DepositTermsRejectionReason = \"device-envelope\",\n ) {\n super(message);\n this.name = DEPOSIT_TERMS_REJECTED_ERROR_NAME;\n this.reason = reason;\n }\n}\n\n/**\n * Guard for app-side error mapping. Matches `instanceof` OR the documented\n * `name` — providers and foreign realms throw structurally-conforming plain\n * errors that `instanceof` cannot see.\n */\nexport function isDepositTermsRejectedError(\n err: unknown,\n): err is DepositTermsRejectedError {\n if (err instanceof DepositTermsRejectedError) return true;\n if (typeof err !== \"object\" || err === null) return false;\n const shaped = err as { name?: unknown; reason?: unknown };\n // The documented provider shape REQUIRES the reason; a name-only match is\n // a contract violation and propagates unnormalized.\n return (\n shaped.name === DEPOSIT_TERMS_REJECTED_ERROR_NAME &&\n shaped.reason === \"device-envelope\"\n );\n}\n","/**\n * Pure core of the resume-broadcast DepositTerms rebuild (#2220 Part 2).\n *\n * Takes plain chain-derived data (the app orchestrator does the chain reads +\n * sibling discovery) and: (1) asserts sibling completeness against the funded\n * tx's auth-anchor OP_RETURN, (2) recomputes the amount-independent sizing\n * (depositorClaimValue, peginMaxFee, anchor) via WASM, (3) byte-matches each\n * HTLC output's value + scriptPubKey against the funded tx (Gate 1), then (4)\n * projects into DepositTerms. No chain access, no browser state — unit-testable.\n *\n * btc-vault is the protocol source of truth (`compute_min_htlc_value`,\n * `derive_challengers_for`).\n */\n\nimport {\n computeMinClaimValue,\n computeMinPeginFee,\n getPrePeginHtlcConnectorInfo,\n peginP2aAnchorOutput,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport { findAuthAnchorOpReturn } from \"../managers/pegin/assertAuthAnchorOpReturn\";\nimport { assertEncodedHtlcOutputsMatch } from \"../primitives/psbt/assertWasmPeginSizing\";\nimport { stripHexPrefix } from \"../primitives/utils/bitcoin\";\nimport { MAX_REASONABLE_PEGIN_VBYTES } from \"../utils/fee/constants\";\nimport { calculateBtcTxHash } from \"../utils/transaction/btcTxHash\";\n\nimport { buildDepositTerms } from \"./buildDepositTerms\";\nimport type { DepositTerms } from \"./depositTerms\";\n\n/** One HTLC in the shared Pre-PegIn tx, ordered by (and contiguous in) htlcVout. */\nexport interface RebuildSibling {\n /** 32-byte hex hashlock (0x prefix optional), per-vault (feeds the HTLC scriptPubKey). */\n hashlock: string;\n /** btc-vault `pegin_amount` for this sibling (satoshis). */\n amount: bigint;\n}\n\nexport interface RebuildDepositTermsCoreInput {\n /** Stamped tx-graph version (NOT chain-active). */\n vaultCoreVersion: number;\n /** Sibling HTLCs ordered by htlcVout; index === htlcVout (asserted by the app). */\n siblings: readonly RebuildSibling[];\n /** Funded Pre-PegIn tx hex. Gate 0 (hash vs prepeginTxid) is SELF-verified below — callers need not pre-verify. */\n fundedPrePeginTxHex: string;\n\n // Stamped-version participant data (already resolved + sorted by the app).\n depositorBtcPubkey: string;\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengerBtcPubkeys: readonly string[];\n\n // Version-locked scalars.\n protocolFeeRate: bigint;\n minPeginFeeRate: bigint;\n councilQuorum: number;\n councilSize: number;\n timelockPegin: number;\n timelockAssert: number;\n timelockRefund: number;\n\n // Pass-through into DepositTerms.\n prepeginTxid: string;\n /** Funded-tx fee (Σin − Σout), computed by the app; the device's `prepegin_max_fee` bound. */\n prepeginMaxFee: bigint;\n maxAcceptableCommissionBps: number;\n\n /** WASM network descriptor for scriptPubKey derivation. */\n network: Parameters<typeof getPrePeginHtlcConnectorInfo>[0][\"network\"];\n}\n\nexport async function rebuildDepositTermsCore(\n input: RebuildDepositTermsCoreInput,\n): Promise<DepositTerms> {\n const siblingCount = input.siblings.length;\n if (siblingCount === 0) {\n throw new Error(\n \"rebuildDepositTermsCore: at least one sibling is required\",\n );\n }\n for (const [i, sibling] of input.siblings.entries()) {\n if (sibling.amount <= 0n) {\n throw new Error(\n `rebuildDepositTermsCore: sibling[${i}] has non-positive on-chain pegin ` +\n `amount ${sibling.amount}; the chain-read vault record is invalid. Resume refused.`,\n );\n }\n }\n if (input.prepeginMaxFee <= 0n) {\n throw new Error(\n `rebuildDepositTermsCore: prepeginMaxFee must be > 0, got ${input.prepeginMaxFee}`,\n );\n }\n\n // Gate 0, self-verified: everything below trusts these bytes, and a\n // substituted tx replicating the HTLC outputs would pass Gate 1 with a\n // different fee/txid.\n const expectedTxid = stripHexPrefix(input.prepeginTxid).toLowerCase();\n const actualTxid = stripHexPrefix(\n calculateBtcTxHash(input.fundedPrePeginTxHex),\n ).toLowerCase();\n if (actualTxid !== expectedTxid) {\n throw new Error(\n `Funded Pre-PegIn tx hashes to ${actualTxid}, expected ${expectedTxid} ` +\n `(on-chain prepeginTxid). Resume refused.`,\n );\n }\n\n // Completeness anchor: the single auth-anchor OP_RETURN sits at vout === HTLC\n // count — the only guard against an indexer-lagged partial sibling set.\n const found = findAuthAnchorOpReturn(\n stripHexPrefix(input.fundedPrePeginTxHex),\n );\n if (found === undefined) {\n throw new Error(\n `Funded Pre-PegIn carries no single, unambiguous auth-anchor OP_RETURN ` +\n `commitment — it either predates auth anchoring or is malformed. The ` +\n `intent resume path requires the anchor to prove sibling completeness. ` +\n `Resume refused.`,\n );\n }\n if (found.vout !== siblingCount) {\n throw new Error(\n `Auth-anchor OP_RETURN at vout ${found.vout} does not match the ` +\n `discovered sibling count ${siblingCount}; the discovered sibling set ` +\n `does not cover this transaction's HTLC outputs (a lagging index can ` +\n `cause this — retry later). Resume refused.`,\n );\n }\n\n // Amount-independent sizing. Depositor-as-claimer: numLocalChallengers ===\n // numVks (VP excluded) — btc-vault graph.rs derive_challengers_for.\n const numVks = input.vaultKeeperBtcPubkeys.length;\n const numUcs = input.universalChallengerBtcPubkeys.length;\n const depositorClaimValue = await computeMinClaimValue(\n input.vaultCoreVersion,\n numVks,\n numUcs,\n input.councilQuorum,\n input.councilSize,\n input.protocolFeeRate,\n );\n const peginMaxFee = await computeMinPeginFee(\n input.vaultCoreVersion,\n numVks,\n numUcs,\n input.minPeginFeeRate,\n );\n const anchor = await peginP2aAnchorOutput(input.vaultCoreVersion);\n const anchorValue = anchor?.value ?? 0n;\n\n // Independent bounds on the WASM outputs (mirrors assertWasmPeginSizing):\n // Gate 1 only proves the DCV+fee+anchor SUM — these constrain the\n // decomposition the device is shown.\n if (depositorClaimValue <= 0n) {\n throw new Error(\n `WASM returned non-positive depositorClaimValue ${depositorClaimValue}; ` +\n `expected > 0. Resume refused.`,\n );\n }\n if (peginMaxFee <= 0n) {\n throw new Error(\n `WASM returned non-positive peginMaxFee ${peginMaxFee}; expected > 0. ` +\n `Resume refused.`,\n );\n }\n // Explicit anchor check: the sum bound alone would let a negative anchor\n // offset an inflated peginMaxFee (kept local, not inherited from the facade).\n if (anchorValue < 0n) {\n throw new Error(\n `WASM returned negative P2A anchor value ${anchorValue}. Resume refused.`,\n );\n }\n const impliedReserve = peginMaxFee + anchorValue;\n const maxImpliedReserve = input.minPeginFeeRate * MAX_REASONABLE_PEGIN_VBYTES;\n if (impliedReserve <= 0n || impliedReserve > maxImpliedReserve) {\n throw new Error(\n `WASM implied PegIn reserve ${impliedReserve} sat is outside ` +\n `(0, ${maxImpliedReserve}] (minPeginFeeRate=${input.minPeginFeeRate} × ` +\n `${MAX_REASONABLE_PEGIN_VBYTES} vbytes). Resume refused.`,\n );\n }\n\n // Gate 1: per sibling, value = amount + DCV + peginMaxFee + anchor (btc-vault\n // compute_min_htlc_value) + scriptPubKey from the on-chain hashlock,\n // byte-matched against outputs 0..N-1.\n const expectedHtlcValues = input.siblings.map(\n (s) => s.amount + depositorClaimValue + peginMaxFee + anchorValue,\n );\n const expectedHtlcScriptPubKeys = await Promise.all(\n input.siblings.map(async (s) => {\n const connector = await getPrePeginHtlcConnectorInfo({\n txGraphVersion: input.vaultCoreVersion,\n depositorPubkey: input.depositorBtcPubkey,\n vaultProviderPubkey: input.vaultProviderBtcPubkey,\n vaultKeeperPubkeys: [...input.vaultKeeperBtcPubkeys],\n universalChallengerPubkeys: [...input.universalChallengerBtcPubkeys],\n hashlock: stripHexPrefix(s.hashlock),\n timelockRefund: input.timelockRefund,\n network: input.network,\n });\n return connector.scriptPubKey;\n }),\n );\n const fundedOutputs = Transaction.fromHex(\n stripHexPrefix(input.fundedPrePeginTxHex),\n ).outs.map((o) => ({ value: o.value, script: o.script }));\n assertEncodedHtlcOutputsMatch(\n fundedOutputs,\n expectedHtlcValues,\n expectedHtlcScriptPubKeys,\n );\n\n return buildDepositTerms({\n vaultCoreVersion: input.vaultCoreVersion,\n protocolFeeRate: input.protocolFeeRate,\n timelockPegin: input.timelockPegin,\n timelockAssert: input.timelockAssert,\n timelockRefund: input.timelockRefund,\n prepeginTxid: input.prepeginTxid,\n prepeginMaxFee: input.prepeginMaxFee,\n vaultProviderBtcPubkey: input.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: input.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: input.universalChallengerBtcPubkeys,\n maxAcceptableCommissionBps: input.maxAcceptableCommissionBps,\n peginAmounts: input.siblings.map((s) => s.amount),\n depositorClaimValue,\n peginMaxFee,\n });\n}\n","/**\n * Check whether an error from the vault provider indicates that the\n * submitted WOTS public key hash does not match the on-chain\n * commitment. This signals that the wrong wallet is connected (its\n * `deriveContextHash` produces a different vault root and therefore\n * different WOTS keys).\n */\nexport function isWotsMismatchError(error: unknown): boolean {\n const msg = (\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"\"\n ).toLowerCase();\n\n return (\n msg.includes(\"wots\") &&\n msg.includes(\"hash\") &&\n msg.includes(\"does not match\")\n );\n}\n","/**\n * Typed failures for Pre-PegIn parameter recovery (#2203).\n *\n * Split by who can act on them. A root mismatch is user-fixable — wrong\n * wallet, wrong account or wrong network — and must be distinguishable from\n * a search that found nothing, which is ours. Without the split both look\n * identical to the caller.\n *\n * @module recovery/recoveryErrors\n */\n\n/**\n * The re-derived vault root does not match the funded Pre-PegIn's auth-anchor\n * OP_RETURN commitment.\n *\n * The root is bound to the wallet seed, the derivation account AND the\n * network (`derive-context-hash.md` §2.1 folds the connected pubkey and the\n * canonical network name into the HKDF `info`), so all three must match the\n * ones that created the deposit.\n */\nexport class VaultRootMismatchError extends Error {\n constructor(\n readonly derivedAuthAnchorHash: string,\n readonly onChainAuthAnchorHash: string,\n ) {\n super(\n `Re-derived vault root does not match this Pre-PegIn: derived ` +\n `auth-anchor hash ${derivedAuthAnchorHash}, transaction commits to ` +\n `${onChainAuthAnchorHash}. Connect the same wallet, on the same ` +\n `account and the same network, that created the deposit.`,\n );\n this.name = \"VaultRootMismatchError\";\n }\n}\n\n/**\n * The Pre-PegIn carries no single, unambiguous auth-anchor OP_RETURN.\n *\n * The anchor sits at `vout === htlcCount`, and it is the only structural\n * signal for how many HTLC outputs the transaction funds. Without it the\n * vault count would have to be guessed, so recovery refuses rather than\n * deriving the wrong number of hashlocks.\n */\nexport class UnanchoredPrePeginError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UnanchoredPrePeginError\";\n }\n}\n\n/**\n * No candidate parameter set reproduces the funded Pre-PegIn's HTLC outputs.\n *\n * Either the true parameters are outside the enumerated space — an operator\n * roster that rotated more than once has no chain-reachable historical read —\n * or the transaction is not one of ours.\n */\nexport class PeginParamsNotFoundError extends Error {\n constructor(\n readonly candidatesTried: number,\n /** A bounded sample of per-candidate rejection reasons, for diagnosis. */\n readonly sampleRejections: readonly string[],\n /** Versions that could not be read, the likeliest reason for no match. */\n readonly unresolvedLabels: readonly string[] = [],\n ) {\n super(\n `No candidate parameter set reproduces this Pre-PegIn's HTLC outputs ` +\n `(${candidatesTried} candidate(s) tried). ` +\n (unresolvedLabels.length > 0\n ? `${unresolvedLabels.length} version(s) could not be resolved and ` +\n `may hold the answer: ${unresolvedLabels.join(\" | \")}. `\n : \"\") +\n `Sample rejections: ${sampleRejections.join(\" | \")}`,\n );\n this.name = \"PeginParamsNotFoundError\";\n }\n}\n\n/**\n * More than one candidate reproduces the funded Pre-PegIn. Fail closed.\n *\n * Every survivor byte-matched the SAME funded outputs, so they agree on each\n * HTLC scriptPubKey and on the total value of each output, and would produce\n * the same refund transaction. What they disagree on is the SPLIT of that\n * value into `peginAmount` and reserve, and the version stamps that determine\n * it — neither of which the transaction carries any evidence of.\n */\nexport class PeginParamsAmbiguousError extends Error {\n constructor(readonly survivorLabels: readonly string[]) {\n super(\n `${survivorLabels.length} candidate parameter sets reproduce this ` +\n `Pre-PegIn's HTLC outputs, so the vault's stamped versions cannot be ` +\n `determined: ${survivorLabels.join(\" | \")}. Reconstruction refused.`,\n );\n this.name = \"PeginParamsAmbiguousError\";\n }\n}\n\n/**\n * Exactly one candidate matched, but the candidate space was known to be\n * incomplete. Fail closed.\n *\n * Uniqueness only detects a wrong answer when the right answer is ALSO in the\n * space. If the true version could not be read, a different version sharing\n * the same `timelockRefund` and participants matches the transaction just as\n * well — the search subtracts that candidate's reserve and the verifier adds\n * the same reserve back, so it survives — and the sole survivor would be\n * returned with the wrong version stamps and the wrong `peginAmount` split.\n *\n * Resolve the versions below and search again rather than trusting the match.\n */\nexport class PeginParamsIncompleteSpaceError extends Error {\n constructor(\n readonly matchedLabel: string,\n readonly unresolvedLabels: readonly string[],\n ) {\n super(\n `A candidate matched this Pre-PegIn (${matchedLabel}), but ` +\n `${unresolvedLabels.length} version(s) could not be resolved, so the ` +\n `search space did not provably contain the true parameters: ` +\n `${unresolvedLabels.join(\" | \")}. A version sharing the matched ` +\n `timelockRefund and participants is indistinguishable from the true ` +\n `one, so the match cannot be trusted. Reconstruction refused.`,\n );\n this.name = \"PeginParamsIncompleteSpaceError\";\n }\n}\n\n/**\n * A WASM sizing call returned a value no valid parameter set can produce.\n *\n * Raised before the value is consumed, and deliberately NOT treated as a\n * candidate rejection: it indicts the binary rather than the candidate, so it\n * escapes the search instead of being counted as one more parameter set that\n * did not match.\n */\nexport class PeginSizingIntegrityError extends Error {\n constructor(message: string) {\n super(`${message}. Reconstruction refused.`);\n this.name = \"PeginSizingIntegrityError\";\n }\n}\n","/**\n * Re-derive a stranded deposit's per-vault hashlocks from the wallet and the\n * funded Pre-PegIn transaction alone (#2203).\n *\n * An Ethereum reorg that drops the registration takes the vault row with it,\n * so the depositor pubkey, the HTLC vout and the hashlocks are gone. All three\n * are recoverable without a single `vaultId`-keyed read: the depositor pubkey\n * comes from the connected wallet, the vault count from the transaction's\n * auth-anchor OP_RETURN vout, and the hashlocks from the same HKDF pipeline the\n * deposit used.\n *\n * The parameter search that turns these hashlocks back into a refundable\n * template lives in `reconstructPeginParams` and needs no wallet.\n *\n * @module recovery/deriveHashlocksFromPrePegin\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { findAuthAnchorOpReturn } from \"../managers/pegin/assertAuthAnchorOpReturn\";\nimport { expandPerVaultSecrets } from \"../managers/pegin/expandPerVaultSecrets\";\nimport {\n hexToUint8Array,\n stripHexPrefix,\n uint8ArrayToHex,\n} from \"../primitives/utils/bitcoin\";\nimport {\n deriveVaultRoot,\n expandAuthAnchor,\n parseFundingOutpointsFromTx,\n type DeriveContextHashCapableWallet,\n} from \"../vault-secrets\";\n\nimport {\n UnanchoredPrePeginError,\n VaultRootMismatchError,\n} from \"./recoveryErrors\";\n\nexport interface DeriveHashlocksFromPrePeginInput {\n /** Any wallet implementing `deriveContextHash` — the depositor's own. */\n wallet: DeriveContextHashCapableWallet;\n /**\n * Depositor x-only BTC public key (32-byte hex, `0x` optional), read from\n * the CONNECTED WALLET. The reorg destroyed the row that would normally\n * supply it, and taking it from any chain read would defeat the recovery.\n */\n depositorBtcPubkey: string;\n /** Funded (broadcast) Pre-PegIn transaction hex, `0x` optional. */\n fundedPrePeginTxHex: string;\n}\n\nexport interface DeriveHashlocksFromPrePeginResult {\n /** Number of HTLC outputs the transaction funds, from the anchor's vout. */\n vaultCount: number;\n /** 32-byte hex hashlocks (no `0x`), indexed by `htlcVout`. */\n hashlocks: readonly string[];\n /** The transaction's auth-anchor commitment, `SHA256(authAnchor)` as hex. */\n authAnchorHash: string;\n}\n\n/**\n * Derive the per-vault hashlocks committed by a funded Pre-PegIn.\n *\n * Validates the re-derived root against the transaction's auth-anchor\n * OP_RETURN before expanding anything else. That check costs one HKDF call,\n * needs zero protocol parameters, and is what separates \"wrong wallet,\n * account or network\" from \"right wallet, no candidate matched\" — otherwise\n * both surface as a fruitless parameter search.\n *\n * @throws {UnanchoredPrePeginError} If the transaction carries no single,\n * unambiguous auth-anchor OP_RETURN.\n * @throws {VaultRootMismatchError} If the derived root does not commit to the\n * transaction's anchor.\n */\nexport async function deriveHashlocksFromPrePegin(\n input: DeriveHashlocksFromPrePeginInput,\n): Promise<DeriveHashlocksFromPrePeginResult> {\n const { wallet, depositorBtcPubkey, fundedPrePeginTxHex } = input;\n\n const cleanTxHex = stripHexPrefix(fundedPrePeginTxHex);\n const anchor = findAuthAnchorOpReturn(cleanTxHex);\n if (anchor === undefined) {\n throw new UnanchoredPrePeginError(\n `Pre-PegIn carries no single, unambiguous auth-anchor OP_RETURN, so the ` +\n `number of HTLC outputs it funds cannot be determined. Legacy ` +\n `pre-anchor deposits are not recoverable by this path.`,\n );\n }\n // The anchor sits immediately after the HTLC outputs, so its vout IS the\n // vault count. At vout 0 it would claim a Pre-PegIn that funds nothing.\n const vaultCount = anchor.vout;\n if (vaultCount === 0) {\n throw new UnanchoredPrePeginError(\n `Pre-PegIn's auth-anchor OP_RETURN is at vout 0, implying zero HTLC ` +\n `outputs; there is nothing to recover.`,\n );\n }\n\n const fundingOutpoints = parseFundingOutpointsFromTx(cleanTxHex);\n\n const root = await deriveVaultRoot(wallet, {\n depositorBtcPubkey: hexToUint8Array(depositorBtcPubkey),\n fundingOutpoints,\n });\n\n // Ordering is load-bearing: `expandPerVaultSecrets` takes ownership of\n // `root` and zeroes it, so the anchor expansion must run first. On any\n // throw in this window nothing else will wipe the root, so wipe it here.\n try {\n const authAnchorBytes = await expandAuthAnchor(root);\n let derivedAuthAnchorHash: string;\n try {\n derivedAuthAnchorHash = uint8ArrayToHex(sha256(authAnchorBytes));\n } finally {\n authAnchorBytes.fill(0);\n }\n if (derivedAuthAnchorHash !== anchor.hash) {\n throw new VaultRootMismatchError(derivedAuthAnchorHash, anchor.hash);\n }\n } catch (err) {\n root.fill(0);\n throw err;\n }\n\n // Shares the deposit-time expansion rather than a hashlock-only variant, so\n // the two can never drift. Its WOTS output is unused here and dropped with\n // the result object; refunding spends the timelock leaf, not the preimage.\n const { hashlocks } = await expandPerVaultSecrets(root, vaultCount);\n\n return { vaultCount, hashlocks, authAnchorHash: anchor.hash };\n}\n","/**\n * The candidate parameter space `reconstructPeginParams` searches (#2203).\n *\n * A reorg destroys the vault row's version stamps but not the versioned data\n * they point at: `getOffchainParamsByVersion`, `getVaultKeepersByVersion` and\n * `getUniversalChallengersByVersion` are keyed by version, not by `vaultId`,\n * and survive. So the stamps are recoverable by enumeration.\n *\n * Two parameters are deliberately NOT axes here.\n *\n * `vaultCoreVersion` is supplied, not searched. The Pre-PegIn carries no\n * evidence of it: `getPrePeginHtlcConnectorInfo` produces a byte-identical\n * scriptPubKey for v1 and v2, and the version's only other effect on the\n * transaction is the size of the reserve folded into the HTLC value — which\n * the search inverts back out of that same value, so it matches by\n * construction. Enumerating it would guarantee an ambiguous result rather than\n * narrow one. See `__tests__/reconstructPeginParams.test.ts`, which pins the\n * identical-scriptPubKey fact so a future graph version that DOES change the\n * connector fails loudly here.\n *\n * The RFC-006 operation-key epochs are not enumerable at all. Every\n * epoch-keyed read needs `getVaultKeyEpochs(vaultId)` — the destroyed row —\n * and no ABI in this repo exposes an epoch history, a counter, or an event.\n * Only the genesis keys and the operators' current keys are reachable, which\n * is why {@link KeyEpochPolicy} has exactly two values and why they apply to\n * the whole participant set at once: a vault whose participants rotated more\n * than once since creation cannot be reconstructed.\n *\n * @module recovery/peginParamsCandidates\n */\n\nimport type { Address } from \"viem\";\n\n/** One versioned offchain-params snapshot, flattened to what a trial needs. */\nexport interface OffchainParamsCandidate {\n /** `offchainParamsVersion` stamp this snapshot would restore. */\n version: number;\n /** `VersionedOffchainParams.feeRate`. */\n protocolFeeRate: bigint;\n minPeginFeeRate: bigint;\n councilQuorum: number;\n /** `VersionedOffchainParams.securityCouncilKeys.length`. */\n councilSize: number;\n /** `getTimelockPeginByVersion(version)` — not a field on the params struct. */\n timelockPegin: number;\n timelockAssert: number;\n /** `VersionedOffchainParams.tRefund` — the only scalar here that reaches the HTLC scriptPubKey. */\n timelockRefund: number;\n}\n\n/**\n * One participant key set: a vault provider plus the two rosters at the\n * versions the vault was stamped with.\n *\n * Every key here must be an RFC-006 **operation** key resolved at that\n * participant's own key epoch, and the two roster arrays must be sorted —\n * exactly what `resolveParticipantKeysAtEpochs` produces as\n * `vaultKeeperOperationKeysSorted` / `universalChallengerOperationKeysSorted`.\n * Registration keys are NOT interchangeable: they only coincide with operation\n * keys while nobody has rotated, so passing them yields a wrong script on every\n * candidate and a not-found that wrongly implies the true set was tried.\n *\n * The three epochs are stamped independently by three registries, so a mixed\n * state — the vault provider rotated before this deposit, a challenger after —\n * is ordinary rather than impossible. That is why epochs are resolved per\n * participant from the `PegInSubmitted` log rather than chosen as one\n * all-genesis or all-current policy.\n */\nexport interface ParticipantKeySetCandidate {\n vaultProvider: Address;\n /** x-only operation-key hex (no `0x`) at the vault's `vpKeyEpoch`. */\n vaultProviderBtcPubkey: string;\n appVaultKeepersVersion: number;\n /** Sorted operation keys at the vault's `appKeeperKeyEpoch`. */\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengersVersion: number;\n /** Sorted operation keys at the vault's `ucKeyEpoch`. */\n universalChallengerBtcPubkeys: readonly string[];\n}\n\n/** A fully-determined parameter set to trial against the funded Pre-PegIn. */\nexport interface PeginParamsCandidate {\n /**\n * Stamped tx-graph version (NOT the chain-active one). Carried so the result\n * describes itself, but constant across a candidate space — see the module\n * note on why it cannot be searched.\n */\n vaultCoreVersion: number;\n offchainParams: OffchainParamsCandidate;\n participants: ParticipantKeySetCandidate;\n}\n\nexport interface BuildPeginParamsCandidatesInput {\n /**\n * The tx-graph version the stranded deposit was built with. A scalar, not a\n * list: the transaction cannot discriminate it, so this is taken on trust.\n * Read it from the orphaned `PegInSubmitted` log — `activeVaultCoreVersion()`\n * is the current version and is wrong for any deposit that predates a bump.\n */\n vaultCoreVersion: number;\n offchainParams: readonly OffchainParamsCandidate[];\n participantKeySets: readonly ParticipantKeySetCandidate[];\n}\n\n/**\n * Expand the two searchable axes into the flat candidate list\n * `reconstructPeginParams` trials.\n *\n * With the parameters read from the `PegInSubmitted` log this is called with\n * one entry per axis and produces a single candidate to verify. The\n * multi-element form is the fallback search.\n */\nexport function buildPeginParamsCandidates(\n input: BuildPeginParamsCandidatesInput,\n): PeginParamsCandidate[] {\n const { vaultCoreVersion, offchainParams, participantKeySets } = input;\n if (!Number.isInteger(vaultCoreVersion) || vaultCoreVersion < 1) {\n throw new Error(\n `buildPeginParamsCandidates: vaultCoreVersion must be a positive integer, got ${vaultCoreVersion}`,\n );\n }\n if (offchainParams.length === 0) {\n throw new Error(\n \"buildPeginParamsCandidates: at least one offchain-params version is required\",\n );\n }\n if (participantKeySets.length === 0) {\n throw new Error(\n \"buildPeginParamsCandidates: at least one participant key set is required\",\n );\n }\n\n const candidates: PeginParamsCandidate[] = [];\n for (const params of offchainParams) {\n for (const participants of participantKeySets) {\n candidates.push({\n vaultCoreVersion,\n offchainParams: params,\n participants,\n });\n }\n }\n return candidates;\n}\n\n/** Which enumeration a version belongs to. */\nexport type CandidateAxis =\n | \"offchainParams\"\n | \"vaultKeepers\"\n | \"universalChallengers\";\n\n/**\n * A version the caller enumerated over but could not resolve into candidate\n * data — dropped by `fetchAllOffchainParams`'s validation, or reported to a\n * roster loop's `onSkippedVersion` observer.\n *\n * Recorded rather than ignored because an unresolved version is UNRESOLVABLE,\n * not absent: it may be the very version that stamped the stranded vault, and\n * a search space missing its answer can still return a confident look-alike.\n */\nexport interface UnresolvedVersion {\n axis: CandidateAxis;\n version: number;\n reason: string;\n}\n\n/** Compact identity of an unresolved version, for error messages. */\nexport function describeUnresolvedVersion(\n unresolved: UnresolvedVersion,\n): string {\n return `${unresolved.axis} v${unresolved.version} (${unresolved.reason})`;\n}\n\n/** Compact identity of a candidate, for error messages and result labels. */\nexport function describePeginParamsCandidate(\n candidate: PeginParamsCandidate,\n): string {\n const { participants } = candidate;\n return (\n `core=${candidate.vaultCoreVersion} ` +\n `offchain=${candidate.offchainParams.version} ` +\n `keepers=${participants.appVaultKeepersVersion} ` +\n `challengers=${participants.universalChallengersVersion} ` +\n `vp=${participants.vaultProvider}`\n );\n}\n","/**\n * Recover a stranded deposit's destroyed protocol parameters by trialling an\n * enumerated candidate space against the funded Pre-PegIn transaction (#2203).\n *\n * This is a candidate enumerator plus a loop over an EXISTING verifier.\n * `rebuildDepositTermsCore` already answers \"do these parameters and this\n * funded transaction agree?\" — it rebuilds every HTLC scriptPubKey through the\n * WASM oracle and byte-matches script and value against the transaction, from\n * plain inputs with no chain access. Recovery reuses it verbatim so there is\n * exactly one implementation of that judgement in the codebase.\n *\n * No wallet, no `vaultId`, no browser: every input is either supplied by the\n * caller or read off the transaction. Hashlocks come from\n * `deriveHashlocksFromPrePegin`.\n *\n * @module recovery/reconstructPeginParams\n */\n\nimport {\n computeMinClaimValue,\n computeMinPeginFee,\n peginP2aAnchorOutput,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport type { DepositTerms } from \"../deposit-terms/depositTerms\";\nimport { rebuildDepositTermsCore } from \"../deposit-terms/rebuildDepositTermsCore\";\nimport { findAuthAnchorOpReturn } from \"../managers/pegin/assertAuthAnchorOpReturn\";\nimport { HtlcOutputMismatchError } from \"../primitives/psbt/assertWasmPeginSizing\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../primitives/utils/bitcoin\";\nimport { calculateBtcTxHash } from \"../utils/transaction/btcTxHash\";\n\nimport {\n describePeginParamsCandidate,\n describeUnresolvedVersion,\n type PeginParamsCandidate,\n type UnresolvedVersion,\n} from \"./peginParamsCandidates\";\nimport {\n PeginParamsAmbiguousError,\n PeginParamsIncompleteSpaceError,\n PeginParamsNotFoundError,\n PeginSizingIntegrityError,\n UnanchoredPrePeginError,\n} from \"./recoveryErrors\";\n\n/**\n * How many per-candidate reasons an error message carries, for rejections and\n * for unevaluated candidates alike. The full list is one line per candidate and\n * runs to hundreds; a handful is enough to tell \"wrong transaction\" from\n * \"roster not enumerated\". Only the message is capped — the counts that drive\n * the fail-closed decisions stay exact.\n */\nconst MAX_REPORTED_REJECTIONS = 5;\n\nexport interface ReconstructPeginParamsInput {\n /** Hashlocks indexed by `htlcVout`, from `deriveHashlocksFromPrePegin`. */\n hashlocks: readonly string[];\n /** Funded (broadcast) Pre-PegIn transaction hex, `0x` optional. */\n fundedPrePeginTxHex: string;\n /** Depositor x-only BTC pubkey hex, from the connected wallet. */\n depositorBtcPubkey: string;\n /**\n * Funded-transaction fee, `Σin − Σout`. Not derivable from the transaction\n * alone — the input values live in the funding UTXOs — but those are\n * Bitcoin-keyed reads, which a reorg on Ethereum leaves intact.\n */\n prepeginMaxFee: bigint;\n /**\n * Commission ceiling projected into the returned {@link DepositTerms}. Not a\n * search axis: it never reaches the HTLC scriptPubKey or value, so the\n * transaction carries no evidence of it and the caller must supply the bound\n * it is willing to accept.\n */\n maxAcceptableCommissionBps: number;\n network: Network;\n /** The space to search, from `buildPeginParamsCandidates`. */\n candidates: readonly PeginParamsCandidate[];\n /**\n * Versions the caller enumerated over but could not resolve. Required, and\n * `[]` is the explicit claim that the enumeration was complete — so a caller\n * cannot arrive at a trusted answer by forgetting to mention its gaps.\n *\n * A non-empty list turns a sole match into a\n * {@link PeginParamsIncompleteSpaceError}: uniqueness only rules out a wrong\n * answer when the right answer was in the space to begin with.\n */\n unresolvedVersions: readonly UnresolvedVersion[];\n}\n\nexport interface ReconstructPeginParamsResult {\n /** The single candidate whose rebuild matched the funded transaction. */\n candidate: PeginParamsCandidate;\n /**\n * Terms projected from the matched candidate.\n *\n * The transaction pins the participant keys, `timelockRefund`, and each\n * HTLC's scriptPubKey and total value. It does NOT pin how that value splits\n * into `peginAmount`, `depositorClaimValue` and `peginMaxFee` — that split\n * follows from the matched candidate's fee-side parameters, which the\n * transaction carries no evidence of.\n */\n terms: DepositTerms;\n /**\n * Per-vault `peginAmount`, inverted from the observed HTLC output values.\n * Only as sound as the matched candidate's reserve — see {@link terms}.\n */\n peginAmounts: readonly bigint[];\n /** The transaction's `SHA256(authAnchor)` commitment, for the refund rebuild. */\n authAnchorHash: string;\n /** Size of the space actually trialled, for the recovery record. */\n candidatesTried: number;\n /**\n * Every candidate that matched and projected identical terms.\n *\n * More than one is normal rather than suspicious: candidates differing only\n * in fields the transaction cannot express — version labels whose\n * script- and value-relevant content is the same — are indistinguishable by\n * construction and describe the same deposit. They are reported rather than\n * refused; refusal is reserved for survivors whose terms actually differ.\n */\n matchedCandidates: readonly PeginParamsCandidate[];\n}\n\n/**\n * Canonical form of a match's observable content, for equality only.\n *\n * Two survivors agreeing here produce the same refund and the same reported\n * amounts, so the difference between them is a label with no consequence.\n */\nfunction matchFingerprint(\n terms: DepositTerms,\n peginAmounts: readonly bigint[],\n): string {\n return JSON.stringify({ terms, peginAmounts }, (_key, value) =>\n typeof value === \"bigint\" ? `${value}` : value,\n );\n}\n\n/** Amount-independent per-HTLC reserve, recomputed exactly as the verifier does. */\ninterface PeginSizing {\n depositorClaimValue: bigint;\n peginMaxFee: bigint;\n p2aAnchorValue: bigint;\n}\n\n/** Identity of the inputs that determine a candidate's reserve, for memoising it. */\nfunction sizingCacheKey(candidate: PeginParamsCandidate): string {\n const { offchainParams: params, participants } = candidate;\n return [\n candidate.vaultCoreVersion,\n participants.vaultKeeperBtcPubkeys.length,\n participants.universalChallengerBtcPubkeys.length,\n params.councilQuorum,\n params.councilSize,\n params.protocolFeeRate,\n params.minPeginFeeRate,\n ].join(\"|\");\n}\n\n/**\n * Compute the amount-independent reserve for one candidate, asserting the WASM\n * outputs before they are consumed (CLAUDE.md critical path 1).\n *\n * Only binary-integrity invariants are asserted here — a claim value or fee of\n * zero, or a negative anchor, is impossible for ANY valid parameter set, so it\n * indicts the binary rather than the candidate and must escape the search loop\n * instead of being counted as a rejection. The plausibility band on the implied\n * reserve is deliberately NOT duplicated: it depends on the candidate's own\n * `minPeginFeeRate`, so a candidate can legitimately fail it, and the verifier\n * applies it where it belongs — as a candidate filter.\n *\n * An absent anchor reads as `0n` because the facade returns `null` for graph\n * versions whose PegIn carries no anchor and never a zero-valued placeholder,\n * which is the same reading `rebuildDepositTermsCore` takes.\n */\nasync function computePeginSizing(\n candidate: PeginParamsCandidate,\n): Promise<PeginSizing> {\n const { offchainParams: params, participants } = candidate;\n // Depositor-as-claimer: the local-challenger count is the keeper count\n // (the vault provider is excluded) — btc-vault graph.rs derive_challengers_for.\n const numVks = participants.vaultKeeperBtcPubkeys.length;\n const numUcs = participants.universalChallengerBtcPubkeys.length;\n const [depositorClaimValue, peginMaxFee, anchorOutput] = await Promise.all([\n computeMinClaimValue(\n candidate.vaultCoreVersion,\n numVks,\n numUcs,\n params.councilQuorum,\n params.councilSize,\n params.protocolFeeRate,\n ),\n computeMinPeginFee(\n candidate.vaultCoreVersion,\n numVks,\n numUcs,\n params.minPeginFeeRate,\n ),\n peginP2aAnchorOutput(candidate.vaultCoreVersion),\n ]);\n const p2aAnchorValue = anchorOutput?.value ?? 0n;\n\n if (depositorClaimValue <= 0n) {\n throw new PeginSizingIntegrityError(\n `WASM returned non-positive depositorClaimValue ${depositorClaimValue} for graph version ${candidate.vaultCoreVersion}`,\n );\n }\n if (peginMaxFee <= 0n) {\n throw new PeginSizingIntegrityError(\n `WASM returned non-positive peginMaxFee ${peginMaxFee} for graph version ${candidate.vaultCoreVersion}`,\n );\n }\n if (p2aAnchorValue < 0n) {\n throw new PeginSizingIntegrityError(\n `WASM returned negative P2A anchor value ${p2aAnchorValue} for graph version ${candidate.vaultCoreVersion}`,\n );\n }\n\n return { depositorClaimValue, peginMaxFee, p2aAnchorValue };\n}\n\n/**\n * Search the candidate space for the parameter set that reproduces the funded\n * Pre-PegIn, and project it back into {@link DepositTerms}.\n *\n * Per candidate: invert each vault's `peginAmount` from the observed HTLC\n * output value via the protocol identity `htlcValue = peginAmount +\n * depositorClaimValue + peginMaxFee + p2aAnchorValue`, then hand the result to\n * `rebuildDepositTermsCore`, which independently recomputes that same sizing\n * and byte-matches both the value and the scriptPubKey of every HTLC output.\n *\n * Because the amount is inverted from the value it is compared against, the\n * VALUE check cannot discriminate between candidates: it holds for any\n * candidate whose reserve leaves a positive amount. The scriptPubKey does all\n * the discriminating, and `getPrePeginHtlcConnectorInfo` accepts exactly one\n * offchain-params scalar — `timelockRefund`. So two offchain versions sharing\n * a `timelockRefund` are indistinguishable however much their fee rates or\n * council parameters differ, and that is precisely the ambiguity this function\n * refuses to guess through. The value check is still run, by the verifier, as\n * the bound that stops a candidate whose reserve exceeds the funded output.\n *\n * Every candidate is trialled; the loop does not stop at the first match,\n * because detecting ambiguity is the point. Matches are then COMPARED rather\n * than counted — several candidates matching is the ordinary case whenever\n * their differences cannot reach the transaction, and refusing on a count\n * alone would reject a deposit whose refund is fully determined.\n *\n * A candidate is only rejected on a Gate-1 byte mismatch, which is the\n * transaction positively disagreeing with it. Any other failure means the\n * candidate was never really evaluated, so it is recorded as unresolved and\n * feeds the same fail-closed path as a version that could not be read —\n * otherwise an incidental error on the TRUE candidate would remove it silently\n * and hand back a look-alike as a trusted unique answer.\n *\n * @throws {UnanchoredPrePeginError} If the transaction carries no single,\n * unambiguous auth-anchor OP_RETURN.\n * @throws {PeginParamsNotFoundError} If no candidate matched.\n * @throws {PeginParamsAmbiguousError} If matches disagree on the projected terms.\n * @throws {PeginParamsIncompleteSpaceError} If a match is found but the space\n * was known to be incomplete, or a candidate failed to evaluate.\n */\nexport async function reconstructPeginParams(\n input: ReconstructPeginParamsInput,\n): Promise<ReconstructPeginParamsResult> {\n const {\n hashlocks,\n fundedPrePeginTxHex,\n depositorBtcPubkey,\n prepeginMaxFee,\n maxAcceptableCommissionBps,\n network,\n candidates,\n unresolvedVersions,\n } = input;\n\n if (hashlocks.length === 0) {\n throw new Error(\n \"reconstructPeginParams: at least one hashlock is required\",\n );\n }\n if (candidates.length === 0) {\n throw new Error(\"reconstructPeginParams: candidate space is empty\");\n }\n\n // The verifier compares this against WASM-built scripts and does not\n // normalise, while the derivation half accepts a `0x` prefix. Passing one\n // wallet string to both must not make step 1 pass and step 2 fail every\n // candidate, which would present as \"no parameters found\".\n const normalizedDepositorBtcPubkey =\n processPublicKeyToXOnly(depositorBtcPubkey);\n\n const cleanTxHex = stripHexPrefix(fundedPrePeginTxHex);\n const anchor = findAuthAnchorOpReturn(cleanTxHex);\n if (anchor === undefined) {\n throw new UnanchoredPrePeginError(\n `Pre-PegIn carries no single, unambiguous auth-anchor OP_RETURN; the ` +\n `sibling set cannot be proven complete. Reconstruction refused.`,\n );\n }\n // The verifier re-checks this, but catching it here attributes the failure\n // to the hashlock set rather than to every candidate in turn.\n if (anchor.vout !== hashlocks.length) {\n throw new Error(\n `reconstructPeginParams: ${hashlocks.length} hashlock(s) supplied but ` +\n `the auth-anchor OP_RETURN sits at vout ${anchor.vout}, so the ` +\n `transaction funds ${anchor.vout} HTLC output(s). Reconstruction refused.`,\n );\n }\n\n const outputs = Transaction.fromHex(cleanTxHex).outs;\n const observedHtlcValues = hashlocks.map((_, i) => BigInt(outputs[i].value));\n const prepeginTxid = stripHexPrefix(\n calculateBtcTxHash(cleanTxHex),\n ).toLowerCase();\n\n const sizingCache = new Map<string, PeginSizing>();\n const survivors: {\n candidate: PeginParamsCandidate;\n terms: DepositTerms;\n peginAmounts: bigint[];\n fingerprint: string;\n }[] = [];\n const rejections: string[] = [];\n // Count drives the fail-closed decision and must stay exact; only the labels\n // that reach the error message are capped, since in the fallback search every\n // candidate can land here and each entry embeds a full error message.\n const unevaluatedLabels: string[] = [];\n let unevaluatedCount = 0;\n\n for (const candidate of candidates) {\n try {\n const cacheKey = sizingCacheKey(candidate);\n let sizing = sizingCache.get(cacheKey);\n if (sizing === undefined) {\n sizing = await computePeginSizing(candidate);\n sizingCache.set(cacheKey, sizing);\n }\n const reserve =\n sizing.depositorClaimValue + sizing.peginMaxFee + sizing.p2aAnchorValue;\n const peginAmounts = observedHtlcValues.map((value) => value - reserve);\n // A reserve larger than the funded output is the transaction positively\n // disagreeing with this candidate — the value bound doing the one piece\n // of discriminating it can. The verifier would reject it too, but with a\n // generic error that would be misread as \"never evaluated\" and would\n // pollute the space with phantom gaps.\n const shortIndex = peginAmounts.findIndex((amount) => amount <= 0n);\n if (shortIndex !== -1) {\n throw new HtlcOutputMismatchError(\n `HTLC output[${shortIndex}] value ${observedHtlcValues[shortIndex]} ` +\n `is not above this candidate's reserve ${reserve}, so it implies a ` +\n `non-positive pegin amount ${peginAmounts[shortIndex]}.`,\n );\n }\n\n const { offchainParams: params, participants } = candidate;\n const terms = await rebuildDepositTermsCore({\n vaultCoreVersion: candidate.vaultCoreVersion,\n siblings: hashlocks.map((hashlock, i) => ({\n hashlock,\n amount: peginAmounts[i],\n })),\n fundedPrePeginTxHex: cleanTxHex,\n depositorBtcPubkey: normalizedDepositorBtcPubkey,\n vaultProviderBtcPubkey: participants.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: participants.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys:\n participants.universalChallengerBtcPubkeys,\n protocolFeeRate: params.protocolFeeRate,\n minPeginFeeRate: params.minPeginFeeRate,\n councilQuorum: params.councilQuorum,\n councilSize: params.councilSize,\n timelockPegin: params.timelockPegin,\n timelockAssert: params.timelockAssert,\n timelockRefund: params.timelockRefund,\n prepeginTxid,\n prepeginMaxFee,\n maxAcceptableCommissionBps,\n network,\n });\n\n survivors.push({\n candidate,\n terms,\n peginAmounts,\n fingerprint: matchFingerprint(terms, peginAmounts),\n });\n } catch (err) {\n // A malformed WASM sizing output is not a property of the candidate, so\n // counting it as a rejection would bury a broken binary under \"no\n // candidate matched\". Let it out.\n if (err instanceof PeginSizingIntegrityError) {\n throw err;\n }\n const label = describePeginParamsCandidate(candidate);\n const reason = err instanceof Error ? err.message : String(err);\n if (err instanceof HtlcOutputMismatchError) {\n // The transaction positively disagrees with this candidate — the\n // expected outcome for all but one, so it cannot propagate. Recorded,\n // and a sample reaches the not-found error to keep it diagnosable.\n if (rejections.length < MAX_REPORTED_REJECTIONS) {\n rejections.push(`[${label}] ${reason}`);\n }\n } else {\n // Anything else says nothing about the transaction: this candidate was\n // never actually evaluated. Treating it as a rejection would let an\n // incidental failure on the true candidate hand back a look-alike.\n unevaluatedCount++;\n if (unevaluatedLabels.length < MAX_REPORTED_REJECTIONS) {\n unevaluatedLabels.push(`${label} (${reason})`);\n }\n }\n }\n }\n\n // A candidate that could not be evaluated is a hole in the space in exactly\n // the same way an unreadable version is, so the two are reported together.\n const gapCount = unresolvedVersions.length + unevaluatedCount;\n const gaps = [\n ...unresolvedVersions.map(describeUnresolvedVersion),\n ...unevaluatedLabels,\n ];\n\n if (survivors.length === 0) {\n throw new PeginParamsNotFoundError(candidates.length, rejections, gaps);\n }\n\n // Compare, do not count. Survivors all byte-matched the same funded outputs,\n // so they already agree on every HTLC scriptPubKey and total value; if they\n // also project identical terms there is nothing to choose between them and\n // nothing is at stake in choosing.\n const distinct = new Set(survivors.map((s) => s.fingerprint));\n if (distinct.size > 1) {\n throw new PeginParamsAmbiguousError(\n survivors.map((s) => describePeginParamsCandidate(s.candidate)),\n );\n }\n\n // Reported after ambiguity: disagreeing survivors are the more specific\n // diagnosis, and that error already names them.\n if (gapCount > 0) {\n throw new PeginParamsIncompleteSpaceError(\n describePeginParamsCandidate(survivors[0].candidate),\n gaps,\n );\n }\n\n return {\n candidate: survivors[0].candidate,\n terms: survivors[0].terms,\n peginAmounts: survivors[0].peginAmounts,\n authAnchorHash: anchor.hash,\n candidatesTried: candidates.length,\n matchedCandidates: survivors.map((s) => s.candidate),\n };\n}\n"],"names":["DEPOSIT_TERMS_REJECTED_ERROR_NAME","DepositTermsRejectedError","message","reason","__publicField","isDepositTermsRejectedError","err","shaped","rebuildDepositTermsCore","input","siblingCount","i","sibling","expectedTxid","stripHexPrefix","actualTxid","calculateBtcTxHash","found","findAuthAnchorOpReturn","numVks","numUcs","depositorClaimValue","computeMinClaimValue","peginMaxFee","computeMinPeginFee","anchor","peginP2aAnchorOutput","anchorValue","impliedReserve","maxImpliedReserve","MAX_REASONABLE_PEGIN_VBYTES","expectedHtlcValues","s","expectedHtlcScriptPubKeys","getPrePeginHtlcConnectorInfo","fundedOutputs","Transaction","assertEncodedHtlcOutputsMatch","buildDepositTerms","isWotsMismatchError","error","msg","VaultRootMismatchError","derivedAuthAnchorHash","onChainAuthAnchorHash","UnanchoredPrePeginError","PeginParamsNotFoundError","candidatesTried","sampleRejections","unresolvedLabels","PeginParamsAmbiguousError","survivorLabels","PeginParamsIncompleteSpaceError","matchedLabel","PeginSizingIntegrityError","deriveHashlocksFromPrePegin","wallet","depositorBtcPubkey","fundedPrePeginTxHex","cleanTxHex","vaultCount","fundingOutpoints","parseFundingOutpointsFromTx","root","deriveVaultRoot","hexToUint8Array","authAnchorBytes","expandAuthAnchor","uint8ArrayToHex","sha256","hashlocks","expandPerVaultSecrets","buildPeginParamsCandidates","vaultCoreVersion","offchainParams","participantKeySets","candidates","params","participants","describeUnresolvedVersion","unresolved","describePeginParamsCandidate","candidate","MAX_REPORTED_REJECTIONS","matchFingerprint","terms","peginAmounts","_key","value","sizingCacheKey","computePeginSizing","anchorOutput","p2aAnchorValue","reconstructPeginParams","prepeginMaxFee","maxAcceptableCommissionBps","network","unresolvedVersions","normalizedDepositorBtcPubkey","processPublicKeyToXOnly","outputs","observedHtlcValues","_","prepeginTxid","sizingCache","survivors","rejections","unevaluatedLabels","unevaluatedCount","cacheKey","sizing","reserve","shortIndex","amount","HtlcOutputMismatchError","hashlock","label","gapCount","gaps"],"mappings":"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"}
@@ -5,7 +5,7 @@ import * as w from "bitcoinjs-lib";
5
5
  import { s as m, v as p } from "./bitcoin-d9eaaZB_.js";
6
6
  import { s as R } from "./sha2-BYVxyZzX.js";
7
7
  import "@babylonlabs-io/babylon-tbv-rust-wasm";
8
- import { c as b, d as y, e as x, f } from "./verifyScriptPathSchnorrSignature-XsICweGG.js";
8
+ import { c as b, d as y, e as x, f } from "./verifyScriptPathSchnorrSignature-i-koT139.js";
9
9
  import { c as d } from "./signing-DaLvGwQe.js";
10
10
  const S = 106, T = 32, l = 34;
11
11
  function W(e, t, s) {
@@ -275,4 +275,4 @@ export {
275
275
  O as s,
276
276
  L as v
277
277
  };
278
- //# sourceMappingURL=signPsbtsWithFallback-DC4rUCsy.js.map
278
+ //# sourceMappingURL=signPsbtsWithFallback-Bb66DfY5.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signPsbtsWithFallback-DC4rUCsy.js","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/deposit-terms/depositTerms.ts","../src/tbv/core/managers/PayoutManager.ts","../src/tbv/core/managers/pegin/signPsbtsWithFallback.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","import type { BitcoinWallet } from \"../../../shared/wallets/interfaces\";\n\n// Field docs use /** */ so they survive into the emitted .d.ts — the published\n// package is the only contract the external provider author (#2109) sees.\n\nexport interface DepositTermsVaultGroup {\n /** 0-based; equals the group's position (groups are ascending by vout). */\n readonly htlcVout: number;\n /** x-only hex (64 chars), as validated on-chain upstream. */\n readonly vaultProviderBtcPubkey: string;\n /** sats */\n readonly peginAmount: bigint;\n /**\n * sats; the MAXIMUM commission the depositor accepts —\n * floor(peginAmount * maxAcceptableCommissionBps / 10_000), the same\n * ceiling the registration calldata carries. An approving wallet MUST\n * enforce the VP payout commission output `<= commissionFee` (e.g. the\n * Ledger vault app does, firmware >= c8db53e), and every commission the\n * contract admits stays under this ceiling (floor is monotonic in bps),\n * so no contract-admitted deposit can be refused at payout. The actual\n * stamped commission may be lower. Display the quoted value in UI, not this.\n */\n readonly commissionFee: bigint;\n /** sats; the same value for every vault. */\n readonly depositorClaimValue: bigint;\n /**\n * sats; the cap an approving wallet enforces on the PegIn tx fee. Equals\n * the graph's exact (minimum) PegIn fee, which is deterministic — so the\n * cap is satisfied exact-by-construction.\n */\n readonly peginMaxFee: bigint;\n}\n\n/**\n * Field names follow btc-vault vocabulary (the protocol source of truth);\n * a device-wire encoder maps them to its intent fields (e.g. the Ledger TLV:\n * protocolFeeRate -> base_fee_rate, timelockPegin -> pegin_csv_timelock,\n * timelockAssert -> payout_timelock, peginAmount -> vault_amount).\n */\nexport interface DepositTerms {\n /**\n * btc-vault tx-graph version (`vaultCoreVersion`) these terms describe —\n * the vault's stamped on-chain version for resumes, the chain's\n * `activeVaultCoreVersion` for fresh deposits. It selects the PegIn shape\n * an approving wallet must expect: v1 = 2 outputs, no anchor; v2 = TRUC\n * nVersion 3, 3 outputs with a 240-sat P2A anchor at vout 2, and an\n * Assert OP_RETURN marker that raises the claim value\n * (btc-vault `transactions/pegin.rs`, `assert_marker.rs`). A provider that\n * supports only one shape MUST reject the others here rather than\n * mis-validating the PSBTs later.\n */\n vaultCoreVersion: number;\n /**\n * sat/vB; the tx-graph fee rate (protocolFeeRate), NOT the mempool funding\n * rate. Approving wallets bound each payout's fee against this rate —\n * pass the exact graph rate, not an inflated ceiling.\n */\n protocolFeeRate: bigint;\n /** Vault-UTXO CSV timelock (blocks). */\n timelockPegin: number;\n /**\n * btc-vault `timelock_assert` (t2) — the CSV on Assert output 0. Its own\n * param, though production derives it and `timelockPegin` from one value.\n */\n timelockAssert: number;\n /** HTLC refund CSV timelock (blocks). */\n timelockRefund: number;\n /**\n * 64-char hex in display order. A device-wire encoder may need the\n * little-endian form — some hardware byte-compares it against\n * PSBT_IN_PREVIOUS_TXID rather than recomputing the txid.\n */\n prepeginTxid: string;\n /** sats; the funded Pre-PegIn fee (an approving wallet caps the signed fee at this). */\n prepeginMaxFee: bigint;\n /**\n * x-only hex. Sorted ascending by the upstream on-chain validation\n * (validateOnChainParticipantKeys); the builder passes them through\n * unasserted — approving devices may reject unsorted lists at load.\n */\n vaultKeeperBtcPubkeys: readonly string[];\n /**\n * x-only hex, sorted ascending upstream independently of vaultKeeperBtcPubkeys (same\n * pass-through contract). Universal challengers only — the full graph\n * challenger set is vaultKeeperBtcPubkeys ∪ universalChallengerBtcPubkeys (vault keepers are the local\n * challengers).\n */\n universalChallengerBtcPubkeys: readonly string[];\n /** Per-vault groups, ordered by ascending htlcVout. */\n vaults: readonly DepositTermsVaultGroup[];\n}\n\n/**\n * Implemented only by depositor-approval wallets (e.g. a Ledger vault\n * provider). Provider obligations:\n *\n * - Envelope: validate terms against the device's envelope BEFORE the\n * ceremony, rejecting with the shape `{ name: \"DepositTermsRejectedError\",\n * reason: \"device-envelope\", message }` (matched structurally, not by class).\n * - Idempotence: a byte-equal re-approval MUST be a no-op while the\n * device-side approval is live; anything that invalidates it (a later\n * `deriveContextHash`, a signing failure) MUST clear the memo.\n *\n * Seam invariant: any derive invalidates a prior approval, so the SDK\n * re-approves after every derive and before the next terms-bound signature.\n * The re-approval sites are `PeginManager.preparePegin`,\n * `runDepositorPresignFlow`, and `signAndBroadcast` (the Pre-PegIn broadcast,\n * which derives immediately before approving — see `ensurePrePeginTermsApproval`).\n */\nexport interface DepositTermsApprover {\n approveDepositTerms(terms: DepositTerms): Promise<void>;\n}\n\n/** True when the wallet implements {@link DepositTermsApprover.approveDepositTerms}. */\nexport function supportsDepositApproval(\n wallet: BitcoinWallet,\n): wallet is BitcoinWallet & DepositTermsApprover {\n return (\n typeof (wallet as Partial<DepositTermsApprover>).approveDepositTerms ===\n \"function\"\n );\n}\n\n/**\n * Spreadable forward of `approveDepositTerms` for wallet-wrapper objects.\n * Object spread drops prototype methods, so every `{...wallet}` wrapper site\n * must re-attach the capability explicitly: `...forwardDepositApproval(wallet)`.\n */\nexport function forwardDepositApproval(\n wallet: BitcoinWallet,\n): Partial<DepositTermsApprover> {\n return supportsDepositApproval(wallet)\n ? { approveDepositTerms: (terms) => wallet.approveDepositTerms(terms) }\n : {};\n}\n\nexport interface BuildDepositTermsInputs {\n /** btc-vault tx-graph version the graph is built under. */\n vaultCoreVersion: number;\n protocolFeeRate: bigint;\n timelockPegin: number;\n /** btc-vault `timelock_assert` (t2) — its own param; NOT derived from timelockPegin here. */\n timelockAssert: number;\n timelockRefund: number;\n prepeginTxid: string;\n prepeginMaxFee: bigint;\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengerBtcPubkeys: readonly string[];\n /** Ceiling bps, not the quote — see {@link DepositTermsVaultGroup.commissionFee}. */\n maxAcceptableCommissionBps: number;\n peginAmounts: readonly bigint[];\n depositorClaimValue: bigint;\n peginMaxFee: bigint;\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../shared/wallets\";\nimport {\n assertPsbtUnsignedTxMatches,\n assertScriptPathSchnorrSignature,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\n\n/** Payout PSBTs are signed by the depositor on input 0 (Taproot script-path). */\nconst PAYOUT_SIGNED_INPUT_INDEX = 0;\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Forwarded to\n * {@link buildPayoutPsbt} to derive the matching graph's payout scripts.\n */\n vaultCoreVersion: number;\n\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n /** btc-vault `timelock_assert`; payout input 1's sequence. */\n timelockAssert: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n\n /**\n * Version-locked tx-graph fee rate (sat/vB) the graph was built with.\n * Forwarded to {@link buildPayoutPsbt} for the fee band.\n */\n protocolFeeRate: bigint;\n\n /** Security council member count; forwarded to the fee floor (see PayoutParams). */\n councilSize: number;\n\n /**\n * RFC-006 resolved payout destinations, keyed by lowercased x-only operation\n * pubkey. Forwarded verbatim to {@link buildPayoutPsbt}; every VK claimer\n * must be present.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /** RFC-006 VP commission destination. Forwarded to {@link buildPayoutPsbt}. */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: params.vaultCoreVersion,\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n timelockAssert: params.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n protocolFeeRate: params.protocolFeeRate,\n councilSize: params.councilSize,\n vkClaimerPayoutScriptPubKeys: params.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: params.vpCommissionScriptPubKey,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n // Critical Path #7: verify the signature against a sighash recomputed from\n // the PSBT we built, not the wallet-returned one.\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: payoutPsbt.psbtHex,\n signatureHex: signature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(transactions: SignPayoutParams[]): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: tx.vaultCoreVersion,\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n timelockAssert: tx.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n protocolFeeRate: tx.protocolFeeRate,\n councilSize: tx.councilSize,\n vkClaimerPayoutScriptPubKeys: tx.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: tx.vpCommissionScriptPubKey,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtsToSign[i],\n signatureHex: payoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n}\n","/**\n * Wallet-signing helper that routes a lone PSBT to `signPsbt`, prefers native\n * `signPsbts` for real batches, and falls back to sequential `signPsbt` for\n * wallets that don't implement batch signing.\n *\n * @module managers/pegin/signPsbtsWithFallback\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets\";\n\n/**\n * Sign one or more PSBTs against a wallet.\n *\n * A single PSBT is always signed via `signPsbt`, never the batch endpoint: it's\n * one wallet interaction either way, and `signPsbt` is the universally-required\n * wallet method (batch `signPsbts` is optional), so single-sign is the portable\n * choice — some integrations (notably MPC / institutional wallets) prefer it.\n * For a real batch (>1), wallets exposing native `signPsbts` (e.g. UniSat) sign\n * in one interaction; others loop `signPsbt`.\n *\n * @throws If native `signPsbts` returns a different number of signed PSBTs\n * than were submitted.\n */\nexport async function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtsHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (psbtsHexes.length === 1) {\n return [await wallet.signPsbt(psbtsHexes[0], options?.[0])];\n }\n\n if (typeof wallet.signPsbts === \"function\") {\n const signedPsbts = await wallet.signPsbts(psbtsHexes, options);\n if (signedPsbts.length !== psbtsHexes.length) {\n throw new Error(\n `Expected ${psbtsHexes.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n return signedPsbts;\n }\n\n const signedPsbts: string[] = [];\n for (let i = 0; i < psbtsHexes.length; i++) {\n signedPsbts.push(await wallet.signPsbt(psbtsHexes[i], options?.[i]));\n }\n return signedPsbts;\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","supportsDepositApproval","wallet","forwardDepositApproval","terms","PAYOUT_SIGNED_INPUT_INDEX","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","signature","extractPayoutSignature","assertScriptPathSchnorrSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature","signPsbtsWithFallback","psbtsHexes","options"],"mappings":"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"}
1
+ {"version":3,"file":"signPsbtsWithFallback-Bb66DfY5.js","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/deposit-terms/depositTerms.ts","../src/tbv/core/managers/PayoutManager.ts","../src/tbv/core/managers/pegin/signPsbtsWithFallback.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","import type { BitcoinWallet } from \"../../../shared/wallets/interfaces\";\n\n// Field docs use /** */ so they survive into the emitted .d.ts — the published\n// package is the only contract the external provider author (#2109) sees.\n\nexport interface DepositTermsVaultGroup {\n /** 0-based; equals the group's position (groups are ascending by vout). */\n readonly htlcVout: number;\n /** x-only hex (64 chars), as validated on-chain upstream. */\n readonly vaultProviderBtcPubkey: string;\n /** sats */\n readonly peginAmount: bigint;\n /**\n * sats; the MAXIMUM commission the depositor accepts —\n * floor(peginAmount * maxAcceptableCommissionBps / 10_000), the same\n * ceiling the registration calldata carries. An approving wallet MUST\n * enforce the VP payout commission output `<= commissionFee` (e.g. the\n * Ledger vault app does, firmware >= c8db53e), and every commission the\n * contract admits stays under this ceiling (floor is monotonic in bps),\n * so no contract-admitted deposit can be refused at payout. The actual\n * stamped commission may be lower. Display the quoted value in UI, not this.\n */\n readonly commissionFee: bigint;\n /** sats; the same value for every vault. */\n readonly depositorClaimValue: bigint;\n /**\n * sats; the cap an approving wallet enforces on the PegIn tx fee. Equals\n * the graph's exact (minimum) PegIn fee, which is deterministic — so the\n * cap is satisfied exact-by-construction.\n */\n readonly peginMaxFee: bigint;\n}\n\n/**\n * Field names follow btc-vault vocabulary (the protocol source of truth);\n * a device-wire encoder maps them to its intent fields (e.g. the Ledger TLV:\n * protocolFeeRate -> base_fee_rate, timelockPegin -> pegin_csv_timelock,\n * timelockAssert -> payout_timelock, peginAmount -> vault_amount).\n */\nexport interface DepositTerms {\n /**\n * btc-vault tx-graph version (`vaultCoreVersion`) these terms describe —\n * the vault's stamped on-chain version for resumes, the chain's\n * `activeVaultCoreVersion` for fresh deposits. It selects the PegIn shape\n * an approving wallet must expect: v1 = 2 outputs, no anchor; v2 = TRUC\n * nVersion 3, 3 outputs with a 240-sat P2A anchor at vout 2, and an\n * Assert OP_RETURN marker that raises the claim value\n * (btc-vault `transactions/pegin.rs`, `assert_marker.rs`). A provider that\n * supports only one shape MUST reject the others here rather than\n * mis-validating the PSBTs later.\n */\n vaultCoreVersion: number;\n /**\n * sat/vB; the tx-graph fee rate (protocolFeeRate), NOT the mempool funding\n * rate. Approving wallets bound each payout's fee against this rate —\n * pass the exact graph rate, not an inflated ceiling.\n */\n protocolFeeRate: bigint;\n /** Vault-UTXO CSV timelock (blocks). */\n timelockPegin: number;\n /**\n * btc-vault `timelock_assert` (t2) — the CSV on Assert output 0. Its own\n * param, though production derives it and `timelockPegin` from one value.\n */\n timelockAssert: number;\n /** HTLC refund CSV timelock (blocks). */\n timelockRefund: number;\n /**\n * 64-char hex in display order. A device-wire encoder may need the\n * little-endian form — some hardware byte-compares it against\n * PSBT_IN_PREVIOUS_TXID rather than recomputing the txid.\n */\n prepeginTxid: string;\n /** sats; the funded Pre-PegIn fee (an approving wallet caps the signed fee at this). */\n prepeginMaxFee: bigint;\n /**\n * x-only hex. Sorted ascending by the upstream on-chain validation\n * (validateOnChainParticipantKeys); the builder passes them through\n * unasserted — approving devices may reject unsorted lists at load.\n */\n vaultKeeperBtcPubkeys: readonly string[];\n /**\n * x-only hex, sorted ascending upstream independently of vaultKeeperBtcPubkeys (same\n * pass-through contract). Universal challengers only — the full graph\n * challenger set is vaultKeeperBtcPubkeys ∪ universalChallengerBtcPubkeys (vault keepers are the local\n * challengers).\n */\n universalChallengerBtcPubkeys: readonly string[];\n /** Per-vault groups, ordered by ascending htlcVout. */\n vaults: readonly DepositTermsVaultGroup[];\n}\n\n/**\n * Implemented only by depositor-approval wallets (e.g. a Ledger vault\n * provider). Provider obligations:\n *\n * - Envelope: validate terms against the device's envelope BEFORE the\n * ceremony, rejecting with the shape `{ name: \"DepositTermsRejectedError\",\n * reason: \"device-envelope\", message }` (matched structurally, not by class).\n * - Idempotence: a byte-equal re-approval MUST be a no-op while the\n * device-side approval is live; anything that invalidates it (a later\n * `deriveContextHash`, a signing failure) MUST clear the memo.\n *\n * Seam invariant: any derive invalidates a prior approval, so the SDK\n * re-approves after every derive and before the next terms-bound signature.\n * The re-approval sites are `PeginManager.preparePegin`,\n * `runDepositorPresignFlow`, and `signAndBroadcast` (the Pre-PegIn broadcast,\n * which derives immediately before approving — see `ensurePrePeginTermsApproval`).\n */\nexport interface DepositTermsApprover {\n approveDepositTerms(terms: DepositTerms): Promise<void>;\n}\n\n/** True when the wallet implements {@link DepositTermsApprover.approveDepositTerms}. */\nexport function supportsDepositApproval(\n wallet: BitcoinWallet,\n): wallet is BitcoinWallet & DepositTermsApprover {\n return (\n typeof (wallet as Partial<DepositTermsApprover>).approveDepositTerms ===\n \"function\"\n );\n}\n\n/**\n * Spreadable forward of `approveDepositTerms` for wallet-wrapper objects.\n * Object spread drops prototype methods, so every `{...wallet}` wrapper site\n * must re-attach the capability explicitly: `...forwardDepositApproval(wallet)`.\n */\nexport function forwardDepositApproval(\n wallet: BitcoinWallet,\n): Partial<DepositTermsApprover> {\n return supportsDepositApproval(wallet)\n ? { approveDepositTerms: (terms) => wallet.approveDepositTerms(terms) }\n : {};\n}\n\nexport interface BuildDepositTermsInputs {\n /** btc-vault tx-graph version the graph is built under. */\n vaultCoreVersion: number;\n protocolFeeRate: bigint;\n timelockPegin: number;\n /** btc-vault `timelock_assert` (t2) — its own param; NOT derived from timelockPegin here. */\n timelockAssert: number;\n timelockRefund: number;\n prepeginTxid: string;\n prepeginMaxFee: bigint;\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengerBtcPubkeys: readonly string[];\n /** Ceiling bps, not the quote — see {@link DepositTermsVaultGroup.commissionFee}. */\n maxAcceptableCommissionBps: number;\n peginAmounts: readonly bigint[];\n depositorClaimValue: bigint;\n peginMaxFee: bigint;\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../shared/wallets\";\nimport {\n assertPsbtUnsignedTxMatches,\n assertScriptPathSchnorrSignature,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\n\n/** Payout PSBTs are signed by the depositor on input 0 (Taproot script-path). */\nconst PAYOUT_SIGNED_INPUT_INDEX = 0;\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Forwarded to\n * {@link buildPayoutPsbt} to derive the matching graph's payout scripts.\n */\n vaultCoreVersion: number;\n\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n /** btc-vault `timelock_assert`; payout input 1's sequence. */\n timelockAssert: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n\n /**\n * Version-locked tx-graph fee rate (sat/vB) the graph was built with.\n * Forwarded to {@link buildPayoutPsbt} for the fee band.\n */\n protocolFeeRate: bigint;\n\n /** Security council member count; forwarded to the fee floor (see PayoutParams). */\n councilSize: number;\n\n /**\n * RFC-006 resolved payout destinations, keyed by lowercased x-only operation\n * pubkey. Forwarded verbatim to {@link buildPayoutPsbt}; every VK claimer\n * must be present.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /** RFC-006 VP commission destination. Forwarded to {@link buildPayoutPsbt}. */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: params.vaultCoreVersion,\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n timelockAssert: params.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n protocolFeeRate: params.protocolFeeRate,\n councilSize: params.councilSize,\n vkClaimerPayoutScriptPubKeys: params.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: params.vpCommissionScriptPubKey,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n // Critical Path #7: verify the signature against a sighash recomputed from\n // the PSBT we built, not the wallet-returned one.\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: payoutPsbt.psbtHex,\n signatureHex: signature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(transactions: SignPayoutParams[]): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: tx.vaultCoreVersion,\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n timelockAssert: tx.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n protocolFeeRate: tx.protocolFeeRate,\n councilSize: tx.councilSize,\n vkClaimerPayoutScriptPubKeys: tx.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: tx.vpCommissionScriptPubKey,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtsToSign[i],\n signatureHex: payoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n}\n","/**\n * Wallet-signing helper that routes a lone PSBT to `signPsbt`, prefers native\n * `signPsbts` for real batches, and falls back to sequential `signPsbt` for\n * wallets that don't implement batch signing.\n *\n * @module managers/pegin/signPsbtsWithFallback\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets\";\n\n/**\n * Sign one or more PSBTs against a wallet.\n *\n * A single PSBT is always signed via `signPsbt`, never the batch endpoint: it's\n * one wallet interaction either way, and `signPsbt` is the universally-required\n * wallet method (batch `signPsbts` is optional), so single-sign is the portable\n * choice — some integrations (notably MPC / institutional wallets) prefer it.\n * For a real batch (>1), wallets exposing native `signPsbts` (e.g. UniSat) sign\n * in one interaction; others loop `signPsbt`.\n *\n * @throws If native `signPsbts` returns a different number of signed PSBTs\n * than were submitted.\n */\nexport async function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtsHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (psbtsHexes.length === 1) {\n return [await wallet.signPsbt(psbtsHexes[0], options?.[0])];\n }\n\n if (typeof wallet.signPsbts === \"function\") {\n const signedPsbts = await wallet.signPsbts(psbtsHexes, options);\n if (signedPsbts.length !== psbtsHexes.length) {\n throw new Error(\n `Expected ${psbtsHexes.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n return signedPsbts;\n }\n\n const signedPsbts: string[] = [];\n for (let i = 0; i < psbtsHexes.length; i++) {\n signedPsbts.push(await wallet.signPsbt(psbtsHexes[i], options?.[i]));\n }\n return signedPsbts;\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","supportsDepositApproval","wallet","forwardDepositApproval","terms","PAYOUT_SIGNED_INPUT_INDEX","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","signature","extractPayoutSignature","assertScriptPathSchnorrSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature","signPsbtsWithFallback","psbtsHexes","options"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";var H=Object.defineProperty;var m=(e,t,r)=>t in e?H(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var b=(e,t,r)=>m(e,typeof t!="symbol"?t+"":t,r);const B=require("bitcoinjs-lib"),g=require("./bitcoin-m84N5a8o.cjs"),R=require("./sha2-DsrLC4NM.cjs");require("@babylonlabs-io/babylon-tbv-rust-wasm");const u=require("./verifyScriptPathSchnorrSignature-B8zK1ctE.cjs"),y=require("./signing-Bnsro0hE.cjs");function E(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const f=E(B),k=106,S=32,p=34;function O(e,t,r){const o=g.stripHexPrefix(e),s=f.Transaction.fromHex(o);if(s.outs.length<=t)throw new Error(`Pre-PegIn auth-anchor OP_RETURN missing: tx has ${s.outs.length} outputs, expected at least ${t+1} (vault outputs + OP_RETURN)`);const c=s.outs[t],i=c.script;if(i.length!==p||i[0]!==k||i[1]!==S)throw new Error(`Pre-PegIn auth-anchor OP_RETURN at vout ${t} has unexpected script encoding (got ${i.length}-byte script with prefix 0x${i.slice(0,Math.min(2,i.length)).toString("hex")}; expected ${p}-byte OP_RETURN + PUSH32 layout)`);const l=i.slice(2).toString("hex").toLowerCase();if(l!==r.toLowerCase())throw new Error(`Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${t}: tx pushes ${l}, expected ${r}`);if(c.value!==0)throw new Error(`Pre-PegIn auth-anchor OP_RETURN at vout ${t} has non-zero value ${c.value}; OP_RETURN outputs must be 0-value`)}function $(e){let t;try{t=f.Transaction.fromHex(g.stripHexPrefix(e))}catch{return}const r=[];for(let o=0;o<t.outs.length;o++){const s=t.outs[o],c=s.script;c.length===p&&c[0]===k&&c[1]===S&&s.value===0&&r.push({vout:o,hash:c.slice(2).toString("hex").toLowerCase()})}return r.length===1?r[0]:void 0}const x=66;function v(e){if(!e.startsWith("0x")&&!e.startsWith("0X"))throw new Error("Expected 0x-prefixed hex string");const t=e.slice(2);if(t.length%2!==0)throw new Error(`Hex string has odd length: ${t.length}`);if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("Hex string contains non-hex characters");const r=new Uint8Array(t.length/2);for(let o=0;o<r.length;o++)r[o]=parseInt(t.slice(o*2,o*2+2),16);return r}function _(e){return`0x${Array.from(e).map(t=>t.toString(16).padStart(2,"0")).join("")}`}function h(e,t){if(e.length!==x)throw new Error(`${t} must be exactly 32 bytes (${x} hex chars with 0x prefix), got ${e.length}`)}function w(e){h(e,"Secret");const t=v(e),r=R.sha256(t);return _(r)}function A(e,t){return h(e,"Secret"),h(t,"Hashlock"),v(t),w(e).toLowerCase()===t.toLowerCase()}function T(e){return typeof e.approveDepositTerms=="function"}function K(e){return T(e)?{approveDepositTerms:t=>e.approveDepositTerms(t)}:{}}const d=0;class C{constructor(t){b(this,"config");this.config=t}async signPayoutTransaction(t){const r=await this.config.btcWallet.getPublicKeyHex(),{depositorPubkey:o}=g.validateWalletPubkey(r,t.depositorBtcPubkey),s=await u.buildPayoutPsbt({vaultCoreVersion:t.vaultCoreVersion,payoutTxHex:t.payoutTxHex,peginTxHex:t.peginTxHex,assertTxHex:t.assertTxHex,depositorBtcPubkey:o,vaultProviderBtcPubkey:t.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:t.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:t.universalChallengerBtcPubkeys,timelockPegin:t.timelockPegin,timelockAssert:t.timelockAssert,network:this.config.network,claimerBtcPubkey:t.claimerBtcPubkey,registeredPayoutScriptPubKey:t.registeredPayoutScriptPubKey,commissionBps:t.commissionBps,protocolFeeRate:t.protocolFeeRate,councilSize:t.councilSize,vkClaimerPayoutScriptPubKeys:t.vkClaimerPayoutScriptPubKeys,vpCommissionScriptPubKey:t.vpCommissionScriptPubKey}),c=await this.config.btcWallet.signPsbt(s.psbtHex,y.createTaprootScriptPathSignOptions(r,1));u.assertPsbtUnsignedTxMatches({requestedPsbtHex:s.psbtHex,returnedPsbtHex:c});const i=u.extractPayoutSignature(c,o);return u.assertScriptPathSchnorrSignature({requestedPsbtHex:s.psbtHex,signatureHex:i,signerXOnlyPubkeyHex:o,inputIndex:d}),{signature:i,depositorBtcPubkey:o}}getNetwork(){return this.config.network}supportsBatchSigning(){return typeof this.config.btcWallet.signPsbts=="function"}async signPayoutTransactionsBatch(t){if(!this.supportsBatchSigning())throw new Error("Wallet does not support batch signing (signPsbts method not available)");const r=await this.config.btcWallet.getPublicKeyHex(),o=[],s=[],c=[];for(const n of t){const{depositorPubkey:a}=g.validateWalletPubkey(r,n.depositorBtcPubkey);c.push(a);const P=await u.buildPayoutPsbt({vaultCoreVersion:n.vaultCoreVersion,payoutTxHex:n.payoutTxHex,peginTxHex:n.peginTxHex,assertTxHex:n.assertTxHex,depositorBtcPubkey:a,vaultProviderBtcPubkey:n.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:n.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:n.universalChallengerBtcPubkeys,timelockPegin:n.timelockPegin,timelockAssert:n.timelockAssert,network:this.config.network,claimerBtcPubkey:n.claimerBtcPubkey,registeredPayoutScriptPubKey:n.registeredPayoutScriptPubKey,commissionBps:n.commissionBps,protocolFeeRate:n.protocolFeeRate,councilSize:n.councilSize,vkClaimerPayoutScriptPubKeys:n.vkClaimerPayoutScriptPubKeys,vpCommissionScriptPubKey:n.vpCommissionScriptPubKey});o.push(P.psbtHex),s.push(y.createTaprootScriptPathSignOptions(r,1))}const i=await this.config.btcWallet.signPsbts(o,s);if(i.length!==t.length)throw new Error(`Expected ${t.length} signed PSBTs but received ${i.length}`);const l=[];for(let n=0;n<t.length;n++){const a=c[n];u.assertPsbtUnsignedTxMatches({requestedPsbtHex:o[n],returnedPsbtHex:i[n]});const P=u.extractPayoutSignature(i[n],a);u.assertScriptPathSchnorrSignature({requestedPsbtHex:o[n],signatureHex:P,signerXOnlyPubkeyHex:a,inputIndex:d}),l.push({payoutSignature:P,depositorBtcPubkey:a})}return l}}async function U(e,t,r){if(t.length===1)return[await e.signPsbt(t[0],r==null?void 0:r[0])];if(typeof e.signPsbts=="function"){const s=await e.signPsbts(t,r);if(s.length!==t.length)throw new Error(`Expected ${t.length} signed PSBTs but received ${s.length}`);return s}const o=[];for(let s=0;s<t.length;s++)o.push(await e.signPsbt(t[s],r==null?void 0:r[s]));return o}exports.PayoutManager=C;exports.assertAuthAnchorOpReturn=O;exports.computeHashlock=w;exports.findAuthAnchorOpReturn=$;exports.forwardDepositApproval=K;exports.signPsbtsWithFallback=U;exports.supportsDepositApproval=T;exports.validateSecretAgainstHashlock=A;
2
- //# sourceMappingURL=signPsbtsWithFallback-DbOmw8Ou.cjs.map
1
+ "use strict";var H=Object.defineProperty;var m=(e,t,r)=>t in e?H(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var b=(e,t,r)=>m(e,typeof t!="symbol"?t+"":t,r);const B=require("bitcoinjs-lib"),g=require("./bitcoin-m84N5a8o.cjs"),R=require("./sha2-DsrLC4NM.cjs");require("@babylonlabs-io/babylon-tbv-rust-wasm");const u=require("./verifyScriptPathSchnorrSignature-DPAd_qke.cjs"),y=require("./signing-Bnsro0hE.cjs");function E(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const f=E(B),k=106,S=32,p=34;function O(e,t,r){const o=g.stripHexPrefix(e),s=f.Transaction.fromHex(o);if(s.outs.length<=t)throw new Error(`Pre-PegIn auth-anchor OP_RETURN missing: tx has ${s.outs.length} outputs, expected at least ${t+1} (vault outputs + OP_RETURN)`);const c=s.outs[t],i=c.script;if(i.length!==p||i[0]!==k||i[1]!==S)throw new Error(`Pre-PegIn auth-anchor OP_RETURN at vout ${t} has unexpected script encoding (got ${i.length}-byte script with prefix 0x${i.slice(0,Math.min(2,i.length)).toString("hex")}; expected ${p}-byte OP_RETURN + PUSH32 layout)`);const l=i.slice(2).toString("hex").toLowerCase();if(l!==r.toLowerCase())throw new Error(`Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${t}: tx pushes ${l}, expected ${r}`);if(c.value!==0)throw new Error(`Pre-PegIn auth-anchor OP_RETURN at vout ${t} has non-zero value ${c.value}; OP_RETURN outputs must be 0-value`)}function $(e){let t;try{t=f.Transaction.fromHex(g.stripHexPrefix(e))}catch{return}const r=[];for(let o=0;o<t.outs.length;o++){const s=t.outs[o],c=s.script;c.length===p&&c[0]===k&&c[1]===S&&s.value===0&&r.push({vout:o,hash:c.slice(2).toString("hex").toLowerCase()})}return r.length===1?r[0]:void 0}const x=66;function v(e){if(!e.startsWith("0x")&&!e.startsWith("0X"))throw new Error("Expected 0x-prefixed hex string");const t=e.slice(2);if(t.length%2!==0)throw new Error(`Hex string has odd length: ${t.length}`);if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("Hex string contains non-hex characters");const r=new Uint8Array(t.length/2);for(let o=0;o<r.length;o++)r[o]=parseInt(t.slice(o*2,o*2+2),16);return r}function _(e){return`0x${Array.from(e).map(t=>t.toString(16).padStart(2,"0")).join("")}`}function h(e,t){if(e.length!==x)throw new Error(`${t} must be exactly 32 bytes (${x} hex chars with 0x prefix), got ${e.length}`)}function w(e){h(e,"Secret");const t=v(e),r=R.sha256(t);return _(r)}function A(e,t){return h(e,"Secret"),h(t,"Hashlock"),v(t),w(e).toLowerCase()===t.toLowerCase()}function T(e){return typeof e.approveDepositTerms=="function"}function K(e){return T(e)?{approveDepositTerms:t=>e.approveDepositTerms(t)}:{}}const d=0;class C{constructor(t){b(this,"config");this.config=t}async signPayoutTransaction(t){const r=await this.config.btcWallet.getPublicKeyHex(),{depositorPubkey:o}=g.validateWalletPubkey(r,t.depositorBtcPubkey),s=await u.buildPayoutPsbt({vaultCoreVersion:t.vaultCoreVersion,payoutTxHex:t.payoutTxHex,peginTxHex:t.peginTxHex,assertTxHex:t.assertTxHex,depositorBtcPubkey:o,vaultProviderBtcPubkey:t.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:t.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:t.universalChallengerBtcPubkeys,timelockPegin:t.timelockPegin,timelockAssert:t.timelockAssert,network:this.config.network,claimerBtcPubkey:t.claimerBtcPubkey,registeredPayoutScriptPubKey:t.registeredPayoutScriptPubKey,commissionBps:t.commissionBps,protocolFeeRate:t.protocolFeeRate,councilSize:t.councilSize,vkClaimerPayoutScriptPubKeys:t.vkClaimerPayoutScriptPubKeys,vpCommissionScriptPubKey:t.vpCommissionScriptPubKey}),c=await this.config.btcWallet.signPsbt(s.psbtHex,y.createTaprootScriptPathSignOptions(r,1));u.assertPsbtUnsignedTxMatches({requestedPsbtHex:s.psbtHex,returnedPsbtHex:c});const i=u.extractPayoutSignature(c,o);return u.assertScriptPathSchnorrSignature({requestedPsbtHex:s.psbtHex,signatureHex:i,signerXOnlyPubkeyHex:o,inputIndex:d}),{signature:i,depositorBtcPubkey:o}}getNetwork(){return this.config.network}supportsBatchSigning(){return typeof this.config.btcWallet.signPsbts=="function"}async signPayoutTransactionsBatch(t){if(!this.supportsBatchSigning())throw new Error("Wallet does not support batch signing (signPsbts method not available)");const r=await this.config.btcWallet.getPublicKeyHex(),o=[],s=[],c=[];for(const n of t){const{depositorPubkey:a}=g.validateWalletPubkey(r,n.depositorBtcPubkey);c.push(a);const P=await u.buildPayoutPsbt({vaultCoreVersion:n.vaultCoreVersion,payoutTxHex:n.payoutTxHex,peginTxHex:n.peginTxHex,assertTxHex:n.assertTxHex,depositorBtcPubkey:a,vaultProviderBtcPubkey:n.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:n.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:n.universalChallengerBtcPubkeys,timelockPegin:n.timelockPegin,timelockAssert:n.timelockAssert,network:this.config.network,claimerBtcPubkey:n.claimerBtcPubkey,registeredPayoutScriptPubKey:n.registeredPayoutScriptPubKey,commissionBps:n.commissionBps,protocolFeeRate:n.protocolFeeRate,councilSize:n.councilSize,vkClaimerPayoutScriptPubKeys:n.vkClaimerPayoutScriptPubKeys,vpCommissionScriptPubKey:n.vpCommissionScriptPubKey});o.push(P.psbtHex),s.push(y.createTaprootScriptPathSignOptions(r,1))}const i=await this.config.btcWallet.signPsbts(o,s);if(i.length!==t.length)throw new Error(`Expected ${t.length} signed PSBTs but received ${i.length}`);const l=[];for(let n=0;n<t.length;n++){const a=c[n];u.assertPsbtUnsignedTxMatches({requestedPsbtHex:o[n],returnedPsbtHex:i[n]});const P=u.extractPayoutSignature(i[n],a);u.assertScriptPathSchnorrSignature({requestedPsbtHex:o[n],signatureHex:P,signerXOnlyPubkeyHex:a,inputIndex:d}),l.push({payoutSignature:P,depositorBtcPubkey:a})}return l}}async function U(e,t,r){if(t.length===1)return[await e.signPsbt(t[0],r==null?void 0:r[0])];if(typeof e.signPsbts=="function"){const s=await e.signPsbts(t,r);if(s.length!==t.length)throw new Error(`Expected ${t.length} signed PSBTs but received ${s.length}`);return s}const o=[];for(let s=0;s<t.length;s++)o.push(await e.signPsbt(t[s],r==null?void 0:r[s]));return o}exports.PayoutManager=C;exports.assertAuthAnchorOpReturn=O;exports.computeHashlock=w;exports.findAuthAnchorOpReturn=$;exports.forwardDepositApproval=K;exports.signPsbtsWithFallback=U;exports.supportsDepositApproval=T;exports.validateSecretAgainstHashlock=A;
2
+ //# sourceMappingURL=signPsbtsWithFallback-PTgWxsG3.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"signPsbtsWithFallback-DbOmw8Ou.cjs","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/deposit-terms/depositTerms.ts","../src/tbv/core/managers/PayoutManager.ts","../src/tbv/core/managers/pegin/signPsbtsWithFallback.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","import type { BitcoinWallet } from \"../../../shared/wallets/interfaces\";\n\n// Field docs use /** */ so they survive into the emitted .d.ts — the published\n// package is the only contract the external provider author (#2109) sees.\n\nexport interface DepositTermsVaultGroup {\n /** 0-based; equals the group's position (groups are ascending by vout). */\n readonly htlcVout: number;\n /** x-only hex (64 chars), as validated on-chain upstream. */\n readonly vaultProviderBtcPubkey: string;\n /** sats */\n readonly peginAmount: bigint;\n /**\n * sats; the MAXIMUM commission the depositor accepts —\n * floor(peginAmount * maxAcceptableCommissionBps / 10_000), the same\n * ceiling the registration calldata carries. An approving wallet MUST\n * enforce the VP payout commission output `<= commissionFee` (e.g. the\n * Ledger vault app does, firmware >= c8db53e), and every commission the\n * contract admits stays under this ceiling (floor is monotonic in bps),\n * so no contract-admitted deposit can be refused at payout. The actual\n * stamped commission may be lower. Display the quoted value in UI, not this.\n */\n readonly commissionFee: bigint;\n /** sats; the same value for every vault. */\n readonly depositorClaimValue: bigint;\n /**\n * sats; the cap an approving wallet enforces on the PegIn tx fee. Equals\n * the graph's exact (minimum) PegIn fee, which is deterministic — so the\n * cap is satisfied exact-by-construction.\n */\n readonly peginMaxFee: bigint;\n}\n\n/**\n * Field names follow btc-vault vocabulary (the protocol source of truth);\n * a device-wire encoder maps them to its intent fields (e.g. the Ledger TLV:\n * protocolFeeRate -> base_fee_rate, timelockPegin -> pegin_csv_timelock,\n * timelockAssert -> payout_timelock, peginAmount -> vault_amount).\n */\nexport interface DepositTerms {\n /**\n * btc-vault tx-graph version (`vaultCoreVersion`) these terms describe —\n * the vault's stamped on-chain version for resumes, the chain's\n * `activeVaultCoreVersion` for fresh deposits. It selects the PegIn shape\n * an approving wallet must expect: v1 = 2 outputs, no anchor; v2 = TRUC\n * nVersion 3, 3 outputs with a 240-sat P2A anchor at vout 2, and an\n * Assert OP_RETURN marker that raises the claim value\n * (btc-vault `transactions/pegin.rs`, `assert_marker.rs`). A provider that\n * supports only one shape MUST reject the others here rather than\n * mis-validating the PSBTs later.\n */\n vaultCoreVersion: number;\n /**\n * sat/vB; the tx-graph fee rate (protocolFeeRate), NOT the mempool funding\n * rate. Approving wallets bound each payout's fee against this rate —\n * pass the exact graph rate, not an inflated ceiling.\n */\n protocolFeeRate: bigint;\n /** Vault-UTXO CSV timelock (blocks). */\n timelockPegin: number;\n /**\n * btc-vault `timelock_assert` (t2) — the CSV on Assert output 0. Its own\n * param, though production derives it and `timelockPegin` from one value.\n */\n timelockAssert: number;\n /** HTLC refund CSV timelock (blocks). */\n timelockRefund: number;\n /**\n * 64-char hex in display order. A device-wire encoder may need the\n * little-endian form — some hardware byte-compares it against\n * PSBT_IN_PREVIOUS_TXID rather than recomputing the txid.\n */\n prepeginTxid: string;\n /** sats; the funded Pre-PegIn fee (an approving wallet caps the signed fee at this). */\n prepeginMaxFee: bigint;\n /**\n * x-only hex. Sorted ascending by the upstream on-chain validation\n * (validateOnChainParticipantKeys); the builder passes them through\n * unasserted — approving devices may reject unsorted lists at load.\n */\n vaultKeeperBtcPubkeys: readonly string[];\n /**\n * x-only hex, sorted ascending upstream independently of vaultKeeperBtcPubkeys (same\n * pass-through contract). Universal challengers only — the full graph\n * challenger set is vaultKeeperBtcPubkeys ∪ universalChallengerBtcPubkeys (vault keepers are the local\n * challengers).\n */\n universalChallengerBtcPubkeys: readonly string[];\n /** Per-vault groups, ordered by ascending htlcVout. */\n vaults: readonly DepositTermsVaultGroup[];\n}\n\n/**\n * Implemented only by depositor-approval wallets (e.g. a Ledger vault\n * provider). Provider obligations:\n *\n * - Envelope: validate terms against the device's envelope BEFORE the\n * ceremony, rejecting with the shape `{ name: \"DepositTermsRejectedError\",\n * reason: \"device-envelope\", message }` (matched structurally, not by class).\n * - Idempotence: a byte-equal re-approval MUST be a no-op while the\n * device-side approval is live; anything that invalidates it (a later\n * `deriveContextHash`, a signing failure) MUST clear the memo.\n *\n * Seam invariant: any derive invalidates a prior approval, so the SDK\n * re-approves after every derive and before the next terms-bound signature.\n * The re-approval sites are `PeginManager.preparePegin`,\n * `runDepositorPresignFlow`, and `signAndBroadcast` (the Pre-PegIn broadcast,\n * which derives immediately before approving — see `ensurePrePeginTermsApproval`).\n */\nexport interface DepositTermsApprover {\n approveDepositTerms(terms: DepositTerms): Promise<void>;\n}\n\n/** True when the wallet implements {@link DepositTermsApprover.approveDepositTerms}. */\nexport function supportsDepositApproval(\n wallet: BitcoinWallet,\n): wallet is BitcoinWallet & DepositTermsApprover {\n return (\n typeof (wallet as Partial<DepositTermsApprover>).approveDepositTerms ===\n \"function\"\n );\n}\n\n/**\n * Spreadable forward of `approveDepositTerms` for wallet-wrapper objects.\n * Object spread drops prototype methods, so every `{...wallet}` wrapper site\n * must re-attach the capability explicitly: `...forwardDepositApproval(wallet)`.\n */\nexport function forwardDepositApproval(\n wallet: BitcoinWallet,\n): Partial<DepositTermsApprover> {\n return supportsDepositApproval(wallet)\n ? { approveDepositTerms: (terms) => wallet.approveDepositTerms(terms) }\n : {};\n}\n\nexport interface BuildDepositTermsInputs {\n /** btc-vault tx-graph version the graph is built under. */\n vaultCoreVersion: number;\n protocolFeeRate: bigint;\n timelockPegin: number;\n /** btc-vault `timelock_assert` (t2) — its own param; NOT derived from timelockPegin here. */\n timelockAssert: number;\n timelockRefund: number;\n prepeginTxid: string;\n prepeginMaxFee: bigint;\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengerBtcPubkeys: readonly string[];\n /** Ceiling bps, not the quote — see {@link DepositTermsVaultGroup.commissionFee}. */\n maxAcceptableCommissionBps: number;\n peginAmounts: readonly bigint[];\n depositorClaimValue: bigint;\n peginMaxFee: bigint;\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../shared/wallets\";\nimport {\n assertPsbtUnsignedTxMatches,\n assertScriptPathSchnorrSignature,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\n\n/** Payout PSBTs are signed by the depositor on input 0 (Taproot script-path). */\nconst PAYOUT_SIGNED_INPUT_INDEX = 0;\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Forwarded to\n * {@link buildPayoutPsbt} to derive the matching graph's payout scripts.\n */\n vaultCoreVersion: number;\n\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n /** btc-vault `timelock_assert`; payout input 1's sequence. */\n timelockAssert: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n\n /**\n * Version-locked tx-graph fee rate (sat/vB) the graph was built with.\n * Forwarded to {@link buildPayoutPsbt} for the fee band.\n */\n protocolFeeRate: bigint;\n\n /** Security council member count; forwarded to the fee floor (see PayoutParams). */\n councilSize: number;\n\n /**\n * RFC-006 resolved payout destinations, keyed by lowercased x-only operation\n * pubkey. Forwarded verbatim to {@link buildPayoutPsbt}; every VK claimer\n * must be present.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /** RFC-006 VP commission destination. Forwarded to {@link buildPayoutPsbt}. */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: params.vaultCoreVersion,\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n timelockAssert: params.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n protocolFeeRate: params.protocolFeeRate,\n councilSize: params.councilSize,\n vkClaimerPayoutScriptPubKeys: params.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: params.vpCommissionScriptPubKey,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n // Critical Path #7: verify the signature against a sighash recomputed from\n // the PSBT we built, not the wallet-returned one.\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: payoutPsbt.psbtHex,\n signatureHex: signature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(transactions: SignPayoutParams[]): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: tx.vaultCoreVersion,\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n timelockAssert: tx.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n protocolFeeRate: tx.protocolFeeRate,\n councilSize: tx.councilSize,\n vkClaimerPayoutScriptPubKeys: tx.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: tx.vpCommissionScriptPubKey,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtsToSign[i],\n signatureHex: payoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n}\n","/**\n * Wallet-signing helper that routes a lone PSBT to `signPsbt`, prefers native\n * `signPsbts` for real batches, and falls back to sequential `signPsbt` for\n * wallets that don't implement batch signing.\n *\n * @module managers/pegin/signPsbtsWithFallback\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets\";\n\n/**\n * Sign one or more PSBTs against a wallet.\n *\n * A single PSBT is always signed via `signPsbt`, never the batch endpoint: it's\n * one wallet interaction either way, and `signPsbt` is the universally-required\n * wallet method (batch `signPsbts` is optional), so single-sign is the portable\n * choice — some integrations (notably MPC / institutional wallets) prefer it.\n * For a real batch (>1), wallets exposing native `signPsbts` (e.g. UniSat) sign\n * in one interaction; others loop `signPsbt`.\n *\n * @throws If native `signPsbts` returns a different number of signed PSBTs\n * than were submitted.\n */\nexport async function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtsHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (psbtsHexes.length === 1) {\n return [await wallet.signPsbt(psbtsHexes[0], options?.[0])];\n }\n\n if (typeof wallet.signPsbts === \"function\") {\n const signedPsbts = await wallet.signPsbts(psbtsHexes, options);\n if (signedPsbts.length !== psbtsHexes.length) {\n throw new Error(\n `Expected ${psbtsHexes.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n return signedPsbts;\n }\n\n const signedPsbts: string[] = [];\n for (let i = 0; i < psbtsHexes.length; i++) {\n signedPsbts.push(await wallet.signPsbt(psbtsHexes[i], options?.[i]));\n }\n return signedPsbts;\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","supportsDepositApproval","wallet","forwardDepositApproval","terms","PAYOUT_SIGNED_INPUT_INDEX","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","signature","extractPayoutSignature","assertScriptPathSchnorrSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature","signPsbtsWithFallback","psbtsHexes","options"],"mappings":"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"}
1
+ {"version":3,"file":"signPsbtsWithFallback-PTgWxsG3.cjs","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/deposit-terms/depositTerms.ts","../src/tbv/core/managers/PayoutManager.ts","../src/tbv/core/managers/pegin/signPsbtsWithFallback.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","import type { BitcoinWallet } from \"../../../shared/wallets/interfaces\";\n\n// Field docs use /** */ so they survive into the emitted .d.ts — the published\n// package is the only contract the external provider author (#2109) sees.\n\nexport interface DepositTermsVaultGroup {\n /** 0-based; equals the group's position (groups are ascending by vout). */\n readonly htlcVout: number;\n /** x-only hex (64 chars), as validated on-chain upstream. */\n readonly vaultProviderBtcPubkey: string;\n /** sats */\n readonly peginAmount: bigint;\n /**\n * sats; the MAXIMUM commission the depositor accepts —\n * floor(peginAmount * maxAcceptableCommissionBps / 10_000), the same\n * ceiling the registration calldata carries. An approving wallet MUST\n * enforce the VP payout commission output `<= commissionFee` (e.g. the\n * Ledger vault app does, firmware >= c8db53e), and every commission the\n * contract admits stays under this ceiling (floor is monotonic in bps),\n * so no contract-admitted deposit can be refused at payout. The actual\n * stamped commission may be lower. Display the quoted value in UI, not this.\n */\n readonly commissionFee: bigint;\n /** sats; the same value for every vault. */\n readonly depositorClaimValue: bigint;\n /**\n * sats; the cap an approving wallet enforces on the PegIn tx fee. Equals\n * the graph's exact (minimum) PegIn fee, which is deterministic — so the\n * cap is satisfied exact-by-construction.\n */\n readonly peginMaxFee: bigint;\n}\n\n/**\n * Field names follow btc-vault vocabulary (the protocol source of truth);\n * a device-wire encoder maps them to its intent fields (e.g. the Ledger TLV:\n * protocolFeeRate -> base_fee_rate, timelockPegin -> pegin_csv_timelock,\n * timelockAssert -> payout_timelock, peginAmount -> vault_amount).\n */\nexport interface DepositTerms {\n /**\n * btc-vault tx-graph version (`vaultCoreVersion`) these terms describe —\n * the vault's stamped on-chain version for resumes, the chain's\n * `activeVaultCoreVersion` for fresh deposits. It selects the PegIn shape\n * an approving wallet must expect: v1 = 2 outputs, no anchor; v2 = TRUC\n * nVersion 3, 3 outputs with a 240-sat P2A anchor at vout 2, and an\n * Assert OP_RETURN marker that raises the claim value\n * (btc-vault `transactions/pegin.rs`, `assert_marker.rs`). A provider that\n * supports only one shape MUST reject the others here rather than\n * mis-validating the PSBTs later.\n */\n vaultCoreVersion: number;\n /**\n * sat/vB; the tx-graph fee rate (protocolFeeRate), NOT the mempool funding\n * rate. Approving wallets bound each payout's fee against this rate —\n * pass the exact graph rate, not an inflated ceiling.\n */\n protocolFeeRate: bigint;\n /** Vault-UTXO CSV timelock (blocks). */\n timelockPegin: number;\n /**\n * btc-vault `timelock_assert` (t2) — the CSV on Assert output 0. Its own\n * param, though production derives it and `timelockPegin` from one value.\n */\n timelockAssert: number;\n /** HTLC refund CSV timelock (blocks). */\n timelockRefund: number;\n /**\n * 64-char hex in display order. A device-wire encoder may need the\n * little-endian form — some hardware byte-compares it against\n * PSBT_IN_PREVIOUS_TXID rather than recomputing the txid.\n */\n prepeginTxid: string;\n /** sats; the funded Pre-PegIn fee (an approving wallet caps the signed fee at this). */\n prepeginMaxFee: bigint;\n /**\n * x-only hex. Sorted ascending by the upstream on-chain validation\n * (validateOnChainParticipantKeys); the builder passes them through\n * unasserted — approving devices may reject unsorted lists at load.\n */\n vaultKeeperBtcPubkeys: readonly string[];\n /**\n * x-only hex, sorted ascending upstream independently of vaultKeeperBtcPubkeys (same\n * pass-through contract). Universal challengers only — the full graph\n * challenger set is vaultKeeperBtcPubkeys ∪ universalChallengerBtcPubkeys (vault keepers are the local\n * challengers).\n */\n universalChallengerBtcPubkeys: readonly string[];\n /** Per-vault groups, ordered by ascending htlcVout. */\n vaults: readonly DepositTermsVaultGroup[];\n}\n\n/**\n * Implemented only by depositor-approval wallets (e.g. a Ledger vault\n * provider). Provider obligations:\n *\n * - Envelope: validate terms against the device's envelope BEFORE the\n * ceremony, rejecting with the shape `{ name: \"DepositTermsRejectedError\",\n * reason: \"device-envelope\", message }` (matched structurally, not by class).\n * - Idempotence: a byte-equal re-approval MUST be a no-op while the\n * device-side approval is live; anything that invalidates it (a later\n * `deriveContextHash`, a signing failure) MUST clear the memo.\n *\n * Seam invariant: any derive invalidates a prior approval, so the SDK\n * re-approves after every derive and before the next terms-bound signature.\n * The re-approval sites are `PeginManager.preparePegin`,\n * `runDepositorPresignFlow`, and `signAndBroadcast` (the Pre-PegIn broadcast,\n * which derives immediately before approving — see `ensurePrePeginTermsApproval`).\n */\nexport interface DepositTermsApprover {\n approveDepositTerms(terms: DepositTerms): Promise<void>;\n}\n\n/** True when the wallet implements {@link DepositTermsApprover.approveDepositTerms}. */\nexport function supportsDepositApproval(\n wallet: BitcoinWallet,\n): wallet is BitcoinWallet & DepositTermsApprover {\n return (\n typeof (wallet as Partial<DepositTermsApprover>).approveDepositTerms ===\n \"function\"\n );\n}\n\n/**\n * Spreadable forward of `approveDepositTerms` for wallet-wrapper objects.\n * Object spread drops prototype methods, so every `{...wallet}` wrapper site\n * must re-attach the capability explicitly: `...forwardDepositApproval(wallet)`.\n */\nexport function forwardDepositApproval(\n wallet: BitcoinWallet,\n): Partial<DepositTermsApprover> {\n return supportsDepositApproval(wallet)\n ? { approveDepositTerms: (terms) => wallet.approveDepositTerms(terms) }\n : {};\n}\n\nexport interface BuildDepositTermsInputs {\n /** btc-vault tx-graph version the graph is built under. */\n vaultCoreVersion: number;\n protocolFeeRate: bigint;\n timelockPegin: number;\n /** btc-vault `timelock_assert` (t2) — its own param; NOT derived from timelockPegin here. */\n timelockAssert: number;\n timelockRefund: number;\n prepeginTxid: string;\n prepeginMaxFee: bigint;\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: readonly string[];\n universalChallengerBtcPubkeys: readonly string[];\n /** Ceiling bps, not the quote — see {@link DepositTermsVaultGroup.commissionFee}. */\n maxAcceptableCommissionBps: number;\n peginAmounts: readonly bigint[];\n depositorClaimValue: bigint;\n peginMaxFee: bigint;\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../shared/wallets\";\nimport {\n assertPsbtUnsignedTxMatches,\n assertScriptPathSchnorrSignature,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\n\n/** Payout PSBTs are signed by the depositor on input 0 (Taproot script-path). */\nconst PAYOUT_SIGNED_INPUT_INDEX = 0;\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Forwarded to\n * {@link buildPayoutPsbt} to derive the matching graph's payout scripts.\n */\n vaultCoreVersion: number;\n\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n /** btc-vault `timelock_assert`; payout input 1's sequence. */\n timelockAssert: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n\n /**\n * Version-locked tx-graph fee rate (sat/vB) the graph was built with.\n * Forwarded to {@link buildPayoutPsbt} for the fee band.\n */\n protocolFeeRate: bigint;\n\n /** Security council member count; forwarded to the fee floor (see PayoutParams). */\n councilSize: number;\n\n /**\n * RFC-006 resolved payout destinations, keyed by lowercased x-only operation\n * pubkey. Forwarded verbatim to {@link buildPayoutPsbt}; every VK claimer\n * must be present.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /** RFC-006 VP commission destination. Forwarded to {@link buildPayoutPsbt}. */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: params.vaultCoreVersion,\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n timelockAssert: params.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n protocolFeeRate: params.protocolFeeRate,\n councilSize: params.councilSize,\n vkClaimerPayoutScriptPubKeys: params.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: params.vpCommissionScriptPubKey,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n // Critical Path #7: verify the signature against a sighash recomputed from\n // the PSBT we built, not the wallet-returned one.\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: payoutPsbt.psbtHex,\n signatureHex: signature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(transactions: SignPayoutParams[]): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n vaultCoreVersion: tx.vaultCoreVersion,\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n timelockAssert: tx.timelockAssert,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n protocolFeeRate: tx.protocolFeeRate,\n councilSize: tx.councilSize,\n vkClaimerPayoutScriptPubKeys: tx.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: tx.vpCommissionScriptPubKey,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtsToSign[i],\n signatureHex: payoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: PAYOUT_SIGNED_INPUT_INDEX,\n });\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n}\n","/**\n * Wallet-signing helper that routes a lone PSBT to `signPsbt`, prefers native\n * `signPsbts` for real batches, and falls back to sequential `signPsbt` for\n * wallets that don't implement batch signing.\n *\n * @module managers/pegin/signPsbtsWithFallback\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets\";\n\n/**\n * Sign one or more PSBTs against a wallet.\n *\n * A single PSBT is always signed via `signPsbt`, never the batch endpoint: it's\n * one wallet interaction either way, and `signPsbt` is the universally-required\n * wallet method (batch `signPsbts` is optional), so single-sign is the portable\n * choice — some integrations (notably MPC / institutional wallets) prefer it.\n * For a real batch (>1), wallets exposing native `signPsbts` (e.g. UniSat) sign\n * in one interaction; others loop `signPsbt`.\n *\n * @throws If native `signPsbts` returns a different number of signed PSBTs\n * than were submitted.\n */\nexport async function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtsHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (psbtsHexes.length === 1) {\n return [await wallet.signPsbt(psbtsHexes[0], options?.[0])];\n }\n\n if (typeof wallet.signPsbts === \"function\") {\n const signedPsbts = await wallet.signPsbts(psbtsHexes, options);\n if (signedPsbts.length !== psbtsHexes.length) {\n throw new Error(\n `Expected ${psbtsHexes.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n return signedPsbts;\n }\n\n const signedPsbts: string[] = [];\n for (let i = 0; i < psbtsHexes.length; i++) {\n signedPsbts.push(await wallet.signPsbt(psbtsHexes[i], options?.[i]));\n }\n return signedPsbts;\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","supportsDepositApproval","wallet","forwardDepositApproval","terms","PAYOUT_SIGNED_INPUT_INDEX","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","signature","extractPayoutSignature","assertScriptPathSchnorrSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature","signPsbtsWithFallback","psbtsHexes","options"],"mappings":"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"}