@babylonlabs-io/ts-sdk 0.36.2 → 0.37.1

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 (118) hide show
  1. package/dist/PayoutManager-BxAY2x0g.cjs +2 -0
  2. package/dist/PayoutManager-BxAY2x0g.cjs.map +1 -0
  3. package/dist/{PayoutManager-s_uH8Uuj.js → PayoutManager-sfxuOBGq.js} +51 -43
  4. package/dist/PayoutManager-sfxuOBGq.js.map +1 -0
  5. package/dist/{PeginManager-CB-dVkT2.js → PeginManager-C7-XYrkK.js} +13 -14
  6. package/dist/{PeginManager-CB-dVkT2.js.map → PeginManager-C7-XYrkK.js.map} +1 -1
  7. package/dist/{PeginManager-BPXVXu8t.cjs → PeginManager-CRuwG4I-.cjs} +2 -2
  8. package/dist/{PeginManager-BPXVXu8t.cjs.map → PeginManager-CRuwG4I-.cjs.map} +1 -1
  9. package/dist/assertPsbtUnsignedTxMatches-BoHwgW30.cjs +2 -0
  10. package/dist/assertPsbtUnsignedTxMatches-BoHwgW30.cjs.map +1 -0
  11. package/dist/assertPsbtUnsignedTxMatches-D7RxpR4A.js +263 -0
  12. package/dist/assertPsbtUnsignedTxMatches-D7RxpR4A.js.map +1 -0
  13. package/dist/{bitcoin-B0S8SHCX.js → bitcoin-B5aNKtsk.js} +77 -60
  14. package/dist/{bitcoin-B0S8SHCX.js.map → bitcoin-B5aNKtsk.js.map} +1 -1
  15. package/dist/bitcoin-CHfKAhcI.cjs +2 -0
  16. package/dist/{bitcoin-B3aqjuMP.cjs.map → bitcoin-CHfKAhcI.cjs.map} +1 -1
  17. package/dist/{buildAndBroadcastRefund-C2VqXiOx.js → buildAndBroadcastRefund-C1eOhIdo.js} +322 -322
  18. package/dist/buildAndBroadcastRefund-C1eOhIdo.js.map +1 -0
  19. package/dist/buildAndBroadcastRefund-_CEDUU5H.cjs +2 -0
  20. package/dist/buildAndBroadcastRefund-_CEDUU5H.cjs.map +1 -0
  21. package/dist/{challengeAssert-Yyyj-EdR.cjs → challengeAssert-BKDS_ADt.cjs} +2 -2
  22. package/dist/{challengeAssert-Yyyj-EdR.cjs.map → challengeAssert-BKDS_ADt.cjs.map} +1 -1
  23. package/dist/{challengeAssert-BzxQmdZy.js → challengeAssert-BXESW00N.js} +7 -7
  24. package/dist/{challengeAssert-BzxQmdZy.js.map → challengeAssert-BXESW00N.js.map} +1 -1
  25. package/dist/fundPeginTransaction-BBE3wTjR.cjs +2 -0
  26. package/dist/{fundPeginTransaction-DaWoYCgO.cjs.map → fundPeginTransaction-BBE3wTjR.cjs.map} +1 -1
  27. package/dist/fundPeginTransaction-t-6TsHAY.js +84 -0
  28. package/dist/{fundPeginTransaction-oV-dNJOU.js.map → fundPeginTransaction-t-6TsHAY.js.map} +1 -1
  29. package/dist/index.cjs +1 -1
  30. package/dist/index.js +174 -166
  31. package/dist/{noPayout-BXeUw0Qq.cjs → noPayout-B6s8vrW6.cjs} +2 -2
  32. package/dist/{noPayout-BXeUw0Qq.cjs.map → noPayout-B6s8vrW6.cjs.map} +1 -1
  33. package/dist/{noPayout-DBX6G96_.js → noPayout-BhgknZBx.js} +2 -2
  34. package/dist/{noPayout-DBX6G96_.js.map → noPayout-BhgknZBx.js.map} +1 -1
  35. package/dist/{peginInput-tbw9BpZy.cjs → peginInput-57FK2O99.cjs} +2 -2
  36. package/dist/{peginInput-tbw9BpZy.cjs.map → peginInput-57FK2O99.cjs.map} +1 -1
  37. package/dist/{peginInput-C2QPvuhR.js → peginInput-CYJzbuwA.js} +3 -3
  38. package/dist/{peginInput-C2QPvuhR.js.map → peginInput-CYJzbuwA.js.map} +1 -1
  39. package/dist/{reservation-CHUGW0F_.js → reservation-CB-4FBPk.js} +37 -36
  40. package/dist/reservation-CB-4FBPk.js.map +1 -0
  41. package/dist/reservation-hjXStM03.cjs +2 -0
  42. package/dist/reservation-hjXStM03.cjs.map +1 -0
  43. package/dist/tbv/core/clients/index.cjs +1 -1
  44. package/dist/tbv/core/clients/index.js +1 -1
  45. package/dist/tbv/core/clients/vault-provider/validators.d.ts.map +1 -1
  46. package/dist/tbv/core/index.cjs +1 -1
  47. package/dist/tbv/core/index.js +172 -164
  48. package/dist/tbv/core/managers/PayoutManager.d.ts.map +1 -1
  49. package/dist/tbv/core/managers/PeginManager.d.ts +7 -8
  50. package/dist/tbv/core/managers/PeginManager.d.ts.map +1 -1
  51. package/dist/tbv/core/managers/index.cjs +1 -1
  52. package/dist/tbv/core/managers/index.js +2 -2
  53. package/dist/tbv/core/primitives/index.cjs +1 -1
  54. package/dist/tbv/core/primitives/index.d.ts +3 -1
  55. package/dist/tbv/core/primitives/index.d.ts.map +1 -1
  56. package/dist/tbv/core/primitives/index.js +31 -27
  57. package/dist/tbv/core/primitives/psbt/__tests__/assertPsbtUnsignedTxMatches.test.d.ts +5 -0
  58. package/dist/tbv/core/primitives/psbt/__tests__/assertPsbtUnsignedTxMatches.test.d.ts.map +1 -0
  59. package/dist/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.d.ts +31 -0
  60. package/dist/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.d.ts.map +1 -0
  61. package/dist/tbv/core/primitives/psbt/index.d.ts +2 -0
  62. package/dist/tbv/core/primitives/psbt/index.d.ts.map +1 -1
  63. package/dist/tbv/core/primitives/utils/bitcoin.d.ts +33 -3
  64. package/dist/tbv/core/primitives/utils/bitcoin.d.ts.map +1 -1
  65. package/dist/tbv/core/primitives/utils/index.d.ts +1 -1
  66. package/dist/tbv/core/primitives/utils/index.d.ts.map +1 -1
  67. package/dist/tbv/core/services/deposit/runDepositorPresignFlow.d.ts.map +1 -1
  68. package/dist/tbv/core/services/deposit/signDepositorGraph.d.ts.map +1 -1
  69. package/dist/tbv/core/services/index.cjs +1 -1
  70. package/dist/tbv/core/services/index.js +2 -2
  71. package/dist/tbv/core/utils/fee/__tests__/peginFeeMath.test.d.ts +19 -0
  72. package/dist/tbv/core/utils/fee/__tests__/peginFeeMath.test.d.ts.map +1 -0
  73. package/dist/tbv/core/utils/fee/index.d.ts +1 -0
  74. package/dist/tbv/core/utils/fee/index.d.ts.map +1 -1
  75. package/dist/tbv/core/utils/fee/peginFeeMath.d.ts +99 -0
  76. package/dist/tbv/core/utils/fee/peginFeeMath.d.ts.map +1 -0
  77. package/dist/tbv/core/utils/index.cjs +1 -1
  78. package/dist/tbv/core/utils/index.js +44 -40
  79. package/dist/tbv/core/utils/transaction/fundPeginTransaction.d.ts.map +1 -1
  80. package/dist/tbv/core/utils/utxo/reservation.d.ts +4 -1
  81. package/dist/tbv/core/utils/utxo/reservation.d.ts.map +1 -1
  82. package/dist/tbv/core/utils/utxo/selectUtxos.d.ts.map +1 -1
  83. package/dist/tbv/index.cjs +1 -1
  84. package/dist/tbv/index.js +172 -164
  85. package/dist/testing/index.cjs +1 -1
  86. package/dist/testing/index.js +1 -1
  87. package/dist/vault-registry-reader-7gOYnrQD.cjs +2 -0
  88. package/dist/vault-registry-reader-7gOYnrQD.cjs.map +1 -0
  89. package/dist/{vault-registry-reader-CrLodprY.js → vault-registry-reader-Blhu9FW2.js} +130 -125
  90. package/dist/vault-registry-reader-Blhu9FW2.js.map +1 -0
  91. package/dist/waitForTransactionReceiptSmartAware-CmgFXFza.js +265 -0
  92. package/dist/waitForTransactionReceiptSmartAware-CmgFXFza.js.map +1 -0
  93. package/dist/waitForTransactionReceiptSmartAware-tv1mtSIY.cjs +2 -0
  94. package/dist/waitForTransactionReceiptSmartAware-tv1mtSIY.cjs.map +1 -0
  95. package/package.json +1 -1
  96. package/dist/PayoutManager-BhJoQZsG.cjs +0 -2
  97. package/dist/PayoutManager-BhJoQZsG.cjs.map +0 -1
  98. package/dist/PayoutManager-s_uH8Uuj.js.map +0 -1
  99. package/dist/bitcoin-B3aqjuMP.cjs +0 -2
  100. package/dist/buildAndBroadcastRefund-C2VqXiOx.js.map +0 -1
  101. package/dist/buildAndBroadcastRefund-CBIfcF47.cjs +0 -2
  102. package/dist/buildAndBroadcastRefund-CBIfcF47.cjs.map +0 -1
  103. package/dist/fundPeginTransaction-DaWoYCgO.cjs +0 -2
  104. package/dist/fundPeginTransaction-oV-dNJOU.js +0 -76
  105. package/dist/payout-BNFMBXS6.js +0 -193
  106. package/dist/payout-BNFMBXS6.js.map +0 -1
  107. package/dist/payout-DQ_fmJUA.cjs +0 -2
  108. package/dist/payout-DQ_fmJUA.cjs.map +0 -1
  109. package/dist/reservation-CHUGW0F_.js.map +0 -1
  110. package/dist/reservation-ho7mjW3X.cjs +0 -2
  111. package/dist/reservation-ho7mjW3X.cjs.map +0 -1
  112. package/dist/vault-registry-reader-CLnhAUN4.cjs +0 -2
  113. package/dist/vault-registry-reader-CLnhAUN4.cjs.map +0 -1
  114. package/dist/vault-registry-reader-CrLodprY.js.map +0 -1
  115. package/dist/waitForTransactionReceiptSmartAware-Cj_DKm0G.js +0 -217
  116. package/dist/waitForTransactionReceiptSmartAware-Cj_DKm0G.js.map +0 -1
  117. package/dist/waitForTransactionReceiptSmartAware-D9ykVriz.cjs +0 -2
  118. package/dist/waitForTransactionReceiptSmartAware-D9ykVriz.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitForTransactionReceiptSmartAware-CmgFXFza.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts","../src/tbv/core/utils/eth/waitForTransactionReceiptSmartAware.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n","/**\n * Smart-account-aware wrapper around viem's `waitForTransactionReceipt`.\n *\n * Externally Owned Accounts (EOAs) — wallets controlled by a single private\n * key, e.g. MetaMask or a hardware wallet. `eth_sendTransaction` returns a real\n * Ethereum tx hash, which viem can poll directly. This wrapper detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\n *\n * Smart-contract accounts (e.g. Safe multisigs) — the wallet address is a\n * deployed contract that decides whether to accept a transaction. WalletConnect's\n * `eth_sendTransaction` returns a `safeTxHash` (an EIP-712 hash of the\n * *proposal*) rather than a real tx hash, and the proposal is held in Safe's\n * off-chain Transaction Service until quorum signs and executes it. We poll\n * that service for the proposal until execution, then wait for receipt on the\n * real Ethereum tx hash exposed in the service's response.\n *\n * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} from \"viem\";\n\n/**\n * Chains where the Safe Transaction Service is supported by this utility.\n * Extend the map as more Safe-enabled chains are needed.\n */\nconst SAFE_TX_SERVICE_BASE_URLS: Record<number, string> = {\n 1: \"https://safe-transaction-mainnet.safe.global\",\n 11155111: \"https://safe-transaction-sepolia.safe.global\",\n};\n\nconst DEFAULT_SAFE_POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_SAFE_POLL_TIMEOUT_MS = 4 * 60 * 60 * 1_000;\nconst SAFE_TX_SERVICE_FETCH_TIMEOUT_MS = 10_000;\n\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\n */\n timeout?: number;\n /** Total budget for waiting on Safe quorum + execution. Default 4h. */\n safePollTimeoutMs?: number;\n /** Poll cadence against the Safe Transaction Service. Default 5s. */\n safePollIntervalMs?: number;\n}\n\nexport async function waitForTransactionReceiptSmartAware(\n params: WaitForTransactionReceiptSmartAwareParams,\n): Promise<TransactionReceipt> {\n const {\n publicClient,\n walletAddress,\n hash,\n confirmations,\n timeout,\n safePollTimeoutMs = DEFAULT_SAFE_POLL_TIMEOUT_MS,\n safePollIntervalMs = DEFAULT_SAFE_POLL_INTERVAL_MS,\n } = params;\n\n const code = await publicClient.getCode({ address: walletAddress });\n const isSmartAccount = code !== undefined && code !== \"0x\";\n\n if (!isSmartAccount) {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n const chainId = await publicClient.getChainId();\n const realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\n const baseUrl = SAFE_TX_SERVICE_BASE_URLS[chainId];\n if (!baseUrl) {\n throw new Error(\n `Safe Transaction Service not configured for chainId ${chainId}. ` +\n `Connected wallet appears to be a smart-contract account, but this ` +\n `chain is not in the supported list. Either connect an EOA or extend ` +\n `SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`,\n );\n }\n\n const url = `${baseUrl}/api/v1/multisig-transactions/${safeTxHash}/`;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const controller = new AbortController();\n const fetchTimeoutId = setTimeout(\n () => controller.abort(),\n SAFE_TX_SERVICE_FETCH_TIMEOUT_MS,\n );\n\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (err) {\n // Transient failure (AbortError on per-request timeout, DNS hiccup,\n // connection reset, etc.). Log and continue to the next poll iteration\n // instead of consuming the entire safePollTimeoutMs budget on one blip.\n // The outer `while (Date.now() < deadline)` is what enforces the overall\n // budget; this catch deliberately preserves it.\n console.warn(\n `Safe Transaction Service request failed (will retry in ${pollIntervalMs}ms): ` +\n (err instanceof Error ? err.message : String(err)),\n );\n await sleep(pollIntervalMs);\n continue;\n } finally {\n clearTimeout(fetchTimeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as SafeMultisigTransaction;\n if (data.isExecuted) {\n if (data.isSuccessful === false) {\n throw new Error(\n `Safe transaction ${safeTxHash} was executed on chain but reverted. ` +\n `Check the Safe queue UI for details.`,\n );\n }\n if (data.transactionHash) {\n return data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\n // Transient server error — same treatment as a hung connection: log and retry.\n console.warn(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}; retrying in ${pollIntervalMs}ms.`,\n );\n } else {\n // Other 4xx (403, 410, etc.) is likely permanent — surface immediately.\n throw new Error(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}.`,\n );\n }\n\n await sleep(pollIntervalMs);\n }\n\n throw new Error(\n `Timed out after ${timeoutMs}ms waiting for Safe transaction ${safeTxHash} ` +\n `to reach quorum and execute. The proposal is still pending in the Safe ` +\n `queue — co-signers must sign and execute it before the dApp can proceed.`,\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","calculateBtcTxHash","txHex","cleanHex","Transaction","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type","SAFE_TX_SERVICE_BASE_URLS","DEFAULT_SAFE_POLL_INTERVAL_MS","DEFAULT_SAFE_POLL_TIMEOUT_MS","SAFE_TX_SERVICE_FETCH_TIMEOUT_MS","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";const m=require("bitcoinjs-lib"),g=require("buffer"),f=require("./fundPeginTransaction-BBE3wTjR.cjs");function p(e){const{numInputs:t,numOutputs:n,feeRate:r}=e;if(!Number.isInteger(t)||t<0)throw new Error(`computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${t}`);if(!Number.isInteger(n)||n<1)throw new Error(`computePeginBaseFeeSats: numOutputs must be a positive integer, got ${n}`);const s=t*f.P2TR_INPUT_SIZE+n*f.MAX_NON_LEGACY_OUTPUT_SIZE+f.TX_BUFFER_SIZE_OVERHEAD;return BigInt(Math.ceil(s*r))+BigInt(f.rateBasedTxBufferFee(r))}function T(e){return BigInt(Math.ceil(f.MAX_NON_LEGACY_OUTPUT_SIZE*e))}function E(e){const{totalInputValue:t,peginAmount:n,baseFee:r,changeOutputFee:s}=e,c=t-n-r;if(c<0n)throw new Error(`applyChangeOutputPolicy: insufficient funds (need ${n+r} sats, have ${t})`);const i=c-s;return i>f.DUST_THRESHOLD?{fee:r+s,changeAmount:i,emitChangeOutput:!0}:{fee:r+c,changeAmount:0n,emitChangeOutput:!1}}function I(e){const{numInputs:t,numOutputs:n,totalBalance:r,feeRate:s}=e;if(r<=0n)return null;const c=p({numInputs:t,numOutputs:n,feeRate:s}),i=r-c;return i>0n?i:0n}function P(e){const t=new Set;for(const n of e){const r=`${n.txid.toLowerCase()}:${n.vout}`;if(t.has(r))throw new Error(`Duplicate UTXO detected: ${n.txid}:${n.vout}. This indicates a data integrity issue with the UTXO source.`);t.add(r)}}function O(e,t,n,r){if(!Number.isInteger(r)||r<1)throw new Error(`Invalid numOutputs: expected a positive integer, got ${r}`);if(e.length===0)throw new Error("Insufficient funds: no UTXOs available");P(e);const s=e.filter(o=>{const a=g.Buffer.from(o.scriptPubKey,"hex");return!!m.script.decompile(a)});if(s.length===0)throw new Error("Insufficient funds: no valid UTXOs available (all have invalid scripts)");const c=[...s].sort((o,a)=>a.value-o.value),i=[];let u=0n,l=0n;for(const o of c){i.push(o),u+=BigInt(o.value);const a=p({numInputs:i.length,numOutputs:r,feeRate:n}),d=T(n);if(u<t+a){l=a;continue}const w=E({totalInputValue:u,peginAmount:t,baseFee:a,changeOutputFee:d});return{selectedUTXOs:i,totalValue:u,fee:w.fee,changeAmount:w.changeAmount}}throw new Error(`Insufficient funds: need ${t+l} sats (${t} pegin + ${l} fee), have ${u} sats`)}function U(e){return e>f.DUST_THRESHOLD}function F(){return f.BTC_DUST_SAT}function v(e){const t=e.startsWith("0x")?e.slice(2):e;return`0x${m.Transaction.fromHex(t).getId()}`}var h=(e=>(e.P2PKH="P2PKH",e.P2SH="P2SH",e.P2WPKH="P2WPKH",e.P2WSH="P2WSH",e.P2TR="P2TR",e.UNKNOWN="UNKNOWN",e))(h||{});function _(e){const t=e.length;return t===25&&e[0]===118&&e[1]===169&&e[2]===20&&e[23]===136&&e[24]===172?"P2PKH":t===23&&e[0]===169&&e[1]===20&&e[22]===135?"P2SH":t===22&&e[0]===0&&e[1]===20?"P2WPKH":t===34&&e[0]===0&&e[1]===32?"P2WSH":t===34&&e[0]===81&&e[1]===32?"P2TR":"UNKNOWN"}function A(e,t){const n=g.Buffer.from(e.scriptPubKey,"hex"),r=_(n);switch(r){case h.P2WPKH:return{witnessUtxo:{script:n,value:e.value}};case h.P2WSH:{if(!e.witnessScript)throw new Error("Missing witnessScript for P2WSH input");return{witnessUtxo:{script:n,value:e.value},witnessScript:g.Buffer.from(e.witnessScript,"hex")}}case h.P2TR:{if(t&&t.length!==32)throw new Error(`Invalid tapInternalKey length: expected 32 bytes, got ${t.length}`);return{witnessUtxo:{script:n,value:e.value},...t&&{tapInternalKey:t}}}default:throw new Error(`Unsupported script type: ${r}`)}}const $={1:"https://safe-transaction-mainnet.safe.global",11155111:"https://safe-transaction-sepolia.safe.global"},x=5e3,H=14400*1e3,C=1e4;async function R(e){const{publicClient:t,walletAddress:n,hash:r,confirmations:s,timeout:c,safePollTimeoutMs:i=H,safePollIntervalMs:u=x}=e,l=await t.getCode({address:n});if(!(l!==void 0&&l!=="0x"))return t.waitForTransactionReceipt({hash:r,confirmations:s,timeout:c});const a=await t.getChainId(),d=await B({chainId:a,safeTxHash:r,pollIntervalMs:u,timeoutMs:i});return t.waitForTransactionReceipt({hash:d,confirmations:s})}async function B({chainId:e,safeTxHash:t,pollIntervalMs:n,timeoutMs:r}){const s=$[e];if(!s)throw new Error(`Safe Transaction Service not configured for chainId ${e}. Connected wallet appears to be a smart-contract account, but this chain is not in the supported list. Either connect an EOA or extend SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`);const c=`${s}/api/v1/multisig-transactions/${t}/`,i=Date.now()+r;for(;Date.now()<i;){const u=new AbortController,l=setTimeout(()=>u.abort(),C);let o;try{o=await fetch(c,{signal:u.signal})}catch(a){console.warn(`Safe Transaction Service request failed (will retry in ${n}ms): `+(a instanceof Error?a.message:String(a))),await S(n);continue}finally{clearTimeout(l)}if(o.ok){const a=await o.json();if(a.isExecuted){if(a.isSuccessful===!1)throw new Error(`Safe transaction ${t} was executed on chain but reverted. Check the Safe queue UI for details.`);if(a.transactionHash)return a.transactionHash}}else if(o.status!==404)if(o.status>=500)console.warn(`Safe Transaction Service returned ${o.status} for ${t}; retrying in ${n}ms.`);else throw new Error(`Safe Transaction Service returned ${o.status} for ${t}.`);await S(n)}throw new Error(`Timed out after ${r}ms waiting for Safe transaction ${t} to reach quorum and execute. The proposal is still pending in the Safe queue — co-signers must sign and execute it before the dApp can proceed.`)}function S(e){return new Promise(t=>{setTimeout(t,e)})}exports.BitcoinScriptType=h;exports.applyChangeOutputPolicy=E;exports.calculateBtcTxHash=v;exports.computeChangeOutputFeeSats=T;exports.computeMaxDeposit=I;exports.computePeginBaseFeeSats=p;exports.getDustThreshold=F;exports.getPsbtInputFields=A;exports.getScriptType=_;exports.selectUtxosForPegin=O;exports.shouldAddChangeOutput=U;exports.waitForTransactionReceiptSmartAware=R;
2
+ //# sourceMappingURL=waitForTransactionReceiptSmartAware-tv1mtSIY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitForTransactionReceiptSmartAware-tv1mtSIY.cjs","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts","../src/tbv/core/utils/eth/waitForTransactionReceiptSmartAware.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n","/**\n * Smart-account-aware wrapper around viem's `waitForTransactionReceipt`.\n *\n * Externally Owned Accounts (EOAs) — wallets controlled by a single private\n * key, e.g. MetaMask or a hardware wallet. `eth_sendTransaction` returns a real\n * Ethereum tx hash, which viem can poll directly. This wrapper detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\n *\n * Smart-contract accounts (e.g. Safe multisigs) — the wallet address is a\n * deployed contract that decides whether to accept a transaction. WalletConnect's\n * `eth_sendTransaction` returns a `safeTxHash` (an EIP-712 hash of the\n * *proposal*) rather than a real tx hash, and the proposal is held in Safe's\n * off-chain Transaction Service until quorum signs and executes it. We poll\n * that service for the proposal until execution, then wait for receipt on the\n * real Ethereum tx hash exposed in the service's response.\n *\n * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} from \"viem\";\n\n/**\n * Chains where the Safe Transaction Service is supported by this utility.\n * Extend the map as more Safe-enabled chains are needed.\n */\nconst SAFE_TX_SERVICE_BASE_URLS: Record<number, string> = {\n 1: \"https://safe-transaction-mainnet.safe.global\",\n 11155111: \"https://safe-transaction-sepolia.safe.global\",\n};\n\nconst DEFAULT_SAFE_POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_SAFE_POLL_TIMEOUT_MS = 4 * 60 * 60 * 1_000;\nconst SAFE_TX_SERVICE_FETCH_TIMEOUT_MS = 10_000;\n\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\n */\n timeout?: number;\n /** Total budget for waiting on Safe quorum + execution. Default 4h. */\n safePollTimeoutMs?: number;\n /** Poll cadence against the Safe Transaction Service. Default 5s. */\n safePollIntervalMs?: number;\n}\n\nexport async function waitForTransactionReceiptSmartAware(\n params: WaitForTransactionReceiptSmartAwareParams,\n): Promise<TransactionReceipt> {\n const {\n publicClient,\n walletAddress,\n hash,\n confirmations,\n timeout,\n safePollTimeoutMs = DEFAULT_SAFE_POLL_TIMEOUT_MS,\n safePollIntervalMs = DEFAULT_SAFE_POLL_INTERVAL_MS,\n } = params;\n\n const code = await publicClient.getCode({ address: walletAddress });\n const isSmartAccount = code !== undefined && code !== \"0x\";\n\n if (!isSmartAccount) {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n const chainId = await publicClient.getChainId();\n const realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\n const baseUrl = SAFE_TX_SERVICE_BASE_URLS[chainId];\n if (!baseUrl) {\n throw new Error(\n `Safe Transaction Service not configured for chainId ${chainId}. ` +\n `Connected wallet appears to be a smart-contract account, but this ` +\n `chain is not in the supported list. Either connect an EOA or extend ` +\n `SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`,\n );\n }\n\n const url = `${baseUrl}/api/v1/multisig-transactions/${safeTxHash}/`;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const controller = new AbortController();\n const fetchTimeoutId = setTimeout(\n () => controller.abort(),\n SAFE_TX_SERVICE_FETCH_TIMEOUT_MS,\n );\n\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (err) {\n // Transient failure (AbortError on per-request timeout, DNS hiccup,\n // connection reset, etc.). Log and continue to the next poll iteration\n // instead of consuming the entire safePollTimeoutMs budget on one blip.\n // The outer `while (Date.now() < deadline)` is what enforces the overall\n // budget; this catch deliberately preserves it.\n console.warn(\n `Safe Transaction Service request failed (will retry in ${pollIntervalMs}ms): ` +\n (err instanceof Error ? err.message : String(err)),\n );\n await sleep(pollIntervalMs);\n continue;\n } finally {\n clearTimeout(fetchTimeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as SafeMultisigTransaction;\n if (data.isExecuted) {\n if (data.isSuccessful === false) {\n throw new Error(\n `Safe transaction ${safeTxHash} was executed on chain but reverted. ` +\n `Check the Safe queue UI for details.`,\n );\n }\n if (data.transactionHash) {\n return data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\n // Transient server error — same treatment as a hung connection: log and retry.\n console.warn(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}; retrying in ${pollIntervalMs}ms.`,\n );\n } else {\n // Other 4xx (403, 410, etc.) is likely permanent — surface immediately.\n throw new Error(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}.`,\n );\n }\n\n await sleep(pollIntervalMs);\n }\n\n throw new Error(\n `Timed out after ${timeoutMs}ms waiting for Safe transaction ${safeTxHash} ` +\n `to reach quorum and execute. The proposal is still pending in the Safe ` +\n `queue — co-signers must sign and execute it before the dApp can proceed.`,\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","calculateBtcTxHash","txHex","cleanHex","Transaction","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type","SAFE_TX_SERVICE_BASE_URLS","DEFAULT_SAFE_POLL_INTERVAL_MS","DEFAULT_SAFE_POLL_TIMEOUT_MS","SAFE_TX_SERVICE_FETCH_TIMEOUT_MS","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonlabs-io/ts-sdk",
3
- "version": "0.36.2",
3
+ "version": "0.37.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,2 +0,0 @@
1
- "use strict";var k=Object.defineProperty;var w=(e,t,o)=>t in e?k(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o;var P=(e,t,o)=>w(e,typeof t!="symbol"?t+"":t,o);const f=require("./sha2-DsrLC4NM.cjs"),h=require("./signing-Bnsro0hE.cjs");require("@babylonlabs-io/babylon-tbv-rust-wasm");const r=require("./payout-DQ_fmJUA.cjs"),b=require("./bitcoin-B3aqjuMP.cjs"),p=66;function x(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 o=new Uint8Array(t.length/2);for(let n=0;n<o.length;n++)o[n]=parseInt(t.slice(n*2,n*2+2),16);return o}function H(e){return`0x${Array.from(e).map(t=>t.toString(16).padStart(2,"0")).join("")}`}function g(e,t){if(e.length!==p)throw new Error(`${t} must be exactly 32 bytes (${p} hex chars with 0x prefix), got ${e.length}`)}function d(e){g(e,"Secret");const t=x(e),o=f.sha256(t);return H(o)}function B(e,t){return g(e,"Secret"),g(t,"Hashlock"),x(t),d(e).toLowerCase()===t.toLowerCase()}class v{constructor(t){P(this,"config");this.config=t}async signPayoutTransaction(t){this.validatePayoutOutputs(t.payoutTxHex,t.registeredPayoutScriptPubKey);const o=await this.config.btcWallet.getPublicKeyHex(),{depositorPubkey:n}=b.validateWalletPubkey(o,t.depositorBtcPubkey),a=await r.buildPayoutPsbt({payoutTxHex:t.payoutTxHex,peginTxHex:t.peginTxHex,assertTxHex:t.assertTxHex,depositorBtcPubkey:n,vaultProviderBtcPubkey:t.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:t.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:t.universalChallengerBtcPubkeys,timelockPegin:t.timelockPegin,network:this.config.network}),u=await this.config.btcWallet.signPsbt(a.psbtHex,h.createTaprootScriptPathSignOptions(o,1));return{signature:r.extractPayoutSignature(u,n),depositorBtcPubkey:n}}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 o=await this.config.btcWallet.getPublicKeyHex(),n=[],a=[],u=[];for(const s of t){this.validatePayoutOutputs(s.payoutTxHex,s.registeredPayoutScriptPubKey);const{depositorPubkey:i}=b.validateWalletPubkey(o,s.depositorBtcPubkey);u.push(i);const l=await r.buildPayoutPsbt({payoutTxHex:s.payoutTxHex,peginTxHex:s.peginTxHex,assertTxHex:s.assertTxHex,depositorBtcPubkey:i,vaultProviderBtcPubkey:s.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:s.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:s.universalChallengerBtcPubkeys,timelockPegin:s.timelockPegin,network:this.config.network});n.push(l.psbtHex),a.push(h.createTaprootScriptPathSignOptions(o,1))}const c=await this.config.btcWallet.signPsbts(n,a);if(c.length!==t.length)throw new Error(`Expected ${t.length} signed PSBTs but received ${c.length}`);const y=[];for(let s=0;s<t.length;s++){const i=u[s],l=r.extractPayoutSignature(c[s],i);y.push({payoutSignature:l,depositorBtcPubkey:i})}return y}validatePayoutOutputs(t,o){r.assertPayoutOutputMatchesRegistered(t,o)}}exports.PayoutManager=v;exports.computeHashlock=d;exports.validateSecretAgainstHashlock=B;
2
- //# sourceMappingURL=PayoutManager-BhJoQZsG.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PayoutManager-BhJoQZsG.cjs","sources":["../src/tbv/core/services/htlc/index.ts","../src/tbv/core/managers/PayoutManager.ts"],"sourcesContent":["/**\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","/**\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 {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../shared/wallets\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\nimport {\n assertPayoutOutputMatchesRegistered,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\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 * 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\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/**\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 payout TX outputs pay to the registered depositor payout address\n this.validatePayoutOutputs(\n params.payoutTxHex,\n params.registeredPayoutScriptPubKey,\n );\n\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)\n const payoutPsbt = await buildPayoutPsbt({\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 network: this.config.network,\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 // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\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(\n transactions: SignPayoutParams[],\n ): 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 payout TX outputs pay to the registered depositor payout address\n this.validatePayoutOutputs(\n tx.payoutTxHex,\n tx.registeredPayoutScriptPubKey,\n );\n\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\n const payoutPsbt = await buildPayoutPsbt({\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 network: this.config.network,\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 const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n\n /**\n * Validates that the payout transaction's largest output pays to the\n * registered depositor payout address (scriptPubKey).\n *\n * This prevents two attack vectors from a malicious vault provider:\n * 1. Substituting a completely different payout address\n * 2. Including a dust output to the correct address while routing\n * the actual funds to an attacker-controlled address\n *\n * @param payoutTxHex - Raw payout transaction hex\n * @param registeredPayoutScriptPubKey - On-chain registered scriptPubKey (hex, with or without 0x prefix)\n * @throws Error if scriptPubKey is invalid hex\n * @throws Error if the largest output does not pay to the registered address\n */\n private validatePayoutOutputs(\n payoutTxHex: string,\n registeredPayoutScriptPubKey: string,\n ): void {\n assertPayoutOutputMatchesRegistered(\n payoutTxHex,\n registeredPayoutScriptPubKey,\n );\n }\n}\n"],"names":["HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","i","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","extractPayoutSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","tx","signedPsbts","results","payoutSignature","payoutTxHex","registeredPayoutScriptPubKey","assertPayoutOutputMatchesRegistered"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PayoutManager-s_uH8Uuj.js","sources":["../src/tbv/core/services/htlc/index.ts","../src/tbv/core/managers/PayoutManager.ts"],"sourcesContent":["/**\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","/**\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 {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../shared/wallets\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\nimport {\n assertPayoutOutputMatchesRegistered,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\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 * 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\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/**\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 payout TX outputs pay to the registered depositor payout address\n this.validatePayoutOutputs(\n params.payoutTxHex,\n params.registeredPayoutScriptPubKey,\n );\n\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)\n const payoutPsbt = await buildPayoutPsbt({\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 network: this.config.network,\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 // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\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(\n transactions: SignPayoutParams[],\n ): 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 payout TX outputs pay to the registered depositor payout address\n this.validatePayoutOutputs(\n tx.payoutTxHex,\n tx.registeredPayoutScriptPubKey,\n );\n\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\n const payoutPsbt = await buildPayoutPsbt({\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 network: this.config.network,\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 const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n\n /**\n * Validates that the payout transaction's largest output pays to the\n * registered depositor payout address (scriptPubKey).\n *\n * This prevents two attack vectors from a malicious vault provider:\n * 1. Substituting a completely different payout address\n * 2. Including a dust output to the correct address while routing\n * the actual funds to an attacker-controlled address\n *\n * @param payoutTxHex - Raw payout transaction hex\n * @param registeredPayoutScriptPubKey - On-chain registered scriptPubKey (hex, with or without 0x prefix)\n * @throws Error if scriptPubKey is invalid hex\n * @throws Error if the largest output does not pay to the registered address\n */\n private validatePayoutOutputs(\n payoutTxHex: string,\n registeredPayoutScriptPubKey: string,\n ): void {\n assertPayoutOutputMatchesRegistered(\n payoutTxHex,\n registeredPayoutScriptPubKey,\n );\n }\n}\n"],"names":["HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","i","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","extractPayoutSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","tx","signedPsbts","results","payoutSignature","payoutTxHex","registeredPayoutScriptPubKey","assertPayoutOutputMatchesRegistered"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use strict";const l=require("buffer"),i=require("bitcoinjs-lib"),b=192,s=64,c=66,y=130,k=128;function o(e){return e.startsWith("0x")||e.startsWith("0X")?e.slice(2):e}function x(e){return e.startsWith("0x")?e:e.startsWith("0X")?`0x${e.slice(2)}`:`0x${e}`}function a(e){const t=o(e);if(!d(t))throw new Error(`Invalid hex string: ${e}`);const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=parseInt(t.slice(r,r+2),16);return n}function f(e){return Array.from(e).map(t=>t.toString(16).padStart(2,"0")).join("")}function H(e){return f(new Uint8Array(e.hash).slice().reverse())}function w(e){return e.length===32?e:e.slice(1,33)}function d(e){return/^[0-9a-fA-F]*$/.test(e)&&e.length%2===0}function E(e){const t=o(e);if(!d(t))throw new Error(`Invalid hex characters in public key: ${e}`);if(t.length===s)return t;if(t.length!==c&&t.length!==y)throw new Error(`Invalid public key length: ${t.length} (expected ${s}, ${c}, or ${y} hex chars)`);const n=a(t);return f(w(n))}function v(e){const t=o(e);return d(t)}function m(e,t){if(!t)throw new Error("validateWalletPubkey requires expectedDepositorPubkey. Pass the on-chain registered depositor pubkey to avoid a self-comparison.");const n=E(e),r=t;if(n.toLowerCase()!==r.toLowerCase())throw new Error(`Wallet public key does not match vault depositor. Expected: ${r}, Got: ${n}. Please connect the wallet that was used to create this vault.`);return{walletPubkeyRaw:e,walletPubkeyXOnly:n,depositorPubkey:r}}const p=100000000n;function g(e){if(e<0n)return`-${g(-e)}`;const t=e/p;let r=(e%p).toString().padStart(8,"0");return r=r.replace(/0+$/,""),r.length>0?`${t}.${r}`:t.toString()}function A(){try{i.payments.p2tr({internalPubkey:l.Buffer.alloc(32,1)})}catch(e){if(e instanceof Error&&e.message.includes("No ECC Library provided"))throw new Error('ECC library not initialized. You must call initEccLib(ecc) from "bitcoinjs-lib" before using the SDK. See the ts-sdk README for setup instructions.')}}function h(e){switch(e){case"bitcoin":return i.networks.bitcoin;case"testnet":case"signet":return i.networks.testnet;case"regtest":return i.networks.regtest;default:throw new Error(`Unknown network: ${e}`)}}function S(e,t){A();const n=a(E(e)),{address:r}=i.payments.p2tr({internalPubkey:l.Buffer.from(n),network:h(t)});if(!r)throw new Error("Failed to derive taproot address from public key");return r}function P(e,t){const n=o(e);if(n.length!==66)throw new Error(`Native SegWit requires a compressed public key (66 hex chars), got ${n.length}`);const{address:r}=i.payments.p2wpkh({pubkey:l.Buffer.from(a(n)),network:h(t)});if(!r)throw new Error("Failed to derive native segwit address from public key");return r}function N(e,t,n){const r=o(t);try{if(e===S(r,n))return!0}catch{}const u=[];r.length===c?u.push(r):r.length===s&&u.push(`02${r}`,`03${r}`);for(const _ of u)try{if(e===P(_,n))return!0}catch{}return!1}exports.COMPRESSED_PUBKEY_HEX_LEN=c;exports.SCHNORR_SIG_HEX_LEN=k;exports.TAPSCRIPT_LEAF_VERSION=b;exports.X_ONLY_PUBKEY_HEX_LEN=s;exports.deriveNativeSegwitAddress=P;exports.deriveTaprootAddress=S;exports.ensureHexPrefix=x;exports.formatSatoshisToBtc=g;exports.getNetwork=h;exports.hexToUint8Array=a;exports.inputTxidHex=H;exports.isAddressFromPublicKey=N;exports.isValidHex=v;exports.processPublicKeyToXOnly=E;exports.stripHexPrefix=o;exports.toXOnly=w;exports.uint8ArrayToHex=f;exports.validateWalletPubkey=m;
2
- //# sourceMappingURL=bitcoin-B3aqjuMP.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buildAndBroadcastRefund-C2VqXiOx.js","sources":["../src/tbv/core/services/activation/activateVault.ts","../src/tbv/core/services/deposit/waitForPeginStatus.ts","../src/tbv/core/services/deposit/submitWotsPublicKey.ts","../src/tbv/core/services/deposit/signDepositorGraph.ts","../src/tbv/core/services/deposit/runDepositorPresignFlow.ts","../src/tbv/core/services/deposit/validation.ts","../src/tbv/core/services/deposit/validateOnChainParticipantKeys.ts","../src/tbv/core/services/deposit/verifyRegisteredVaultVersions.ts","../src/tbv/core/services/pegout/state.ts","../src/tbv/core/services/refund/errors.ts","../src/tbv/core/services/refund/buildAndBroadcastRefund.ts"],"sourcesContent":["/**\n * Vault activation — reveal HTLC secret on Ethereum to move the vault from\n * Verified to Active. The on-chain contract validates `sha256(s) == hashlock`\n * and the activation deadline; this function pre-validates inputs (including\n * an optional hashlock check) and delegates the actual contract write to an\n * injected callback so the SDK stays transport-agnostic.\n *\n * @module services/activation\n */\n\nimport type { Abi, Address, Hash, Hex } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { ensureHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport { validateSecretAgainstHashlock } from \"../htlc\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\nconst ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;\n// ETH calldata convention: 0x prefix REQUIRED, even number of hex chars, may\n// be empty (\"0x\"). Named distinctly from the BTC-hex regex in\n// buildAndBroadcastRefund.ts (which allows an optional prefix and requires\n// non-empty) to make the convention explicit at the call site.\nconst ETH_HEX_BYTES_RE = /^0x([0-9a-fA-F]{2})*$/;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\nfunction assertAddress(value: string, label: string): void {\n if (!ADDRESS_HEX_RE.test(value)) {\n throw new Error(\n `${label} must be a 20-byte 0x-prefixed hex address (42 chars)`,\n );\n }\n}\n\nfunction assertHexBytes(value: string, label: string): void {\n if (!ETH_HEX_BYTES_RE.test(value)) {\n throw new Error(\n `${label} must be a 0x-prefixed hex string with an even number of hex chars`,\n );\n }\n}\n\n/**\n * A single ETH contract-write call. The SDK assembles these; the caller\n * executes them via viem, wagmi, a wallet provider, or any other transport.\n */\nexport interface EthContractWriteCall {\n address: Address;\n abi: Abi;\n functionName: string;\n args: readonly unknown[];\n}\n\n/**\n * Minimum shape the SDK requires from any contract-write result. Callers may\n * return richer objects (e.g. including the receipt) — the SDK propagates\n * them unchanged via the generic parameter on {@link EthContractWriter}.\n */\nexport interface EthContractWriteResult {\n transactionHash: Hash;\n}\n\n/**\n * Caller-provided contract writer. The generic `R` lets callers return any\n * transport-specific result shape (e.g. `{ transactionHash, receipt }`);\n * the SDK forwards that shape back through `activateVault`.\n */\nexport type EthContractWriter<R extends EthContractWriteResult = EthContractWriteResult> = (\n call: EthContractWriteCall,\n) => Promise<R>;\n\nexport interface ActivateVaultInput<\n R extends EthContractWriteResult = EthContractWriteResult,\n> {\n /** BTCVaultRegistry contract address (env-specific). */\n btcVaultRegistryAddress: Address;\n /** Vault ID (bytes32, 0x-prefixed). */\n vaultId: Hex;\n /**\n * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n * `0X` prefix is normalised before validation.\n */\n secret: string;\n /**\n * Optional hashlock for client-side pre-validation. When provided, the SDK\n * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n */\n hashlock?: Hex;\n /**\n * Activation metadata passed through to the contract. Required to keep\n * the \"empty metadata\" convention explicit at the call site — pass `\"0x\"`\n * (empty bytes) when no metadata is needed. Must be a 0x-prefixed hex\n * string with an even number of hex chars.\n */\n activationMetadata: Hex;\n /** Caller-provided write callback — see {@link EthContractWriter}. */\n writeContract: EthContractWriter<R>;\n /**\n * Optional abort signal. Checked before validation runs; since validation\n * is fully synchronous, cancellation between validation and the write is\n * not observable and callers should rely on the transport's own\n * cancellation support for that window.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Reveal the HTLC secret on Ethereum and activate the vault.\n *\n * Validates inputs, optionally pre-checks the secret against the expected\n * hashlock, and delegates the contract write to `writeContract`. Returns\n * whatever the writer returns so callers can keep richer transport-specific\n * metadata (e.g. viem receipts) end-to-end.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n * or if `sha256(secret) != hashlock`\n * @throws `Error` if `activationMetadata` is not a 0x-prefixed hex byte\n * string (must have an even number of hex chars). Pass `\"0x\"` for\n * empty metadata.\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVault<\n R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultInput<R>): Promise<R> {\n const {\n btcVaultRegistryAddress,\n vaultId,\n hashlock,\n activationMetadata,\n writeContract,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n\n assertAddress(btcVaultRegistryAddress, \"btcVaultRegistryAddress\");\n assertBytes32(vaultId, \"vaultId\");\n\n const normalizedSecret = ensureHexPrefix(input.secret);\n assertBytes32(normalizedSecret, \"secret\");\n\n if (hashlock !== undefined) {\n assertBytes32(hashlock, \"hashlock\");\n if (!validateSecretAgainstHashlock(normalizedSecret, hashlock)) {\n throw new Error(\n \"Invalid secret: SHA256(secret) does not match the provided hashlock\",\n );\n }\n }\n\n assertHexBytes(activationMetadata, \"activationMetadata\");\n\n return writeContract({\n address: btcVaultRegistryAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"activateVaultWithSecret\",\n args: [vaultId, normalizedSecret, activationMetadata],\n });\n}\n","/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * Pure polling utility with no framework dependencies (no localStorage, no React).\n * Handles \"PegIn not found\" as transient (VP hasn't ingested yet).\n */\n\nimport { JsonRpcError } from \"../../clients/vault-provider/json-rpc-client\";\nimport {\n RpcErrorCode,\n VP_TERMINAL_STATUSES,\n type DaemonStatus,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader } from \"./interfaces\";\n\n/** Default polling interval (10 seconds). */\nconst DEFAULT_POLL_INTERVAL_MS = 10_000;\n\nexport interface WaitForPeginStatusParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Set of acceptable statuses — polling stops when the VP reports one of these */\n targetStatuses: ReadonlySet<DaemonStatus>;\n /** Maximum time to wait in milliseconds */\n timeoutMs: number;\n /** Polling interval in milliseconds (default: 10s) */\n pollIntervalMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * @returns The DaemonStatus string that matched one of the targets\n * @throws Error on timeout, abort, or non-transient RPC error\n */\nexport async function waitForPeginStatus(\n params: WaitForPeginStatusParams,\n): Promise<DaemonStatus> {\n const {\n statusReader,\n peginTxid,\n targetStatuses,\n timeoutMs,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n signal,\n } = params;\n\n const startTime = Date.now();\n\n while (true) {\n if (signal?.aborted) {\n throw new Error(\n `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n throw new Error(\n `Polling timeout after ${timeoutMs}ms for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n try {\n const response = await statusReader.getPeginStatus(\n { pegin_txid: peginTxid },\n signal,\n );\n\n // Reject responses echoing a different pegin txid.\n if (response.pegin_txid.toLowerCase() !== peginTxid.toLowerCase()) {\n throw new Error(\n `getPeginStatus returned status for pegin ${response.pegin_txid.slice(0, 8)}…, requested ${peginTxid.slice(0, 8)}…`,\n );\n }\n\n const status = response.status as DaemonStatus;\n if (targetStatuses.has(status)) {\n return status;\n }\n // Fail fast on terminal statuses to avoid waiting for timeout\n if (VP_TERMINAL_STATUSES.has(status) && !targetStatuses.has(status)) {\n throw new Error(\n `Pegin ${peginTxid.slice(0, 8)}… reached terminal status \"${status}\" while waiting for ${[...targetStatuses].join(\", \")}`,\n );\n }\n } catch (error) {\n // \"PegIn not found\" is transient — VP hasn't ingested the pegin yet.\n // Check structured error code first, fall back to message matching.\n const isNotFound =\n (error instanceof JsonRpcError &&\n error.code === RpcErrorCode.NOT_FOUND) ||\n (error instanceof Error && error.message.includes(\"PegIn not found\"));\n if (!isNotFound) {\n throw error;\n }\n }\n\n // Wait before next poll, with abort support\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(\n new Error(\n `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n ),\n );\n };\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, pollIntervalMs);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","/**\n * Submit pre-derived WOTS public keys to the vault provider.\n *\n * Polls `getPeginStatus` until the VP reaches `PendingDepositorWotsPK`,\n * then submits the keys. If the VP has already moved past WOTS step\n * (e.g., resume flow), submission is skipped.\n *\n * The caller is responsible for deriving WOTS keys externally using\n * `expandWotsSeed` + `deriveWotsBlocksFromSeed` from the SDK's\n * `tbv/core/vault-secrets` and `tbv/core/wots` modules respectively.\n */\n\nimport {\n DaemonStatus,\n POST_WOTS_STATUSES,\n type WotsBlockPublicKey,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader, WotsKeySubmitter } from \"./interfaces\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n/** Maximum time to wait for VP to reach PendingDepositorWotsPK (5 min). */\nconst STATUS_POLL_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** All statuses we accept — either ready for submission or already past it. */\nconst TARGET_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_WOTS_PK,\n ...POST_WOTS_STATUSES,\n]);\n\nexport interface SubmitWotsPublicKeyParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the WOTS key submission interface */\n wotsSubmitter: WotsKeySubmitter;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Pre-derived WOTS block public keys (one per assert block) */\n wotsPublicKeys: WotsBlockPublicKey[];\n /** Maximum time to wait for VP to be ready (default: 5 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Submit WOTS public keys to the vault provider.\n *\n * @throws Error on timeout, abort, or RPC error\n */\nexport async function submitWotsPublicKey(\n params: SubmitWotsPublicKeyParams,\n): Promise<void> {\n const {\n statusReader,\n wotsSubmitter,\n peginTxid,\n depositorPk,\n wotsPublicKeys,\n timeoutMs = STATUS_POLL_TIMEOUT_MS,\n signal,\n } = params;\n\n signal?.throwIfAborted();\n\n // Wait until VP has ingested the pegin and is ready for the WOTS key.\n const status = await waitForPeginStatus({\n statusReader,\n peginTxid,\n targetStatuses: TARGET_STATUSES,\n timeoutMs,\n signal,\n });\n\n // Key was already submitted in a previous session (e.g. resume flow)\n if (POST_WOTS_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n await wotsSubmitter.submitDepositorWotsKey(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n wots_public_keys: wotsPublicKeys,\n },\n signal,\n );\n}\n","/**\n * Depositor Graph Signing Service\n *\n * Signs the depositor's own graph transactions (Payout, NoPayout per challenger)\n * for the depositor-as-claimer flow.\n *\n * Both PSBTs are constructed locally from authoritative on-chain connector\n * parameters and the VP-advertised transaction hexes (which are themselves\n * cross-checked against on-chain or protocol-defined sinks). Building PSBTs\n * locally is essential: every field that enters the Taproot sighash\n * (witnessUtxo, tapLeafScript, controlBlock, tapInternalKey) must come from\n * trusted sources, otherwise a malicious VP could substitute metadata that\n * makes the depositor's signature valid for a different spend.\n *\n * Transaction counts: 1 Payout + N NoPayout = 1 + N total PSBTs.\n *\n * @see btc-vault docs/pegin.md - \"Automatic Graph Creation & Presigning\"\n * @see btc-vault crates/vault/src/transactions/nopayout.rs - NoPayout structure\n */\n\nimport { type Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../../shared/wallets/interfaces\";\nimport type {\n DepositorAsClaimerPresignatures,\n DepositorGraphTransactions,\n DepositorPreSigsPerChallenger,\n PresignDataPerChallenger,\n} from \"../../clients/vault-provider/types\";\nimport {\n assertNoPayoutOutputMatchesChallenger,\n buildNoPayoutPsbt,\n} from \"../../primitives/psbt/noPayout\";\nimport {\n assertPayoutOutputMatchesRegistered,\n buildPayoutPsbt,\n extractPayoutSignature,\n} from \"../../primitives/psbt/payout\";\nimport {\n stripHexPrefix,\n uint8ArrayToHex,\n validateWalletPubkey,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\n/**\n * The depositor signs exactly one input (index 0) per payout/nopayout PSBT.\n * Used to construct SignPsbtOptions for wallet.signPsbt(). PSBTs may carry\n * additional inputs (the payout PSBT includes the assert prevout; the nopayout\n * PSBT includes the two ChallengeAssert prevouts) so the Taproot SIGHASH_DEFAULT\n * sighash commits to all prevouts, but those inputs are not signed by the\n * depositor.\n */\nconst DEPOSITOR_SIGNED_INPUT_COUNT = 1;\n\n/** Tracks which indices in the flat PSBT array belong to which challenger */\ninterface ChallengerEntry {\n challengerPubkey: string;\n noPayoutIdx: number;\n}\n\n/** Result of the collect phase - flat PSBT array with index mapping */\ninterface CollectedDepositorGraphPsbts {\n psbtHexes: string[];\n signOptions: SignPsbtOptions[];\n challengerEntries: ChallengerEntry[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Compute the local-challenger set for the depositor-as-claimer flow.\n *\n * Per the protocol (see btc-vault crates/vault/src/lib.rs - `LocalChallengers`):\n * localChallengers = {VaultProvider, VaultKeepers} \\ {Claimer}\n *\n * For the depositor-as-claimer flow the claimer is the depositor, so this\n * removes the depositor from the union if present (it usually isn't).\n * The protocol guarantees the result is non-empty by construction.\n */\nfunction deriveLocalChallengers(\n vaultProviderBtcPubkey: string,\n vaultKeeperBtcPubkeys: string[],\n depositorBtcPubkey: string,\n): string[] {\n const depositor = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n const all = [vaultProviderBtcPubkey, ...vaultKeeperBtcPubkeys].map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n const filtered = all.filter((k) => k !== depositor);\n if (filtered.length === 0) {\n throw new Error(\n \"Cannot derive localChallengers: removing depositor from {vaultProvider, vaultKeepers} left an empty set\",\n );\n }\n // Catch upstream misconfiguration (VP key === a VK key) at its source.\n // Otherwise downstream set comparisons would silently dedupe and report\n // a misleading \"missing challenger\" error.\n if (new Set(filtered).size !== filtered.length) {\n throw new Error(\n \"Cannot derive localChallengers: vaultProvider key duplicates a vaultKeeper key — signing context is misconfigured\",\n );\n }\n return filtered;\n}\n\n/**\n * Reject VP-supplied `challenger_presign_data` whose pubkey set does not\n * exactly equal `localChallengers`.\n *\n * Threat model: a malicious or buggy VP could omit, duplicate, or inject\n * unrelated entries. Missing entries → depositor activates with incomplete\n * recovery material (omitted challenger later becomes unenforceable).\n * Duplicates or extras → wallet signs PSBTs for challengers the protocol\n * doesn't recognize, handing the VP signatures it shouldn't have.\n */\nfunction assertChallengerSetMatchesLocalChallengers(\n challengerPresignData: PresignDataPerChallenger[],\n localChallengers: string[],\n): void {\n const suppliedList = challengerPresignData.map((c) =>\n stripHexPrefix(c.challenger_pubkey).toLowerCase(),\n );\n const suppliedSet = new Set(suppliedList);\n if (suppliedSet.size !== suppliedList.length) {\n throw new Error(\n \"Depositor graph contains duplicate challenger entries in challenger_presign_data\",\n );\n }\n const expectedSet = new Set(localChallengers);\n const missing = localChallengers.filter((c) => !suppliedSet.has(c));\n const extra = suppliedList.filter((c) => !expectedSet.has(c));\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `Depositor graph challenger set does not match localChallengers` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n );\n }\n}\n\n/**\n * Read the txid that the given input references in the unsigned tx, in display\n * (big-endian) hex order. bitcoinjs-lib stores `input.hash` in internal\n * little-endian byte order, which is the reverse of how txids are normally\n * displayed.\n */\nfunction readInputTxid(tx: Transaction, inputIndex: number): string {\n const input = tx.ins[inputIndex];\n return uint8ArrayToHex(new Uint8Array(input.hash).slice().reverse());\n}\n\n/**\n * Verify the noPayout transaction's input at `inputIndex` references the\n * given parent transaction at vout 0 (per nopayout.rs the layout is fixed:\n * Assert:0, ChallengeAssertX:0, ChallengeAssertY:0).\n */\nfunction assertInputReferencesParent(\n noPayoutTx: Transaction,\n inputIndex: number,\n parentTx: Transaction,\n parentLabel: string,\n challengerPubkey: string,\n): void {\n const input = noPayoutTx.ins[inputIndex];\n if (input.index !== 0) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} expected to spend ${parentLabel} vout 0, got vout ${input.index}`,\n );\n }\n const parentTxid = parentTx.getId();\n const inputTxid = readInputTxid(noPayoutTx, inputIndex);\n if (inputTxid !== parentTxid) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} does not reference ${parentLabel} (expected txid ${parentTxid}, got ${inputTxid})`,\n );\n }\n}\n\n// ============================================================================\n// Collect phase\n// ============================================================================\n\n/**\n * Build the depositor's payout PSBT and per-challenger NoPayout PSBTs locally\n * from authoritative connector params.\n *\n * Layout of returned arrays: [Payout, NoPayout_0, NoPayout_1, ...]\n */\nasync function collectDepositorGraphPsbts(\n depositorGraph: DepositorGraphTransactions,\n walletPublicKey: string,\n ctx: DepositorGraphSigningContext,\n): Promise<CollectedDepositorGraphPsbts> {\n const psbtHexes: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const challengerEntries: ChallengerEntry[] = [];\n\n // 1. Fail-fast on a malformed VP response BEFORE doing any PSBT-build\n // work that would be wasted if the challenger set is wrong.\n const localChallengers = deriveLocalChallengers(\n ctx.vaultProviderBtcPubkey,\n ctx.vaultKeeperBtcPubkeys,\n ctx.depositorBtcPubkey,\n );\n assertChallengerSetMatchesLocalChallengers(\n depositorGraph.challenger_presign_data,\n localChallengers,\n );\n\n // 2. Validate the payout transaction's largest output pays to the\n // depositor's on-chain registered payout scriptPubKey. The payout tx\n // hex is supplied by the VP and otherwise unconstrained; this assertion\n // pins the destination of the funds.\n assertPayoutOutputMatchesRegistered(\n depositorGraph.payout_tx.tx_hex,\n ctx.registeredPayoutScriptPubKey,\n );\n\n // 3. Build the payout PSBT locally. Every sighash-relevant field\n // (witnessUtxo, tapLeafScript, controlBlock, tapInternalKey) is derived\n // from on-chain trusted connector params, not from the VP. The VP-\n // supplied assert tx hex is implicitly pinned by buildPayoutPsbt's\n // input-1 txid check against payoutTx.ins[1].hash.\n const builtPayout = await buildPayoutPsbt({\n payoutTxHex: depositorGraph.payout_tx.tx_hex,\n peginTxHex: ctx.peginTxHex,\n assertTxHex: depositorGraph.assert_tx.tx_hex,\n depositorBtcPubkey: ctx.depositorBtcPubkey,\n vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: ctx.universalChallengerBtcPubkeys,\n timelockPegin: ctx.timelockPegin,\n network: ctx.network,\n });\n psbtHexes.push(builtPayout.psbtHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n // 4. Per-challenger: build the NoPayout PSBT locally too.\n const claimerPubkey = stripHexPrefix(ctx.depositorBtcPubkey);\n const assertTxParsed = Transaction.fromHex(\n stripHexPrefix(depositorGraph.assert_tx.tx_hex),\n );\n\n for (const challenger of depositorGraph.challenger_presign_data) {\n const challengerPubkey = stripHexPrefix(challenger.challenger_pubkey);\n\n const noPayoutIdx = psbtHexes.length;\n const noPayoutHex = await buildLocalNoPayoutPsbt({\n challenger,\n challengerPubkey,\n claimerPubkey,\n localChallengers,\n assertTxParsed,\n ctx,\n });\n psbtHexes.push(noPayoutHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n challengerEntries.push({\n challengerPubkey,\n noPayoutIdx,\n });\n }\n\n return { psbtHexes, signOptions, challengerEntries };\n}\n\ninterface BuildLocalNoPayoutPsbtParams {\n challenger: PresignDataPerChallenger;\n challengerPubkey: string;\n claimerPubkey: string;\n localChallengers: string[];\n assertTxParsed: Transaction;\n ctx: DepositorGraphSigningContext;\n}\n\n/**\n * Build a single NoPayout PSBT for one challenger from authoritative\n * inputs. Validates the VP-supplied parent transactions match what the\n * NoPayout transaction commits to via input txids, and asserts the output\n * pays to the protocol-defined challenger sink before returning.\n *\n * NoPayout transaction layout (per\n * btc-vault crates/vault/src/transactions/nopayout.rs):\n * - 3 inputs (fixed order):\n * - Input 0: Assert tx output 0 (depositor signs - NoPayout path)\n * - Input 1: ChallengeAssertX tx output 0 (with timelock)\n * - Input 2: ChallengeAssertY tx output 0 (with timelock)\n * - 1 output: BIP-86 P2TR to the challenger\n */\nasync function buildLocalNoPayoutPsbt(\n params: BuildLocalNoPayoutPsbtParams,\n): Promise<string> {\n const {\n challenger,\n challengerPubkey,\n claimerPubkey,\n localChallengers,\n assertTxParsed,\n ctx,\n } = params;\n\n // Pin the output sink before doing any sighash-relevant work.\n assertNoPayoutOutputMatchesChallenger(\n challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n ctx.network,\n );\n\n // Parse the NoPayout tx and the two ChallengeAssert parents.\n const noPayoutTx = Transaction.fromHex(\n stripHexPrefix(challenger.nopayout_tx.tx_hex),\n );\n const challengeAssertXTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_x_tx.tx_hex),\n );\n const challengeAssertYTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_y_tx.tx_hex),\n );\n\n if (noPayoutTx.ins.length !== 3) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) must have exactly 3 inputs, got ${noPayoutTx.ins.length}`,\n );\n }\n\n // Pin every input's parent. Each parent's outs[0] is the authoritative\n // prevout - because we verified the parent's txid matches what the NoPayout\n // tx commits to, the parent cannot be substituted without changing the\n // NoPayout txid.\n assertInputReferencesParent(\n noPayoutTx,\n 0,\n assertTxParsed,\n \"Assert\",\n challengerPubkey,\n );\n assertInputReferencesParent(\n noPayoutTx,\n 1,\n challengeAssertXTx,\n \"ChallengeAssertX\",\n challengerPubkey,\n );\n assertInputReferencesParent(\n noPayoutTx,\n 2,\n challengeAssertYTx,\n \"ChallengeAssertY\",\n challengerPubkey,\n );\n\n const prevouts = [\n assertTxParsed.outs[0],\n challengeAssertXTx.outs[0],\n challengeAssertYTx.outs[0],\n ].map((out) => ({\n script_pubkey: uint8ArrayToHex(new Uint8Array(out.script)),\n value: out.value,\n }));\n\n return buildNoPayoutPsbt({\n noPayoutTxHex: challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n prevouts,\n connectorParams: {\n claimer: claimerPubkey,\n localChallengers,\n universalChallengers: ctx.universalChallengerBtcPubkeys,\n timelockAssert: ctx.timelockAssert,\n councilMembers: ctx.councilMembers,\n councilQuorum: ctx.councilQuorum,\n },\n });\n}\n\n// ============================================================================\n// Extract phase\n// ============================================================================\n\n/**\n * Extract all signatures from signed PSBTs and assemble into presignatures.\n */\nfunction extractDepositorGraphSignatures(\n signedPsbtHexes: string[],\n challengerEntries: ChallengerEntry[],\n depositorPubkey: string,\n): DepositorAsClaimerPresignatures {\n const payoutSignature = extractPayoutSignature(\n signedPsbtHexes[0],\n depositorPubkey,\n );\n\n const perChallenger: Record<string, DepositorPreSigsPerChallenger> = {};\n for (const entry of challengerEntries) {\n perChallenger[entry.challengerPubkey] = {\n nopayout_signature: extractPayoutSignature(\n signedPsbtHexes[entry.noPayoutIdx],\n depositorPubkey,\n ),\n };\n }\n\n return {\n payout_signatures: {\n payout_signature: payoutSignature,\n },\n per_challenger: perChallenger,\n };\n}\n\n/**\n * Sign multiple PSBTs, using batch signing when the wallet supports it.\n * Falls back to sequential `signPsbt` calls for wallets without `signPsbts`.\n */\nasync function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (typeof wallet.signPsbts === \"function\") {\n return wallet.signPsbts(psbtHexes, options);\n }\n\n const signed: string[] = [];\n for (let i = 0; i < psbtHexes.length; i++) {\n signed.push(await wallet.signPsbt(psbtHexes[i], options?.[i]));\n }\n return signed;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Authoritative inputs required to construct the depositor's Payout AND every\n * per-challenger NoPayout PSBT locally. Every field here must come from\n * trusted on-chain sources, not from the vault provider response. They feed\n * directly into the Taproot sighash.\n */\nexport interface DepositorGraphSigningContext {\n /** Raw pegin BTC transaction hex (provides the depositor's signed prevout) */\n peginTxHex: string;\n /** Depositor's BTC public key (x-only, 64-char hex, no 0x prefix) */\n depositorBtcPubkey: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Pegin CSV timelock from the locked offchain params version (blocks) */\n timelockPegin: number;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Sourced from the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n */\n timelockAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix). Sourced from\n * the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).securityCouncilKeys`.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold. Sourced from the on-chain ProtocolParams\n * contract via `ViemProtocolParamsReader.getOffchainParamsByVersion(...).councilQuorum`.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /**\n * On-chain registered depositor payout scriptPubKey (hex, with or without\n * 0x prefix). Used to assert the VP-advertised payout transaction pays to\n * the depositor's registered address before the wallet produces a signature.\n */\n registeredPayoutScriptPubKey: string;\n}\n\nexport interface SignDepositorGraphParams {\n /** The depositor graph from VP response */\n depositorGraph: DepositorGraphTransactions;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** Authoritative inputs used to rebuild every PSBT locally */\n signingContext: DepositorGraphSigningContext;\n}\n\n/**\n * Sign all depositor graph transactions and assemble into presignatures.\n *\n * Flow:\n * 1. Build payout + per-challenger nopayout PSBTs locally\n * 2. Batch sign via wallet.signPsbts() if available, else sequential signPsbt()\n * 3. Extract Schnorr signatures from each signed PSBT\n * 4. Assemble into DepositorAsClaimerPresignatures\n */\nexport async function signDepositorGraph(\n params: SignDepositorGraphParams,\n): Promise<DepositorAsClaimerPresignatures> {\n const { depositorGraph, btcWallet, signingContext } = params;\n\n const walletPublicKey = await btcWallet.getPublicKeyHex();\n // Fail fast if the connected wallet doesn't match the on-chain registered\n // depositor key — otherwise extractPayoutSignature later fails after\n // multiple wallet popups with an opaque \"no signature found\" error.\n const { depositorPubkey } = validateWalletPubkey(\n walletPublicKey,\n stripHexPrefix(signingContext.depositorBtcPubkey),\n );\n\n // 1. Build all PSBTs locally\n const { psbtHexes, signOptions, challengerEntries } =\n await collectDepositorGraphPsbts(\n depositorGraph,\n walletPublicKey,\n signingContext,\n );\n\n // 2. Sign all PSBTs (batch when supported, sequential fallback for mobile)\n const signedPsbtHexes = await signPsbtsWithFallback(\n btcWallet,\n psbtHexes,\n signOptions,\n );\n\n if (signedPsbtHexes.length !== psbtHexes.length) {\n throw new Error(\n `Wallet returned ${signedPsbtHexes.length} signed PSBTs, expected ${psbtHexes.length}`,\n );\n }\n\n // 3. Extract signatures and assemble presignatures\n return extractDepositorGraphSignatures(\n signedPsbtHexes,\n challengerEntries,\n depositorPubkey,\n );\n}\n","/**\n * Payout Signing Orchestration\n *\n * Polls VP for `PendingDepositorSignatures`, fetches presign transactions,\n * signs payouts via PayoutManager, signs the depositor graph, and submits\n * all signatures back to the VP.\n *\n * This is the main deposit protocol step between registration and activation.\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport * as bitcoin from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport { DaemonStatus } from \"../../clients/vault-provider/types\";\nimport type {\n ClaimerSignatures,\n ClaimerTransactions,\n} from \"../../clients/vault-provider/types\";\nimport { PayoutManager } from \"../../managers/PayoutManager\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport type { PeginStatusReader, PresignClient } from \"./interfaces\";\nimport { signDepositorGraph } from \"./signDepositorGraph\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context required for signing payout transactions.\n * Caller builds this from on-chain data (contract queries, GraphQL, config).\n */\nexport interface PayoutSigningContext {\n /** Raw pegin BTC transaction hex (for PSBT construction) */\n peginTxHex: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Depositor's BTC public key (x-only hex, no prefix) */\n depositorBtcPubkey: string;\n /** Pegin timelock from the locked offchain params version */\n timelockPegin: number;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Source: ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n timelockAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix).\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).securityCouncilKeys`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold.\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).councilQuorum`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /** On-chain registered depositor payout scriptPubKey (hex) */\n registeredPayoutScriptPubKey: string;\n}\n\nexport interface RunDepositorPresignFlowParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the presign transaction flow interface */\n presignClient: PresignClient;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Signing context built from on-chain data */\n signingContext: PayoutSigningContext;\n /** Maximum polling timeout in milliseconds (default: 20 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Optional progress callback (completed claimers, total claimers) */\n onProgress?: (completed: number, total: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum polling timeout (20 minutes) — VP may take 15-20 min to prepare. */\nconst MAX_POLLING_TIMEOUT_MS = 20 * 60 * 1000;\n\n/** Statuses after payout signatures are submitted — if VP is already here, skip. */\nconst POST_PAYOUT_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_ACKS,\n DaemonStatus.PENDING_ACTIVATION,\n DaemonStatus.ACTIVATED,\n]);\n\nconst TARGET_STATUS: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_SIGNATURES,\n ...POST_PAYOUT_STATUSES,\n]);\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\ninterface PreparedTransaction {\n claimerPubkeyXOnly: string;\n payoutTxHex: string;\n assertTxHex: string;\n}\n\nfunction prepareTransactionsForSigning(\n claimerTransactions: ClaimerTransactions[],\n): PreparedTransaction[] {\n return claimerTransactions.map((tx) => ({\n claimerPubkeyXOnly: processPublicKeyToXOnly(tx.claimer_pubkey),\n payoutTxHex: tx.payout_tx.tx_hex,\n assertTxHex: tx.assert_tx.tx_hex,\n }));\n}\n\n/**\n * Derive BIP-86 P2TR scriptPubKey hex from an x-only public key.\n * Requires bitcoinjs-lib ECC to be initialized by the caller.\n */\nfunction deriveBip86ScriptPubKey(xOnlyPubkeyHex: string): string {\n const { output } = bitcoin.payments.p2tr({\n internalPubkey: Buffer.from(xOnlyPubkeyHex, \"hex\"),\n });\n if (!output) {\n throw new Error(\"Failed to derive BIP-86 P2TR scriptPubKey\");\n }\n return output.toString(\"hex\");\n}\n\n/**\n * Resolve the expected payout scriptPubKey for a given claimer.\n *\n * - VP/Depositor claimer: payout goes to the depositor's registered payout address\n * - VK claimer: payout goes to a BIP-86 P2TR address derived from the VK's pubkey\n *\n * Note: BIP-86 derivation for VK claimers requires bitcoinjs-lib's ECC to be initialized.\n */\nfunction resolvePayoutScriptPubKey(\n claimerPubkeyXOnly: string,\n context: PayoutSigningContext,\n): string {\n const claimer = stripHexPrefix(claimerPubkeyXOnly).toLowerCase();\n const vpPubkey = stripHexPrefix(\n context.vaultProviderBtcPubkey,\n ).toLowerCase();\n const depositorPubkey = stripHexPrefix(\n context.depositorBtcPubkey,\n ).toLowerCase();\n\n if (claimer === vpPubkey || claimer === depositorPubkey) {\n return context.registeredPayoutScriptPubKey;\n }\n\n // Verify claimer is a known vault keeper\n const isVaultKeeper = context.vaultKeeperBtcPubkeys.some(\n (vk) => stripHexPrefix(vk).toLowerCase() === claimer,\n );\n if (!isVaultKeeper) {\n throw new Error(\n `Unknown claimer pubkey ${claimer}: not VP, depositor, or a registered vault keeper`,\n );\n }\n\n // VK claimer: derive BIP-86 P2TR scriptPubKey from the VK's x-only pubkey\n const scriptPubKey = deriveBip86ScriptPubKey(claimer);\n return `0x${scriptPubKey}`;\n}\n\nfunction buildPayoutSigningInput(\n tx: PreparedTransaction,\n context: PayoutSigningContext,\n) {\n return {\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: context.peginTxHex,\n assertTxHex: tx.assertTxHex,\n vaultProviderBtcPubkey: context.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: context.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: context.universalChallengerBtcPubkeys,\n depositorBtcPubkey: context.depositorBtcPubkey,\n timelockPegin: context.timelockPegin,\n registeredPayoutScriptPubKey: resolvePayoutScriptPubKey(\n tx.claimerPubkeyXOnly,\n context,\n ),\n };\n}\n\n/**\n * Sign all payout transactions using PayoutManager.\n * Uses batch signing when wallet supports it, sequential otherwise.\n */\nasync function signPayoutTransactions(\n btcWallet: BitcoinWallet,\n context: PayoutSigningContext,\n transactions: PreparedTransaction[],\n onProgress?: (completed: number, total: number) => void,\n): Promise<Record<string, ClaimerSignatures>> {\n const payoutManager = new PayoutManager({\n network: context.network,\n btcWallet,\n });\n\n const totalClaimers = transactions.length;\n onProgress?.(0, totalClaimers);\n\n let payoutSignatures: string[];\n\n if (payoutManager.supportsBatchSigning()) {\n const results = await payoutManager.signPayoutTransactionsBatch(\n transactions.map((tx) => buildPayoutSigningInput(tx, context)),\n );\n payoutSignatures = results.map((r) => r.payoutSignature);\n } else {\n payoutSignatures = [];\n for (let i = 0; i < transactions.length; i++) {\n onProgress?.(i, totalClaimers);\n const result = await payoutManager.signPayoutTransaction(\n buildPayoutSigningInput(transactions[i], context),\n );\n payoutSignatures.push(result.signature);\n }\n }\n\n const signatures: Record<string, ClaimerSignatures> = {};\n for (let i = 0; i < transactions.length; i++) {\n signatures[transactions[i].claimerPubkeyXOnly] = {\n payout_signature: payoutSignatures[i],\n };\n }\n\n onProgress?.(totalClaimers, totalClaimers);\n return signatures;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Poll for payout transactions, sign them, sign the depositor graph,\n * and submit all signatures to the vault provider.\n *\n * This is the main deposit protocol step between registration and activation.\n *\n * @throws Error on timeout, abort, signing failure, or RPC error\n */\nexport async function runDepositorPresignFlow(\n params: RunDepositorPresignFlowParams,\n): Promise<void> {\n const {\n statusReader,\n presignClient,\n btcWallet,\n peginTxid,\n depositorPk,\n signingContext,\n timeoutMs = MAX_POLLING_TIMEOUT_MS,\n signal,\n onProgress,\n } = params;\n\n // Phase 1: Poll until VP is ready for depositor signatures (or already past)\n const status = await waitForPeginStatus({\n statusReader,\n peginTxid,\n targetStatuses: TARGET_STATUS,\n timeoutMs,\n signal,\n });\n\n // Resume-safe: if VP already moved past payout signing, nothing to do\n if (POST_PAYOUT_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n // Phase 2: Fetch presign transactions\n const response = await presignClient.requestDepositorPresignTransactions(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n },\n signal,\n );\n\n signal?.throwIfAborted();\n\n // Phase 3: Sign VP/VK claimer payout transactions\n // Filter out the depositor's own claimer entry — its payout is signed\n // separately via signDepositorGraph (Phase 4) using VP-provided PSBTs.\n // Including it here would cause a redundant wallet signing prompt whose\n // result is discarded when the depositor graph signature overwrites it.\n const depositorPkNormalized = processPublicKeyToXOnly(depositorPk);\n const nonDepositorTxs = response.txs.filter(\n (tx) => processPublicKeyToXOnly(tx.claimer_pubkey) !== depositorPkNormalized,\n );\n const preparedTransactions = prepareTransactionsForSigning(nonDepositorTxs);\n const claimerSignatures = await signPayoutTransactions(\n btcWallet,\n signingContext,\n preparedTransactions,\n onProgress,\n );\n\n signal?.throwIfAborted();\n\n // Phase 4: Sign depositor-as-claimer graph. Both Payout and per-challenger\n // NoPayout PSBTs are rebuilt locally inside signDepositorGraph from these\n // authoritative connector params and the on-chain protocol parameters.\n const depositorClaimerPresignatures = await signDepositorGraph({\n depositorGraph: response.depositor_graph,\n btcWallet,\n signingContext: {\n peginTxHex: signingContext.peginTxHex,\n depositorBtcPubkey: depositorPk,\n vaultProviderBtcPubkey: signingContext.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: signingContext.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys:\n signingContext.universalChallengerBtcPubkeys,\n timelockPegin: signingContext.timelockPegin,\n timelockAssert: signingContext.timelockAssert,\n councilMembers: signingContext.councilMembers,\n councilQuorum: signingContext.councilQuorum,\n network: signingContext.network,\n registeredPayoutScriptPubKey: signingContext.registeredPayoutScriptPubKey,\n },\n });\n\n signal?.throwIfAborted();\n\n // Phase 5: Submit all signatures to VP\n // Include depositor's own payout signature in the signatures map\n const allSignatures = { ...claimerSignatures };\n allSignatures[stripHexPrefix(depositorPk)] =\n depositorClaimerPresignatures.payout_signatures;\n\n await presignClient.submitDepositorPresignatures(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n signatures: allSignatures,\n depositor_claimer_presignatures: depositorClaimerPresignatures,\n },\n signal,\n );\n}\n","/**\n * Pure validation functions for deposit operations.\n *\n * All validations return a consistent {@link ValidationResult} format or throw\n * on critical failures (e.g. missing protocol participants).\n *\n * Business rules (single-provider limit, max vault count) and form-flow\n * checks (wallet connected) belong in the consumer layer.\n *\n * @module tbv/core/services/deposit/validation\n */\n\nimport {\n formatSatoshisToBtc,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n warnings?: string[];\n}\n\n/**\n * Parameters for checking if a deposit form is valid.\n */\nexport interface DepositFormValidityParams {\n /** Deposit amount in satoshis */\n amountSats: bigint;\n /** Minimum deposit from protocol params */\n minDeposit: bigint;\n /** Maximum deposit from protocol params (optional) */\n maxDeposit?: bigint;\n /** User's available BTC balance in satoshis */\n btcBalance: bigint;\n /** Estimated transaction fee in satoshis */\n estimatedFeeSats?: bigint;\n /** Depositor claim value in satoshis (required output for challenge transactions) */\n depositorClaimValue?: bigint;\n}\n\nexport interface RemainingCapacityParams {\n /** Requested deposit amount in satoshis */\n amount: bigint;\n /**\n * Effective remaining capacity in satoshis (min of protocol-total and\n * per-address remaining). `null` means no cap applies.\n */\n effectiveRemaining: bigint | null;\n}\n\n/** Narrow structural type for UTXO — avoids importing vault-specific types. */\ninterface UtxoLike {\n txid: string;\n vout: number;\n value: number;\n}\n\n/**\n * Parameters for validating multi-vault deposit flow inputs.\n *\n * Callers must resolve any async loading states before calling — the SDK\n * validates resolved data, not React hook state.\n *\n * Form-flow checks (wallet connected, provider selected) are the caller's\n * responsibility and are NOT performed here.\n */\nexport interface MultiVaultDepositFlowInputs {\n vaultAmounts: bigint[];\n confirmedUTXOs: UtxoLike[];\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: string[];\n universalChallengerBtcPubkeys: string[];\n /** Protocol minimum deposit per vault (satoshis) */\n minDeposit: bigint;\n /** Protocol maximum deposit per vault (satoshis) */\n maxDeposit?: bigint;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction isValidXOnlyHex(hex: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(hex);\n}\n\n// ---------------------------------------------------------------------------\n// Validation functions\n// ---------------------------------------------------------------------------\n\n/**\n * Check if deposit amount is within valid range and affordable.\n *\n * Returns false when fees/claim value are not yet known (still loading),\n * and includes them in the balance check once available.\n */\nexport function isDepositAmountValid(\n params: DepositFormValidityParams,\n): boolean {\n const {\n amountSats,\n minDeposit,\n maxDeposit,\n btcBalance,\n estimatedFeeSats,\n depositorClaimValue,\n } = params;\n\n if (amountSats <= 0n) return false;\n if (amountSats < minDeposit) return false;\n if (maxDeposit && maxDeposit > 0n && amountSats > maxDeposit) return false;\n\n if (estimatedFeeSats == null || depositorClaimValue == null) return false;\n\n const totalRequired = amountSats + estimatedFeeSats + depositorClaimValue;\n if (totalRequired > btcBalance) return false;\n\n return true;\n}\n\n/**\n * Validate deposit amount against minimum and maximum constraints.\n */\nexport function validateDepositAmount(\n amount: bigint,\n minDeposit: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (amount <= 0n) {\n return {\n valid: false,\n error: \"Deposit amount must be greater than zero\",\n };\n }\n\n if (amount < minDeposit) {\n return {\n valid: false,\n error: `Minimum deposit is ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n\n if (maxDeposit && maxDeposit > 0n && amount > maxDeposit) {\n return {\n valid: false,\n error: `Maximum deposit is ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that the requested deposit fits within the effective remaining cap.\n */\nexport function validateRemainingCapacity(\n params: RemainingCapacityParams,\n): ValidationResult {\n const { amount, effectiveRemaining } = params;\n if (effectiveRemaining === null) return { valid: true };\n\n if (effectiveRemaining === 0n) {\n return {\n valid: false,\n error: \"Supply cap reached — deposits temporarily paused\",\n };\n }\n\n if (amount > effectiveRemaining) {\n return {\n valid: false,\n error: `Vault size exceeds remaining capacity (${formatSatoshisToBtc(effectiveRemaining)} BTC)`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that selected providers exist in the available set.\n *\n * Business rules (e.g. single-provider limit) are the caller's responsibility.\n */\nexport function validateProviderSelection(\n selectedProviders: string[],\n availableProviders: string[],\n): ValidationResult {\n if (!selectedProviders || selectedProviders.length === 0) {\n return {\n valid: false,\n error: \"At least one vault provider must be selected\",\n };\n }\n\n const availableProvidersLower = availableProviders.map((p) =>\n p.toLowerCase(),\n );\n const invalidProviders = selectedProviders.filter(\n (p) => !availableProvidersLower.includes(p.toLowerCase()),\n );\n\n if (invalidProviders.length > 0) {\n return {\n valid: false,\n error: \"Invalid vault provider selected\",\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault amounts array for multi-vault deposits.\n * Checks count, positivity, and per-vault min/max protocol limits.\n *\n * Max vault count limits are the caller's responsibility.\n */\nexport function validateVaultAmounts(\n amounts: bigint[],\n minDeposit?: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (!amounts || amounts.length === 0) {\n return {\n valid: false,\n error: \"At least one vault amount required\",\n };\n }\n\n for (let i = 0; i < amounts.length; i++) {\n const amount = amounts[i];\n if (amount <= 0n) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount must be positive`,\n };\n }\n if (minDeposit && amount < minDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC is below minimum deposit ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n if (maxDeposit && amount > maxDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC exceeds maximum deposit ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault provider BTC public key format.\n */\nexport function validateVaultProviderPubkey(pubkey: string): ValidationResult {\n const stripped = stripHexPrefix(pubkey);\n if (!isValidXOnlyHex(stripped)) {\n return {\n valid: false,\n error:\n \"Invalid pubkey format: must be 64 hex characters (32-byte x-only public key, no 0x prefix)\",\n };\n }\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers for multi-vault validation\n// ---------------------------------------------------------------------------\n\nfunction validateVaultKeepers(vaultKeeperBtcPubkeys: string[]): void {\n if (!vaultKeeperBtcPubkeys || vaultKeeperBtcPubkeys.length === 0) {\n throw new Error(\n \"No vault keepers available. The system requires at least one vault keeper to create a deposit.\",\n );\n }\n}\n\nfunction validateUniversalChallengers(\n universalChallengerBtcPubkeys: string[],\n): void {\n if (\n !universalChallengerBtcPubkeys ||\n universalChallengerBtcPubkeys.length === 0\n ) {\n throw new Error(\n \"No universal challengers available. The system requires at least one universal challenger to create a deposit.\",\n );\n }\n}\n\nfunction validateUTXOState(confirmedUTXOs: UtxoLike[]): void {\n if (confirmedUTXOs.length === 0) {\n throw new Error(\"No spendable UTXOs available\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Multi-vault composite validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validate protocol-level multi-vault deposit inputs.\n * Throws an error if any validation fails.\n *\n * Form-flow checks (wallet connections, provider selection) must be\n * performed by the caller before invoking this function.\n */\nexport function validateMultiVaultDepositInputs(\n params: MultiVaultDepositFlowInputs,\n): void {\n const {\n vaultAmounts,\n confirmedUTXOs,\n vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys,\n minDeposit,\n maxDeposit,\n } = params;\n\n const amountsValidation = validateVaultAmounts(\n vaultAmounts,\n minDeposit,\n maxDeposit,\n );\n if (!amountsValidation.valid) {\n throw new Error(amountsValidation.error);\n }\n\n // Vault provider pubkey\n const pubkeyValidation = validateVaultProviderPubkey(vaultProviderBtcPubkey);\n if (!pubkeyValidation.valid) {\n throw new Error(pubkeyValidation.error);\n }\n\n validateVaultKeepers(vaultKeeperBtcPubkeys);\n validateUniversalChallengers(universalChallengerBtcPubkeys);\n validateUTXOState(confirmedUTXOs);\n}\n","import type { Address } from \"viem\";\n\nimport type {\n UniversalChallengerReader,\n VaultKeeperReader,\n VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { processPublicKeyToXOnly } from \"../../primitives/utils/bitcoin\";\n\nexport interface ValidateOnChainParticipantKeysParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultKeeperReader: VaultKeeperReader;\n universalChallengerReader: UniversalChallengerReader;\n vaultProviderEthAddress: Address;\n applicationEntryPoint: Address;\n expectedVaultProviderBtcPubkey: string;\n expectedVaultKeeperBtcPubkeys: string[];\n expectedUniversalChallengerBtcPubkeys: string[];\n}\n\nexport interface ValidatedOnChainParticipantKeys {\n vaultProviderBtcPubkeyXOnly: string;\n vaultKeeperBtcPubkeysSorted: string[];\n universalChallengerBtcPubkeysSorted: string[];\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n}\n\nexport async function validateOnChainParticipantKeys(\n params: ValidateOnChainParticipantKeysParams,\n): Promise<ValidatedOnChainParticipantKeys> {\n const {\n vaultRegistryReader,\n vaultKeeperReader,\n universalChallengerReader,\n vaultProviderEthAddress,\n applicationEntryPoint,\n expectedVaultProviderBtcPubkey,\n expectedVaultKeeperBtcPubkeys,\n expectedUniversalChallengerBtcPubkeys,\n } = params;\n\n const [\n onChainVpKey,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n ] = await Promise.all([\n vaultRegistryReader.getVaultProviderBtcPubKey(vaultProviderEthAddress),\n vaultKeeperReader.getCurrentVaultKeepersVersion(applicationEntryPoint),\n universalChallengerReader.getLatestUniversalChallengersVersion(),\n ]);\n\n const [onChainKeepers, onChainChallengers] = await Promise.all([\n vaultKeeperReader.getVaultKeepersByVersion(\n applicationEntryPoint,\n expectedAppVaultKeepersVersion,\n ),\n universalChallengerReader.getUniversalChallengersByVersion(\n expectedUniversalChallengersVersion,\n ),\n ]);\n\n const canonical = (k: string) => processPublicKeyToXOnly(k).toLowerCase();\n const sortedSet = (keys: string[]) => keys.map(canonical).sort();\n\n const expectedVpKeyXOnly = canonical(expectedVaultProviderBtcPubkey);\n if (expectedVpKeyXOnly !== onChainVpKey) {\n throw new Error(\n `Vault provider BTC pubkey indexer hint does not match BTCVaultRegistry for ${vaultProviderEthAddress}. Refresh and try again.`,\n );\n }\n\n const expectedKeepers = sortedSet(expectedVaultKeeperBtcPubkeys);\n const onChainKeepersSorted = sortedSet(\n onChainKeepers.map((p) => p.btcPubKey),\n );\n if (\n expectedKeepers.length !== onChainKeepersSorted.length ||\n expectedKeepers.some((k, i) => k !== onChainKeepersSorted[i])\n ) {\n throw new Error(\n `Vault keeper BTC pubkeys (v${expectedAppVaultKeepersVersion}) indexer set does not match ApplicationRegistry on-chain set. Refresh and try again.`,\n );\n }\n\n const expectedChallengers = sortedSet(expectedUniversalChallengerBtcPubkeys);\n const onChainChallengersSorted = sortedSet(\n onChainChallengers.map((p) => p.btcPubKey),\n );\n if (\n expectedChallengers.length !== onChainChallengersSorted.length ||\n expectedChallengers.some((k, i) => k !== onChainChallengersSorted[i])\n ) {\n throw new Error(\n `Universal challenger BTC pubkeys (v${expectedUniversalChallengersVersion}) indexer set does not match ProtocolParams on-chain set. Refresh and try again.`,\n );\n }\n\n return {\n vaultProviderBtcPubkeyXOnly: onChainVpKey,\n vaultKeeperBtcPubkeysSorted: onChainKeepersSorted,\n universalChallengerBtcPubkeysSorted: onChainChallengersSorted,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n };\n}\n","import type { Hex } from \"viem\";\n\nimport type { VaultRegistryReader } from \"../../clients/eth/types\";\n\nexport interface VerifyRegisteredVaultVersionsParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultIds: readonly Hex[];\n expectedOffchainParamsVersion: number;\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n}\n\n// Distinct from a transient RPC failure: the orchestrator removes pending\n// pegin entries only when a real mismatch is confirmed on-chain.\nexport class RegisteredVaultVersionMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RegisteredVaultVersionMismatchError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field so the cleanup path stays reliable.\nexport function isRegisteredVaultVersionMismatchError(\n err: unknown,\n): err is RegisteredVaultVersionMismatchError {\n return (\n err instanceof RegisteredVaultVersionMismatchError ||\n (err instanceof Error && err.name === \"RegisteredVaultVersionMismatchError\")\n );\n}\n\nexport async function verifyRegisteredVaultVersions(\n params: VerifyRegisteredVaultVersionsParams,\n): Promise<void> {\n const {\n vaultRegistryReader,\n vaultIds,\n expectedOffchainParamsVersion,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n } = params;\n\n const infos = await vaultRegistryReader.getProtocolInfoBatch(vaultIds);\n\n const mismatches: string[] = [];\n infos.forEach((v, i) => {\n const id = vaultIds[i];\n if (v.offchainParamsVersion !== expectedOffchainParamsVersion) {\n mismatches.push(\n `vault ${id}: offchainParams expected v${expectedOffchainParamsVersion}, got v${v.offchainParamsVersion}`,\n );\n }\n if (v.appVaultKeepersVersion !== expectedAppVaultKeepersVersion) {\n mismatches.push(\n `vault ${id}: appVaultKeepers expected v${expectedAppVaultKeepersVersion}, got v${v.appVaultKeepersVersion}`,\n );\n }\n if (v.universalChallengersVersion !== expectedUniversalChallengersVersion) {\n mismatches.push(\n `vault ${id}: universalChallengers expected v${expectedUniversalChallengersVersion}, got v${v.universalChallengersVersion}`,\n );\n }\n });\n\n if (mismatches.length > 0) {\n throw new RegisteredVaultVersionMismatchError(\n `Aborting BTC broadcast: signer-set or offchain-params versions changed during registration (${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ETH vault will time out per protocol rules.`,\n );\n }\n}\n","/**\n * Pegout state definitions and protocol-level terminal checks.\n *\n * Maps VP-reported pegout statuses from `vaultProvider_batchGetPegoutStatus`\n * to protocol lifecycle states.\n *\n * Lifecycle:\n * ClaimEventReceived -> ClaimBroadcast -> AssertBroadcast -> PayoutBroadcast (success)\n * > ChallengeAssertObserved -> WronglyChallengedBroadcast -> PayoutBroadcast\n * > ChallengeAssertObserved -> Failed (challenger won)\n */\n\n/** Claimer-side pegout statuses reported by the VP. */\nexport enum ClaimerPegoutStatusValue {\n CLAIM_EVENT_RECEIVED = \"ClaimEventReceived\",\n CLAIM_BROADCAST = \"ClaimBroadcast\",\n ASSERT_BROADCAST = \"AssertBroadcast\",\n CHALLENGE_ASSERT_OBSERVED = \"ChallengeAssertObserved\",\n WRONGLY_CHALLENGED_BROADCAST = \"WronglyChallengedBroadcast\",\n PAYOUT_BROADCAST = \"PayoutBroadcast\",\n FAILED = \"Failed\",\n}\n\nconst PEGOUT_TERMINAL_STATUSES = new Set<string>([\n ClaimerPegoutStatusValue.PAYOUT_BROADCAST,\n ClaimerPegoutStatusValue.FAILED,\n]);\n\n/** Whether a claimer status string maps to a known pegout state. */\nexport function isRecognizedPegoutStatus(status: string): boolean {\n return Object.values(ClaimerPegoutStatusValue).includes(\n status as ClaimerPegoutStatusValue,\n );\n}\n\n/**\n * Whether a claimer status is a hard-terminal pegout status\n * (PayoutBroadcast or Failed). Soft-terminal conditions (polling\n * thresholds) are a consumer-side concern.\n */\nexport function isPegoutTerminalStatus(\n claimerStatus: string | undefined,\n): boolean {\n return !!claimerStatus && PEGOUT_TERMINAL_STATUSES.has(claimerStatus);\n}\n","/**\n * Domain errors thrown by the refund service.\n *\n * @module services/refund/errors\n */\n\nimport type { Hex } from \"viem\";\n\n/**\n * Thrown when the broadcast transport rejects the refund tx because the CSV\n * timelock has not yet matured (BIP68 non-final). Callers can surface a\n * friendly \"wait until block N\" message; the original transport error is\n * available via {@link cause}.\n */\nexport class BIP68NotMatureError extends Error {\n public readonly vaultId: Hex;\n public override readonly cause: Error;\n\n constructor(vaultId: Hex, cause: Error) {\n super(`Refund not yet mature (BIP68 not final): ${cause.message}`);\n this.name = \"BIP68NotMatureError\";\n this.vaultId = vaultId;\n this.cause = cause;\n }\n}\n","/**\n * Vault refund orchestration — reclaim BTC from an expired Pre-PegIn HTLC via\n * the CSV-timelocked refund script (leaf 1). SDK owns the sequence of:\n * fetch → fee calc → PSBT build → sign → finalize → broadcast. Pre-fetched\n * data (fee rate) is passed by value; the data-flow-dependent reads\n * (`readVault`, `readPrePeginContext(vault)`) and the interactive transports\n * (`signPsbt`, `broadcastTx`) stay as injected callbacks so the caller keeps\n * its transport choice (viem, wagmi, mempool client, etc.) and error decoding.\n *\n * @module services/refund\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Psbt } from \"bitcoinjs-lib\";\nimport type { Address, Hex } from \"viem\";\n\nimport type { SignPsbtOptions } from \"../../../../shared/wallets/interfaces/BitcoinWallet\";\nimport { buildRefundPsbt } from \"../../primitives/psbt/refund\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\nimport { BIP68NotMatureError } from \"./errors\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\n// BTC raw-hex convention: 0x prefix optional, even number of hex chars, must\n// be non-empty. Named distinctly from the ETH-hex regex in activateVault.ts\n// (which requires a 0x prefix and allows empty \"0x\") to make the convention\n// explicit at the call site.\nconst BTC_HEX_BYTES_RE = /^(?:0x)?(?:[0-9a-fA-F]{2})+$/;\n// Pubkeys are either 32 bytes (x-only, 64 hex chars) or 33 bytes (compressed,\n// 66 hex chars). 65 hex chars is not a valid byte length — reject it here\n// rather than letting the malformed value surface as an opaque PSBT/signing\n// failure later.\nconst PUBKEY_HEX_RE = /^(?:0x)?(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{66})$/;\n// Conservative upper bound for the fixed-shape refund tx (1 P2TR script-path\n// input spending the HTLC refund leaf → 1 P2TR/P2WPKH output). Taproot\n// script-path witness: 64-byte Schnorr sig + refund script + control block.\n// This is protocol-owned knowledge; callers don't parameterise it.\nexport const REFUND_VSIZE = 160;\n\n/**\n * Network fee (sats) the SDK will charge for a refund tx at the given\n * sat/vB rate. Mirrors the internal computation in\n * {@link buildAndBroadcastRefund} so callers (e.g. UI fee previews) don't\n * have to duplicate the constant.\n */\nexport function estimateRefundFeeSats(feeRateSatsVb: number): bigint {\n if (!Number.isFinite(feeRateSatsVb) || feeRateSatsVb <= 0) {\n throw new Error(\n `feeRateSatsVb must be a positive finite number, got ${feeRateSatsVb}`,\n );\n }\n return BigInt(Math.ceil(feeRateSatsVb * REFUND_VSIZE));\n}\n// Refund tx has exactly one input — the HTLC output at htlcVout from the\n// Pre-PegIn tx. Used to tell the signer how many sign entries to generate.\n// (Not the taproot leaf index; the leaf is encoded into the PSBT by the\n// WASM PSBT builder based on the refund script path.)\nconst REFUND_INPUT_COUNT = 1;\nconst MAX_VOUT = 0xffff;\nconst BIP68_ERROR_RE = /non-BIP68-final/i;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\n/**\n * Authoritative vault fields needed to build a refund. Versioning fields,\n * the hashlock, and htlcVout must come from the on-chain contract (never the\n * indexer). The amount + `unsignedPrePeginTxHex` + `depositorBtcPubkey` can\n * come from the indexer since they are not security-critical for signing\n * (the PSBT builder re-derives the HTLC script from on-chain params).\n */\nexport interface VaultRefundData {\n hashlock: Hex;\n htlcVout: number;\n offchainParamsVersion: number;\n appVaultKeepersVersion: number;\n universalChallengersVersion: number;\n vaultProvider: Address;\n applicationEntryPoint: Address;\n /** Pre-PegIn HTLC output value in satoshis. */\n amount: bigint;\n /**\n * Funded, pre-witness Pre-PegIn transaction hex. 0x prefix optional.\n * The name mirrors the contract/indexer schema; the bytes are the\n * funded form (refund construction needs real outpoints).\n */\n unsignedPrePeginTxHex: string;\n /** Depositor's BTC public key (x-only or compressed hex; 0x prefix optional). */\n depositorBtcPubkey: string;\n}\n\n/**\n * Version-resolved protocol context that parameterises the HTLC's taproot\n * scripts. The *signer-set* fields (`vaultKeeperPubkeys`,\n * `universalChallengerPubkeys`) and the version-locked numeric protocol\n * params **must** be sourced from the on-chain contract at the version\n * pinned in {@link VaultRefundData} — this is the trust boundary.\n * `vaultProviderPubkey` today is sourced from the GraphQL indexer via\n * `fetchVaultProviderById`; the caller is responsible for any additional\n * cross-check it requires. Keeper and challenger pubkey arrays must be\n * pre-sorted the same way the Rust protocol sorts them (canonical for\n * script derivation).\n */\nexport interface RefundPrePeginContext {\n vaultProviderPubkey: string;\n vaultKeeperPubkeys: readonly string[];\n universalChallengerPubkeys: readonly string[];\n timelockRefund: number;\n feeRate: bigint;\n numLocalChallengers: number;\n councilQuorum: number;\n councilSize: number;\n network: Network;\n}\n\n/** Minimum shape required from a broadcast result. */\nexport interface BtcBroadcastResult {\n txId: string;\n}\n\nexport type BtcBroadcaster<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> = (signedTxHex: string) => Promise<R>;\n\nexport type RefundPsbtSigner = (\n psbtHex: string,\n opts: SignPsbtOptions,\n) => Promise<string>;\n\nexport interface RefundInput<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> {\n vaultId: Hex;\n /**\n * Fetch authoritative on-chain + indexer vault data. The SDK passes no\n * arguments — the caller closes over `vaultId` (or any other context it\n * needs).\n */\n readVault: () => Promise<VaultRefundData>;\n /**\n * Fetch the version-pinned refund context (sorted pubkeys, timelock, etc.)\n * derived from the vault's locked versions.\n */\n readPrePeginContext: (\n vault: VaultRefundData,\n ) => Promise<RefundPrePeginContext>;\n /**\n * Mempool-derived sat/vB fee rate to use for the refund tx (positive\n * number). Caller fetches this before invoking — it does not depend on\n * any value the SDK computes, and folding it into the call keeps the\n * orchestration honest.\n */\n feeRate: number;\n /** BTC wallet signer; receives a PSBT hex + taproot script-path options. */\n signPsbt: RefundPsbtSigner;\n /** Broadcast callback — returns whatever shape the caller needs. */\n broadcastTx: BtcBroadcaster<R>;\n /** Checked at every async boundary. */\n signal?: AbortSignal;\n}\n\nfunction assertNonNegativeInteger(value: number, label: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer, got ${value}`);\n }\n}\n\nfunction validateVaultRefundData(v: VaultRefundData): void {\n assertBytes32(v.hashlock, \"hashlock\");\n if (\n !Number.isInteger(v.htlcVout) ||\n v.htlcVout < 0 ||\n v.htlcVout > MAX_VOUT\n ) {\n throw new Error(\n `htlcVout must be an integer 0-${MAX_VOUT}, got ${v.htlcVout}`,\n );\n }\n // Version fields flow directly into on-chain script derivation via\n // `readPrePeginContext` — NaN, negative, or non-integer values would\n // silently produce wrong scripts. Guard here as defence in depth even\n // though the caller sources these from bigint on-chain reads.\n assertNonNegativeInteger(v.offchainParamsVersion, \"offchainParamsVersion\");\n assertNonNegativeInteger(v.appVaultKeepersVersion, \"appVaultKeepersVersion\");\n assertNonNegativeInteger(\n v.universalChallengersVersion,\n \"universalChallengersVersion\",\n );\n if (typeof v.unsignedPrePeginTxHex !== \"string\" || v.unsignedPrePeginTxHex.length === 0) {\n throw new Error(\"unsignedPrePeginTxHex must be a non-empty hex string\");\n }\n if (!BTC_HEX_BYTES_RE.test(v.unsignedPrePeginTxHex)) {\n throw new Error(\n \"unsignedPrePeginTxHex must be a hex byte string (optional 0x prefix, even length)\",\n );\n }\n if (!v.depositorBtcPubkey || !PUBKEY_HEX_RE.test(v.depositorBtcPubkey)) {\n throw new Error(\n \"depositorBtcPubkey must be 32 or 33 bytes of hex (optional 0x prefix)\",\n );\n }\n if (typeof v.amount !== \"bigint\" || v.amount <= 0n) {\n throw new Error(`amount must be a positive bigint, got ${v.amount}`);\n }\n}\n\nfunction validateRefundPrePeginContext(c: RefundPrePeginContext): void {\n if (!c.vaultProviderPubkey || !PUBKEY_HEX_RE.test(c.vaultProviderPubkey)) {\n throw new Error(\"vaultProviderPubkey must be 32 or 33 bytes of hex\");\n }\n if (c.vaultKeeperPubkeys.length === 0) {\n throw new Error(\"vaultKeeperPubkeys must be non-empty\");\n }\n if (c.universalChallengerPubkeys.length === 0) {\n throw new Error(\"universalChallengerPubkeys must be non-empty\");\n }\n if (!Number.isInteger(c.timelockRefund) || c.timelockRefund <= 0) {\n throw new Error(\n `timelockRefund must be a positive integer, got ${c.timelockRefund}`,\n );\n }\n if (typeof c.feeRate !== \"bigint\" || c.feeRate <= 0n) {\n throw new Error(\n `protocol feeRate must be a positive bigint, got ${c.feeRate}`,\n );\n }\n if (\n !Number.isInteger(c.numLocalChallengers) ||\n c.numLocalChallengers < 0\n ) {\n throw new Error(\"numLocalChallengers must be a non-negative integer\");\n }\n if (\n !Number.isInteger(c.councilQuorum) ||\n !Number.isInteger(c.councilSize) ||\n c.councilQuorum <= 0 ||\n c.councilSize <= 0 ||\n c.councilQuorum > c.councilSize\n ) {\n throw new Error(\n `councilQuorum (${c.councilQuorum}) must be in [1, councilSize=${c.councilSize}]`,\n );\n }\n}\n\nfunction finalizeAndExtract(signedPsbtHex: string): string {\n const psbt = Psbt.fromHex(signedPsbtHex);\n try {\n psbt.finalizeAllInputs();\n } catch (e: unknown) {\n // Some wallets (e.g. Keystone) finalize during signPsbt; bitcoinjs then\n // throws \"Input is already finalized\". Treat that case as a no-op.\n const message = e instanceof Error ? e.message : String(e);\n if (!message.includes(\"already finalized\")) {\n throw new Error(`Failed to finalize refund PSBT: ${message}`);\n }\n }\n return psbt.extractTransaction().toHex();\n}\n\n/**\n * Build, sign, and broadcast a refund transaction for an expired vault.\n *\n * Trust boundary: `readVault` must source the hashlock, htlcVout, and\n * versioning fields from the on-chain contract — an indexer-only path\n * leaves the refund flow open to signer-set substitution. The SDK does\n * not enforce this; it is the caller's responsibility.\n *\n * The broadcast transport is expected to surface Bitcoin's `non-BIP68-final`\n * policy rejection as an `Error` whose message contains that string; when\n * it does, the SDK wraps it in {@link BIP68NotMatureError}. All other\n * transport errors propagate unchanged.\n *\n * @returns whatever the injected `broadcastTx` returns (generic pass-through)\n * @throws `Error` if any validation fails\n * @throws {@link BIP68NotMatureError} if the broadcast is rejected because\n * the refund CSV timelock has not yet matured\n * @throws anything `readVault`, `readPrePeginContext`,\n * `signPsbt`, or `broadcastTx` throws\n */\nexport async function buildAndBroadcastRefund<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n>(input: RefundInput<R>): Promise<R> {\n const {\n vaultId,\n readVault,\n readPrePeginContext,\n feeRate,\n signPsbt,\n broadcastTx,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n assertBytes32(vaultId, \"vaultId\");\n\n const vault = await readVault();\n validateVaultRefundData(vault);\n signal?.throwIfAborted();\n\n const ctx = await readPrePeginContext(vault);\n validateRefundPrePeginContext(ctx);\n signal?.throwIfAborted();\n\n if (!Number.isFinite(feeRate) || feeRate <= 0) {\n throw new Error(`feeRate must be a positive number, got ${feeRate}`);\n }\n const refundFee = BigInt(Math.ceil(feeRate * REFUND_VSIZE));\n signal?.throwIfAborted();\n\n // `vault.depositorBtcPubkey` may arrive as wallet-native compressed sec1\n // (33 bytes) because the caller fetches it live from the wallet for\n // signing. WASM script derivation wants x-only (32 bytes), so normalize\n // here; the raw form is kept for the wallet sign call below.\n const xOnlyDepositorPubkey = processPublicKeyToXOnly(\n vault.depositorBtcPubkey,\n );\n const { psbtHex } = await buildRefundPsbt({\n prePeginParams: {\n depositorPubkey: xOnlyDepositorPubkey,\n vaultProviderPubkey: stripHexPrefix(ctx.vaultProviderPubkey),\n vaultKeeperPubkeys: ctx.vaultKeeperPubkeys.map(stripHexPrefix),\n universalChallengerPubkeys:\n ctx.universalChallengerPubkeys.map(stripHexPrefix),\n hashlocks: [stripHexPrefix(vault.hashlock)],\n timelockRefund: ctx.timelockRefund,\n pegInAmounts: [vault.amount],\n feeRate: ctx.feeRate,\n numLocalChallengers: ctx.numLocalChallengers,\n councilQuorum: ctx.councilQuorum,\n councilSize: ctx.councilSize,\n network: ctx.network,\n },\n fundedPrePeginTxHex: stripHexPrefix(vault.unsignedPrePeginTxHex),\n htlcVout: vault.htlcVout,\n refundFee,\n // buildRefundPsbt's top-level `hashlock` param is documented as \"no 0x\n // prefix\" and flows into the WASM HTLC connector derivation; a prefixed\n // value would derive the wrong refund script leaf and yield an\n // unspendable PSBT. Match the `hashlocks` array handling above.\n hashlock: stripHexPrefix(vault.hashlock),\n });\n signal?.throwIfAborted();\n\n const signOptions = createTaprootScriptPathSignOptions(\n vault.depositorBtcPubkey,\n REFUND_INPUT_COUNT,\n );\n const signedPsbtHex = await signPsbt(psbtHex, signOptions);\n const signedTxHex = finalizeAndExtract(signedPsbtHex);\n signal?.throwIfAborted();\n\n try {\n return await broadcastTx(signedTxHex);\n } catch (error) {\n if (error instanceof Error && BIP68_ERROR_RE.test(error.message)) {\n throw new BIP68NotMatureError(vaultId, error);\n }\n throw error;\n }\n}\n"],"names":["BYTES32_HEX_RE","ADDRESS_HEX_RE","ETH_HEX_BYTES_RE","assertBytes32","value","label","assertAddress","assertHexBytes","activateVault","input","btcVaultRegistryAddress","vaultId","hashlock","activationMetadata","writeContract","signal","normalizedSecret","ensureHexPrefix","validateSecretAgainstHashlock","BTCVaultRegistryABI","DEFAULT_POLL_INTERVAL_MS","waitForPeginStatus","params","statusReader","peginTxid","targetStatuses","timeoutMs","pollIntervalMs","startTime","response","status","VP_TERMINAL_STATUSES","error","JsonRpcError","RpcErrorCode","resolve","reject","onAbort","timeoutId","STATUS_POLL_TIMEOUT_MS","TARGET_STATUSES","DaemonStatus","POST_WOTS_STATUSES","submitWotsPublicKey","wotsSubmitter","depositorPk","wotsPublicKeys","DEPOSITOR_SIGNED_INPUT_COUNT","deriveLocalChallengers","vaultProviderBtcPubkey","vaultKeeperBtcPubkeys","depositorBtcPubkey","depositor","stripHexPrefix","filtered","k","assertChallengerSetMatchesLocalChallengers","challengerPresignData","localChallengers","suppliedList","c","suppliedSet","expectedSet","missing","extra","readInputTxid","tx","inputIndex","uint8ArrayToHex","assertInputReferencesParent","noPayoutTx","parentTx","parentLabel","challengerPubkey","parentTxid","inputTxid","collectDepositorGraphPsbts","depositorGraph","walletPublicKey","ctx","psbtHexes","signOptions","challengerEntries","assertPayoutOutputMatchesRegistered","builtPayout","buildPayoutPsbt","createTaprootScriptPathSignOptions","claimerPubkey","assertTxParsed","Transaction","challenger","noPayoutIdx","noPayoutHex","buildLocalNoPayoutPsbt","assertNoPayoutOutputMatchesChallenger","challengeAssertXTx","challengeAssertYTx","prevouts","out","buildNoPayoutPsbt","extractDepositorGraphSignatures","signedPsbtHexes","depositorPubkey","payoutSignature","extractPayoutSignature","perChallenger","entry","signPsbtsWithFallback","wallet","options","signed","signDepositorGraph","btcWallet","signingContext","validateWalletPubkey","MAX_POLLING_TIMEOUT_MS","POST_PAYOUT_STATUSES","TARGET_STATUS","prepareTransactionsForSigning","claimerTransactions","processPublicKeyToXOnly","deriveBip86ScriptPubKey","xOnlyPubkeyHex","output","bitcoin","Buffer","resolvePayoutScriptPubKey","claimerPubkeyXOnly","context","claimer","vpPubkey","vk","buildPayoutSigningInput","signPayoutTransactions","transactions","onProgress","payoutManager","PayoutManager","totalClaimers","payoutSignatures","r","i","result","signatures","runDepositorPresignFlow","presignClient","depositorPkNormalized","nonDepositorTxs","preparedTransactions","claimerSignatures","depositorClaimerPresignatures","allSignatures","isValidXOnlyHex","hex","isDepositAmountValid","amountSats","minDeposit","maxDeposit","btcBalance","estimatedFeeSats","depositorClaimValue","validateDepositAmount","amount","formatSatoshisToBtc","validateRemainingCapacity","effectiveRemaining","validateProviderSelection","selectedProviders","availableProviders","availableProvidersLower","p","validateVaultAmounts","amounts","validateVaultProviderPubkey","pubkey","stripped","validateVaultKeepers","validateUniversalChallengers","universalChallengerBtcPubkeys","validateUTXOState","confirmedUTXOs","validateMultiVaultDepositInputs","vaultAmounts","amountsValidation","pubkeyValidation","validateOnChainParticipantKeys","vaultRegistryReader","vaultKeeperReader","universalChallengerReader","vaultProviderEthAddress","applicationEntryPoint","expectedVaultProviderBtcPubkey","expectedVaultKeeperBtcPubkeys","expectedUniversalChallengerBtcPubkeys","onChainVpKey","expectedAppVaultKeepersVersion","expectedUniversalChallengersVersion","onChainKeepers","onChainChallengers","canonical","sortedSet","keys","expectedKeepers","onChainKeepersSorted","expectedChallengers","onChainChallengersSorted","RegisteredVaultVersionMismatchError","message","isRegisteredVaultVersionMismatchError","err","verifyRegisteredVaultVersions","vaultIds","expectedOffchainParamsVersion","infos","mismatches","v","id","ClaimerPegoutStatusValue","PEGOUT_TERMINAL_STATUSES","isRecognizedPegoutStatus","isPegoutTerminalStatus","claimerStatus","BIP68NotMatureError","cause","__publicField","BTC_HEX_BYTES_RE","PUBKEY_HEX_RE","REFUND_VSIZE","estimateRefundFeeSats","feeRateSatsVb","REFUND_INPUT_COUNT","MAX_VOUT","BIP68_ERROR_RE","assertNonNegativeInteger","validateVaultRefundData","validateRefundPrePeginContext","finalizeAndExtract","signedPsbtHex","psbt","Psbt","e","buildAndBroadcastRefund","readVault","readPrePeginContext","feeRate","signPsbt","broadcastTx","vault","refundFee","xOnlyDepositorPubkey","psbtHex","buildRefundPsbt","signedTxHex"],"mappings":"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"}