@babylonlabs-io/ts-sdk 0.33.2 → 0.33.4

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 (147) hide show
  1. package/dist/BTCVaultRegistry.abi-DbJ5lsFJ.js +417 -0
  2. package/dist/BTCVaultRegistry.abi-DbJ5lsFJ.js.map +1 -0
  3. package/dist/BTCVaultRegistry.abi-ZdPpION2.cjs +2 -0
  4. package/dist/BTCVaultRegistry.abi-ZdPpION2.cjs.map +1 -0
  5. package/dist/PayoutManager-C9XHKZ5j.js +200 -0
  6. package/dist/PayoutManager-C9XHKZ5j.js.map +1 -0
  7. package/dist/PayoutManager-DDnNB0mj.cjs +2 -0
  8. package/dist/PayoutManager-DDnNB0mj.cjs.map +1 -0
  9. package/dist/PeginManager-BXg2S1mH.cjs +2 -0
  10. package/dist/PeginManager-BXg2S1mH.cjs.map +1 -0
  11. package/dist/{PeginManager-BtXhdqkm.js → PeginManager-C5HaMAcw.js} +367 -480
  12. package/dist/PeginManager-C5HaMAcw.js.map +1 -0
  13. package/dist/ProtocolParams.abi-BmvHwQJV.cjs +2 -0
  14. package/dist/ProtocolParams.abi-BmvHwQJV.cjs.map +1 -0
  15. package/dist/ProtocolParams.abi-DXu8L0Fn.js +382 -0
  16. package/dist/ProtocolParams.abi-DXu8L0Fn.js.map +1 -0
  17. package/dist/{bitcoin-0_T6KJON.js → bitcoin-B0S8SHCX.js} +24 -20
  18. package/dist/bitcoin-B0S8SHCX.js.map +1 -0
  19. package/dist/bitcoin-B3aqjuMP.cjs +2 -0
  20. package/dist/bitcoin-B3aqjuMP.cjs.map +1 -0
  21. package/dist/buildAndBroadcastRefund-CIPPpchL.cjs +2 -0
  22. package/dist/buildAndBroadcastRefund-CIPPpchL.cjs.map +1 -0
  23. package/dist/buildAndBroadcastRefund-D_QlvCsw.js +777 -0
  24. package/dist/buildAndBroadcastRefund-D_QlvCsw.js.map +1 -0
  25. package/dist/challengeAssert-BzxQmdZy.js +128 -0
  26. package/dist/challengeAssert-BzxQmdZy.js.map +1 -0
  27. package/dist/challengeAssert-Yyyj-EdR.cjs +2 -0
  28. package/dist/challengeAssert-Yyyj-EdR.cjs.map +1 -0
  29. package/dist/errors-9AkghWyk.js +22 -0
  30. package/dist/errors-9AkghWyk.js.map +1 -0
  31. package/dist/errors-BP73_stm.cjs +2 -0
  32. package/dist/errors-BP73_stm.cjs.map +1 -0
  33. package/dist/errors-Bu0H-dZD.cjs +2 -0
  34. package/dist/errors-Bu0H-dZD.cjs.map +1 -0
  35. package/dist/errors-CznAK5NB.js +96 -0
  36. package/dist/errors-CznAK5NB.js.map +1 -0
  37. package/dist/index.cjs +1 -1
  38. package/dist/index.js +156 -146
  39. package/dist/index.js.map +1 -1
  40. package/dist/mempoolApi-CAIge7Nj.js +205 -0
  41. package/dist/mempoolApi-CAIge7Nj.js.map +1 -0
  42. package/dist/mempoolApi-YNkKjQCU.cjs +2 -0
  43. package/dist/mempoolApi-YNkKjQCU.cjs.map +1 -0
  44. package/dist/noPayout-BXeUw0Qq.cjs +2 -0
  45. package/dist/noPayout-BXeUw0Qq.cjs.map +1 -0
  46. package/dist/noPayout-DBX6G96_.js +141 -0
  47. package/dist/noPayout-DBX6G96_.js.map +1 -0
  48. package/dist/payout-BNFMBXS6.js +193 -0
  49. package/dist/payout-BNFMBXS6.js.map +1 -0
  50. package/dist/payout-DQ_fmJUA.cjs +2 -0
  51. package/dist/payout-DQ_fmJUA.cjs.map +1 -0
  52. package/dist/peginInput-C2QPvuhR.js +177 -0
  53. package/dist/peginInput-C2QPvuhR.js.map +1 -0
  54. package/dist/peginInput-tbw9BpZy.cjs +2 -0
  55. package/dist/peginInput-tbw9BpZy.cjs.map +1 -0
  56. package/dist/peginState-BijNNT15.cjs +2 -0
  57. package/dist/peginState-BijNNT15.cjs.map +1 -0
  58. package/dist/peginState-CBAlxgXk.js +51 -0
  59. package/dist/peginState-CBAlxgXk.js.map +1 -0
  60. package/dist/psbtInputFields-2224j2ZY.js +128 -0
  61. package/dist/psbtInputFields-2224j2ZY.js.map +1 -0
  62. package/dist/psbtInputFields-B1lrwYzH.cjs +2 -0
  63. package/dist/psbtInputFields-B1lrwYzH.cjs.map +1 -0
  64. package/dist/reservation-CHUGW0F_.js +142 -0
  65. package/dist/reservation-CHUGW0F_.js.map +1 -0
  66. package/dist/reservation-ho7mjW3X.cjs +2 -0
  67. package/dist/reservation-ho7mjW3X.cjs.map +1 -0
  68. package/dist/signing-Bnsro0hE.cjs +2 -0
  69. package/dist/signing-Bnsro0hE.cjs.map +1 -0
  70. package/dist/signing-DaLvGwQe.js +16 -0
  71. package/dist/signing-DaLvGwQe.js.map +1 -0
  72. package/dist/tbv/core/clients/index.cjs +1 -1
  73. package/dist/tbv/core/clients/index.js +31 -30
  74. package/dist/tbv/core/clients/index.js.map +1 -1
  75. package/dist/tbv/core/contracts/index.cjs +2 -0
  76. package/dist/tbv/core/contracts/index.cjs.map +1 -0
  77. package/dist/tbv/core/contracts/index.js +14 -0
  78. package/dist/tbv/core/contracts/index.js.map +1 -0
  79. package/dist/tbv/core/index.cjs +1 -1
  80. package/dist/tbv/core/index.js +156 -146
  81. package/dist/tbv/core/index.js.map +1 -1
  82. package/dist/tbv/core/managers/PayoutManager.d.ts +8 -4
  83. package/dist/tbv/core/managers/PayoutManager.d.ts.map +1 -1
  84. package/dist/tbv/core/managers/PeginManager.d.ts +15 -1
  85. package/dist/tbv/core/managers/PeginManager.d.ts.map +1 -1
  86. package/dist/tbv/core/managers/index.cjs +2 -0
  87. package/dist/tbv/core/managers/index.cjs.map +1 -0
  88. package/dist/tbv/core/managers/index.js +7 -0
  89. package/dist/tbv/core/managers/index.js.map +1 -0
  90. package/dist/tbv/core/primitives/index.cjs +1 -1
  91. package/dist/tbv/core/primitives/index.js +31 -29
  92. package/dist/tbv/core/primitives/index.js.map +1 -1
  93. package/dist/tbv/core/primitives/utils/bitcoin.d.ts +6 -4
  94. package/dist/tbv/core/primitives/utils/bitcoin.d.ts.map +1 -1
  95. package/dist/tbv/core/services/index.cjs +1 -1
  96. package/dist/tbv/core/services/index.js +28 -27
  97. package/dist/tbv/core/services/index.js.map +1 -1
  98. package/dist/tbv/core/utils/index.cjs +1 -1
  99. package/dist/tbv/core/utils/index.js +21 -20
  100. package/dist/tbv/core/utils/index.js.map +1 -1
  101. package/dist/tbv/index.cjs +1 -1
  102. package/dist/tbv/index.js +156 -146
  103. package/dist/tbv/index.js.map +1 -1
  104. package/dist/testing/index.cjs +1 -1
  105. package/dist/testing/index.js +1 -1
  106. package/dist/types-ByW6nSLj.js +318 -0
  107. package/dist/types-ByW6nSLj.js.map +1 -0
  108. package/dist/types-CfCZyfid.cjs +2 -0
  109. package/dist/types-CfCZyfid.cjs.map +1 -0
  110. package/dist/vault-registry-reader-OK2V08tk.cjs +2 -0
  111. package/dist/vault-registry-reader-OK2V08tk.cjs.map +1 -0
  112. package/dist/{vault-registry-reader-eiBfG4uQ.js → vault-registry-reader-WiNVOsnD.js} +263 -840
  113. package/dist/vault-registry-reader-WiNVOsnD.js.map +1 -0
  114. package/package.json +11 -1
  115. package/dist/PeginManager-BtXhdqkm.js.map +0 -1
  116. package/dist/PeginManager-ztgWQqza.cjs +0 -2
  117. package/dist/PeginManager-ztgWQqza.cjs.map +0 -1
  118. package/dist/bitcoin-0_T6KJON.js.map +0 -1
  119. package/dist/bitcoin-EYBKDtEW.cjs +0 -2
  120. package/dist/bitcoin-EYBKDtEW.cjs.map +0 -1
  121. package/dist/buildAndBroadcastRefund-Ci_pVTNu.js +0 -965
  122. package/dist/buildAndBroadcastRefund-Ci_pVTNu.js.map +0 -1
  123. package/dist/buildAndBroadcastRefund-DKr9hbDn.cjs +0 -2
  124. package/dist/buildAndBroadcastRefund-DKr9hbDn.cjs.map +0 -1
  125. package/dist/challengeAssert-06GLZtV8.cjs +0 -2
  126. package/dist/challengeAssert-06GLZtV8.cjs.map +0 -1
  127. package/dist/challengeAssert-KGVKQh0J.js +0 -298
  128. package/dist/challengeAssert-KGVKQh0J.js.map +0 -1
  129. package/dist/noPayout-BmMd4NNH.js +0 -327
  130. package/dist/noPayout-BmMd4NNH.js.map +0 -1
  131. package/dist/noPayout-Bp2TYA_X.cjs +0 -2
  132. package/dist/noPayout-Bp2TYA_X.cjs.map +0 -1
  133. package/dist/psbtInputFields-BLi7Ta-T.cjs +0 -2
  134. package/dist/psbtInputFields-BLi7Ta-T.cjs.map +0 -1
  135. package/dist/psbtInputFields-DPCFHgGd.js +0 -264
  136. package/dist/psbtInputFields-DPCFHgGd.js.map +0 -1
  137. package/dist/signing-BZigafm0.js +0 -64
  138. package/dist/signing-BZigafm0.js.map +0 -1
  139. package/dist/signing-DHSXjhLM.cjs +0 -2
  140. package/dist/signing-DHSXjhLM.cjs.map +0 -1
  141. package/dist/types-PthVHz5q.cjs +0 -2
  142. package/dist/types-PthVHz5q.cjs.map +0 -1
  143. package/dist/types-SYvSQWnc.js +0 -732
  144. package/dist/types-SYvSQWnc.js.map +0 -1
  145. package/dist/vault-registry-reader-CmDdymw4.cjs +0 -2
  146. package/dist/vault-registry-reader-CmDdymw4.cjs.map +0 -1
  147. package/dist/vault-registry-reader-eiBfG4uQ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peginInput-tbw9BpZy.cjs","sources":["../src/tbv/core/primitives/psbt/pegin.ts","../src/tbv/core/primitives/psbt/peginInput.ts"],"sourcesContent":["/**\n * Pre-PegIn PSBT Builder Primitive\n *\n * This module provides pure functions for building unfunded Pre-PegIn transactions\n * and deriving PegIn transactions from them, using the WASM implementation from\n * @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * Pre-PegIn Flow:\n * 1. buildPrePeginPsbt() — creates unfunded Pre-PegIn tx (HTLC output)\n * 2. [caller funds Pre-PegIn tx and computes txid]\n * 3. buildPeginTxFromFundedPrePegin() — derives PegIn tx spending the HTLC\n * 4. buildPeginInputPsbt() — PSBT for depositor to sign PegIn HTLC leaf 0 input\n *\n * @module primitives/psbt/pegin\n */\n\nimport {\n buildPeginTxFromPrePegin,\n createPrePeginTransaction,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport { parseUnfundedWasmTransaction } from \"../../utils/transaction/fundPeginTransaction\";\n\n/**\n * Parameters for building an unfunded Pre-PegIn PSBT\n */\nexport interface PrePeginParams {\n /** Depositor's BTC public key (x-only, 64-char hex without 0x prefix) */\n depositorPubkey: string;\n /** Vault provider's BTC public key (x-only, 64-char hex) */\n vaultProviderPubkey: string;\n /** Array of vault keeper BTC public keys (x-only, 64-char hex) */\n vaultKeeperPubkeys: string[];\n /** Array of universal challenger BTC public keys (x-only, 64-char hex) */\n universalChallengerPubkeys: string[];\n /** SHA256 hash commitment(s) (64 hex chars = 32 bytes each) */\n hashlocks: readonly string[];\n /** CSV timelock in blocks for the HTLC refund path */\n timelockRefund: number;\n /** Amounts to peg in (satoshis), one per deposit */\n pegInAmounts: readonly bigint[];\n /** Fee rate in sat/vB from contract offchain params */\n feeRate: bigint;\n /** Number of local challengers (from contract params) */\n numLocalChallengers: number;\n /** M in M-of-N council multisig (from contract params) */\n councilQuorum: number;\n /** N in M-of-N council multisig (from contract params) */\n councilSize: number;\n /** Bitcoin network */\n network: Network;\n /**\n * Optional 32-byte `SHA256(auth_anchor)` commitment (64-char hex, no\n * `0x` prefix). If provided, the Pre-PegIn tx will include an\n * `OP_RETURN <PUSH32 authAnchorHash>` output at vout =\n * `hashlocks.length`, binding the depositor's bearer-token\n * `auth_anchor` preimage to this Pre-PegIn.\n */\n authAnchorHash?: string;\n}\n\n/**\n * Byte length of an `auth_anchor_hash` commitment when encoded as a\n * lowercase hex string (32 bytes → 64 hex chars).\n */\nconst AUTH_ANCHOR_HASH_HEX_LEN = 64;\n\nconst HEX_PATTERN = /^[0-9a-fA-F]+$/;\n\n/**\n * Result of building an unfunded Pre-PegIn transaction\n */\nexport interface PrePeginPsbtResult {\n /**\n * Unfunded transaction hex (no inputs, HTLC outputs + optional\n * auth-anchor OP_RETURN + CPFP anchor).\n *\n * The caller is responsible for:\n * - Selecting UTXOs covering totalOutputValue + network fees\n * - Funding the transaction (add inputs and change output)\n * - Calling buildPeginTxFromFundedPrePegin() with the funded tx hex\n */\n psbtHex: string;\n /** Sum of all unfunded outputs — use this for UTXO selection */\n totalOutputValue: bigint;\n /** HTLC output values in satoshis, one per deposit (each includes peginAmount + depositorClaimValue + minPeginFee) */\n htlcValues: readonly bigint[];\n /** HTLC output scriptPubKeys (hex encoded), one per deposit */\n htlcScriptPubKeys: readonly string[];\n /** HTLC Taproot addresses, one per deposit */\n htlcAddresses: readonly string[];\n /** Pegin amounts in satoshis, one per deposit */\n peginAmounts: readonly bigint[];\n /** Depositor claim value computed by WASM from contract parameters */\n depositorClaimValue: bigint;\n /**\n * Vout index of the auth-anchor `OP_RETURN` output if one was\n * included (i.e. `authAnchorHash` was provided), or `null` if not.\n * Always equals `htlcValues.length` when present.\n */\n authAnchorVout: number | null;\n}\n\n/**\n * Parameters for building the PegIn transaction from a funded Pre-PegIn tx\n */\nexport interface BuildPeginTxParams {\n /** Same PrePeginParams used to create the Pre-PegIn transaction */\n prePeginParams: PrePeginParams;\n /** CSV timelock in blocks for the PegIn vault output */\n timelockPegin: number;\n /** Hex-encoded funded Pre-PegIn transaction */\n fundedPrePeginTxHex: string;\n /** Index of the HTLC output to spend */\n htlcVout: number;\n}\n\n/**\n * Result of building the PegIn transaction\n */\nexport interface PeginTxResult {\n /** PegIn transaction hex (1 input spending HTLC, 1 vault output) */\n txHex: string;\n /** PegIn transaction ID */\n txid: string;\n /** Vault output scriptPubKey (hex encoded) */\n vaultScriptPubKey: string;\n /** Vault output value in satoshis */\n vaultValue: bigint;\n}\n\n/**\n * Build unfunded Pre-PegIn transaction using WASM.\n *\n * Creates a Bitcoin transaction template with no inputs, an HTLC output, and a\n * CPFP anchor output. The HTLC value is computed internally from the contract\n * parameters — the caller does not need to compute depositorClaimValue separately.\n *\n * @param params - Pre-PegIn parameters\n * @returns Unfunded Pre-PegIn transaction details with HTLC output information\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPrePeginPsbt(\n params: PrePeginParams,\n): Promise<PrePeginPsbtResult> {\n const authAnchorHash = normalizeAuthAnchorHash(params.authAnchorHash);\n\n const result = await createPrePeginTransaction({\n depositorPubkey: params.depositorPubkey,\n vaultProviderPubkey: params.vaultProviderPubkey,\n vaultKeeperPubkeys: params.vaultKeeperPubkeys,\n universalChallengerPubkeys: params.universalChallengerPubkeys,\n hashlocks: [...params.hashlocks],\n timelockRefund: params.timelockRefund,\n pegInAmounts: [...params.pegInAmounts],\n feeRate: params.feeRate,\n numLocalChallengers: params.numLocalChallengers,\n councilQuorum: params.councilQuorum,\n councilSize: params.councilSize,\n network: params.network,\n authAnchorHash,\n });\n\n // Parse the unfunded tx to sum all output values\n // (HTLCs + optional OP_RETURN + CPFP anchor). This is the amount\n // UTXOs must cover before adding network fees.\n const parsed = parseUnfundedWasmTransaction(result.txHex);\n const totalOutputValue = parsed.outputs.reduce(\n (sum, o) => sum + BigInt(o.value),\n 0n,\n );\n\n // The WASM places the OP_RETURN commitment immediately after the\n // HTLC outputs when authAnchorHash is provided.\n const authAnchorVout =\n authAnchorHash !== undefined ? result.htlcValues.length : null;\n\n return {\n psbtHex: result.txHex,\n totalOutputValue,\n htlcValues: result.htlcValues,\n htlcScriptPubKeys: result.htlcScriptPubKeys,\n htlcAddresses: result.htlcAddresses,\n peginAmounts: result.peginAmounts,\n depositorClaimValue: result.depositorClaimValue,\n authAnchorVout,\n };\n}\n\n/**\n * Validate and normalize an `authAnchorHash` hex string before passing\n * it to the WASM boundary. WASM expects exactly 64 lowercase hex chars.\n */\nfunction normalizeAuthAnchorHash(\n value: string | undefined,\n): string | undefined {\n if (value === undefined) return undefined;\n const cleaned =\n value.startsWith(\"0x\") || value.startsWith(\"0X\") ? value.slice(2) : value;\n if (\n cleaned.length !== AUTH_ANCHOR_HASH_HEX_LEN ||\n !HEX_PATTERN.test(cleaned)\n ) {\n throw new Error(\n `authAnchorHash must be 32-byte hex (${AUTH_ANCHOR_HASH_HEX_LEN} chars, no 0x prefix); got length ${cleaned.length}`,\n );\n }\n return cleaned.toLowerCase();\n}\n\n/**\n * Build the PegIn transaction from a funded Pre-PegIn transaction.\n *\n * The PegIn transaction spends the Pre-PegIn HTLC output at htlcVout via the\n * hashlock + all-party script (leaf 0).\n *\n * @param params - Build parameters including Pre-PegIn params and funded tx hex\n * @returns PegIn transaction details\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPeginTxFromFundedPrePegin(\n params: BuildPeginTxParams,\n): Promise<PeginTxResult> {\n // WASM reconstructs the Pre-PegIn template from these params to\n // decode the funded tx. Must pass `authAnchorHash` (normalized\n // identically to buildPrePeginPsbt) so the reconstruction matches\n // the original outputs, including the OP_RETURN at vout =\n // hashlocks.length.\n const result = await buildPeginTxFromPrePegin(\n {\n depositorPubkey: params.prePeginParams.depositorPubkey,\n vaultProviderPubkey: params.prePeginParams.vaultProviderPubkey,\n vaultKeeperPubkeys: params.prePeginParams.vaultKeeperPubkeys,\n universalChallengerPubkeys:\n params.prePeginParams.universalChallengerPubkeys,\n hashlocks: [...params.prePeginParams.hashlocks],\n timelockRefund: params.prePeginParams.timelockRefund,\n pegInAmounts: [...params.prePeginParams.pegInAmounts],\n feeRate: params.prePeginParams.feeRate,\n numLocalChallengers: params.prePeginParams.numLocalChallengers,\n councilQuorum: params.prePeginParams.councilQuorum,\n councilSize: params.prePeginParams.councilSize,\n network: params.prePeginParams.network,\n authAnchorHash: normalizeAuthAnchorHash(\n params.prePeginParams.authAnchorHash,\n ),\n },\n params.timelockPegin,\n params.fundedPrePeginTxHex,\n params.htlcVout,\n );\n\n return {\n txHex: result.txHex,\n txid: result.txid,\n vaultScriptPubKey: result.vaultScriptPubKey,\n vaultValue: result.vaultValue,\n };\n}\n","/**\n * PegIn Input PSBT Builder\n *\n * Builds the PSBT for the depositor to sign the PegIn transaction's HTLC input\n * (Pre-PegIn HTLC leaf 0 — the hashlock + all-party script).\n *\n * This is the \"Sign Pegin transaction HTLC leaf 0 input\" step in the pre-pegin\n * flow. The depositor signs input 0 of the PegIn transaction,\n * which spends output 0 of the funded Pre-PegIn transaction via script-path.\n *\n * @module primitives/psbt/peginInput\n */\n\nimport {\n getPrePeginHtlcConnectorInfo,\n tapInternalPubkey,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Buffer } from \"buffer\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\nimport { TAPSCRIPT_LEAF_VERSION, hexToUint8Array, stripHexPrefix, uint8ArrayToHex } from \"../utils/bitcoin\";\n\n/**\n * Parameters for building the PegIn input PSBT\n */\nexport interface BuildPeginInputPsbtParams {\n /**\n * PegIn transaction hex (1 input spending Pre-PegIn HTLC output 0).\n * Returned by buildPeginTxFromFundedPrePegin().\n */\n peginTxHex: string;\n /**\n * Funded Pre-PegIn transaction hex.\n * Used to look up the HTLC output that the PegIn input spends.\n */\n fundedPrePeginTxHex: string;\n /** Depositor's BTC public key (x-only, 64-char hex) */\n depositorPubkey: string;\n /** Vault provider's BTC public key (x-only, 64-char hex) */\n vaultProviderPubkey: string;\n /** Vault keeper BTC public keys (x-only, 64-char hex) */\n vaultKeeperPubkeys: string[];\n /** Universal challenger BTC public keys (x-only, 64-char hex) */\n universalChallengerPubkeys: string[];\n /** SHA256 hash commitment (64 hex chars = 32 bytes) */\n hashlock: string;\n /** CSV timelock in blocks for the HTLC refund path */\n timelockRefund: number;\n /** Bitcoin network */\n network: Network;\n}\n\n/**\n * Result of building the PegIn input PSBT\n */\nexport interface BuildPeginInputPsbtResult {\n /** PSBT hex for the depositor to sign */\n psbtHex: string;\n}\n\n/**\n * Build PSBT for depositor to sign the PegIn transaction's HTLC leaf 0 input.\n *\n * The PegIn transaction spends the Pre-PegIn HTLC output (output 0) via the\n * hashlock + all-party script (leaf 0). The depositor provides one of the required\n * signatures; the vault provider and keepers provide theirs separately via the\n * signPeginInput RPC.\n *\n * The PSBT uses Taproot script-path spending:\n * - witnessUtxo: the Pre-PegIn HTLC output\n * - tapLeafScript: hashlock leaf script + control block\n * - tapInternalKey: NUMS unspendable key (BIP-341 nothing-up-my-sleeve)\n *\n * @param params - PegIn input PSBT parameters\n * @returns PSBT hex ready for depositor signing\n * @throws If PegIn tx does not have exactly 1 input\n * @throws If PegIn input does not reference the Pre-PegIn HTLC output\n * @throws If Pre-PegIn tx output 0 is not found\n */\nexport async function buildPeginInputPsbt(\n params: BuildPeginInputPsbtParams,\n): Promise<BuildPeginInputPsbtResult> {\n const peginTxHex = stripHexPrefix(params.peginTxHex);\n const fundedPrePeginTxHex = stripHexPrefix(params.fundedPrePeginTxHex);\n\n const htlcConnector = await getPrePeginHtlcConnectorInfo({\n depositorPubkey: params.depositorPubkey,\n vaultProviderPubkey: params.vaultProviderPubkey,\n vaultKeeperPubkeys: params.vaultKeeperPubkeys,\n universalChallengerPubkeys: params.universalChallengerPubkeys,\n hashlock: params.hashlock,\n timelockRefund: params.timelockRefund,\n network: params.network,\n });\n\n const peginTx = Transaction.fromHex(peginTxHex);\n const prePeginTx = Transaction.fromHex(fundedPrePeginTxHex);\n\n if (peginTx.ins.length !== 1) {\n throw new Error(\n `PegIn transaction must have exactly 1 input, got ${peginTx.ins.length}`,\n );\n }\n\n const peginInput = peginTx.ins[0];\n\n // Verify PegIn input 0 spends Pre-PegIn output 0\n const prePeginTxid = prePeginTx.getId();\n const peginInputTxid = uint8ArrayToHex(\n new Uint8Array(peginInput.hash).slice().reverse(),\n );\n\n if (peginInputTxid !== prePeginTxid) {\n throw new Error(\n `PegIn input does not reference the Pre-PegIn transaction. ` +\n `Expected ${prePeginTxid}, got ${peginInputTxid}`,\n );\n }\n\n const htlcOutput = prePeginTx.outs[peginInput.index];\n if (!htlcOutput) {\n throw new Error(\n `Pre-PegIn output ${peginInput.index} not found ` +\n `(Pre-PegIn has ${prePeginTx.outs.length} outputs)`,\n );\n }\n\n const hashlockScript = hexToUint8Array(htlcConnector.hashlockScript);\n const hashlockControlBlock = hexToUint8Array(htlcConnector.hashlockControlBlock);\n\n const psbt = new Psbt();\n psbt.setVersion(peginTx.version);\n psbt.setLocktime(peginTx.locktime);\n\n // Input 0: PegIn input spending Pre-PegIn HTLC output 0 via hashlock leaf (leaf 0).\n // The depositor signs using Taproot script-path spending.\n psbt.addInput({\n hash: peginInput.hash,\n index: peginInput.index,\n sequence: peginInput.sequence,\n witnessUtxo: {\n script: htlcOutput.script,\n value: htlcOutput.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(hashlockScript),\n controlBlock: Buffer.from(hashlockControlBlock),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n // sighashType omitted — defaults to SIGHASH_DEFAULT (0x00) for Taproot\n });\n\n for (const output of peginTx.outs) {\n psbt.addOutput({\n script: output.script,\n value: output.value,\n });\n }\n\n return { psbtHex: psbt.toHex() };\n}\n\n/**\n * Extract the depositor's Schnorr signature from a signed PegIn input PSBT.\n *\n * Supports non-finalized PSBTs with tapScriptSig entries. Finalized PSBTs are\n * rejected because the witness stack does not reliably identify the depositor\n * signature by public key.\n *\n * PegIn input signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte. Signatures with an appended sighash byte\n * are rejected rather than stripped.\n *\n * @param signedPsbtHex - Signed PSBT hex\n * @param depositorPubkey - Depositor's x-only public key (64-char hex)\n * @returns 64-byte Schnorr signature (128 hex chars, no sighash flag)\n * @throws If no signature is found for the depositor's key\n */\nexport function extractPeginInputSignature(\n signedPsbtHex: string,\n depositorPubkey: string,\n): string {\n const signedPsbt = Psbt.fromHex(signedPsbtHex);\n const input = signedPsbt.data.inputs[0];\n\n if (!input) {\n throw new Error(\"PegIn PSBT has no inputs\");\n }\n\n // Non-finalized PSBT — extract from tapScriptSig\n if (input.tapScriptSig && input.tapScriptSig.length > 0) {\n const depositorPubkeyBytes = Buffer.from(\n hexToUint8Array(depositorPubkey),\n );\n\n for (const sigEntry of input.tapScriptSig) {\n if (sigEntry.pubkey.equals(depositorPubkeyBytes)) {\n return extractSchnorrSig(sigEntry.signature);\n }\n }\n\n throw new Error(\n `No PegIn input signature found for depositor pubkey: ${depositorPubkey}`,\n );\n }\n\n // Finalized PSBT — the witness stack order depends on the wallet's finalizer,\n // so we cannot reliably pick the depositor's signature by position. Require\n // the non-finalized tapScriptSig path which identifies signatures by pubkey.\n if (input.finalScriptWitness && input.finalScriptWitness.length > 0) {\n throw new Error(\n \"PegIn input PSBT is already finalized. Cannot reliably extract the \" +\n \"depositor signature from the witness stack. Ensure the wallet returns \" +\n \"a non-finalized PSBT with tapScriptSig entries.\",\n );\n }\n\n throw new Error(\n \"No tapScriptSig or finalScriptWitness found in signed PegIn input PSBT\",\n );\n}\n\n/**\n * Finalize a signed PegIn input PSBT and return the depositor-signed transaction hex.\n *\n * The default tapscript finalizer builds the full witness stack [sig, script, controlBlock]\n * that vaultd requires when verifying the depositor signature on-chain.\n *\n * @param signedPsbtHex - Non-finalized signed PSBT hex (returned by wallet with autoFinalized: false)\n * @returns Depositor-signed PegIn transaction hex with full taproot witness stack\n */\nexport function finalizePeginInputPsbt(signedPsbtHex: string): string {\n const psbt = Psbt.fromHex(signedPsbtHex);\n\n // Some wallets (UniSat, OKX) ignore autoFinalized: false and return\n // already-finalized PSBTs. finalizeAllInputs() throws in that case,\n // so fall back to verifying the wallet already finalized all inputs.\n try {\n psbt.finalizeAllInputs();\n } catch (e) {\n const allFinalized = psbt.data.inputs.every(\n (inp) => inp.finalScriptWitness || inp.finalScriptSig,\n );\n if (!allFinalized) {\n throw new Error(\n `PSBT finalization failed and wallet did not auto-finalize: ${e}`,\n );\n }\n }\n\n return psbt.extractTransaction().toHex();\n}\n\n/**\n * Extract and validate a 64-byte Schnorr signature.\n * PegIn input signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte. Reject 65-byte signatures instead of\n * stripping the sighash byte because it changes the signed Taproot message.\n * @internal\n */\nexport function extractSchnorrSig(sig: Uint8Array): string {\n if (sig.length === 64) {\n return uint8ArrayToHex(new Uint8Array(sig));\n }\n if (sig.length === 65) {\n throw new Error(\n `Unexpected sighash byte 0x${sig[64].toString(16).padStart(2, \"0\")} in PegIn input signature. ` +\n \"Expected implicit SIGHASH_DEFAULT as a 64-byte signature.\",\n );\n }\n throw new Error(`Unexpected PegIn input signature length: ${sig.length}`);\n}\n"],"names":["AUTH_ANCHOR_HASH_HEX_LEN","HEX_PATTERN","buildPrePeginPsbt","params","authAnchorHash","normalizeAuthAnchorHash","result","createPrePeginTransaction","totalOutputValue","parseUnfundedWasmTransaction","sum","o","authAnchorVout","value","cleaned","buildPeginTxFromFundedPrePegin","buildPeginTxFromPrePegin","buildPeginInputPsbt","peginTxHex","stripHexPrefix","fundedPrePeginTxHex","htlcConnector","getPrePeginHtlcConnectorInfo","peginTx","Transaction","prePeginTx","peginInput","prePeginTxid","peginInputTxid","uint8ArrayToHex","htlcOutput","hashlockScript","hexToUint8Array","hashlockControlBlock","psbt","Psbt","TAPSCRIPT_LEAF_VERSION","Buffer","tapInternalPubkey","output","extractPeginInputSignature","signedPsbtHex","depositorPubkey","input","depositorPubkeyBytes","sigEntry","extractSchnorrSig","finalizePeginInputPsbt","e","inp","sig"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";var I=(i=>(i[i.PENDING=0]="PENDING",i[i.VERIFIED=1]="VERIFIED",i[i.ACTIVE=2]="ACTIVE",i[i.REDEEMED=3]="REDEEMED",i[i.LIQUIDATED=4]="LIQUIDATED",i[i.INVALID=5]="INVALID",i[i.DEPOSITOR_WITHDRAWN=6]="DEPOSITOR_WITHDRAWN",i[i.EXPIRED=7]="EXPIRED",i))(I||{}),l=(i=>(i.SUBMIT_WOTS_KEY="SUBMIT_WOTS_KEY",i.SIGN_PAYOUT_TRANSACTIONS="SIGN_PAYOUT_TRANSACTIONS",i.SIGN_AND_BROADCAST_TO_BITCOIN="SIGN_AND_BROADCAST_TO_BITCOIN",i.ACTIVATE_VAULT="ACTIVATE_VAULT",i.REFUND_HTLC="REFUND_HTLC",i))(l||{});function N(i,e={}){const{transactionsReady:A,needsWotsKey:E,pendingIngestion:T,canRefund:_,hasProviderTerminalFailure:D}=e;return i===0?D?{contractStatus:i,availableActions:[]}:E?{contractStatus:i,availableActions:["SUBMIT_WOTS_KEY"]}:T===!0&&!A?{contractStatus:i,availableActions:["SIGN_AND_BROADCAST_TO_BITCOIN"]}:T===void 0&&!A?{contractStatus:i,availableActions:[]}:A?{contractStatus:i,availableActions:["SIGN_PAYOUT_TRANSACTIONS"]}:{contractStatus:i,availableActions:[]}:i===1?{contractStatus:i,availableActions:["ACTIVATE_VAULT"]}:i===2?{contractStatus:i,availableActions:[]}:i===3?{contractStatus:i,availableActions:[]}:i===4?{contractStatus:i,availableActions:[]}:i===7?{contractStatus:i,availableActions:_?["REFUND_HTLC"]:[]}:i===5?{contractStatus:i,availableActions:[]}:i===6?{contractStatus:i,availableActions:[]}:{contractStatus:i,availableActions:[]}}function O(i,e){return i.availableActions.includes(e)}exports.ContractStatus=I;exports.PeginAction=l;exports.canPerformAction=O;exports.getPeginProtocolState=N;
2
+ //# sourceMappingURL=peginState-BijNNT15.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peginState-BijNNT15.cjs","sources":["../src/tbv/core/services/deposit/peginState.ts"],"sourcesContent":["// ============================================================================\n// State Definitions\n// ============================================================================\n\n/**\n * Vault status — combines on-chain contract status (0-4) with indexer-derived\n * statuses (5-7). The contract enum (BTCVaultRegistry.sol BTCVaultStatus) only\n * has: Pending(0), Verified(1), Active(2), Redeemed(3), Expired(4).\n * The indexer maps these and adds extra statuses for UI display.\n *\n * IMPORTANT: With the new contract architecture:\n * - Core vault status (BTCVaultRegistry) does NOT change when used by applications\n * - Vaults remain at ACTIVE status even when used in DeFi positions\n * - Application usage status is tracked separately by each integration controller\n */\nexport enum ContractStatus {\n /** Status 0: Request submitted, waiting for ACKs */\n PENDING = 0,\n /** Status 1: All ACKs collected, ready for secret activation */\n VERIFIED = 1,\n /** Status 2: HTLC secret revealed, vault is active and usable (stays here even when used by apps) */\n ACTIVE = 2,\n /** Status 3: Vault has been redeemed, BTC is claimable */\n REDEEMED = 3,\n /** Status 4 (indexer-only): Vault was liquidated (collateral seized due to unpaid debt) */\n LIQUIDATED = 4,\n /** Status 5 (indexer-only): Vault is invalid — BTC UTXOs were spent in a different transaction */\n INVALID = 5,\n /** Status 6 (indexer-only): Depositor has withdrawn their BTC (redemption complete) */\n DEPOSITOR_WITHDRAWN = 6,\n /** Status 7 (indexer-only): Vault expired due to AckTimeout or ActivationTimeout */\n EXPIRED = 7,\n}\n\n/** Reason why a vault expired */\nexport type ExpirationReason =\n | \"ack_timeout\"\n | \"proof_timeout\"\n | \"activation_timeout\";\n\n// ============================================================================\n// Protocol State Model\n// ============================================================================\n\n/**\n * Available actions user can take\n */\nexport enum PeginAction {\n /** Submit WOTS key (re-derives via wallet `deriveContextHash`) */\n SUBMIT_WOTS_KEY = \"SUBMIT_WOTS_KEY\",\n /** Sign payout transactions */\n SIGN_PAYOUT_TRANSACTIONS = \"SIGN_PAYOUT_TRANSACTIONS\",\n /** Sign and broadcast peg-in transaction to Bitcoin */\n SIGN_AND_BROADCAST_TO_BITCOIN = \"SIGN_AND_BROADCAST_TO_BITCOIN\",\n /** Reveal HTLC secret on Ethereum to activate vault */\n ACTIVATE_VAULT = \"ACTIVATE_VAULT\",\n /** Sign and broadcast HTLC refund transaction for an expired vault */\n REFUND_HTLC = \"REFUND_HTLC\",\n}\n\n/**\n * Protocol-level peg-in state (framework-agnostic)\n */\nexport interface PeginProtocolState {\n /** Smart contract status (source of truth for on-chain state) */\n contractStatus: ContractStatus;\n /** Available user actions (empty array when no action is available) */\n availableActions: PeginAction[];\n}\n\n/**\n * Options for getPeginProtocolState function.\n *\n * All fields represent protocol-level state from the vault provider or\n * on-chain contracts. Client-side tracking (localStorage, polling state)\n * is NOT included — consumers handle that in their own layer.\n */\nexport interface GetPeginProtocolStateOptions {\n /** Whether claim/payout transactions are ready from VP */\n transactionsReady?: boolean;\n /** Whether the vault provider is waiting for the depositor's WOTS public key */\n needsWotsKey?: boolean;\n /** Whether the vault provider hasn't ingested this peg-in yet */\n pendingIngestion?: boolean;\n /** Whether the depositor can refund the HTLC (Pre-PegIn tx available) */\n canRefund?: boolean;\n /** Whether the vault provider reported a terminal failure */\n hasProviderTerminalFailure?: boolean;\n}\n\n// ============================================================================\n// State Machine Logic\n// ============================================================================\n\n/**\n * Determine the current protocol state and available actions based on contract\n * status and vault provider state. Framework-agnostic: returns only\n * protocol-level data with no display labels, messages, or UI concerns.\n *\n * Client-side tracking overrides (e.g. suppressing actions after the user\n * has already acted but on-chain state hasn't caught up) are the caller's\n * responsibility.\n *\n * @param contractStatus - On-chain contract status (source of truth)\n * @param options - Vault provider state\n * @returns Protocol state with available actions\n */\nexport function getPeginProtocolState(\n contractStatus: ContractStatus,\n options: GetPeginProtocolStateOptions = {},\n): PeginProtocolState {\n const {\n transactionsReady,\n needsWotsKey,\n pendingIngestion,\n canRefund,\n hasProviderTerminalFailure,\n } = options;\n\n if (contractStatus === ContractStatus.PENDING) {\n if (hasProviderTerminalFailure) {\n return { contractStatus, availableActions: [] };\n }\n\n if (needsWotsKey) {\n return {\n contractStatus,\n availableActions: [PeginAction.SUBMIT_WOTS_KEY],\n };\n }\n\n if (pendingIngestion === true && !transactionsReady) {\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_AND_BROADCAST_TO_BITCOIN],\n };\n }\n\n if (pendingIngestion === undefined && !transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n if (!transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_PAYOUT_TRANSACTIONS],\n };\n }\n\n if (contractStatus === ContractStatus.VERIFIED) {\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_VAULT],\n };\n }\n\n if (contractStatus === ContractStatus.ACTIVE) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.REDEEMED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.LIQUIDATED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.EXPIRED) {\n return {\n contractStatus,\n availableActions: canRefund ? [PeginAction.REFUND_HTLC] : [],\n };\n }\n\n if (contractStatus === ContractStatus.INVALID) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.DEPOSITOR_WITHDRAWN) {\n return { contractStatus, availableActions: [] };\n }\n\n return { contractStatus, availableActions: [] };\n}\n\n/**\n * Check if a specific action is available in the current state\n */\nexport function canPerformAction(\n state: PeginProtocolState,\n action: PeginAction,\n): boolean {\n return state.availableActions.includes(action);\n}\n"],"names":["ContractStatus","PeginAction","getPeginProtocolState","contractStatus","options","transactionsReady","needsWotsKey","pendingIngestion","canRefund","hasProviderTerminalFailure","canPerformAction","state","action"],"mappings":"aAeO,IAAKA,GAAAA,IAEVA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAEAA,EAAAA,EAAA,SAAW,CAAA,EAAX,WAEAA,EAAAA,EAAA,OAAS,CAAA,EAAT,SAEAA,EAAAA,EAAA,SAAW,CAAA,EAAX,WAEAA,EAAAA,EAAA,WAAa,CAAA,EAAb,aAEAA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAEAA,EAAAA,EAAA,oBAAsB,CAAA,EAAtB,sBAEAA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAhBUA,IAAAA,GAAA,CAAA,CAAA,EAgCAC,GAAAA,IAEVA,EAAA,gBAAkB,kBAElBA,EAAA,yBAA2B,2BAE3BA,EAAA,8BAAgC,gCAEhCA,EAAA,eAAiB,iBAEjBA,EAAA,YAAc,cAVJA,IAAAA,GAAA,CAAA,CAAA,EA4DL,SAASC,EACdC,EACAC,EAAwC,GACpB,CACpB,KAAM,CACJ,kBAAAC,EACA,aAAAC,EACA,iBAAAC,EACA,UAAAC,EACA,2BAAAC,CAAA,EACEL,EAEJ,OAAID,IAAmB,EACjBM,EACK,CAAE,eAAAN,EAAgB,iBAAkB,EAAC,EAG1CG,EACK,CACL,eAAAH,EACA,iBAAkB,CAAC,iBAAA,CAA2B,EAI9CI,IAAqB,IAAQ,CAACF,EACzB,CACL,eAAAF,EACA,iBAAkB,CAAC,+BAAA,CAAyC,EAI5DI,IAAqB,QAAa,CAACF,EAC9B,CAAE,eAAAF,EAAgB,iBAAkB,EAAC,EAGzCE,EAIE,CACL,eAAAF,EACA,iBAAkB,CAAC,0BAAA,CAAoC,EALhD,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAS5CA,IAAmB,EACd,CACL,eAAAA,EACA,iBAAkB,CAAC,gBAAA,CAA0B,EAI7CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CACL,eAAAA,EACA,iBAAkBK,EAAY,CAAC,eAA2B,CAAA,CAAC,EAI3DL,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAGvC,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,CAC9C,CAKO,SAASO,EACdC,EACAC,EACS,CACT,OAAOD,EAAM,iBAAiB,SAASC,CAAM,CAC/C"}
@@ -0,0 +1,51 @@
1
+ var _ = /* @__PURE__ */ ((i) => (i[i.PENDING = 0] = "PENDING", i[i.VERIFIED = 1] = "VERIFIED", i[i.ACTIVE = 2] = "ACTIVE", i[i.REDEEMED = 3] = "REDEEMED", i[i.LIQUIDATED = 4] = "LIQUIDATED", i[i.INVALID = 5] = "INVALID", i[i.DEPOSITOR_WITHDRAWN = 6] = "DEPOSITOR_WITHDRAWN", i[i.EXPIRED = 7] = "EXPIRED", i))(_ || {}), D = /* @__PURE__ */ ((i) => (i.SUBMIT_WOTS_KEY = "SUBMIT_WOTS_KEY", i.SIGN_PAYOUT_TRANSACTIONS = "SIGN_PAYOUT_TRANSACTIONS", i.SIGN_AND_BROADCAST_TO_BITCOIN = "SIGN_AND_BROADCAST_TO_BITCOIN", i.ACTIVATE_VAULT = "ACTIVATE_VAULT", i.REFUND_HTLC = "REFUND_HTLC", i))(D || {});
2
+ function N(i, A = {}) {
3
+ const {
4
+ transactionsReady: e,
5
+ needsWotsKey: I,
6
+ pendingIngestion: T,
7
+ canRefund: l,
8
+ hasProviderTerminalFailure: E
9
+ } = A;
10
+ return i === 0 ? E ? { contractStatus: i, availableActions: [] } : I ? {
11
+ contractStatus: i,
12
+ availableActions: [
13
+ "SUBMIT_WOTS_KEY"
14
+ /* SUBMIT_WOTS_KEY */
15
+ ]
16
+ } : T === !0 && !e ? {
17
+ contractStatus: i,
18
+ availableActions: [
19
+ "SIGN_AND_BROADCAST_TO_BITCOIN"
20
+ /* SIGN_AND_BROADCAST_TO_BITCOIN */
21
+ ]
22
+ } : T === void 0 && !e ? { contractStatus: i, availableActions: [] } : e ? {
23
+ contractStatus: i,
24
+ availableActions: [
25
+ "SIGN_PAYOUT_TRANSACTIONS"
26
+ /* SIGN_PAYOUT_TRANSACTIONS */
27
+ ]
28
+ } : { contractStatus: i, availableActions: [] } : i === 1 ? {
29
+ contractStatus: i,
30
+ availableActions: [
31
+ "ACTIVATE_VAULT"
32
+ /* ACTIVATE_VAULT */
33
+ ]
34
+ } : i === 2 ? { contractStatus: i, availableActions: [] } : i === 3 ? { contractStatus: i, availableActions: [] } : i === 4 ? { contractStatus: i, availableActions: [] } : i === 7 ? {
35
+ contractStatus: i,
36
+ availableActions: l ? [
37
+ "REFUND_HTLC"
38
+ /* REFUND_HTLC */
39
+ ] : []
40
+ } : i === 5 ? { contractStatus: i, availableActions: [] } : i === 6 ? { contractStatus: i, availableActions: [] } : { contractStatus: i, availableActions: [] };
41
+ }
42
+ function O(i, A) {
43
+ return i.availableActions.includes(A);
44
+ }
45
+ export {
46
+ _ as C,
47
+ D as P,
48
+ O as c,
49
+ N as g
50
+ };
51
+ //# sourceMappingURL=peginState-CBAlxgXk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peginState-CBAlxgXk.js","sources":["../src/tbv/core/services/deposit/peginState.ts"],"sourcesContent":["// ============================================================================\n// State Definitions\n// ============================================================================\n\n/**\n * Vault status — combines on-chain contract status (0-4) with indexer-derived\n * statuses (5-7). The contract enum (BTCVaultRegistry.sol BTCVaultStatus) only\n * has: Pending(0), Verified(1), Active(2), Redeemed(3), Expired(4).\n * The indexer maps these and adds extra statuses for UI display.\n *\n * IMPORTANT: With the new contract architecture:\n * - Core vault status (BTCVaultRegistry) does NOT change when used by applications\n * - Vaults remain at ACTIVE status even when used in DeFi positions\n * - Application usage status is tracked separately by each integration controller\n */\nexport enum ContractStatus {\n /** Status 0: Request submitted, waiting for ACKs */\n PENDING = 0,\n /** Status 1: All ACKs collected, ready for secret activation */\n VERIFIED = 1,\n /** Status 2: HTLC secret revealed, vault is active and usable (stays here even when used by apps) */\n ACTIVE = 2,\n /** Status 3: Vault has been redeemed, BTC is claimable */\n REDEEMED = 3,\n /** Status 4 (indexer-only): Vault was liquidated (collateral seized due to unpaid debt) */\n LIQUIDATED = 4,\n /** Status 5 (indexer-only): Vault is invalid — BTC UTXOs were spent in a different transaction */\n INVALID = 5,\n /** Status 6 (indexer-only): Depositor has withdrawn their BTC (redemption complete) */\n DEPOSITOR_WITHDRAWN = 6,\n /** Status 7 (indexer-only): Vault expired due to AckTimeout or ActivationTimeout */\n EXPIRED = 7,\n}\n\n/** Reason why a vault expired */\nexport type ExpirationReason =\n | \"ack_timeout\"\n | \"proof_timeout\"\n | \"activation_timeout\";\n\n// ============================================================================\n// Protocol State Model\n// ============================================================================\n\n/**\n * Available actions user can take\n */\nexport enum PeginAction {\n /** Submit WOTS key (re-derives via wallet `deriveContextHash`) */\n SUBMIT_WOTS_KEY = \"SUBMIT_WOTS_KEY\",\n /** Sign payout transactions */\n SIGN_PAYOUT_TRANSACTIONS = \"SIGN_PAYOUT_TRANSACTIONS\",\n /** Sign and broadcast peg-in transaction to Bitcoin */\n SIGN_AND_BROADCAST_TO_BITCOIN = \"SIGN_AND_BROADCAST_TO_BITCOIN\",\n /** Reveal HTLC secret on Ethereum to activate vault */\n ACTIVATE_VAULT = \"ACTIVATE_VAULT\",\n /** Sign and broadcast HTLC refund transaction for an expired vault */\n REFUND_HTLC = \"REFUND_HTLC\",\n}\n\n/**\n * Protocol-level peg-in state (framework-agnostic)\n */\nexport interface PeginProtocolState {\n /** Smart contract status (source of truth for on-chain state) */\n contractStatus: ContractStatus;\n /** Available user actions (empty array when no action is available) */\n availableActions: PeginAction[];\n}\n\n/**\n * Options for getPeginProtocolState function.\n *\n * All fields represent protocol-level state from the vault provider or\n * on-chain contracts. Client-side tracking (localStorage, polling state)\n * is NOT included — consumers handle that in their own layer.\n */\nexport interface GetPeginProtocolStateOptions {\n /** Whether claim/payout transactions are ready from VP */\n transactionsReady?: boolean;\n /** Whether the vault provider is waiting for the depositor's WOTS public key */\n needsWotsKey?: boolean;\n /** Whether the vault provider hasn't ingested this peg-in yet */\n pendingIngestion?: boolean;\n /** Whether the depositor can refund the HTLC (Pre-PegIn tx available) */\n canRefund?: boolean;\n /** Whether the vault provider reported a terminal failure */\n hasProviderTerminalFailure?: boolean;\n}\n\n// ============================================================================\n// State Machine Logic\n// ============================================================================\n\n/**\n * Determine the current protocol state and available actions based on contract\n * status and vault provider state. Framework-agnostic: returns only\n * protocol-level data with no display labels, messages, or UI concerns.\n *\n * Client-side tracking overrides (e.g. suppressing actions after the user\n * has already acted but on-chain state hasn't caught up) are the caller's\n * responsibility.\n *\n * @param contractStatus - On-chain contract status (source of truth)\n * @param options - Vault provider state\n * @returns Protocol state with available actions\n */\nexport function getPeginProtocolState(\n contractStatus: ContractStatus,\n options: GetPeginProtocolStateOptions = {},\n): PeginProtocolState {\n const {\n transactionsReady,\n needsWotsKey,\n pendingIngestion,\n canRefund,\n hasProviderTerminalFailure,\n } = options;\n\n if (contractStatus === ContractStatus.PENDING) {\n if (hasProviderTerminalFailure) {\n return { contractStatus, availableActions: [] };\n }\n\n if (needsWotsKey) {\n return {\n contractStatus,\n availableActions: [PeginAction.SUBMIT_WOTS_KEY],\n };\n }\n\n if (pendingIngestion === true && !transactionsReady) {\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_AND_BROADCAST_TO_BITCOIN],\n };\n }\n\n if (pendingIngestion === undefined && !transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n if (!transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_PAYOUT_TRANSACTIONS],\n };\n }\n\n if (contractStatus === ContractStatus.VERIFIED) {\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_VAULT],\n };\n }\n\n if (contractStatus === ContractStatus.ACTIVE) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.REDEEMED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.LIQUIDATED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.EXPIRED) {\n return {\n contractStatus,\n availableActions: canRefund ? [PeginAction.REFUND_HTLC] : [],\n };\n }\n\n if (contractStatus === ContractStatus.INVALID) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.DEPOSITOR_WITHDRAWN) {\n return { contractStatus, availableActions: [] };\n }\n\n return { contractStatus, availableActions: [] };\n}\n\n/**\n * Check if a specific action is available in the current state\n */\nexport function canPerformAction(\n state: PeginProtocolState,\n action: PeginAction,\n): boolean {\n return state.availableActions.includes(action);\n}\n"],"names":["ContractStatus","PeginAction","getPeginProtocolState","contractStatus","options","transactionsReady","needsWotsKey","pendingIngestion","canRefund","hasProviderTerminalFailure","canPerformAction","state","action"],"mappings":"AAeO,IAAKA,sBAAAA,OAEVA,EAAAA,EAAA,UAAU,CAAA,IAAV,WAEAA,EAAAA,EAAA,WAAW,CAAA,IAAX,YAEAA,EAAAA,EAAA,SAAS,CAAA,IAAT,UAEAA,EAAAA,EAAA,WAAW,CAAA,IAAX,YAEAA,EAAAA,EAAA,aAAa,CAAA,IAAb,cAEAA,EAAAA,EAAA,UAAU,CAAA,IAAV,WAEAA,EAAAA,EAAA,sBAAsB,CAAA,IAAtB,uBAEAA,EAAAA,EAAA,UAAU,CAAA,IAAV,WAhBUA,IAAAA,KAAA,CAAA,CAAA,GAgCAC,sBAAAA,OAEVA,EAAA,kBAAkB,mBAElBA,EAAA,2BAA2B,4BAE3BA,EAAA,gCAAgC,iCAEhCA,EAAA,iBAAiB,kBAEjBA,EAAA,cAAc,eAVJA,IAAAA,KAAA,CAAA,CAAA;AA4DL,SAASC,EACdC,GACAC,IAAwC,IACpB;AACpB,QAAM;AAAA,IACJ,mBAAAC;AAAA,IACA,cAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,WAAAC;AAAA,IACA,4BAAAC;AAAA,EAAA,IACEL;AAEJ,SAAID,MAAmB,IACjBM,IACK,EAAE,gBAAAN,GAAgB,kBAAkB,GAAC,IAG1CG,IACK;AAAA,IACL,gBAAAH;AAAA,IACA,kBAAkB;AAAA,MAAC;AAAA;AAAA,IAAA;AAAA,EAA2B,IAI9CI,MAAqB,MAAQ,CAACF,IACzB;AAAA,IACL,gBAAAF;AAAA,IACA,kBAAkB;AAAA,MAAC;AAAA;AAAA,IAAA;AAAA,EAAyC,IAI5DI,MAAqB,UAAa,CAACF,IAC9B,EAAE,gBAAAF,GAAgB,kBAAkB,GAAC,IAGzCE,IAIE;AAAA,IACL,gBAAAF;AAAA,IACA,kBAAkB;AAAA,MAAC;AAAA;AAAA,IAAA;AAAA,EAAoC,IALhD,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAS5CA,MAAmB,IACd;AAAA,IACL,gBAAAA;AAAA,IACA,kBAAkB;AAAA,MAAC;AAAA;AAAA,IAAA;AAAA,EAA0B,IAI7CA,MAAmB,IACd,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAG1CA,MAAmB,IACd,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAG1CA,MAAmB,IACd,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAG1CA,MAAmB,IACd;AAAA,IACL,gBAAAA;AAAA,IACA,kBAAkBK,IAAY;AAAA,MAAC;AAAA;AAAA,QAA2B,CAAA;AAAA,EAAC,IAI3DL,MAAmB,IACd,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAG1CA,MAAmB,IACd,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC,IAGvC,EAAE,gBAAAA,GAAgB,kBAAkB,GAAC;AAC9C;AAKO,SAASO,EACdC,GACAC,GACS;AACT,SAAOD,EAAM,iBAAiB,SAASC,CAAM;AAC/C;"}
@@ -0,0 +1,128 @@
1
+ import { script as S, Transaction as v } from "bitcoinjs-lib";
2
+ import { Buffer as u } from "buffer";
3
+ import { P as U, r as H, D as w, M as g, B as I, T as x } from "./fundPeginTransaction-oV-dNJOU.js";
4
+ function E(t) {
5
+ const e = /* @__PURE__ */ new Set();
6
+ for (const n of t) {
7
+ const s = `${n.txid.toLowerCase()}:${n.vout}`;
8
+ if (e.has(s))
9
+ throw new Error(
10
+ `Duplicate UTXO detected: ${n.txid}:${n.vout}. This indicates a data integrity issue with the UTXO source.`
11
+ );
12
+ e.add(s);
13
+ }
14
+ }
15
+ function p(t, e, n, s) {
16
+ if (!Number.isInteger(s) || s < 1)
17
+ throw new Error(
18
+ `Invalid numOutputs: expected a positive integer, got ${s}`
19
+ );
20
+ if (t.length === 0)
21
+ throw new Error("Insufficient funds: no UTXOs available");
22
+ E(t);
23
+ const h = t.filter((o) => {
24
+ const i = u.from(o.scriptPubKey, "hex");
25
+ return !!S.decompile(i);
26
+ });
27
+ if (h.length === 0)
28
+ throw new Error(
29
+ "Insufficient funds: no valid UTXOs available (all have invalid scripts)"
30
+ );
31
+ const T = [...h].sort((o, i) => i.value - o.value), f = [];
32
+ let a = 0n, r = 0n;
33
+ for (const o of T) {
34
+ f.push(o), a += BigInt(o.value);
35
+ const i = f.length * U, d = s * g, P = i + d + x;
36
+ if (r = BigInt(Math.ceil(P * n)) + BigInt(H(n)), a - e - r > w) {
37
+ const l = BigInt(
38
+ Math.ceil(g * n)
39
+ );
40
+ r += l;
41
+ }
42
+ if (a >= e + r) {
43
+ const l = a - e - r;
44
+ return {
45
+ selectedUTXOs: f,
46
+ totalValue: a,
47
+ fee: r,
48
+ changeAmount: l
49
+ };
50
+ }
51
+ }
52
+ throw new Error(
53
+ `Insufficient funds: need ${e + r} sats (${e} pegin + ${r} fee), have ${a} sats`
54
+ );
55
+ }
56
+ function N(t) {
57
+ return t > w;
58
+ }
59
+ function D() {
60
+ return I;
61
+ }
62
+ function B(t) {
63
+ const e = t.startsWith("0x") ? t.slice(2) : t;
64
+ return `0x${v.fromHex(e).getId()}`;
65
+ }
66
+ var c = /* @__PURE__ */ ((t) => (t.P2PKH = "P2PKH", t.P2SH = "P2SH", t.P2WPKH = "P2WPKH", t.P2WSH = "P2WSH", t.P2TR = "P2TR", t.UNKNOWN = "UNKNOWN", t))(c || {});
67
+ function m(t) {
68
+ const e = t.length;
69
+ return e === 25 && t[0] === 118 && // OP_DUP
70
+ t[1] === 169 && // OP_HASH160
71
+ t[2] === 20 && // Push 20 bytes
72
+ t[23] === 136 && // OP_EQUALVERIFY
73
+ t[24] === 172 ? "P2PKH" : e === 23 && t[0] === 169 && // OP_HASH160
74
+ t[1] === 20 && // Push 20 bytes
75
+ t[22] === 135 ? "P2SH" : e === 22 && t[0] === 0 && // OP_0
76
+ t[1] === 20 ? "P2WPKH" : e === 34 && t[0] === 0 && // OP_0
77
+ t[1] === 32 ? "P2WSH" : e === 34 && t[0] === 81 && // OP_1
78
+ t[1] === 32 ? "P2TR" : "UNKNOWN";
79
+ }
80
+ function F(t, e) {
81
+ const n = u.from(t.scriptPubKey, "hex"), s = m(n);
82
+ switch (s) {
83
+ case c.P2WPKH:
84
+ return {
85
+ witnessUtxo: {
86
+ script: n,
87
+ value: t.value
88
+ }
89
+ };
90
+ case c.P2WSH: {
91
+ if (!t.witnessScript)
92
+ throw new Error("Missing witnessScript for P2WSH input");
93
+ return {
94
+ witnessUtxo: {
95
+ script: n,
96
+ value: t.value
97
+ },
98
+ witnessScript: u.from(t.witnessScript, "hex")
99
+ };
100
+ }
101
+ case c.P2TR: {
102
+ if (e && e.length !== 32)
103
+ throw new Error(
104
+ `Invalid tapInternalKey length: expected 32 bytes, got ${e.length}`
105
+ );
106
+ return {
107
+ witnessUtxo: {
108
+ script: n,
109
+ value: t.value
110
+ },
111
+ // tapInternalKey is needed for Taproot signing
112
+ ...e && { tapInternalKey: e }
113
+ };
114
+ }
115
+ default:
116
+ throw new Error(`Unsupported script type: ${s}`);
117
+ }
118
+ }
119
+ export {
120
+ c as B,
121
+ N as a,
122
+ m as b,
123
+ B as c,
124
+ F as d,
125
+ D as g,
126
+ p as s
127
+ };
128
+ //# sourceMappingURL=psbtInputFields-2224j2ZY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psbtInputFields-2224j2ZY.js","sources":["../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"],"sourcesContent":["/**\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 {\n BTC_DUST_SAT,\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"../fee/constants\";\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 and recalculate fee\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n // Recalculate fee based on CURRENT number of inputs\n const inputSize = selectedUTXOs.length * P2TR_INPUT_SIZE;\n const outputSize = numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE;\n const baseTxSize = inputSize + outputSize + TX_BUFFER_SIZE_OVERHEAD;\n\n // Calculate base fee with buffer\n estimatedFee =\n BigInt(Math.ceil(baseTxSize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate));\n\n // Check if there will be change left after pegin amount and fee\n const changeAmount = accumulatedValue - peginAmount - estimatedFee;\n\n // If change is above dust, add fee for change output\n if (changeAmount > DUST_THRESHOLD) {\n const changeOutputFee = BigInt(\n Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate),\n );\n estimatedFee += changeOutputFee;\n }\n\n // Check if we have enough to cover pegin amount + fees\n if (accumulatedValue >= peginAmount + estimatedFee) {\n // Success! We have enough funds\n const finalChangeAmount = accumulatedValue - peginAmount - estimatedFee;\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: estimatedFee,\n changeAmount: finalChangeAmount,\n };\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"],"names":["assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","peginAmount","feeRate","numOutputs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","inputSize","P2TR_INPUT_SIZE","outputSize","MAX_NON_LEGACY_OUTPUT_SIZE","baseTxSize","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","DUST_THRESHOLD","changeOutputFee","finalChangeAmount","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","calculateBtcTxHash","txHex","cleanHex","Transaction","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type"],"mappings":";;;AAsDA,SAASA,EAAuBC,GAAqB;AACnD,QAAMC,wBAAW,IAAA;AACjB,aAAWC,KAAQF,GAAO;AACxB,UAAMG,IAAM,GAAGD,EAAK,KAAK,aAAa,IAAIA,EAAK,IAAI;AACnD,QAAID,EAAK,IAAIE,CAAG;AACd,YAAM,IAAI;AAAA,QACR,4BAA4BD,EAAK,IAAI,IAAIA,EAAK,IAAI;AAAA,MAAA;AAItD,IAAAD,EAAK,IAAIE,CAAG;AAAA,EACd;AACF;AAsBO,SAASC,EACdC,GACAC,GACAC,GACAC,GACqB;AACrB,MAAI,CAAC,OAAO,UAAUA,CAAU,KAAKA,IAAa;AAChD,UAAM,IAAI;AAAA,MACR,wDAAwDA,CAAU;AAAA,IAAA;AAItE,MAAIH,EAAe,WAAW;AAC5B,UAAM,IAAI,MAAM,wCAAwC;AAG1D,EAAAN,EAAuBM,CAAc;AAIrC,QAAMI,IAAaJ,EAAe,OAAO,CAACH,MAAS;AACjD,UAAMQ,IAASC,EAAO,KAAKT,EAAK,cAAc,KAAK;AAEnD,WAAO,CAAC,CADiBU,EAAc,UAAUF,CAAM;AAAA,EAEzD,CAAC;AAED,MAAID,EAAW,WAAW;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAMJ,QAAMI,IAAc,CAAC,GAAGJ,CAAU,EAAE,KAAK,CAACK,GAAGC,MAAMA,EAAE,QAAQD,EAAE,KAAK,GAE9DE,IAAwB,CAAA;AAC9B,MAAIC,IAAmB,IACnBC,IAAe;AAGnB,aAAWhB,KAAQW,GAAa;AAC9B,IAAAG,EAAc,KAAKd,CAAI,GACvBe,KAAoB,OAAOf,EAAK,KAAK;AAGrC,UAAMiB,IAAYH,EAAc,SAASI,GACnCC,IAAab,IAAac,GAC1BC,IAAaJ,IAAYE,IAAaG;AAW5C,QARAN,IACE,OAAO,KAAK,KAAKK,IAAahB,CAAO,CAAC,IACtC,OAAOkB,EAAqBlB,CAAO,CAAC,GAGjBU,IAAmBX,IAAcY,IAGnCQ,GAAgB;AACjC,YAAMC,IAAkB;AAAA,QACtB,KAAK,KAAKL,IAA6Bf,CAAO;AAAA,MAAA;AAEhD,MAAAW,KAAgBS;AAAA,IAClB;AAGA,QAAIV,KAAoBX,IAAcY,GAAc;AAElD,YAAMU,IAAoBX,IAAmBX,IAAcY;AAE3D,aAAO;AAAA,QACL,eAAAF;AAAA,QACA,YAAYC;AAAA,QACZ,KAAKC;AAAA,QACL,cAAcU;AAAA,MAAA;AAAA,IAElB;AAAA,EACF;AAGA,QAAM,IAAI;AAAA,IACR,4BAA4BtB,IAAcY,CAAY,UAAUZ,CAAW,YAAYY,CAAY,eAAeD,CAAgB;AAAA,EAAA;AAEtI;AAQO,SAASY,EAAsBC,GAA+B;AACnE,SAAOA,IAAeJ;AACxB;AAOO,SAASK,IAA2B;AACzC,SAAOC;AACT;ACzKO,SAASC,EAAmBC,GAAoB;AAErD,QAAMC,IAAWD,EAAM,WAAW,IAAI,IAAIA,EAAM,MAAM,CAAC,IAAIA;AAO3D,SAAO,KAJIE,EAAY,QAAQD,CAAQ,EACvB,MAAA,CAGA;AAClB;ACrBO,IAAKE,sBAAAA,OACVA,EAAA,QAAQ,SACRA,EAAA,OAAO,QACPA,EAAA,SAAS,UACTA,EAAA,QAAQ,SACRA,EAAA,OAAO,QACPA,EAAA,UAAU,WANAA,IAAAA,KAAA,CAAA,CAAA;AAuBL,SAASC,EAAcC,GAAyC;AACrE,QAAMC,IAASD,EAAa;AAG5B,SACEC,MAAW,MACXD,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,EAAE,MAAM;AAAA,EACrBA,EAAa,EAAE,MAAM,MAEd,UAKPC,MAAW,MACXD,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,EAAE,MAAM,MAEd,SAKPC,MAAW,MACXD,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM,KAEb,WAKPC,MAAW,MACXD,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM,KAEb,UAKPC,MAAW,MACXD,EAAa,CAAC,MAAM;AAAA,EACpBA,EAAa,CAAC,MAAM,KAEb,SAGF;AACT;ACnCO,SAASE,EACdvC,GACAwC,GACiB;AACjB,QAAMH,IAAe5B,EAAO,KAAKT,EAAK,cAAc,KAAK,GACnDyC,IAAOL,EAAcC,CAAY;AAEvC,UAAQI,GAAA;AAAA,IACN,KAAKN,EAAkB;AACrB,aAAO;AAAA,QACL,aAAa;AAAA,UACX,QAAQE;AAAA,UACR,OAAOrC,EAAK;AAAA,QAAA;AAAA,MACd;AAAA,IAIJ,KAAKmC,EAAkB,OAAO;AAC5B,UAAI,CAACnC,EAAK;AACR,cAAM,IAAI,MAAM,uCAAuC;AAEzD,aAAO;AAAA,QACL,aAAa;AAAA,UACX,QAAQqC;AAAA,UACR,OAAOrC,EAAK;AAAA,QAAA;AAAA,QAEd,eAAeS,EAAO,KAAKT,EAAK,eAAe,KAAK;AAAA,MAAA;AAAA,IAExD;AAAA,IAEA,KAAKmC,EAAkB,MAAM;AAC3B,UAAIK,KAAoBA,EAAiB,WAAW;AAClD,cAAM,IAAI;AAAA,UACR,yDAAyDA,EAAiB,MAAM;AAAA,QAAA;AAGpF,aAAO;AAAA,QACL,aAAa;AAAA,UACX,QAAQH;AAAA,UACR,OAAOrC,EAAK;AAAA,QAAA;AAAA;AAAA,QAGd,GAAIwC,KAAoB,EAAE,gBAAgBA,EAAA;AAAA,MAAiB;AAAA,IAE/D;AAAA,IAEA;AACE,YAAM,IAAI,MAAM,4BAA4BC,CAAI,EAAE;AAAA,EAAA;AAExD;"}
@@ -0,0 +1,2 @@
1
+ "use strict";const T=require("bitcoinjs-lib"),h=require("buffer"),i=require("./fundPeginTransaction-DaWoYCgO.cjs");function U(t){const e=new Set;for(const n of t){const r=`${n.txid.toLowerCase()}:${n.vout}`;if(e.has(r))throw new Error(`Duplicate UTXO detected: ${n.txid}:${n.vout}. This indicates a data integrity issue with the UTXO source.`);e.add(r)}}function H(t,e,n,r){if(!Number.isInteger(r)||r<1)throw new Error(`Invalid numOutputs: expected a positive integer, got ${r}`);if(t.length===0)throw new Error("Insufficient funds: no UTXOs available");U(t);const d=t.filter(a=>{const c=h.Buffer.from(a.scriptPubKey,"hex");return!!T.script.decompile(c)});if(d.length===0)throw new Error("Insufficient funds: no valid UTXOs available (all have invalid scripts)");const S=[...d].sort((a,c)=>c.value-a.value),l=[];let o=0n,s=0n;for(const a of S){l.push(a),o+=BigInt(a.value);const c=l.length*i.P2TR_INPUT_SIZE,g=r*i.MAX_NON_LEGACY_OUTPUT_SIZE,P=c+g+i.TX_BUFFER_SIZE_OVERHEAD;if(s=BigInt(Math.ceil(P*n))+BigInt(i.rateBasedTxBufferFee(n)),o-e-s>i.DUST_THRESHOLD){const f=BigInt(Math.ceil(i.MAX_NON_LEGACY_OUTPUT_SIZE*n));s+=f}if(o>=e+s){const f=o-e-s;return{selectedUTXOs:l,totalValue:o,fee:s,changeAmount:f}}}throw new Error(`Insufficient funds: need ${e+s} sats (${e} pegin + ${s} fee), have ${o} sats`)}function v(t){return t>i.DUST_THRESHOLD}function I(){return i.BTC_DUST_SAT}function E(t){const e=t.startsWith("0x")?t.slice(2):t;return`0x${T.Transaction.fromHex(e).getId()}`}var u=(t=>(t.P2PKH="P2PKH",t.P2SH="P2SH",t.P2WPKH="P2WPKH",t.P2WSH="P2WSH",t.P2TR="P2TR",t.UNKNOWN="UNKNOWN",t))(u||{});function w(t){const e=t.length;return e===25&&t[0]===118&&t[1]===169&&t[2]===20&&t[23]===136&&t[24]===172?"P2PKH":e===23&&t[0]===169&&t[1]===20&&t[22]===135?"P2SH":e===22&&t[0]===0&&t[1]===20?"P2WPKH":e===34&&t[0]===0&&t[1]===32?"P2WSH":e===34&&t[0]===81&&t[1]===32?"P2TR":"UNKNOWN"}function x(t,e){const n=h.Buffer.from(t.scriptPubKey,"hex"),r=w(n);switch(r){case u.P2WPKH:return{witnessUtxo:{script:n,value:t.value}};case u.P2WSH:{if(!t.witnessScript)throw new Error("Missing witnessScript for P2WSH input");return{witnessUtxo:{script:n,value:t.value},witnessScript:h.Buffer.from(t.witnessScript,"hex")}}case u.P2TR:{if(e&&e.length!==32)throw new Error(`Invalid tapInternalKey length: expected 32 bytes, got ${e.length}`);return{witnessUtxo:{script:n,value:t.value},...e&&{tapInternalKey:e}}}default:throw new Error(`Unsupported script type: ${r}`)}}exports.BitcoinScriptType=u;exports.calculateBtcTxHash=E;exports.getDustThreshold=I;exports.getPsbtInputFields=x;exports.getScriptType=w;exports.selectUtxosForPegin=H;exports.shouldAddChangeOutput=v;
2
+ //# sourceMappingURL=psbtInputFields-B1lrwYzH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psbtInputFields-B1lrwYzH.cjs","sources":["../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"],"sourcesContent":["/**\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 {\n BTC_DUST_SAT,\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"../fee/constants\";\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 and recalculate fee\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n // Recalculate fee based on CURRENT number of inputs\n const inputSize = selectedUTXOs.length * P2TR_INPUT_SIZE;\n const outputSize = numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE;\n const baseTxSize = inputSize + outputSize + TX_BUFFER_SIZE_OVERHEAD;\n\n // Calculate base fee with buffer\n estimatedFee =\n BigInt(Math.ceil(baseTxSize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate));\n\n // Check if there will be change left after pegin amount and fee\n const changeAmount = accumulatedValue - peginAmount - estimatedFee;\n\n // If change is above dust, add fee for change output\n if (changeAmount > DUST_THRESHOLD) {\n const changeOutputFee = BigInt(\n Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate),\n );\n estimatedFee += changeOutputFee;\n }\n\n // Check if we have enough to cover pegin amount + fees\n if (accumulatedValue >= peginAmount + estimatedFee) {\n // Success! We have enough funds\n const finalChangeAmount = accumulatedValue - peginAmount - estimatedFee;\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: estimatedFee,\n changeAmount: finalChangeAmount,\n };\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"],"names":["assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","peginAmount","feeRate","numOutputs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","b","selectedUTXOs","accumulatedValue","estimatedFee","inputSize","P2TR_INPUT_SIZE","outputSize","MAX_NON_LEGACY_OUTPUT_SIZE","baseTxSize","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","DUST_THRESHOLD","changeOutputFee","finalChangeAmount","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","calculateBtcTxHash","txHex","cleanHex","Transaction","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type"],"mappings":"mHAsDA,SAASA,EAAuBC,EAAqB,CACnD,MAAMC,MAAW,IACjB,UAAWC,KAAQF,EAAO,CACxB,MAAMG,EAAM,GAAGD,EAAK,KAAK,aAAa,IAAIA,EAAK,IAAI,GACnD,GAAID,EAAK,IAAIE,CAAG,EACd,MAAM,IAAI,MACR,4BAA4BD,EAAK,IAAI,IAAIA,EAAK,IAAI,+DAAA,EAItDD,EAAK,IAAIE,CAAG,CACd,CACF,CAsBO,SAASC,EACdC,EACAC,EACAC,EACAC,EACqB,CACrB,GAAI,CAAC,OAAO,UAAUA,CAAU,GAAKA,EAAa,EAChD,MAAM,IAAI,MACR,wDAAwDA,CAAU,EAAA,EAItE,GAAIH,EAAe,SAAW,EAC5B,MAAM,IAAI,MAAM,wCAAwC,EAG1DN,EAAuBM,CAAc,EAIrC,MAAMI,EAAaJ,EAAe,OAAQH,GAAS,CACjD,MAAMQ,EAASC,EAAAA,OAAO,KAAKT,EAAK,aAAc,KAAK,EAEnD,MAAO,CAAC,CADiBU,EAAAA,OAAc,UAAUF,CAAM,CAEzD,CAAC,EAED,GAAID,EAAW,SAAW,EACxB,MAAM,IAAI,MACR,yEAAA,EAMJ,MAAMI,EAAc,CAAC,GAAGJ,CAAU,EAAE,KAAK,CAAC,EAAGK,IAAMA,EAAE,MAAQ,EAAE,KAAK,EAE9DC,EAAwB,CAAA,EAC9B,IAAIC,EAAmB,GACnBC,EAAe,GAGnB,UAAWf,KAAQW,EAAa,CAC9BE,EAAc,KAAKb,CAAI,EACvBc,GAAoB,OAAOd,EAAK,KAAK,EAGrC,MAAMgB,EAAYH,EAAc,OAASI,EAAAA,gBACnCC,EAAaZ,EAAaa,EAAAA,2BAC1BC,EAAaJ,EAAYE,EAAaG,EAAAA,wBAW5C,GARAN,EACE,OAAO,KAAK,KAAKK,EAAaf,CAAO,CAAC,EACtC,OAAOiB,uBAAqBjB,CAAO,CAAC,EAGjBS,EAAmBV,EAAcW,EAGnCQ,EAAAA,eAAgB,CACjC,MAAMC,EAAkB,OACtB,KAAK,KAAKL,EAAAA,2BAA6Bd,CAAO,CAAA,EAEhDU,GAAgBS,CAClB,CAGA,GAAIV,GAAoBV,EAAcW,EAAc,CAElD,MAAMU,EAAoBX,EAAmBV,EAAcW,EAE3D,MAAO,CACL,cAAAF,EACA,WAAYC,EACZ,IAAKC,EACL,aAAcU,CAAA,CAElB,CACF,CAGA,MAAM,IAAI,MACR,4BAA4BrB,EAAcW,CAAY,UAAUX,CAAW,YAAYW,CAAY,eAAeD,CAAgB,OAAA,CAEtI,CAQO,SAASY,EAAsBC,EAA+B,CACnE,OAAOA,EAAeJ,EAAAA,cACxB,CAOO,SAASK,GAA2B,CACzC,OAAOC,EAAAA,YACT,CCzKO,SAASC,EAAmBC,EAAoB,CAErD,MAAMC,EAAWD,EAAM,WAAW,IAAI,EAAIA,EAAM,MAAM,CAAC,EAAIA,EAO3D,MAAO,KAJIE,EAAAA,YAAY,QAAQD,CAAQ,EACvB,MAAA,CAGA,EAClB,CCrBO,IAAKE,GAAAA,IACVA,EAAA,MAAQ,QACRA,EAAA,KAAO,OACPA,EAAA,OAAS,SACTA,EAAA,MAAQ,QACRA,EAAA,KAAO,OACPA,EAAA,QAAU,UANAA,IAAAA,GAAA,CAAA,CAAA,EAuBL,SAASC,EAAcC,EAAyC,CACrE,MAAMC,EAASD,EAAa,OAG5B,OACEC,IAAW,IACXD,EAAa,CAAC,IAAM,KACpBA,EAAa,CAAC,IAAM,KACpBA,EAAa,CAAC,IAAM,IACpBA,EAAa,EAAE,IAAM,KACrBA,EAAa,EAAE,IAAM,IAEd,QAKPC,IAAW,IACXD,EAAa,CAAC,IAAM,KACpBA,EAAa,CAAC,IAAM,IACpBA,EAAa,EAAE,IAAM,IAEd,OAKPC,IAAW,IACXD,EAAa,CAAC,IAAM,GACpBA,EAAa,CAAC,IAAM,GAEb,SAKPC,IAAW,IACXD,EAAa,CAAC,IAAM,GACpBA,EAAa,CAAC,IAAM,GAEb,QAKPC,IAAW,IACXD,EAAa,CAAC,IAAM,IACpBA,EAAa,CAAC,IAAM,GAEb,OAGF,SACT,CCnCO,SAASE,EACdtC,EACAuC,EACiB,CACjB,MAAMH,EAAe3B,EAAAA,OAAO,KAAKT,EAAK,aAAc,KAAK,EACnDwC,EAAOL,EAAcC,CAAY,EAEvC,OAAQI,EAAA,CACN,KAAKN,EAAkB,OACrB,MAAO,CACL,YAAa,CACX,OAAQE,EACR,MAAOpC,EAAK,KAAA,CACd,EAIJ,KAAKkC,EAAkB,MAAO,CAC5B,GAAI,CAAClC,EAAK,cACR,MAAM,IAAI,MAAM,uCAAuC,EAEzD,MAAO,CACL,YAAa,CACX,OAAQoC,EACR,MAAOpC,EAAK,KAAA,EAEd,cAAeS,EAAAA,OAAO,KAAKT,EAAK,cAAe,KAAK,CAAA,CAExD,CAEA,KAAKkC,EAAkB,KAAM,CAC3B,GAAIK,GAAoBA,EAAiB,SAAW,GAClD,MAAM,IAAI,MACR,yDAAyDA,EAAiB,MAAM,EAAA,EAGpF,MAAO,CACL,YAAa,CACX,OAAQH,EACR,MAAOpC,EAAK,KAAA,EAGd,GAAIuC,GAAoB,CAAE,eAAgBA,CAAA,CAAiB,CAE/D,CAEA,QACE,MAAM,IAAI,MAAM,4BAA4BC,CAAI,EAAE,CAAA,CAExD"}
@@ -0,0 +1,142 @@
1
+ var v = Object.defineProperty;
2
+ var m = (n, t, e) => t in n ? v(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
3
+ var f = (n, t, e) => m(n, typeof t != "symbol" ? t + "" : t, e);
4
+ import { Transaction as h } from "bitcoinjs-lib";
5
+ import { Buffer as x } from "buffer";
6
+ import { s as g } from "./bitcoin-B0S8SHCX.js";
7
+ import { C as l } from "./peginState-CBAlxgXk.js";
8
+ import { F as w, P as T, M as d, T as U } from "./fundPeginTransaction-oV-dNJOU.js";
9
+ class E extends Error {
10
+ constructor(e) {
11
+ const r = e.length, a = r === 1 ? "The UTXO for this peg-in is no longer available. It may have been spent in another transaction. Please create a new peg-in request with a different UTXO." : `${r} UTXOs for this peg-in are no longer available. They may have been spent. Please create a new peg-in request with different UTXOs.`;
12
+ super(a);
13
+ f(this, "missingUtxos");
14
+ this.name = "UtxoNotAvailableError", this.missingUtxos = e;
15
+ }
16
+ }
17
+ function S(n) {
18
+ const t = n.startsWith("0x") ? n.slice(2) : n;
19
+ let e;
20
+ try {
21
+ e = h.fromHex(t);
22
+ } catch (r) {
23
+ throw new Error(
24
+ `Failed to parse BTC transaction: ${r instanceof Error ? r.message : String(r)}`
25
+ );
26
+ }
27
+ return e.ins.map((r) => ({
28
+ // Bitcoin stores txid in reverse byte order
29
+ txid: x.from(r.hash).reverse().toString("hex"),
30
+ vout: r.index
31
+ }));
32
+ }
33
+ function I(n, t) {
34
+ const e = S(n);
35
+ if (e.length === 0)
36
+ throw new Error("Transaction has no inputs");
37
+ const r = /* @__PURE__ */ new Set();
38
+ for (const o of e) {
39
+ const i = `${o.txid.toLowerCase()}:${o.vout}`;
40
+ if (r.has(i))
41
+ throw new Error(
42
+ `Transaction contains duplicate input ${o.txid}:${o.vout}. This would produce an invalid Bitcoin transaction.`
43
+ );
44
+ r.add(i);
45
+ }
46
+ const a = new Set(
47
+ t.map((o) => `${o.txid.toLowerCase()}:${o.vout}`)
48
+ ), s = [];
49
+ for (const o of e) {
50
+ const i = `${o.txid.toLowerCase()}:${o.vout}`;
51
+ a.has(i) || s.push({
52
+ txid: o.txid,
53
+ vout: o.vout
54
+ });
55
+ }
56
+ return {
57
+ allAvailable: s.length === 0,
58
+ missingUtxos: s,
59
+ totalInputs: e.length
60
+ };
61
+ }
62
+ function $(n, t) {
63
+ const e = I(n, t);
64
+ if (!e.allAvailable)
65
+ throw new E(e.missingUtxos);
66
+ }
67
+ function u(n) {
68
+ try {
69
+ return h.fromHex(g(n)).ins.map((e) => ({ txid: x.from(e.hash).reverse().toString("hex"), vout: e.index }));
70
+ } catch (t) {
71
+ return console.warn(
72
+ "[utxoReservation] Failed to parse transaction hex; skipping inputs",
73
+ {
74
+ category: "utxoReservation",
75
+ error: t instanceof Error ? t.message : String(t)
76
+ }
77
+ ), [];
78
+ }
79
+ }
80
+ function b(n, t) {
81
+ const e = n.txid.toLowerCase();
82
+ return t.some(
83
+ (r) => r.txid.toLowerCase() === e && r.vout === n.vout
84
+ );
85
+ }
86
+ function A(n) {
87
+ const e = 2 * T + d + // vault output
88
+ d + // change output
89
+ U, r = Math.ceil(e * n * w);
90
+ return BigInt(r);
91
+ }
92
+ function L(n) {
93
+ const t = [], {
94
+ vaults: e = [],
95
+ pendingPegins: r = [],
96
+ utxoReservations: a = []
97
+ } = n, s = new Set(
98
+ e.map((o) => {
99
+ var i;
100
+ return (i = o.id) == null ? void 0 : i.toLowerCase();
101
+ }).filter((o) => o !== void 0)
102
+ );
103
+ for (const o of r)
104
+ o.id && s.has(o.id.toLowerCase()) || o.unsignedTxHex && t.push(...u(o.unsignedTxHex));
105
+ for (const o of e)
106
+ o.status !== l.PENDING && o.status !== l.VERIFIED || t.push(...u(o.unsignedPrePeginTx));
107
+ for (const o of a)
108
+ t.push(...u(o.unsignedTxHex));
109
+ return t;
110
+ }
111
+ function O(n) {
112
+ const { availableUtxos: t, reservedUtxoRefs: e, requiredAmount: r, feeRate: a } = n;
113
+ if (!t || t.length === 0)
114
+ return [];
115
+ if (e.length === 0)
116
+ return t;
117
+ const s = t.filter(
118
+ (c) => !b(c, e)
119
+ );
120
+ if (s.length === 0)
121
+ throw new Error(
122
+ "All available UTXOs are reserved by pending deposits. Wait for pending deposits to confirm or cancel them before starting a new deposit."
123
+ );
124
+ const o = A(a), i = r + o;
125
+ if (s.reduce(
126
+ (c, p) => c + BigInt(p.value),
127
+ 0n
128
+ ) < i)
129
+ throw new Error(
130
+ "Insufficient unreserved UTXOs for this deposit amount. Wait for pending deposits to confirm or cancel them."
131
+ );
132
+ return s;
133
+ }
134
+ export {
135
+ E as U,
136
+ $ as a,
137
+ L as c,
138
+ S as e,
139
+ O as s,
140
+ I as v
141
+ };
142
+ //# sourceMappingURL=reservation-CHUGW0F_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reservation-CHUGW0F_.js","sources":["../src/tbv/core/utils/utxo/availability.ts","../src/tbv/core/utils/utxo/reservation.ts"],"sourcesContent":["/**\n * UTXO Availability Validation\n *\n * Validates that UTXOs referenced in a pre-pegin transaction are still unspent\n * BEFORE asking the user to sign. This prevents wasted signing effort when\n * UTXOs have already been spent by unrelated transactions.\n *\n * These functions are pure — they accept pre-fetched UTXOs and perform no I/O.\n * The vault service wrapper is responsible for fetching UTXOs from the mempool.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport type { UtxoRef } from \"./reservation\";\n\n/**\n * Information about a missing/spent UTXO.\n */\nexport interface MissingUtxoInfo {\n /** Transaction ID of the missing UTXO */\n txid: string;\n /** Output index of the missing UTXO */\n vout: number;\n}\n\n/**\n * Result of UTXO validation.\n */\nexport interface UtxoValidationResult {\n /** Whether all UTXOs are still available */\n allAvailable: boolean;\n /** List of missing UTXOs (if any) */\n missingUtxos: MissingUtxoInfo[];\n /** Total number of inputs checked */\n totalInputs: number;\n}\n\n/**\n * Error thrown when UTXOs are not available.\n */\nexport class UtxoNotAvailableError extends Error {\n public readonly missingUtxos: MissingUtxoInfo[];\n\n constructor(missingUtxos: MissingUtxoInfo[]) {\n const count = missingUtxos.length;\n const message =\n count === 1\n ? \"The UTXO for this peg-in is no longer available. It may have been spent in another transaction. Please create a new peg-in request with a different UTXO.\"\n : `${count} UTXOs for this peg-in are no longer available. They may have been spent. Please create a new peg-in request with different UTXOs.`;\n\n super(message);\n this.name = \"UtxoNotAvailableError\";\n this.missingUtxos = missingUtxos;\n }\n}\n\n/**\n * Extract input references (txid:vout) from an unsigned transaction.\n *\n * @param unsignedTxHex - Unsigned transaction hex\n * @returns Array of input references\n */\nexport function extractInputsFromTransaction(\n unsignedTxHex: string,\n): Array<{ txid: string; vout: number }> {\n const cleanHex = unsignedTxHex.startsWith(\"0x\")\n ? unsignedTxHex.slice(2)\n : unsignedTxHex;\n\n let tx: Transaction;\n try {\n tx = Transaction.fromHex(cleanHex);\n } catch (error) {\n throw new Error(\n `Failed to parse BTC transaction: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n return tx.ins.map((input) => ({\n // Bitcoin stores txid in reverse byte order\n txid: Buffer.from(input.hash).reverse().toString(\"hex\"),\n vout: input.index,\n }));\n}\n\n/**\n * Validate that all UTXOs in a transaction are still available.\n *\n * Pure function — accepts pre-fetched UTXOs instead of making network calls.\n * This should be called BEFORE signing to avoid wasting user effort\n * signing a transaction that will fail to broadcast.\n *\n * @param unsignedTxHex - Unsigned transaction hex\n * @param availableUtxos - Pre-fetched list of available UTXOs for the depositor\n * @returns Validation result with missing UTXO details\n */\nexport function validateUtxosAvailable(\n unsignedTxHex: string,\n availableUtxos: UtxoRef[],\n): UtxoValidationResult {\n const inputs = extractInputsFromTransaction(unsignedTxHex);\n\n if (inputs.length === 0) {\n throw new Error(\"Transaction has no inputs\");\n }\n\n // Detect duplicate inputs (same txid:vout referenced more than once).\n // This would produce an invalid Bitcoin transaction.\n const inputKeys = new Set<string>();\n for (const input of inputs) {\n const key = `${input.txid.toLowerCase()}:${input.vout}`;\n if (inputKeys.has(key)) {\n throw new Error(\n `Transaction contains duplicate input ${input.txid}:${input.vout}. ` +\n `This would produce an invalid Bitcoin transaction.`,\n );\n }\n inputKeys.add(key);\n }\n\n // Create a set of available UTXOs for O(1) lookup (lowercase for consistency with reservation.ts)\n const availableSet = new Set(\n availableUtxos.map((utxo) => `${utxo.txid.toLowerCase()}:${utxo.vout}`),\n );\n\n // Check which inputs are missing\n const missingUtxos: MissingUtxoInfo[] = [];\n for (const input of inputs) {\n const key = `${input.txid.toLowerCase()}:${input.vout}`;\n if (!availableSet.has(key)) {\n missingUtxos.push({\n txid: input.txid,\n vout: input.vout,\n });\n }\n }\n\n return {\n allAvailable: missingUtxos.length === 0,\n missingUtxos,\n totalInputs: inputs.length,\n };\n}\n\n/**\n * Validate UTXOs and throw if any are not available.\n *\n * Pure convenience function that combines validation and error throwing.\n *\n * @param unsignedTxHex - Unsigned transaction hex\n * @param availableUtxos - Pre-fetched list of available UTXOs for the depositor\n * @throws UtxoNotAvailableError if any UTXOs are not available\n * @throws Error if validation fails\n */\nexport function assertUtxosAvailable(\n unsignedTxHex: string,\n availableUtxos: UtxoRef[],\n): void {\n const result = validateUtxosAvailable(unsignedTxHex, availableUtxos);\n\n if (!result.allAvailable) {\n throw new UtxoNotAvailableError(result.missingUtxos);\n }\n}\n","/**\n * UTXO reservation utilities for vault deposits.\n *\n * Handles tracking which UTXOs are already in use by pending deposits\n * and selecting available UTXOs with smart fallback logic.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport { ContractStatus } from \"../../services/deposit/peginState\";\nimport {\n FEE_SAFETY_MARGIN,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"../fee/constants\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** A txid:vout pair uniquely identifying a UTXO (outpoint). */\nexport interface UtxoRef {\n txid: string;\n vout: number;\n}\n\n/** Narrow structural type for pending pegin data. */\nexport interface PendingPeginLike {\n /**\n * Optional vault id. When present, used to skip pending pegins that are\n * already indexed on-chain so the canonical vault copy wins over a\n * tamperable off-chain entry.\n */\n id?: string;\n selectedUTXOs?: Array<{ txid: string; vout: number }>;\n unsignedTxHex?: string;\n}\n\n/** Narrow structural type for vault data. */\nexport interface VaultLike {\n /**\n * Optional vault id. When present, enables on-chain correlation with\n * pending pegins sharing the same id.\n */\n id?: string;\n status: number;\n unsignedPrePeginTx: string;\n}\n\nexport interface SelectUtxosForDepositParams<\n T extends { txid: string; vout: number; value: number },\n> {\n /** All available UTXOs from the wallet. */\n availableUtxos: T[];\n /** UTXOs that are reserved/in-flight and should be avoided if possible. */\n reservedUtxoRefs: UtxoRef[];\n /** Required deposit amount in satoshis (excluding fees). */\n requiredAmount: bigint;\n /** Fee rate in sat/vB. Used to estimate fee buffer for sufficiency check. */\n feeRate: number;\n}\n\n/** Narrow structural type for early UTXO reservations (pre-ETH-registration). */\nexport interface UtxoReservationLike {\n unsignedTxHex: string;\n}\n\nexport interface CollectReservedUtxoRefsParams {\n vaults?: VaultLike[];\n pendingPegins?: PendingPeginLike[];\n utxoReservations?: UtxoReservationLike[];\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\n/**\n * Parse a transaction hex and return the UTXO references of all inputs.\n *\n * Parse failures are logged and yield no refs. A malformed hex from an\n * untrusted source (e.g. off-chain storage) must not silently collapse the\n * reservation set — logging makes tampering visible in telemetry instead\n * of swallowing the error.\n */\nfunction extractInputUtxoRefs(txHex: string): UtxoRef[] {\n try {\n const tx = Transaction.fromHex(stripHexPrefix(txHex));\n return tx.ins.map((input) => {\n const txid = Buffer.from(input.hash).reverse().toString(\"hex\");\n return { txid, vout: input.index };\n });\n } catch (error) {\n console.warn(\n \"[utxoReservation] Failed to parse transaction hex; skipping inputs\",\n {\n category: \"utxoReservation\",\n error: error instanceof Error ? error.message : String(error),\n },\n );\n return [];\n }\n}\n\n/** Check if a UTXO matches any reserved ref (case-insensitive txid comparison). */\nfunction isUtxoReserved(\n utxo: { txid: string; vout: number },\n reservedRefs: UtxoRef[],\n): boolean {\n const txidLower = utxo.txid.toLowerCase();\n return reservedRefs.some(\n (ref) => ref.txid.toLowerCase() === txidLower && ref.vout === utxo.vout,\n );\n}\n\n/**\n * Estimate minimum fee buffer for UTXO pre-selection.\n *\n * WARNING: This is a ROUGH ESTIMATE used only to check if unreserved UTXOs\n * are likely sufficient BEFORE the actual signing flow begins. The actual\n * fee calculation happens in the SDK's `selectUtxosForPegin` during signing.\n *\n * Assumptions:\n * - 2 inputs (conservative estimate for most deposits)\n * - 1 vault output (P2TR, 43 vBytes)\n * - 1 change output (P2TR, 43 vBytes)\n * - Transaction overhead (11 vBytes)\n * - 10% safety margin\n */\nfunction estimateMinimumFeeBuffer(feeRate: number): bigint {\n const ASSUMED_INPUTS = 2;\n\n const estimatedTxSize =\n ASSUMED_INPUTS * P2TR_INPUT_SIZE +\n MAX_NON_LEGACY_OUTPUT_SIZE + // vault output\n MAX_NON_LEGACY_OUTPUT_SIZE + // change output\n TX_BUFFER_SIZE_OVERHEAD;\n\n const estimatedFee = Math.ceil(estimatedTxSize * feeRate * FEE_SAFETY_MARGIN);\n return BigInt(estimatedFee);\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Collect UTXO refs from in-flight deposits (PENDING/VERIFIED vaults and\n * pending pegins).\n *\n * On-chain vault data is canonical: for any pending pegin whose `id` matches\n * an indexed on-chain vault, the pending-pegin copy is ignored entirely —\n * the `vaults` branch below extracts refs from the indexer-supplied\n * `unsignedPrePeginTx` so tampered off-chain data cannot poison the\n * reservation set once the vault is indexed.\n *\n * For pegins not yet indexed, refs are derived from the stored\n * `unsignedTxHex` only. The `selectedUTXOs` sidecar is NOT used for\n * reservation: if it disagreed with the transaction's inputs (e.g. because\n * the off-chain source was tampered), trusting it would poison the reserved\n * set. The transaction hex must be validated at the source boundary before\n * being handed to this function; parsing and using its inputs is the single\n * source of truth here.\n */\nexport function collectReservedUtxoRefs(\n params: CollectReservedUtxoRefsParams,\n): UtxoRef[] {\n const reserved: UtxoRef[] = [];\n const {\n vaults = [],\n pendingPegins = [],\n utxoReservations = [],\n } = params;\n\n const onChainVaultIds = new Set(\n vaults\n .map((v) => v.id?.toLowerCase())\n .filter((id): id is string => id !== undefined),\n );\n\n for (const pending of pendingPegins) {\n if (pending.id && onChainVaultIds.has(pending.id.toLowerCase())) {\n continue;\n }\n if (pending.unsignedTxHex) {\n reserved.push(...extractInputUtxoRefs(pending.unsignedTxHex));\n }\n }\n\n for (const vault of vaults) {\n if (\n vault.status !== ContractStatus.PENDING &&\n vault.status !== ContractStatus.VERIFIED\n ) {\n continue;\n }\n reserved.push(...extractInputUtxoRefs(vault.unsignedPrePeginTx));\n }\n\n // Early reservations written before ETH registration to prevent cross-tab\n // UTXO conflicts. These are cleaned up when the deposit completes or fails.\n for (const reservation of utxoReservations) {\n reserved.push(...extractInputUtxoRefs(reservation.unsignedTxHex));\n }\n\n return reserved;\n}\n\n/**\n * Select UTXOs for a deposit, filtering out reserved ones.\n *\n * Logic:\n * 1. Filter out reserved UTXOs from the available pool\n * 2. If unreserved UTXOs are sufficient for the required amount + estimated fee, return them\n * 3. Otherwise, throw — never silently reuse reserved UTXOs, as this risks double-spend\n * failures that strand registered-but-unbroadcastable vaults\n *\n * @param params - Selection parameters\n * @returns Array of unreserved UTXOs to use for the deposit\n * @throws When all UTXOs are reserved or unreserved UTXOs are insufficient\n */\nexport function selectUtxosForDeposit<\n T extends { txid: string; vout: number; value: number },\n>(params: SelectUtxosForDepositParams<T>): T[] {\n const { availableUtxos, reservedUtxoRefs, requiredAmount, feeRate } = params;\n\n // Edge case: no UTXOs available\n if (!availableUtxos || availableUtxos.length === 0) {\n return [];\n }\n\n // Edge case: no reservations, return all\n if (reservedUtxoRefs.length === 0) {\n return availableUtxos;\n }\n\n // Filter out reserved UTXOs\n const unreserved = availableUtxos.filter(\n (utxo) => !isUtxoReserved(utxo, reservedUtxoRefs),\n );\n\n if (unreserved.length === 0) {\n throw new Error(\n \"All available UTXOs are reserved by pending deposits. \" +\n \"Wait for pending deposits to confirm or cancel them before starting a new deposit.\",\n );\n }\n\n const feeBuffer = estimateMinimumFeeBuffer(feeRate);\n const totalRequired = requiredAmount + feeBuffer;\n const unreservedTotal = unreserved.reduce(\n (sum, u) => sum + BigInt(u.value),\n 0n,\n );\n if (unreservedTotal < totalRequired) {\n throw new Error(\n \"Insufficient unreserved UTXOs for this deposit amount. \" +\n \"Wait for pending deposits to confirm or cancel them.\",\n );\n }\n\n return unreserved;\n}\n"],"names":["UtxoNotAvailableError","missingUtxos","count","message","__publicField","extractInputsFromTransaction","unsignedTxHex","cleanHex","tx","Transaction","error","input","Buffer","validateUtxosAvailable","availableUtxos","inputs","inputKeys","key","availableSet","utxo","assertUtxosAvailable","result","extractInputUtxoRefs","txHex","stripHexPrefix","isUtxoReserved","reservedRefs","txidLower","ref","estimateMinimumFeeBuffer","feeRate","estimatedTxSize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","estimatedFee","FEE_SAFETY_MARGIN","collectReservedUtxoRefs","params","reserved","vaults","pendingPegins","utxoReservations","onChainVaultIds","v","_a","id","pending","vault","ContractStatus","reservation","selectUtxosForDeposit","reservedUtxoRefs","requiredAmount","unreserved","feeBuffer","totalRequired","sum","u"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";var T=Object.defineProperty;var m=(o,e,t)=>e in o?T(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var f=(o,e,t)=>m(o,typeof e!="symbol"?e+"":e,t);const x=require("bitcoinjs-lib"),v=require("buffer"),w=require("./bitcoin-B3aqjuMP.cjs"),d=require("./peginState-BijNNT15.cjs"),c=require("./fundPeginTransaction-DaWoYCgO.cjs");class h extends Error{constructor(t){const r=t.length,a=r===1?"The UTXO for this peg-in is no longer available. It may have been spent in another transaction. Please create a new peg-in request with a different UTXO.":`${r} UTXOs for this peg-in are no longer available. They may have been spent. Please create a new peg-in request with different UTXOs.`;super(a);f(this,"missingUtxos");this.name="UtxoNotAvailableError",this.missingUtxos=t}}function p(o){const e=o.startsWith("0x")?o.slice(2):o;let t;try{t=x.Transaction.fromHex(e)}catch(r){throw new Error(`Failed to parse BTC transaction: ${r instanceof Error?r.message:String(r)}`)}return t.ins.map(r=>({txid:v.Buffer.from(r.hash).reverse().toString("hex"),vout:r.index}))}function g(o,e){const t=p(o);if(t.length===0)throw new Error("Transaction has no inputs");const r=new Set;for(const n of t){const i=`${n.txid.toLowerCase()}:${n.vout}`;if(r.has(i))throw new Error(`Transaction contains duplicate input ${n.txid}:${n.vout}. This would produce an invalid Bitcoin transaction.`);r.add(i)}const a=new Set(e.map(n=>`${n.txid.toLowerCase()}:${n.vout}`)),s=[];for(const n of t){const i=`${n.txid.toLowerCase()}:${n.vout}`;a.has(i)||s.push({txid:n.txid,vout:n.vout})}return{allAvailable:s.length===0,missingUtxos:s,totalInputs:t.length}}function E(o,e){const t=g(o,e);if(!t.allAvailable)throw new h(t.missingUtxos)}function l(o){try{return x.Transaction.fromHex(w.stripHexPrefix(o)).ins.map(t=>({txid:v.Buffer.from(t.hash).reverse().toString("hex"),vout:t.index}))}catch(e){return console.warn("[utxoReservation] Failed to parse transaction hex; skipping inputs",{category:"utxoReservation",error:e instanceof Error?e.message:String(e)}),[]}}function S(o,e){const t=o.txid.toLowerCase();return e.some(r=>r.txid.toLowerCase()===t&&r.vout===o.vout)}function b(o){const t=2*c.P2TR_INPUT_SIZE+c.MAX_NON_LEGACY_OUTPUT_SIZE+c.MAX_NON_LEGACY_OUTPUT_SIZE+c.TX_BUFFER_SIZE_OVERHEAD,r=Math.ceil(t*o*c.FEE_SAFETY_MARGIN);return BigInt(r)}function A(o){const e=[],{vaults:t=[],pendingPegins:r=[],utxoReservations:a=[]}=o,s=new Set(t.map(n=>{var i;return(i=n.id)==null?void 0:i.toLowerCase()}).filter(n=>n!==void 0));for(const n of r)n.id&&s.has(n.id.toLowerCase())||n.unsignedTxHex&&e.push(...l(n.unsignedTxHex));for(const n of t)n.status!==d.ContractStatus.PENDING&&n.status!==d.ContractStatus.VERIFIED||e.push(...l(n.unsignedPrePeginTx));for(const n of a)e.push(...l(n.unsignedTxHex));return e}function I(o){const{availableUtxos:e,reservedUtxoRefs:t,requiredAmount:r,feeRate:a}=o;if(!e||e.length===0)return[];if(t.length===0)return e;const s=e.filter(u=>!S(u,t));if(s.length===0)throw new Error("All available UTXOs are reserved by pending deposits. Wait for pending deposits to confirm or cancel them before starting a new deposit.");const n=b(a),i=r+n;if(s.reduce((u,U)=>u+BigInt(U.value),0n)<i)throw new Error("Insufficient unreserved UTXOs for this deposit amount. Wait for pending deposits to confirm or cancel them.");return s}exports.UtxoNotAvailableError=h;exports.assertUtxosAvailable=E;exports.collectReservedUtxoRefs=A;exports.extractInputsFromTransaction=p;exports.selectUtxosForDeposit=I;exports.validateUtxosAvailable=g;
2
+ //# sourceMappingURL=reservation-ho7mjW3X.cjs.map