@babylonlabs-io/ts-sdk 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/dist/ApplicationRegistry.abi-s8cQ1ooR.js +160 -0
  2. package/dist/ApplicationRegistry.abi-s8cQ1ooR.js.map +1 -0
  3. package/dist/ApplicationRegistry.abi-sMiTWmnL.cjs +2 -0
  4. package/dist/ApplicationRegistry.abi-sMiTWmnL.cjs.map +1 -0
  5. package/dist/{BTCVaultRegistry.abi-BQ5tFNSu.cjs → BTCVaultRegistry.abi-CohlJX7c.cjs} +2 -2
  6. package/dist/{BTCVaultRegistry.abi-BQ5tFNSu.cjs.map → BTCVaultRegistry.abi-CohlJX7c.cjs.map} +1 -1
  7. package/dist/{BTCVaultRegistry.abi-B0x9_Bza.js → BTCVaultRegistry.abi-DBW0dyqH.js} +63 -1
  8. package/dist/{BTCVaultRegistry.abi-B0x9_Bza.js.map → BTCVaultRegistry.abi-DBW0dyqH.js.map} +1 -1
  9. package/dist/{PayoutManager-BSwbUQeF.js → PayoutManager-BZ212yml.js} +124 -123
  10. package/dist/PayoutManager-BZ212yml.js.map +1 -0
  11. package/dist/PayoutManager-DOdsgcUR.cjs +2 -0
  12. package/dist/PayoutManager-DOdsgcUR.cjs.map +1 -0
  13. package/dist/PeginManager-8EUlm10F.cjs +2 -0
  14. package/dist/{PeginManager-IgDHy_eU.cjs.map → PeginManager-8EUlm10F.cjs.map} +1 -1
  15. package/dist/{PeginManager-D8WGB8eN.js → PeginManager-QRwMUxZg.js} +45 -43
  16. package/dist/{PeginManager-D8WGB8eN.js.map → PeginManager-QRwMUxZg.js.map} +1 -1
  17. package/dist/{ProtocolParams.abi-q0AidvYX.js → ProtocolParams.abi-CKOVqFJP.js} +2 -158
  18. package/dist/ProtocolParams.abi-CKOVqFJP.js.map +1 -0
  19. package/dist/ProtocolParams.abi-CjrT0545.cjs +2 -0
  20. package/dist/ProtocolParams.abi-CjrT0545.cjs.map +1 -0
  21. package/dist/btcTxHash-BRZ8_EZq.js +9 -0
  22. package/dist/btcTxHash-BRZ8_EZq.js.map +1 -0
  23. package/dist/btcTxHash-CGb5BqFL.cjs +2 -0
  24. package/dist/btcTxHash-CGb5BqFL.cjs.map +1 -0
  25. package/dist/buildAndBroadcastReclaim-DjX7wVb2.cjs +2 -0
  26. package/dist/buildAndBroadcastReclaim-DjX7wVb2.cjs.map +1 -0
  27. package/dist/buildAndBroadcastReclaim-DoprHX7o.js +3077 -0
  28. package/dist/buildAndBroadcastReclaim-DoprHX7o.js.map +1 -0
  29. package/dist/buildVaultContextInputForClaim-9XZaqIiw.cjs +2 -0
  30. package/dist/buildVaultContextInputForClaim-9XZaqIiw.cjs.map +1 -0
  31. package/dist/buildVaultContextInputForClaim-DhBoQKyN.js +25 -0
  32. package/dist/buildVaultContextInputForClaim-DhBoQKyN.js.map +1 -0
  33. package/dist/constants-BeYquVd-.js +36 -0
  34. package/dist/constants-BeYquVd-.js.map +1 -0
  35. package/dist/constants-BwCg5K6R.cjs +2 -0
  36. package/dist/constants-BwCg5K6R.cjs.map +1 -0
  37. package/dist/{index-D2mybjXD.js → index-CVBtBjQ5.js} +2 -2
  38. package/dist/{index-D2mybjXD.js.map → index-CVBtBjQ5.js.map} +1 -1
  39. package/dist/{index-MRdle0fs.cjs → index-DpjYs5yr.cjs} +2 -2
  40. package/dist/{index-MRdle0fs.cjs.map → index-DpjYs5yr.cjs.map} +1 -1
  41. package/dist/index.cjs +1 -1
  42. package/dist/index.js +288 -260
  43. package/dist/index.js.map +1 -1
  44. package/dist/{noPayout-qBXWKwav.js → noPayout-B-vGXJ-V.js} +3 -3
  45. package/dist/{noPayout-qBXWKwav.js.map → noPayout-B-vGXJ-V.js.map} +1 -1
  46. package/dist/{noPayout-DAWBQ0eG.cjs → noPayout-DOHak0ui.cjs} +2 -2
  47. package/dist/{noPayout-DAWBQ0eG.cjs.map → noPayout-DOHak0ui.cjs.map} +1 -1
  48. package/dist/pegin-registration-client-BWMwxtr8.js +718 -0
  49. package/dist/pegin-registration-client-BWMwxtr8.js.map +1 -0
  50. package/dist/pegin-registration-client-D4EguWaz.cjs +2 -0
  51. package/dist/pegin-registration-client-D4EguWaz.cjs.map +1 -0
  52. package/dist/{primeVpAuth-BDWBKYhv.cjs → primeVpAuth-BPj2TPdD.cjs} +2 -2
  53. package/dist/{primeVpAuth-BDWBKYhv.cjs.map → primeVpAuth-BPj2TPdD.cjs.map} +1 -1
  54. package/dist/{primeVpAuth-D0ZnsjRi.js → primeVpAuth-CiCdRNGf.js} +2 -2
  55. package/dist/{primeVpAuth-D0ZnsjRi.js.map → primeVpAuth-CiCdRNGf.js.map} +1 -1
  56. package/dist/registration-records-CKLAv3gw.js +447 -0
  57. package/dist/registration-records-CKLAv3gw.js.map +1 -0
  58. package/dist/registration-records-DFQgBaYi.cjs +2 -0
  59. package/dist/registration-records-DFQgBaYi.cjs.map +1 -0
  60. package/dist/{secp256k1-D-H9jmd7.js → secp256k1-BuxBFUHm.js} +3 -3
  61. package/dist/{secp256k1-D-H9jmd7.js.map → secp256k1-BuxBFUHm.js.map} +1 -1
  62. package/dist/signer-set-reader-C1VmC9_U.js +264 -0
  63. package/dist/signer-set-reader-C1VmC9_U.js.map +1 -0
  64. package/dist/signer-set-reader-C6g47hdk.cjs +2 -0
  65. package/dist/signer-set-reader-C6g47hdk.cjs.map +1 -0
  66. package/dist/{signing-Dhr2t0Z6.js → signing-C1rR4tqs.js} +9 -15
  67. package/dist/signing-C1rR4tqs.js.map +1 -0
  68. package/dist/signing-DvISftFk.cjs +2 -0
  69. package/dist/signing-DvISftFk.cjs.map +1 -0
  70. package/dist/tbv/core/clients/eth/__tests__/registration-records.test.d.ts +2 -0
  71. package/dist/tbv/core/clients/eth/__tests__/registration-records.test.d.ts.map +1 -0
  72. package/dist/tbv/core/clients/eth/claimable-event-error.d.ts +14 -0
  73. package/dist/tbv/core/clients/eth/claimable-event-error.d.ts.map +1 -0
  74. package/dist/tbv/core/clients/eth/index.cjs +1 -1
  75. package/dist/tbv/core/clients/eth/index.d.ts +3 -1
  76. package/dist/tbv/core/clients/eth/index.d.ts.map +1 -1
  77. package/dist/tbv/core/clients/eth/index.js +27 -21
  78. package/dist/tbv/core/clients/eth/index.js.map +1 -1
  79. package/dist/tbv/core/clients/eth/payout-script.d.ts +8 -0
  80. package/dist/tbv/core/clients/eth/payout-script.d.ts.map +1 -1
  81. package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts +13 -0
  82. package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts.map +1 -1
  83. package/dist/tbv/core/clients/eth/registration-logs-error.d.ts +11 -1
  84. package/dist/tbv/core/clients/eth/registration-logs-error.d.ts.map +1 -1
  85. package/dist/tbv/core/clients/eth/registration-records.d.ts +29 -0
  86. package/dist/tbv/core/clients/eth/registration-records.d.ts.map +1 -0
  87. package/dist/tbv/core/clients/eth/types.d.ts +120 -3
  88. package/dist/tbv/core/clients/eth/types.d.ts.map +1 -1
  89. package/dist/tbv/core/clients/eth/vault-registry-reader.d.ts +9 -10
  90. package/dist/tbv/core/clients/eth/vault-registry-reader.d.ts.map +1 -1
  91. package/dist/tbv/core/clients/index.cjs +1 -1
  92. package/dist/tbv/core/clients/index.js +56 -50
  93. package/dist/tbv/core/clients/index.js.map +1 -1
  94. package/dist/tbv/core/clients/vault-provider/status/index.cjs +1 -1
  95. package/dist/tbv/core/clients/vault-provider/status/index.js +1 -1
  96. package/dist/tbv/core/contracts/abis/BTCVaultRegistry.abi.d.ts +45 -0
  97. package/dist/tbv/core/contracts/abis/BTCVaultRegistry.abi.d.ts.map +1 -1
  98. package/dist/tbv/core/contracts/index.cjs +1 -1
  99. package/dist/tbv/core/contracts/index.js +16 -15
  100. package/dist/tbv/core/contracts/index.js.map +1 -1
  101. package/dist/tbv/core/deposit-terms/__tests__/claimSiblings.test.d.ts +2 -0
  102. package/dist/tbv/core/deposit-terms/__tests__/claimSiblings.test.d.ts.map +1 -0
  103. package/dist/tbv/core/deposit-terms/__tests__/fixtures/realFundedPrePegin.d.ts +29 -0
  104. package/dist/tbv/core/deposit-terms/__tests__/fixtures/realFundedPrePegin.d.ts.map +1 -0
  105. package/dist/tbv/core/deposit-terms/__tests__/fundedPrePeginFee.test.d.ts +2 -0
  106. package/dist/tbv/core/deposit-terms/__tests__/fundedPrePeginFee.test.d.ts.map +1 -0
  107. package/dist/tbv/core/deposit-terms/__tests__/rebuildDepositTermsForClaim.golden.test.d.ts +2 -0
  108. package/dist/tbv/core/deposit-terms/__tests__/rebuildDepositTermsForClaim.golden.test.d.ts.map +1 -0
  109. package/dist/tbv/core/deposit-terms/__tests__/rebuildDepositTermsForClaim.test.d.ts +2 -0
  110. package/dist/tbv/core/deposit-terms/__tests__/rebuildDepositTermsForClaim.test.d.ts.map +1 -0
  111. package/dist/tbv/core/deposit-terms/claimSiblings.d.ts +23 -0
  112. package/dist/tbv/core/deposit-terms/claimSiblings.d.ts.map +1 -0
  113. package/dist/tbv/core/deposit-terms/fundedPrePeginFee.d.ts +15 -0
  114. package/dist/tbv/core/deposit-terms/fundedPrePeginFee.d.ts.map +1 -0
  115. package/dist/tbv/core/deposit-terms/index.d.ts +3 -0
  116. package/dist/tbv/core/deposit-terms/index.d.ts.map +1 -1
  117. package/dist/tbv/core/deposit-terms/rebuildDepositTermsForClaim.d.ts +40 -0
  118. package/dist/tbv/core/deposit-terms/rebuildDepositTermsForClaim.d.ts.map +1 -0
  119. package/dist/tbv/core/index.cjs +1 -1
  120. package/dist/tbv/core/index.js +287 -259
  121. package/dist/tbv/core/index.js.map +1 -1
  122. package/dist/tbv/core/managers/index.cjs +1 -1
  123. package/dist/tbv/core/managers/index.js +2 -2
  124. package/dist/tbv/core/managers/pegin/signPsbtsWithFallback.d.ts +6 -2
  125. package/dist/tbv/core/managers/pegin/signPsbtsWithFallback.d.ts.map +1 -1
  126. package/dist/tbv/core/primitives/index.cjs +1 -1
  127. package/dist/tbv/core/primitives/index.js +4 -4
  128. package/dist/tbv/core/primitives/psbt/constants.d.ts +54 -6
  129. package/dist/tbv/core/primitives/psbt/constants.d.ts.map +1 -1
  130. package/dist/tbv/core/services/delegated-claim/__tests__/assembleWatchtowerArtifactsFromSignatures.test.d.ts +2 -0
  131. package/dist/tbv/core/services/delegated-claim/__tests__/assembleWatchtowerArtifactsFromSignatures.test.d.ts.map +1 -0
  132. package/dist/tbv/core/services/delegated-claim/__tests__/assertBinding.test.d.ts +2 -0
  133. package/dist/tbv/core/services/delegated-claim/__tests__/assertBinding.test.d.ts.map +1 -0
  134. package/dist/tbv/core/services/delegated-claim/__tests__/assertPayoutFeeAndTimelocks.test.d.ts +2 -0
  135. package/dist/tbv/core/services/delegated-claim/__tests__/assertPayoutFeeAndTimelocks.test.d.ts.map +1 -0
  136. package/dist/tbv/core/services/delegated-claim/__tests__/fixtures/delegatedClaimPsbts.d.ts +49 -0
  137. package/dist/tbv/core/services/delegated-claim/__tests__/fixtures/delegatedClaimPsbts.d.ts.map +1 -0
  138. package/dist/tbv/core/services/delegated-claim/__tests__/payoutBinding.test.d.ts +2 -0
  139. package/dist/tbv/core/services/delegated-claim/__tests__/payoutBinding.test.d.ts.map +1 -0
  140. package/dist/tbv/core/services/delegated-claim/__tests__/payoutInputLeaf.test.d.ts +2 -0
  141. package/dist/tbv/core/services/delegated-claim/__tests__/payoutInputLeaf.test.d.ts.map +1 -0
  142. package/dist/tbv/core/services/delegated-claim/__tests__/planDelegatedClaimSigning.test.d.ts +2 -0
  143. package/dist/tbv/core/services/delegated-claim/__tests__/planDelegatedClaimSigning.test.d.ts.map +1 -0
  144. package/dist/tbv/core/services/delegated-claim/__tests__/readDelegatedClaimVaultContext.test.d.ts +2 -0
  145. package/dist/tbv/core/services/delegated-claim/__tests__/readDelegatedClaimVaultContext.test.d.ts.map +1 -0
  146. package/dist/tbv/core/services/delegated-claim/__tests__/realSchnorrVerification.test.d.ts +2 -0
  147. package/dist/tbv/core/services/delegated-claim/__tests__/realSchnorrVerification.test.d.ts.map +1 -0
  148. package/dist/tbv/core/services/delegated-claim/__tests__/signDelegatedClaimPlan.test.d.ts +2 -0
  149. package/dist/tbv/core/services/delegated-claim/__tests__/signDelegatedClaimPlan.test.d.ts.map +1 -0
  150. package/dist/tbv/core/services/delegated-claim/__tests__/signingRequests.test.d.ts +2 -0
  151. package/dist/tbv/core/services/delegated-claim/__tests__/signingRequests.test.d.ts.map +1 -0
  152. package/dist/tbv/core/services/delegated-claim/assembleWatchtowerArtifacts.d.ts +34 -9
  153. package/dist/tbv/core/services/delegated-claim/assembleWatchtowerArtifacts.d.ts.map +1 -1
  154. package/dist/tbv/core/services/delegated-claim/assembleWatchtowerArtifactsFromSignatures.d.ts +36 -0
  155. package/dist/tbv/core/services/delegated-claim/assembleWatchtowerArtifactsFromSignatures.d.ts.map +1 -0
  156. package/dist/tbv/core/services/delegated-claim/assertBinding.d.ts +37 -0
  157. package/dist/tbv/core/services/delegated-claim/assertBinding.d.ts.map +1 -0
  158. package/dist/tbv/core/services/delegated-claim/assertPayoutFeeAndTimelocks.d.ts +35 -0
  159. package/dist/tbv/core/services/delegated-claim/assertPayoutFeeAndTimelocks.d.ts.map +1 -0
  160. package/dist/tbv/core/services/delegated-claim/index.d.ts +22 -4
  161. package/dist/tbv/core/services/delegated-claim/index.d.ts.map +1 -1
  162. package/dist/tbv/core/services/delegated-claim/payoutBinding.d.ts +10 -4
  163. package/dist/tbv/core/services/delegated-claim/payoutBinding.d.ts.map +1 -1
  164. package/dist/tbv/core/services/delegated-claim/payoutInputLeaf.d.ts +34 -0
  165. package/dist/tbv/core/services/delegated-claim/payoutInputLeaf.d.ts.map +1 -0
  166. package/dist/tbv/core/services/delegated-claim/planDelegatedClaimSigning.d.ts +43 -0
  167. package/dist/tbv/core/services/delegated-claim/planDelegatedClaimSigning.d.ts.map +1 -0
  168. package/dist/tbv/core/services/delegated-claim/readDelegatedClaimVaultContext.d.ts +70 -0
  169. package/dist/tbv/core/services/delegated-claim/readDelegatedClaimVaultContext.d.ts.map +1 -0
  170. package/dist/tbv/core/services/delegated-claim/readWatchtowerArtifacts.d.ts +31 -2
  171. package/dist/tbv/core/services/delegated-claim/readWatchtowerArtifacts.d.ts.map +1 -1
  172. package/dist/tbv/core/services/delegated-claim/signDelegatedClaimPlan.d.ts +110 -0
  173. package/dist/tbv/core/services/delegated-claim/signDelegatedClaimPlan.d.ts.map +1 -0
  174. package/dist/tbv/core/services/delegated-claim/signingRequests.d.ts +24 -0
  175. package/dist/tbv/core/services/delegated-claim/signingRequests.d.ts.map +1 -0
  176. package/dist/tbv/core/services/delegated-claim/types.d.ts +110 -9
  177. package/dist/tbv/core/services/delegated-claim/types.d.ts.map +1 -1
  178. package/dist/tbv/core/services/delegated-claim/vaultIdBinding.d.ts +16 -1
  179. package/dist/tbv/core/services/delegated-claim/vaultIdBinding.d.ts.map +1 -1
  180. package/dist/tbv/core/services/delegated-claim/verifySignatureForRequest.d.ts +19 -0
  181. package/dist/tbv/core/services/delegated-claim/verifySignatureForRequest.d.ts.map +1 -0
  182. package/dist/tbv/core/services/delegated-claim/verifyingKeyBinding.d.ts +40 -0
  183. package/dist/tbv/core/services/delegated-claim/verifyingKeyBinding.d.ts.map +1 -0
  184. package/dist/tbv/core/services/delegated-claim/walletIdentity.d.ts +23 -0
  185. package/dist/tbv/core/services/delegated-claim/walletIdentity.d.ts.map +1 -0
  186. package/dist/tbv/core/services/deposit/graphFingerprint.d.ts +39 -0
  187. package/dist/tbv/core/services/deposit/graphFingerprint.d.ts.map +1 -1
  188. package/dist/tbv/core/services/deposit/runDepositorPresignFlow.d.ts.map +1 -1
  189. package/dist/tbv/core/services/deposit/signDepositorGraph.d.ts.map +1 -1
  190. package/dist/tbv/core/services/index.cjs +1 -1
  191. package/dist/tbv/core/services/index.js +89 -76
  192. package/dist/tbv/core/services/participants/__tests__/readStampedParticipantKeys.test.d.ts +2 -0
  193. package/dist/tbv/core/services/participants/__tests__/readStampedParticipantKeys.test.d.ts.map +1 -0
  194. package/dist/tbv/core/services/participants/index.d.ts +1 -0
  195. package/dist/tbv/core/services/participants/index.d.ts.map +1 -1
  196. package/dist/tbv/core/services/participants/readStampedParticipantKeys.d.ts +29 -0
  197. package/dist/tbv/core/services/participants/readStampedParticipantKeys.d.ts.map +1 -0
  198. package/dist/tbv/core/services/participants/resolveParticipantKeys.d.ts +1 -1
  199. package/dist/tbv/core/services/participants/resolveParticipantKeys.d.ts.map +1 -1
  200. package/dist/tbv/core/utils/index.cjs +1 -1
  201. package/dist/tbv/core/utils/index.js +47 -46
  202. package/dist/tbv/core/utils/index.js.map +1 -1
  203. package/dist/tbv/core/vault-secrets/__tests__/buildVaultContextInputForClaim.test.d.ts +2 -0
  204. package/dist/tbv/core/vault-secrets/__tests__/buildVaultContextInputForClaim.test.d.ts.map +1 -0
  205. package/dist/tbv/core/vault-secrets/buildVaultContextInputForClaim.d.ts +18 -0
  206. package/dist/tbv/core/vault-secrets/buildVaultContextInputForClaim.d.ts.map +1 -0
  207. package/dist/tbv/core/vault-secrets/index.cjs +1 -1
  208. package/dist/tbv/core/vault-secrets/index.d.ts +1 -0
  209. package/dist/tbv/core/vault-secrets/index.d.ts.map +1 -1
  210. package/dist/tbv/core/vault-secrets/index.js +12 -10
  211. package/dist/tbv/core/vault-secrets/index.js.map +1 -1
  212. package/dist/tbv/index.cjs +1 -1
  213. package/dist/tbv/index.js +287 -259
  214. package/dist/tbv/index.js.map +1 -1
  215. package/dist/toRefundInputs-BZXvuOMY.cjs +2 -0
  216. package/dist/toRefundInputs-BZXvuOMY.cjs.map +1 -0
  217. package/dist/toRefundInputs-dKDSvpVI.js +698 -0
  218. package/dist/toRefundInputs-dKDSvpVI.js.map +1 -0
  219. package/dist/types-Bda75Od0.cjs +2 -0
  220. package/dist/types-Bda75Od0.cjs.map +1 -0
  221. package/dist/types-C5jj3Cc-.js +198 -0
  222. package/dist/types-C5jj3Cc-.js.map +1 -0
  223. package/dist/{verifyKeyPathSchnorrSignature-Gm9EmZkx.cjs → verifyKeyPathSchnorrSignature-CQUut2-O.cjs} +2 -2
  224. package/dist/{verifyKeyPathSchnorrSignature-Gm9EmZkx.cjs.map → verifyKeyPathSchnorrSignature-CQUut2-O.cjs.map} +1 -1
  225. package/dist/{verifyKeyPathSchnorrSignature-BQHNhRj6.js → verifyKeyPathSchnorrSignature-DxPbeSdQ.js} +2 -2
  226. package/dist/{verifyKeyPathSchnorrSignature-BQHNhRj6.js.map → verifyKeyPathSchnorrSignature-DxPbeSdQ.js.map} +1 -1
  227. package/dist/{verifyScriptPathSchnorrSignature-CpjSTfMj.js → verifyScriptPathSchnorrSignature-B3-VkvnK.js} +6 -4
  228. package/dist/{verifyScriptPathSchnorrSignature-CpjSTfMj.js.map → verifyScriptPathSchnorrSignature-B3-VkvnK.js.map} +1 -1
  229. package/dist/{verifyScriptPathSchnorrSignature-BKZihCjk.cjs → verifyScriptPathSchnorrSignature-D2t1yACC.cjs} +2 -2
  230. package/dist/{verifyScriptPathSchnorrSignature-BKZihCjk.cjs.map → verifyScriptPathSchnorrSignature-D2t1yACC.cjs.map} +1 -1
  231. package/package.json +3 -3
  232. package/dist/PayoutManager-B6GFbG3X.cjs +0 -2
  233. package/dist/PayoutManager-B6GFbG3X.cjs.map +0 -1
  234. package/dist/PayoutManager-BSwbUQeF.js.map +0 -1
  235. package/dist/PeginManager-IgDHy_eU.cjs +0 -2
  236. package/dist/ProtocolParams.abi-Db2USqQm.cjs +0 -2
  237. package/dist/ProtocolParams.abi-Db2USqQm.cjs.map +0 -1
  238. package/dist/ProtocolParams.abi-q0AidvYX.js.map +0 -1
  239. package/dist/buildAndBroadcastReclaim-C55KaRJF.js +0 -2139
  240. package/dist/buildAndBroadcastReclaim-C55KaRJF.js.map +0 -1
  241. package/dist/buildAndBroadcastReclaim-CefzR6i_.cjs +0 -2
  242. package/dist/buildAndBroadcastReclaim-CefzR6i_.cjs.map +0 -1
  243. package/dist/constants-BTZdyOuk.cjs +0 -2
  244. package/dist/constants-BTZdyOuk.cjs.map +0 -1
  245. package/dist/constants-D5vORbQE.js +0 -29
  246. package/dist/constants-D5vORbQE.js.map +0 -1
  247. package/dist/onChainBtcPubkey-BlzxYImF.cjs +0 -2
  248. package/dist/onChainBtcPubkey-BlzxYImF.cjs.map +0 -1
  249. package/dist/onChainBtcPubkey-CCTdl53s.js +0 -159
  250. package/dist/onChainBtcPubkey-CCTdl53s.js.map +0 -1
  251. package/dist/pegin-registration-client-B0j6ugt9.cjs +0 -2
  252. package/dist/pegin-registration-client-B0j6ugt9.cjs.map +0 -1
  253. package/dist/pegin-registration-client-ilkV1mLP.js +0 -878
  254. package/dist/pegin-registration-client-ilkV1mLP.js.map +0 -1
  255. package/dist/signing-3l5Aqd-H.cjs +0 -2
  256. package/dist/signing-3l5Aqd-H.cjs.map +0 -1
  257. package/dist/signing-Dhr2t0Z6.js.map +0 -1
  258. package/dist/toRefundInputs-10RYIfh1.js +0 -559
  259. package/dist/toRefundInputs-10RYIfh1.js.map +0 -1
  260. package/dist/toRefundInputs-a-0HRyrl.cjs +0 -2
  261. package/dist/toRefundInputs-a-0HRyrl.cjs.map +0 -1
  262. package/dist/types-BueQoMjO.cjs +0 -2
  263. package/dist/types-BueQoMjO.cjs.map +0 -1
  264. package/dist/types-DGkejGZH.js +0 -456
  265. package/dist/types-DGkejGZH.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"verifyScriptPathSchnorrSignature-BKZihCjk.cjs","sources":["../src/tbv/core/primitives/challengers.ts","../src/tbv/core/primitives/psbt/assertWasmPeginSizing.ts","../src/tbv/core/primitives/psbt/depositorClaim.ts","../src/tbv/core/primitives/psbt/pegin.ts","../src/tbv/core/primitives/scripts/payout.ts","../src/tbv/core/primitives/utils/taproot.ts","../src/tbv/core/primitives/psbt/assertPayoutFeeBand.ts","../src/tbv/core/primitives/psbt/standardPayoutScript.ts","../src/tbv/core/primitives/psbt/payout.ts","../src/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.ts","../src/tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature.ts"],"sourcesContent":["/**\n * Challenger derivation utilities — counting local challengers for UI-level\n * validation (e.g. minimum deposit amounts) and deriving the exact\n * local-challenger set a claimer's tx graph is built from.\n */\n\nimport { processPublicKeyToXOnly } from \"./utils/bitcoin\";\n\n/**\n * Normalize a public key to lowercase x-only hex for reliable comparison.\n *\n * Handles `0x` prefixes, compressed (33-byte), and uncompressed (65-byte) keys.\n */\nfunction normalizeKey(key: string): string {\n return processPublicKeyToXOnly(key).toLowerCase();\n}\n\n/**\n * Compute the number of local challengers for a vault.\n *\n * Mirrors the VP's `compute_num_challengers()` logic:\n * local challengers = {vault_provider} ∪ {vault_keepers} − {depositor}\n *\n * Keys are normalized to x-only lowercase hex before comparison, so\n * `0x`-prefixed, compressed, or mixed-case keys are handled correctly.\n *\n * @param vaultProviderPubkey - Vault provider BTC public key\n * @param vaultKeeperPubkeys - Vault keeper BTC public keys\n * @param depositorPubkey - Depositor (claimer) BTC public key\n * @returns Number of local challengers\n */\nexport function computeNumLocalChallengers(\n vaultProviderPubkey: string,\n vaultKeeperPubkeys: string[],\n depositorPubkey: string,\n): number {\n const localSet = new Set<string>();\n localSet.add(normalizeKey(vaultProviderPubkey));\n for (const vk of vaultKeeperPubkeys) {\n localSet.add(normalizeKey(vk));\n }\n localSet.delete(normalizeKey(depositorPubkey));\n return localSet.size;\n}\n\n/** Roles a claimer can hold; determines which local-challenger rule applies. */\nexport interface DeriveLocalChallengersParams {\n /** The graph's claimer (VP, a vault keeper, or the depositor). */\n claimerBtcPubkey: string;\n /** Depositor registered on-chain for the vault. */\n depositorBtcPubkey: string;\n /** Vault provider registered on-chain for the vault. */\n vaultProviderBtcPubkey: string;\n /** Vault keepers registered on-chain for the vault. */\n vaultKeeperBtcPubkeys: string[];\n}\n\n/**\n * Derive the local-challenger set for a claimer's graph.\n *\n * Byte-for-byte mirror of btc-vault `derive_challengers_for`\n * (`crates/vault/src/tx_graph/graph.rs:257-284`):\n * - depositor-as-claimer → vault keepers only (VP excluded)\n * - VP- or VK-claimer → `({VP} ∪ VKs) − {claimer}`, sorted and deduplicated\n *\n * The protocol guarantees the depositor is not a vault keeper\n * (`TxGraphParams::validate`), so the depositor filter in the first branch is\n * defense-in-depth; it surfaces a clear error if a misconfigured context ever\n * violates the invariant.\n *\n * @throws If no local challenger remains, or (depositor path) the keeper set\n * contains duplicates — both mean the signing context is misconfigured.\n */\nexport function deriveLocalChallengers(\n params: DeriveLocalChallengersParams,\n): string[] {\n const claimer = normalizeKey(params.claimerBtcPubkey);\n const depositor = normalizeKey(params.depositorBtcPubkey);\n const vaultKeepers = params.vaultKeeperBtcPubkeys.map(normalizeKey);\n\n if (claimer === depositor) {\n const filtered = vaultKeepers.filter((k) => k !== depositor);\n if (filtered.length === 0) {\n throw new Error(\n \"Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)\",\n );\n }\n if (new Set(filtered).size !== filtered.length) {\n throw new Error(\n \"Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured\",\n );\n }\n return filtered;\n }\n\n // Rust sorts then dedups before filtering; a Set keeps insertion order, so\n // sort after deduping for the same result.\n const deduped = [\n ...new Set([normalizeKey(params.vaultProviderBtcPubkey), ...vaultKeepers]),\n ].sort();\n const filtered = deduped.filter((k) => k !== claimer);\n if (filtered.length === 0) {\n throw new Error(\n `Cannot derive localChallengers: no vault provider or vault keeper remains after excluding claimer ${claimer}`,\n );\n }\n return filtered;\n}\n","/**\n * Cross-check the values and outputs WASM returns from `createPrePeginTransaction`\n * against independently-known expectations before they feed a signed\n * Bitcoin transaction or the on-chain PegIn registration.\n *\n * CLAUDE.md critical path #1: the Rust/WASM layer computes\n * `htlcValue = peginAmount + depositorClaimValue + p2aAnchorValue +\n * minPeginFee` internally (the anchor term is 0 for graph versions without a\n * P2A anchor, 240 sats for v2/v3) and JS receives the outputs with no runtime\n * validation. A doctored or buggy binary that returns a different\n * `peginAmount`, an out-of-formula `htlcValue`, or a wrong\n * `depositorClaimValue` would otherwise be committed verbatim - taxing the\n * depositor or starving the downstream tx graph of fees.\n *\n * @module primitives/psbt/assertWasmPeginSizing\n */\n\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { script as bscript, opcodes, payments } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\nimport {\n computeMinClaimValue,\n computeMinPeginFee,\n peginP2aAnchorOutput,\n tapInternalPubkey,\n type PrePeginResult,\n} from \"../../wasm\";\n\nimport {\n MAX_REASONABLE_PEGIN_VBYTES,\n peginOutputCount,\n} from \"../../utils/fee/constants\";\nimport type { ParsedOutput } from \"../../utils/transaction/fundPeginTransaction\";\nimport {\n TAPSCRIPT_LEAF_VERSION,\n assertEccInitialized,\n deriveBip86ScriptPubKeyHex,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\n\nimport {\n PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS,\n PRE_PEGIN_AUTH_SCRIPT_PREFIX,\n PRE_PEGIN_CPFP_VALUE_SATS,\n PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT,\n PRE_PEGIN_TX_LOCKTIME,\n PRE_PEGIN_TX_VERSION,\n} from \"./constants\";\nimport type { PrePeginParams } from \"./pegin\";\n\nconst PREIMAGE_LENGTH_BYTES = 32;\nconst MAX_U16 = 0xffff;\nconst SEC1_EVEN_Y_PREFIX = 0x02;\n\ntype ScriptChunk = number | Buffer;\n\ntype PrePeginHtlcParams = Pick<\n PrePeginParams,\n | \"depositorPubkey\"\n | \"vaultProviderPubkey\"\n | \"vaultKeeperPubkeys\"\n | \"universalChallengerPubkeys\"\n | \"timelockRefund\"\n>;\n\ntype PeginPayoutParams = Pick<\n PrePeginParams,\n | \"depositorPubkey\"\n | \"vaultProviderPubkey\"\n | \"vaultKeeperPubkeys\"\n | \"universalChallengerPubkeys\"\n>;\n\ninterface ExpectedPrePeginHtlc {\n hashlockScript: Buffer;\n hashlockControlBlock: Buffer;\n refundScript: Buffer;\n refundControlBlock: Buffer;\n scriptPubKey: Buffer;\n tapMerkleRoot: Buffer;\n}\n\nfunction normalizeXOnlyKey(value: string, label: string): string {\n const key = stripHexPrefix(value).toLowerCase();\n if (!/^[0-9a-f]{64}$/.test(key)) {\n throw new Error(`${label} must be a 32-byte x-only public key.`);\n }\n\n try {\n secp256k1.Point.fromBytes(\n Buffer.concat([\n Buffer.from([SEC1_EVEN_Y_PREFIX]),\n Buffer.from(key, \"hex\"),\n ]),\n );\n } catch {\n throw new Error(`${label} is not a secp256k1 x-coordinate.`);\n }\n\n return key;\n}\n\nfunction normalizeKeyGroup(values: readonly string[], label: string): string[] {\n if (values.length === 0) {\n throw new Error(`${label} must not be empty.`);\n }\n\n const keys = values\n .map((value, index) => normalizeXOnlyKey(value, `${label}[${index}]`))\n .sort();\n if (new Set(keys).size !== keys.length) {\n throw new Error(`${label} must not contain duplicate keys.`);\n }\n return keys;\n}\n\nfunction nOfNChunks(keys: readonly string[], verify: boolean): ScriptChunk[] {\n const chunks: ScriptChunk[] = [];\n keys.forEach((key, index) => {\n chunks.push(\n Buffer.from(key, \"hex\"),\n index === 0 ? opcodes.OP_CHECKSIG : opcodes.OP_CHECKSIGADD,\n );\n });\n chunks.push(\n bscript.number.encode(keys.length),\n verify ? opcodes.OP_NUMEQUALVERIFY : opcodes.OP_NUMEQUAL,\n );\n return chunks;\n}\n\n/**\n * Derive the canonical Pre-PegIn HTLC without using vault WASM output.\n */\nexport function deriveExpectedPrePeginHtlc(\n params: PrePeginHtlcParams,\n hashlock: string,\n): ExpectedPrePeginHtlc {\n assertEccInitialized();\n\n const depositor = normalizeXOnlyKey(\n params.depositorPubkey,\n \"depositorPubkey\",\n );\n const vaultProvider = normalizeXOnlyKey(\n params.vaultProviderPubkey,\n \"vaultProviderPubkey\",\n );\n const vaultKeepers = normalizeKeyGroup(\n params.vaultKeeperPubkeys,\n \"vaultKeeperPubkeys\",\n );\n const universalChallengers = normalizeKeyGroup(\n params.universalChallengerPubkeys,\n \"universalChallengerPubkeys\",\n );\n const cleanHashlock = stripHexPrefix(hashlock).toLowerCase();\n if (!/^[0-9a-f]{64}$/.test(cleanHashlock)) {\n throw new Error(\"hashlock must be 32 bytes.\");\n }\n if (\n !Number.isInteger(params.timelockRefund) ||\n params.timelockRefund < 1 ||\n params.timelockRefund > MAX_U16\n ) {\n throw new Error(\"timelockRefund must be an integer from 1 to 65535.\");\n }\n\n const hashlockScript = bscript.compile([\n opcodes.OP_SIZE,\n bscript.number.encode(PREIMAGE_LENGTH_BYTES),\n opcodes.OP_EQUALVERIFY,\n opcodes.OP_SHA256,\n Buffer.from(cleanHashlock, \"hex\"),\n opcodes.OP_EQUALVERIFY,\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n Buffer.from(vaultProvider, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n ...nOfNChunks(vaultKeepers, true),\n ...nOfNChunks(universalChallengers, false),\n ]);\n const refundScript = bscript.compile([\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n bscript.number.encode(params.timelockRefund),\n opcodes.OP_CHECKSEQUENCEVERIFY,\n ]);\n const scriptTree: [\n { output: Buffer; version: number },\n { output: Buffer; version: number },\n ] = [\n { output: hashlockScript, version: TAPSCRIPT_LEAF_VERSION },\n { output: refundScript, version: TAPSCRIPT_LEAF_VERSION },\n ];\n const { hash, output, witness } = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree,\n redeem: {\n output: hashlockScript,\n redeemVersion: TAPSCRIPT_LEAF_VERSION,\n },\n });\n const hashlockControlBlock = witness?.[witness.length - 1];\n const refundPayment = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree,\n redeem: {\n output: refundScript,\n redeemVersion: TAPSCRIPT_LEAF_VERSION,\n },\n });\n const refundControlBlock =\n refundPayment.witness?.[refundPayment.witness.length - 1];\n if (!hash || !output || !hashlockControlBlock || !refundControlBlock) {\n throw new Error(\"Failed to derive the expected Pre-PegIn HTLC output.\");\n }\n\n return {\n hashlockScript,\n hashlockControlBlock,\n refundScript,\n refundControlBlock,\n scriptPubKey: output,\n tapMerkleRoot: hash,\n };\n}\n\n/**\n * Derive the canonical PegIn vault (payout) scriptPubKey without using vault\n * WASM output.\n */\nexport function deriveExpectedPeginPayoutScriptPubKey(\n params: PeginPayoutParams,\n timelockPegin: number,\n): Buffer {\n assertEccInitialized();\n\n const depositor = normalizeXOnlyKey(\n params.depositorPubkey,\n \"depositorPubkey\",\n );\n const vaultProvider = normalizeXOnlyKey(\n params.vaultProviderPubkey,\n \"vaultProviderPubkey\",\n );\n const vaultKeepers = normalizeKeyGroup(\n params.vaultKeeperPubkeys,\n \"vaultKeeperPubkeys\",\n );\n const universalChallengers = normalizeKeyGroup(\n params.universalChallengerPubkeys,\n \"universalChallengerPubkeys\",\n );\n\n // btc-vault crates/vault/src/connectors/pegin_payout.rs at 2c1177ec,\n // 27c0062b, and e1e50f66 uses this single leaf and the fixed internal key.\n const payoutScript = bscript.compile([\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n Buffer.from(vaultProvider, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n ...nOfNChunks(vaultKeepers, true),\n ...nOfNChunks(universalChallengers, true),\n bscript.number.encode(timelockPegin),\n opcodes.OP_CHECKSEQUENCEVERIFY,\n ]);\n const { output } = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree: { output: payoutScript, version: TAPSCRIPT_LEAF_VERSION },\n });\n if (!output) {\n throw new Error(\"Failed to derive the expected PegIn payout output.\");\n }\n\n return output;\n}\n\n/**\n * Assert the WASM Pre-PegIn sizing result is internally consistent and\n * matches what the caller requested.\n *\n * Two layers of checks. Pure-JS, binary-INDEPENDENT: the per-HTLC\n * `peginAmount` must echo the requested amount, array lengths must match,\n * every value must be positive, and the implied per-HTLC reserve\n * (`htlcValue - peginAmount - depositorClaimValue`) must be strictly\n * positive and under the plausibility cap\n * ({@link MAX_REASONABLE_PEGIN_VBYTES}). WASM-vs-WASM consistency: the\n * reserve must also EXACTLY equal `computeMinPeginFee(version, ...) +\n * p2aAnchorValue(version)` (like the `computeMinClaimValue` check) -\n * exact, but both sides come from the same binary, which is why the\n * independent bounds above are kept alongside it.\n *\n * The resume rebuild (`deposit-terms/rebuildDepositTermsCore.ts`) mirrors these\n * bounds (positivity + reserve band) - keep the two in sync when editing.\n *\n * @param result - The result returned by `createPrePeginTransaction`.\n * @param params - The parameters that were passed to build it.\n * @returns The independently computed `minPeginFee` this function already\n * asserted against `result`'s implied reserve - callers that need the\n * value should reuse this instead of recomputing it.\n * @throws If any value is missing, non-positive, mismatched against the\n * request, or outside the protocol formula.\n */\nexport async function assertWasmPeginSizing(\n result: PrePeginResult,\n params: PrePeginParams,\n): Promise<bigint> {\n const expectedCount = params.pegInAmounts.length;\n\n if (params.hashlocks.length !== expectedCount) {\n throw new Error(\n `Pre-PegIn has ${params.hashlocks.length} hashlock(s), expected ` +\n `${expectedCount} (one per requested deposit).`,\n );\n }\n\n // Count: every parallel array must carry exactly one entry per requested\n // deposit, otherwise the per-HTLC indexing downstream is meaningless.\n if (result.htlcValues.length !== expectedCount) {\n throw new Error(\n `WASM Pre-PegIn returned ${result.htlcValues.length} HTLC value(s), ` +\n `expected ${expectedCount} (one per requested deposit).`,\n );\n }\n if (\n result.peginAmounts.length !== expectedCount ||\n result.htlcScriptPubKeys.length !== expectedCount ||\n result.htlcAddresses.length !== expectedCount\n ) {\n throw new Error(\n `WASM Pre-PegIn returned mismatched array lengths ` +\n `(htlcValues=${result.htlcValues.length}, ` +\n `peginAmounts=${result.peginAmounts.length}, ` +\n `htlcScriptPubKeys=${result.htlcScriptPubKeys.length}, ` +\n `htlcAddresses=${result.htlcAddresses.length}); ` +\n `expected ${expectedCount} each.`,\n );\n }\n\n // depositorClaimValue: positivity + WASM-vs-WASM consistency. Sized by the\n // tx-graph `feeRate` (see PrePeginParams.feeRate), so the standalone\n // `computeMinClaimValue` must reproduce the constructor's internal value.\n if (result.depositorClaimValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn returned non-positive depositorClaimValue ` +\n `${result.depositorClaimValue}; expected > 0.`,\n );\n }\n const expectedClaimValue = await computeMinClaimValue(\n // Must price the same graph version the builder constructed.\n params.vaultCoreVersion,\n params.numLocalChallengers,\n params.universalChallengerPubkeys.length,\n params.councilQuorum,\n params.councilSize,\n params.feeRate,\n );\n if (result.depositorClaimValue !== expectedClaimValue) {\n throw new Error(\n `WASM Pre-PegIn depositorClaimValue ${result.depositorClaimValue} does ` +\n `not match the independently computed minimum claim value ` +\n `${expectedClaimValue} (vaultCoreVersion=${params.vaultCoreVersion}, ` +\n `numLocalChallengers=${params.numLocalChallengers}, ` +\n `numUniversalChallengers=${params.universalChallengerPubkeys.length}, ` +\n `councilQuorum=${params.councilQuorum}, councilSize=${params.councilSize}, ` +\n `feeRate=${params.feeRate}).`,\n );\n }\n\n // The per-HTLC reserve above pegin+claim decomposes into the version's\n // P2A anchor value (0 when the version has no anchor) plus the exact\n // minimum PegIn fee. Both terms are Rust-model values fetched through\n // independent WASM entry points; the builder must reproduce their sum.\n // The Rust fee model sizes the PegIn input witness from the vault keeper\n // and universal challenger counts (btc-vault `PegInTx::estimate_vsize`).\n const anchor = await peginP2aAnchorOutput(params.vaultCoreVersion);\n const anchorValue = anchor?.value ?? 0n;\n const expectedPeginFee = await computeMinPeginFee(\n params.vaultCoreVersion,\n params.vaultKeeperPubkeys.length,\n params.universalChallengerPubkeys.length,\n params.minPeginFeeRate,\n );\n const expectedReserve = expectedPeginFee + anchorValue;\n // Binary-INDEPENDENT plausibility cap: the exact identity below compares\n // WASM against WASM, so a consistently doctored binary could satisfy it\n // with an inflated reserve. This pure-JS bound (max standard-relay tx\n // vbytes x the caller's fee rate) caps how much a compromised binary can\n // burn.\n const maxImpliedReserve =\n params.minPeginFeeRate * MAX_REASONABLE_PEGIN_VBYTES;\n\n for (let i = 0; i < expectedCount; i++) {\n const requested = params.pegInAmounts[i];\n const peginAmount = result.peginAmounts[i];\n const htlcValue = result.htlcValues[i];\n\n // Amount echo (strongest, fully independent): the recorded pegin amount\n // must equal exactly what the caller requested. A mismatch is the\n // WASM-tax attack - the contract would record a doctored amount while the\n // depositor's wallet funds the original, and the difference is a\n // WASM-controlled tax.\n if (peginAmount !== requested) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] ${peginAmount} does not match the ` +\n `requested amount ${requested}; refusing to build a tx whose ` +\n `recorded amount differs from the depositor's request.`,\n );\n }\n if (peginAmount <= 0n) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] is non-positive (${peginAmount}); ` +\n `expected > 0.`,\n );\n }\n if (htlcValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] is non-positive (${htlcValue}); ` +\n `expected > 0.`,\n );\n }\n\n // Formula: htlcValue = peginAmount + depositorClaimValue +\n // p2aAnchorValue + minPeginFee. The reserve must match exactly - a\n // shortfall starves the PegIn's fee/anchor, an excess locks sats\n // irrecoverably in the HTLC.\n const impliedReserve = htlcValue - peginAmount - result.depositorClaimValue;\n // Independent JS-side bounds first (see maxImpliedReserve above): the\n // reserve must be strictly positive (a zero reserve starves the PegIn\n // of its fee) and plausibly sized, regardless of what the binary's own\n // reference entry points claim.\n if (impliedReserve <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} does not strictly ` +\n `cover peginAmount ${peginAmount} + depositorClaimValue ` +\n `${result.depositorClaimValue} + a PegIn reserve (implied ` +\n `reserve ${impliedReserve}).`,\n );\n }\n if (impliedReserve > maxImpliedReserve) {\n throw new Error(\n `WASM Pre-PegIn implied reserve for HTLC[${i}] (${impliedReserve} ` +\n `sat) exceeds the plausibility cap ${maxImpliedReserve} sat ` +\n `(minPeginFeeRate=${params.minPeginFeeRate} × ` +\n `${MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${htlcValue} ` +\n `appears grossly inflated.`,\n );\n }\n if (impliedReserve !== expectedReserve) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} implies a PegIn ` +\n `fee+anchor reserve of ${impliedReserve} sat, expected exactly ` +\n `${expectedReserve} sat (minPeginFee ${expectedPeginFee} + ` +\n `p2aAnchor ${anchorValue} for vaultCoreVersion ` +\n `${params.vaultCoreVersion}, vaultKeepers=` +\n `${params.vaultKeeperPubkeys.length}, universalChallengers=` +\n `${params.universalChallengerPubkeys.length}, minPeginFeeRate=` +\n `${params.minPeginFeeRate}).`,\n );\n }\n }\n\n return expectedPeginFee;\n}\n\n/**\n * A funded Pre-PegIn's HTLC outputs disagree with the expected value or\n * scriptPubKey.\n *\n * Typed so a caller trialling several parameter sets can tell \"the transaction\n * definitively does not match these parameters\" - a genuine, informative\n * rejection - from \"evaluating this parameter set failed\", which says nothing\n * about the transaction and must not be counted as a rejection. Without the\n * distinction an incidental failure on the true parameter set silently removes\n * it from consideration and a look-alike wins.\n */\nexport class HtlcOutputMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HtlcOutputMismatchError\";\n }\n}\n\n/**\n * Bind the validated metadata to the bytes that actually get funded and\n * signed.\n *\n * `assertWasmPeginSizing` proves the WASM values match the request and the\n * protocol formula. It also checks the script metadata count. The transaction\n * the depositor funds and signs is `result.txHex`. If the encoded tx carried a\n * different HTLC output value or script than the metadata, the depositor would\n * fund a transaction whose outputs differ from the checked metadata. This\n * function closes that link.\n *\n * The WASM lays out HTLC outputs first (vouts `0..N-1`), then the optional\n * auth-anchor OP_RETURN, then the CPFP anchor - so we only compare the first\n * `htlcValues.length` outputs.\n *\n * @param outputs - Outputs parsed from the unfunded Pre-PegIn tx hex.\n * @param htlcValues - The (already value-validated) per-HTLC values.\n * @param htlcScriptPubKeys - The per-HTLC scriptPubKeys (hex).\n * @throws {HtlcOutputMismatchError} If the encoded outputs are too few, or any\n * HTLC output's value or scriptPubKey disagrees with the validated metadata.\n */\nexport function assertEncodedHtlcOutputsMatch(\n outputs: readonly ParsedOutput[],\n htlcValues: readonly bigint[],\n htlcScriptPubKeys: readonly string[],\n): void {\n if (outputs.length < htlcValues.length) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn tx has ${outputs.length} output(s), fewer than the ` +\n `${htlcValues.length} HTLC output(s) the cross-check validated.`,\n );\n }\n\n for (let i = 0; i < htlcValues.length; i++) {\n const encodedValue = BigInt(outputs[i].value);\n if (encodedValue !== htlcValues[i]) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn HTLC output[${i}] value ${encodedValue} does not ` +\n `match the cross-checked htlcValue ${htlcValues[i]}; the funded/signed ` +\n `tx would not pay the validated amount.`,\n );\n }\n\n const encodedScript = outputs[i].script.toString(\"hex\").toLowerCase();\n const expectedScript = htlcScriptPubKeys[i].toLowerCase();\n if (encodedScript !== expectedScript) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn HTLC output[${i}] scriptPubKey ${encodedScript} does ` +\n `not match the cross-checked htlcScriptPubKey ${expectedScript}.`,\n );\n }\n }\n}\n\n/**\n * Validate the unfunded Pre-PegIn protocol output layout.\n *\n * This check rejects a wrong output count, a wrong auth or CPFP output, and an\n * HTLC output that does not match the request. Run it before value summing,\n * UTXO selection, or signing.\n */\nexport function assertUnfundedPrePeginOutputLayout(\n outputs: readonly ParsedOutput[],\n htlcValues: readonly bigint[],\n htlcScriptPubKeys: readonly string[],\n params: PrePeginParams,\n authAnchorHash: string | undefined,\n version: number,\n locktime: number,\n): void {\n if (version !== PRE_PEGIN_TX_VERSION) {\n throw new Error(\n `WASM Pre-PegIn transaction version ${version}; expected ` +\n `${PRE_PEGIN_TX_VERSION}.`,\n );\n }\n if (locktime !== PRE_PEGIN_TX_LOCKTIME) {\n throw new Error(\n `WASM Pre-PegIn transaction locktime ${locktime}; expected ` +\n `${PRE_PEGIN_TX_LOCKTIME}.`,\n );\n }\n\n if (htlcValues.length < PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT) {\n throw new Error(\n `WASM Pre-PegIn output layout has ${htlcValues.length} HTLC output(s); ` +\n `expected at least ${PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT}.`,\n );\n }\n\n const authOutputIndex = htlcValues.length;\n const cpfpOutputIndex =\n authOutputIndex + Number(authAnchorHash !== undefined);\n const expectedCount = peginOutputCount(\n htlcValues.length,\n authAnchorHash !== undefined,\n );\n if (outputs.length !== expectedCount) {\n throw new Error(\n `WASM Pre-PegIn output layout has ${outputs.length} output(s); ` +\n `expected exactly ${expectedCount}.`,\n );\n }\n\n assertEncodedHtlcOutputsMatch(outputs, htlcValues, htlcScriptPubKeys);\n\n for (let i = 0; i < htlcValues.length; i++) {\n const expectedScript = deriveExpectedPrePeginHtlc(\n params,\n params.hashlocks[i],\n ).scriptPubKey;\n if (!outputs[i].script.equals(expectedScript)) {\n throw new HtlcOutputMismatchError(\n `Pre-PegIn HTLC output[${i}] does not match the independently ` +\n `derived scriptPubKey.`,\n );\n }\n }\n\n if (authAnchorHash !== undefined) {\n const authOutput = outputs[authOutputIndex];\n if (authOutput.value !== PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS) {\n throw new Error(\n `WASM Pre-PegIn auth output[${authOutputIndex}] value ` +\n `${authOutput.value} sat; expected exactly ` +\n `${PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS} sat.`,\n );\n }\n\n const encodedAuthScript = authOutput.script.toString(\"hex\").toLowerCase();\n const expectedAuthScript =\n PRE_PEGIN_AUTH_SCRIPT_PREFIX + authAnchorHash.toLowerCase();\n if (encodedAuthScript !== expectedAuthScript) {\n throw new Error(\n `WASM Pre-PegIn auth output[${authOutputIndex}] scriptPubKey ` +\n `${encodedAuthScript}; expected ${expectedAuthScript}.`,\n );\n }\n }\n\n const cpfpOutput = outputs[cpfpOutputIndex];\n if (cpfpOutput.value !== PRE_PEGIN_CPFP_VALUE_SATS) {\n throw new Error(\n `WASM Pre-PegIn CPFP output[${cpfpOutputIndex}] value ` +\n `${cpfpOutput.value} sat; expected exactly ` +\n `${PRE_PEGIN_CPFP_VALUE_SATS} sat.`,\n );\n }\n\n const encodedCpfpScript = cpfpOutput.script.toString(\"hex\").toLowerCase();\n const expectedCpfpScript = stripHexPrefix(\n deriveBip86ScriptPubKeyHex(params.depositorPubkey),\n ).toLowerCase();\n if (encodedCpfpScript !== expectedCpfpScript) {\n throw new Error(\n `WASM Pre-PegIn CPFP output[${cpfpOutputIndex}] scriptPubKey ` +\n `${encodedCpfpScript}; expected depositor BIP-86 script ` +\n `${expectedCpfpScript}.`,\n );\n }\n}\n","/**\n * Depositor-claim output descriptor\n *\n * The PegIn transaction's vout 1 carries `depositorClaimValue` — the reserve\n * that funds the depositor's own `claim_tx`, their recourse if the vault\n * provider fails during peg-out.\n *\n * It is btc-vault's `SingleKeyConnector` (`crates/vault/src/connectors/mod.rs`):\n * a Taproot output with the NUMS internal key (key path provably unspendable)\n * and exactly one tapleaf at depth 0, `<depositor> OP_CHECKSIG`. No timelock,\n * no counterparty key — the depositor's Schnorr signature alone spends it.\n *\n * Two call sites need this, and they must not drift:\n * - `assertPeginTxShape` (./pegin) validates the encoded PegIn output against\n * the scriptPubKey before the depositor signs the peg-in.\n * - `buildReclaimPsbt` (./reclaim) spends that same output, and needs the leaf\n * script and control block to populate the PSBT's `tapLeafScript`.\n *\n * Keeping one definition here is what makes the reclaim's script binding\n * meaningful: the bytes the reclaim signs against are the same bytes the\n * peg-in validated.\n *\n * @module primitives/psbt/depositorClaim\n */\n\nimport { tapInternalPubkey } from \"../../wasm\";\nimport { Buffer } from \"buffer\";\nimport { payments, script as bscript, opcodes } from \"bitcoinjs-lib\";\n\nimport {\n TAPSCRIPT_LEAF_VERSION,\n hexToUint8Array,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\n\n/**\n * Vout of the depositor-claim output in every PegIn version (btc-vault: vault\n * at 0, depositor claim at 1, optional P2A anchor appended after).\n *\n * Version-invariant: the graph version dispatches only the trailing P2A anchor\n * (absent in v1, 240 sats at vout 2 in v2/v3). Nothing touches vout 1.\n */\nexport const PEGIN_DEPOSITOR_CLAIM_VOUT = 1;\n\n/** The single-leaf taptree's spend material for one depositor key. */\nexport interface DepositorClaimDescriptor {\n /** P2TR scriptPubKey the PegIn pays at {@link PEGIN_DEPOSITOR_CLAIM_VOUT}. */\n scriptPubKey: Buffer;\n /** The one tapleaf: `<depositor> OP_CHECKSIG`, 34 bytes. */\n leafScript: Buffer;\n /**\n * Control block for that leaf: `[leafVersion | outputKeyParity] || NUMS`,\n * 33 bytes. The tree has a single leaf at depth 0, so it carries no sibling\n * hashes.\n */\n controlBlock: Buffer;\n /**\n * The NUMS internal key the taptree commits to — the PSBT's `tapInternalKey`.\n * Carried here so an input's internal key and control block provably come\n * from the same derivation.\n */\n internalKey: Buffer;\n}\n\n/**\n * Derive the depositor-claim output's spend material in JS, independently of\n * WASM — the Rust `SingleKeyConnector` has no WASM wrapper, so this is the\n * only derivation available on the JS side.\n *\n * Takes no graph version: the connector is identical across v1/v2/v3. A future\n * `VAULT_WASM_COMMIT` bump that changed it would break `assertPeginTxShape` at\n * peg-in build time, which is where that regression should surface.\n *\n * @param depositorPubkey - x-only depositor pubkey, 64-char hex (no 0x prefix)\n * @throws If bitcoinjs cannot derive the P2TR output or its control block\n */\nexport function deriveDepositorClaimDescriptor(\n depositorPubkey: string,\n): DepositorClaimDescriptor {\n // Validate before compiling: `bscript.compile` happily emits a shorter push\n // for a truncated key, yielding a well-formed script that is not the claim\n // leaf. Downstream that becomes a scriptPubKey nobody can spend, so reject\n // here rather than deriving something plausible. Mirrors the same check in\n // `deriveBip86ScriptPubKeyHex`.\n const cleanPubkey = stripHexPrefix(depositorPubkey);\n if (!/^[0-9a-fA-F]{64}$/.test(cleanPubkey)) {\n throw new Error(\n \"Invalid depositor pubkey for the depositor-claim script: must be an \" +\n \"x-only key of 64 hex characters (32 bytes).\",\n );\n }\n\n const leafScript = bscript.compile([\n Buffer.from(hexToUint8Array(cleanPubkey)),\n opcodes.OP_CHECKSIG,\n ]);\n\n const internalKey = Buffer.from(tapInternalPubkey);\n const redeem = { output: leafScript, redeemVersion: TAPSCRIPT_LEAF_VERSION };\n const { output, witness } = payments.p2tr({\n internalPubkey: internalKey,\n scriptTree: { output: leafScript },\n redeem,\n });\n\n if (!output) {\n throw new Error(\n \"Failed to derive the depositor-claim P2TR scriptPubKey for PegIn output validation\",\n );\n }\n // bitcoinjs appends the control block last in the script-path witness stack\n // ([...redeemWitness, script, controlBlock]); with `redeem` supplied it is\n // always present, but a missing entry must fail rather than sign unspendably.\n const controlBlock = witness?.[witness.length - 1];\n if (!controlBlock) {\n throw new Error(\n \"Failed to derive the depositor-claim tapleaf control block; refusing to build an unspendable input\",\n );\n }\n\n return { scriptPubKey: output, leafScript, controlBlock, internalKey };\n}\n\n/**\n * The depositor-claim output's scriptPubKey alone — the peg-in validation path,\n * which has no need of the spend material.\n */\nexport function deriveDepositorClaimScriptPubKey(\n depositorPubkey: string,\n): Buffer {\n return deriveDepositorClaimDescriptor(depositorPubkey).scriptPubKey;\n}\n","/**\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 { Transaction } from \"bitcoinjs-lib\";\nimport {\n buildPeginTxFromPrePegin,\n computeMinClaimValue,\n createPrePeginTransaction,\n peginP2aAnchorOutput,\n validatePeginP2aAnchor,\n type Network,\n} from \"../../wasm\";\n\nimport { parseUnfundedWasmTransaction } from \"../../utils/transaction/fundPeginTransaction\";\nimport { stripHexPrefix, uint8ArrayToHex } from \"../utils/bitcoin\";\n\nimport {\n assertUnfundedPrePeginOutputLayout,\n assertWasmPeginSizing,\n deriveExpectedPeginPayoutScriptPubKey,\n} from \"./assertWasmPeginSizing\";\nimport {\n PEGIN_INPUT_SEQUENCE,\n PEGIN_TX_LOCKTIME,\n PEGIN_TX_VERSION_CORE_1,\n PEGIN_TX_VERSION_CORE_2_AND_3,\n} from \"./constants\";\nimport {\n PEGIN_DEPOSITOR_CLAIM_VOUT,\n deriveDepositorClaimScriptPubKey,\n} from \"./depositorClaim\";\n\n/**\n * Parameters for building an unfunded Pre-PegIn PSBT\n */\nexport interface PrePeginParams {\n /**\n * Vault core (tx-graph) version to build. Fresh deposits use the contract's\n * `ProtocolParams.activeVaultCoreVersion()`; resumed vaults use their\n * stamped on-chain `vaultCoreVersion`. The WASM facade fails closed on\n * versions it wasn't compiled with.\n */\n vaultCoreVersion: number;\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 /** TX-graph fee rate in sat/vB from contract offchain params; sizes the depositor claim value */\n feeRate: bigint;\n /** Minimum PegIn fee rate in sat/vB from contract offchain params; sizes the PegIn tx fee */\n minPeginFeeRate: 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 * The largest PegIn timelock in blocks. The engine stores it as a Rust u16.\n */\nconst MAX_TIMELOCK_PEGIN_BLOCKS = 0xffff;\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 /**\n * HTLC output values in satoshis, one per deposit. Each includes\n * peginAmount + depositorClaimValue + p2aAnchorValue + minPeginFee (the\n * anchor term is 0 for graph versions without a P2A anchor, 240 for v2/v3).\n */\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 * Minimum PegIn fee (sats), independently computed and asserted against\n * `htlcValues`' implied reserve by {@link assertWasmPeginSizing}. Reuse\n * this instead of recomputing — it is already the cross-checked value.\n */\n minPeginFee: bigint;\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 /**\n * PegIn transaction hex. 1 input spending the HTLC; outputs are\n * version-shaped: v1 = vault + depositor claim, v2/v3 = vault + depositor\n * claim + P2A anchor at vout 2 (nVersion 3 / TRUC).\n */\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 txGraphVersion: params.vaultCoreVersion,\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 minPeginFeeRate: params.minPeginFeeRate,\n numLocalChallengers: params.numLocalChallengers,\n councilQuorum: params.councilQuorum,\n councilSize: params.councilSize,\n network: params.network,\n authAnchorHash,\n });\n\n // CLAUDE.md critical path #1: the WASM outputs reach JS with no runtime\n // validation. Cross-check every value-bearing field against the request\n // and the protocol formula before it can feed a signed tx or the on-chain\n // PegIn registration. Both the sizing and commit passes route through here.\n const minPeginFee = await assertWasmPeginSizing(result, params);\n\n // Parse the unfunded tx before summing 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\n // Bind the validated metadata to the bytes that get funded and signed.\n // Reject a wrong count, a wrong auth or CPFP output, or an HTLC that does\n // not match the request before UTXO selection uses the output values.\n assertUnfundedPrePeginOutputLayout(\n parsed.outputs,\n result.htlcValues,\n result.htlcScriptPubKeys,\n params,\n authAnchorHash,\n parsed.version,\n parsed.locktime,\n );\n\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 minPeginFee,\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 */\nexport function 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 `timelockPegin` is not a whole number from 1 to 65535, if WASM\n * initialization fails or parameters are invalid, or if the WASM result does\n * not match the request (for example, a vault scriptPubKey that differs from\n * the independently derived payout scriptPubKey)\n */\nexport async function buildPeginTxFromFundedPrePegin(\n params: BuildPeginTxParams,\n): Promise<PeginTxResult> {\n // Reject the timelock before it crosses into the engine. The engine keeps\n // only the low 16 bits and drops a fraction, and rejects only a zero\n // result. Thus 65537 would build a one-block vault, not the requested one.\n if (\n !Number.isInteger(params.timelockPegin) ||\n params.timelockPegin < 1 ||\n params.timelockPegin > MAX_TIMELOCK_PEGIN_BLOCKS\n ) {\n throw new Error(\n `PegIn timelock ${params.timelockPegin} must be a whole number of ` +\n `blocks from 1 to ${MAX_TIMELOCK_PEGIN_BLOCKS}.`,\n );\n }\n\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 txGraphVersion: params.prePeginParams.vaultCoreVersion,\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 minPeginFeeRate: params.prePeginParams.minPeginFeeRate,\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 await assertPeginTxShape(result, params);\n\n return {\n txHex: result.txHex,\n txid: result.txid,\n vaultScriptPubKey: result.vaultScriptPubKey,\n vaultValue: result.vaultValue,\n };\n}\n\n/**\n * PegIn outputs common to every graph version: the vault output (vout 0)\n * and the depositor claim output (vout 1). Versions with a P2A anchor (v2)\n * append it after these.\n */\nconst PEGIN_BASE_OUTPUT_COUNT = 2;\n\n/**\n * Cross-check the WASM-built PegIn transaction's header, input, and outputs\n * against the request before the depositor signs it.\n *\n * CLAUDE.md critical path #1: the metadata (`vaultValue`, `txid`,\n * `vaultScriptPubKey`) and the tx bytes both come from WASM — bind them to\n * each other and to the caller's requested amount so a doctored binary\n * can't commit one thing and encode another. The reported and the encoded\n * vault scriptPubKey must both equal the payout scriptPubKey that TypeScript\n * derives from the request. The vault output value is the\n * exact on-chain vault amount, so it must equal the requested peg-in amount\n * (btc-vault: PegIn vout 0 carries `pegin_amount` verbatim). The P2A anchor\n * (exact value/vout/script for v2; complete absence for v1) is enforced by\n * the version-dispatched `validatePeginP2aAnchor`.\n *\n * @throws If the encoded transaction disagrees with the request, metadata,\n * or canonical shape for its Vault Core version.\n */\nasync function assertPeginTxShape(\n result: {\n txHex: string;\n txid: string;\n vaultScriptPubKey: string;\n vaultValue: bigint;\n },\n params: BuildPeginTxParams,\n): Promise<void> {\n const version = params.prePeginParams.vaultCoreVersion;\n const expectedTxVersion =\n version === 1\n ? PEGIN_TX_VERSION_CORE_1\n : version === 2 || version === 3\n ? PEGIN_TX_VERSION_CORE_2_AND_3\n : undefined;\n if (expectedTxVersion === undefined) {\n throw new Error(`Unsupported vaultCoreVersion ${version} for PegIn tx.`);\n }\n\n await validatePeginP2aAnchor(version, result.txHex);\n\n const anchor = await peginP2aAnchorOutput(version);\n const expectedOutputCount = PEGIN_BASE_OUTPUT_COUNT + (anchor ? 1 : 0);\n\n const peginTx = Transaction.fromHex(stripHexPrefix(result.txHex));\n\n if (peginTx.version !== expectedTxVersion) {\n throw new Error(\n `PegIn tx version ${peginTx.version} does not match ` +\n `vaultCoreVersion ${version}; expected ${expectedTxVersion}.`,\n );\n }\n if (peginTx.locktime !== PEGIN_TX_LOCKTIME) {\n throw new Error(\n `PegIn tx locktime ${peginTx.locktime} does not match the canonical ` +\n `locktime ${PEGIN_TX_LOCKTIME}.`,\n );\n }\n\n // Input bind: the PegIn must spend EXACTLY the requested HTLC outpoint\n // (fundedPrePeginTxid, htlcVout). Without this, a doctored tx could spend\n // a sibling HTLC while the caller registers it under `htlcVout` — in a\n // multi-HTLC batch that both double-spends the sibling and strands the\n // registered vault. The downstream PSBT builder follows the tx's own\n // input index, so this is the only place the requested vout is enforced.\n if (peginTx.ins.length !== 1) {\n throw new Error(\n `PegIn tx has ${peginTx.ins.length} input(s), expected exactly 1 ` +\n `(the Pre-PegIn HTLC outpoint).`,\n );\n }\n const fundedPrePeginTxid = Transaction.fromHex(\n stripHexPrefix(params.fundedPrePeginTxHex),\n ).getId();\n const inputTxid = uint8ArrayToHex(\n new Uint8Array(peginTx.ins[0].hash).slice().reverse(),\n );\n if (inputTxid !== fundedPrePeginTxid) {\n throw new Error(\n `PegIn input spends txid ${inputTxid}, expected the funded Pre-PegIn ` +\n `${fundedPrePeginTxid}.`,\n );\n }\n if (peginTx.ins[0].index !== params.htlcVout) {\n throw new Error(\n `PegIn input spends Pre-PegIn output ${peginTx.ins[0].index}, ` +\n `expected the requested HTLC vout ${params.htlcVout}.`,\n );\n }\n if (peginTx.ins[0].sequence !== PEGIN_INPUT_SEQUENCE) {\n throw new Error(\n `PegIn input sequence ${peginTx.ins[0].sequence} does not match the ` +\n `canonical sequence ${PEGIN_INPUT_SEQUENCE}.`,\n );\n }\n if (peginTx.ins[0].script.length !== 0) {\n throw new Error(\"PegIn input scriptSig must be empty.\");\n }\n if (peginTx.ins[0].witness.length !== 0) {\n throw new Error(\"PegIn input witness must be empty before signing.\");\n }\n\n if (peginTx.outs.length !== expectedOutputCount) {\n throw new Error(\n `PegIn tx has ${peginTx.outs.length} output(s), expected exactly ` +\n `${expectedOutputCount} for vaultCoreVersion ${version} (vault + ` +\n `depositor claim${anchor ? \" + P2A anchor\" : \"\"}).`,\n );\n }\n\n const requestedAmount = params.prePeginParams.pegInAmounts[params.htlcVout];\n if (result.vaultValue !== requestedAmount) {\n throw new Error(\n `PegIn vault output value ${result.vaultValue} does not match the ` +\n `requested peg-in amount ${requestedAmount} for HTLC ` +\n `${params.htlcVout}; refusing to sign a vault that locks a ` +\n `different amount than requested.`,\n );\n }\n\n const encodedVaultOut = peginTx.outs[0];\n if (BigInt(encodedVaultOut.value) !== result.vaultValue) {\n throw new Error(\n `Encoded PegIn vault output value ${encodedVaultOut.value} does not ` +\n `match the WASM-reported vaultValue ${result.vaultValue}.`,\n );\n }\n const expectedVaultScript = deriveExpectedPeginPayoutScriptPubKey(\n params.prePeginParams,\n params.timelockPegin,\n ).toString(\"hex\");\n if (\n stripHexPrefix(result.vaultScriptPubKey).toLowerCase() !==\n expectedVaultScript\n ) {\n throw new Error(\n `WASM-reported PegIn vaultScriptPubKey ${result.vaultScriptPubKey} ` +\n `does not match the independently derived payout scriptPubKey ` +\n `${expectedVaultScript}.`,\n );\n }\n const encodedVaultScript = encodedVaultOut.script.toString(\"hex\");\n if (encodedVaultScript !== expectedVaultScript) {\n throw new Error(\n `Encoded PegIn vault output scriptPubKey ${encodedVaultScript} does ` +\n `not match the independently derived payout scriptPubKey ` +\n `${expectedVaultScript}.`,\n );\n }\n\n const encodedTxid = peginTx.getId();\n if (encodedTxid !== stripHexPrefix(result.txid).toLowerCase()) {\n throw new Error(\n `Encoded PegIn txid ${encodedTxid} does not match the WASM-reported ` +\n `txid ${result.txid}.`,\n );\n }\n\n // Depositor-claim output (vout 1): a doctored binary could burn the claim\n // into miner fee or redirect it while the vault, anchor, and txid binds\n // all pass. Bind its value to the independently recomputed minimum claim\n // value (the same WASM-vs-WASM identity `assertWasmPeginSizing` uses) and\n // its script to a fully JS-derived expectation. With the vault bind, the\n // output count, and value conservation against the validated HTLC input,\n // the implied fee is bound too.\n const expectedClaimValue = await computeMinClaimValue(\n version,\n params.prePeginParams.numLocalChallengers,\n params.prePeginParams.universalChallengerPubkeys.length,\n params.prePeginParams.councilQuorum,\n params.prePeginParams.councilSize,\n params.prePeginParams.feeRate,\n );\n const encodedClaimOut = peginTx.outs[PEGIN_DEPOSITOR_CLAIM_VOUT];\n if (BigInt(encodedClaimOut.value) !== expectedClaimValue) {\n throw new Error(\n `Encoded PegIn depositor-claim output value ${encodedClaimOut.value} ` +\n `does not match the independently computed claim value ` +\n `${expectedClaimValue} for vaultCoreVersion ${version}.`,\n );\n }\n\n const expectedClaimScript = deriveDepositorClaimScriptPubKey(\n params.prePeginParams.depositorPubkey,\n );\n if (!encodedClaimOut.script.equals(expectedClaimScript)) {\n throw new Error(\n `Encoded PegIn depositor-claim output scriptPubKey ` +\n `${encodedClaimOut.script.toString(\"hex\")} does not pay to the ` +\n `depositor's claim script (expected ` +\n `${expectedClaimScript.toString(\"hex\")}).`,\n );\n }\n}\n","/**\n * Payout Script Generator Primitive\n *\n * This module provides pure functions for generating payout scripts and taproot information\n * by wrapping the WASM implementation from @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * The payout script is used for signing payout transactions in the vault system.\n * It defines the spending conditions for the vault output, enabling the depositor\n * to authorize payouts during the peg-in flow (Step 3).\n *\n * @remarks\n * This is a low-level primitive. For most use cases, prefer using {@link buildPayoutPsbt}\n * which handles script creation internally. For high-level wallet orchestration, use\n * PayoutManager from the managers module.\n *\n * @see {@link buildPayoutPsbt} - Higher-level function that uses this internally\n *\n * @module primitives/scripts/payout\n */\n\nimport {\n createPayoutConnector,\n type Network,\n} from \"../../wasm\";\n\n/**\n * Parameters for creating a payout script.\n *\n * These parameters define the participants in a vault and are used to generate\n * the taproot script that controls how funds can be spent from the vault.\n */\nexport interface PayoutScriptParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n * payout connector the WASM derives.\n */\n vaultCoreVersion: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * This is the user depositing BTC into the vault. The depositor must sign\n * payout transactions to authorize fund distribution.\n */\n depositor: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * The service provider managing vault operations. Also referred to as\n * \"claimer\" in the WASM layer.\n */\n vaultProvider: string;\n\n /**\n * Array of vault keeper BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * Vault keepers participate in vault operations and script spending conditions.\n */\n vaultKeepers: string[];\n\n /**\n * Array of universal challenger BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * These parties can challenge the vault under certain conditions.\n */\n universalChallengers: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Bitcoin network for script generation.\n *\n * Must match the network used for all other vault operations to ensure\n * address encoding compatibility.\n */\n network: Network;\n}\n\n/**\n * Result of creating a payout script.\n *\n * Contains all the taproot-related data needed for constructing and signing\n * payout transactions from the vault.\n */\nexport interface PayoutScriptResult {\n /**\n * The payout script hex used in taproot script path spending.\n *\n * This is the raw script bytes that define the spending conditions,\n * encoded as a hexadecimal string. Used when constructing the\n * tapLeafScript for PSBT signing.\n */\n payoutScript: string;\n\n /**\n * The taproot script hash (leaf hash) for the payout script.\n *\n * This is the tagged hash of the script used in taproot tree construction.\n * Required for computing the control block during script path spending.\n */\n taprootScriptHash: string;\n\n /**\n * The full scriptPubKey for the vault output address.\n *\n * This is the complete output script (OP_1 <32-byte-key>) that should be\n * used when creating the vault output in a peg-in transaction.\n */\n scriptPubKey: string;\n\n /**\n * The vault Bitcoin address derived from the script.\n *\n * A human-readable bech32m address (bc1p... for mainnet, tb1p... for testnet/signet)\n * that can be used to receive funds into the vault.\n */\n address: string;\n\n /**\n * Serialized control block for Taproot script path spend (hex encoded).\n *\n * Computed by the Rust WASM PeginPayoutConnector. Used directly in\n * tapLeafScript when building payout PSBTs.\n */\n payoutControlBlock: string;\n}\n\n/**\n * Create payout script and taproot information using WASM.\n *\n * This is a pure function that wraps the Rust WASM implementation.\n * The payout connector generates the necessary taproot scripts and information\n * required for signing payout transactions.\n *\n * @remarks\n * The generated script encodes spending conditions that require signatures from\n * the depositor and vault provider (or liquidators in challenge scenarios).\n * This script is used internally by {@link buildPayoutPsbt}.\n *\n * @param params - Payout script parameters defining vault participants and network\n * @returns Payout script and taproot information for PSBT construction\n *\n * @see {@link buildPayoutPsbt} - Use this for building complete payout PSBTs\n */\nexport async function createPayoutScript(\n params: PayoutScriptParams,\n): Promise<PayoutScriptResult> {\n // Call the WASM wrapper with the correct parameter structure\n const connector = await createPayoutConnector(\n {\n txGraphVersion: params.vaultCoreVersion,\n depositor: params.depositor,\n vaultProvider: params.vaultProvider,\n vaultKeepers: params.vaultKeepers,\n universalChallengers: params.universalChallengers,\n timelockPegin: params.timelockPegin,\n },\n params.network,\n );\n\n return {\n payoutScript: connector.payoutScript,\n taprootScriptHash: connector.taprootScriptHash,\n scriptPubKey: connector.scriptPubKey,\n address: connector.address,\n payoutControlBlock: connector.payoutControlBlock,\n };\n}\n","/**\n * BIP-341 Taproot primitives shared by the PSBT builders and verifiers.\n *\n * Everything here is a byte-level restatement of BIP-341; the spec is the\n * source of truth for each constant and each hash preimage.\n *\n * @see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\n * @module tbv/core/primitives/utils/taproot\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { crypto as bcrypto } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\n// Bitcoin CompactSize (varint) prefix markers — values fixed by the protocol.\n// https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\nconst COMPACT_SIZE_UINT16_PREFIX = 0xfd; // value in [0xfd, 0xffff] → 0xfd + uint16 LE\nconst COMPACT_SIZE_UINT32_PREFIX = 0xfe; // value in [0x10000, 0xffffffff] → 0xfe + uint32 LE\nconst COMPACT_SIZE_UINT16_MAX = 0xffff;\nconst COMPACT_SIZE_UINT32_MAX = 0xffffffff;\n\n/** BIP-341 tag for the TapLeaf hash. */\nconst TAPLEAF_TAG = \"TapLeaf\";\n/** BIP-341 tag for an internal merkle node of the taptree. */\nconst TAPBRANCH_TAG = \"TapBranch\";\n/** BIP-341 tag for the output-key tweak. */\nconst TAPTWEAK_TAG = \"TapTweak\";\n\n/** BIP-341 control block prefix: 1 leaf-version/parity byte + 32-byte internal key. */\nconst CONTROL_BLOCK_PREFIX_LEN = 33;\n/** Each merkle-path element in a control block is a 32-byte node hash. */\nconst CONTROL_BLOCK_NODE_LEN = 32;\n/** BIP-341 caps the taptree at 128 levels, so the path holds at most 128 nodes. */\nconst CONTROL_BLOCK_MAX_NODES = 128;\n/** Low bit of control-block byte 0 carries the output key's y-parity. */\nconst CONTROL_BLOCK_PARITY_MASK = 0x01;\n/** Remaining bits of control-block byte 0 carry the leaf version. */\nconst CONTROL_BLOCK_LEAF_VERSION_MASK = 0xfe;\n\n/** A P2TR scriptPubKey is `OP_1 <32-byte push>` followed by the output key. */\nconst P2TR_SCRIPT_PUBKEY_PREFIX = Buffer.from([0x51, 0x20]);\n\n/**\n * Encode a length as a Bitcoin CompactSize (varint). Tapscript leaf scripts can\n * exceed 252 bytes (WOTS scripts), so the multi-byte forms are required, not\n * just the single-byte fast path.\n */\nfunction encodeCompactSize(n: number): Buffer {\n if (n < COMPACT_SIZE_UINT16_PREFIX) {\n return Buffer.from([n]);\n }\n if (n <= COMPACT_SIZE_UINT16_MAX) {\n const value = Buffer.alloc(2); // uint16, little-endian\n value.writeUInt16LE(n);\n return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT16_PREFIX]), value]);\n }\n if (n <= COMPACT_SIZE_UINT32_MAX) {\n const value = Buffer.alloc(4); // uint32, little-endian\n value.writeUInt32LE(n);\n return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT32_PREFIX]), value]);\n }\n throw new Error(`Script too large to encode as CompactSize: ${n} bytes`);\n}\n\n/**\n * Compute the BIP-341 TapLeaf hash for a tapscript leaf:\n * `tagged_hash(\"TapLeaf\", leaf_version || compact_size(script) || script)`.\n */\nexport function computeTapLeafHash(\n leafVersion: number,\n script: Uint8Array,\n): Buffer {\n const preimage = Buffer.concat([\n Buffer.from([leafVersion]),\n encodeCompactSize(script.length),\n Buffer.from(script),\n ]);\n return bcrypto.taggedHash(TAPLEAF_TAG, preimage);\n}\n\nexport interface TaprootScriptPathBinding {\n /** Tapscript leaf version, e.g. `0xc0`. */\n leafVersion: number;\n /** The tapscript leaf being spent. */\n script: Uint8Array;\n /** BIP-341 control block: version/parity byte, internal key, merkle path. */\n controlBlock: Uint8Array;\n}\n\n/**\n * Recompute the P2TR scriptPubKey that a `(leafVersion, script, controlBlock)`\n * triple can spend, by walking the control block's merkle path to the taptree\n * root and tweaking the control block's internal key with it (BIP-341\n * \"Script validation rules\").\n *\n * Comparing the result against a real previous output is what proves the triple\n * belongs to that output rather than to some other taptree.\n *\n * @throws If the control block is malformed, the leaf version disagrees with\n * the control block, the tweak is off-curve, or the recovered output key's\n * parity contradicts the control block.\n */\nexport function computeTaprootScriptPubKey(\n binding: TaprootScriptPathBinding,\n): Buffer {\n const { leafVersion, script, controlBlock } = binding;\n\n const pathLen = controlBlock.length - CONTROL_BLOCK_PREFIX_LEN;\n if (\n pathLen < 0 ||\n pathLen % CONTROL_BLOCK_NODE_LEN !== 0 ||\n pathLen / CONTROL_BLOCK_NODE_LEN > CONTROL_BLOCK_MAX_NODES\n ) {\n throw new Error(\n `Malformed Taproot control block: length ${controlBlock.length} must be ` +\n `${CONTROL_BLOCK_PREFIX_LEN} + 32*m with 0 <= m <= ${CONTROL_BLOCK_MAX_NODES}`,\n );\n }\n\n const controlLeafVersion = controlBlock[0] & CONTROL_BLOCK_LEAF_VERSION_MASK;\n if (controlLeafVersion !== leafVersion) {\n throw new Error(\n `Taproot control block leaf version 0x${controlLeafVersion.toString(16)} ` +\n `does not match the tapLeafScript leaf version 0x${leafVersion.toString(16)}`,\n );\n }\n\n const internalKey = Buffer.from(\n controlBlock.subarray(1, CONTROL_BLOCK_PREFIX_LEN),\n );\n\n // Fold the merkle path into the taptree root; siblings are hashed in\n // lexicographic order (BIP-341).\n let node = computeTapLeafHash(leafVersion, script);\n for (\n let offset = CONTROL_BLOCK_PREFIX_LEN;\n offset < controlBlock.length;\n offset += CONTROL_BLOCK_NODE_LEN\n ) {\n const sibling = Buffer.from(\n controlBlock.subarray(offset, offset + CONTROL_BLOCK_NODE_LEN),\n );\n node = bcrypto.taggedHash(\n TAPBRANCH_TAG,\n Buffer.compare(node, sibling) <= 0\n ? Buffer.concat([node, sibling])\n : Buffer.concat([sibling, node]),\n );\n }\n\n const tweak = bcrypto.taggedHash(\n TAPTWEAK_TAG,\n Buffer.concat([internalKey, node]),\n );\n const tweaked = ecc.xOnlyPointAddTweak(internalKey, tweak);\n if (tweaked === null) {\n throw new Error(\n \"Taproot control block does not yield a valid output key (tweak is off-curve)\",\n );\n }\n const expectedParity = controlBlock[0] & CONTROL_BLOCK_PARITY_MASK;\n if (tweaked.parity !== expectedParity) {\n throw new Error(\n `Taproot output key parity ${tweaked.parity} contradicts the control ` +\n `block's parity bit ${expectedParity}`,\n );\n }\n\n return Buffer.concat([\n P2TR_SCRIPT_PUBKEY_PREFIX,\n Buffer.from(tweaked.xOnlyPubkey),\n ]);\n}\n","/**\n * Bands a VP-built payout's implicit fee before the depositor pre-signs it.\n * Ceiling (ours, #2105) blocks a VP deflating outputs and burning the\n * difference as miner fee; floor (vault-wasm `computePayoutFeeFloor`) rejects\n * a fee no legitimate VP model produces and that may not relay at redemption.\n *\n * Caller (`buildPayoutPsbt`) must have run `assertPayoutFeeBandDomain` first,\n * and `implicitFeeSats` must be `inputs − outputs` over verified prevouts.\n * Band non-emptiness is proven by the corner sweep in the tests, not by a\n * closed-form argument — slack is not monotone at small rosters.\n *\n * @module primitives/psbt/assertPayoutFeeBand\n */\n\nimport { computePayoutFeeFloor } from \"../../wasm\";\n\n/**\n * Floor-model limit, NOT a protocol bound: vault-wasm seeds dummy keys from a\n * u8, so 257 keepers trap in `VaultKeepers::new` (measured). The contract sets\n * no keeper maximum — a larger roster is an upstream vault-wasm fix.\n */\nconst MAX_FLOOR_MODEL_KEEPERS = 256;\n\n/**\n * Mirrors `vault-contracts-aave-v4 ProtocolParams.sol:22`\n * MAX_UNIVERSAL_CHALLENGERS_SIZE. Safe to copy\n * rather than read: it is a bytecode `constant`, not a governance param, so it\n * cannot move without a redeploy — re-verify it on the next one.\n */\nconst MAX_UNIVERSAL_CHALLENGERS = 1500;\n\n/** Floor-model limit, same u8 dummy-seeding cause as the keeper cap (measured). */\nconst MAX_FLOOR_MODEL_COUNCIL_SIZE = 256;\n\n/**\n * Coarse garbage-input guard, far above the contract's own cap\n * (`vault-contracts-aave-v4 ProtocolParams.sol:44` MAX_FEE_RATE_SAT_VB = 1000).\n */\nconst MAX_SANE_FEE_RATE_SAT_PER_VB = 0xffffffffn;\n\n/**\n * Ceiling vsize model `BASE + PER_PARTICIPANT * (N + M)` — ~13%+ headroom over\n * the exact vsize the VP pays. Values adopted per #2105; kept at parity with\n * the Ledger app's bound so device-signable and SDK-acceptable stay aligned.\n */\nconst MAX_PAYOUT_VSIZE_BASE = 500;\nconst MAX_PAYOUT_VSIZE_PER_PARTICIPANT = 55;\n\n/** P2TR length the model assumes; a longer pinned outs[0] extends the ceiling linearly. */\nconst PAYOUT_BOUND_ASSUMED_SCRIPT_LEN = 34;\n\n/** Shape/rate inputs the fee band is evaluated over. */\nexport interface PayoutFeeBandParams {\n /** Vault core (tx-graph) version — selects the floor's pinned model set. */\n vaultCoreVersion: number;\n /** Vault keeper count (N). */\n numVaultKeepers: number;\n /** Universal challenger count (M). */\n numUniversalChallengers: number;\n /** Security council size from the locked offchain params version. */\n councilSize: number;\n /** Version-locked tx-graph fee rate (sat/vB); anchors both band ends. */\n protocolFeeRate: bigint;\n}\n\n/**\n * Validate the fee-band inputs lie in the accepted domain. Bounds differ in\n * KIND per role — see each constant.\n *\n * @throws If the rate, a participant count, or `councilSize` is out of range\n */\nexport function assertPayoutFeeBandDomain(params: PayoutFeeBandParams): void {\n if (\n typeof params.protocolFeeRate !== \"bigint\" ||\n params.protocolFeeRate <= 0n ||\n params.protocolFeeRate > MAX_SANE_FEE_RATE_SAT_PER_VB\n ) {\n throw new Error(\n `protocolFeeRate must be in [1, ${MAX_SANE_FEE_RATE_SAT_PER_VB}] sat/vB, ` +\n `got ${params.protocolFeeRate}`,\n );\n }\n // Lower bound 1: btc-vault's VaultKeepers::new / UniversalChallengers::new\n // reject an empty set (crates/vault/src/lib.rs).\n for (const [role, count, max, reason] of [\n [\n \"keepers\",\n params.numVaultKeepers,\n MAX_FLOOR_MODEL_KEEPERS,\n \"fee-floor model limit\",\n ],\n [\n \"challengers\",\n params.numUniversalChallengers,\n MAX_UNIVERSAL_CHALLENGERS,\n \"protocol maximum\",\n ],\n ] as const) {\n if (!Number.isInteger(count) || count < 1 || count > max) {\n // Only an over-max count is attributable to the bound's reason.\n throw new Error(\n `Participant count for ${role} (${count}) is outside the supported ` +\n `range [1, ${max}]${count > max ? ` (${reason})` : \"\"}.`,\n );\n }\n }\n // Unvalidated at the WASM boundary: a non-integer truncates at the u32 ABI\n // and 0 is silently promoted to a 1-member council by the estimator.\n if (\n !Number.isInteger(params.councilSize) ||\n params.councilSize < 1 ||\n params.councilSize > MAX_FLOOR_MODEL_COUNCIL_SIZE\n ) {\n throw new Error(\n `councilSize must be an integer in ` +\n `[1, ${MAX_FLOOR_MODEL_COUNCIL_SIZE}], got ${params.councilSize}`,\n );\n }\n}\n\n/**\n * Assert `floor <= implicitFeeSats <= ceiling`. `out1Len` is `undefined` for\n * the 2-output (no-commission) layouts.\n *\n * @throws If the fee is below the floor or above the ceiling\n */\nexport async function assertPayoutFeeInBand(\n params: PayoutFeeBandParams,\n measured: {\n implicitFeeSats: number;\n out0Len: number;\n out1Len: number | undefined;\n },\n): Promise<void> {\n const { implicitFeeSats, out0Len, out1Len } = measured;\n const numParticipants =\n params.numVaultKeepers + params.numUniversalChallengers;\n const implicitFee = BigInt(implicitFeeSats);\n\n // Ceiling first: synchronous, no WASM round-trip. Only outs[0] widens it —\n // including the VP-controlled out1Len would hand a padded commission script\n // up to 94 vB x rate of extra burnable band.\n const scriptExcess = Math.max(0, out0Len - PAYOUT_BOUND_ASSUMED_SCRIPT_LEN);\n const maxPayoutVsize =\n MAX_PAYOUT_VSIZE_BASE +\n MAX_PAYOUT_VSIZE_PER_PARTICIPANT * numParticipants +\n scriptExcess;\n const maxFeeSats = params.protocolFeeRate * BigInt(maxPayoutVsize);\n if (implicitFee > maxFeeSats) {\n throw new Error(\n `Payout implicit fee ${implicitFeeSats} sats exceeds the safety cap ` +\n `of ${maxFeeSats} sats (${params.protocolFeeRate} sat/vB x ` +\n `${maxPayoutVsize} vB for ${numParticipants} ` +\n `participants); refusing to sign payout.`,\n );\n }\n\n // Local challengers are exactly the keeper count for every claimer role\n // (btc-vault graph.rs derive_challengers).\n const minFeeSats = await computePayoutFeeFloor(\n params.vaultCoreVersion,\n params.numVaultKeepers,\n params.numUniversalChallengers,\n params.numVaultKeepers,\n params.councilSize,\n out0Len,\n out1Len,\n params.protocolFeeRate,\n );\n if (implicitFee < minFeeSats) {\n throw new Error(\n `Payout implicit fee ${implicitFeeSats} sats is below the floor of ` +\n `${minFeeSats} sats (the smallest fee any known vault-provider ` +\n `build produces for this shape); refusing to sign payout.`,\n );\n }\n}\n","/**\n * Standard-scriptPubKey validation for registry-supplied payout destinations.\n *\n * RFC-006 lets an operator register an arbitrary payout scriptPubKey, so the\n * bytes we pin a `PayoutTx` output against are now chosen by that operator\n * rather than derived by us. Pinning an output to them without checking the\n * shape would let a keeper that registered a garbage or unspendable script get\n * the depositor to pre-sign a payout nobody can ever claim.\n *\n * This bound is ours alone — it is deliberately NOT parity with `vaultd`.\n * `vaultd` uses registry scripts verbatim at graph-build time and documents\n * why: applying a predicate the contract does not apply would block every\n * peg-in for an operator the instant the two disagreed. The registry itself\n * only bounds length to 1..128 bytes, and the registration CLI has an\n * `--allow-non-standard` escape hatch.\n *\n * We hold the stricter line because we occupy a different position: we are the\n * party being asked to *pre-sign* against these bytes, and a depositor\n * signature pinned to a provably unspendable output is not recoverable. The\n * bound is also load-bearing for the payout fee band — capping a standard\n * output at 34 bytes is what lets `assertPayoutFeeBand` reason about `outs[1]`\n * without a separate length cap.\n *\n * The deliberate consequence: an operator that registers a non-standard script\n * via `--allow-non-standard` can have its graphs built by `vaultd` and still be\n * refused here, blocking deposits against that operator in this dApp. That is\n * fail-closed by choice — the alternative is pre-signing into a destination we\n * cannot verify is spendable.\n *\n * @module primitives/psbt/standardPayoutScript\n */\n\nimport { stripHexPrefix } from \"../utils/bitcoin\";\n\n/** `OP_0 <20>` — P2WPKH. */\nconst P2WPKH_LEN = 22;\n/** `OP_0 <32>` — P2WSH. */\nconst P2WSH_LEN = 34;\n/** `OP_1 <32>` — P2TR. */\nconst P2TR_LEN = 34;\n/** `OP_DUP OP_HASH160 <20> OP_EQUALVERIFY OP_CHECKSIG` — P2PKH. */\nconst P2PKH_LEN = 25;\n/** `OP_HASH160 <20> OP_EQUAL` — P2SH. */\nconst P2SH_LEN = 23;\n\nfunction isP2wpkh(s: string): boolean {\n return s.length === P2WPKH_LEN * 2 && s.startsWith(\"0014\");\n}\nfunction isP2wsh(s: string): boolean {\n return s.length === P2WSH_LEN * 2 && s.startsWith(\"0020\");\n}\nfunction isP2tr(s: string): boolean {\n return s.length === P2TR_LEN * 2 && s.startsWith(\"5120\");\n}\nfunction isP2pkh(s: string): boolean {\n return (\n s.length === P2PKH_LEN * 2 && s.startsWith(\"76a914\") && s.endsWith(\"88ac\")\n );\n}\nfunction isP2sh(s: string): boolean {\n return s.length === P2SH_LEN * 2 && s.startsWith(\"a914\") && s.endsWith(\"87\");\n}\n\n/**\n * Assert a registry-supplied scriptPubKey is a standard, spendable output type\n * (P2TR, P2WPKH, P2WSH, P2PKH, or P2SH).\n *\n * Rejects empty scripts, `OP_RETURN` (provably unspendable), and anything else\n * that is not one of the five standard forms. `label` names the source in the\n * error, e.g. `vault keeper payout script (admin=0x…)`.\n */\nexport function assertStandardPayoutScript(\n scriptHex: string,\n label: string,\n): void {\n const script = stripHexPrefix(scriptHex).toLowerCase();\n\n if (script.length === 0) {\n throw new Error(`${label} is empty`);\n }\n if (!/^[0-9a-f]+$/.test(script) || script.length % 2 !== 0) {\n throw new Error(`${label} is not valid hex`);\n }\n if (script.startsWith(\"6a\")) {\n throw new Error(\n `${label} is an OP_RETURN output, which is provably unspendable`,\n );\n }\n\n if (\n !isP2tr(script) &&\n !isP2wpkh(script) &&\n !isP2wsh(script) &&\n !isP2pkh(script) &&\n !isP2sh(script)\n ) {\n throw new Error(\n `${label} is not a standard scriptPubKey ` +\n `(expected P2TR, P2WPKH, P2WSH, P2PKH, or P2SH; got ${script.length / 2} bytes: ${script})`,\n );\n }\n}\n","/**\n * Payout PSBT Builder Primitives\n *\n * This module provides pure functions for building unsigned payout PSBTs and extracting\n * Schnorr signatures from signed PSBTs. It uses WASM-generated scripts from the payout\n * connector and bitcoinjs-lib for PSBT construction.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @module primitives/psbt/payout\n */\n\nimport { Psbt, Transaction, type TxInput, type TxOutput } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\nimport {\n getAssertPayoutScriptInfo,\n tapInternalPubkey,\n type Network,\n} from \"../../wasm\";\nimport { deriveLocalChallengers } from \"../challengers\";\nimport { createPayoutScript } from \"../scripts/payout\";\nimport {\n TAPSCRIPT_LEAF_VERSION,\n deriveBip86ScriptPubKeyHex,\n hexToUint8Array,\n isValidHex,\n stripHexPrefix,\n uint8ArrayToHex,\n} from \"../utils/bitcoin\";\nimport { computeTaprootScriptPubKey } from \"../utils/taproot\";\nimport {\n assertPayoutFeeBandDomain,\n assertPayoutFeeInBand,\n} from \"./assertPayoutFeeBand\";\nimport {\n ASSERT_PAYOUT_OUTPUT_INDEX,\n BPS_DENOMINATOR,\n DEPOSITOR_PAYOUT_INPUT_COUNT,\n MAX_PAYOUT_SCRIPT_LEN,\n MAX_VP_COMMISSION_BPS_EXCLUSIVE,\n NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n PAYOUT_ANCHOR_DUST_SATS,\n PAYOUT_TX_LOCKTIME,\n PAYOUT_TX_VERSION,\n PEGIN_VAULT_OUTPUT_INDEX,\n VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n} from \"./constants\";\nimport { assertStandardPayoutScript } from \"./standardPayoutScript\";\n\n/**\n * Number of items in a Taproot script-path spend witness stack for a\n * single-signature script: [signature, script, controlBlock].\n *\n * The current payout script requires exactly one depositor signature. If the\n * protocol evolves to require multiple signatures in the payout script, this\n * invariant and the finalized-PSBT extraction path must be revisited because\n * the first witness item would no longer necessarily be the depositor's.\n */\nconst TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE = 3;\n\n/**\n * Parameters for building an unsigned Payout PSBT\n *\n * Payout ends two of the peg-out paths; see {@link buildPayoutPsbt} for all of\n * them. Input 1 references the Assert transaction.\n */\nexport interface PayoutParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n * payout connector scripts are derived.\n */\n vaultCoreVersion: number;\n\n /**\n * Payout transaction hex (unsigned)\n * This is the transaction that needs to be signed by the depositor\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex\n * Payout input 1 references Assert output 0\n */\n assertTxHex: string;\n\n /**\n * Peg-in transaction hex\n * This transaction created the vault output that we're spending\n */\n peginTxHex: string;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix)\n */\n depositorBtcPubkey: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex)\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex)\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex)\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output (btc-vault `timelock_pegin`);\n * payout input 0's sequence.\n */\n timelockPegin: number;\n\n /**\n * CSV timelock in blocks on the Assert:0 payout leaf (btc-vault\n * `timelock_assert`); payout input 1's sequence.\n */\n timelockAssert: number;\n\n /**\n * Bitcoin network\n */\n network: Network;\n\n /**\n * Claimer's x-only BTC public key (64-char hex, no prefix). Drives role\n * inference (VP / depositor-as-claimer / VK-claimer) inside `buildPayoutPsbt`.\n */\n claimerBtcPubkey: string;\n\n /**\n * On-chain registered depositor payout scriptPubKey (hex, 0x optional).\n * Expected outs[0].script for VP- and depositor-claimer roles; unused for\n * VK-claimer (its outs[0].script is derived from `claimerBtcPubkey`).\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * VP commission in basis points (`BTCVaultRegistry.vaultProviderCommissionBps`).\n * Caps the VP-claimer outs[1].value. The protocol minimum is enforced\n * upstream; here only `0 <= bps < 10_000` is checked, for safe cap math.\n */\n commissionBps: number;\n\n /**\n * Tx-graph fee rate (sat/vB) the graph was built with — the version-locked\n * `offchainParams.feeRate` at the vault's stamped `offchainParamsVersion`,\n * NOT a live read. Anchors both ends of the fee band.\n */\n protocolFeeRate: bigint;\n\n /**\n * Security council member x-only public keys (hex) from the locked offchain\n * params version — `getOffchainParamsByVersion(...).securityCouncilKeys`.\n * The council occupies the last leaf of the Assert:0 taptree\n * (btc-vault `crates/vault/src/connectors/assert_payout_nopayout_council.rs`),\n * so the keys are needed to rebuild input 1's payout leaf, and the count\n * feeds the fee-band domain.\n */\n councilMembers: string[];\n\n /**\n * M-of-N council quorum from the locked offchain params version —\n * `getOffchainParamsByVersion(...).councilQuorum`. Shapes the council leaf's\n * multisig script, and with it the Assert:0 taptree root.\n */\n councilQuorum: number;\n\n /**\n * RFC-006. Expected `outs[0].script` per vault-keeper claimer, keyed by\n * lowercased x-only **operation** pubkey (no `0x`), resolved from\n * `ApplicationRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n * `appKeeperKeyEpoch`.\n *\n * Every VK claimer must be present: a claimer missing from the map is an\n * error rather than a cue to derive BIP-86, because a gap means resolution\n * was incomplete and we do not know what that keeper registered.\n *\n * Each entry accepts either that registered script or the BIP-86 default of\n * the same bonded key, so graphs built before btc-vault#2440 remain signable\n * — see {@link acceptedPayoutScriptHexes} for why that is required and when\n * it can be dropped.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n\n /**\n * RFC-006. Expected `outs[1].script` for the VP-claimer commission output,\n * from `BTCVaultRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n * `vpKeyEpoch`. The BIP-86 default of the bonded VP key is accepted alongside\n * it — see {@link acceptedPayoutScriptHexes}.\n */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Result of building an unsigned payout PSBT\n */\nexport interface PayoutPsbtResult {\n /**\n * Unsigned PSBT hex ready for signing\n */\n psbtHex: string;\n}\n\n/**\n * Build unsigned Payout PSBT for depositor to sign.\n *\n * Payout ends two of the peg-out paths (btc-vault\n * `crates/vault/docs/btc-transactions-spec.md`):\n * - Happy path: Claim -> Assert -> Payout.\n * - Challenge path, claimer wins: Claim -> Assert -> ChallengeAssert ->\n * WronglyChallenged -> Payout.\n * - Challenge path, challenger wins: Claim -> Assert -> ChallengeAssert -> NoPayout.\n * - Emergency path: the Security Council spends Assert:0 via CouncilNoPayout.\n *\n * So a raised challenge does not remove Payout; only NoPayout (challenger wins)\n * or CouncilNoPayout (council emergency) blocks it.\n * Payout references the Assert tx and needs its timelock matured.\n *\n * Payout transactions have the following structure:\n * - Input 0: from PeginTx output0 (signed by depositor)\n * - Input 1: from Assert output0 (NOT signed by depositor)\n *\n * Both inputs carry their taproot script-path leaf. Input 1's is not signed\n * here — it is what a hardware signer reads to display the payout terms.\n *\n * @param params - Payout parameters\n * @returns Unsigned PSBT ready for depositor to sign\n *\n * @throws If payout transaction does not have exactly 2 inputs\n * @throws If input 0 does not spend PegIn:0 (vault UTXO)\n * @throws If input 1 does not spend Assert:0 (proof output)\n * @throws If previous output is not found for either input\n * @throws If sum of output values exceeds sum of input values (invalid tx)\n * @throws If the implicit fee (inputs − outputs) is outside the fee band —\n * below the floor or above the fee-band ceiling (see\n * {@link assertPayoutFeeInBand})\n * @throws If `protocolFeeRate`, a participant count, or the council size is\n * outside the accepted input domain (see {@link assertPayoutFeeBandDomain})\n * @throws If a non-anchor scriptPubKey length is outside `[1,\n * {@link MAX_PAYOUT_SCRIPT_LEN}]`\n * @throws If `claimerBtcPubkey` is not VP, depositor, or a registered VK\n * @throws If payout output count, outs[0] script, outs[last] anchor value, or\n * (VP-claimer) outs[1] commission cap do not match the protocol layout\n * @throws If `commissionBps` is not a non-negative integer below 10_000\n * @throws If the locally rebuilt Assert:0 payout leaf does not bind to the\n * Assert output input 1 spends\n */\nexport async function buildPayoutPsbt(\n params: PayoutParams,\n): Promise<PayoutPsbtResult> {\n const feeBandParams = {\n vaultCoreVersion: params.vaultCoreVersion,\n numVaultKeepers: params.vaultKeeperBtcPubkeys.length,\n numUniversalChallengers: params.universalChallengerBtcPubkeys.length,\n councilSize: params.councilMembers.length,\n protocolFeeRate: params.protocolFeeRate,\n };\n assertPayoutFeeBandDomain(feeBandParams);\n\n const payoutTx = Transaction.fromHex(stripHexPrefix(params.payoutTxHex));\n const peginTx = Transaction.fromHex(stripHexPrefix(params.peginTxHex));\n const assertTx = Transaction.fromHex(stripHexPrefix(params.assertTxHex));\n\n if (payoutTx.ins.length !== DEPOSITOR_PAYOUT_INPUT_COUNT) {\n throw new Error(\n `Payout transaction must have exactly ${DEPOSITOR_PAYOUT_INPUT_COUNT} ` +\n `inputs, got ${payoutTx.ins.length}`,\n );\n }\n // btc-vault builds every payout with these literals (payout.rs:154-155);\n // the sighash commits to them, so refuse rather than sign a foreign shape.\n if (payoutTx.version !== PAYOUT_TX_VERSION) {\n throw new Error(\n `Payout transaction version ${payoutTx.version} must be ` +\n `${PAYOUT_TX_VERSION}; refusing to sign payout.`,\n );\n }\n if (payoutTx.locktime !== PAYOUT_TX_LOCKTIME) {\n throw new Error(\n `Payout transaction locktime ${payoutTx.locktime} must be ` +\n `${PAYOUT_TX_LOCKTIME}; refusing to sign payout.`,\n );\n }\n // Input sequences carry the CSV timelocks (payout.rs:108,119). Each is\n // checked after its own outpoint resolves so a wrong prevout reports itself.\n const peginPrevOut = requirePrevOut(\n payoutTx.ins[0],\n 0,\n peginTx,\n \"PegIn\",\n PEGIN_VAULT_OUTPUT_INDEX,\n );\n if (payoutTx.ins[0].sequence !== params.timelockPegin) {\n throw new Error(\n `Payout input 0 sequence ${payoutTx.ins[0].sequence} must equal the ` +\n `PegIn CSV timelock ${params.timelockPegin}; refusing to sign payout.`,\n );\n }\n\n const assertPrevOut = requirePrevOut(\n payoutTx.ins[1],\n 1,\n assertTx,\n \"Assert\",\n ASSERT_PAYOUT_OUTPUT_INDEX,\n );\n if (payoutTx.ins[1].sequence !== params.timelockAssert) {\n throw new Error(\n `Payout input 1 sequence ${payoutTx.ins[1].sequence} must equal the ` +\n `Assert CSV timelock ${params.timelockAssert}; refusing to sign payout.`,\n );\n }\n // Assert:0's value is deliberately NOT validated: the taproot sighash\n // commits to input 1's amount and outpoint, so a misstated value yields a\n // signature invalid against the real Assert tx — nothing to protect. (The\n // device pins the band [546, 546 + base_fee_rate*500] here, Q15.)\n\n // Per-role output validation — blocks an extra attacker output or value\n // routed into a non-payout slot. Returns the layout-trusted script lengths\n // the fee band consumes.\n const { out0Len, out1Len } = assertPayoutOutputLayout(\n payoutTx,\n peginPrevOut.value,\n params,\n );\n\n const inputValueSats = peginPrevOut.value + assertPrevOut.value;\n let outputValueSats = 0;\n for (const out of payoutTx.outs) outputValueSats += out.value;\n if (outputValueSats > inputValueSats) {\n throw new Error(\n `Payout outputs (${outputValueSats} sats) exceed inputs ` +\n `(${inputValueSats} sats); invalid transaction.`,\n );\n }\n const implicitFeeSats = inputValueSats - outputValueSats;\n await assertPayoutFeeInBand(feeBandParams, {\n implicitFeeSats,\n out0Len,\n out1Len,\n });\n\n // Only assembly needs the WASM-derived scripts — derive them after the\n // transaction has passed every validation gate.\n const payoutConnector = await createPayoutScript({\n vaultCoreVersion: params.vaultCoreVersion,\n depositor: params.depositorBtcPubkey,\n vaultProvider: params.vaultProviderBtcPubkey,\n vaultKeepers: params.vaultKeeperBtcPubkeys,\n universalChallengers: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n network: params.network,\n });\n\n const assertPayoutLeaf = await resolveAssertPayoutLeaf(params, assertPrevOut);\n\n return {\n psbtHex: assemblePayoutPsbt(\n payoutTx,\n peginPrevOut,\n assertPrevOut,\n payoutConnector,\n assertPayoutLeaf,\n ),\n };\n}\n\n/** The Assert:0 payout tapscript leaf, as attached to payout input 1. */\ninterface AssertPayoutLeaf {\n script: Uint8Array;\n controlBlock: Uint8Array;\n}\n\n/**\n * Rebuild the Assert:0 payout leaf from the vault's on-chain participant set\n * and assert it spends `assertPrevOut`.\n *\n * The depositor does not sign input 1, but the Ledger vault app reads this leaf\n * off the PSBT to display the payout terms\n * (`sign_psbt_validate.c::vault_read_payout_leaf_script`), so a leaf that\n * belonged to a different taptree would show the depositor terms the\n * transaction cannot actually enforce. Critical Path #3: derive, then bind to\n * the real previous output — never forward VP-supplied bytes.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nasync function resolveAssertPayoutLeaf(\n params: PayoutParams,\n assertPrevOut: TxOutput,\n): Promise<AssertPayoutLeaf> {\n const { payoutScript, payoutControlBlock } = await getAssertPayoutScriptInfo({\n txGraphVersion: params.vaultCoreVersion,\n claimer: stripHexPrefix(params.claimerBtcPubkey).toLowerCase(),\n localChallengers: deriveLocalChallengers({\n claimerBtcPubkey: params.claimerBtcPubkey,\n depositorBtcPubkey: params.depositorBtcPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n }),\n universalChallengers: params.universalChallengerBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n ),\n timelockAssert: params.timelockAssert,\n councilMembers: params.councilMembers.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n ),\n councilQuorum: params.councilQuorum,\n });\n\n const script = hexToUint8Array(payoutScript);\n const controlBlock = hexToUint8Array(payoutControlBlock);\n const boundScriptPubKey = computeTaprootScriptPubKey({\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script,\n controlBlock,\n });\n if (!assertPrevOut.script.equals(boundScriptPubKey)) {\n throw new Error(\n `Rebuilt Assert:0 payout leaf does not spend the Assert output being ` +\n `referenced: leaf binds to ${boundScriptPubKey.toString(\"hex\")}, ` +\n `Assert:${ASSERT_PAYOUT_OUTPUT_INDEX} pays ` +\n `${assertPrevOut.script.toString(\"hex\")}; refusing to sign payout.`,\n );\n }\n\n return { script, controlBlock };\n}\n\n/**\n * Verify `payoutTx.ins[inputIndex]` spends `parentTx:expectedVout` (both txid\n * AND vout — the vout is the input-side anchor that prevents a malicious VP\n * from binding the depositor's signature to a different output of the same\n * parent) and return the referenced previous output.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction requirePrevOut(\n input: TxInput,\n inputIndex: number,\n parentTx: Transaction,\n parentLabel: string,\n expectedVout: number,\n): TxOutput {\n const inputTxid = uint8ArrayToHex(\n new Uint8Array(input.hash).slice().reverse(),\n );\n const parentTxid = parentTx.getId();\n if (inputTxid !== parentTxid || input.index !== expectedVout) {\n throw new Error(\n `Input ${inputIndex} must spend ${parentLabel}:${expectedVout}. ` +\n `Expected ${parentTxid}:${expectedVout}, got ${inputTxid}:${input.index}`,\n );\n }\n const prevOut = parentTx.outs[input.index];\n if (!prevOut) {\n throw new Error(\n `Previous output not found for input ${inputIndex} ` +\n `(txid: ${inputTxid}, index: ${input.index})`,\n );\n }\n return prevOut;\n}\n\n/**\n * Assemble the unsigned PSBT from the validated payout transaction.\n *\n * IMPORTANT: For Taproot SIGHASH_DEFAULT (0x00), the sighash commits to ALL\n * inputs' prevouts, not just the one being signed — both inputs must be in\n * the PSBT so the wallet computes the sighash the VP expects.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assemblePayoutPsbt(\n payoutTx: Transaction,\n peginPrevOut: TxOutput,\n assertPrevOut: TxOutput,\n payoutConnector: { payoutScript: string; payoutControlBlock: string },\n assertPayoutLeaf: AssertPayoutLeaf,\n): string {\n const psbt = new Psbt();\n psbt.setVersion(payoutTx.version);\n psbt.setLocktime(payoutTx.locktime);\n\n const [input0, input1] = payoutTx.ins;\n\n // Input 0: depositor signs via Taproot script path — carries tapLeafScript.\n psbt.addInput({\n hash: input0.hash,\n index: input0.index,\n sequence: input0.sequence,\n witnessUtxo: {\n script: peginPrevOut.script,\n value: peginPrevOut.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(hexToUint8Array(payoutConnector.payoutScript)),\n controlBlock: Buffer.from(\n hexToUint8Array(payoutConnector.payoutControlBlock),\n ),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n // sighashType omitted - defaults to SIGHASH_DEFAULT (0x00) for Taproot\n });\n\n // Input 1: from Assert — not signed by the depositor, but the Ledger vault\n // app reads the payout leaf here to display the terms, so it must be present.\n psbt.addInput({\n hash: input1.hash,\n index: input1.index,\n sequence: input1.sequence,\n witnessUtxo: {\n script: assertPrevOut.script,\n value: assertPrevOut.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(assertPayoutLeaf.script),\n controlBlock: Buffer.from(assertPayoutLeaf.controlBlock),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n });\n\n for (const output of payoutTx.outs) {\n psbt.addOutput({\n script: output.script,\n value: output.value,\n });\n }\n\n return psbt.toHex();\n}\n\n/**\n * The scriptPubKeys a payout output may legitimately pay for `bondedPubkey`.\n *\n * ## What this is\n *\n * A deliberate dual-accept: both the operator's RFC-006 **registered** payout\n * script and the **BIP-86 default** of its bonded operation key are treated as\n * valid destinations for the same output.\n *\n * ## Why both forms exist\n *\n * A vault provider running a daemon that predates btc-vault#2440 computes\n * operator payout destinations itself, via BIP-86 over the bonded operation\n * key. From #2440 onward it reads them from the registry instead\n * (`getPayoutScriptAtEpoch`), so an operator can point payouts at cold custody.\n *\n * The two forms are not a transition state that resolves on its own. A payout\n * graph is built once, at BaBe Setup, and is **never rebuilt** — so a vault\n * whose graph was built before its network's #2440 upgrade pays BIP-86 for the\n * rest of its life. Accepting only the registered form makes every such vault\n * permanently unsignable: the depositor can never complete the deposit, and\n * the BTC is already committed by then. Confirmed on devnet 2026-08-04, where\n * a pre-upgrade vault failed exactly this way.\n *\n * ## Why this is not a weakening\n *\n * Both candidates are derived here from on-chain state — the registry read and\n * a local BIP-86 derivation over the bonded key. Neither is taken from the VP's\n * response, so a substituted or attacker-chosen script still fails.\n *\n * The only substitution this permits is between two destinations the *same\n * operator* already controls, which moves that operator's own funds between its\n * own addresses. It cannot redirect value to a third party, and it cannot touch\n * the depositor's output. For the VP commission the value cap\n * (`floor(peginValue × commissionBps / 10_000)`) still bounds depositor\n * exposure independently of where the commission goes. The BIP-86 branch is\n * also precisely what every pre-RFC-006 client pinned, so this is the older\n * rule retained alongside the newer one, not a laxer rule invented for it.\n *\n * ## When this can be removed\n *\n * This validation runs at one point in the lifecycle: depositor payout signing\n * (`PENDING` → `VERIFIED`). It is not re-run on refund or withdrawal. So the\n * BIP-86 branch stops being reachable for a network once every vault\n * registered before that network's #2440 upgrade has left\n * `PendingDepositorSignatures` — signed and activated, or expired past its\n * activation deadline. That is bounded by the vault lifetime, not indefinite.\n *\n * Removal is therefore gated by the **last** network to upgrade, since this\n * code is shared. Concretely: drop the BIP-86 branch only once devnet, testnet\n * and mainnet have all been on #2440 for longer than the activation deadline,\n * and no vault registered before those upgrades is still awaiting signatures.\n * Deleting it earlier strands deposits with BTC already locked; there is no\n * recovery path short of the refund timelock.\n *\n * @internal Helper invoked by {@link assertPayoutOutputLayout}.\n */\nfunction acceptedPayoutScriptHexes(\n registeredScriptPubKey: string,\n bondedPubkey: string,\n): string[] {\n const registered = stripHexPrefix(registeredScriptPubKey).toLowerCase();\n const legacyBip86 = stripHexPrefix(\n deriveBip86ScriptPubKeyHex(bondedPubkey),\n ).toLowerCase();\n return registered === legacyBip86 ? [registered] : [registered, legacyBip86];\n}\n\n/** Whether `script` matches any of `acceptedHexes`. */\nfunction matchesAnyScript(script: Buffer, acceptedHexes: string[]): boolean {\n return acceptedHexes.some((hex) => script.equals(Buffer.from(hex, \"hex\")));\n}\n\n/**\n * Validate a payout transaction's output structure for the claimer's role,\n * keyed on `claimerBtcPubkey`. Pins per role: `outs.length`, `outs[0].script`,\n * `outs[last].value` (anchor dust), and — for the VP-claimer —\n * `outs[1].script` plus `outs[1].value`, capped at\n * `floor(peginValue × commissionBps / 10_000)`. Canonical layouts: VP-claimer\n * = [payout, commission, anchor]; depositor/VK-claimer = [payout, anchor].\n *\n * `outs[last].script` (the CPFP anchor) is intentionally not pinned: the value\n * pin above bounds depositor exposure regardless of where that dust goes.\n *\n * `outs[1].script` (the VP commission) was unpinned for the same reason.\n * RFC-006 supersedes that rationale: the commission destination is now an\n * operator-registered scriptPubKey we resolve independently at the vault's\n * frozen `vpKeyEpoch`, so it is pinned too — `vpCommissionScriptPubKey` is a\n * required parameter, so there is no unpinned case. The value cap stays — it is\n * still what bounds exposure — and the script pin is added precision, not a\n * replacement for it.\n *\n * What counts as a match differs by output.\n * {@link acceptedPayoutScriptHexes} accepts two candidates — the registered\n * scriptPubKey and the BIP-86 derivation over the bonded key, the latter as a\n * transitional allowance — and backs the VK-claimer's `outs[0]` and the VP\n * commission `outs[1]`. The VP-claimer and depositor-as-claimer `outs[0]` is\n * pinned to the registered payout script alone.\n *\n * Returns the layout-trusted non-anchor script lengths for the fee band:\n * `out0Len` from the pinned `outs[0]` script, and `out1Len` from the pinned\n * VP-claimer commission output, `undefined` for the other roles. Only\n * `out0Len` is range-checked, against the contract's registration cap\n * `[1, MAX_PAYOUT_SCRIPT_LEN]`; `outs[1]` carries no length cap of its own\n * because `assertStandardPayoutScript` already bounds it to a standard output\n * type, which makes the cap unreachable.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assertPayoutOutputLayout(\n payoutTx: Transaction,\n peginValueSats: number,\n params: PayoutParams,\n): { out0Len: number; out1Len: number | undefined } {\n const {\n claimerBtcPubkey,\n vaultProviderBtcPubkey,\n depositorBtcPubkey,\n vaultKeeperBtcPubkeys,\n registeredPayoutScriptPubKey,\n commissionBps,\n vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey,\n } = params;\n\n if (!isValidHex(registeredPayoutScriptPubKey)) {\n throw new Error(\"Invalid registeredPayoutScriptPubKey: not valid hex\");\n }\n\n const claimer = stripHexPrefix(claimerBtcPubkey).toLowerCase();\n const vp = stripHexPrefix(vaultProviderBtcPubkey).toLowerCase();\n const dep = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n const keepers = vaultKeeperBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n\n type Role = \"vp-claimer\" | \"depositor-as-claimer\" | \"vk-claimer\";\n let role: Role;\n let expectedOutCount: number;\n let acceptedOut0ScriptHexes: string[];\n\n if (claimer === vp) {\n role = \"vp-claimer\";\n expectedOutCount = VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n } else if (claimer === dep) {\n role = \"depositor-as-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n } else if (keepers.includes(claimer)) {\n role = \"vk-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n // RFC-006: a keeper's payout goes to the scriptPubKey it registered\n // on-chain, resolved at the vault's frozen keeper epoch. Deriving BIP-86\n // locally as the sole expectation would reject a valid payout the moment a\n // keeper points its payouts at cold custody.\n //\n // A missing entry is an error, never a BIP-86 fallback: the registry\n // backfills BIP-86 itself for keepers that never registered a script, so a\n // gap here means the resolution was incomplete, not that this keeper is on\n // the default.\n const registered = vkClaimerPayoutScriptPubKeys[claimer];\n if (registered === undefined) {\n throw new Error(\n `No registered payout script resolved for vault keeper claimer ${claimer}`,\n );\n }\n assertStandardPayoutScript(\n registered,\n `Vault keeper payout script for claimer ${claimer}`,\n );\n acceptedOut0ScriptHexes = acceptedPayoutScriptHexes(registered, claimer);\n } else {\n throw new Error(\n `Unknown claimer pubkey ${claimer}: not VP, depositor, or a registered vault keeper`,\n );\n }\n\n if (payoutTx.outs.length !== expectedOutCount) {\n throw new Error(\n `Payout transaction has ${payoutTx.outs.length} output(s), ` +\n `expected exactly ${expectedOutCount} for role ${role}.`,\n );\n }\n\n if (!matchesAnyScript(payoutTx.outs[0].script, acceptedOut0ScriptHexes)) {\n throw new Error(\n `Payout transaction output 0 does not pay the expected scriptPubKey for role ${role}. ` +\n `Accepted: ${acceptedOut0ScriptHexes.join(\" or \")}; ` +\n `got ${payoutTx.outs[0].script.toString(\"hex\")}`,\n );\n }\n\n const anchorIdx = expectedOutCount - 1;\n if (payoutTx.outs[anchorIdx].value !== PAYOUT_ANCHOR_DUST_SATS) {\n throw new Error(\n `Payout CPFP anchor (out ${anchorIdx}) value ${payoutTx.outs[anchorIdx].value} sats ` +\n `must equal ${PAYOUT_ANCHOR_DUST_SATS} sats`,\n );\n }\n\n if (role === \"vp-claimer\") {\n // RFC-006 commission destination. Checked before the value cap so a\n // substituted destination reports as such rather than surfacing later as a\n // confusing amount error.\n assertStandardPayoutScript(\n vpCommissionScriptPubKey,\n \"Vault provider commission payout script\",\n );\n const acceptedCommissionScriptHexes = acceptedPayoutScriptHexes(\n vpCommissionScriptPubKey,\n vp,\n );\n if (\n !matchesAnyScript(payoutTx.outs[1].script, acceptedCommissionScriptHexes)\n ) {\n throw new Error(\n `Payout transaction output 1 does not pay the vault provider's commission scriptPubKey. ` +\n `Accepted: ${acceptedCommissionScriptHexes.join(\" or \")}; ` +\n `got ${payoutTx.outs[1].script.toString(\"hex\")}`,\n );\n }\n\n // Structural guard only — a non-negative integer below the bps\n // denominator, so the cap math `floor(peginValue * bps / 10_000)` is\n // meaningful. The protocol minimum is enforced at the trust boundary\n // (`prepareSigningContext`); a too-low value here is fail-safe.\n if (\n !Number.isInteger(commissionBps) ||\n commissionBps < 0 ||\n commissionBps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n ) {\n throw new Error(\n `commissionBps must be an integer in ` +\n `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${commissionBps}`,\n );\n }\n const maxCommissionSats = Math.floor(\n (peginValueSats * commissionBps) / BPS_DENOMINATOR,\n );\n if (payoutTx.outs[1].value > maxCommissionSats) {\n throw new Error(\n `Payout VP commission (out 1) value ${payoutTx.outs[1].value} sats ` +\n `exceeds cap ${maxCommissionSats} sats ` +\n `(${commissionBps} bps of peginValue=${peginValueSats})`,\n );\n }\n }\n\n // The accepted-script set can hold more than one candidate (dual-accept), so\n // the length the fee band consumes is that of the output actually present —\n // which the check above has already pinned to one of the accepted values.\n const out0Len = payoutTx.outs[0].script.length;\n if (out0Len === 0 || out0Len > MAX_PAYOUT_SCRIPT_LEN) {\n throw new Error(\n `Payout receiver scriptPubKey length ${out0Len} is outside the ` +\n `contract's registration cap [1, ${MAX_PAYOUT_SCRIPT_LEN}]; ` +\n `refusing to sign payout.`,\n );\n }\n // No length cap on outs[1]: under RFC-006 the commission output is pinned to\n // the operator's registered script, and `assertStandardPayoutScript` above\n // already bounds that to a standard type (34 bytes at most). A separate\n // 128-byte cap here would be unreachable. Still measured, because the fee\n // floor consumes it — and now from a pinned source rather than a\n // VP-controlled one.\n const out1Len =\n role === \"vp-claimer\" ? payoutTx.outs[1].script.length : undefined;\n\n return { out0Len, out1Len };\n}\n\n/**\n * Extract Schnorr signature from signed payout PSBT.\n *\n * This function supports two cases:\n * 1. Non-finalized PSBT: Extracts from tapScriptSig field\n * 2. Finalized PSBT: Extracts from witness data\n *\n * The signature is returned as a 64-byte hex string (128 hex characters).\n * Payout signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte.\n *\n * @param signedPsbtHex - Signed PSBT hex\n * @param depositorPubkey - Depositor's public key (x-only, 64-char hex)\n * @param inputIndex - Input index to extract signature from (default: 0)\n * @returns 64-byte Schnorr signature (128 hex characters, no sighash flag)\n *\n * @throws If no signature is found in the PSBT\n * @throws If the signature has an unexpected length\n */\nexport function extractPayoutSignature(\n signedPsbtHex: string,\n depositorPubkey: string,\n inputIndex = 0,\n): string {\n const signedPsbt = Psbt.fromHex(signedPsbtHex);\n\n if (inputIndex >= signedPsbt.data.inputs.length) {\n throw new Error(\n `Input index ${inputIndex} out of range (${signedPsbt.data.inputs.length} inputs)`,\n );\n }\n\n const input = signedPsbt.data.inputs[inputIndex];\n\n // Case 1: Non-finalized PSBT — extract from tapScriptSig\n if (input.tapScriptSig && input.tapScriptSig.length > 0) {\n const depositorPubkeyBytes = hexToUint8Array(depositorPubkey);\n\n for (const sigEntry of input.tapScriptSig) {\n if (sigEntry.pubkey.equals(Buffer.from(depositorPubkeyBytes))) {\n return extractSchnorrSig(sigEntry.signature, inputIndex);\n }\n }\n\n throw new Error(\n `No signature found for depositor pubkey: ${depositorPubkey} at input ${inputIndex}`,\n );\n }\n\n // Case 2: Finalized PSBT — extract from finalScriptWitness\n // Taproot single-signature script-path witness: [signature, script, controlBlock].\n // Enforce the exact stack size so that if a wallet produces an unexpected\n // finalization (e.g. a multi-signature stack, an annex, or malformed data),\n // we fail loudly instead of silently returning witnessStack[0] which may\n // not be the depositor's signature.\n if (input.finalScriptWitness && input.finalScriptWitness.length > 0) {\n const witnessStack = parseWitnessStack(input.finalScriptWitness);\n if (witnessStack.length !== TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE) {\n throw new Error(\n `Unexpected finalized witness stack size at input ${inputIndex}: ` +\n `expected ${TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE} items (signature, script, controlBlock), ` +\n `got ${witnessStack.length}`,\n );\n }\n return extractSchnorrSig(witnessStack[0], inputIndex);\n }\n\n throw new Error(\n `No tapScriptSig or finalScriptWitness found in signed PSBT at input ${inputIndex}`,\n );\n}\n\n/**\n * Extract and validate a 64-byte Schnorr signature.\n * Rejects 65-byte signatures because the appended sighash byte changes the\n * Taproot message being signed; stripping it would produce an unverifiable\n * SIGHASH_DEFAULT signature.\n * @internal\n */\nfunction extractSchnorrSig(sig: Uint8Array, inputIndex: number): 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\")} at input ${inputIndex}. ` +\n \"Expected implicit SIGHASH_DEFAULT as a 64-byte signature.\",\n );\n }\n throw new Error(\n `Unexpected signature length at input ${inputIndex}: ${sig.length}`,\n );\n}\n\n/**\n * Parse a BIP-141 serialized witness stack into individual stack items.\n * Format: [varint item_count] [varint len, data]...\n *\n * Throws on malformed input (truncated buffer, 8-byte varints, or trailing\n * bytes) so callers never receive silently-corrupted witness items.\n * @internal\n */\nfunction parseWitnessStack(witness: Buffer): Buffer[] {\n const items: Buffer[] = [];\n let offset = 0;\n\n const requireBytes = (n: number): void => {\n if (offset + n > witness.length) {\n throw new Error(\n `Malformed witness data: need ${n} byte(s) at offset ${offset}, only ${witness.length - offset} remaining`,\n );\n }\n };\n\n const readVarInt = (): number => {\n requireBytes(1);\n const first = witness[offset++];\n if (first < 0xfd) return first;\n if (first === 0xfd) {\n requireBytes(2);\n const val = (witness[offset] | (witness[offset + 1] << 8)) >>> 0;\n offset += 2;\n return val;\n }\n if (first === 0xfe) {\n requireBytes(4);\n const val =\n (witness[offset] |\n (witness[offset + 1] << 8) |\n (witness[offset + 2] << 16) |\n (witness[offset + 3] << 24)) >>>\n 0;\n offset += 4;\n return val;\n }\n // 0xff — 8-byte varint. Not used for witness sizes in practice and JS\n // numbers cannot represent all 64-bit values exactly, so reject rather\n // than risk silent truncation.\n throw new Error(\n `Malformed witness data: 8-byte varint (0xff) not supported at offset ${offset - 1}`,\n );\n };\n\n const count = readVarInt();\n for (let i = 0; i < count; i++) {\n const len = readVarInt();\n requireBytes(len);\n items.push(Buffer.from(witness.subarray(offset, offset + len)));\n offset += len;\n }\n\n if (offset !== witness.length) {\n throw new Error(\n `Malformed witness data: ${witness.length - offset} trailing byte(s) after parsing ${count} item(s)`,\n );\n }\n\n return items;\n}\n","/**\n * Asserts a wallet-returned PSBT encodes the same unsigned transaction\n * as the locally-built PSBT we asked the wallet to sign. Per-input PSBT\n * metadata (witnessUtxo, tapLeafScript, sighashType) is intentionally NOT\n * compared — those fields are committed to the Schnorr sighash and the\n * vault provider's `verify_depositor_signature` rejects mismatches there.\n * This primitive defends the path where a colluding VP would otherwise\n * accept a wallet-substituted signature.\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\n/**\n * Thrown when a wallet-returned PSBT encodes a different unsigned\n * transaction than the one the caller asked the wallet to sign.\n */\nexport class PsbtSubstitutionError extends Error {\n constructor(detail: string) {\n super(\n `Wallet returned a PSBT for a different transaction: ${detail}`,\n );\n this.name = \"PsbtSubstitutionError\";\n }\n}\n\nexport interface AssertPsbtUnsignedTxMatchesParams {\n /** PSBT we built locally and asked the wallet to sign. */\n requestedPsbtHex: string;\n /** PSBT the wallet returned after signing. */\n returnedPsbtHex: string;\n}\n\nfunction parsePsbt(label: \"requested\" | \"returned\", hex: string): Psbt {\n try {\n return Psbt.fromHex(hex);\n } catch (cause) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n throw new Error(`Failed to parse ${label} PSBT: ${reason}`);\n }\n}\n\n/**\n * Length of the hex prefix included in mismatch errors. Short enough that\n * full prevout txids and output scriptPubKeys never reach logs / error\n * trackers, long enough to disambiguate during forensic triage.\n */\nconst REDACTED_HEX_PREFIX_LEN = 8;\n\nfunction redactHex(buf: Buffer): string {\n return `${buf.toString(\"hex\").slice(0, REDACTED_HEX_PREFIX_LEN)}…`;\n}\n\n/**\n * `bitcoinjs-lib` exposes `txInputs[i].hash` in internal little-endian form;\n * a human reading logs expects the big-endian txid an explorer would show.\n * Reverse before truncating so the surfaced prefix matches what an operator\n * can search for.\n */\nfunction redactTxid(internalHash: Buffer): string {\n const reversed = Buffer.from(internalHash).reverse();\n return redactHex(reversed);\n}\n\n/**\n * Compare two PSBTs and throw `PsbtSubstitutionError` unless they encode\n * the same unsigned transaction (version, locktime, inputs, outputs).\n *\n * @throws PsbtSubstitutionError on any mismatch in the unsigned tx\n * @throws Error if either PSBT cannot be parsed\n */\nexport function assertPsbtUnsignedTxMatches(\n params: AssertPsbtUnsignedTxMatchesParams,\n): void {\n const requested = parsePsbt(\"requested\", params.requestedPsbtHex);\n const returned = parsePsbt(\"returned\", params.returnedPsbtHex);\n\n if (requested.version !== returned.version) {\n throw new PsbtSubstitutionError(\n `tx version differs (requested=${requested.version}, returned=${returned.version})`,\n );\n }\n if (requested.locktime !== returned.locktime) {\n throw new PsbtSubstitutionError(\n `tx locktime differs (requested=${requested.locktime}, returned=${returned.locktime})`,\n );\n }\n if (requested.txInputs.length !== returned.txInputs.length) {\n throw new PsbtSubstitutionError(\n `input count differs (requested=${requested.txInputs.length}, returned=${returned.txInputs.length})`,\n );\n }\n if (requested.txOutputs.length !== returned.txOutputs.length) {\n throw new PsbtSubstitutionError(\n `output count differs (requested=${requested.txOutputs.length}, returned=${returned.txOutputs.length})`,\n );\n }\n for (let i = 0; i < requested.txInputs.length; i++) {\n const r = requested.txInputs[i];\n const s = returned.txInputs[i];\n if (!r.hash.equals(s.hash)) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout txid differs (requested=${redactTxid(r.hash)}, returned=${redactTxid(s.hash)})`,\n );\n }\n if (r.index !== s.index) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout vout differs (requested=${r.index}, returned=${s.index})`,\n );\n }\n if (r.sequence !== s.sequence) {\n throw new PsbtSubstitutionError(\n `input ${i} sequence differs (requested=${r.sequence}, returned=${s.sequence})`,\n );\n }\n }\n for (let i = 0; i < requested.txOutputs.length; i++) {\n const r = requested.txOutputs[i];\n const s = returned.txOutputs[i];\n if (!r.script.equals(s.script)) {\n throw new PsbtSubstitutionError(\n `output ${i} scriptPubKey differs (requested=${redactHex(r.script)}, returned=${redactHex(s.script)})`,\n );\n }\n if (r.value !== s.value) {\n throw new PsbtSubstitutionError(\n `output ${i} value differs (requested=${r.value}, returned=${s.value})`,\n );\n }\n }\n}\n","/**\n * Independent BIP-340 verification of a wallet-returned Taproot script-path\n * Schnorr signature against an independently-recomputed sighash.\n *\n * Critical Path #7 (CLAUDE.md): the SDK requests script-path signatures with\n * `useTweakedSigner: false, autoFinalized: false`. Wallet support for the\n * untweaked-key flag is inconsistent — older OKX / mobile bridges silently sign\n * with the *tweaked* key, Keystone ignores the flag — and a compromised\n * extension can stuff a 64-byte stub into `tapScriptSig`. A bad signature that\n * the SDK forwards is only caught on broadcast; in the worst case it passes the\n * VP off-chain but Bitcoin rejects it, leaving the depositor's BTC locked in the\n * HTLC until `timelockRefund` matures. This guard rejects such signatures before\n * they are trusted.\n *\n * Why verify against the *locally-built* PSBT, not the wallet-returned one:\n * `assertPsbtUnsignedTxMatches` pins the unsigned transaction but deliberately\n * skips per-input metadata (`witnessUtxo`, `tapLeafScript`). A malicious wallet\n * could rewrite those consistently in the returned PSBT so a wrong-message\n * signature self-validates. The trusted prevout scripts/values and leaf script\n * therefore come from the PSBT we built ourselves (derived from on-chain / WASM\n * sources); only the 64-byte signature comes from the wallet.\n *\n * Reuses the exact primitives `bip322Verify.ts` already depends on — no new\n * dependency:\n * - `@bitcoin-js/tiny-secp256k1-asmjs` → `verifySchnorr`\n * - `bitcoinjs-lib` → `Transaction.hashForWitnessV1`\n * - `../utils/taproot` → `computeTapLeafHash`\n *\n * @module tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\n\nimport { Buffer } from \"buffer\";\n\nimport { X_ONLY_PUBKEY_HEX_LEN } from \"../../utils/validation\";\nimport {\n SCHNORR_SIG_HEX_LEN,\n TAPSCRIPT_LEAF_VERSION,\n hexToUint8Array,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\nimport { computeTapLeafHash } from \"../utils/taproot\";\n\nexport interface VerifyScriptPathSchnorrSignatureParams {\n /**\n * Hex of the PSBT we built locally and sent to the wallet (the trusted\n * source of prevout scripts/values and the leaf script). NOT the\n * wallet-returned PSBT.\n */\n requestedPsbtHex: string;\n /** The 64-byte Schnorr signature extracted from the wallet's response (128 hex chars). */\n signatureHex: string;\n /** X-only public key (64 hex chars) the wallet signed the script-path leaf with. */\n signerXOnlyPubkeyHex: string;\n /** Index of the input the signature is for. */\n inputIndex: number;\n}\n\n/**\n * Assert that `signatureHex` is a valid BIP-340 Schnorr signature by the\n * `signerXOnlyPubkeyHex` key over the Taproot script-path sighash of\n * `requestedPsbtHex` input `inputIndex` (SIGHASH_DEFAULT).\n *\n * @throws If the requested PSBT is malformed, lacks the prevout/leaf data needed\n * to recompute the sighash, or the signature does not verify.\n */\nexport function assertScriptPathSchnorrSignature(\n params: VerifyScriptPathSchnorrSignatureParams,\n): void {\n const { requestedPsbtHex, signatureHex, signerXOnlyPubkeyHex, inputIndex } =\n params;\n\n const signatureRaw = stripHexPrefix(signatureHex);\n if (signatureRaw.length !== SCHNORR_SIG_HEX_LEN) {\n throw new Error(\n `Schnorr signature for input ${inputIndex} must be ${SCHNORR_SIG_HEX_LEN} hex chars ` +\n `(64 bytes), got ${signatureRaw.length}.`,\n );\n }\n\n const signerXOnly = stripHexPrefix(signerXOnlyPubkeyHex);\n if (signerXOnly.length !== X_ONLY_PUBKEY_HEX_LEN) {\n throw new Error(\n `Signer x-only pubkey for input ${inputIndex} must be ${X_ONLY_PUBKEY_HEX_LEN} hex chars ` +\n `(32 bytes), got ${signerXOnly.length}.`,\n );\n }\n\n const psbt = Psbt.fromHex(requestedPsbtHex);\n\n if (inputIndex < 0 || inputIndex >= psbt.data.inputs.length) {\n throw new Error(\n `Input index ${inputIndex} out of range (${psbt.data.inputs.length} inputs).`,\n );\n }\n\n // Taproot's sighash commits to every input's prevout (script + value), so all\n // inputs must carry a witnessUtxo. A missing one is a build error, not a\n // value we can default — fail loudly.\n const prevOutScripts: Buffer[] = [];\n const values: number[] = [];\n for (let i = 0; i < psbt.data.inputs.length; i++) {\n const witnessUtxo = psbt.data.inputs[i].witnessUtxo;\n if (!witnessUtxo) {\n throw new Error(\n `Cannot verify signature: input ${i} of the requested PSBT has no witnessUtxo ` +\n `(required to recompute the Taproot sighash).`,\n );\n }\n prevOutScripts.push(witnessUtxo.script);\n values.push(witnessUtxo.value);\n }\n\n // The signed input must expose exactly one tapLeafScript — the leaf the\n // depositor signs. Zero means we sent the wrong PSBT; more than one means an\n // ambiguous spend path we never construct for a single-signature input.\n const tapLeafScripts = psbt.data.inputs[inputIndex].tapLeafScript;\n if (!tapLeafScripts || tapLeafScripts.length !== 1) {\n throw new Error(\n `Cannot verify signature: input ${inputIndex} of the requested PSBT must have exactly ` +\n `one tapLeafScript, got ${tapLeafScripts?.length ?? 0}.`,\n );\n }\n const leaf = tapLeafScripts[0];\n if (leaf.leafVersion !== TAPSCRIPT_LEAF_VERSION) {\n throw new Error(\n `Cannot verify signature: input ${inputIndex} tapLeafScript has leaf version ` +\n `0x${leaf.leafVersion.toString(16)}, expected 0x${TAPSCRIPT_LEAF_VERSION.toString(16)}.`,\n );\n }\n\n const leafHash = computeTapLeafHash(leaf.leafVersion, leaf.script);\n\n // Reconstruct the unsigned transaction from the requested PSBT using only\n // public bitcoinjs-lib API (same pattern as bip322Verify.ts), then compute the\n // BIP-341 script-path sighash with SIGHASH_DEFAULT.\n const tx = new Transaction();\n tx.version = psbt.version;\n tx.locktime = psbt.locktime;\n for (const input of psbt.txInputs) {\n tx.addInput(input.hash, input.index, input.sequence);\n }\n for (const output of psbt.txOutputs) {\n tx.addOutput(output.script, output.value);\n }\n\n const sighash = tx.hashForWitnessV1(\n inputIndex,\n prevOutScripts,\n values,\n Transaction.SIGHASH_DEFAULT,\n leafHash,\n );\n\n const isValid = ecc.verifySchnorr(\n sighash,\n hexToUint8Array(signerXOnly),\n hexToUint8Array(signatureRaw),\n );\n\n if (!isValid) {\n throw new Error(\n `Schnorr signature for input ${inputIndex} (signer ${signerXOnly}) does not verify ` +\n `against the expected Taproot script-path sighash. The wallet may have signed with ` +\n `the tweaked key, signed a different transaction, or returned an invalid signature.`,\n );\n }\n}\n"],"names":["normalizeKey","key","processPublicKeyToXOnly","computeNumLocalChallengers","vaultProviderPubkey","vaultKeeperPubkeys","depositorPubkey","localSet","vk","deriveLocalChallengers","params","claimer","depositor","vaultKeepers","filtered","k","PREIMAGE_LENGTH_BYTES","MAX_U16","SEC1_EVEN_Y_PREFIX","normalizeXOnlyKey","value","label","stripHexPrefix","secp256k1","Buffer","normalizeKeyGroup","values","keys","index","nOfNChunks","verify","chunks","opcodes","bscript","deriveExpectedPrePeginHtlc","hashlock","assertEccInitialized","vaultProvider","universalChallengers","cleanHashlock","hashlockScript","refundScript","scriptTree","TAPSCRIPT_LEAF_VERSION","hash","output","witness","payments","tapInternalPubkey","hashlockControlBlock","refundPayment","refundControlBlock","_a","deriveExpectedPeginPayoutScriptPubKey","timelockPegin","payoutScript","assertWasmPeginSizing","result","expectedCount","expectedClaimValue","computeMinClaimValue","anchor","peginP2aAnchorOutput","anchorValue","expectedPeginFee","computeMinPeginFee","expectedReserve","maxImpliedReserve","MAX_REASONABLE_PEGIN_VBYTES","i","requested","peginAmount","htlcValue","impliedReserve","HtlcOutputMismatchError","message","assertEncodedHtlcOutputsMatch","outputs","htlcValues","htlcScriptPubKeys","encodedValue","encodedScript","expectedScript","assertUnfundedPrePeginOutputLayout","authAnchorHash","version","locktime","PRE_PEGIN_TX_VERSION","PRE_PEGIN_TX_LOCKTIME","PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT","authOutputIndex","cpfpOutputIndex","peginOutputCount","authOutput","PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS","encodedAuthScript","expectedAuthScript","PRE_PEGIN_AUTH_SCRIPT_PREFIX","cpfpOutput","PRE_PEGIN_CPFP_VALUE_SATS","encodedCpfpScript","expectedCpfpScript","deriveBip86ScriptPubKeyHex","PEGIN_DEPOSITOR_CLAIM_VOUT","deriveDepositorClaimDescriptor","cleanPubkey","leafScript","hexToUint8Array","internalKey","redeem","controlBlock","deriveDepositorClaimScriptPubKey","AUTH_ANCHOR_HASH_HEX_LEN","HEX_PATTERN","MAX_TIMELOCK_PEGIN_BLOCKS","buildPrePeginPsbt","normalizeAuthAnchorHash","createPrePeginTransaction","minPeginFee","parsed","parseUnfundedWasmTransaction","totalOutputValue","sum","o","authAnchorVout","cleaned","buildPeginTxFromFundedPrePegin","buildPeginTxFromPrePegin","assertPeginTxShape","PEGIN_BASE_OUTPUT_COUNT","expectedTxVersion","PEGIN_TX_VERSION_CORE_1","PEGIN_TX_VERSION_CORE_2_AND_3","validatePeginP2aAnchor","expectedOutputCount","peginTx","Transaction","PEGIN_TX_LOCKTIME","fundedPrePeginTxid","inputTxid","uint8ArrayToHex","PEGIN_INPUT_SEQUENCE","requestedAmount","encodedVaultOut","expectedVaultScript","encodedVaultScript","encodedTxid","encodedClaimOut","expectedClaimScript","createPayoutScript","connector","createPayoutConnector","COMPACT_SIZE_UINT16_PREFIX","COMPACT_SIZE_UINT32_PREFIX","COMPACT_SIZE_UINT16_MAX","COMPACT_SIZE_UINT32_MAX","TAPLEAF_TAG","TAPBRANCH_TAG","TAPTWEAK_TAG","CONTROL_BLOCK_PREFIX_LEN","CONTROL_BLOCK_NODE_LEN","CONTROL_BLOCK_MAX_NODES","CONTROL_BLOCK_PARITY_MASK","CONTROL_BLOCK_LEAF_VERSION_MASK","P2TR_SCRIPT_PUBKEY_PREFIX","encodeCompactSize","n","computeTapLeafHash","leafVersion","script","preimage","bcrypto","computeTaprootScriptPubKey","binding","pathLen","controlLeafVersion","node","offset","sibling","tweak","tweaked","ecc","expectedParity","MAX_FLOOR_MODEL_KEEPERS","MAX_UNIVERSAL_CHALLENGERS","MAX_FLOOR_MODEL_COUNCIL_SIZE","MAX_SANE_FEE_RATE_SAT_PER_VB","MAX_PAYOUT_VSIZE_BASE","MAX_PAYOUT_VSIZE_PER_PARTICIPANT","PAYOUT_BOUND_ASSUMED_SCRIPT_LEN","assertPayoutFeeBandDomain","role","count","max","reason","assertPayoutFeeInBand","measured","implicitFeeSats","out0Len","out1Len","numParticipants","implicitFee","scriptExcess","maxPayoutVsize","maxFeeSats","minFeeSats","computePayoutFeeFloor","P2WPKH_LEN","P2WSH_LEN","P2TR_LEN","P2PKH_LEN","P2SH_LEN","isP2wpkh","s","isP2wsh","isP2tr","isP2pkh","isP2sh","assertStandardPayoutScript","scriptHex","TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE","buildPayoutPsbt","feeBandParams","payoutTx","assertTx","DEPOSITOR_PAYOUT_INPUT_COUNT","PAYOUT_TX_VERSION","PAYOUT_TX_LOCKTIME","peginPrevOut","requirePrevOut","PEGIN_VAULT_OUTPUT_INDEX","assertPrevOut","ASSERT_PAYOUT_OUTPUT_INDEX","assertPayoutOutputLayout","inputValueSats","outputValueSats","out","payoutConnector","assertPayoutLeaf","resolveAssertPayoutLeaf","assemblePayoutPsbt","payoutControlBlock","getAssertPayoutScriptInfo","boundScriptPubKey","input","inputIndex","parentTx","parentLabel","expectedVout","parentTxid","prevOut","psbt","Psbt","input0","input1","acceptedPayoutScriptHexes","registeredScriptPubKey","bondedPubkey","registered","legacyBip86","matchesAnyScript","acceptedHexes","hex","peginValueSats","claimerBtcPubkey","vaultProviderBtcPubkey","depositorBtcPubkey","vaultKeeperBtcPubkeys","registeredPayoutScriptPubKey","commissionBps","vkClaimerPayoutScriptPubKeys","vpCommissionScriptPubKey","isValidHex","vp","dep","keepers","expectedOutCount","acceptedOut0ScriptHexes","VP_CLAIMER_PAYOUT_OUTPUT_COUNT","NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT","anchorIdx","PAYOUT_ANCHOR_DUST_SATS","acceptedCommissionScriptHexes","MAX_VP_COMMISSION_BPS_EXCLUSIVE","maxCommissionSats","BPS_DENOMINATOR","MAX_PAYOUT_SCRIPT_LEN","extractPayoutSignature","signedPsbtHex","signedPsbt","depositorPubkeyBytes","sigEntry","extractSchnorrSig","witnessStack","parseWitnessStack","sig","items","requireBytes","readVarInt","first","val","len","PsbtSubstitutionError","detail","parsePsbt","cause","REDACTED_HEX_PREFIX_LEN","redactHex","buf","redactTxid","internalHash","reversed","assertPsbtUnsignedTxMatches","returned","r","assertScriptPathSchnorrSignature","requestedPsbtHex","signatureHex","signerXOnlyPubkeyHex","signatureRaw","SCHNORR_SIG_HEX_LEN","signerXOnly","X_ONLY_PUBKEY_HEX_LEN","prevOutScripts","witnessUtxo","tapLeafScripts","leaf","leafHash","tx","sighash"],"mappings":"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"}
1
+ {"version":3,"file":"verifyScriptPathSchnorrSignature-D2t1yACC.cjs","sources":["../src/tbv/core/primitives/challengers.ts","../src/tbv/core/primitives/psbt/assertWasmPeginSizing.ts","../src/tbv/core/primitives/psbt/depositorClaim.ts","../src/tbv/core/primitives/psbt/pegin.ts","../src/tbv/core/primitives/scripts/payout.ts","../src/tbv/core/primitives/utils/taproot.ts","../src/tbv/core/primitives/psbt/assertPayoutFeeBand.ts","../src/tbv/core/primitives/psbt/standardPayoutScript.ts","../src/tbv/core/primitives/psbt/payout.ts","../src/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.ts","../src/tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature.ts"],"sourcesContent":["/**\n * Challenger derivation utilities — counting local challengers for UI-level\n * validation (e.g. minimum deposit amounts) and deriving the exact\n * local-challenger set a claimer's tx graph is built from.\n */\n\nimport { processPublicKeyToXOnly } from \"./utils/bitcoin\";\n\n/**\n * Normalize a public key to lowercase x-only hex for reliable comparison.\n *\n * Handles `0x` prefixes, compressed (33-byte), and uncompressed (65-byte) keys.\n */\nfunction normalizeKey(key: string): string {\n return processPublicKeyToXOnly(key).toLowerCase();\n}\n\n/**\n * Compute the number of local challengers for a vault.\n *\n * Mirrors the VP's `compute_num_challengers()` logic:\n * local challengers = {vault_provider} ∪ {vault_keepers} − {depositor}\n *\n * Keys are normalized to x-only lowercase hex before comparison, so\n * `0x`-prefixed, compressed, or mixed-case keys are handled correctly.\n *\n * @param vaultProviderPubkey - Vault provider BTC public key\n * @param vaultKeeperPubkeys - Vault keeper BTC public keys\n * @param depositorPubkey - Depositor (claimer) BTC public key\n * @returns Number of local challengers\n */\nexport function computeNumLocalChallengers(\n vaultProviderPubkey: string,\n vaultKeeperPubkeys: string[],\n depositorPubkey: string,\n): number {\n const localSet = new Set<string>();\n localSet.add(normalizeKey(vaultProviderPubkey));\n for (const vk of vaultKeeperPubkeys) {\n localSet.add(normalizeKey(vk));\n }\n localSet.delete(normalizeKey(depositorPubkey));\n return localSet.size;\n}\n\n/** Roles a claimer can hold; determines which local-challenger rule applies. */\nexport interface DeriveLocalChallengersParams {\n /** The graph's claimer (VP, a vault keeper, or the depositor). */\n claimerBtcPubkey: string;\n /** Depositor registered on-chain for the vault. */\n depositorBtcPubkey: string;\n /** Vault provider registered on-chain for the vault. */\n vaultProviderBtcPubkey: string;\n /** Vault keepers registered on-chain for the vault. */\n vaultKeeperBtcPubkeys: string[];\n}\n\n/**\n * Derive the local-challenger set for a claimer's graph.\n *\n * Byte-for-byte mirror of btc-vault `derive_challengers_for`\n * (`crates/vault/src/tx_graph/graph.rs:257-284`):\n * - depositor-as-claimer → vault keepers only (VP excluded)\n * - VP- or VK-claimer → `({VP} ∪ VKs) − {claimer}`, sorted and deduplicated\n *\n * The protocol guarantees the depositor is not a vault keeper\n * (`TxGraphParams::validate`), so the depositor filter in the first branch is\n * defense-in-depth; it surfaces a clear error if a misconfigured context ever\n * violates the invariant.\n *\n * @throws If no local challenger remains, or (depositor path) the keeper set\n * contains duplicates — both mean the signing context is misconfigured.\n */\nexport function deriveLocalChallengers(\n params: DeriveLocalChallengersParams,\n): string[] {\n const claimer = normalizeKey(params.claimerBtcPubkey);\n const depositor = normalizeKey(params.depositorBtcPubkey);\n const vaultKeepers = params.vaultKeeperBtcPubkeys.map(normalizeKey);\n\n if (claimer === depositor) {\n const filtered = vaultKeepers.filter((k) => k !== depositor);\n if (filtered.length === 0) {\n throw new Error(\n \"Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)\",\n );\n }\n if (new Set(filtered).size !== filtered.length) {\n throw new Error(\n \"Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured\",\n );\n }\n return filtered;\n }\n\n // Rust sorts then dedups before filtering; a Set keeps insertion order, so\n // sort after deduping for the same result.\n const deduped = [\n ...new Set([normalizeKey(params.vaultProviderBtcPubkey), ...vaultKeepers]),\n ].sort();\n const filtered = deduped.filter((k) => k !== claimer);\n if (filtered.length === 0) {\n throw new Error(\n `Cannot derive localChallengers: no vault provider or vault keeper remains after excluding claimer ${claimer}`,\n );\n }\n return filtered;\n}\n","/**\n * Cross-check the values and outputs WASM returns from `createPrePeginTransaction`\n * against independently-known expectations before they feed a signed\n * Bitcoin transaction or the on-chain PegIn registration.\n *\n * CLAUDE.md critical path #1: the Rust/WASM layer computes\n * `htlcValue = peginAmount + depositorClaimValue + p2aAnchorValue +\n * minPeginFee` internally (the anchor term is 0 for graph versions without a\n * P2A anchor, 240 sats for v2/v3) and JS receives the outputs with no runtime\n * validation. A doctored or buggy binary that returns a different\n * `peginAmount`, an out-of-formula `htlcValue`, or a wrong\n * `depositorClaimValue` would otherwise be committed verbatim - taxing the\n * depositor or starving the downstream tx graph of fees.\n *\n * @module primitives/psbt/assertWasmPeginSizing\n */\n\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { script as bscript, opcodes, payments } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\nimport {\n computeMinClaimValue,\n computeMinPeginFee,\n peginP2aAnchorOutput,\n tapInternalPubkey,\n type PrePeginResult,\n} from \"../../wasm\";\n\nimport {\n MAX_REASONABLE_PEGIN_VBYTES,\n peginOutputCount,\n} from \"../../utils/fee/constants\";\nimport type { ParsedOutput } from \"../../utils/transaction/fundPeginTransaction\";\nimport {\n TAPSCRIPT_LEAF_VERSION,\n assertEccInitialized,\n deriveBip86ScriptPubKeyHex,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\n\nimport {\n PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS,\n PRE_PEGIN_AUTH_SCRIPT_PREFIX,\n PRE_PEGIN_CPFP_VALUE_SATS,\n PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT,\n PRE_PEGIN_TX_LOCKTIME,\n PRE_PEGIN_TX_VERSION,\n} from \"./constants\";\nimport type { PrePeginParams } from \"./pegin\";\n\nconst PREIMAGE_LENGTH_BYTES = 32;\nconst MAX_U16 = 0xffff;\nconst SEC1_EVEN_Y_PREFIX = 0x02;\n\ntype ScriptChunk = number | Buffer;\n\ntype PrePeginHtlcParams = Pick<\n PrePeginParams,\n | \"depositorPubkey\"\n | \"vaultProviderPubkey\"\n | \"vaultKeeperPubkeys\"\n | \"universalChallengerPubkeys\"\n | \"timelockRefund\"\n>;\n\ntype PeginPayoutParams = Pick<\n PrePeginParams,\n | \"depositorPubkey\"\n | \"vaultProviderPubkey\"\n | \"vaultKeeperPubkeys\"\n | \"universalChallengerPubkeys\"\n>;\n\ninterface ExpectedPrePeginHtlc {\n hashlockScript: Buffer;\n hashlockControlBlock: Buffer;\n refundScript: Buffer;\n refundControlBlock: Buffer;\n scriptPubKey: Buffer;\n tapMerkleRoot: Buffer;\n}\n\nfunction normalizeXOnlyKey(value: string, label: string): string {\n const key = stripHexPrefix(value).toLowerCase();\n if (!/^[0-9a-f]{64}$/.test(key)) {\n throw new Error(`${label} must be a 32-byte x-only public key.`);\n }\n\n try {\n secp256k1.Point.fromBytes(\n Buffer.concat([\n Buffer.from([SEC1_EVEN_Y_PREFIX]),\n Buffer.from(key, \"hex\"),\n ]),\n );\n } catch {\n throw new Error(`${label} is not a secp256k1 x-coordinate.`);\n }\n\n return key;\n}\n\nfunction normalizeKeyGroup(values: readonly string[], label: string): string[] {\n if (values.length === 0) {\n throw new Error(`${label} must not be empty.`);\n }\n\n const keys = values\n .map((value, index) => normalizeXOnlyKey(value, `${label}[${index}]`))\n .sort();\n if (new Set(keys).size !== keys.length) {\n throw new Error(`${label} must not contain duplicate keys.`);\n }\n return keys;\n}\n\nfunction nOfNChunks(keys: readonly string[], verify: boolean): ScriptChunk[] {\n const chunks: ScriptChunk[] = [];\n keys.forEach((key, index) => {\n chunks.push(\n Buffer.from(key, \"hex\"),\n index === 0 ? opcodes.OP_CHECKSIG : opcodes.OP_CHECKSIGADD,\n );\n });\n chunks.push(\n bscript.number.encode(keys.length),\n verify ? opcodes.OP_NUMEQUALVERIFY : opcodes.OP_NUMEQUAL,\n );\n return chunks;\n}\n\n/**\n * Derive the canonical Pre-PegIn HTLC without using vault WASM output.\n */\nexport function deriveExpectedPrePeginHtlc(\n params: PrePeginHtlcParams,\n hashlock: string,\n): ExpectedPrePeginHtlc {\n assertEccInitialized();\n\n const depositor = normalizeXOnlyKey(\n params.depositorPubkey,\n \"depositorPubkey\",\n );\n const vaultProvider = normalizeXOnlyKey(\n params.vaultProviderPubkey,\n \"vaultProviderPubkey\",\n );\n const vaultKeepers = normalizeKeyGroup(\n params.vaultKeeperPubkeys,\n \"vaultKeeperPubkeys\",\n );\n const universalChallengers = normalizeKeyGroup(\n params.universalChallengerPubkeys,\n \"universalChallengerPubkeys\",\n );\n const cleanHashlock = stripHexPrefix(hashlock).toLowerCase();\n if (!/^[0-9a-f]{64}$/.test(cleanHashlock)) {\n throw new Error(\"hashlock must be 32 bytes.\");\n }\n if (\n !Number.isInteger(params.timelockRefund) ||\n params.timelockRefund < 1 ||\n params.timelockRefund > MAX_U16\n ) {\n throw new Error(\"timelockRefund must be an integer from 1 to 65535.\");\n }\n\n const hashlockScript = bscript.compile([\n opcodes.OP_SIZE,\n bscript.number.encode(PREIMAGE_LENGTH_BYTES),\n opcodes.OP_EQUALVERIFY,\n opcodes.OP_SHA256,\n Buffer.from(cleanHashlock, \"hex\"),\n opcodes.OP_EQUALVERIFY,\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n Buffer.from(vaultProvider, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n ...nOfNChunks(vaultKeepers, true),\n ...nOfNChunks(universalChallengers, false),\n ]);\n const refundScript = bscript.compile([\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n bscript.number.encode(params.timelockRefund),\n opcodes.OP_CHECKSEQUENCEVERIFY,\n ]);\n const scriptTree: [\n { output: Buffer; version: number },\n { output: Buffer; version: number },\n ] = [\n { output: hashlockScript, version: TAPSCRIPT_LEAF_VERSION },\n { output: refundScript, version: TAPSCRIPT_LEAF_VERSION },\n ];\n const { hash, output, witness } = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree,\n redeem: {\n output: hashlockScript,\n redeemVersion: TAPSCRIPT_LEAF_VERSION,\n },\n });\n const hashlockControlBlock = witness?.[witness.length - 1];\n const refundPayment = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree,\n redeem: {\n output: refundScript,\n redeemVersion: TAPSCRIPT_LEAF_VERSION,\n },\n });\n const refundControlBlock =\n refundPayment.witness?.[refundPayment.witness.length - 1];\n if (!hash || !output || !hashlockControlBlock || !refundControlBlock) {\n throw new Error(\"Failed to derive the expected Pre-PegIn HTLC output.\");\n }\n\n return {\n hashlockScript,\n hashlockControlBlock,\n refundScript,\n refundControlBlock,\n scriptPubKey: output,\n tapMerkleRoot: hash,\n };\n}\n\n/**\n * Derive the canonical PegIn vault (payout) scriptPubKey without using vault\n * WASM output.\n */\nexport function deriveExpectedPeginPayoutScriptPubKey(\n params: PeginPayoutParams,\n timelockPegin: number,\n): Buffer {\n assertEccInitialized();\n\n const depositor = normalizeXOnlyKey(\n params.depositorPubkey,\n \"depositorPubkey\",\n );\n const vaultProvider = normalizeXOnlyKey(\n params.vaultProviderPubkey,\n \"vaultProviderPubkey\",\n );\n const vaultKeepers = normalizeKeyGroup(\n params.vaultKeeperPubkeys,\n \"vaultKeeperPubkeys\",\n );\n const universalChallengers = normalizeKeyGroup(\n params.universalChallengerPubkeys,\n \"universalChallengerPubkeys\",\n );\n\n // btc-vault crates/vault/src/connectors/pegin_payout.rs at 2c1177ec,\n // 27c0062b, and e1e50f66 uses this single leaf and the fixed internal key.\n const payoutScript = bscript.compile([\n Buffer.from(depositor, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n Buffer.from(vaultProvider, \"hex\"),\n opcodes.OP_CHECKSIGVERIFY,\n ...nOfNChunks(vaultKeepers, true),\n ...nOfNChunks(universalChallengers, true),\n bscript.number.encode(timelockPegin),\n opcodes.OP_CHECKSEQUENCEVERIFY,\n ]);\n const { output } = payments.p2tr({\n internalPubkey: Buffer.from(tapInternalPubkey),\n scriptTree: { output: payoutScript, version: TAPSCRIPT_LEAF_VERSION },\n });\n if (!output) {\n throw new Error(\"Failed to derive the expected PegIn payout output.\");\n }\n\n return output;\n}\n\n/**\n * Assert the WASM Pre-PegIn sizing result is internally consistent and\n * matches what the caller requested.\n *\n * Two layers of checks. Pure-JS, binary-INDEPENDENT: the per-HTLC\n * `peginAmount` must echo the requested amount, array lengths must match,\n * every value must be positive, and the implied per-HTLC reserve\n * (`htlcValue - peginAmount - depositorClaimValue`) must be strictly\n * positive and under the plausibility cap\n * ({@link MAX_REASONABLE_PEGIN_VBYTES}). WASM-vs-WASM consistency: the\n * reserve must also EXACTLY equal `computeMinPeginFee(version, ...) +\n * p2aAnchorValue(version)` (like the `computeMinClaimValue` check) -\n * exact, but both sides come from the same binary, which is why the\n * independent bounds above are kept alongside it.\n *\n * The resume rebuild (`deposit-terms/rebuildDepositTermsCore.ts`) mirrors these\n * bounds (positivity + reserve band) - keep the two in sync when editing.\n *\n * @param result - The result returned by `createPrePeginTransaction`.\n * @param params - The parameters that were passed to build it.\n * @returns The independently computed `minPeginFee` this function already\n * asserted against `result`'s implied reserve - callers that need the\n * value should reuse this instead of recomputing it.\n * @throws If any value is missing, non-positive, mismatched against the\n * request, or outside the protocol formula.\n */\nexport async function assertWasmPeginSizing(\n result: PrePeginResult,\n params: PrePeginParams,\n): Promise<bigint> {\n const expectedCount = params.pegInAmounts.length;\n\n if (params.hashlocks.length !== expectedCount) {\n throw new Error(\n `Pre-PegIn has ${params.hashlocks.length} hashlock(s), expected ` +\n `${expectedCount} (one per requested deposit).`,\n );\n }\n\n // Count: every parallel array must carry exactly one entry per requested\n // deposit, otherwise the per-HTLC indexing downstream is meaningless.\n if (result.htlcValues.length !== expectedCount) {\n throw new Error(\n `WASM Pre-PegIn returned ${result.htlcValues.length} HTLC value(s), ` +\n `expected ${expectedCount} (one per requested deposit).`,\n );\n }\n if (\n result.peginAmounts.length !== expectedCount ||\n result.htlcScriptPubKeys.length !== expectedCount ||\n result.htlcAddresses.length !== expectedCount\n ) {\n throw new Error(\n `WASM Pre-PegIn returned mismatched array lengths ` +\n `(htlcValues=${result.htlcValues.length}, ` +\n `peginAmounts=${result.peginAmounts.length}, ` +\n `htlcScriptPubKeys=${result.htlcScriptPubKeys.length}, ` +\n `htlcAddresses=${result.htlcAddresses.length}); ` +\n `expected ${expectedCount} each.`,\n );\n }\n\n // depositorClaimValue: positivity + WASM-vs-WASM consistency. Sized by the\n // tx-graph `feeRate` (see PrePeginParams.feeRate), so the standalone\n // `computeMinClaimValue` must reproduce the constructor's internal value.\n if (result.depositorClaimValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn returned non-positive depositorClaimValue ` +\n `${result.depositorClaimValue}; expected > 0.`,\n );\n }\n const expectedClaimValue = await computeMinClaimValue(\n // Must price the same graph version the builder constructed.\n params.vaultCoreVersion,\n params.numLocalChallengers,\n params.universalChallengerPubkeys.length,\n params.councilQuorum,\n params.councilSize,\n params.feeRate,\n );\n if (result.depositorClaimValue !== expectedClaimValue) {\n throw new Error(\n `WASM Pre-PegIn depositorClaimValue ${result.depositorClaimValue} does ` +\n `not match the independently computed minimum claim value ` +\n `${expectedClaimValue} (vaultCoreVersion=${params.vaultCoreVersion}, ` +\n `numLocalChallengers=${params.numLocalChallengers}, ` +\n `numUniversalChallengers=${params.universalChallengerPubkeys.length}, ` +\n `councilQuorum=${params.councilQuorum}, councilSize=${params.councilSize}, ` +\n `feeRate=${params.feeRate}).`,\n );\n }\n\n // The per-HTLC reserve above pegin+claim decomposes into the version's\n // P2A anchor value (0 when the version has no anchor) plus the exact\n // minimum PegIn fee. Both terms are Rust-model values fetched through\n // independent WASM entry points; the builder must reproduce their sum.\n // The Rust fee model sizes the PegIn input witness from the vault keeper\n // and universal challenger counts (btc-vault `PegInTx::estimate_vsize`).\n const anchor = await peginP2aAnchorOutput(params.vaultCoreVersion);\n const anchorValue = anchor?.value ?? 0n;\n const expectedPeginFee = await computeMinPeginFee(\n params.vaultCoreVersion,\n params.vaultKeeperPubkeys.length,\n params.universalChallengerPubkeys.length,\n params.minPeginFeeRate,\n );\n const expectedReserve = expectedPeginFee + anchorValue;\n // Binary-INDEPENDENT plausibility cap: the exact identity below compares\n // WASM against WASM, so a consistently doctored binary could satisfy it\n // with an inflated reserve. This pure-JS bound (max standard-relay tx\n // vbytes x the caller's fee rate) caps how much a compromised binary can\n // burn.\n const maxImpliedReserve =\n params.minPeginFeeRate * MAX_REASONABLE_PEGIN_VBYTES;\n\n for (let i = 0; i < expectedCount; i++) {\n const requested = params.pegInAmounts[i];\n const peginAmount = result.peginAmounts[i];\n const htlcValue = result.htlcValues[i];\n\n // Amount echo (strongest, fully independent): the recorded pegin amount\n // must equal exactly what the caller requested. A mismatch is the\n // WASM-tax attack - the contract would record a doctored amount while the\n // depositor's wallet funds the original, and the difference is a\n // WASM-controlled tax.\n if (peginAmount !== requested) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] ${peginAmount} does not match the ` +\n `requested amount ${requested}; refusing to build a tx whose ` +\n `recorded amount differs from the depositor's request.`,\n );\n }\n if (peginAmount <= 0n) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] is non-positive (${peginAmount}); ` +\n `expected > 0.`,\n );\n }\n if (htlcValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] is non-positive (${htlcValue}); ` +\n `expected > 0.`,\n );\n }\n\n // Formula: htlcValue = peginAmount + depositorClaimValue +\n // p2aAnchorValue + minPeginFee. The reserve must match exactly - a\n // shortfall starves the PegIn's fee/anchor, an excess locks sats\n // irrecoverably in the HTLC.\n const impliedReserve = htlcValue - peginAmount - result.depositorClaimValue;\n // Independent JS-side bounds first (see maxImpliedReserve above): the\n // reserve must be strictly positive (a zero reserve starves the PegIn\n // of its fee) and plausibly sized, regardless of what the binary's own\n // reference entry points claim.\n if (impliedReserve <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} does not strictly ` +\n `cover peginAmount ${peginAmount} + depositorClaimValue ` +\n `${result.depositorClaimValue} + a PegIn reserve (implied ` +\n `reserve ${impliedReserve}).`,\n );\n }\n if (impliedReserve > maxImpliedReserve) {\n throw new Error(\n `WASM Pre-PegIn implied reserve for HTLC[${i}] (${impliedReserve} ` +\n `sat) exceeds the plausibility cap ${maxImpliedReserve} sat ` +\n `(minPeginFeeRate=${params.minPeginFeeRate} × ` +\n `${MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${htlcValue} ` +\n `appears grossly inflated.`,\n );\n }\n if (impliedReserve !== expectedReserve) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} implies a PegIn ` +\n `fee+anchor reserve of ${impliedReserve} sat, expected exactly ` +\n `${expectedReserve} sat (minPeginFee ${expectedPeginFee} + ` +\n `p2aAnchor ${anchorValue} for vaultCoreVersion ` +\n `${params.vaultCoreVersion}, vaultKeepers=` +\n `${params.vaultKeeperPubkeys.length}, universalChallengers=` +\n `${params.universalChallengerPubkeys.length}, minPeginFeeRate=` +\n `${params.minPeginFeeRate}).`,\n );\n }\n }\n\n return expectedPeginFee;\n}\n\n/**\n * A funded Pre-PegIn's HTLC outputs disagree with the expected value or\n * scriptPubKey.\n *\n * Typed so a caller trialling several parameter sets can tell \"the transaction\n * definitively does not match these parameters\" - a genuine, informative\n * rejection - from \"evaluating this parameter set failed\", which says nothing\n * about the transaction and must not be counted as a rejection. Without the\n * distinction an incidental failure on the true parameter set silently removes\n * it from consideration and a look-alike wins.\n */\nexport class HtlcOutputMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HtlcOutputMismatchError\";\n }\n}\n\n/**\n * Bind the validated metadata to the bytes that actually get funded and\n * signed.\n *\n * `assertWasmPeginSizing` proves the WASM values match the request and the\n * protocol formula. It also checks the script metadata count. The transaction\n * the depositor funds and signs is `result.txHex`. If the encoded tx carried a\n * different HTLC output value or script than the metadata, the depositor would\n * fund a transaction whose outputs differ from the checked metadata. This\n * function closes that link.\n *\n * The WASM lays out HTLC outputs first (vouts `0..N-1`), then the optional\n * auth-anchor OP_RETURN, then the CPFP anchor - so we only compare the first\n * `htlcValues.length` outputs.\n *\n * @param outputs - Outputs parsed from the unfunded Pre-PegIn tx hex.\n * @param htlcValues - The (already value-validated) per-HTLC values.\n * @param htlcScriptPubKeys - The per-HTLC scriptPubKeys (hex).\n * @throws {HtlcOutputMismatchError} If the encoded outputs are too few, or any\n * HTLC output's value or scriptPubKey disagrees with the validated metadata.\n */\nexport function assertEncodedHtlcOutputsMatch(\n outputs: readonly ParsedOutput[],\n htlcValues: readonly bigint[],\n htlcScriptPubKeys: readonly string[],\n): void {\n if (outputs.length < htlcValues.length) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn tx has ${outputs.length} output(s), fewer than the ` +\n `${htlcValues.length} HTLC output(s) the cross-check validated.`,\n );\n }\n\n for (let i = 0; i < htlcValues.length; i++) {\n const encodedValue = BigInt(outputs[i].value);\n if (encodedValue !== htlcValues[i]) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn HTLC output[${i}] value ${encodedValue} does not ` +\n `match the cross-checked htlcValue ${htlcValues[i]}; the funded/signed ` +\n `tx would not pay the validated amount.`,\n );\n }\n\n const encodedScript = outputs[i].script.toString(\"hex\").toLowerCase();\n const expectedScript = htlcScriptPubKeys[i].toLowerCase();\n if (encodedScript !== expectedScript) {\n throw new HtlcOutputMismatchError(\n `Encoded Pre-PegIn HTLC output[${i}] scriptPubKey ${encodedScript} does ` +\n `not match the cross-checked htlcScriptPubKey ${expectedScript}.`,\n );\n }\n }\n}\n\n/**\n * Validate the unfunded Pre-PegIn protocol output layout.\n *\n * This check rejects a wrong output count, a wrong auth or CPFP output, and an\n * HTLC output that does not match the request. Run it before value summing,\n * UTXO selection, or signing.\n */\nexport function assertUnfundedPrePeginOutputLayout(\n outputs: readonly ParsedOutput[],\n htlcValues: readonly bigint[],\n htlcScriptPubKeys: readonly string[],\n params: PrePeginParams,\n authAnchorHash: string | undefined,\n version: number,\n locktime: number,\n): void {\n if (version !== PRE_PEGIN_TX_VERSION) {\n throw new Error(\n `WASM Pre-PegIn transaction version ${version}; expected ` +\n `${PRE_PEGIN_TX_VERSION}.`,\n );\n }\n if (locktime !== PRE_PEGIN_TX_LOCKTIME) {\n throw new Error(\n `WASM Pre-PegIn transaction locktime ${locktime}; expected ` +\n `${PRE_PEGIN_TX_LOCKTIME}.`,\n );\n }\n\n if (htlcValues.length < PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT) {\n throw new Error(\n `WASM Pre-PegIn output layout has ${htlcValues.length} HTLC output(s); ` +\n `expected at least ${PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT}.`,\n );\n }\n\n const authOutputIndex = htlcValues.length;\n const cpfpOutputIndex =\n authOutputIndex + Number(authAnchorHash !== undefined);\n const expectedCount = peginOutputCount(\n htlcValues.length,\n authAnchorHash !== undefined,\n );\n if (outputs.length !== expectedCount) {\n throw new Error(\n `WASM Pre-PegIn output layout has ${outputs.length} output(s); ` +\n `expected exactly ${expectedCount}.`,\n );\n }\n\n assertEncodedHtlcOutputsMatch(outputs, htlcValues, htlcScriptPubKeys);\n\n for (let i = 0; i < htlcValues.length; i++) {\n const expectedScript = deriveExpectedPrePeginHtlc(\n params,\n params.hashlocks[i],\n ).scriptPubKey;\n if (!outputs[i].script.equals(expectedScript)) {\n throw new HtlcOutputMismatchError(\n `Pre-PegIn HTLC output[${i}] does not match the independently ` +\n `derived scriptPubKey.`,\n );\n }\n }\n\n if (authAnchorHash !== undefined) {\n const authOutput = outputs[authOutputIndex];\n if (authOutput.value !== PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS) {\n throw new Error(\n `WASM Pre-PegIn auth output[${authOutputIndex}] value ` +\n `${authOutput.value} sat; expected exactly ` +\n `${PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS} sat.`,\n );\n }\n\n const encodedAuthScript = authOutput.script.toString(\"hex\").toLowerCase();\n const expectedAuthScript =\n PRE_PEGIN_AUTH_SCRIPT_PREFIX + authAnchorHash.toLowerCase();\n if (encodedAuthScript !== expectedAuthScript) {\n throw new Error(\n `WASM Pre-PegIn auth output[${authOutputIndex}] scriptPubKey ` +\n `${encodedAuthScript}; expected ${expectedAuthScript}.`,\n );\n }\n }\n\n const cpfpOutput = outputs[cpfpOutputIndex];\n if (cpfpOutput.value !== PRE_PEGIN_CPFP_VALUE_SATS) {\n throw new Error(\n `WASM Pre-PegIn CPFP output[${cpfpOutputIndex}] value ` +\n `${cpfpOutput.value} sat; expected exactly ` +\n `${PRE_PEGIN_CPFP_VALUE_SATS} sat.`,\n );\n }\n\n const encodedCpfpScript = cpfpOutput.script.toString(\"hex\").toLowerCase();\n const expectedCpfpScript = stripHexPrefix(\n deriveBip86ScriptPubKeyHex(params.depositorPubkey),\n ).toLowerCase();\n if (encodedCpfpScript !== expectedCpfpScript) {\n throw new Error(\n `WASM Pre-PegIn CPFP output[${cpfpOutputIndex}] scriptPubKey ` +\n `${encodedCpfpScript}; expected depositor BIP-86 script ` +\n `${expectedCpfpScript}.`,\n );\n }\n}\n","/**\n * Depositor-claim output descriptor\n *\n * The PegIn transaction's vout 1 carries `depositorClaimValue` — the reserve\n * that funds the depositor's own `claim_tx`, their recourse if the vault\n * provider fails during peg-out.\n *\n * It is btc-vault's `SingleKeyConnector` (`crates/vault/src/connectors/mod.rs`):\n * a Taproot output with the NUMS internal key (key path provably unspendable)\n * and exactly one tapleaf at depth 0, `<depositor> OP_CHECKSIG`. No timelock,\n * no counterparty key — the depositor's Schnorr signature alone spends it.\n *\n * Two call sites need this, and they must not drift:\n * - `assertPeginTxShape` (./pegin) validates the encoded PegIn output against\n * the scriptPubKey before the depositor signs the peg-in.\n * - `buildReclaimPsbt` (./reclaim) spends that same output, and needs the leaf\n * script and control block to populate the PSBT's `tapLeafScript`.\n *\n * Keeping one definition here is what makes the reclaim's script binding\n * meaningful: the bytes the reclaim signs against are the same bytes the\n * peg-in validated.\n *\n * @module primitives/psbt/depositorClaim\n */\n\nimport { tapInternalPubkey } from \"../../wasm\";\nimport { Buffer } from \"buffer\";\nimport { payments, script as bscript, opcodes } from \"bitcoinjs-lib\";\n\nimport {\n TAPSCRIPT_LEAF_VERSION,\n hexToUint8Array,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\n\n/**\n * Vout of the depositor-claim output in every PegIn version (btc-vault: vault\n * at 0, depositor claim at 1, optional P2A anchor appended after).\n *\n * Version-invariant: the graph version dispatches only the trailing P2A anchor\n * (absent in v1, 240 sats at vout 2 in v2/v3). Nothing touches vout 1.\n */\nexport const PEGIN_DEPOSITOR_CLAIM_VOUT = 1;\n\n/** The single-leaf taptree's spend material for one depositor key. */\nexport interface DepositorClaimDescriptor {\n /** P2TR scriptPubKey the PegIn pays at {@link PEGIN_DEPOSITOR_CLAIM_VOUT}. */\n scriptPubKey: Buffer;\n /** The one tapleaf: `<depositor> OP_CHECKSIG`, 34 bytes. */\n leafScript: Buffer;\n /**\n * Control block for that leaf: `[leafVersion | outputKeyParity] || NUMS`,\n * 33 bytes. The tree has a single leaf at depth 0, so it carries no sibling\n * hashes.\n */\n controlBlock: Buffer;\n /**\n * The NUMS internal key the taptree commits to — the PSBT's `tapInternalKey`.\n * Carried here so an input's internal key and control block provably come\n * from the same derivation.\n */\n internalKey: Buffer;\n}\n\n/**\n * Derive the depositor-claim output's spend material in JS, independently of\n * WASM — the Rust `SingleKeyConnector` has no WASM wrapper, so this is the\n * only derivation available on the JS side.\n *\n * Takes no graph version: the connector is identical across v1/v2/v3. A future\n * `VAULT_WASM_COMMIT` bump that changed it would break `assertPeginTxShape` at\n * peg-in build time, which is where that regression should surface.\n *\n * @param depositorPubkey - x-only depositor pubkey, 64-char hex (no 0x prefix)\n * @throws If bitcoinjs cannot derive the P2TR output or its control block\n */\nexport function deriveDepositorClaimDescriptor(\n depositorPubkey: string,\n): DepositorClaimDescriptor {\n // Validate before compiling: `bscript.compile` happily emits a shorter push\n // for a truncated key, yielding a well-formed script that is not the claim\n // leaf. Downstream that becomes a scriptPubKey nobody can spend, so reject\n // here rather than deriving something plausible. Mirrors the same check in\n // `deriveBip86ScriptPubKeyHex`.\n const cleanPubkey = stripHexPrefix(depositorPubkey);\n if (!/^[0-9a-fA-F]{64}$/.test(cleanPubkey)) {\n throw new Error(\n \"Invalid depositor pubkey for the depositor-claim script: must be an \" +\n \"x-only key of 64 hex characters (32 bytes).\",\n );\n }\n\n const leafScript = bscript.compile([\n Buffer.from(hexToUint8Array(cleanPubkey)),\n opcodes.OP_CHECKSIG,\n ]);\n\n const internalKey = Buffer.from(tapInternalPubkey);\n const redeem = { output: leafScript, redeemVersion: TAPSCRIPT_LEAF_VERSION };\n const { output, witness } = payments.p2tr({\n internalPubkey: internalKey,\n scriptTree: { output: leafScript },\n redeem,\n });\n\n if (!output) {\n throw new Error(\n \"Failed to derive the depositor-claim P2TR scriptPubKey for PegIn output validation\",\n );\n }\n // bitcoinjs appends the control block last in the script-path witness stack\n // ([...redeemWitness, script, controlBlock]); with `redeem` supplied it is\n // always present, but a missing entry must fail rather than sign unspendably.\n const controlBlock = witness?.[witness.length - 1];\n if (!controlBlock) {\n throw new Error(\n \"Failed to derive the depositor-claim tapleaf control block; refusing to build an unspendable input\",\n );\n }\n\n return { scriptPubKey: output, leafScript, controlBlock, internalKey };\n}\n\n/**\n * The depositor-claim output's scriptPubKey alone — the peg-in validation path,\n * which has no need of the spend material.\n */\nexport function deriveDepositorClaimScriptPubKey(\n depositorPubkey: string,\n): Buffer {\n return deriveDepositorClaimDescriptor(depositorPubkey).scriptPubKey;\n}\n","/**\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 { Transaction } from \"bitcoinjs-lib\";\nimport {\n buildPeginTxFromPrePegin,\n computeMinClaimValue,\n createPrePeginTransaction,\n peginP2aAnchorOutput,\n validatePeginP2aAnchor,\n type Network,\n} from \"../../wasm\";\n\nimport { parseUnfundedWasmTransaction } from \"../../utils/transaction/fundPeginTransaction\";\nimport { stripHexPrefix, uint8ArrayToHex } from \"../utils/bitcoin\";\n\nimport {\n assertUnfundedPrePeginOutputLayout,\n assertWasmPeginSizing,\n deriveExpectedPeginPayoutScriptPubKey,\n} from \"./assertWasmPeginSizing\";\nimport {\n PEGIN_INPUT_SEQUENCE,\n PEGIN_TX_LOCKTIME,\n PEGIN_TX_VERSION_CORE_1,\n PEGIN_TX_VERSION_CORE_2_AND_3,\n} from \"./constants\";\nimport {\n PEGIN_DEPOSITOR_CLAIM_VOUT,\n deriveDepositorClaimScriptPubKey,\n} from \"./depositorClaim\";\n\n/**\n * Parameters for building an unfunded Pre-PegIn PSBT\n */\nexport interface PrePeginParams {\n /**\n * Vault core (tx-graph) version to build. Fresh deposits use the contract's\n * `ProtocolParams.activeVaultCoreVersion()`; resumed vaults use their\n * stamped on-chain `vaultCoreVersion`. The WASM facade fails closed on\n * versions it wasn't compiled with.\n */\n vaultCoreVersion: number;\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 /** TX-graph fee rate in sat/vB from contract offchain params; sizes the depositor claim value */\n feeRate: bigint;\n /** Minimum PegIn fee rate in sat/vB from contract offchain params; sizes the PegIn tx fee */\n minPeginFeeRate: 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 * The largest PegIn timelock in blocks. The engine stores it as a Rust u16.\n */\nconst MAX_TIMELOCK_PEGIN_BLOCKS = 0xffff;\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 /**\n * HTLC output values in satoshis, one per deposit. Each includes\n * peginAmount + depositorClaimValue + p2aAnchorValue + minPeginFee (the\n * anchor term is 0 for graph versions without a P2A anchor, 240 for v2/v3).\n */\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 * Minimum PegIn fee (sats), independently computed and asserted against\n * `htlcValues`' implied reserve by {@link assertWasmPeginSizing}. Reuse\n * this instead of recomputing — it is already the cross-checked value.\n */\n minPeginFee: bigint;\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 /**\n * PegIn transaction hex. 1 input spending the HTLC; outputs are\n * version-shaped: v1 = vault + depositor claim, v2/v3 = vault + depositor\n * claim + P2A anchor at vout 2 (nVersion 3 / TRUC).\n */\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 txGraphVersion: params.vaultCoreVersion,\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 minPeginFeeRate: params.minPeginFeeRate,\n numLocalChallengers: params.numLocalChallengers,\n councilQuorum: params.councilQuorum,\n councilSize: params.councilSize,\n network: params.network,\n authAnchorHash,\n });\n\n // CLAUDE.md critical path #1: the WASM outputs reach JS with no runtime\n // validation. Cross-check every value-bearing field against the request\n // and the protocol formula before it can feed a signed tx or the on-chain\n // PegIn registration. Both the sizing and commit passes route through here.\n const minPeginFee = await assertWasmPeginSizing(result, params);\n\n // Parse the unfunded tx before summing 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\n // Bind the validated metadata to the bytes that get funded and signed.\n // Reject a wrong count, a wrong auth or CPFP output, or an HTLC that does\n // not match the request before UTXO selection uses the output values.\n assertUnfundedPrePeginOutputLayout(\n parsed.outputs,\n result.htlcValues,\n result.htlcScriptPubKeys,\n params,\n authAnchorHash,\n parsed.version,\n parsed.locktime,\n );\n\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 minPeginFee,\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 */\nexport function 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 `timelockPegin` is not a whole number from 1 to 65535, if WASM\n * initialization fails or parameters are invalid, or if the WASM result does\n * not match the request (for example, a vault scriptPubKey that differs from\n * the independently derived payout scriptPubKey)\n */\nexport async function buildPeginTxFromFundedPrePegin(\n params: BuildPeginTxParams,\n): Promise<PeginTxResult> {\n // Reject the timelock before it crosses into the engine. The engine keeps\n // only the low 16 bits and drops a fraction, and rejects only a zero\n // result. Thus 65537 would build a one-block vault, not the requested one.\n if (\n !Number.isInteger(params.timelockPegin) ||\n params.timelockPegin < 1 ||\n params.timelockPegin > MAX_TIMELOCK_PEGIN_BLOCKS\n ) {\n throw new Error(\n `PegIn timelock ${params.timelockPegin} must be a whole number of ` +\n `blocks from 1 to ${MAX_TIMELOCK_PEGIN_BLOCKS}.`,\n );\n }\n\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 txGraphVersion: params.prePeginParams.vaultCoreVersion,\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 minPeginFeeRate: params.prePeginParams.minPeginFeeRate,\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 await assertPeginTxShape(result, params);\n\n return {\n txHex: result.txHex,\n txid: result.txid,\n vaultScriptPubKey: result.vaultScriptPubKey,\n vaultValue: result.vaultValue,\n };\n}\n\n/**\n * PegIn outputs common to every graph version: the vault output (vout 0)\n * and the depositor claim output (vout 1). Versions with a P2A anchor (v2)\n * append it after these.\n */\nconst PEGIN_BASE_OUTPUT_COUNT = 2;\n\n/**\n * Cross-check the WASM-built PegIn transaction's header, input, and outputs\n * against the request before the depositor signs it.\n *\n * CLAUDE.md critical path #1: the metadata (`vaultValue`, `txid`,\n * `vaultScriptPubKey`) and the tx bytes both come from WASM — bind them to\n * each other and to the caller's requested amount so a doctored binary\n * can't commit one thing and encode another. The reported and the encoded\n * vault scriptPubKey must both equal the payout scriptPubKey that TypeScript\n * derives from the request. The vault output value is the\n * exact on-chain vault amount, so it must equal the requested peg-in amount\n * (btc-vault: PegIn vout 0 carries `pegin_amount` verbatim). The P2A anchor\n * (exact value/vout/script for v2; complete absence for v1) is enforced by\n * the version-dispatched `validatePeginP2aAnchor`.\n *\n * @throws If the encoded transaction disagrees with the request, metadata,\n * or canonical shape for its Vault Core version.\n */\nasync function assertPeginTxShape(\n result: {\n txHex: string;\n txid: string;\n vaultScriptPubKey: string;\n vaultValue: bigint;\n },\n params: BuildPeginTxParams,\n): Promise<void> {\n const version = params.prePeginParams.vaultCoreVersion;\n const expectedTxVersion =\n version === 1\n ? PEGIN_TX_VERSION_CORE_1\n : version === 2 || version === 3\n ? PEGIN_TX_VERSION_CORE_2_AND_3\n : undefined;\n if (expectedTxVersion === undefined) {\n throw new Error(`Unsupported vaultCoreVersion ${version} for PegIn tx.`);\n }\n\n await validatePeginP2aAnchor(version, result.txHex);\n\n const anchor = await peginP2aAnchorOutput(version);\n const expectedOutputCount = PEGIN_BASE_OUTPUT_COUNT + (anchor ? 1 : 0);\n\n const peginTx = Transaction.fromHex(stripHexPrefix(result.txHex));\n\n if (peginTx.version !== expectedTxVersion) {\n throw new Error(\n `PegIn tx version ${peginTx.version} does not match ` +\n `vaultCoreVersion ${version}; expected ${expectedTxVersion}.`,\n );\n }\n if (peginTx.locktime !== PEGIN_TX_LOCKTIME) {\n throw new Error(\n `PegIn tx locktime ${peginTx.locktime} does not match the canonical ` +\n `locktime ${PEGIN_TX_LOCKTIME}.`,\n );\n }\n\n // Input bind: the PegIn must spend EXACTLY the requested HTLC outpoint\n // (fundedPrePeginTxid, htlcVout). Without this, a doctored tx could spend\n // a sibling HTLC while the caller registers it under `htlcVout` — in a\n // multi-HTLC batch that both double-spends the sibling and strands the\n // registered vault. The downstream PSBT builder follows the tx's own\n // input index, so this is the only place the requested vout is enforced.\n if (peginTx.ins.length !== 1) {\n throw new Error(\n `PegIn tx has ${peginTx.ins.length} input(s), expected exactly 1 ` +\n `(the Pre-PegIn HTLC outpoint).`,\n );\n }\n const fundedPrePeginTxid = Transaction.fromHex(\n stripHexPrefix(params.fundedPrePeginTxHex),\n ).getId();\n const inputTxid = uint8ArrayToHex(\n new Uint8Array(peginTx.ins[0].hash).slice().reverse(),\n );\n if (inputTxid !== fundedPrePeginTxid) {\n throw new Error(\n `PegIn input spends txid ${inputTxid}, expected the funded Pre-PegIn ` +\n `${fundedPrePeginTxid}.`,\n );\n }\n if (peginTx.ins[0].index !== params.htlcVout) {\n throw new Error(\n `PegIn input spends Pre-PegIn output ${peginTx.ins[0].index}, ` +\n `expected the requested HTLC vout ${params.htlcVout}.`,\n );\n }\n if (peginTx.ins[0].sequence !== PEGIN_INPUT_SEQUENCE) {\n throw new Error(\n `PegIn input sequence ${peginTx.ins[0].sequence} does not match the ` +\n `canonical sequence ${PEGIN_INPUT_SEQUENCE}.`,\n );\n }\n if (peginTx.ins[0].script.length !== 0) {\n throw new Error(\"PegIn input scriptSig must be empty.\");\n }\n if (peginTx.ins[0].witness.length !== 0) {\n throw new Error(\"PegIn input witness must be empty before signing.\");\n }\n\n if (peginTx.outs.length !== expectedOutputCount) {\n throw new Error(\n `PegIn tx has ${peginTx.outs.length} output(s), expected exactly ` +\n `${expectedOutputCount} for vaultCoreVersion ${version} (vault + ` +\n `depositor claim${anchor ? \" + P2A anchor\" : \"\"}).`,\n );\n }\n\n const requestedAmount = params.prePeginParams.pegInAmounts[params.htlcVout];\n if (result.vaultValue !== requestedAmount) {\n throw new Error(\n `PegIn vault output value ${result.vaultValue} does not match the ` +\n `requested peg-in amount ${requestedAmount} for HTLC ` +\n `${params.htlcVout}; refusing to sign a vault that locks a ` +\n `different amount than requested.`,\n );\n }\n\n const encodedVaultOut = peginTx.outs[0];\n if (BigInt(encodedVaultOut.value) !== result.vaultValue) {\n throw new Error(\n `Encoded PegIn vault output value ${encodedVaultOut.value} does not ` +\n `match the WASM-reported vaultValue ${result.vaultValue}.`,\n );\n }\n const expectedVaultScript = deriveExpectedPeginPayoutScriptPubKey(\n params.prePeginParams,\n params.timelockPegin,\n ).toString(\"hex\");\n if (\n stripHexPrefix(result.vaultScriptPubKey).toLowerCase() !==\n expectedVaultScript\n ) {\n throw new Error(\n `WASM-reported PegIn vaultScriptPubKey ${result.vaultScriptPubKey} ` +\n `does not match the independently derived payout scriptPubKey ` +\n `${expectedVaultScript}.`,\n );\n }\n const encodedVaultScript = encodedVaultOut.script.toString(\"hex\");\n if (encodedVaultScript !== expectedVaultScript) {\n throw new Error(\n `Encoded PegIn vault output scriptPubKey ${encodedVaultScript} does ` +\n `not match the independently derived payout scriptPubKey ` +\n `${expectedVaultScript}.`,\n );\n }\n\n const encodedTxid = peginTx.getId();\n if (encodedTxid !== stripHexPrefix(result.txid).toLowerCase()) {\n throw new Error(\n `Encoded PegIn txid ${encodedTxid} does not match the WASM-reported ` +\n `txid ${result.txid}.`,\n );\n }\n\n // Depositor-claim output (vout 1): a doctored binary could burn the claim\n // into miner fee or redirect it while the vault, anchor, and txid binds\n // all pass. Bind its value to the independently recomputed minimum claim\n // value (the same WASM-vs-WASM identity `assertWasmPeginSizing` uses) and\n // its script to a fully JS-derived expectation. With the vault bind, the\n // output count, and value conservation against the validated HTLC input,\n // the implied fee is bound too.\n const expectedClaimValue = await computeMinClaimValue(\n version,\n params.prePeginParams.numLocalChallengers,\n params.prePeginParams.universalChallengerPubkeys.length,\n params.prePeginParams.councilQuorum,\n params.prePeginParams.councilSize,\n params.prePeginParams.feeRate,\n );\n const encodedClaimOut = peginTx.outs[PEGIN_DEPOSITOR_CLAIM_VOUT];\n if (BigInt(encodedClaimOut.value) !== expectedClaimValue) {\n throw new Error(\n `Encoded PegIn depositor-claim output value ${encodedClaimOut.value} ` +\n `does not match the independently computed claim value ` +\n `${expectedClaimValue} for vaultCoreVersion ${version}.`,\n );\n }\n\n const expectedClaimScript = deriveDepositorClaimScriptPubKey(\n params.prePeginParams.depositorPubkey,\n );\n if (!encodedClaimOut.script.equals(expectedClaimScript)) {\n throw new Error(\n `Encoded PegIn depositor-claim output scriptPubKey ` +\n `${encodedClaimOut.script.toString(\"hex\")} does not pay to the ` +\n `depositor's claim script (expected ` +\n `${expectedClaimScript.toString(\"hex\")}).`,\n );\n }\n}\n","/**\n * Payout Script Generator Primitive\n *\n * This module provides pure functions for generating payout scripts and taproot information\n * by wrapping the WASM implementation from @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * The payout script is used for signing payout transactions in the vault system.\n * It defines the spending conditions for the vault output, enabling the depositor\n * to authorize payouts during the peg-in flow (Step 3).\n *\n * @remarks\n * This is a low-level primitive. For most use cases, prefer using {@link buildPayoutPsbt}\n * which handles script creation internally. For high-level wallet orchestration, use\n * PayoutManager from the managers module.\n *\n * @see {@link buildPayoutPsbt} - Higher-level function that uses this internally\n *\n * @module primitives/scripts/payout\n */\n\nimport {\n createPayoutConnector,\n type Network,\n} from \"../../wasm\";\n\n/**\n * Parameters for creating a payout script.\n *\n * These parameters define the participants in a vault and are used to generate\n * the taproot script that controls how funds can be spent from the vault.\n */\nexport interface PayoutScriptParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n * payout connector the WASM derives.\n */\n vaultCoreVersion: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * This is the user depositing BTC into the vault. The depositor must sign\n * payout transactions to authorize fund distribution.\n */\n depositor: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * The service provider managing vault operations. Also referred to as\n * \"claimer\" in the WASM layer.\n */\n vaultProvider: string;\n\n /**\n * Array of vault keeper BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * Vault keepers participate in vault operations and script spending conditions.\n */\n vaultKeepers: string[];\n\n /**\n * Array of universal challenger BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * These parties can challenge the vault under certain conditions.\n */\n universalChallengers: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Bitcoin network for script generation.\n *\n * Must match the network used for all other vault operations to ensure\n * address encoding compatibility.\n */\n network: Network;\n}\n\n/**\n * Result of creating a payout script.\n *\n * Contains all the taproot-related data needed for constructing and signing\n * payout transactions from the vault.\n */\nexport interface PayoutScriptResult {\n /**\n * The payout script hex used in taproot script path spending.\n *\n * This is the raw script bytes that define the spending conditions,\n * encoded as a hexadecimal string. Used when constructing the\n * tapLeafScript for PSBT signing.\n */\n payoutScript: string;\n\n /**\n * The taproot script hash (leaf hash) for the payout script.\n *\n * This is the tagged hash of the script used in taproot tree construction.\n * Required for computing the control block during script path spending.\n */\n taprootScriptHash: string;\n\n /**\n * The full scriptPubKey for the vault output address.\n *\n * This is the complete output script (OP_1 <32-byte-key>) that should be\n * used when creating the vault output in a peg-in transaction.\n */\n scriptPubKey: string;\n\n /**\n * The vault Bitcoin address derived from the script.\n *\n * A human-readable bech32m address (bc1p... for mainnet, tb1p... for testnet/signet)\n * that can be used to receive funds into the vault.\n */\n address: string;\n\n /**\n * Serialized control block for Taproot script path spend (hex encoded).\n *\n * Computed by the Rust WASM PeginPayoutConnector. Used directly in\n * tapLeafScript when building payout PSBTs.\n */\n payoutControlBlock: string;\n}\n\n/**\n * Create payout script and taproot information using WASM.\n *\n * This is a pure function that wraps the Rust WASM implementation.\n * The payout connector generates the necessary taproot scripts and information\n * required for signing payout transactions.\n *\n * @remarks\n * The generated script encodes spending conditions that require signatures from\n * the depositor and vault provider (or liquidators in challenge scenarios).\n * This script is used internally by {@link buildPayoutPsbt}.\n *\n * @param params - Payout script parameters defining vault participants and network\n * @returns Payout script and taproot information for PSBT construction\n *\n * @see {@link buildPayoutPsbt} - Use this for building complete payout PSBTs\n */\nexport async function createPayoutScript(\n params: PayoutScriptParams,\n): Promise<PayoutScriptResult> {\n // Call the WASM wrapper with the correct parameter structure\n const connector = await createPayoutConnector(\n {\n txGraphVersion: params.vaultCoreVersion,\n depositor: params.depositor,\n vaultProvider: params.vaultProvider,\n vaultKeepers: params.vaultKeepers,\n universalChallengers: params.universalChallengers,\n timelockPegin: params.timelockPegin,\n },\n params.network,\n );\n\n return {\n payoutScript: connector.payoutScript,\n taprootScriptHash: connector.taprootScriptHash,\n scriptPubKey: connector.scriptPubKey,\n address: connector.address,\n payoutControlBlock: connector.payoutControlBlock,\n };\n}\n","/**\n * BIP-341 Taproot primitives shared by the PSBT builders and verifiers.\n *\n * Everything here is a byte-level restatement of BIP-341; the spec is the\n * source of truth for each constant and each hash preimage.\n *\n * @see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\n * @module tbv/core/primitives/utils/taproot\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { crypto as bcrypto } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\n// Bitcoin CompactSize (varint) prefix markers — values fixed by the protocol.\n// https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\nconst COMPACT_SIZE_UINT16_PREFIX = 0xfd; // value in [0xfd, 0xffff] → 0xfd + uint16 LE\nconst COMPACT_SIZE_UINT32_PREFIX = 0xfe; // value in [0x10000, 0xffffffff] → 0xfe + uint32 LE\nconst COMPACT_SIZE_UINT16_MAX = 0xffff;\nconst COMPACT_SIZE_UINT32_MAX = 0xffffffff;\n\n/** BIP-341 tag for the TapLeaf hash. */\nconst TAPLEAF_TAG = \"TapLeaf\";\n/** BIP-341 tag for an internal merkle node of the taptree. */\nconst TAPBRANCH_TAG = \"TapBranch\";\n/** BIP-341 tag for the output-key tweak. */\nconst TAPTWEAK_TAG = \"TapTweak\";\n\n/** BIP-341 control block prefix: 1 leaf-version/parity byte + 32-byte internal key. */\nconst CONTROL_BLOCK_PREFIX_LEN = 33;\n/** Each merkle-path element in a control block is a 32-byte node hash. */\nconst CONTROL_BLOCK_NODE_LEN = 32;\n/** BIP-341 caps the taptree at 128 levels, so the path holds at most 128 nodes. */\nconst CONTROL_BLOCK_MAX_NODES = 128;\n/** Low bit of control-block byte 0 carries the output key's y-parity. */\nconst CONTROL_BLOCK_PARITY_MASK = 0x01;\n/** Remaining bits of control-block byte 0 carry the leaf version. */\nconst CONTROL_BLOCK_LEAF_VERSION_MASK = 0xfe;\n\n/** A P2TR scriptPubKey is `OP_1 <32-byte push>` followed by the output key. */\nconst P2TR_SCRIPT_PUBKEY_PREFIX = Buffer.from([0x51, 0x20]);\n\n/**\n * Encode a length as a Bitcoin CompactSize (varint). Tapscript leaf scripts can\n * exceed 252 bytes (WOTS scripts), so the multi-byte forms are required, not\n * just the single-byte fast path.\n */\nfunction encodeCompactSize(n: number): Buffer {\n if (n < COMPACT_SIZE_UINT16_PREFIX) {\n return Buffer.from([n]);\n }\n if (n <= COMPACT_SIZE_UINT16_MAX) {\n const value = Buffer.alloc(2); // uint16, little-endian\n value.writeUInt16LE(n);\n return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT16_PREFIX]), value]);\n }\n if (n <= COMPACT_SIZE_UINT32_MAX) {\n const value = Buffer.alloc(4); // uint32, little-endian\n value.writeUInt32LE(n);\n return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT32_PREFIX]), value]);\n }\n throw new Error(`Script too large to encode as CompactSize: ${n} bytes`);\n}\n\n/**\n * Compute the BIP-341 TapLeaf hash for a tapscript leaf:\n * `tagged_hash(\"TapLeaf\", leaf_version || compact_size(script) || script)`.\n */\nexport function computeTapLeafHash(\n leafVersion: number,\n script: Uint8Array,\n): Buffer {\n const preimage = Buffer.concat([\n Buffer.from([leafVersion]),\n encodeCompactSize(script.length),\n Buffer.from(script),\n ]);\n return bcrypto.taggedHash(TAPLEAF_TAG, preimage);\n}\n\nexport interface TaprootScriptPathBinding {\n /** Tapscript leaf version, e.g. `0xc0`. */\n leafVersion: number;\n /** The tapscript leaf being spent. */\n script: Uint8Array;\n /** BIP-341 control block: version/parity byte, internal key, merkle path. */\n controlBlock: Uint8Array;\n}\n\n/**\n * Recompute the P2TR scriptPubKey that a `(leafVersion, script, controlBlock)`\n * triple can spend, by walking the control block's merkle path to the taptree\n * root and tweaking the control block's internal key with it (BIP-341\n * \"Script validation rules\").\n *\n * Comparing the result against a real previous output is what proves the triple\n * belongs to that output rather than to some other taptree.\n *\n * @throws If the control block is malformed, the leaf version disagrees with\n * the control block, the tweak is off-curve, or the recovered output key's\n * parity contradicts the control block.\n */\nexport function computeTaprootScriptPubKey(\n binding: TaprootScriptPathBinding,\n): Buffer {\n const { leafVersion, script, controlBlock } = binding;\n\n const pathLen = controlBlock.length - CONTROL_BLOCK_PREFIX_LEN;\n if (\n pathLen < 0 ||\n pathLen % CONTROL_BLOCK_NODE_LEN !== 0 ||\n pathLen / CONTROL_BLOCK_NODE_LEN > CONTROL_BLOCK_MAX_NODES\n ) {\n throw new Error(\n `Malformed Taproot control block: length ${controlBlock.length} must be ` +\n `${CONTROL_BLOCK_PREFIX_LEN} + 32*m with 0 <= m <= ${CONTROL_BLOCK_MAX_NODES}`,\n );\n }\n\n const controlLeafVersion = controlBlock[0] & CONTROL_BLOCK_LEAF_VERSION_MASK;\n if (controlLeafVersion !== leafVersion) {\n throw new Error(\n `Taproot control block leaf version 0x${controlLeafVersion.toString(16)} ` +\n `does not match the tapLeafScript leaf version 0x${leafVersion.toString(16)}`,\n );\n }\n\n const internalKey = Buffer.from(\n controlBlock.subarray(1, CONTROL_BLOCK_PREFIX_LEN),\n );\n\n // Fold the merkle path into the taptree root; siblings are hashed in\n // lexicographic order (BIP-341).\n let node = computeTapLeafHash(leafVersion, script);\n for (\n let offset = CONTROL_BLOCK_PREFIX_LEN;\n offset < controlBlock.length;\n offset += CONTROL_BLOCK_NODE_LEN\n ) {\n const sibling = Buffer.from(\n controlBlock.subarray(offset, offset + CONTROL_BLOCK_NODE_LEN),\n );\n node = bcrypto.taggedHash(\n TAPBRANCH_TAG,\n Buffer.compare(node, sibling) <= 0\n ? Buffer.concat([node, sibling])\n : Buffer.concat([sibling, node]),\n );\n }\n\n const tweak = bcrypto.taggedHash(\n TAPTWEAK_TAG,\n Buffer.concat([internalKey, node]),\n );\n const tweaked = ecc.xOnlyPointAddTweak(internalKey, tweak);\n if (tweaked === null) {\n throw new Error(\n \"Taproot control block does not yield a valid output key (tweak is off-curve)\",\n );\n }\n const expectedParity = controlBlock[0] & CONTROL_BLOCK_PARITY_MASK;\n if (tweaked.parity !== expectedParity) {\n throw new Error(\n `Taproot output key parity ${tweaked.parity} contradicts the control ` +\n `block's parity bit ${expectedParity}`,\n );\n }\n\n return Buffer.concat([\n P2TR_SCRIPT_PUBKEY_PREFIX,\n Buffer.from(tweaked.xOnlyPubkey),\n ]);\n}\n","/**\n * Bands a VP-built payout's implicit fee before the depositor pre-signs it.\n * Ceiling (ours, #2105) blocks a VP deflating outputs and burning the\n * difference as miner fee; floor (vault-wasm `computePayoutFeeFloor`) rejects\n * a fee no legitimate VP model produces and that may not relay at redemption.\n *\n * Caller (`buildPayoutPsbt`) must have run `assertPayoutFeeBandDomain` first,\n * and `implicitFeeSats` must be `inputs − outputs` over verified prevouts.\n * Band non-emptiness is proven by the corner sweep in the tests, not by a\n * closed-form argument — slack is not monotone at small rosters.\n *\n * @module primitives/psbt/assertPayoutFeeBand\n */\n\nimport { computePayoutFeeFloor } from \"../../wasm\";\n\n/**\n * Floor-model limit, NOT a protocol bound: vault-wasm seeds dummy keys from a\n * u8, so 257 keepers trap in `VaultKeepers::new` (measured). The contract sets\n * no keeper maximum — a larger roster is an upstream vault-wasm fix.\n */\nconst MAX_FLOOR_MODEL_KEEPERS = 256;\n\n/**\n * Mirrors `vault-contracts-aave-v4 ProtocolParams.sol:22`\n * MAX_UNIVERSAL_CHALLENGERS_SIZE. Safe to copy\n * rather than read: it is a bytecode `constant`, not a governance param, so it\n * cannot move without a redeploy — re-verify it on the next one.\n */\nconst MAX_UNIVERSAL_CHALLENGERS = 1500;\n\n/** Floor-model limit, same u8 dummy-seeding cause as the keeper cap (measured). */\nconst MAX_FLOOR_MODEL_COUNCIL_SIZE = 256;\n\n/**\n * Coarse garbage-input guard, far above the contract's own cap\n * (`vault-contracts-aave-v4 ProtocolParams.sol:44` MAX_FEE_RATE_SAT_VB = 1000).\n */\nconst MAX_SANE_FEE_RATE_SAT_PER_VB = 0xffffffffn;\n\n/**\n * Ceiling vsize model `BASE + PER_PARTICIPANT * (N + M)` — ~13%+ headroom over\n * the exact vsize the VP pays. Values adopted per #2105; kept at parity with\n * the Ledger app's bound so device-signable and SDK-acceptable stay aligned.\n */\nconst MAX_PAYOUT_VSIZE_BASE = 500;\nconst MAX_PAYOUT_VSIZE_PER_PARTICIPANT = 55;\n\n/** P2TR length the model assumes; a longer pinned outs[0] extends the ceiling linearly. */\nconst PAYOUT_BOUND_ASSUMED_SCRIPT_LEN = 34;\n\n/** Shape/rate inputs the fee band is evaluated over. */\nexport interface PayoutFeeBandParams {\n /** Vault core (tx-graph) version — selects the floor's pinned model set. */\n vaultCoreVersion: number;\n /** Vault keeper count (N). */\n numVaultKeepers: number;\n /** Universal challenger count (M). */\n numUniversalChallengers: number;\n /** Security council size from the locked offchain params version. */\n councilSize: number;\n /** Version-locked tx-graph fee rate (sat/vB); anchors both band ends. */\n protocolFeeRate: bigint;\n}\n\n/**\n * Validate the fee-band inputs lie in the accepted domain. Bounds differ in\n * KIND per role — see each constant.\n *\n * @throws If the rate, a participant count, or `councilSize` is out of range\n */\nexport function assertPayoutFeeBandDomain(params: PayoutFeeBandParams): void {\n if (\n typeof params.protocolFeeRate !== \"bigint\" ||\n params.protocolFeeRate <= 0n ||\n params.protocolFeeRate > MAX_SANE_FEE_RATE_SAT_PER_VB\n ) {\n throw new Error(\n `protocolFeeRate must be in [1, ${MAX_SANE_FEE_RATE_SAT_PER_VB}] sat/vB, ` +\n `got ${params.protocolFeeRate}`,\n );\n }\n // Lower bound 1: btc-vault's VaultKeepers::new / UniversalChallengers::new\n // reject an empty set (crates/vault/src/lib.rs).\n for (const [role, count, max, reason] of [\n [\n \"keepers\",\n params.numVaultKeepers,\n MAX_FLOOR_MODEL_KEEPERS,\n \"fee-floor model limit\",\n ],\n [\n \"challengers\",\n params.numUniversalChallengers,\n MAX_UNIVERSAL_CHALLENGERS,\n \"protocol maximum\",\n ],\n ] as const) {\n if (!Number.isInteger(count) || count < 1 || count > max) {\n // Only an over-max count is attributable to the bound's reason.\n throw new Error(\n `Participant count for ${role} (${count}) is outside the supported ` +\n `range [1, ${max}]${count > max ? ` (${reason})` : \"\"}.`,\n );\n }\n }\n // Unvalidated at the WASM boundary: a non-integer truncates at the u32 ABI\n // and 0 is silently promoted to a 1-member council by the estimator.\n if (\n !Number.isInteger(params.councilSize) ||\n params.councilSize < 1 ||\n params.councilSize > MAX_FLOOR_MODEL_COUNCIL_SIZE\n ) {\n throw new Error(\n `councilSize must be an integer in ` +\n `[1, ${MAX_FLOOR_MODEL_COUNCIL_SIZE}], got ${params.councilSize}`,\n );\n }\n}\n\n/**\n * Assert `floor <= implicitFeeSats <= ceiling`. `out1Len` is `undefined` for\n * the 2-output (no-commission) layouts.\n *\n * @throws If the fee is below the floor or above the ceiling\n */\nexport async function assertPayoutFeeInBand(\n params: PayoutFeeBandParams,\n measured: {\n implicitFeeSats: number;\n out0Len: number;\n out1Len: number | undefined;\n },\n): Promise<void> {\n const { implicitFeeSats, out0Len, out1Len } = measured;\n const numParticipants =\n params.numVaultKeepers + params.numUniversalChallengers;\n const implicitFee = BigInt(implicitFeeSats);\n\n // Ceiling first: synchronous, no WASM round-trip. Only outs[0] widens it —\n // including the VP-controlled out1Len would hand a padded commission script\n // up to 94 vB x rate of extra burnable band.\n const scriptExcess = Math.max(0, out0Len - PAYOUT_BOUND_ASSUMED_SCRIPT_LEN);\n const maxPayoutVsize =\n MAX_PAYOUT_VSIZE_BASE +\n MAX_PAYOUT_VSIZE_PER_PARTICIPANT * numParticipants +\n scriptExcess;\n const maxFeeSats = params.protocolFeeRate * BigInt(maxPayoutVsize);\n if (implicitFee > maxFeeSats) {\n throw new Error(\n `Payout implicit fee ${implicitFeeSats} sats exceeds the safety cap ` +\n `of ${maxFeeSats} sats (${params.protocolFeeRate} sat/vB x ` +\n `${maxPayoutVsize} vB for ${numParticipants} ` +\n `participants); refusing to sign payout.`,\n );\n }\n\n // Local challengers are exactly the keeper count for every claimer role\n // (btc-vault graph.rs derive_challengers).\n const minFeeSats = await computePayoutFeeFloor(\n params.vaultCoreVersion,\n params.numVaultKeepers,\n params.numUniversalChallengers,\n params.numVaultKeepers,\n params.councilSize,\n out0Len,\n out1Len,\n params.protocolFeeRate,\n );\n if (implicitFee < minFeeSats) {\n throw new Error(\n `Payout implicit fee ${implicitFeeSats} sats is below the floor of ` +\n `${minFeeSats} sats (the smallest fee any known vault-provider ` +\n `build produces for this shape); refusing to sign payout.`,\n );\n }\n}\n","/**\n * Standard-scriptPubKey validation for registry-supplied payout destinations.\n *\n * RFC-006 lets an operator register an arbitrary payout scriptPubKey, so the\n * bytes we pin a `PayoutTx` output against are now chosen by that operator\n * rather than derived by us. Pinning an output to them without checking the\n * shape would let a keeper that registered a garbage or unspendable script get\n * the depositor to pre-sign a payout nobody can ever claim.\n *\n * This bound is ours alone — it is deliberately NOT parity with `vaultd`.\n * `vaultd` uses registry scripts verbatim at graph-build time and documents\n * why: applying a predicate the contract does not apply would block every\n * peg-in for an operator the instant the two disagreed. The registry itself\n * only bounds length to 1..128 bytes, and the registration CLI has an\n * `--allow-non-standard` escape hatch.\n *\n * We hold the stricter line because we occupy a different position: we are the\n * party being asked to *pre-sign* against these bytes, and a depositor\n * signature pinned to a provably unspendable output is not recoverable. The\n * bound is also load-bearing for the payout fee band — capping a standard\n * output at 34 bytes is what lets `assertPayoutFeeBand` reason about `outs[1]`\n * without a separate length cap.\n *\n * The deliberate consequence: an operator that registers a non-standard script\n * via `--allow-non-standard` can have its graphs built by `vaultd` and still be\n * refused here, blocking deposits against that operator in this dApp. That is\n * fail-closed by choice — the alternative is pre-signing into a destination we\n * cannot verify is spendable.\n *\n * @module primitives/psbt/standardPayoutScript\n */\n\nimport { stripHexPrefix } from \"../utils/bitcoin\";\n\n/** `OP_0 <20>` — P2WPKH. */\nconst P2WPKH_LEN = 22;\n/** `OP_0 <32>` — P2WSH. */\nconst P2WSH_LEN = 34;\n/** `OP_1 <32>` — P2TR. */\nconst P2TR_LEN = 34;\n/** `OP_DUP OP_HASH160 <20> OP_EQUALVERIFY OP_CHECKSIG` — P2PKH. */\nconst P2PKH_LEN = 25;\n/** `OP_HASH160 <20> OP_EQUAL` — P2SH. */\nconst P2SH_LEN = 23;\n\nfunction isP2wpkh(s: string): boolean {\n return s.length === P2WPKH_LEN * 2 && s.startsWith(\"0014\");\n}\nfunction isP2wsh(s: string): boolean {\n return s.length === P2WSH_LEN * 2 && s.startsWith(\"0020\");\n}\nfunction isP2tr(s: string): boolean {\n return s.length === P2TR_LEN * 2 && s.startsWith(\"5120\");\n}\nfunction isP2pkh(s: string): boolean {\n return (\n s.length === P2PKH_LEN * 2 && s.startsWith(\"76a914\") && s.endsWith(\"88ac\")\n );\n}\nfunction isP2sh(s: string): boolean {\n return s.length === P2SH_LEN * 2 && s.startsWith(\"a914\") && s.endsWith(\"87\");\n}\n\n/**\n * Assert a registry-supplied scriptPubKey is a standard, spendable output type\n * (P2TR, P2WPKH, P2WSH, P2PKH, or P2SH).\n *\n * Rejects empty scripts, `OP_RETURN` (provably unspendable), and anything else\n * that is not one of the five standard forms. `label` names the source in the\n * error, e.g. `vault keeper payout script (admin=0x…)`.\n */\nexport function assertStandardPayoutScript(\n scriptHex: string,\n label: string,\n): void {\n const script = stripHexPrefix(scriptHex).toLowerCase();\n\n if (script.length === 0) {\n throw new Error(`${label} is empty`);\n }\n if (!/^[0-9a-f]+$/.test(script) || script.length % 2 !== 0) {\n throw new Error(`${label} is not valid hex`);\n }\n if (script.startsWith(\"6a\")) {\n throw new Error(\n `${label} is an OP_RETURN output, which is provably unspendable`,\n );\n }\n\n if (\n !isP2tr(script) &&\n !isP2wpkh(script) &&\n !isP2wsh(script) &&\n !isP2pkh(script) &&\n !isP2sh(script)\n ) {\n throw new Error(\n `${label} is not a standard scriptPubKey ` +\n `(expected P2TR, P2WPKH, P2WSH, P2PKH, or P2SH; got ${script.length / 2} bytes: ${script})`,\n );\n }\n}\n","/**\n * Payout PSBT Builder Primitives\n *\n * This module provides pure functions for building unsigned payout PSBTs and extracting\n * Schnorr signatures from signed PSBTs. It uses WASM-generated scripts from the payout\n * connector and bitcoinjs-lib for PSBT construction.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @module primitives/psbt/payout\n */\n\nimport { Psbt, Transaction, type TxInput, type TxOutput } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\nimport {\n getAssertPayoutScriptInfo,\n tapInternalPubkey,\n type Network,\n} from \"../../wasm\";\nimport { deriveLocalChallengers } from \"../challengers\";\nimport { createPayoutScript } from \"../scripts/payout\";\nimport {\n TAPSCRIPT_LEAF_VERSION,\n deriveBip86ScriptPubKeyHex,\n hexToUint8Array,\n isValidHex,\n stripHexPrefix,\n uint8ArrayToHex,\n} from \"../utils/bitcoin\";\nimport { computeTaprootScriptPubKey } from \"../utils/taproot\";\nimport {\n assertPayoutFeeBandDomain,\n assertPayoutFeeInBand,\n} from \"./assertPayoutFeeBand\";\nimport {\n ASSERT_PAYOUT_OUTPUT_INDEX,\n BPS_DENOMINATOR,\n DEPOSITOR_PAYOUT_INPUT_COUNT,\n MAX_PAYOUT_SCRIPT_LEN,\n MAX_VP_COMMISSION_BPS_EXCLUSIVE,\n NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n PAYOUT_ANCHOR_DUST_SATS,\n PAYOUT_TX_LOCKTIME,\n PAYOUT_TX_VERSION,\n PEGIN_VAULT_OUTPUT_INDEX,\n VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n} from \"./constants\";\nimport { assertStandardPayoutScript } from \"./standardPayoutScript\";\n\n/**\n * Number of items in a Taproot script-path spend witness stack for a\n * single-signature script: [signature, script, controlBlock].\n *\n * The current payout script requires exactly one depositor signature. If the\n * protocol evolves to require multiple signatures in the payout script, this\n * invariant and the finalized-PSBT extraction path must be revisited because\n * the first witness item would no longer necessarily be the depositor's.\n */\nconst TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE = 3;\n\n/**\n * Parameters for building an unsigned Payout PSBT\n *\n * Payout ends two of the peg-out paths; see {@link buildPayoutPsbt} for all of\n * them. Input 1 references the Assert transaction.\n */\nexport interface PayoutParams {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n * payout connector scripts are derived.\n */\n vaultCoreVersion: number;\n\n /**\n * Payout transaction hex (unsigned)\n * This is the transaction that needs to be signed by the depositor\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex\n * Payout input 1 references Assert output 0\n */\n assertTxHex: string;\n\n /**\n * Peg-in transaction hex\n * This transaction created the vault output that we're spending\n */\n peginTxHex: string;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix)\n */\n depositorBtcPubkey: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex)\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex)\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex)\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output (btc-vault `timelock_pegin`);\n * payout input 0's sequence.\n */\n timelockPegin: number;\n\n /**\n * CSV timelock in blocks on the Assert:0 payout leaf (btc-vault\n * `timelock_assert`); payout input 1's sequence.\n */\n timelockAssert: number;\n\n /**\n * Bitcoin network\n */\n network: Network;\n\n /**\n * Claimer's x-only BTC public key (64-char hex, no prefix). Drives role\n * inference (VP / depositor-as-claimer / VK-claimer) inside `buildPayoutPsbt`.\n */\n claimerBtcPubkey: string;\n\n /**\n * On-chain registered depositor payout scriptPubKey (hex, 0x optional).\n * Expected outs[0].script for VP- and depositor-claimer roles; unused for\n * VK-claimer (its outs[0].script is derived from `claimerBtcPubkey`).\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * VP commission in basis points (`BTCVaultRegistry.vaultProviderCommissionBps`).\n * Caps the VP-claimer outs[1].value. The protocol minimum is enforced\n * upstream; here only `0 <= bps < 10_000` is checked, for safe cap math.\n */\n commissionBps: number;\n\n /**\n * Tx-graph fee rate (sat/vB) the graph was built with — the version-locked\n * `offchainParams.feeRate` at the vault's stamped `offchainParamsVersion`,\n * NOT a live read. Anchors both ends of the fee band.\n */\n protocolFeeRate: bigint;\n\n /**\n * Security council member x-only public keys (hex) from the locked offchain\n * params version — `getOffchainParamsByVersion(...).securityCouncilKeys`.\n * The council occupies the last leaf of the Assert:0 taptree\n * (btc-vault `crates/vault/src/connectors/assert_payout_nopayout_council.rs`),\n * so the keys are needed to rebuild input 1's payout leaf, and the count\n * feeds the fee-band domain.\n */\n councilMembers: string[];\n\n /**\n * M-of-N council quorum from the locked offchain params version —\n * `getOffchainParamsByVersion(...).councilQuorum`. Shapes the council leaf's\n * multisig script, and with it the Assert:0 taptree root.\n */\n councilQuorum: number;\n\n /**\n * RFC-006. Expected `outs[0].script` per vault-keeper claimer, keyed by\n * lowercased x-only **operation** pubkey (no `0x`), resolved from\n * `ApplicationRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n * `appKeeperKeyEpoch`.\n *\n * Every VK claimer must be present: a claimer missing from the map is an\n * error rather than a cue to derive BIP-86, because a gap means resolution\n * was incomplete and we do not know what that keeper registered.\n *\n * Each entry accepts either that registered script or the BIP-86 default of\n * the same bonded key, so graphs built before btc-vault#2440 remain signable\n * — see {@link acceptedPayoutScriptHexes} for why that is required and when\n * it can be dropped.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n\n /**\n * RFC-006. Expected `outs[1].script` for the VP-claimer commission output,\n * from `BTCVaultRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n * `vpKeyEpoch`. The BIP-86 default of the bonded VP key is accepted alongside\n * it — see {@link acceptedPayoutScriptHexes}.\n */\n vpCommissionScriptPubKey: string;\n}\n\n/**\n * Result of building an unsigned payout PSBT\n */\nexport interface PayoutPsbtResult {\n /**\n * Unsigned PSBT hex ready for signing\n */\n psbtHex: string;\n}\n\n/**\n * Build unsigned Payout PSBT for depositor to sign.\n *\n * Payout ends two of the peg-out paths (btc-vault\n * `crates/vault/docs/btc-transactions-spec.md`):\n * - Happy path: Claim -> Assert -> Payout.\n * - Challenge path, claimer wins: Claim -> Assert -> ChallengeAssert ->\n * WronglyChallenged -> Payout.\n * - Challenge path, challenger wins: Claim -> Assert -> ChallengeAssert -> NoPayout.\n * - Emergency path: the Security Council spends Assert:0 via CouncilNoPayout.\n *\n * So a raised challenge does not remove Payout; only NoPayout (challenger wins)\n * or CouncilNoPayout (council emergency) blocks it.\n * Payout references the Assert tx and needs its timelock matured.\n *\n * Payout transactions have the following structure:\n * - Input 0: from PeginTx output0 (signed by depositor)\n * - Input 1: from Assert output0 (NOT signed by depositor)\n *\n * Both inputs carry their taproot script-path leaf. Input 1's is not signed\n * here — it is what a hardware signer reads to display the payout terms.\n *\n * @param params - Payout parameters\n * @returns Unsigned PSBT ready for depositor to sign\n *\n * @throws If payout transaction does not have exactly 2 inputs\n * @throws If input 0 does not spend PegIn:0 (vault UTXO)\n * @throws If input 1 does not spend Assert:0 (proof output)\n * @throws If previous output is not found for either input\n * @throws If sum of output values exceeds sum of input values (invalid tx)\n * @throws If the implicit fee (inputs − outputs) is outside the fee band —\n * below the floor or above the fee-band ceiling (see\n * {@link assertPayoutFeeInBand})\n * @throws If `protocolFeeRate`, a participant count, or the council size is\n * outside the accepted input domain (see {@link assertPayoutFeeBandDomain})\n * @throws If a non-anchor scriptPubKey length is outside `[1,\n * {@link MAX_PAYOUT_SCRIPT_LEN}]`\n * @throws If `claimerBtcPubkey` is not VP, depositor, or a registered VK\n * @throws If payout output count, outs[0] script, outs[last] anchor value, or\n * (VP-claimer) outs[1] commission cap do not match the protocol layout\n * @throws If `commissionBps` is not a non-negative integer below 10_000\n * @throws If the locally rebuilt Assert:0 payout leaf does not bind to the\n * Assert output input 1 spends\n */\nexport async function buildPayoutPsbt(\n params: PayoutParams,\n): Promise<PayoutPsbtResult> {\n const feeBandParams = {\n vaultCoreVersion: params.vaultCoreVersion,\n numVaultKeepers: params.vaultKeeperBtcPubkeys.length,\n numUniversalChallengers: params.universalChallengerBtcPubkeys.length,\n councilSize: params.councilMembers.length,\n protocolFeeRate: params.protocolFeeRate,\n };\n assertPayoutFeeBandDomain(feeBandParams);\n\n const payoutTx = Transaction.fromHex(stripHexPrefix(params.payoutTxHex));\n const peginTx = Transaction.fromHex(stripHexPrefix(params.peginTxHex));\n const assertTx = Transaction.fromHex(stripHexPrefix(params.assertTxHex));\n\n if (payoutTx.ins.length !== DEPOSITOR_PAYOUT_INPUT_COUNT) {\n throw new Error(\n `Payout transaction must have exactly ${DEPOSITOR_PAYOUT_INPUT_COUNT} ` +\n `inputs, got ${payoutTx.ins.length}`,\n );\n }\n // btc-vault builds every payout with these literals (payout.rs:154-155);\n // the sighash commits to them, so refuse rather than sign a foreign shape.\n if (payoutTx.version !== PAYOUT_TX_VERSION) {\n throw new Error(\n `Payout transaction version ${payoutTx.version} must be ` +\n `${PAYOUT_TX_VERSION}; refusing to sign payout.`,\n );\n }\n if (payoutTx.locktime !== PAYOUT_TX_LOCKTIME) {\n throw new Error(\n `Payout transaction locktime ${payoutTx.locktime} must be ` +\n `${PAYOUT_TX_LOCKTIME}; refusing to sign payout.`,\n );\n }\n // Input sequences carry the CSV timelocks (payout.rs:108,119). Each is\n // checked after its own outpoint resolves so a wrong prevout reports itself.\n const peginPrevOut = requirePrevOut(\n payoutTx.ins[0],\n 0,\n peginTx,\n \"PegIn\",\n PEGIN_VAULT_OUTPUT_INDEX,\n );\n if (payoutTx.ins[0].sequence !== params.timelockPegin) {\n throw new Error(\n `Payout input 0 sequence ${payoutTx.ins[0].sequence} must equal the ` +\n `PegIn CSV timelock ${params.timelockPegin}; refusing to sign payout.`,\n );\n }\n\n const assertPrevOut = requirePrevOut(\n payoutTx.ins[1],\n 1,\n assertTx,\n \"Assert\",\n ASSERT_PAYOUT_OUTPUT_INDEX,\n );\n if (payoutTx.ins[1].sequence !== params.timelockAssert) {\n throw new Error(\n `Payout input 1 sequence ${payoutTx.ins[1].sequence} must equal the ` +\n `Assert CSV timelock ${params.timelockAssert}; refusing to sign payout.`,\n );\n }\n // Assert:0's value is deliberately NOT validated: the taproot sighash\n // commits to input 1's amount and outpoint, so a misstated value yields a\n // signature invalid against the real Assert tx — nothing to protect. (The\n // device pins the band [546, 546 + base_fee_rate*500] here, Q15.)\n\n // Per-role output validation — blocks an extra attacker output or value\n // routed into a non-payout slot. Returns the layout-trusted script lengths\n // the fee band consumes.\n const { out0Len, out1Len } = assertPayoutOutputLayout(\n payoutTx,\n peginPrevOut.value,\n params,\n );\n\n const inputValueSats = peginPrevOut.value + assertPrevOut.value;\n let outputValueSats = 0;\n for (const out of payoutTx.outs) outputValueSats += out.value;\n if (outputValueSats > inputValueSats) {\n throw new Error(\n `Payout outputs (${outputValueSats} sats) exceed inputs ` +\n `(${inputValueSats} sats); invalid transaction.`,\n );\n }\n const implicitFeeSats = inputValueSats - outputValueSats;\n await assertPayoutFeeInBand(feeBandParams, {\n implicitFeeSats,\n out0Len,\n out1Len,\n });\n\n // Only assembly needs the WASM-derived scripts — derive them after the\n // transaction has passed every validation gate.\n const payoutConnector = await createPayoutScript({\n vaultCoreVersion: params.vaultCoreVersion,\n depositor: params.depositorBtcPubkey,\n vaultProvider: params.vaultProviderBtcPubkey,\n vaultKeepers: params.vaultKeeperBtcPubkeys,\n universalChallengers: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n network: params.network,\n });\n\n const assertPayoutLeaf = await resolveAssertPayoutLeaf(params, assertPrevOut);\n\n return {\n psbtHex: assemblePayoutPsbt(\n payoutTx,\n peginPrevOut,\n assertPrevOut,\n payoutConnector,\n assertPayoutLeaf,\n ),\n };\n}\n\n/** The Assert:0 payout tapscript leaf, as attached to payout input 1. */\ninterface AssertPayoutLeaf {\n script: Uint8Array;\n controlBlock: Uint8Array;\n}\n\n/**\n * Rebuild the Assert:0 payout leaf from the vault's on-chain participant set\n * and assert it spends `assertPrevOut`.\n *\n * The depositor does not sign input 1, but the Ledger vault app reads this leaf\n * off the PSBT to display the payout terms\n * (`sign_psbt_validate.c::vault_read_payout_leaf_script`), so a leaf that\n * belonged to a different taptree would show the depositor terms the\n * transaction cannot actually enforce. Critical Path #3: derive, then bind to\n * the real previous output — never forward VP-supplied bytes.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nasync function resolveAssertPayoutLeaf(\n params: PayoutParams,\n assertPrevOut: TxOutput,\n): Promise<AssertPayoutLeaf> {\n const { payoutScript, payoutControlBlock } = await getAssertPayoutScriptInfo({\n txGraphVersion: params.vaultCoreVersion,\n claimer: stripHexPrefix(params.claimerBtcPubkey).toLowerCase(),\n localChallengers: deriveLocalChallengers({\n claimerBtcPubkey: params.claimerBtcPubkey,\n depositorBtcPubkey: params.depositorBtcPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n }),\n universalChallengers: params.universalChallengerBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n ),\n timelockAssert: params.timelockAssert,\n councilMembers: params.councilMembers.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n ),\n councilQuorum: params.councilQuorum,\n });\n\n const script = hexToUint8Array(payoutScript);\n const controlBlock = hexToUint8Array(payoutControlBlock);\n const boundScriptPubKey = computeTaprootScriptPubKey({\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script,\n controlBlock,\n });\n if (!assertPrevOut.script.equals(boundScriptPubKey)) {\n throw new Error(\n `Rebuilt Assert:0 payout leaf does not spend the Assert output being ` +\n `referenced: leaf binds to ${boundScriptPubKey.toString(\"hex\")}, ` +\n `Assert:${ASSERT_PAYOUT_OUTPUT_INDEX} pays ` +\n `${assertPrevOut.script.toString(\"hex\")}; refusing to sign payout.`,\n );\n }\n\n return { script, controlBlock };\n}\n\n/**\n * Verify `payoutTx.ins[inputIndex]` spends `parentTx:expectedVout` (both txid\n * AND vout — the vout is the input-side anchor that prevents a malicious VP\n * from binding the depositor's signature to a different output of the same\n * parent) and return the referenced previous output.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction requirePrevOut(\n input: TxInput,\n inputIndex: number,\n parentTx: Transaction,\n parentLabel: string,\n expectedVout: number,\n): TxOutput {\n const inputTxid = uint8ArrayToHex(\n new Uint8Array(input.hash).slice().reverse(),\n );\n const parentTxid = parentTx.getId();\n if (inputTxid !== parentTxid || input.index !== expectedVout) {\n throw new Error(\n `Input ${inputIndex} must spend ${parentLabel}:${expectedVout}. ` +\n `Expected ${parentTxid}:${expectedVout}, got ${inputTxid}:${input.index}`,\n );\n }\n const prevOut = parentTx.outs[input.index];\n if (!prevOut) {\n throw new Error(\n `Previous output not found for input ${inputIndex} ` +\n `(txid: ${inputTxid}, index: ${input.index})`,\n );\n }\n return prevOut;\n}\n\n/**\n * Assemble the unsigned PSBT from the validated payout transaction.\n *\n * IMPORTANT: For Taproot SIGHASH_DEFAULT (0x00), the sighash commits to ALL\n * inputs' prevouts, not just the one being signed — both inputs must be in\n * the PSBT so the wallet computes the sighash the VP expects.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assemblePayoutPsbt(\n payoutTx: Transaction,\n peginPrevOut: TxOutput,\n assertPrevOut: TxOutput,\n payoutConnector: { payoutScript: string; payoutControlBlock: string },\n assertPayoutLeaf: AssertPayoutLeaf,\n): string {\n const psbt = new Psbt();\n psbt.setVersion(payoutTx.version);\n psbt.setLocktime(payoutTx.locktime);\n\n const [input0, input1] = payoutTx.ins;\n\n // Input 0: depositor signs via Taproot script path — carries tapLeafScript.\n psbt.addInput({\n hash: input0.hash,\n index: input0.index,\n sequence: input0.sequence,\n witnessUtxo: {\n script: peginPrevOut.script,\n value: peginPrevOut.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(hexToUint8Array(payoutConnector.payoutScript)),\n controlBlock: Buffer.from(\n hexToUint8Array(payoutConnector.payoutControlBlock),\n ),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n // sighashType omitted - defaults to SIGHASH_DEFAULT (0x00) for Taproot\n });\n\n // Input 1: from Assert — not signed by the depositor, but the Ledger vault\n // app reads the payout leaf here to display the terms, so it must be present.\n psbt.addInput({\n hash: input1.hash,\n index: input1.index,\n sequence: input1.sequence,\n witnessUtxo: {\n script: assertPrevOut.script,\n value: assertPrevOut.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(assertPayoutLeaf.script),\n controlBlock: Buffer.from(assertPayoutLeaf.controlBlock),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n });\n\n for (const output of payoutTx.outs) {\n psbt.addOutput({\n script: output.script,\n value: output.value,\n });\n }\n\n return psbt.toHex();\n}\n\n/**\n * The scriptPubKeys a payout output may legitimately pay for `bondedPubkey`.\n *\n * ## What this is\n *\n * A deliberate dual-accept: both the operator's RFC-006 **registered** payout\n * script and the **BIP-86 default** of its bonded operation key are treated as\n * valid destinations for the same output.\n *\n * ## Why both forms exist\n *\n * A vault provider running a daemon that predates btc-vault#2440 computes\n * operator payout destinations itself, via BIP-86 over the bonded operation\n * key. From #2440 onward it reads them from the registry instead\n * (`getPayoutScriptAtEpoch`), so an operator can point payouts at cold custody.\n *\n * The two forms are not a transition state that resolves on its own. A payout\n * graph is built once, at BaBe Setup, and is **never rebuilt** — so a vault\n * whose graph was built before its network's #2440 upgrade pays BIP-86 for the\n * rest of its life. Accepting only the registered form makes every such vault\n * permanently unsignable: the depositor can never complete the deposit, and\n * the BTC is already committed by then. Confirmed on devnet 2026-08-04, where\n * a pre-upgrade vault failed exactly this way.\n *\n * ## Why this is not a weakening\n *\n * Both candidates are derived here from on-chain state — the registry read and\n * a local BIP-86 derivation over the bonded key. Neither is taken from the VP's\n * response, so a substituted or attacker-chosen script still fails.\n *\n * The only substitution this permits is between two destinations the *same\n * operator* already controls, which moves that operator's own funds between its\n * own addresses. It cannot redirect value to a third party, and it cannot touch\n * the depositor's output. For the VP commission the value cap\n * (`floor(peginValue × commissionBps / 10_000)`) still bounds depositor\n * exposure independently of where the commission goes. The BIP-86 branch is\n * also precisely what every pre-RFC-006 client pinned, so this is the older\n * rule retained alongside the newer one, not a laxer rule invented for it.\n *\n * ## When this can be removed\n *\n * This validation runs at one point in the lifecycle: depositor payout signing\n * (`PENDING` → `VERIFIED`). It is not re-run on refund or withdrawal. So the\n * BIP-86 branch stops being reachable for a network once every vault\n * registered before that network's #2440 upgrade has left\n * `PendingDepositorSignatures` — signed and activated, or expired past its\n * activation deadline. That is bounded by the vault lifetime, not indefinite.\n *\n * Removal is therefore gated by the **last** network to upgrade, since this\n * code is shared. Concretely: drop the BIP-86 branch only once devnet, testnet\n * and mainnet have all been on #2440 for longer than the activation deadline,\n * and no vault registered before those upgrades is still awaiting signatures.\n * Deleting it earlier strands deposits with BTC already locked; there is no\n * recovery path short of the refund timelock.\n *\n * @internal Helper invoked by {@link assertPayoutOutputLayout}.\n */\nfunction acceptedPayoutScriptHexes(\n registeredScriptPubKey: string,\n bondedPubkey: string,\n): string[] {\n const registered = stripHexPrefix(registeredScriptPubKey).toLowerCase();\n const legacyBip86 = stripHexPrefix(\n deriveBip86ScriptPubKeyHex(bondedPubkey),\n ).toLowerCase();\n return registered === legacyBip86 ? [registered] : [registered, legacyBip86];\n}\n\n/** Whether `script` matches any of `acceptedHexes`. */\nfunction matchesAnyScript(script: Buffer, acceptedHexes: string[]): boolean {\n return acceptedHexes.some((hex) => script.equals(Buffer.from(hex, \"hex\")));\n}\n\n/**\n * Validate a payout transaction's output structure for the claimer's role,\n * keyed on `claimerBtcPubkey`. Pins per role: `outs.length`, `outs[0].script`,\n * `outs[last].value` (anchor dust), and — for the VP-claimer —\n * `outs[1].script` plus `outs[1].value`, capped at\n * `floor(peginValue × commissionBps / 10_000)`. Canonical layouts: VP-claimer\n * = [payout, commission, anchor]; depositor/VK-claimer = [payout, anchor].\n *\n * `outs[last].script` (the CPFP anchor) is intentionally not pinned: the value\n * pin above bounds depositor exposure regardless of where that dust goes.\n *\n * `outs[1].script` (the VP commission) was unpinned for the same reason.\n * RFC-006 supersedes that rationale: the commission destination is now an\n * operator-registered scriptPubKey we resolve independently at the vault's\n * frozen `vpKeyEpoch`, so it is pinned too — `vpCommissionScriptPubKey` is a\n * required parameter, so there is no unpinned case. The value cap stays — it is\n * still what bounds exposure — and the script pin is added precision, not a\n * replacement for it.\n *\n * What counts as a match differs by output.\n * {@link acceptedPayoutScriptHexes} accepts two candidates — the registered\n * scriptPubKey and the BIP-86 derivation over the bonded key, the latter as a\n * transitional allowance — and backs the VK-claimer's `outs[0]` and the VP\n * commission `outs[1]`. The VP-claimer and depositor-as-claimer `outs[0]` is\n * pinned to the registered payout script alone.\n *\n * Returns the layout-trusted non-anchor script lengths for the fee band:\n * `out0Len` from the pinned `outs[0]` script, and `out1Len` from the pinned\n * VP-claimer commission output, `undefined` for the other roles. Only\n * `out0Len` is range-checked, against the contract's registration cap\n * `[1, MAX_PAYOUT_SCRIPT_LEN]`; `outs[1]` carries no length cap of its own\n * because `assertStandardPayoutScript` already bounds it to a standard output\n * type, which makes the cap unreachable.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assertPayoutOutputLayout(\n payoutTx: Transaction,\n peginValueSats: number,\n params: PayoutParams,\n): { out0Len: number; out1Len: number | undefined } {\n const {\n claimerBtcPubkey,\n vaultProviderBtcPubkey,\n depositorBtcPubkey,\n vaultKeeperBtcPubkeys,\n registeredPayoutScriptPubKey,\n commissionBps,\n vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey,\n } = params;\n\n if (!isValidHex(registeredPayoutScriptPubKey)) {\n throw new Error(\"Invalid registeredPayoutScriptPubKey: not valid hex\");\n }\n\n const claimer = stripHexPrefix(claimerBtcPubkey).toLowerCase();\n const vp = stripHexPrefix(vaultProviderBtcPubkey).toLowerCase();\n const dep = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n const keepers = vaultKeeperBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n\n type Role = \"vp-claimer\" | \"depositor-as-claimer\" | \"vk-claimer\";\n let role: Role;\n let expectedOutCount: number;\n let acceptedOut0ScriptHexes: string[];\n\n if (claimer === vp) {\n role = \"vp-claimer\";\n expectedOutCount = VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n } else if (claimer === dep) {\n role = \"depositor-as-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n } else if (keepers.includes(claimer)) {\n role = \"vk-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n // RFC-006: a keeper's payout goes to the scriptPubKey it registered\n // on-chain, resolved at the vault's frozen keeper epoch. Deriving BIP-86\n // locally as the sole expectation would reject a valid payout the moment a\n // keeper points its payouts at cold custody.\n //\n // A missing entry is an error, never a BIP-86 fallback: the registry\n // backfills BIP-86 itself for keepers that never registered a script, so a\n // gap here means the resolution was incomplete, not that this keeper is on\n // the default.\n const registered = vkClaimerPayoutScriptPubKeys[claimer];\n if (registered === undefined) {\n throw new Error(\n `No registered payout script resolved for vault keeper claimer ${claimer}`,\n );\n }\n assertStandardPayoutScript(\n registered,\n `Vault keeper payout script for claimer ${claimer}`,\n );\n acceptedOut0ScriptHexes = acceptedPayoutScriptHexes(registered, claimer);\n } else {\n throw new Error(\n `Unknown claimer pubkey ${claimer}: not VP, depositor, or a registered vault keeper`,\n );\n }\n\n if (payoutTx.outs.length !== expectedOutCount) {\n throw new Error(\n `Payout transaction has ${payoutTx.outs.length} output(s), ` +\n `expected exactly ${expectedOutCount} for role ${role}.`,\n );\n }\n\n if (!matchesAnyScript(payoutTx.outs[0].script, acceptedOut0ScriptHexes)) {\n throw new Error(\n `Payout transaction output 0 does not pay the expected scriptPubKey for role ${role}. ` +\n `Accepted: ${acceptedOut0ScriptHexes.join(\" or \")}; ` +\n `got ${payoutTx.outs[0].script.toString(\"hex\")}`,\n );\n }\n\n const anchorIdx = expectedOutCount - 1;\n if (payoutTx.outs[anchorIdx].value !== PAYOUT_ANCHOR_DUST_SATS) {\n throw new Error(\n `Payout CPFP anchor (out ${anchorIdx}) value ${payoutTx.outs[anchorIdx].value} sats ` +\n `must equal ${PAYOUT_ANCHOR_DUST_SATS} sats`,\n );\n }\n\n if (role === \"vp-claimer\") {\n // RFC-006 commission destination. Checked before the value cap so a\n // substituted destination reports as such rather than surfacing later as a\n // confusing amount error.\n assertStandardPayoutScript(\n vpCommissionScriptPubKey,\n \"Vault provider commission payout script\",\n );\n const acceptedCommissionScriptHexes = acceptedPayoutScriptHexes(\n vpCommissionScriptPubKey,\n vp,\n );\n if (\n !matchesAnyScript(payoutTx.outs[1].script, acceptedCommissionScriptHexes)\n ) {\n throw new Error(\n `Payout transaction output 1 does not pay the vault provider's commission scriptPubKey. ` +\n `Accepted: ${acceptedCommissionScriptHexes.join(\" or \")}; ` +\n `got ${payoutTx.outs[1].script.toString(\"hex\")}`,\n );\n }\n\n // Structural guard only — a non-negative integer below the bps\n // denominator, so the cap math `floor(peginValue * bps / 10_000)` is\n // meaningful. The protocol minimum is enforced at the trust boundary\n // (`prepareSigningContext`); a too-low value here is fail-safe.\n if (\n !Number.isInteger(commissionBps) ||\n commissionBps < 0 ||\n commissionBps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n ) {\n throw new Error(\n `commissionBps must be an integer in ` +\n `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${commissionBps}`,\n );\n }\n const maxCommissionSats = Math.floor(\n (peginValueSats * commissionBps) / BPS_DENOMINATOR,\n );\n if (payoutTx.outs[1].value > maxCommissionSats) {\n throw new Error(\n `Payout VP commission (out 1) value ${payoutTx.outs[1].value} sats ` +\n `exceeds cap ${maxCommissionSats} sats ` +\n `(${commissionBps} bps of peginValue=${peginValueSats})`,\n );\n }\n }\n\n // The accepted-script set can hold more than one candidate (dual-accept), so\n // the length the fee band consumes is that of the output actually present —\n // which the check above has already pinned to one of the accepted values.\n const out0Len = payoutTx.outs[0].script.length;\n if (out0Len === 0 || out0Len > MAX_PAYOUT_SCRIPT_LEN) {\n throw new Error(\n `Payout receiver scriptPubKey length ${out0Len} is outside the ` +\n `contract's registration cap [1, ${MAX_PAYOUT_SCRIPT_LEN}]; ` +\n `refusing to sign payout.`,\n );\n }\n // No length cap on outs[1]: under RFC-006 the commission output is pinned to\n // the operator's registered script, and `assertStandardPayoutScript` above\n // already bounds that to a standard type (34 bytes at most). A separate\n // 128-byte cap here would be unreachable. Still measured, because the fee\n // floor consumes it — and now from a pinned source rather than a\n // VP-controlled one.\n const out1Len =\n role === \"vp-claimer\" ? payoutTx.outs[1].script.length : undefined;\n\n return { out0Len, out1Len };\n}\n\n/**\n * Extract Schnorr signature from signed payout PSBT.\n *\n * This function supports two cases:\n * 1. Non-finalized PSBT: Extracts from tapScriptSig field\n * 2. Finalized PSBT: Extracts from witness data\n *\n * The signature is returned as a 64-byte hex string (128 hex characters).\n * Payout signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte.\n *\n * @param signedPsbtHex - Signed PSBT hex\n * @param depositorPubkey - Depositor's public key (x-only, 64-char hex)\n * @param inputIndex - Input index to extract signature from (default: 0)\n * @returns 64-byte Schnorr signature (128 hex characters, no sighash flag)\n *\n * @throws If no signature is found in the PSBT\n * @throws If the signature has an unexpected length\n */\nexport function extractPayoutSignature(\n signedPsbtHex: string,\n depositorPubkey: string,\n inputIndex = 0,\n): string {\n const signedPsbt = Psbt.fromHex(signedPsbtHex);\n\n if (inputIndex >= signedPsbt.data.inputs.length) {\n throw new Error(\n `Input index ${inputIndex} out of range (${signedPsbt.data.inputs.length} inputs)`,\n );\n }\n\n const input = signedPsbt.data.inputs[inputIndex];\n\n // Case 1: Non-finalized PSBT — extract from tapScriptSig\n if (input.tapScriptSig && input.tapScriptSig.length > 0) {\n const depositorPubkeyBytes = hexToUint8Array(depositorPubkey);\n\n for (const sigEntry of input.tapScriptSig) {\n if (sigEntry.pubkey.equals(Buffer.from(depositorPubkeyBytes))) {\n return extractSchnorrSig(sigEntry.signature, inputIndex);\n }\n }\n\n throw new Error(\n `No signature found for depositor pubkey: ${depositorPubkey} at input ${inputIndex}`,\n );\n }\n\n // Case 2: Finalized PSBT — extract from finalScriptWitness\n // Taproot single-signature script-path witness: [signature, script, controlBlock].\n // Enforce the exact stack size so that if a wallet produces an unexpected\n // finalization (e.g. a multi-signature stack, an annex, or malformed data),\n // we fail loudly instead of silently returning witnessStack[0] which may\n // not be the depositor's signature.\n if (input.finalScriptWitness && input.finalScriptWitness.length > 0) {\n const witnessStack = parseWitnessStack(input.finalScriptWitness);\n if (witnessStack.length !== TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE) {\n throw new Error(\n `Unexpected finalized witness stack size at input ${inputIndex}: ` +\n `expected ${TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE} items (signature, script, controlBlock), ` +\n `got ${witnessStack.length}`,\n );\n }\n return extractSchnorrSig(witnessStack[0], inputIndex);\n }\n\n throw new Error(\n `No tapScriptSig or finalScriptWitness found in signed PSBT at input ${inputIndex}`,\n );\n}\n\n/**\n * Extract and validate a 64-byte Schnorr signature.\n * Rejects 65-byte signatures because the appended sighash byte changes the\n * Taproot message being signed; stripping it would produce an unverifiable\n * SIGHASH_DEFAULT signature.\n * @internal\n */\nfunction extractSchnorrSig(sig: Uint8Array, inputIndex: number): 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\")} at input ${inputIndex}. ` +\n \"Expected implicit SIGHASH_DEFAULT as a 64-byte signature.\",\n );\n }\n throw new Error(\n `Unexpected signature length at input ${inputIndex}: ${sig.length}`,\n );\n}\n\n/**\n * Parse a BIP-141 serialized witness stack into individual stack items.\n * Format: [varint item_count] [varint len, data]...\n *\n * Throws on malformed input (truncated buffer, 8-byte varints, or trailing\n * bytes) so callers never receive silently-corrupted witness items.\n * @internal\n */\nfunction parseWitnessStack(witness: Buffer): Buffer[] {\n const items: Buffer[] = [];\n let offset = 0;\n\n const requireBytes = (n: number): void => {\n if (offset + n > witness.length) {\n throw new Error(\n `Malformed witness data: need ${n} byte(s) at offset ${offset}, only ${witness.length - offset} remaining`,\n );\n }\n };\n\n const readVarInt = (): number => {\n requireBytes(1);\n const first = witness[offset++];\n if (first < 0xfd) return first;\n if (first === 0xfd) {\n requireBytes(2);\n const val = (witness[offset] | (witness[offset + 1] << 8)) >>> 0;\n offset += 2;\n return val;\n }\n if (first === 0xfe) {\n requireBytes(4);\n const val =\n (witness[offset] |\n (witness[offset + 1] << 8) |\n (witness[offset + 2] << 16) |\n (witness[offset + 3] << 24)) >>>\n 0;\n offset += 4;\n return val;\n }\n // 0xff — 8-byte varint. Not used for witness sizes in practice and JS\n // numbers cannot represent all 64-bit values exactly, so reject rather\n // than risk silent truncation.\n throw new Error(\n `Malformed witness data: 8-byte varint (0xff) not supported at offset ${offset - 1}`,\n );\n };\n\n const count = readVarInt();\n for (let i = 0; i < count; i++) {\n const len = readVarInt();\n requireBytes(len);\n items.push(Buffer.from(witness.subarray(offset, offset + len)));\n offset += len;\n }\n\n if (offset !== witness.length) {\n throw new Error(\n `Malformed witness data: ${witness.length - offset} trailing byte(s) after parsing ${count} item(s)`,\n );\n }\n\n return items;\n}\n","/**\n * Asserts a wallet-returned PSBT encodes the same unsigned transaction\n * as the locally-built PSBT we asked the wallet to sign. Per-input PSBT\n * metadata (witnessUtxo, tapLeafScript, sighashType) is intentionally NOT\n * compared — those fields are committed to the Schnorr sighash and the\n * vault provider's `verify_depositor_signature` rejects mismatches there.\n * This primitive defends the path where a colluding VP would otherwise\n * accept a wallet-substituted signature.\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\n/**\n * Thrown when a wallet-returned PSBT encodes a different unsigned\n * transaction than the one the caller asked the wallet to sign.\n */\nexport class PsbtSubstitutionError extends Error {\n constructor(detail: string) {\n super(\n `Wallet returned a PSBT for a different transaction: ${detail}`,\n );\n this.name = \"PsbtSubstitutionError\";\n }\n}\n\nexport interface AssertPsbtUnsignedTxMatchesParams {\n /** PSBT we built locally and asked the wallet to sign. */\n requestedPsbtHex: string;\n /** PSBT the wallet returned after signing. */\n returnedPsbtHex: string;\n}\n\nfunction parsePsbt(label: \"requested\" | \"returned\", hex: string): Psbt {\n try {\n return Psbt.fromHex(hex);\n } catch (cause) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n throw new Error(`Failed to parse ${label} PSBT: ${reason}`);\n }\n}\n\n/**\n * Length of the hex prefix included in mismatch errors. Short enough that\n * full prevout txids and output scriptPubKeys never reach logs / error\n * trackers, long enough to disambiguate during forensic triage.\n */\nconst REDACTED_HEX_PREFIX_LEN = 8;\n\nfunction redactHex(buf: Buffer): string {\n return `${buf.toString(\"hex\").slice(0, REDACTED_HEX_PREFIX_LEN)}…`;\n}\n\n/**\n * `bitcoinjs-lib` exposes `txInputs[i].hash` in internal little-endian form;\n * a human reading logs expects the big-endian txid an explorer would show.\n * Reverse before truncating so the surfaced prefix matches what an operator\n * can search for.\n */\nfunction redactTxid(internalHash: Buffer): string {\n const reversed = Buffer.from(internalHash).reverse();\n return redactHex(reversed);\n}\n\n/**\n * Compare two PSBTs and throw `PsbtSubstitutionError` unless they encode\n * the same unsigned transaction (version, locktime, inputs, outputs).\n *\n * @throws PsbtSubstitutionError on any mismatch in the unsigned tx\n * @throws Error if either PSBT cannot be parsed\n */\nexport function assertPsbtUnsignedTxMatches(\n params: AssertPsbtUnsignedTxMatchesParams,\n): void {\n const requested = parsePsbt(\"requested\", params.requestedPsbtHex);\n const returned = parsePsbt(\"returned\", params.returnedPsbtHex);\n\n if (requested.version !== returned.version) {\n throw new PsbtSubstitutionError(\n `tx version differs (requested=${requested.version}, returned=${returned.version})`,\n );\n }\n if (requested.locktime !== returned.locktime) {\n throw new PsbtSubstitutionError(\n `tx locktime differs (requested=${requested.locktime}, returned=${returned.locktime})`,\n );\n }\n if (requested.txInputs.length !== returned.txInputs.length) {\n throw new PsbtSubstitutionError(\n `input count differs (requested=${requested.txInputs.length}, returned=${returned.txInputs.length})`,\n );\n }\n if (requested.txOutputs.length !== returned.txOutputs.length) {\n throw new PsbtSubstitutionError(\n `output count differs (requested=${requested.txOutputs.length}, returned=${returned.txOutputs.length})`,\n );\n }\n for (let i = 0; i < requested.txInputs.length; i++) {\n const r = requested.txInputs[i];\n const s = returned.txInputs[i];\n if (!r.hash.equals(s.hash)) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout txid differs (requested=${redactTxid(r.hash)}, returned=${redactTxid(s.hash)})`,\n );\n }\n if (r.index !== s.index) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout vout differs (requested=${r.index}, returned=${s.index})`,\n );\n }\n if (r.sequence !== s.sequence) {\n throw new PsbtSubstitutionError(\n `input ${i} sequence differs (requested=${r.sequence}, returned=${s.sequence})`,\n );\n }\n }\n for (let i = 0; i < requested.txOutputs.length; i++) {\n const r = requested.txOutputs[i];\n const s = returned.txOutputs[i];\n if (!r.script.equals(s.script)) {\n throw new PsbtSubstitutionError(\n `output ${i} scriptPubKey differs (requested=${redactHex(r.script)}, returned=${redactHex(s.script)})`,\n );\n }\n if (r.value !== s.value) {\n throw new PsbtSubstitutionError(\n `output ${i} value differs (requested=${r.value}, returned=${s.value})`,\n );\n }\n }\n}\n","/**\n * Independent BIP-340 verification of a wallet-returned Taproot script-path\n * Schnorr signature against an independently-recomputed sighash.\n *\n * Critical Path #7 (CLAUDE.md): the SDK requests script-path signatures with\n * `useTweakedSigner: false, autoFinalized: false`. Wallet support for the\n * untweaked-key flag is inconsistent — older OKX / mobile bridges silently sign\n * with the *tweaked* key, Keystone ignores the flag — and a compromised\n * extension can stuff a 64-byte stub into `tapScriptSig`. A bad signature that\n * the SDK forwards is only caught on broadcast; in the worst case it passes the\n * VP off-chain but Bitcoin rejects it, leaving the depositor's BTC locked in the\n * HTLC until `timelockRefund` matures. This guard rejects such signatures before\n * they are trusted.\n *\n * Why verify against the *locally-built* PSBT, not the wallet-returned one:\n * `assertPsbtUnsignedTxMatches` pins the unsigned transaction but deliberately\n * skips per-input metadata (`witnessUtxo`, `tapLeafScript`). A malicious wallet\n * could rewrite those consistently in the returned PSBT so a wrong-message\n * signature self-validates. The trusted prevout scripts/values and leaf script\n * therefore come from the PSBT we built ourselves (derived from on-chain / WASM\n * sources); only the 64-byte signature comes from the wallet.\n *\n * Reuses the exact primitives `bip322Verify.ts` already depends on — no new\n * dependency:\n * - `@bitcoin-js/tiny-secp256k1-asmjs` → `verifySchnorr`\n * - `bitcoinjs-lib` → `Transaction.hashForWitnessV1`\n * - `../utils/taproot` → `computeTapLeafHash`\n *\n * @module tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\n\nimport { Buffer } from \"buffer\";\n\nimport { X_ONLY_PUBKEY_HEX_LEN } from \"../../utils/validation\";\nimport {\n SCHNORR_SIG_HEX_LEN,\n TAPSCRIPT_LEAF_VERSION,\n hexToUint8Array,\n stripHexPrefix,\n} from \"../utils/bitcoin\";\nimport { computeTapLeafHash } from \"../utils/taproot\";\n\nexport interface VerifyScriptPathSchnorrSignatureParams {\n /**\n * Hex of the PSBT we built locally and sent to the wallet (the trusted\n * source of prevout scripts/values and the leaf script). NOT the\n * wallet-returned PSBT.\n */\n requestedPsbtHex: string;\n /** The 64-byte Schnorr signature extracted from the wallet's response (128 hex chars). */\n signatureHex: string;\n /** X-only public key (64 hex chars) the wallet signed the script-path leaf with. */\n signerXOnlyPubkeyHex: string;\n /** Index of the input the signature is for. */\n inputIndex: number;\n}\n\n/**\n * Assert that `signatureHex` is a valid BIP-340 Schnorr signature by the\n * `signerXOnlyPubkeyHex` key over the Taproot script-path sighash of\n * `requestedPsbtHex` input `inputIndex` (SIGHASH_DEFAULT).\n *\n * @throws If the requested PSBT is malformed, lacks the prevout/leaf data needed\n * to recompute the sighash, or the signature does not verify.\n */\nexport function assertScriptPathSchnorrSignature(\n params: VerifyScriptPathSchnorrSignatureParams,\n): void {\n const { requestedPsbtHex, signatureHex, signerXOnlyPubkeyHex, inputIndex } =\n params;\n\n const signatureRaw = stripHexPrefix(signatureHex);\n if (signatureRaw.length !== SCHNORR_SIG_HEX_LEN) {\n throw new Error(\n `Schnorr signature for input ${inputIndex} must be ${SCHNORR_SIG_HEX_LEN} hex chars ` +\n `(64 bytes), got ${signatureRaw.length}.`,\n );\n }\n\n const signerXOnly = stripHexPrefix(signerXOnlyPubkeyHex);\n if (signerXOnly.length !== X_ONLY_PUBKEY_HEX_LEN) {\n throw new Error(\n `Signer x-only pubkey for input ${inputIndex} must be ${X_ONLY_PUBKEY_HEX_LEN} hex chars ` +\n `(32 bytes), got ${signerXOnly.length}.`,\n );\n }\n\n const psbt = Psbt.fromHex(requestedPsbtHex);\n\n if (inputIndex < 0 || inputIndex >= psbt.data.inputs.length) {\n throw new Error(\n `Input index ${inputIndex} out of range (${psbt.data.inputs.length} inputs).`,\n );\n }\n\n // Taproot's sighash commits to every input's prevout (script + value), so all\n // inputs must carry a witnessUtxo. A missing one is a build error, not a\n // value we can default — fail loudly.\n const prevOutScripts: Buffer[] = [];\n const values: number[] = [];\n for (let i = 0; i < psbt.data.inputs.length; i++) {\n const witnessUtxo = psbt.data.inputs[i].witnessUtxo;\n if (!witnessUtxo) {\n throw new Error(\n `Cannot verify signature: input ${i} of the requested PSBT has no witnessUtxo ` +\n `(required to recompute the Taproot sighash).`,\n );\n }\n prevOutScripts.push(witnessUtxo.script);\n values.push(witnessUtxo.value);\n }\n\n // The signed input must expose exactly one tapLeafScript — the leaf the\n // depositor signs. Zero means we sent the wrong PSBT; more than one means an\n // ambiguous spend path we never construct for a single-signature input.\n const tapLeafScripts = psbt.data.inputs[inputIndex].tapLeafScript;\n if (!tapLeafScripts || tapLeafScripts.length !== 1) {\n throw new Error(\n `Cannot verify signature: input ${inputIndex} of the requested PSBT must have exactly ` +\n `one tapLeafScript, got ${tapLeafScripts?.length ?? 0}.`,\n );\n }\n const leaf = tapLeafScripts[0];\n if (leaf.leafVersion !== TAPSCRIPT_LEAF_VERSION) {\n throw new Error(\n `Cannot verify signature: input ${inputIndex} tapLeafScript has leaf version ` +\n `0x${leaf.leafVersion.toString(16)}, expected 0x${TAPSCRIPT_LEAF_VERSION.toString(16)}.`,\n );\n }\n\n const leafHash = computeTapLeafHash(leaf.leafVersion, leaf.script);\n\n // Reconstruct the unsigned transaction from the requested PSBT using only\n // public bitcoinjs-lib API (same pattern as bip322Verify.ts), then compute the\n // BIP-341 script-path sighash with SIGHASH_DEFAULT.\n const tx = new Transaction();\n tx.version = psbt.version;\n tx.locktime = psbt.locktime;\n for (const input of psbt.txInputs) {\n tx.addInput(input.hash, input.index, input.sequence);\n }\n for (const output of psbt.txOutputs) {\n tx.addOutput(output.script, output.value);\n }\n\n const sighash = tx.hashForWitnessV1(\n inputIndex,\n prevOutScripts,\n values,\n Transaction.SIGHASH_DEFAULT,\n leafHash,\n );\n\n const isValid = ecc.verifySchnorr(\n sighash,\n hexToUint8Array(signerXOnly),\n hexToUint8Array(signatureRaw),\n );\n\n if (!isValid) {\n throw new Error(\n `Schnorr signature for input ${inputIndex} (signer ${signerXOnly}) does not verify ` +\n `against the expected Taproot script-path sighash. The wallet may have signed with ` +\n `the tweaked key, signed a different transaction, or returned an invalid signature.`,\n );\n }\n}\n"],"names":["normalizeKey","key","processPublicKeyToXOnly","computeNumLocalChallengers","vaultProviderPubkey","vaultKeeperPubkeys","depositorPubkey","localSet","vk","deriveLocalChallengers","params","claimer","depositor","vaultKeepers","filtered","k","PREIMAGE_LENGTH_BYTES","MAX_U16","SEC1_EVEN_Y_PREFIX","normalizeXOnlyKey","value","label","stripHexPrefix","secp256k1","Buffer","normalizeKeyGroup","values","keys","index","nOfNChunks","verify","chunks","opcodes","bscript","deriveExpectedPrePeginHtlc","hashlock","assertEccInitialized","vaultProvider","universalChallengers","cleanHashlock","hashlockScript","refundScript","scriptTree","TAPSCRIPT_LEAF_VERSION","hash","output","witness","payments","tapInternalPubkey","hashlockControlBlock","refundPayment","refundControlBlock","_a","deriveExpectedPeginPayoutScriptPubKey","timelockPegin","payoutScript","assertWasmPeginSizing","result","expectedCount","expectedClaimValue","computeMinClaimValue","anchor","peginP2aAnchorOutput","anchorValue","expectedPeginFee","computeMinPeginFee","expectedReserve","maxImpliedReserve","MAX_REASONABLE_PEGIN_VBYTES","i","requested","peginAmount","htlcValue","impliedReserve","HtlcOutputMismatchError","message","assertEncodedHtlcOutputsMatch","outputs","htlcValues","htlcScriptPubKeys","encodedValue","encodedScript","expectedScript","assertUnfundedPrePeginOutputLayout","authAnchorHash","version","locktime","PRE_PEGIN_TX_VERSION","PRE_PEGIN_TX_LOCKTIME","PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT","authOutputIndex","cpfpOutputIndex","peginOutputCount","authOutput","PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS","encodedAuthScript","expectedAuthScript","PRE_PEGIN_AUTH_SCRIPT_PREFIX","cpfpOutput","PRE_PEGIN_CPFP_VALUE_SATS","encodedCpfpScript","expectedCpfpScript","deriveBip86ScriptPubKeyHex","PEGIN_DEPOSITOR_CLAIM_VOUT","deriveDepositorClaimDescriptor","cleanPubkey","leafScript","hexToUint8Array","internalKey","redeem","controlBlock","deriveDepositorClaimScriptPubKey","AUTH_ANCHOR_HASH_HEX_LEN","HEX_PATTERN","MAX_TIMELOCK_PEGIN_BLOCKS","buildPrePeginPsbt","normalizeAuthAnchorHash","createPrePeginTransaction","minPeginFee","parsed","parseUnfundedWasmTransaction","totalOutputValue","sum","o","authAnchorVout","cleaned","buildPeginTxFromFundedPrePegin","buildPeginTxFromPrePegin","assertPeginTxShape","PEGIN_BASE_OUTPUT_COUNT","expectedTxVersion","PEGIN_TX_VERSION_CORE_1","PEGIN_TX_VERSION_CORE_2_AND_3","validatePeginP2aAnchor","expectedOutputCount","peginTx","Transaction","PEGIN_TX_LOCKTIME","fundedPrePeginTxid","inputTxid","uint8ArrayToHex","PEGIN_INPUT_SEQUENCE","requestedAmount","encodedVaultOut","expectedVaultScript","encodedVaultScript","encodedTxid","encodedClaimOut","expectedClaimScript","createPayoutScript","connector","createPayoutConnector","COMPACT_SIZE_UINT16_PREFIX","COMPACT_SIZE_UINT32_PREFIX","COMPACT_SIZE_UINT16_MAX","COMPACT_SIZE_UINT32_MAX","TAPLEAF_TAG","TAPBRANCH_TAG","TAPTWEAK_TAG","CONTROL_BLOCK_PREFIX_LEN","CONTROL_BLOCK_NODE_LEN","CONTROL_BLOCK_MAX_NODES","CONTROL_BLOCK_PARITY_MASK","CONTROL_BLOCK_LEAF_VERSION_MASK","P2TR_SCRIPT_PUBKEY_PREFIX","encodeCompactSize","n","computeTapLeafHash","leafVersion","script","preimage","bcrypto","computeTaprootScriptPubKey","binding","pathLen","controlLeafVersion","node","offset","sibling","tweak","tweaked","ecc","expectedParity","MAX_FLOOR_MODEL_KEEPERS","MAX_UNIVERSAL_CHALLENGERS","MAX_FLOOR_MODEL_COUNCIL_SIZE","MAX_SANE_FEE_RATE_SAT_PER_VB","MAX_PAYOUT_VSIZE_BASE","MAX_PAYOUT_VSIZE_PER_PARTICIPANT","PAYOUT_BOUND_ASSUMED_SCRIPT_LEN","assertPayoutFeeBandDomain","role","count","max","reason","assertPayoutFeeInBand","measured","implicitFeeSats","out0Len","out1Len","numParticipants","implicitFee","scriptExcess","maxPayoutVsize","maxFeeSats","minFeeSats","computePayoutFeeFloor","P2WPKH_LEN","P2WSH_LEN","P2TR_LEN","P2PKH_LEN","P2SH_LEN","isP2wpkh","s","isP2wsh","isP2tr","isP2pkh","isP2sh","assertStandardPayoutScript","scriptHex","TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE","buildPayoutPsbt","feeBandParams","payoutTx","assertTx","DEPOSITOR_PAYOUT_INPUT_COUNT","PAYOUT_TX_VERSION","PAYOUT_TX_LOCKTIME","peginPrevOut","requirePrevOut","PEGIN_VAULT_OUTPUT_INDEX","assertPrevOut","ASSERT_PAYOUT_OUTPUT_INDEX","assertPayoutOutputLayout","inputValueSats","outputValueSats","out","payoutConnector","assertPayoutLeaf","resolveAssertPayoutLeaf","assemblePayoutPsbt","payoutControlBlock","getAssertPayoutScriptInfo","boundScriptPubKey","input","inputIndex","parentTx","parentLabel","expectedVout","parentTxid","prevOut","psbt","Psbt","input0","input1","acceptedPayoutScriptHexes","registeredScriptPubKey","bondedPubkey","registered","legacyBip86","matchesAnyScript","acceptedHexes","hex","peginValueSats","claimerBtcPubkey","vaultProviderBtcPubkey","depositorBtcPubkey","vaultKeeperBtcPubkeys","registeredPayoutScriptPubKey","commissionBps","vkClaimerPayoutScriptPubKeys","vpCommissionScriptPubKey","isValidHex","vp","dep","keepers","expectedOutCount","acceptedOut0ScriptHexes","VP_CLAIMER_PAYOUT_OUTPUT_COUNT","NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT","anchorIdx","PAYOUT_ANCHOR_DUST_SATS","acceptedCommissionScriptHexes","MAX_VP_COMMISSION_BPS_EXCLUSIVE","maxCommissionSats","BPS_DENOMINATOR","MAX_PAYOUT_SCRIPT_LEN","extractPayoutSignature","signedPsbtHex","signedPsbt","depositorPubkeyBytes","sigEntry","extractSchnorrSig","witnessStack","parseWitnessStack","sig","items","requireBytes","readVarInt","first","val","len","PsbtSubstitutionError","detail","parsePsbt","cause","REDACTED_HEX_PREFIX_LEN","redactHex","buf","redactTxid","internalHash","reversed","assertPsbtUnsignedTxMatches","returned","r","assertScriptPathSchnorrSignature","requestedPsbtHex","signatureHex","signerXOnlyPubkeyHex","signatureRaw","SCHNORR_SIG_HEX_LEN","signerXOnly","X_ONLY_PUBKEY_HEX_LEN","prevOutScripts","witnessUtxo","tapLeafScripts","leaf","leafHash","tx","sighash"],"mappings":"+nBAaA,SAASA,EAAaC,EAAqB,CACzC,OAAOC,EAAAA,wBAAwBD,CAAG,EAAE,YAAA,CACtC,CAgBO,SAASE,GACdC,EACAC,EACAC,EACQ,CACR,MAAMC,MAAe,IACrBA,EAAS,IAAIP,EAAaI,CAAmB,CAAC,EAC9C,UAAWI,KAAMH,EACfE,EAAS,IAAIP,EAAaQ,CAAE,CAAC,EAE/B,OAAAD,EAAS,OAAOP,EAAaM,CAAe,CAAC,EACtCC,EAAS,IAClB,CA8BO,SAASE,GACdC,EACU,CACV,MAAMC,EAAUX,EAAaU,EAAO,gBAAgB,EAC9CE,EAAYZ,EAAaU,EAAO,kBAAkB,EAClDG,EAAeH,EAAO,sBAAsB,IAAIV,CAAY,EAElE,GAAIW,IAAYC,EAAW,CACzB,MAAME,EAAWD,EAAa,OAAQE,GAAMA,IAAMH,CAAS,EAC3D,GAAIE,EAAS,SAAW,EACtB,MAAM,IAAI,MACR,4FAAA,EAGJ,GAAI,IAAI,IAAIA,CAAQ,EAAE,OAASA,EAAS,OACtC,MAAM,IAAI,MACR,8FAAA,EAGJ,OAAOA,CACT,CAOA,MAAMA,EAHU,CACd,GAAG,IAAI,IAAI,CAACd,EAAaU,EAAO,sBAAsB,EAAG,GAAGG,CAAY,CAAC,CAAA,EACzE,KAAA,EACuB,OAAQE,GAAMA,IAAMJ,CAAO,EACpD,GAAIG,EAAS,SAAW,EACtB,MAAM,IAAI,MACR,qGAAqGH,CAAO,EAAA,EAGhH,OAAOG,CACT,CCzDA,MAAME,GAAwB,GACxBC,GAAU,MACVC,GAAqB,EA8B3B,SAASC,EAAkBC,EAAeC,EAAuB,CAC/D,MAAMpB,EAAMqB,EAAAA,eAAeF,CAAK,EAAE,YAAA,EAClC,GAAI,CAAC,iBAAiB,KAAKnB,CAAG,EAC5B,MAAM,IAAI,MAAM,GAAGoB,CAAK,uCAAuC,EAGjE,GAAI,CACFE,GAAAA,UAAU,MAAM,UACdC,EAAAA,OAAO,OAAO,CACZA,SAAO,KAAK,CAACN,EAAkB,CAAC,EAChCM,SAAO,KAAKvB,EAAK,KAAK,CAAA,CACvB,CAAA,CAEL,MAAQ,CACN,MAAM,IAAI,MAAM,GAAGoB,CAAK,mCAAmC,CAC7D,CAEA,OAAOpB,CACT,CAEA,SAASwB,EAAkBC,EAA2BL,EAAyB,CAC7E,GAAIK,EAAO,SAAW,EACpB,MAAM,IAAI,MAAM,GAAGL,CAAK,qBAAqB,EAG/C,MAAMM,EAAOD,EACV,IAAI,CAACN,EAAOQ,IAAUT,EAAkBC,EAAO,GAAGC,CAAK,IAAIO,CAAK,GAAG,CAAC,EACpE,KAAA,EACH,GAAI,IAAI,IAAID,CAAI,EAAE,OAASA,EAAK,OAC9B,MAAM,IAAI,MAAM,GAAGN,CAAK,mCAAmC,EAE7D,OAAOM,CACT,CAEA,SAASE,EAAWF,EAAyBG,EAAgC,CAC3E,MAAMC,EAAwB,CAAA,EAC9B,OAAAJ,EAAK,QAAQ,CAAC1B,EAAK2B,IAAU,CAC3BG,EAAO,KACLP,SAAO,KAAKvB,EAAK,KAAK,EACtB2B,IAAU,EAAII,UAAQ,YAAcA,EAAAA,QAAQ,cAAA,CAEhD,CAAC,EACDD,EAAO,KACLE,EAAAA,OAAQ,OAAO,OAAON,EAAK,MAAM,EACjCG,EAASE,EAAAA,QAAQ,kBAAoBA,UAAQ,WAAA,EAExCD,CACT,CAKO,SAASG,GACdxB,EACAyB,EACsB,OACtBC,uBAAA,EAEA,MAAMxB,EAAYO,EAChBT,EAAO,gBACP,iBAAA,EAEI2B,EAAgBlB,EACpBT,EAAO,oBACP,qBAAA,EAEIG,EAAeY,EACnBf,EAAO,mBACP,oBAAA,EAEI4B,EAAuBb,EAC3Bf,EAAO,2BACP,4BAAA,EAEI6B,EAAgBjB,EAAAA,eAAea,CAAQ,EAAE,YAAA,EAC/C,GAAI,CAAC,iBAAiB,KAAKI,CAAa,EACtC,MAAM,IAAI,MAAM,4BAA4B,EAE9C,GACE,CAAC,OAAO,UAAU7B,EAAO,cAAc,GACvCA,EAAO,eAAiB,GACxBA,EAAO,eAAiBO,GAExB,MAAM,IAAI,MAAM,oDAAoD,EAGtE,MAAMuB,EAAiBP,EAAAA,OAAQ,QAAQ,CACrCD,EAAAA,QAAQ,QACRC,SAAQ,OAAO,OAAOjB,EAAqB,EAC3CgB,EAAAA,QAAQ,eACRA,EAAAA,QAAQ,UACRR,SAAO,KAAKe,EAAe,KAAK,EAChCP,EAAAA,QAAQ,eACRR,SAAO,KAAKZ,EAAW,KAAK,EAC5BoB,EAAAA,QAAQ,kBACRR,SAAO,KAAKa,EAAe,KAAK,EAChCL,EAAAA,QAAQ,kBACR,GAAGH,EAAWhB,EAAc,EAAI,EAChC,GAAGgB,EAAWS,EAAsB,EAAK,CAAA,CAC1C,EACKG,EAAeR,EAAAA,OAAQ,QAAQ,CACnCT,SAAO,KAAKZ,EAAW,KAAK,EAC5BoB,EAAAA,QAAQ,kBACRC,EAAAA,OAAQ,OAAO,OAAOvB,EAAO,cAAc,EAC3CsB,UAAQ,sBAAA,CACT,EACKU,EAGF,CACF,CAAE,OAAQF,EAAgB,QAASG,wBAAA,EACnC,CAAE,OAAQF,EAAc,QAASE,EAAAA,sBAAA,CAAuB,EAEpD,CAAE,KAAAC,EAAM,OAAAC,EAAQ,QAAAC,CAAA,EAAYC,EAAAA,SAAS,KAAK,CAC9C,eAAgBvB,EAAAA,OAAO,KAAKwB,mBAAiB,EAC7C,WAAAN,EACA,OAAQ,CACN,OAAQF,EACR,cAAeG,EAAAA,sBAAA,CACjB,CACD,EACKM,EAAuBH,GAAA,YAAAA,EAAUA,EAAQ,OAAS,GAClDI,EAAgBH,EAAAA,SAAS,KAAK,CAClC,eAAgBvB,EAAAA,OAAO,KAAKwB,mBAAiB,EAC7C,WAAAN,EACA,OAAQ,CACN,OAAQD,EACR,cAAeE,EAAAA,sBAAA,CACjB,CACD,EACKQ,GACJC,EAAAF,EAAc,UAAd,YAAAE,EAAwBF,EAAc,QAAQ,OAAS,GACzD,GAAI,CAACN,GAAQ,CAACC,GAAU,CAACI,GAAwB,CAACE,EAChD,MAAM,IAAI,MAAM,sDAAsD,EAGxE,MAAO,CACL,eAAAX,EACA,qBAAAS,EACA,aAAAR,EACA,mBAAAU,EACA,aAAcN,EACd,cAAeD,CAAA,CAEnB,CAMO,SAASS,GACd3C,EACA4C,EACQ,CACRlB,uBAAA,EAEA,MAAMxB,EAAYO,EAChBT,EAAO,gBACP,iBAAA,EAEI2B,EAAgBlB,EACpBT,EAAO,oBACP,qBAAA,EAEIG,EAAeY,EACnBf,EAAO,mBACP,oBAAA,EAEI4B,EAAuBb,EAC3Bf,EAAO,2BACP,4BAAA,EAKI6C,EAAetB,EAAAA,OAAQ,QAAQ,CACnCT,SAAO,KAAKZ,EAAW,KAAK,EAC5BoB,EAAAA,QAAQ,kBACRR,SAAO,KAAKa,EAAe,KAAK,EAChCL,EAAAA,QAAQ,kBACR,GAAGH,EAAWhB,EAAc,EAAI,EAChC,GAAGgB,EAAWS,EAAsB,EAAI,EACxCL,SAAQ,OAAO,OAAOqB,CAAa,EACnCtB,UAAQ,sBAAA,CACT,EACK,CAAE,OAAAa,CAAA,EAAWE,EAAAA,SAAS,KAAK,CAC/B,eAAgBvB,EAAAA,OAAO,KAAKwB,mBAAiB,EAC7C,WAAY,CAAE,OAAQO,EAAc,QAASZ,EAAAA,sBAAA,CAAuB,CACrE,EACD,GAAI,CAACE,EACH,MAAM,IAAI,MAAM,oDAAoD,EAGtE,OAAOA,CACT,CA4BA,eAAsBW,GACpBC,EACA/C,EACiB,CACjB,MAAMgD,EAAgBhD,EAAO,aAAa,OAE1C,GAAIA,EAAO,UAAU,SAAWgD,EAC9B,MAAM,IAAI,MACR,iBAAiBhD,EAAO,UAAU,MAAM,0BACnCgD,CAAa,+BAAA,EAMtB,GAAID,EAAO,WAAW,SAAWC,EAC/B,MAAM,IAAI,MACR,2BAA2BD,EAAO,WAAW,MAAM,4BACrCC,CAAa,+BAAA,EAG/B,GACED,EAAO,aAAa,SAAWC,GAC/BD,EAAO,kBAAkB,SAAWC,GACpCD,EAAO,cAAc,SAAWC,EAEhC,MAAM,IAAI,MACR,gEACiBD,EAAO,WAAW,MAAM,kBACvBA,EAAO,aAAa,MAAM,uBACrBA,EAAO,kBAAkB,MAAM,mBACnCA,EAAO,cAAc,MAAM,eAChCC,CAAa,QAAA,EAO/B,GAAID,EAAO,qBAAuB,GAChC,MAAM,IAAI,MACR,4DACKA,EAAO,mBAAmB,iBAAA,EAGnC,MAAME,EAAqB,MAAMC,EAAAA,qBAE/BlD,EAAO,iBACPA,EAAO,oBACPA,EAAO,2BAA2B,OAClCA,EAAO,cACPA,EAAO,YACPA,EAAO,OAAA,EAET,GAAI+C,EAAO,sBAAwBE,EACjC,MAAM,IAAI,MACR,sCAAsCF,EAAO,mBAAmB,kEAE3DE,CAAkB,sBAAsBjD,EAAO,gBAAgB,yBAC3CA,EAAO,mBAAmB,6BACtBA,EAAO,2BAA2B,MAAM,mBAClDA,EAAO,aAAa,iBAAiBA,EAAO,WAAW,aAC7DA,EAAO,OAAO,IAAA,EAU/B,MAAMmD,EAAS,MAAMC,uBAAqBpD,EAAO,gBAAgB,EAC3DqD,GAAcF,GAAA,YAAAA,EAAQ,QAAS,GAC/BG,EAAmB,MAAMC,EAAAA,mBAC7BvD,EAAO,iBACPA,EAAO,mBAAmB,OAC1BA,EAAO,2BAA2B,OAClCA,EAAO,eAAA,EAEHwD,EAAkBF,EAAmBD,EAMrCI,EACJzD,EAAO,gBAAkB0D,EAAAA,4BAE3B,QAASC,EAAI,EAAGA,EAAIX,EAAeW,IAAK,CACtC,MAAMC,EAAY5D,EAAO,aAAa2D,CAAC,EACjCE,EAAcd,EAAO,aAAaY,CAAC,EACnCG,EAAYf,EAAO,WAAWY,CAAC,EAOrC,GAAIE,IAAgBD,EAClB,MAAM,IAAI,MACR,8BAA8BD,CAAC,KAAKE,CAAW,wCACzBD,CAAS,sFAAA,EAInC,GAAIC,GAAe,GACjB,MAAM,IAAI,MACR,8BAA8BF,CAAC,sBAAsBE,CAAW,kBAAA,EAIpE,GAAIC,GAAa,GACf,MAAM,IAAI,MACR,4BAA4BH,CAAC,sBAAsBG,CAAS,kBAAA,EAShE,MAAMC,EAAiBD,EAAYD,EAAcd,EAAO,oBAKxD,GAAIgB,GAAkB,GACpB,MAAM,IAAI,MACR,4BAA4BJ,CAAC,KAAKG,CAAS,wCACpBD,CAAW,0BAC7Bd,EAAO,mBAAmB,uCAClBgB,CAAc,IAAA,EAG/B,GAAIA,EAAiBN,EACnB,MAAM,IAAI,MACR,2CAA2CE,CAAC,MAAMI,CAAc,sCACzBN,CAAiB,yBAClCzD,EAAO,eAAe,MACvC0D,EAAAA,2BAA2B,uBAAuBI,CAAS,4BAAA,EAIpE,GAAIC,IAAmBP,EACrB,MAAM,IAAI,MACR,4BAA4BG,CAAC,KAAKG,CAAS,0CAChBC,CAAc,0BACpCP,CAAe,qBAAqBF,CAAgB,gBAC1CD,CAAW,yBACrBrD,EAAO,gBAAgB,kBACvBA,EAAO,mBAAmB,MAAM,0BAChCA,EAAO,2BAA2B,MAAM,qBACxCA,EAAO,eAAe,IAAA,CAGjC,CAEA,OAAOsD,CACT,CAaO,MAAMU,UAAgC,KAAM,CACjD,YAAYC,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,yBACd,CACF,CAuBO,SAASC,GACdC,EACAC,EACAC,EACM,CACN,GAAIF,EAAQ,OAASC,EAAW,OAC9B,MAAM,IAAIJ,EACR,4BAA4BG,EAAQ,MAAM,8BACrCC,EAAW,MAAM,4CAAA,EAI1B,QAAST,EAAI,EAAGA,EAAIS,EAAW,OAAQT,IAAK,CAC1C,MAAMW,EAAe,OAAOH,EAAQR,CAAC,EAAE,KAAK,EAC5C,GAAIW,IAAiBF,EAAWT,CAAC,EAC/B,MAAM,IAAIK,EACR,iCAAiCL,CAAC,WAAWW,CAAY,+CAClBF,EAAWT,CAAC,CAAC,4DAAA,EAKxD,MAAMY,EAAgBJ,EAAQR,CAAC,EAAE,OAAO,SAAS,KAAK,EAAE,YAAA,EAClDa,EAAiBH,EAAkBV,CAAC,EAAE,YAAA,EAC5C,GAAIY,IAAkBC,EACpB,MAAM,IAAIR,EACR,iCAAiCL,CAAC,kBAAkBY,CAAa,sDACfC,CAAc,GAAA,CAGtE,CACF,CASO,SAASC,GACdN,EACAC,EACAC,EACArE,EACA0E,EACAC,EACAC,EACM,CACN,GAAID,IAAYE,EAAAA,qBACd,MAAM,IAAI,MACR,sCAAsCF,CAAO,cACxCE,EAAAA,oBAAoB,GAAA,EAG7B,GAAID,IAAaE,EAAAA,sBACf,MAAM,IAAI,MACR,uCAAuCF,CAAQ,cAC1CE,EAAAA,qBAAqB,GAAA,EAI9B,GAAIV,EAAW,OAASW,kCACtB,MAAM,IAAI,MACR,oCAAoCX,EAAW,MAAM,sCAC9BW,EAAAA,+BAA+B,GAAA,EAI1D,MAAMC,EAAkBZ,EAAW,OAC7Ba,EACJD,GAAkB,EAAON,IAAmB,QACxC1B,EAAgBkC,EAAAA,iBACpBd,EAAW,OACXM,IAAmB,MAAA,EAErB,GAAIP,EAAQ,SAAWnB,EACrB,MAAM,IAAI,MACR,oCAAoCmB,EAAQ,MAAM,gCAC5BnB,CAAa,GAAA,EAIvCkB,GAA8BC,EAASC,EAAYC,CAAiB,EAEpE,QAASV,EAAI,EAAGA,EAAIS,EAAW,OAAQT,IAAK,CAC1C,MAAMa,EAAiBhD,GACrBxB,EACAA,EAAO,UAAU2D,CAAC,CAAA,EAClB,aACF,GAAI,CAACQ,EAAQR,CAAC,EAAE,OAAO,OAAOa,CAAc,EAC1C,MAAM,IAAIR,EACR,yBAAyBL,CAAC,0DAAA,CAIhC,CAEA,GAAIe,IAAmB,OAAW,CAChC,MAAMS,EAAahB,EAAQa,CAAe,EAC1C,GAAIG,EAAW,QAAUC,mCACvB,MAAM,IAAI,MACR,8BAA8BJ,CAAe,WACxCG,EAAW,KAAK,0BAChBC,EAAAA,gCAAgC,OAAA,EAIzC,MAAMC,EAAoBF,EAAW,OAAO,SAAS,KAAK,EAAE,YAAA,EACtDG,EACJC,EAAAA,6BAA+Bb,EAAe,YAAA,EAChD,GAAIW,IAAsBC,EACxB,MAAM,IAAI,MACR,8BAA8BN,CAAe,kBACxCK,CAAiB,cAAcC,CAAkB,GAAA,CAG5D,CAEA,MAAME,EAAarB,EAAQc,CAAe,EAC1C,GAAIO,EAAW,QAAUC,4BACvB,MAAM,IAAI,MACR,8BAA8BR,CAAe,WACxCO,EAAW,KAAK,0BAChBC,EAAAA,yBAAyB,OAAA,EAIlC,MAAMC,EAAoBF,EAAW,OAAO,SAAS,KAAK,EAAE,YAAA,EACtDG,EAAqB/E,EAAAA,eACzBgF,EAAAA,2BAA2B5F,EAAO,eAAe,CAAA,EACjD,YAAA,EACF,GAAI0F,IAAsBC,EACxB,MAAM,IAAI,MACR,8BAA8BV,CAAe,kBACxCS,CAAiB,sCACjBC,CAAkB,GAAA,CAG7B,CC1lBO,MAAME,GAA6B,EAkCnC,SAASC,GACdlG,EAC0B,CAM1B,MAAMmG,EAAcnF,EAAAA,eAAehB,CAAe,EAClD,GAAI,CAAC,oBAAoB,KAAKmG,CAAW,EACvC,MAAM,IAAI,MACR,iHAAA,EAKJ,MAAMC,EAAazE,EAAAA,OAAQ,QAAQ,CACjCT,EAAAA,OAAO,KAAKmF,kBAAgBF,CAAW,CAAC,EACxCzE,UAAQ,WAAA,CACT,EAEK4E,EAAcpF,EAAAA,OAAO,KAAKwB,mBAAiB,EAC3C6D,EAAS,CAAE,OAAQH,EAAY,cAAe/D,EAAAA,sBAAA,EAC9C,CAAE,OAAAE,EAAQ,QAAAC,GAAYC,EAAAA,SAAS,KAAK,CACxC,eAAgB6D,EAChB,WAAY,CAAE,OAAQF,CAAA,EACtB,OAAAG,CAAA,CACD,EAED,GAAI,CAAChE,EACH,MAAM,IAAI,MACR,oFAAA,EAMJ,MAAMiE,EAAehE,GAAA,YAAAA,EAAUA,EAAQ,OAAS,GAChD,GAAI,CAACgE,EACH,MAAM,IAAI,MACR,oGAAA,EAIJ,MAAO,CAAE,aAAcjE,EAAQ,WAAA6D,EAAY,aAAAI,EAAc,YAAAF,CAAA,CAC3D,CAMO,SAASG,GACdzG,EACQ,CACR,OAAOkG,GAA+BlG,CAAe,EAAE,YACzD,CCnCA,MAAM0G,EAA2B,GAE3BC,GAAc,iBAKdC,EAA4B,MAyFlC,eAAsBC,GACpBzG,EAC6B,CAC7B,MAAM0E,EAAiBgC,EAAwB1G,EAAO,cAAc,EAE9D+C,EAAS,MAAM4D,4BAA0B,CAC7C,eAAgB3G,EAAO,iBACvB,gBAAiBA,EAAO,gBACxB,oBAAqBA,EAAO,oBAC5B,mBAAoBA,EAAO,mBAC3B,2BAA4BA,EAAO,2BACnC,UAAW,CAAC,GAAGA,EAAO,SAAS,EAC/B,eAAgBA,EAAO,eACvB,aAAc,CAAC,GAAGA,EAAO,YAAY,EACrC,QAASA,EAAO,QAChB,gBAAiBA,EAAO,gBACxB,oBAAqBA,EAAO,oBAC5B,cAAeA,EAAO,cACtB,YAAaA,EAAO,YACpB,QAASA,EAAO,QAChB,eAAA0E,CAAA,CACD,EAMKkC,EAAc,MAAM9D,GAAsBC,EAAQ/C,CAAM,EAKxD6G,EAASC,EAAAA,6BAA6B/D,EAAO,KAAK,EAKxD0B,GACEoC,EAAO,QACP9D,EAAO,WACPA,EAAO,kBACP/C,EACA0E,EACAmC,EAAO,QACPA,EAAO,QAAA,EAGT,MAAME,EAAmBF,EAAO,QAAQ,OACtC,CAACG,EAAKC,IAAMD,EAAM,OAAOC,EAAE,KAAK,EAChC,EAAA,EAKIC,EACJxC,IAAmB,OAAY3B,EAAO,WAAW,OAAS,KAE5D,MAAO,CACL,QAASA,EAAO,MAChB,iBAAAgE,EACA,WAAYhE,EAAO,WACnB,kBAAmBA,EAAO,kBAC1B,cAAeA,EAAO,cACtB,aAAcA,EAAO,aACrB,oBAAqBA,EAAO,oBAC5B,eAAAmE,EACA,YAAAN,CAAA,CAEJ,CAMO,SAASF,EACdhG,EACoB,CACpB,GAAIA,IAAU,OAAW,OACzB,MAAMyG,EACJzG,EAAM,WAAW,IAAI,GAAKA,EAAM,WAAW,IAAI,EAAIA,EAAM,MAAM,CAAC,EAAIA,EACtE,GACEyG,EAAQ,SAAWb,GACnB,CAACC,GAAY,KAAKY,CAAO,EAEzB,MAAM,IAAI,MACR,uCAAuCb,CAAwB,qCAAqCa,EAAQ,MAAM,EAAA,EAGtH,OAAOA,EAAQ,YAAA,CACjB,CAeA,eAAsBC,GACpBpH,EACwB,CAIxB,GACE,CAAC,OAAO,UAAUA,EAAO,aAAa,GACtCA,EAAO,cAAgB,GACvBA,EAAO,cAAgBwG,EAEvB,MAAM,IAAI,MACR,kBAAkBxG,EAAO,aAAa,+CAChBwG,CAAyB,GAAA,EASnD,MAAMzD,EAAS,MAAMsE,EAAAA,yBACnB,CACE,eAAgBrH,EAAO,eAAe,iBACtC,gBAAiBA,EAAO,eAAe,gBACvC,oBAAqBA,EAAO,eAAe,oBAC3C,mBAAoBA,EAAO,eAAe,mBAC1C,2BACEA,EAAO,eAAe,2BACxB,UAAW,CAAC,GAAGA,EAAO,eAAe,SAAS,EAC9C,eAAgBA,EAAO,eAAe,eACtC,aAAc,CAAC,GAAGA,EAAO,eAAe,YAAY,EACpD,QAASA,EAAO,eAAe,QAC/B,gBAAiBA,EAAO,eAAe,gBACvC,oBAAqBA,EAAO,eAAe,oBAC3C,cAAeA,EAAO,eAAe,cACrC,YAAaA,EAAO,eAAe,YACnC,QAASA,EAAO,eAAe,QAC/B,eAAgB0G,EACd1G,EAAO,eAAe,cAAA,CACxB,EAEFA,EAAO,cACPA,EAAO,oBACPA,EAAO,QAAA,EAGT,aAAMsH,GAAmBvE,EAAQ/C,CAAM,EAEhC,CACL,MAAO+C,EAAO,MACd,KAAMA,EAAO,KACb,kBAAmBA,EAAO,kBAC1B,WAAYA,EAAO,UAAA,CAEvB,CAOA,MAAMwE,GAA0B,EAoBhC,eAAeD,GACbvE,EAMA/C,EACe,CACf,MAAM2E,EAAU3E,EAAO,eAAe,iBAChCwH,EACJ7C,IAAY,EACR8C,EAAAA,wBACA9C,IAAY,GAAKA,IAAY,EAC3B+C,EAAAA,8BACA,OACR,GAAIF,IAAsB,OACxB,MAAM,IAAI,MAAM,gCAAgC7C,CAAO,gBAAgB,EAGzE,MAAMgD,yBAAuBhD,EAAS5B,EAAO,KAAK,EAElD,MAAMI,EAAS,MAAMC,EAAAA,qBAAqBuB,CAAO,EAC3CiD,EAAsBL,IAA2BpE,EAAS,EAAI,GAE9D0E,EAAUC,EAAAA,YAAY,QAAQlH,EAAAA,eAAemC,EAAO,KAAK,CAAC,EAEhE,GAAI8E,EAAQ,UAAYL,EACtB,MAAM,IAAI,MACR,oBAAoBK,EAAQ,OAAO,oCACblD,CAAO,cAAc6C,CAAiB,GAAA,EAGhE,GAAIK,EAAQ,WAAaE,oBACvB,MAAM,IAAI,MACR,qBAAqBF,EAAQ,QAAQ,0CACvBE,EAAAA,iBAAiB,GAAA,EAUnC,GAAIF,EAAQ,IAAI,SAAW,EACzB,MAAM,IAAI,MACR,gBAAgBA,EAAQ,IAAI,MAAM,8DAAA,EAItC,MAAMG,EAAqBF,EAAAA,YAAY,QACrClH,EAAAA,eAAeZ,EAAO,mBAAmB,CAAA,EACzC,MAAA,EACIiI,EAAYC,EAAAA,gBAChB,IAAI,WAAWL,EAAQ,IAAI,CAAC,EAAE,IAAI,EAAE,MAAA,EAAQ,QAAA,CAAQ,EAEtD,GAAII,IAAcD,EAChB,MAAM,IAAI,MACR,2BAA2BC,CAAS,mCAC/BD,CAAkB,GAAA,EAG3B,GAAIH,EAAQ,IAAI,CAAC,EAAE,QAAU7H,EAAO,SAClC,MAAM,IAAI,MACR,uCAAuC6H,EAAQ,IAAI,CAAC,EAAE,KAAK,sCACrB7H,EAAO,QAAQ,GAAA,EAGzD,GAAI6H,EAAQ,IAAI,CAAC,EAAE,WAAaM,EAAAA,qBAC9B,MAAM,IAAI,MACR,wBAAwBN,EAAQ,IAAI,CAAC,EAAE,QAAQ,0CACvBM,EAAAA,oBAAoB,GAAA,EAGhD,GAAIN,EAAQ,IAAI,CAAC,EAAE,OAAO,SAAW,EACnC,MAAM,IAAI,MAAM,sCAAsC,EAExD,GAAIA,EAAQ,IAAI,CAAC,EAAE,QAAQ,SAAW,EACpC,MAAM,IAAI,MAAM,mDAAmD,EAGrE,GAAIA,EAAQ,KAAK,SAAWD,EAC1B,MAAM,IAAI,MACR,gBAAgBC,EAAQ,KAAK,MAAM,gCAC9BD,CAAmB,yBAAyBjD,CAAO,4BACpCxB,EAAS,gBAAkB,EAAE,IAAA,EAIrD,MAAMiF,EAAkBpI,EAAO,eAAe,aAAaA,EAAO,QAAQ,EAC1E,GAAI+C,EAAO,aAAeqF,EACxB,MAAM,IAAI,MACR,4BAA4BrF,EAAO,UAAU,+CAChBqF,CAAe,aACvCpI,EAAO,QAAQ,0EAAA,EAKxB,MAAMqI,EAAkBR,EAAQ,KAAK,CAAC,EACtC,GAAI,OAAOQ,EAAgB,KAAK,IAAMtF,EAAO,WAC3C,MAAM,IAAI,MACR,oCAAoCsF,EAAgB,KAAK,gDACjBtF,EAAO,UAAU,GAAA,EAG7D,MAAMuF,EAAsB3F,GAC1B3C,EAAO,eACPA,EAAO,aAAA,EACP,SAAS,KAAK,EAChB,GACEY,EAAAA,eAAemC,EAAO,iBAAiB,EAAE,YAAA,IACzCuF,EAEA,MAAM,IAAI,MACR,yCAAyCvF,EAAO,iBAAiB,iEAE5DuF,CAAmB,GAAA,EAG5B,MAAMC,EAAqBF,EAAgB,OAAO,SAAS,KAAK,EAChE,GAAIE,IAAuBD,EACzB,MAAM,IAAI,MACR,2CAA2CC,CAAkB,iEAExDD,CAAmB,GAAA,EAI5B,MAAME,EAAcX,EAAQ,MAAA,EAC5B,GAAIW,IAAgB5H,EAAAA,eAAemC,EAAO,IAAI,EAAE,cAC9C,MAAM,IAAI,MACR,sBAAsByF,CAAW,0CACvBzF,EAAO,IAAI,GAAA,EAWzB,MAAME,EAAqB,MAAMC,EAAAA,qBAC/ByB,EACA3E,EAAO,eAAe,oBACtBA,EAAO,eAAe,2BAA2B,OACjDA,EAAO,eAAe,cACtBA,EAAO,eAAe,YACtBA,EAAO,eAAe,OAAA,EAElByI,EAAkBZ,EAAQ,KAAKhC,EAA0B,EAC/D,GAAI,OAAO4C,EAAgB,KAAK,IAAMxF,EACpC,MAAM,IAAI,MACR,8CAA8CwF,EAAgB,KAAK,0DAE9DxF,CAAkB,yBAAyB0B,CAAO,GAAA,EAI3D,MAAM+D,EAAsBrC,GAC1BrG,EAAO,eAAe,eAAA,EAExB,GAAI,CAACyI,EAAgB,OAAO,OAAOC,CAAmB,EACpD,MAAM,IAAI,MACR,qDACKD,EAAgB,OAAO,SAAS,KAAK,CAAC,2DAEtCC,EAAoB,SAAS,KAAK,CAAC,IAAA,CAG9C,CCpZA,eAAsBC,GACpB3I,EAC6B,CAE7B,MAAM4I,EAAY,MAAMC,EAAAA,sBACtB,CACE,eAAgB7I,EAAO,iBACvB,UAAWA,EAAO,UAClB,cAAeA,EAAO,cACtB,aAAcA,EAAO,aACrB,qBAAsBA,EAAO,qBAC7B,cAAeA,EAAO,aAAA,EAExBA,EAAO,OAAA,EAGT,MAAO,CACL,aAAc4I,EAAU,aACxB,kBAAmBA,EAAU,kBAC7B,aAAcA,EAAU,aACxB,QAASA,EAAU,QACnB,mBAAoBA,EAAU,kBAAA,CAElC,CC5JA,MAAME,EAA6B,IAC7BC,GAA6B,IAC7BC,GAA0B,MAC1BC,GAA0B,WAG1BC,GAAc,UAEdC,GAAgB,YAEhBC,GAAe,WAGfC,EAA2B,GAE3BC,EAAyB,GAEzBC,EAA0B,IAE1BC,GAA4B,EAE5BC,GAAkC,IAGlCC,GAA4B5I,EAAAA,OAAO,KAAK,CAAC,GAAM,EAAI,CAAC,EAO1D,SAAS6I,GAAkBC,EAAmB,CAC5C,GAAIA,EAAId,EACN,OAAOhI,SAAO,KAAK,CAAC8I,CAAC,CAAC,EAExB,GAAIA,GAAKZ,GAAyB,CAChC,MAAMtI,EAAQI,EAAAA,OAAO,MAAM,CAAC,EAC5B,OAAAJ,EAAM,cAAckJ,CAAC,EACd9I,EAAAA,OAAO,OAAO,CAACA,EAAAA,OAAO,KAAK,CAACgI,CAA0B,CAAC,EAAGpI,CAAK,CAAC,CACzE,CACA,GAAIkJ,GAAKX,GAAyB,CAChC,MAAMvI,EAAQI,EAAAA,OAAO,MAAM,CAAC,EAC5B,OAAAJ,EAAM,cAAckJ,CAAC,EACd9I,EAAAA,OAAO,OAAO,CAACA,EAAAA,OAAO,KAAK,CAACiI,EAA0B,CAAC,EAAGrI,CAAK,CAAC,CACzE,CACA,MAAM,IAAI,MAAM,8CAA8CkJ,CAAC,QAAQ,CACzE,CAMO,SAASC,EACdC,EACAC,EACQ,CACR,MAAMC,EAAWlJ,EAAAA,OAAO,OAAO,CAC7BA,SAAO,KAAK,CAACgJ,CAAW,CAAC,EACzBH,GAAkBI,EAAO,MAAM,EAC/BjJ,EAAAA,OAAO,KAAKiJ,CAAM,CAAA,CACnB,EACD,OAAOE,SAAQ,WAAWf,GAAac,CAAQ,CACjD,CAwBO,SAASE,GACdC,EACQ,CACR,KAAM,CAAE,YAAAL,EAAa,OAAAC,EAAQ,aAAA3D,CAAA,EAAiB+D,EAExCC,EAAUhE,EAAa,OAASiD,EACtC,GACEe,EAAU,GACVA,EAAUd,IAA2B,GACrCc,EAAUd,EAAyBC,EAEnC,MAAM,IAAI,MACR,2CAA2CnD,EAAa,MAAM,YACzDiD,CAAwB,0BAA0BE,CAAuB,EAAA,EAIlF,MAAMc,EAAqBjE,EAAa,CAAC,EAAIqD,GAC7C,GAAIY,IAAuBP,EACzB,MAAM,IAAI,MACR,wCAAwCO,EAAmB,SAAS,EAAE,CAAC,oDAClBP,EAAY,SAAS,EAAE,CAAC,EAAA,EAIjF,MAAM5D,EAAcpF,EAAAA,OAAO,KACzBsF,EAAa,SAAS,EAAGiD,CAAwB,CAAA,EAKnD,IAAIiB,EAAOT,EAAmBC,EAAaC,CAAM,EACjD,QACMQ,EAASlB,EACbkB,EAASnE,EAAa,OACtBmE,GAAUjB,EACV,CACA,MAAMkB,EAAU1J,EAAAA,OAAO,KACrBsF,EAAa,SAASmE,EAAQA,EAASjB,CAAsB,CAAA,EAE/DgB,EAAOL,EAAAA,OAAQ,WACbd,GACArI,EAAAA,OAAO,QAAQwJ,EAAME,CAAO,GAAK,EAC7B1J,EAAAA,OAAO,OAAO,CAACwJ,EAAME,CAAO,CAAC,EAC7B1J,EAAAA,OAAO,OAAO,CAAC0J,EAASF,CAAI,CAAC,CAAA,CAErC,CAEA,MAAMG,EAAQR,EAAAA,OAAQ,WACpBb,GACAtI,EAAAA,OAAO,OAAO,CAACoF,EAAaoE,CAAI,CAAC,CAAA,EAE7BI,EAAUC,GAAI,mBAAmBzE,EAAauE,CAAK,EACzD,GAAIC,IAAY,KACd,MAAM,IAAI,MACR,8EAAA,EAGJ,MAAME,EAAiBxE,EAAa,CAAC,EAAIoD,GACzC,GAAIkB,EAAQ,SAAWE,EACrB,MAAM,IAAI,MACR,6BAA6BF,EAAQ,MAAM,+CACnBE,CAAc,EAAA,EAI1C,OAAO9J,EAAAA,OAAO,OAAO,CACnB4I,GACA5I,SAAO,KAAK4J,EAAQ,WAAW,CAAA,CAChC,CACH,CCvJA,MAAMG,GAA0B,IAQ1BC,GAA4B,KAG5BC,EAA+B,IAM/BC,EAA+B,YAO/BC,GAAwB,IACxBC,GAAmC,GAGnCC,GAAkC,GAsBjC,SAASC,GAA0BpL,EAAmC,CAC3E,GACE,OAAOA,EAAO,iBAAoB,UAClCA,EAAO,iBAAmB,IAC1BA,EAAO,gBAAkBgL,EAEzB,MAAM,IAAI,MACR,kCAAkCA,CAA4B,iBACrDhL,EAAO,eAAe,EAAA,EAKnC,SAAW,CAACqL,EAAMC,EAAOC,EAAKC,CAAM,GAAK,CACvC,CACE,UACAxL,EAAO,gBACP6K,GACA,uBAAA,EAEF,CACE,cACA7K,EAAO,wBACP8K,GACA,kBAAA,CACF,EAEA,GAAI,CAAC,OAAO,UAAUQ,CAAK,GAAKA,EAAQ,GAAKA,EAAQC,EAEnD,MAAM,IAAI,MACR,yBAAyBF,CAAI,KAAKC,CAAK,wCACxBC,CAAG,IAAID,EAAQC,EAAM,KAAKC,CAAM,IAAM,EAAE,GAAA,EAM7D,GACE,CAAC,OAAO,UAAUxL,EAAO,WAAW,GACpCA,EAAO,YAAc,GACrBA,EAAO,YAAc+K,EAErB,MAAM,IAAI,MACR,yCACSA,CAA4B,UAAU/K,EAAO,WAAW,EAAA,CAGvE,CAQA,eAAsByL,GACpBzL,EACA0L,EAKe,CACf,KAAM,CAAE,gBAAAC,EAAiB,QAAAC,EAAS,QAAAC,CAAA,EAAYH,EACxCI,EACJ9L,EAAO,gBAAkBA,EAAO,wBAC5B+L,EAAc,OAAOJ,CAAe,EAKpCK,EAAe,KAAK,IAAI,EAAGJ,EAAUT,EAA+B,EACpEc,EACJhB,GACAC,GAAmCY,EACnCE,EACIE,EAAalM,EAAO,gBAAkB,OAAOiM,CAAc,EACjE,GAAIF,EAAcG,EAChB,MAAM,IAAI,MACR,uBAAuBP,CAAe,mCAC9BO,CAAU,UAAUlM,EAAO,eAAe,aAC7CiM,CAAc,WAAWH,CAAe,0CAAA,EAOjD,MAAMK,EAAa,MAAMC,EAAAA,sBACvBpM,EAAO,iBACPA,EAAO,gBACPA,EAAO,wBACPA,EAAO,gBACPA,EAAO,YACP4L,EACAC,EACA7L,EAAO,eAAA,EAET,GAAI+L,EAAcI,EAChB,MAAM,IAAI,MACR,uBAAuBR,CAAe,+BACjCQ,CAAU,2GAAA,CAIrB,CC7IA,MAAME,GAAa,GAEbC,GAAY,GAEZC,GAAW,GAEXC,GAAY,GAEZC,GAAW,GAEjB,SAASC,GAASC,EAAoB,CACpC,OAAOA,EAAE,SAAWN,GAAa,GAAKM,EAAE,WAAW,MAAM,CAC3D,CACA,SAASC,GAAQD,EAAoB,CACnC,OAAOA,EAAE,SAAWL,GAAY,GAAKK,EAAE,WAAW,MAAM,CAC1D,CACA,SAASE,GAAOF,EAAoB,CAClC,OAAOA,EAAE,SAAWJ,GAAW,GAAKI,EAAE,WAAW,MAAM,CACzD,CACA,SAASG,GAAQH,EAAoB,CACnC,OACEA,EAAE,SAAWH,GAAY,GAAKG,EAAE,WAAW,QAAQ,GAAKA,EAAE,SAAS,MAAM,CAE7E,CACA,SAASI,GAAOJ,EAAoB,CAClC,OAAOA,EAAE,SAAWF,GAAW,GAAKE,EAAE,WAAW,MAAM,GAAKA,EAAE,SAAS,IAAI,CAC7E,CAUO,SAASK,EACdC,EACAtM,EACM,CACN,MAAMoJ,EAASnJ,EAAAA,eAAeqM,CAAS,EAAE,YAAA,EAEzC,GAAIlD,EAAO,SAAW,EACpB,MAAM,IAAI,MAAM,GAAGpJ,CAAK,WAAW,EAErC,GAAI,CAAC,cAAc,KAAKoJ,CAAM,GAAKA,EAAO,OAAS,IAAM,EACvD,MAAM,IAAI,MAAM,GAAGpJ,CAAK,mBAAmB,EAE7C,GAAIoJ,EAAO,WAAW,IAAI,EACxB,MAAM,IAAI,MACR,GAAGpJ,CAAK,wDAAA,EAIZ,GACE,CAACkM,GAAO9C,CAAM,GACd,CAAC2C,GAAS3C,CAAM,GAChB,CAAC6C,GAAQ7C,CAAM,GACf,CAAC+C,GAAQ/C,CAAM,GACf,CAACgD,GAAOhD,CAAM,EAEd,MAAM,IAAI,MACR,GAAGpJ,CAAK,sFACgDoJ,EAAO,OAAS,CAAC,WAAWA,CAAM,GAAA,CAGhG,CC3CA,MAAMmD,EAAwC,EAmM9C,eAAsBC,GACpBnN,EAC2B,CAC3B,MAAMoN,EAAgB,CACpB,iBAAkBpN,EAAO,iBACzB,gBAAiBA,EAAO,sBAAsB,OAC9C,wBAAyBA,EAAO,8BAA8B,OAC9D,YAAaA,EAAO,eAAe,OACnC,gBAAiBA,EAAO,eAAA,EAE1BoL,GAA0BgC,CAAa,EAEvC,MAAMC,EAAWvF,EAAAA,YAAY,QAAQlH,EAAAA,eAAeZ,EAAO,WAAW,CAAC,EACjE6H,EAAUC,EAAAA,YAAY,QAAQlH,EAAAA,eAAeZ,EAAO,UAAU,CAAC,EAC/DsN,EAAWxF,EAAAA,YAAY,QAAQlH,EAAAA,eAAeZ,EAAO,WAAW,CAAC,EAEvE,GAAIqN,EAAS,IAAI,SAAWE,+BAC1B,MAAM,IAAI,MACR,wCAAwCA,EAAAA,4BAA4B,gBACnDF,EAAS,IAAI,MAAM,EAAA,EAKxC,GAAIA,EAAS,UAAYG,oBACvB,MAAM,IAAI,MACR,8BAA8BH,EAAS,OAAO,YACzCG,EAAAA,iBAAiB,4BAAA,EAG1B,GAAIH,EAAS,WAAaI,qBACxB,MAAM,IAAI,MACR,+BAA+BJ,EAAS,QAAQ,YAC3CI,EAAAA,kBAAkB,4BAAA,EAK3B,MAAMC,EAAeC,EACnBN,EAAS,IAAI,CAAC,EACd,EACAxF,EACA,QACA+F,EAAAA,wBAAA,EAEF,GAAIP,EAAS,IAAI,CAAC,EAAE,WAAarN,EAAO,cACtC,MAAM,IAAI,MACR,2BAA2BqN,EAAS,IAAI,CAAC,EAAE,QAAQ,sCAC3BrN,EAAO,aAAa,4BAAA,EAIhD,MAAM6N,EAAgBF,EACpBN,EAAS,IAAI,CAAC,EACd,EACAC,EACA,SACAQ,EAAAA,0BAAA,EAEF,GAAIT,EAAS,IAAI,CAAC,EAAE,WAAarN,EAAO,eACtC,MAAM,IAAI,MACR,2BAA2BqN,EAAS,IAAI,CAAC,EAAE,QAAQ,uCAC1BrN,EAAO,cAAc,4BAAA,EAWlD,KAAM,CAAE,QAAA4L,EAAS,QAAAC,CAAA,EAAYkC,GAC3BV,EACAK,EAAa,MACb1N,CAAA,EAGIgO,EAAiBN,EAAa,MAAQG,EAAc,MAC1D,IAAII,EAAkB,EACtB,UAAWC,KAAOb,EAAS,KAAMY,GAAmBC,EAAI,MACxD,GAAID,EAAkBD,EACpB,MAAM,IAAI,MACR,mBAAmBC,CAAe,yBAC5BD,CAAc,8BAAA,EAGxB,MAAMrC,EAAkBqC,EAAiBC,EACzC,MAAMxC,GAAsB2B,EAAe,CACzC,gBAAAzB,EACA,QAAAC,EACA,QAAAC,CAAA,CACD,EAID,MAAMsC,EAAkB,MAAMxF,GAAmB,CAC/C,iBAAkB3I,EAAO,iBACzB,UAAWA,EAAO,mBAClB,cAAeA,EAAO,uBACtB,aAAcA,EAAO,sBACrB,qBAAsBA,EAAO,8BAC7B,cAAeA,EAAO,cACtB,QAASA,EAAO,OAAA,CACjB,EAEKoO,EAAmB,MAAMC,GAAwBrO,EAAQ6N,CAAa,EAE5E,MAAO,CACL,QAASS,GACPjB,EACAK,EACAG,EACAM,EACAC,CAAA,CACF,CAEJ,CAqBA,eAAeC,GACbrO,EACA6N,EAC2B,CAC3B,KAAM,CAAE,aAAAhL,EAAc,mBAAA0L,CAAA,EAAuB,MAAMC,EAAAA,0BAA0B,CAC3E,eAAgBxO,EAAO,iBACvB,QAASY,EAAAA,eAAeZ,EAAO,gBAAgB,EAAE,YAAA,EACjD,iBAAkBD,GAAuB,CACvC,iBAAkBC,EAAO,iBACzB,mBAAoBA,EAAO,mBAC3B,uBAAwBA,EAAO,uBAC/B,sBAAuBA,EAAO,qBAAA,CAC/B,EACD,qBAAsBA,EAAO,8BAA8B,IAAKK,GAC9DO,EAAAA,eAAeP,CAAC,EAAE,YAAA,CAAY,EAEhC,eAAgBL,EAAO,eACvB,eAAgBA,EAAO,eAAe,IAAKK,GACzCO,EAAAA,eAAeP,CAAC,EAAE,YAAA,CAAY,EAEhC,cAAeL,EAAO,aAAA,CACvB,EAEK+J,EAAS9D,EAAAA,gBAAgBpD,CAAY,EACrCuD,EAAeH,EAAAA,gBAAgBsI,CAAkB,EACjDE,EAAoBvE,GAA2B,CACnD,YAAajI,EAAAA,uBACb,OAAA8H,EACA,aAAA3D,CAAA,CACD,EACD,GAAI,CAACyH,EAAc,OAAO,OAAOY,CAAiB,EAChD,MAAM,IAAI,MACR,iGAC+BA,EAAkB,SAAS,KAAK,CAAC,YACpDX,4BAA0B,SACjCD,EAAc,OAAO,SAAS,KAAK,CAAC,4BAAA,EAI7C,MAAO,CAAE,OAAA9D,EAAQ,aAAA3D,CAAA,CACnB,CAUA,SAASuH,EACPe,EACAC,EACAC,EACAC,EACAC,EACU,CACV,MAAM7G,EAAYC,EAAAA,gBAChB,IAAI,WAAWwG,EAAM,IAAI,EAAE,MAAA,EAAQ,QAAA,CAAQ,EAEvCK,EAAaH,EAAS,MAAA,EAC5B,GAAI3G,IAAc8G,GAAcL,EAAM,QAAUI,EAC9C,MAAM,IAAI,MACR,SAASH,CAAU,eAAeE,CAAW,IAAIC,CAAY,cAC/CC,CAAU,IAAID,CAAY,SAAS7G,CAAS,IAAIyG,EAAM,KAAK,EAAA,EAG7E,MAAMM,EAAUJ,EAAS,KAAKF,EAAM,KAAK,EACzC,GAAI,CAACM,EACH,MAAM,IAAI,MACR,uCAAuCL,CAAU,WACrC1G,CAAS,YAAYyG,EAAM,KAAK,GAAA,EAGhD,OAAOM,CACT,CAWA,SAASV,GACPjB,EACAK,EACAG,EACAM,EACAC,EACQ,CACR,MAAMa,EAAO,IAAIC,OACjBD,EAAK,WAAW5B,EAAS,OAAO,EAChC4B,EAAK,YAAY5B,EAAS,QAAQ,EAElC,KAAM,CAAC8B,EAAQC,CAAM,EAAI/B,EAAS,IAGlC4B,EAAK,SAAS,CACZ,KAAME,EAAO,KACb,MAAOA,EAAO,MACd,SAAUA,EAAO,SACjB,YAAa,CACX,OAAQzB,EAAa,OACrB,MAAOA,EAAa,KAAA,EAEtB,cAAe,CACb,CACE,YAAazL,EAAAA,uBACb,OAAQnB,EAAAA,OAAO,KAAKmF,EAAAA,gBAAgBkI,EAAgB,YAAY,CAAC,EACjE,aAAcrN,EAAAA,OAAO,KACnBmF,EAAAA,gBAAgBkI,EAAgB,kBAAkB,CAAA,CACpD,CACF,EAEF,eAAgBrN,EAAAA,OAAO,KAAKwB,EAAAA,iBAAiB,CAAA,CAE9C,EAID2M,EAAK,SAAS,CACZ,KAAMG,EAAO,KACb,MAAOA,EAAO,MACd,SAAUA,EAAO,SACjB,YAAa,CACX,OAAQvB,EAAc,OACtB,MAAOA,EAAc,KAAA,EAEvB,cAAe,CACb,CACE,YAAa5L,EAAAA,uBACb,OAAQnB,EAAAA,OAAO,KAAKsN,EAAiB,MAAM,EAC3C,aAActN,EAAAA,OAAO,KAAKsN,EAAiB,YAAY,CAAA,CACzD,EAEF,eAAgBtN,EAAAA,OAAO,KAAKwB,EAAAA,iBAAiB,CAAA,CAC9C,EAED,UAAWH,KAAUkL,EAAS,KAC5B4B,EAAK,UAAU,CACb,OAAQ9M,EAAO,OACf,MAAOA,EAAO,KAAA,CACf,EAGH,OAAO8M,EAAK,MAAA,CACd,CA2DA,SAASI,EACPC,EACAC,EACU,CACV,MAAMC,EAAa5O,EAAAA,eAAe0O,CAAsB,EAAE,YAAA,EACpDG,EAAc7O,EAAAA,eAClBgF,EAAAA,2BAA2B2J,CAAY,CAAA,EACvC,YAAA,EACF,OAAOC,IAAeC,EAAc,CAACD,CAAU,EAAI,CAACA,EAAYC,CAAW,CAC7E,CAGA,SAASC,EAAiB3F,EAAgB4F,EAAkC,CAC1E,OAAOA,EAAc,KAAMC,GAAQ7F,EAAO,OAAOjJ,EAAAA,OAAO,KAAK8O,EAAK,KAAK,CAAC,CAAC,CAC3E,CAsCA,SAAS7B,GACPV,EACAwC,EACA7P,EACkD,CAClD,KAAM,CACJ,iBAAA8P,EACA,uBAAAC,EACA,mBAAAC,EACA,sBAAAC,EACA,6BAAAC,EACA,cAAAC,EACA,6BAAAC,EACA,yBAAAC,CAAA,EACErQ,EAEJ,GAAI,CAACsQ,EAAAA,WAAWJ,CAA4B,EAC1C,MAAM,IAAI,MAAM,qDAAqD,EAGvE,MAAMjQ,EAAUW,EAAAA,eAAekP,CAAgB,EAAE,YAAA,EAC3CS,EAAK3P,EAAAA,eAAemP,CAAsB,EAAE,YAAA,EAC5CS,EAAM5P,EAAAA,eAAeoP,CAAkB,EAAE,YAAA,EACzCS,EAAUR,EAAsB,IAAK5P,GACzCO,EAAAA,eAAeP,CAAC,EAAE,YAAA,CAAY,EAIhC,IAAIgL,EACAqF,EACAC,EAEJ,GAAI1Q,IAAYsQ,EACdlF,EAAO,aACPqF,EAAmBE,EAAAA,+BACnBD,EAA0B,CAAC/P,iBAAesP,CAA4B,CAAC,UAC9DjQ,IAAYuQ,EACrBnF,EAAO,uBACPqF,EAAmBG,EAAAA,mCACnBF,EAA0B,CAAC/P,iBAAesP,CAA4B,CAAC,UAC9DO,EAAQ,SAASxQ,CAAO,EAAG,CACpCoL,EAAO,aACPqF,EAAmBG,EAAAA,mCAUnB,MAAMrB,EAAaY,EAA6BnQ,CAAO,EACvD,GAAIuP,IAAe,OACjB,MAAM,IAAI,MACR,iEAAiEvP,CAAO,EAAA,EAG5E+M,EACEwC,EACA,0CAA0CvP,CAAO,EAAA,EAEnD0Q,EAA0BtB,EAA0BG,EAAYvP,CAAO,CACzE,KACE,OAAM,IAAI,MACR,0BAA0BA,CAAO,mDAAA,EAIrC,GAAIoN,EAAS,KAAK,SAAWqD,EAC3B,MAAM,IAAI,MACR,0BAA0BrD,EAAS,KAAK,MAAM,gCACxBqD,CAAgB,aAAarF,CAAI,GAAA,EAI3D,GAAI,CAACqE,EAAiBrC,EAAS,KAAK,CAAC,EAAE,OAAQsD,CAAuB,EACpE,MAAM,IAAI,MACR,+EAA+EtF,CAAI,eACpEsF,EAAwB,KAAK,MAAM,CAAC,SAC1CtD,EAAS,KAAK,CAAC,EAAE,OAAO,SAAS,KAAK,CAAC,EAAA,EAIpD,MAAMyD,EAAYJ,EAAmB,EACrC,GAAIrD,EAAS,KAAKyD,CAAS,EAAE,QAAUC,EAAAA,wBACrC,MAAM,IAAI,MACR,2BAA2BD,CAAS,WAAWzD,EAAS,KAAKyD,CAAS,EAAE,KAAK,oBAC7DC,EAAAA,uBAAuB,OAAA,EAI3C,GAAI1F,IAAS,aAAc,CAIzB2B,EACEqD,EACA,yCAAA,EAEF,MAAMW,EAAgC3B,EACpCgB,EACAE,CAAA,EAEF,GACE,CAACb,EAAiBrC,EAAS,KAAK,CAAC,EAAE,OAAQ2D,CAA6B,EAExE,MAAM,IAAI,MACR,oGACeA,EAA8B,KAAK,MAAM,CAAC,SAChD3D,EAAS,KAAK,CAAC,EAAE,OAAO,SAAS,KAAK,CAAC,EAAA,EAQpD,GACE,CAAC,OAAO,UAAU8C,CAAa,GAC/BA,EAAgB,GAChBA,GAAiBc,kCAEjB,MAAM,IAAI,MACR,2CACSA,EAAAA,+BAA+B,UAAUd,CAAa,EAAA,EAGnE,MAAMe,EAAoB,KAAK,MAC5BrB,EAAiBM,EAAiBgB,EAAAA,eAAA,EAErC,GAAI9D,EAAS,KAAK,CAAC,EAAE,MAAQ6D,EAC3B,MAAM,IAAI,MACR,sCAAsC7D,EAAS,KAAK,CAAC,EAAE,KAAK,qBAC3C6D,CAAiB,UAC5Bf,CAAa,sBAAsBN,CAAc,GAAA,CAG7D,CAKA,MAAMjE,EAAUyB,EAAS,KAAK,CAAC,EAAE,OAAO,OACxC,GAAIzB,IAAY,GAAKA,EAAUwF,wBAC7B,MAAM,IAAI,MACR,uCAAuCxF,CAAO,mDACTwF,EAAAA,qBAAqB,6BAAA,EAU9D,MAAMvF,GACJR,IAAS,aAAegC,EAAS,KAAK,CAAC,EAAE,OAAO,OAAS,OAE3D,MAAO,CAAE,QAAAzB,EAAS,QAAAC,EAAA,CACpB,CAqBO,SAASwF,GACdC,EACA1R,EACA+O,EAAa,EACL,CACR,MAAM4C,EAAarC,EAAAA,KAAK,QAAQoC,CAAa,EAE7C,GAAI3C,GAAc4C,EAAW,KAAK,OAAO,OACvC,MAAM,IAAI,MACR,eAAe5C,CAAU,kBAAkB4C,EAAW,KAAK,OAAO,MAAM,UAAA,EAI5E,MAAM7C,EAAQ6C,EAAW,KAAK,OAAO5C,CAAU,EAG/C,GAAID,EAAM,cAAgBA,EAAM,aAAa,OAAS,EAAG,CACvD,MAAM8C,EAAuBvL,EAAAA,gBAAgBrG,CAAe,EAE5D,UAAW6R,KAAY/C,EAAM,aAC3B,GAAI+C,EAAS,OAAO,OAAO3Q,EAAAA,OAAO,KAAK0Q,CAAoB,CAAC,EAC1D,OAAOE,EAAkBD,EAAS,UAAW9C,CAAU,EAI3D,MAAM,IAAI,MACR,4CAA4C/O,CAAe,aAAa+O,CAAU,EAAA,CAEtF,CAQA,GAAID,EAAM,oBAAsBA,EAAM,mBAAmB,OAAS,EAAG,CACnE,MAAMiD,EAAeC,GAAkBlD,EAAM,kBAAkB,EAC/D,GAAIiD,EAAa,SAAWzE,EAC1B,MAAM,IAAI,MACR,oDAAoDyB,CAAU,cAChDzB,CAAqC,iDAC1CyE,EAAa,MAAM,EAAA,EAGhC,OAAOD,EAAkBC,EAAa,CAAC,EAAGhD,CAAU,CACtD,CAEA,MAAM,IAAI,MACR,uEAAuEA,CAAU,EAAA,CAErF,CASA,SAAS+C,EAAkBG,EAAiBlD,EAA4B,CACtE,GAAIkD,EAAI,SAAW,GACjB,OAAO3J,kBAAgB,IAAI,WAAW2J,CAAG,CAAC,EAE5C,MAAIA,EAAI,SAAW,GACX,IAAI,MACR,6BAA6BA,EAAI,EAAE,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,aAAalD,CAAU,6DAAA,EAIvF,IAAI,MACR,wCAAwCA,CAAU,KAAKkD,EAAI,MAAM,EAAA,CAErE,CAUA,SAASD,GAAkBxP,EAA2B,CACpD,MAAM0P,EAAkB,CAAA,EACxB,IAAIvH,EAAS,EAEb,MAAMwH,EAAgB,GAAoB,CACxC,GAAIxH,EAAS,EAAInI,EAAQ,OACvB,MAAM,IAAI,MACR,gCAAgC,CAAC,sBAAsBmI,CAAM,UAAUnI,EAAQ,OAASmI,CAAM,YAAA,CAGpG,EAEMyH,EAAa,IAAc,CAC/BD,EAAa,CAAC,EACd,MAAME,EAAQ7P,EAAQmI,GAAQ,EAC9B,GAAI0H,EAAQ,IAAM,OAAOA,EACzB,GAAIA,IAAU,IAAM,CAClBF,EAAa,CAAC,EACd,MAAMG,GAAO9P,EAAQmI,CAAM,EAAKnI,EAAQmI,EAAS,CAAC,GAAK,KAAQ,EAC/D,OAAAA,GAAU,EACH2H,CACT,CACA,GAAID,IAAU,IAAM,CAClBF,EAAa,CAAC,EACd,MAAMG,GACH9P,EAAQmI,CAAM,EACZnI,EAAQmI,EAAS,CAAC,GAAK,EACvBnI,EAAQmI,EAAS,CAAC,GAAK,GACvBnI,EAAQmI,EAAS,CAAC,GAAK,MAC1B,EACF,OAAAA,GAAU,EACH2H,CACT,CAIA,MAAM,IAAI,MACR,wEAAwE3H,EAAS,CAAC,EAAA,CAEtF,EAEMe,EAAQ0G,EAAA,EACd,QAASrO,EAAI,EAAGA,EAAI2H,EAAO3H,IAAK,CAC9B,MAAMwO,EAAMH,EAAA,EACZD,EAAaI,CAAG,EAChBL,EAAM,KAAKhR,EAAAA,OAAO,KAAKsB,EAAQ,SAASmI,EAAQA,EAAS4H,CAAG,CAAC,CAAC,EAC9D5H,GAAU4H,CACZ,CAEA,GAAI5H,IAAWnI,EAAQ,OACrB,MAAM,IAAI,MACR,2BAA2BA,EAAQ,OAASmI,CAAM,mCAAmCe,CAAK,UAAA,EAI9F,OAAOwG,CACT,CC37BO,MAAMM,UAA8B,KAAM,CAC/C,YAAYC,EAAgB,CAC1B,MACE,uDAAuDA,CAAM,EAAA,EAE/D,KAAK,KAAO,uBACd,CACF,CASA,SAASC,EAAU3R,EAAiCiP,EAAmB,CACrE,GAAI,CACF,OAAOV,EAAAA,KAAK,QAAQU,CAAG,CACzB,OAAS2C,EAAO,CACd,MAAM/G,EAAS+G,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACpE,MAAM,IAAI,MAAM,mBAAmB5R,CAAK,UAAU6K,CAAM,EAAE,CAC5D,CACF,CAOA,MAAMgH,GAA0B,EAEhC,SAASC,EAAUC,EAAqB,CACtC,MAAO,GAAGA,EAAI,SAAS,KAAK,EAAE,MAAM,EAAGF,EAAuB,CAAC,GACjE,CAQA,SAASG,EAAWC,EAA8B,CAChD,MAAMC,EAAW/R,EAAAA,OAAO,KAAK8R,CAAY,EAAE,QAAA,EAC3C,OAAOH,EAAUI,CAAQ,CAC3B,CASO,SAASC,GACd9S,EACM,CACN,MAAM4D,EAAY0O,EAAU,YAAatS,EAAO,gBAAgB,EAC1D+S,EAAWT,EAAU,WAAYtS,EAAO,eAAe,EAE7D,GAAI4D,EAAU,UAAYmP,EAAS,QACjC,MAAM,IAAIX,EACR,iCAAiCxO,EAAU,OAAO,cAAcmP,EAAS,OAAO,GAAA,EAGpF,GAAInP,EAAU,WAAamP,EAAS,SAClC,MAAM,IAAIX,EACR,kCAAkCxO,EAAU,QAAQ,cAAcmP,EAAS,QAAQ,GAAA,EAGvF,GAAInP,EAAU,SAAS,SAAWmP,EAAS,SAAS,OAClD,MAAM,IAAIX,EACR,kCAAkCxO,EAAU,SAAS,MAAM,cAAcmP,EAAS,SAAS,MAAM,GAAA,EAGrG,GAAInP,EAAU,UAAU,SAAWmP,EAAS,UAAU,OACpD,MAAM,IAAIX,EACR,mCAAmCxO,EAAU,UAAU,MAAM,cAAcmP,EAAS,UAAU,MAAM,GAAA,EAGxG,QAASpP,EAAI,EAAGA,EAAIC,EAAU,SAAS,OAAQD,IAAK,CAClD,MAAMqP,EAAIpP,EAAU,SAASD,CAAC,EACxB,EAAIoP,EAAS,SAASpP,CAAC,EAC7B,GAAI,CAACqP,EAAE,KAAK,OAAO,EAAE,IAAI,EACvB,MAAM,IAAIZ,EACR,SAASzO,CAAC,oCAAoCgP,EAAWK,EAAE,IAAI,CAAC,cAAcL,EAAW,EAAE,IAAI,CAAC,GAAA,EAGpG,GAAIK,EAAE,QAAU,EAAE,MAChB,MAAM,IAAIZ,EACR,SAASzO,CAAC,oCAAoCqP,EAAE,KAAK,cAAc,EAAE,KAAK,GAAA,EAG9E,GAAIA,EAAE,WAAa,EAAE,SACnB,MAAM,IAAIZ,EACR,SAASzO,CAAC,gCAAgCqP,EAAE,QAAQ,cAAc,EAAE,QAAQ,GAAA,CAGlF,CACA,QAASrP,EAAI,EAAGA,EAAIC,EAAU,UAAU,OAAQD,IAAK,CACnD,MAAMqP,EAAIpP,EAAU,UAAUD,CAAC,EACzB,EAAIoP,EAAS,UAAUpP,CAAC,EAC9B,GAAI,CAACqP,EAAE,OAAO,OAAO,EAAE,MAAM,EAC3B,MAAM,IAAIZ,EACR,UAAUzO,CAAC,oCAAoC8O,EAAUO,EAAE,MAAM,CAAC,cAAcP,EAAU,EAAE,MAAM,CAAC,GAAA,EAGvG,GAAIO,EAAE,QAAU,EAAE,MAChB,MAAM,IAAIZ,EACR,UAAUzO,CAAC,6BAA6BqP,EAAE,KAAK,cAAc,EAAE,KAAK,GAAA,CAG1E,CACF,CC/DO,SAASC,GACdjT,EACM,CACN,KAAM,CAAE,iBAAAkT,EAAkB,aAAAC,EAAc,qBAAAC,EAAsB,WAAAzE,GAC5D3O,EAEIqT,EAAezS,EAAAA,eAAeuS,CAAY,EAChD,GAAIE,EAAa,SAAWC,sBAC1B,MAAM,IAAI,MACR,+BAA+B3E,CAAU,YAAY2E,EAAAA,mBAAmB,8BACnDD,EAAa,MAAM,GAAA,EAI5C,MAAME,EAAc3S,EAAAA,eAAewS,CAAoB,EACvD,GAAIG,EAAY,SAAWC,wBACzB,MAAM,IAAI,MACR,kCAAkC7E,CAAU,YAAY6E,EAAAA,qBAAqB,8BACxDD,EAAY,MAAM,GAAA,EAI3C,MAAMtE,EAAOC,EAAAA,KAAK,QAAQgE,CAAgB,EAE1C,GAAIvE,EAAa,GAAKA,GAAcM,EAAK,KAAK,OAAO,OACnD,MAAM,IAAI,MACR,eAAeN,CAAU,kBAAkBM,EAAK,KAAK,OAAO,MAAM,WAAA,EAOtE,MAAMwE,EAA2B,CAAA,EAC3BzS,EAAmB,CAAA,EACzB,QAAS2C,EAAI,EAAGA,EAAIsL,EAAK,KAAK,OAAO,OAAQtL,IAAK,CAChD,MAAM+P,EAAczE,EAAK,KAAK,OAAOtL,CAAC,EAAE,YACxC,GAAI,CAAC+P,EACH,MAAM,IAAI,MACR,kCAAkC/P,CAAC,wFAAA,EAIvC8P,EAAe,KAAKC,EAAY,MAAM,EACtC1S,EAAO,KAAK0S,EAAY,KAAK,CAC/B,CAKA,MAAMC,EAAiB1E,EAAK,KAAK,OAAON,CAAU,EAAE,cACpD,GAAI,CAACgF,GAAkBA,EAAe,SAAW,EAC/C,MAAM,IAAI,MACR,kCAAkChF,CAAU,oEAChBgF,GAAA,YAAAA,EAAgB,SAAU,CAAC,GAAA,EAG3D,MAAMC,EAAOD,EAAe,CAAC,EAC7B,GAAIC,EAAK,cAAgB3R,yBACvB,MAAM,IAAI,MACR,kCAAkC0M,CAAU,qCACrCiF,EAAK,YAAY,SAAS,EAAE,CAAC,gBAAgB3R,EAAAA,uBAAuB,SAAS,EAAE,CAAC,GAAA,EAI3F,MAAM4R,EAAWhK,EAAmB+J,EAAK,YAAaA,EAAK,MAAM,EAK3DE,EAAK,IAAIhM,cACfgM,EAAG,QAAU7E,EAAK,QAClB6E,EAAG,SAAW7E,EAAK,SACnB,UAAWP,KAASO,EAAK,SACvB6E,EAAG,SAASpF,EAAM,KAAMA,EAAM,MAAOA,EAAM,QAAQ,EAErD,UAAWvM,KAAU8M,EAAK,UACxB6E,EAAG,UAAU3R,EAAO,OAAQA,EAAO,KAAK,EAG1C,MAAM4R,EAAUD,EAAG,iBACjBnF,EACA8E,EACAzS,EACA8G,EAAAA,YAAY,gBACZ+L,CAAA,EASF,GAAI,CANYlJ,GAAI,cAClBoJ,EACA9N,EAAAA,gBAAgBsN,CAAW,EAC3BtN,EAAAA,gBAAgBoN,CAAY,CAAA,EAI5B,MAAM,IAAI,MACR,+BAA+B1E,CAAU,YAAY4E,CAAW,wLAAA,CAKtE"}