@atomicfinance/bitcoin-wallet-provider 4.0.0 → 4.0.2

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 (227) hide show
  1. package/.turbo/turbo-lint.log +1 -0
  2. package/.yalc/@node-dlc/core/.nyc_output/687a3c14-9765-45fd-a1fe-f7db3d56fee4.json +1 -0
  3. package/.yalc/@node-dlc/core/.nyc_output/processinfo/687a3c14-9765-45fd-a1fe-f7db3d56fee4.json +1 -0
  4. package/.yalc/@node-dlc/core/.nyc_output/processinfo/index.json +1 -0
  5. package/.yalc/@node-dlc/core/README.md +11 -0
  6. package/.yalc/@node-dlc/core/__tests__/core.spec.ts +7 -0
  7. package/.yalc/@node-dlc/core/__tests__/dlc/CETCalculator.spec.ts +1029 -0
  8. package/.yalc/@node-dlc/core/__tests__/dlc/CoinSelect.spec.ts +179 -0
  9. package/.yalc/@node-dlc/core/__tests__/dlc/PolynomialPayoutCurve.spec.ts +345 -0
  10. package/.yalc/@node-dlc/core/__tests__/dlc/TxBuilder.spec.ts +424 -0
  11. package/.yalc/@node-dlc/core/__tests__/dlc/finance/Builder.spec.ts +492 -0
  12. package/.yalc/@node-dlc/core/__tests__/dlc/finance/CoveredCall.spec.ts +112 -0
  13. package/.yalc/@node-dlc/core/__tests__/dlc/finance/CsoInfo.spec.ts +1124 -0
  14. package/.yalc/@node-dlc/core/__tests__/dlc/finance/LongCall.spec.ts +55 -0
  15. package/.yalc/@node-dlc/core/__tests__/dlc/finance/LongPut.spec.ts +55 -0
  16. package/.yalc/@node-dlc/core/__tests__/dlc/finance/OptionInfo.spec.ts +226 -0
  17. package/.yalc/@node-dlc/core/__tests__/dlc/finance/ShortPut.spec.ts +62 -0
  18. package/.yalc/@node-dlc/core/__tests__/tsconfig.json +8 -0
  19. package/.yalc/@node-dlc/core/__tests__/utils/precision.spec.ts +40 -0
  20. package/.yalc/@node-dlc/core/coverage/lcov-report/base.css +224 -0
  21. package/.yalc/@node-dlc/core/coverage/lcov-report/block-navigation.js +79 -0
  22. package/.yalc/@node-dlc/core/coverage/lcov-report/favicon.png +0 -0
  23. package/.yalc/@node-dlc/core/coverage/lcov-report/index.html +171 -0
  24. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/AsyncProcessingQueue.ts.html +269 -0
  25. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/Base32.ts.html +86 -0
  26. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/BigIntUtils.ts.html +86 -0
  27. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/BitField.ts.html +86 -0
  28. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/ChannelId.ts.html +86 -0
  29. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/LinkedList.ts.html +413 -0
  30. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/LinkedListNode.ts.html +122 -0
  31. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/Queue.ts.html +188 -0
  32. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/ShortChannelId.ts.html +86 -0
  33. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/ShortChannelIdUtils.ts.html +107 -0
  34. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/CETCalculator.ts.html +1430 -0
  35. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/CoinSelect.ts.html +431 -0
  36. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/HyperbolaPayoutCurve.ts.html +644 -0
  37. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/PolynomialPayoutCurve.ts.html +878 -0
  38. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/TxBuilder.ts.html +1088 -0
  39. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/TxFinalizer.ts.html +731 -0
  40. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/Builder.ts.html +2270 -0
  41. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/CoveredCall.ts.html +410 -0
  42. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/CsoInfo.ts.html +1574 -0
  43. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/LinearPayout.ts.html +362 -0
  44. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/LongCall.ts.html +302 -0
  45. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/LongPut.ts.html +302 -0
  46. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/OptionInfo.ts.html +626 -0
  47. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/ShortPut.ts.html +305 -0
  48. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/finance/index.html +216 -0
  49. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/dlc/index.html +186 -0
  50. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/index.html +261 -0
  51. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/index.ts.html +278 -0
  52. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/ChannelId.ts.html +242 -0
  53. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/ChannelKeys.ts.html +407 -0
  54. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/CommitmentNumber.ts.html +362 -0
  55. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/CommitmentSecret.ts.html +203 -0
  56. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/CommitmentSecretStore.ts.html +332 -0
  57. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/Htlc.ts.html +236 -0
  58. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/HtlcDirection.ts.html +131 -0
  59. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/ScriptFactory.ts.html +530 -0
  60. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/TxFactory.ts.html +1094 -0
  61. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/lightning/index.html +231 -0
  62. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/utils/BigIntUtils.ts.html +272 -0
  63. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/utils/Precision.ts.html +149 -0
  64. package/.yalc/@node-dlc/core/coverage/lcov-report/lib/utils/index.html +126 -0
  65. package/.yalc/@node-dlc/core/coverage/lcov-report/prettify.css +1 -0
  66. package/.yalc/@node-dlc/core/coverage/lcov-report/prettify.js +2 -0
  67. package/.yalc/@node-dlc/core/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  68. package/.yalc/@node-dlc/core/coverage/lcov-report/sorter.js +170 -0
  69. package/.yalc/@node-dlc/core/coverage/lcov.info +2426 -0
  70. package/.yalc/@node-dlc/core/dist/AsyncProcessingQueue.d.ts +22 -0
  71. package/.yalc/@node-dlc/core/dist/AsyncProcessingQueue.js +55 -0
  72. package/.yalc/@node-dlc/core/dist/AsyncProcessingQueue.js.map +1 -0
  73. package/.yalc/@node-dlc/core/dist/Base32.d.ts +1 -0
  74. package/.yalc/@node-dlc/core/dist/Base32.js +7 -0
  75. package/.yalc/@node-dlc/core/dist/Base32.js.map +1 -0
  76. package/.yalc/@node-dlc/core/dist/BigIntUtils.d.ts +1 -0
  77. package/.yalc/@node-dlc/core/dist/BigIntUtils.js +8 -0
  78. package/.yalc/@node-dlc/core/dist/BigIntUtils.js.map +1 -0
  79. package/.yalc/@node-dlc/core/dist/BitField.d.ts +1 -0
  80. package/.yalc/@node-dlc/core/dist/BitField.js +7 -0
  81. package/.yalc/@node-dlc/core/dist/BitField.js.map +1 -0
  82. package/.yalc/@node-dlc/core/dist/ChannelId.d.ts +1 -0
  83. package/.yalc/@node-dlc/core/dist/ChannelId.js +7 -0
  84. package/.yalc/@node-dlc/core/dist/ChannelId.js.map +1 -0
  85. package/.yalc/@node-dlc/core/dist/LinkedList.d.ts +30 -0
  86. package/.yalc/@node-dlc/core/dist/LinkedList.js +104 -0
  87. package/.yalc/@node-dlc/core/dist/LinkedList.js.map +1 -0
  88. package/.yalc/@node-dlc/core/dist/LinkedListNode.d.ts +9 -0
  89. package/.yalc/@node-dlc/core/dist/LinkedListNode.js +15 -0
  90. package/.yalc/@node-dlc/core/dist/LinkedListNode.js.map +1 -0
  91. package/.yalc/@node-dlc/core/dist/Queue.d.ts +22 -0
  92. package/.yalc/@node-dlc/core/dist/Queue.js +38 -0
  93. package/.yalc/@node-dlc/core/dist/Queue.js.map +1 -0
  94. package/.yalc/@node-dlc/core/dist/ShortChannelId.d.ts +1 -0
  95. package/.yalc/@node-dlc/core/dist/ShortChannelId.js +7 -0
  96. package/.yalc/@node-dlc/core/dist/ShortChannelId.js.map +1 -0
  97. package/.yalc/@node-dlc/core/dist/ShortChannelIdUtils.d.ts +1 -0
  98. package/.yalc/@node-dlc/core/dist/ShortChannelIdUtils.js +12 -0
  99. package/.yalc/@node-dlc/core/dist/ShortChannelIdUtils.js.map +1 -0
  100. package/.yalc/@node-dlc/core/dist/dlc/CETCalculator.d.ts +50 -0
  101. package/.yalc/@node-dlc/core/dist/dlc/CETCalculator.js +332 -0
  102. package/.yalc/@node-dlc/core/dist/dlc/CETCalculator.js.map +1 -0
  103. package/.yalc/@node-dlc/core/dist/dlc/CoinSelect.d.ts +23 -0
  104. package/.yalc/@node-dlc/core/dist/dlc/CoinSelect.js +77 -0
  105. package/.yalc/@node-dlc/core/dist/dlc/CoinSelect.js.map +1 -0
  106. package/.yalc/@node-dlc/core/dist/dlc/HyperbolaPayoutCurve.d.ts +20 -0
  107. package/.yalc/@node-dlc/core/dist/dlc/HyperbolaPayoutCurve.js +117 -0
  108. package/.yalc/@node-dlc/core/dist/dlc/HyperbolaPayoutCurve.js.map +1 -0
  109. package/.yalc/@node-dlc/core/dist/dlc/PayoutCurve.d.ts +5 -0
  110. package/.yalc/@node-dlc/core/dist/dlc/PayoutCurve.js +3 -0
  111. package/.yalc/@node-dlc/core/dist/dlc/PayoutCurve.js.map +1 -0
  112. package/.yalc/@node-dlc/core/dist/dlc/PolynomialPayoutCurve.d.ts +52 -0
  113. package/.yalc/@node-dlc/core/dist/dlc/PolynomialPayoutCurve.js +173 -0
  114. package/.yalc/@node-dlc/core/dist/dlc/PolynomialPayoutCurve.js.map +1 -0
  115. package/.yalc/@node-dlc/core/dist/dlc/TxBuilder.d.ts +55 -0
  116. package/.yalc/@node-dlc/core/dist/dlc/TxBuilder.js +217 -0
  117. package/.yalc/@node-dlc/core/dist/dlc/TxBuilder.js.map +1 -0
  118. package/.yalc/@node-dlc/core/dist/dlc/TxFinalizer.d.ts +40 -0
  119. package/.yalc/@node-dlc/core/dist/dlc/TxFinalizer.js +146 -0
  120. package/.yalc/@node-dlc/core/dist/dlc/TxFinalizer.js.map +1 -0
  121. package/.yalc/@node-dlc/core/dist/dlc/finance/Builder.d.ts +193 -0
  122. package/.yalc/@node-dlc/core/dist/dlc/finance/Builder.js +422 -0
  123. package/.yalc/@node-dlc/core/dist/dlc/finance/Builder.js.map +1 -0
  124. package/.yalc/@node-dlc/core/dist/dlc/finance/CoveredCall.d.ts +13 -0
  125. package/.yalc/@node-dlc/core/dist/dlc/finance/CoveredCall.js +67 -0
  126. package/.yalc/@node-dlc/core/dist/dlc/finance/CoveredCall.js.map +1 -0
  127. package/.yalc/@node-dlc/core/dist/dlc/finance/CsoInfo.d.ts +114 -0
  128. package/.yalc/@node-dlc/core/dist/dlc/finance/CsoInfo.js +290 -0
  129. package/.yalc/@node-dlc/core/dist/dlc/finance/CsoInfo.js.map +1 -0
  130. package/.yalc/@node-dlc/core/dist/dlc/finance/LinearPayout.d.ts +6 -0
  131. package/.yalc/@node-dlc/core/dist/dlc/finance/LinearPayout.js +79 -0
  132. package/.yalc/@node-dlc/core/dist/dlc/finance/LinearPayout.js.map +1 -0
  133. package/.yalc/@node-dlc/core/dist/dlc/finance/LongCall.d.ts +13 -0
  134. package/.yalc/@node-dlc/core/dist/dlc/finance/LongCall.js +47 -0
  135. package/.yalc/@node-dlc/core/dist/dlc/finance/LongCall.js.map +1 -0
  136. package/.yalc/@node-dlc/core/dist/dlc/finance/LongPut.d.ts +13 -0
  137. package/.yalc/@node-dlc/core/dist/dlc/finance/LongPut.js +47 -0
  138. package/.yalc/@node-dlc/core/dist/dlc/finance/LongPut.js.map +1 -0
  139. package/.yalc/@node-dlc/core/dist/dlc/finance/OptionInfo.d.ts +20 -0
  140. package/.yalc/@node-dlc/core/dist/dlc/finance/OptionInfo.js +101 -0
  141. package/.yalc/@node-dlc/core/dist/dlc/finance/OptionInfo.js.map +1 -0
  142. package/.yalc/@node-dlc/core/dist/dlc/finance/ShortPut.d.ts +12 -0
  143. package/.yalc/@node-dlc/core/dist/dlc/finance/ShortPut.js +46 -0
  144. package/.yalc/@node-dlc/core/dist/dlc/finance/ShortPut.js.map +1 -0
  145. package/.yalc/@node-dlc/core/dist/index.d.ts +39 -0
  146. package/.yalc/@node-dlc/core/dist/index.js +78 -0
  147. package/.yalc/@node-dlc/core/dist/index.js.map +1 -0
  148. package/.yalc/@node-dlc/core/dist/lightning/ChannelId.d.ts +31 -0
  149. package/.yalc/@node-dlc/core/dist/lightning/ChannelId.js +52 -0
  150. package/.yalc/@node-dlc/core/dist/lightning/ChannelId.js.map +1 -0
  151. package/.yalc/@node-dlc/core/dist/lightning/ChannelKeys.d.ts +55 -0
  152. package/.yalc/@node-dlc/core/dist/lightning/ChannelKeys.js +105 -0
  153. package/.yalc/@node-dlc/core/dist/lightning/ChannelKeys.js.map +1 -0
  154. package/.yalc/@node-dlc/core/dist/lightning/CommitmentNumber.d.ts +55 -0
  155. package/.yalc/@node-dlc/core/dist/lightning/CommitmentNumber.js +76 -0
  156. package/.yalc/@node-dlc/core/dist/lightning/CommitmentNumber.js.map +1 -0
  157. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecret.d.ts +26 -0
  158. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecret.js +44 -0
  159. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecret.js.map +1 -0
  160. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecretStore.d.ts +41 -0
  161. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecretStore.js +79 -0
  162. package/.yalc/@node-dlc/core/dist/lightning/CommitmentSecretStore.js.map +1 -0
  163. package/.yalc/@node-dlc/core/dist/lightning/Htlc.d.ts +46 -0
  164. package/.yalc/@node-dlc/core/dist/lightning/Htlc.js +46 -0
  165. package/.yalc/@node-dlc/core/dist/lightning/Htlc.js.map +1 -0
  166. package/.yalc/@node-dlc/core/dist/lightning/HtlcDirection.d.ts +16 -0
  167. package/.yalc/@node-dlc/core/dist/lightning/HtlcDirection.js +21 -0
  168. package/.yalc/@node-dlc/core/dist/lightning/HtlcDirection.js.map +1 -0
  169. package/.yalc/@node-dlc/core/dist/lightning/ScriptFactory.d.ts +76 -0
  170. package/.yalc/@node-dlc/core/dist/lightning/ScriptFactory.js +102 -0
  171. package/.yalc/@node-dlc/core/dist/lightning/ScriptFactory.js.map +1 -0
  172. package/.yalc/@node-dlc/core/dist/lightning/TxFactory.d.ts +83 -0
  173. package/.yalc/@node-dlc/core/dist/lightning/TxFactory.js +222 -0
  174. package/.yalc/@node-dlc/core/dist/lightning/TxFactory.js.map +1 -0
  175. package/.yalc/@node-dlc/core/dist/utils/BigIntUtils.d.ts +9 -0
  176. package/.yalc/@node-dlc/core/dist/utils/BigIntUtils.js +59 -0
  177. package/.yalc/@node-dlc/core/dist/utils/BigIntUtils.js.map +1 -0
  178. package/.yalc/@node-dlc/core/dist/utils/Precision.d.ts +13 -0
  179. package/.yalc/@node-dlc/core/dist/utils/Precision.js +28 -0
  180. package/.yalc/@node-dlc/core/dist/utils/Precision.js.map +1 -0
  181. package/.yalc/@node-dlc/core/lib/AsyncProcessingQueue.ts +63 -0
  182. package/.yalc/@node-dlc/core/lib/Base32.ts +2 -0
  183. package/.yalc/@node-dlc/core/lib/BigIntUtils.ts +2 -0
  184. package/.yalc/@node-dlc/core/lib/BitField.ts +2 -0
  185. package/.yalc/@node-dlc/core/lib/ChannelId.ts +2 -0
  186. package/.yalc/@node-dlc/core/lib/LinkedList.ts +111 -0
  187. package/.yalc/@node-dlc/core/lib/LinkedListNode.ts +14 -0
  188. package/.yalc/@node-dlc/core/lib/Queue.ts +36 -0
  189. package/.yalc/@node-dlc/core/lib/ShortChannelId.ts +2 -0
  190. package/.yalc/@node-dlc/core/lib/ShortChannelIdUtils.ts +9 -0
  191. package/.yalc/@node-dlc/core/lib/dlc/CETCalculator.ts +450 -0
  192. package/.yalc/@node-dlc/core/lib/dlc/CoinSelect.ts +117 -0
  193. package/.yalc/@node-dlc/core/lib/dlc/HyperbolaPayoutCurve.ts +188 -0
  194. package/.yalc/@node-dlc/core/lib/dlc/PayoutCurve.ts +6 -0
  195. package/.yalc/@node-dlc/core/lib/dlc/PolynomialPayoutCurve.ts +266 -0
  196. package/.yalc/@node-dlc/core/lib/dlc/TxBuilder.ts +336 -0
  197. package/.yalc/@node-dlc/core/lib/dlc/TxFinalizer.ts +217 -0
  198. package/.yalc/@node-dlc/core/lib/dlc/finance/Builder.ts +730 -0
  199. package/.yalc/@node-dlc/core/lib/dlc/finance/CoveredCall.ts +110 -0
  200. package/.yalc/@node-dlc/core/lib/dlc/finance/CsoInfo.ts +498 -0
  201. package/.yalc/@node-dlc/core/lib/dlc/finance/LinearPayout.ts +94 -0
  202. package/.yalc/@node-dlc/core/lib/dlc/finance/LongCall.ts +74 -0
  203. package/.yalc/@node-dlc/core/lib/dlc/finance/LongPut.ts +74 -0
  204. package/.yalc/@node-dlc/core/lib/dlc/finance/OptionInfo.ts +182 -0
  205. package/.yalc/@node-dlc/core/lib/dlc/finance/ShortPut.ts +75 -0
  206. package/.yalc/@node-dlc/core/lib/index.ts +66 -0
  207. package/.yalc/@node-dlc/core/lib/lightning/ChannelId.ts +54 -0
  208. package/.yalc/@node-dlc/core/lib/lightning/ChannelKeys.ts +109 -0
  209. package/.yalc/@node-dlc/core/lib/lightning/CommitmentNumber.ts +94 -0
  210. package/.yalc/@node-dlc/core/lib/lightning/CommitmentSecret.ts +41 -0
  211. package/.yalc/@node-dlc/core/lib/lightning/CommitmentSecretStore.ts +84 -0
  212. package/.yalc/@node-dlc/core/lib/lightning/Htlc.ts +52 -0
  213. package/.yalc/@node-dlc/core/lib/lightning/HtlcDirection.ts +17 -0
  214. package/.yalc/@node-dlc/core/lib/lightning/ScriptFactory.ts +150 -0
  215. package/.yalc/@node-dlc/core/lib/lightning/TxFactory.ts +338 -0
  216. package/.yalc/@node-dlc/core/lib/utils/BigIntUtils.ts +64 -0
  217. package/.yalc/@node-dlc/core/lib/utils/Precision.ts +23 -0
  218. package/.yalc/@node-dlc/core/package.json +38 -0
  219. package/.yalc/@node-dlc/core/tsconfig.json +8 -0
  220. package/.yalc/@node-dlc/core/yalc.sig +1 -0
  221. package/CHANGELOG.md +56 -5
  222. package/dist/BitcoinWalletProvider.d.ts +4 -2
  223. package/dist/BitcoinWalletProvider.js +1 -0
  224. package/dist/BitcoinWalletProvider.js.map +1 -1
  225. package/lib/BitcoinWalletProvider.ts +11 -3
  226. package/package.json +5 -5
  227. package/yalc.lock +10 -0
@@ -0,0 +1,336 @@
1
+ /**
2
+ * DLC Transaction Builder with support for "exact amount" DLC scenarios.
3
+ *
4
+ * Key improvements:
5
+ * - Dust filtering: Only creates change outputs above DUST_LIMIT (1000 sats)
6
+ * - No-change support: Enables splicing with exact UTXO amounts
7
+ * - Max collateral calculation: Helper for determining exact amounts
8
+ *
9
+ * This matches the C++ layer behavior and fixes issues with DLC splicing
10
+ * where users want to use exact UTXO amounts without change outputs.
11
+ */
12
+
13
+ import {
14
+ LockTime,
15
+ OutPoint,
16
+ Script,
17
+ Tx,
18
+ TxBuilder,
19
+ Value,
20
+ } from '@node-dlc/bitcoin';
21
+ import {
22
+ DlcAcceptWithoutSigs,
23
+ DlcOffer,
24
+ FundingInput,
25
+ MessageType,
26
+ } from '@node-dlc/messaging';
27
+ import Decimal from 'decimal.js';
28
+
29
+ import { DualFundingTxFinalizer } from './TxFinalizer';
30
+
31
+ // Dust limit matching C++ implementation (1000 satoshis)
32
+ export const DUST_LIMIT = BigInt(1000);
33
+
34
+ export class DlcTxBuilder {
35
+ constructor(
36
+ readonly dlcOffer: DlcOffer,
37
+ readonly dlcAccept: DlcAcceptWithoutSigs,
38
+ ) {}
39
+
40
+ public buildFundingTransaction(): Tx {
41
+ const txBuilder = new BatchDlcTxBuilder([this.dlcOffer], [this.dlcAccept]);
42
+ return txBuilder.buildFundingTransaction();
43
+ }
44
+ }
45
+
46
+ export class BatchDlcTxBuilder {
47
+ constructor(
48
+ readonly dlcOffers: DlcOffer[],
49
+ readonly dlcAccepts: DlcAcceptWithoutSigs[],
50
+ ) {}
51
+
52
+ /**
53
+ * Calculates the maximum collateral that can be used given a set of funding inputs
54
+ * for exact-amount DLC scenarios (no change outputs).
55
+ *
56
+ * @param fundingInputs The inputs to be used for funding
57
+ * @param feeRatePerVb Fee rate in satoshis per virtual byte
58
+ * @param numContracts Number of DLC contracts being created (default: 1)
59
+ * @returns Maximum collateral amount in satoshis
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // Calculate max collateral for DLC splicing scenario
64
+ * const dlcFundingInput = getDlcFundingInput(); // 970,332 sats
65
+ * const additionalInput = getAdditionalInput(); // 100,000 sats
66
+ * const inputs = [dlcFundingInput, additionalInput];
67
+ *
68
+ * const maxCollateral = BatchDlcTxBuilder.calculateMaxCollateral(
69
+ * inputs,
70
+ * BigInt(1), // 1 sat/vB fee rate
71
+ * 1 // Single DLC contract
72
+ * );
73
+ *
74
+ * // Use maxCollateral in DLC offer to ensure exact amount with no change
75
+ * const dlcOffer = createDlcOffer(contractInfo, maxCollateral, ...);
76
+ * ```
77
+ */
78
+ public static calculateMaxCollateral(
79
+ fundingInputs: FundingInput[],
80
+ feeRatePerVb: bigint,
81
+ numContracts = 1,
82
+ ): bigint {
83
+ // Calculate total input value
84
+ const totalInputValue = fundingInputs.reduce((total, input) => {
85
+ return total + input.prevTx.outputs[input.prevTxVout].value.sats;
86
+ }, BigInt(0));
87
+
88
+ // Create a temporary finalizer to calculate fees
89
+ const fakeSPK = Buffer.from(
90
+ '0014663117d27e78eb432505180654e603acb30e8a4a',
91
+ 'hex',
92
+ );
93
+ const finalizer = new DualFundingTxFinalizer(
94
+ fundingInputs,
95
+ fakeSPK,
96
+ fakeSPK,
97
+ [], // No accepter inputs for single-funded scenario
98
+ fakeSPK,
99
+ fakeSPK,
100
+ feeRatePerVb,
101
+ numContracts,
102
+ );
103
+
104
+ // For exact-amount scenarios, we need to account for:
105
+ // 1. Future fees (for CET/refund transactions)
106
+ // 2. Funding transaction fees
107
+ const futureFee = finalizer.offerFutureFee;
108
+ const fundingFee = finalizer.offerFundingFee;
109
+
110
+ // Maximum collateral is input value minus all fees
111
+ const maxCollateral = totalInputValue - futureFee - fundingFee;
112
+
113
+ // Ensure we don't return negative values
114
+ return maxCollateral > BigInt(0) ? maxCollateral : BigInt(0);
115
+ }
116
+
117
+ public buildFundingTransaction(): Tx {
118
+ const tx = new TxBuilder();
119
+ tx.version = 2;
120
+ tx.locktime = LockTime.zero();
121
+
122
+ if (this.dlcOffers.length !== this.dlcAccepts.length)
123
+ throw Error('DlcOffers and DlcAccepts must be the same length');
124
+ if (this.dlcOffers.length === 0) throw Error('DlcOffers must not be empty');
125
+ if (this.dlcAccepts.length === 0)
126
+ throw Error('DlcAccepts must not be empty');
127
+
128
+ // Ensure all DLC offers and accepts have the same funding inputs
129
+ this.ensureSameFundingInputs();
130
+
131
+ const multisigScripts: Script[] = [];
132
+ for (let i = 0; i < this.dlcOffers.length; i++) {
133
+ const offer = this.dlcOffers[i];
134
+ const accept = this.dlcAccepts[i];
135
+
136
+ multisigScripts.push(
137
+ Buffer.compare(offer.fundingPubkey, accept.fundingPubkey) === -1
138
+ ? Script.p2msLock(2, offer.fundingPubkey, accept.fundingPubkey)
139
+ : Script.p2msLock(2, accept.fundingPubkey, offer.fundingPubkey),
140
+ );
141
+ }
142
+
143
+ const witScripts = multisigScripts.map((multisigScript) =>
144
+ Script.p2wshLock(multisigScript),
145
+ );
146
+
147
+ const finalizer = new DualFundingTxFinalizer(
148
+ this.dlcOffers[0].fundingInputs,
149
+ this.dlcOffers[0].payoutSpk,
150
+ this.dlcOffers[0].changeSpk,
151
+ this.dlcAccepts[0].fundingInputs,
152
+ this.dlcAccepts[0].payoutSpk,
153
+ this.dlcAccepts[0].changeSpk,
154
+ this.dlcOffers[0].feeRatePerVb,
155
+ this.dlcOffers.length,
156
+ );
157
+
158
+ this.dlcOffers[0].fundingInputs.forEach((input) => {
159
+ if (input.type !== MessageType.FundingInput)
160
+ throw new Error('Input is not a funding input');
161
+ });
162
+ const offerFundingInputs: FundingInput[] = this.dlcOffers[0].fundingInputs.map(
163
+ (input) => input as FundingInput,
164
+ );
165
+
166
+ const offerTotalFunding = offerFundingInputs.reduce((total, input) => {
167
+ return total + input.prevTx.outputs[input.prevTxVout].value.sats;
168
+ }, BigInt(0));
169
+
170
+ const acceptTotalFunding = this.dlcAccepts[0].fundingInputs.reduce(
171
+ (total, input) => {
172
+ return total + input.prevTx.outputs[input.prevTxVout].value.sats;
173
+ },
174
+ BigInt(0),
175
+ );
176
+
177
+ const fundingInputs: FundingInput[] = [
178
+ ...offerFundingInputs,
179
+ ...this.dlcAccepts[0].fundingInputs,
180
+ ];
181
+
182
+ fundingInputs.sort(
183
+ (a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
184
+ );
185
+
186
+ fundingInputs.forEach((input) => {
187
+ tx.addInput(
188
+ OutPoint.fromString(
189
+ `${input.prevTx.txId.toString()}:${input.prevTxVout}`,
190
+ ),
191
+ );
192
+ });
193
+
194
+ const offerInput = this.dlcOffers.reduce(
195
+ (total, offer) => total + offer.offerCollateral,
196
+ BigInt(0),
197
+ );
198
+ const acceptInput = this.dlcAccepts.reduce(
199
+ (total, accept) => total + accept.acceptCollateral,
200
+ BigInt(0),
201
+ );
202
+
203
+ const totalInputs = this.dlcOffers.map((offer, i) => {
204
+ const offerInput = offer.offerCollateral;
205
+ const acceptInput = this.dlcAccepts[i].acceptCollateral;
206
+ return offerInput + acceptInput;
207
+ });
208
+
209
+ const fundingValues = totalInputs.map((totalInput) => {
210
+ const offerFutureFeePerOffer = new Decimal(
211
+ finalizer.offerFutureFee.toString(),
212
+ )
213
+ .div(this.dlcOffers.length)
214
+ .ceil()
215
+ .toNumber();
216
+ const acceptFutureFeePerAccept = new Decimal(
217
+ finalizer.acceptFutureFee.toString(),
218
+ )
219
+ .div(this.dlcAccepts.length)
220
+ .ceil()
221
+ .toNumber();
222
+
223
+ return (
224
+ totalInput +
225
+ Value.fromSats(offerFutureFeePerOffer).sats +
226
+ Value.fromSats(acceptFutureFeePerAccept).sats
227
+ );
228
+ });
229
+
230
+ const offerChangeValue =
231
+ offerTotalFunding - offerInput - finalizer.offerFees;
232
+ const acceptChangeValue =
233
+ acceptTotalFunding - acceptInput - finalizer.acceptFees;
234
+
235
+ // Validate that we have sufficient funds
236
+ if (offerChangeValue < BigInt(0)) {
237
+ throw new Error(
238
+ `Insufficient funds for offerer: need ${
239
+ offerInput + finalizer.offerFees
240
+ } sats, have ${offerTotalFunding} sats`,
241
+ );
242
+ }
243
+
244
+ // In single-funded DLCs, if accepter has no inputs, they don't pay fees
245
+ // This matches the C++ layer behavior where parties with no inputs have zero fees
246
+ if (acceptChangeValue < BigInt(0) && acceptTotalFunding > BigInt(0)) {
247
+ throw new Error(
248
+ `Insufficient funds for accepter: need ${
249
+ acceptInput + finalizer.acceptFees
250
+ } sats, have ${acceptTotalFunding} sats`,
251
+ );
252
+ }
253
+
254
+ const outputs: Output[] = [];
255
+ witScripts.forEach((witScript, i) => {
256
+ outputs.push({
257
+ value: Value.fromSats(Number(fundingValues[i])),
258
+ script: witScript,
259
+ serialId: this.dlcOffers[i].fundOutputSerialId,
260
+ });
261
+ });
262
+
263
+ // Dust filtering: Only create change outputs if they're above dust threshold
264
+ // This matches the C++ implementation and enables "exact amount" DLC scenarios
265
+ // where all input value goes into the DLC funding output with no change
266
+ if (offerChangeValue >= DUST_LIMIT) {
267
+ outputs.push({
268
+ value: Value.fromSats(Number(offerChangeValue)),
269
+ script: Script.p2wpkhLock(this.dlcOffers[0].changeSpk.slice(2)),
270
+ serialId: this.dlcOffers[0].changeSerialId,
271
+ });
272
+ }
273
+
274
+ if (acceptChangeValue >= DUST_LIMIT) {
275
+ outputs.push({
276
+ value: Value.fromSats(Number(acceptChangeValue)),
277
+ script: Script.p2wpkhLock(this.dlcAccepts[0].changeSpk.slice(2)),
278
+ serialId: this.dlcAccepts[0].changeSerialId,
279
+ });
280
+ }
281
+
282
+ outputs.sort((a, b) => Number(a.serialId) - Number(b.serialId));
283
+
284
+ outputs.forEach((output) => {
285
+ tx.addOutput(output.value, output.script);
286
+ });
287
+
288
+ return tx.toTx();
289
+ }
290
+
291
+ private ensureSameFundingInputs(): void {
292
+ // Check for offers
293
+ const referenceOfferInputs = this.dlcOffers[0].fundingInputs.map((input) =>
294
+ input.serialize().toString('hex'),
295
+ );
296
+ for (let i = 1; i < this.dlcOffers.length; i++) {
297
+ const currentInputs = this.dlcOffers[i].fundingInputs.map((input) =>
298
+ input.serialize().toString('hex'),
299
+ );
300
+ if (!this.arraysEqual(referenceOfferInputs, currentInputs)) {
301
+ throw new Error(
302
+ `Funding inputs for offer ${i} do not match the first offer's funding inputs.`,
303
+ );
304
+ }
305
+ }
306
+
307
+ // Check for accepts
308
+ const referenceAcceptInputs = this.dlcAccepts[0].fundingInputs.map(
309
+ (input) => input.serialize().toString('hex'),
310
+ );
311
+ for (let i = 1; i < this.dlcAccepts.length; i++) {
312
+ const currentInputs = this.dlcAccepts[i].fundingInputs.map((input) =>
313
+ input.serialize().toString('hex'),
314
+ );
315
+ if (!this.arraysEqual(referenceAcceptInputs, currentInputs)) {
316
+ throw new Error(
317
+ `Funding inputs for accept ${i} do not match the first accept's funding inputs.`,
318
+ );
319
+ }
320
+ }
321
+ }
322
+
323
+ private arraysEqual(arr1: string[], arr2: string[]): boolean {
324
+ if (arr1.length !== arr2.length) return false;
325
+ for (let i = 0; i < arr1.length; i++) {
326
+ if (arr1[i] !== arr2[i]) return false;
327
+ }
328
+ return true;
329
+ }
330
+ }
331
+
332
+ interface Output {
333
+ value: Value;
334
+ script: Script;
335
+ serialId: bigint;
336
+ }
@@ -0,0 +1,217 @@
1
+ import { FundingInput, MessageType } from '@node-dlc/messaging';
2
+ import { Decimal } from 'decimal.js';
3
+
4
+ const BATCH_FUND_TX_BASE_WEIGHT = 42;
5
+ const FUNDING_OUTPUT_SIZE = 43;
6
+
7
+ export class DualFundingTxFinalizer {
8
+ constructor(
9
+ readonly offerInputs: FundingInput[],
10
+ readonly offerPayoutSPK: Buffer,
11
+ readonly offerChangeSPK: Buffer,
12
+ readonly acceptInputs: FundingInput[],
13
+ readonly acceptPayoutSPK: Buffer,
14
+ readonly acceptChangeSPK: Buffer,
15
+ readonly feeRate: bigint,
16
+ readonly numContracts = 1,
17
+ ) {}
18
+
19
+ private computeFees(
20
+ _inputs: FundingInput[],
21
+ payoutSPK: Buffer,
22
+ changeSPK: Buffer,
23
+ numContracts: number,
24
+ ): IFees {
25
+ // If no inputs, return zero fees (matches C++ layer behavior for single-funded DLCs)
26
+ if (_inputs.length === 0) {
27
+ return { futureFee: BigInt(0), fundingFee: BigInt(0) };
28
+ }
29
+
30
+ _inputs.forEach((input) => {
31
+ if (input.type !== MessageType.FundingInput) {
32
+ console.error('input', input);
33
+ throw new Error('Input is not a funding input');
34
+ }
35
+ });
36
+ const inputs: FundingInput[] = _inputs.map(
37
+ (input) => input as FundingInput,
38
+ );
39
+ // https://github.com/discreetlogcontracts/dlcspecs/blob/8ee4bbe816c9881c832b1ce320b9f14c72e3506f/Transactions.md#expected-weight-of-the-contract-execution-or-refund-transaction
40
+ const futureFeeWeight = 249 + 4 * payoutSPK.length;
41
+ const futureFeeVBytes = new Decimal(futureFeeWeight)
42
+ .times(numContracts)
43
+ .div(4)
44
+ .ceil()
45
+ .toNumber();
46
+ const futureFee = this.feeRate * BigInt(futureFeeVBytes);
47
+
48
+ // https://github.com/discreetlogcontracts/dlcspecs/blob/8ee4bbe816c9881c832b1ce320b9f14c72e3506f/Transactions.md#expected-weight-of-the-funding-transaction
49
+ const inputWeight = inputs.reduce((total, input) => {
50
+ return total + 164 + input.maxWitnessLen + input.scriptSigLength();
51
+ }, 0);
52
+ const contractWeight =
53
+ (BATCH_FUND_TX_BASE_WEIGHT + FUNDING_OUTPUT_SIZE * numContracts * 4) / 2;
54
+ const outputWeight = 36 + 4 * changeSPK.length + contractWeight;
55
+ const weight = outputWeight + inputWeight;
56
+ const vbytes = new Decimal(weight).div(4).ceil().toNumber();
57
+ const fundingFee = this.feeRate * BigInt(vbytes);
58
+
59
+ return { futureFee, fundingFee };
60
+ }
61
+
62
+ private getOfferFees(): IFees {
63
+ return this.computeFees(
64
+ this.offerInputs,
65
+ this.offerPayoutSPK,
66
+ this.offerChangeSPK,
67
+ this.numContracts,
68
+ );
69
+ }
70
+
71
+ private getAcceptFees(): IFees {
72
+ return this.computeFees(
73
+ this.acceptInputs,
74
+ this.acceptPayoutSPK,
75
+ this.acceptChangeSPK,
76
+ this.numContracts,
77
+ );
78
+ }
79
+
80
+ public get offerFees(): bigint {
81
+ const { futureFee, fundingFee } = this.getOfferFees();
82
+ return futureFee + fundingFee;
83
+ }
84
+
85
+ public get offerFutureFee(): bigint {
86
+ return this.getOfferFees().futureFee;
87
+ }
88
+
89
+ public get offerFundingFee(): bigint {
90
+ return this.getOfferFees().fundingFee;
91
+ }
92
+
93
+ public get acceptFees(): bigint {
94
+ const { futureFee, fundingFee } = this.getAcceptFees();
95
+ return futureFee + fundingFee;
96
+ }
97
+
98
+ public get acceptFutureFee(): bigint {
99
+ return this.getAcceptFees().futureFee;
100
+ }
101
+
102
+ public get acceptFundingFee(): bigint {
103
+ return this.getAcceptFees().fundingFee;
104
+ }
105
+ }
106
+
107
+ export class DualClosingTxFinalizer {
108
+ constructor(
109
+ readonly initiatorInputs: FundingInput[],
110
+ readonly offerPayoutSPK: Buffer,
111
+ readonly acceptPayoutSPK: Buffer,
112
+ readonly feeRate: bigint,
113
+ ) {}
114
+
115
+ private computeFees(payoutSPK: Buffer, _inputs: FundingInput[] = []): bigint {
116
+ _inputs.forEach((input) => {
117
+ if (input.type !== MessageType.FundingInput)
118
+ throw new Error('Input is not a funding input');
119
+ });
120
+ const inputs: FundingInput[] = _inputs.map(
121
+ (input) => input as FundingInput,
122
+ );
123
+ // https://gist.github.com/matthewjablack/08c36baa513af9377508111405b22e03
124
+ const inputWeight = inputs.reduce((total, input) => {
125
+ return total + 164 + input.maxWitnessLen + input.scriptSigLength();
126
+ }, 0);
127
+ const outputWeight = 36 + 4 * payoutSPK.length;
128
+ const weight = 213 + outputWeight + inputWeight;
129
+ const vbytes = new Decimal(weight).div(4).ceil().toNumber();
130
+ const fee = this.feeRate * BigInt(vbytes);
131
+
132
+ return fee;
133
+ }
134
+
135
+ private getOfferInitiatorFees(): bigint {
136
+ return this.computeFees(this.offerPayoutSPK, this.initiatorInputs);
137
+ }
138
+
139
+ private getOfferReciprocatorFees(): bigint {
140
+ return this.computeFees(this.offerPayoutSPK);
141
+ }
142
+
143
+ private getAcceptInitiatorFees(): bigint {
144
+ return this.computeFees(this.acceptPayoutSPK, this.initiatorInputs);
145
+ }
146
+
147
+ private getAcceptReciprocatorFees(): bigint {
148
+ return this.computeFees(this.acceptPayoutSPK);
149
+ }
150
+
151
+ public get offerInitiatorFees(): bigint {
152
+ return this.getOfferInitiatorFees();
153
+ }
154
+
155
+ public get offerReciprocatorFees(): bigint {
156
+ return this.getOfferReciprocatorFees();
157
+ }
158
+
159
+ public get acceptInitiatorFees(): bigint {
160
+ return this.getAcceptInitiatorFees();
161
+ }
162
+
163
+ public get acceptReciprocatorFees(): bigint {
164
+ return this.getAcceptReciprocatorFees();
165
+ }
166
+ }
167
+
168
+ interface IFees {
169
+ futureFee: bigint;
170
+ fundingFee: bigint;
171
+ }
172
+
173
+ export const getFinalizer = (
174
+ feeRate: bigint,
175
+ offerInputs?: FundingInput[],
176
+ acceptInputs?: FundingInput[],
177
+ numContracts?: number,
178
+ ): DualFundingTxFinalizer => {
179
+ const input = new FundingInput();
180
+ input.maxWitnessLen = 108;
181
+ input.redeemScript = Buffer.from('', 'hex');
182
+
183
+ const fakeSPK = Buffer.from(
184
+ '0014663117d27e78eb432505180654e603acb30e8a4a',
185
+ 'hex',
186
+ );
187
+
188
+ offerInputs = offerInputs || Array.from({ length: 1 }, () => input);
189
+
190
+ acceptInputs = acceptInputs || Array.from({ length: 1 }, () => input);
191
+
192
+ return new DualFundingTxFinalizer(
193
+ offerInputs,
194
+ fakeSPK,
195
+ fakeSPK,
196
+ acceptInputs,
197
+ fakeSPK,
198
+ fakeSPK,
199
+ feeRate,
200
+ numContracts,
201
+ );
202
+ };
203
+
204
+ export const getFinalizerByCount = (
205
+ feeRate: bigint,
206
+ numOfferInputs: number,
207
+ numAcceptInputs: number,
208
+ numContracts: number,
209
+ ): DualFundingTxFinalizer => {
210
+ const input = new FundingInput();
211
+ input.maxWitnessLen = 108;
212
+ input.redeemScript = Buffer.from('', 'hex');
213
+
214
+ const offerInputs = Array.from({ length: numOfferInputs }, () => input);
215
+ const acceptInputs = Array.from({ length: numAcceptInputs }, () => input);
216
+ return getFinalizer(feeRate, offerInputs, acceptInputs, numContracts);
217
+ };