@atomiqlabs/sdk 8.8.3 → 8.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +1760 -1760
  3. package/dist/SmartChainAssets.d.ts +181 -181
  4. package/dist/SmartChainAssets.js +181 -181
  5. package/dist/bitcoin/coinselect2/accumulative.d.ts +7 -7
  6. package/dist/bitcoin/coinselect2/accumulative.js +52 -52
  7. package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -7
  8. package/dist/bitcoin/coinselect2/blackjack.js +38 -38
  9. package/dist/bitcoin/coinselect2/index.d.ts +20 -20
  10. package/dist/bitcoin/coinselect2/index.js +69 -69
  11. package/dist/bitcoin/coinselect2/utils.d.ts +82 -82
  12. package/dist/bitcoin/coinselect2/utils.js +158 -158
  13. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -113
  14. package/dist/bitcoin/wallet/BitcoinWallet.js +335 -335
  15. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -116
  16. package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
  17. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -106
  18. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -196
  19. package/dist/enums/FeeType.d.ts +15 -15
  20. package/dist/enums/FeeType.js +19 -19
  21. package/dist/enums/SwapAmountType.d.ts +15 -15
  22. package/dist/enums/SwapAmountType.js +19 -19
  23. package/dist/enums/SwapDirection.d.ts +15 -15
  24. package/dist/enums/SwapDirection.js +19 -19
  25. package/dist/enums/SwapSide.d.ts +15 -15
  26. package/dist/enums/SwapSide.js +19 -19
  27. package/dist/enums/SwapType.d.ts +75 -75
  28. package/dist/enums/SwapType.js +79 -79
  29. package/dist/errors/IntermediaryError.d.ts +13 -13
  30. package/dist/errors/IntermediaryError.js +27 -27
  31. package/dist/errors/RequestError.d.ts +32 -32
  32. package/dist/errors/RequestError.js +54 -54
  33. package/dist/errors/UserError.d.ts +8 -8
  34. package/dist/errors/UserError.js +16 -16
  35. package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
  36. package/dist/events/UnifiedSwapEventListener.js +132 -132
  37. package/dist/http/HttpUtils.d.ts +27 -27
  38. package/dist/http/HttpUtils.js +91 -91
  39. package/dist/http/paramcoders/IParamReader.d.ts +8 -8
  40. package/dist/http/paramcoders/IParamReader.js +2 -2
  41. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -44
  42. package/dist/http/paramcoders/ParamDecoder.js +137 -137
  43. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -20
  44. package/dist/http/paramcoders/ParamEncoder.js +36 -36
  45. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
  46. package/dist/http/paramcoders/SchemaVerifier.js +145 -145
  47. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
  48. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
  49. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -13
  50. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
  51. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
  52. package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -174
  53. package/dist/index.d.ts +85 -85
  54. package/dist/index.js +158 -158
  55. package/dist/intermediaries/Intermediary.d.ts +178 -178
  56. package/dist/intermediaries/Intermediary.js +166 -166
  57. package/dist/intermediaries/IntermediaryDiscovery.d.ts +211 -211
  58. package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
  59. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +450 -450
  60. package/dist/intermediaries/apis/IntermediaryAPI.js +618 -618
  61. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
  62. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
  63. package/dist/lnurl/LNURL.d.ts +102 -102
  64. package/dist/lnurl/LNURL.js +321 -321
  65. package/dist/prices/RedundantSwapPrice.d.ts +110 -110
  66. package/dist/prices/RedundantSwapPrice.js +222 -222
  67. package/dist/prices/SingleSwapPrice.d.ts +34 -34
  68. package/dist/prices/SingleSwapPrice.js +44 -44
  69. package/dist/prices/SwapPriceWithChain.d.ts +107 -107
  70. package/dist/prices/SwapPriceWithChain.js +128 -128
  71. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
  72. package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
  73. package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
  74. package/dist/prices/abstract/IPriceProvider.js +74 -74
  75. package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
  76. package/dist/prices/abstract/ISwapPrice.js +279 -279
  77. package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
  78. package/dist/prices/providers/BinancePriceProvider.js +30 -30
  79. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
  80. package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
  81. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
  82. package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
  83. package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
  84. package/dist/prices/providers/CustomPriceProvider.js +35 -35
  85. package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
  86. package/dist/prices/providers/KrakenPriceProvider.js +45 -45
  87. package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
  88. package/dist/prices/providers/OKXPriceProvider.js +29 -29
  89. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
  90. package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
  91. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
  92. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
  93. package/dist/storage/IUnifiedStorage.d.ts +85 -85
  94. package/dist/storage/IUnifiedStorage.js +2 -2
  95. package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
  96. package/dist/storage/UnifiedSwapStorage.js +116 -116
  97. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
  98. package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
  99. package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
  100. package/dist/storage-browser/LocalStorageManager.js +93 -93
  101. package/dist/swapper/Swapper.d.ts +732 -732
  102. package/dist/swapper/Swapper.js +1713 -1713
  103. package/dist/swapper/SwapperFactory.d.ts +135 -135
  104. package/dist/swapper/SwapperFactory.js +162 -162
  105. package/dist/swapper/SwapperUtils.d.ts +206 -206
  106. package/dist/swapper/SwapperUtils.js +481 -481
  107. package/dist/swapper/SwapperWithChain.d.ts +404 -404
  108. package/dist/swapper/SwapperWithChain.js +469 -469
  109. package/dist/swapper/SwapperWithSigner.d.ts +322 -322
  110. package/dist/swapper/SwapperWithSigner.js +318 -318
  111. package/dist/swaps/IAddressSwap.d.ts +22 -22
  112. package/dist/swaps/IAddressSwap.js +14 -14
  113. package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
  114. package/dist/swaps/IBTCWalletSwap.js +18 -18
  115. package/dist/swaps/IClaimableSwap.d.ts +49 -49
  116. package/dist/swaps/IClaimableSwap.js +15 -15
  117. package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
  118. package/dist/swaps/IClaimableSwapWrapper.js +2 -2
  119. package/dist/swaps/IRefundableSwap.d.ts +43 -43
  120. package/dist/swaps/IRefundableSwap.js +14 -14
  121. package/dist/swaps/ISwap.d.ts +392 -392
  122. package/dist/swaps/ISwap.js +349 -349
  123. package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
  124. package/dist/swaps/ISwapWithGasDrop.js +12 -12
  125. package/dist/swaps/ISwapWrapper.d.ts +285 -285
  126. package/dist/swaps/ISwapWrapper.js +353 -353
  127. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
  128. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
  129. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +139 -139
  130. package/dist/swaps/escrow_swaps/IEscrowSwap.js +170 -170
  131. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +128 -128
  132. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
  133. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +105 -105
  134. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +129 -129
  135. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
  136. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
  137. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +64 -64
  138. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +82 -82
  139. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +531 -531
  140. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
  141. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +190 -190
  142. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
  143. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +583 -583
  144. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
  145. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +235 -235
  146. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
  147. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
  148. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
  149. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +202 -202
  150. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
  151. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
  152. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
  153. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +68 -68
  154. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +117 -117
  155. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -127
  156. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
  157. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +251 -251
  158. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +536 -536
  159. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
  160. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
  161. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +132 -132
  162. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
  163. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +637 -637
  164. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1448 -1448
  165. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +257 -257
  166. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -947
  167. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
  168. package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
  169. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
  170. package/dist/swaps/trusted/ln/LnForGasWrapper.js +83 -83
  171. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
  172. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
  173. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +69 -69
  174. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +93 -93
  175. package/dist/types/AmountData.d.ts +10 -10
  176. package/dist/types/AmountData.js +2 -2
  177. package/dist/types/CustomPriceFunction.d.ts +11 -11
  178. package/dist/types/CustomPriceFunction.js +2 -2
  179. package/dist/types/PriceInfoType.d.ts +28 -28
  180. package/dist/types/PriceInfoType.js +57 -57
  181. package/dist/types/SwapExecutionAction.d.ts +88 -88
  182. package/dist/types/SwapExecutionAction.js +2 -2
  183. package/dist/types/SwapStateInfo.d.ts +5 -5
  184. package/dist/types/SwapStateInfo.js +2 -2
  185. package/dist/types/SwapWithSigner.d.ts +17 -17
  186. package/dist/types/SwapWithSigner.js +43 -43
  187. package/dist/types/Token.d.ts +99 -99
  188. package/dist/types/Token.js +76 -76
  189. package/dist/types/TokenAmount.d.ts +69 -69
  190. package/dist/types/TokenAmount.js +60 -60
  191. package/dist/types/fees/Fee.d.ts +50 -50
  192. package/dist/types/fees/Fee.js +2 -2
  193. package/dist/types/fees/FeeBreakdown.d.ts +11 -11
  194. package/dist/types/fees/FeeBreakdown.js +2 -2
  195. package/dist/types/fees/PercentagePPM.d.ts +17 -17
  196. package/dist/types/fees/PercentagePPM.js +18 -18
  197. package/dist/types/lnurl/LNURLPay.d.ts +61 -61
  198. package/dist/types/lnurl/LNURLPay.js +31 -31
  199. package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
  200. package/dist/types/lnurl/LNURLWithdraw.js +27 -27
  201. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
  202. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -15
  203. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
  204. package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
  205. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
  206. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
  207. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
  208. package/dist/utils/AutomaticClockDriftCorrection.js +70 -70
  209. package/dist/utils/BitcoinUtils.d.ts +16 -16
  210. package/dist/utils/BitcoinUtils.js +141 -141
  211. package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
  212. package/dist/utils/BitcoinWalletUtils.js +14 -14
  213. package/dist/utils/Logger.d.ts +7 -7
  214. package/dist/utils/Logger.js +12 -12
  215. package/dist/utils/RetryUtils.d.ts +22 -22
  216. package/dist/utils/RetryUtils.js +67 -67
  217. package/dist/utils/SwapUtils.d.ts +88 -88
  218. package/dist/utils/SwapUtils.js +72 -72
  219. package/dist/utils/TimeoutUtils.d.ts +17 -17
  220. package/dist/utils/TimeoutUtils.js +55 -55
  221. package/dist/utils/TokenUtils.d.ts +19 -19
  222. package/dist/utils/TokenUtils.js +37 -37
  223. package/dist/utils/TypeUtils.d.ts +7 -7
  224. package/dist/utils/TypeUtils.js +2 -2
  225. package/dist/utils/Utils.d.ts +67 -67
  226. package/dist/utils/Utils.js +208 -208
  227. package/package.json +43 -43
  228. package/src/SmartChainAssets.ts +186 -186
  229. package/src/bitcoin/coinselect2/accumulative.ts +69 -69
  230. package/src/bitcoin/coinselect2/blackjack.ts +50 -50
  231. package/src/bitcoin/coinselect2/index.ts +93 -93
  232. package/src/bitcoin/coinselect2/utils.ts +236 -236
  233. package/src/bitcoin/wallet/BitcoinWallet.ts +439 -439
  234. package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -140
  235. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -225
  236. package/src/enums/FeeType.ts +15 -15
  237. package/src/enums/SwapAmountType.ts +16 -16
  238. package/src/enums/SwapDirection.ts +15 -15
  239. package/src/enums/SwapSide.ts +16 -16
  240. package/src/enums/SwapType.ts +75 -75
  241. package/src/errors/IntermediaryError.ts +28 -28
  242. package/src/errors/RequestError.ts +64 -64
  243. package/src/errors/UserError.ts +15 -15
  244. package/src/events/UnifiedSwapEventListener.ts +173 -173
  245. package/src/http/HttpUtils.ts +91 -91
  246. package/src/http/paramcoders/IParamReader.ts +9 -9
  247. package/src/http/paramcoders/ParamDecoder.ts +145 -145
  248. package/src/http/paramcoders/ParamEncoder.ts +40 -40
  249. package/src/http/paramcoders/SchemaVerifier.ts +153 -153
  250. package/src/http/paramcoders/client/ResponseParamDecoder.ts +57 -57
  251. package/src/http/paramcoders/client/StreamParamEncoder.ts +28 -28
  252. package/src/http/paramcoders/client/StreamingFetchPromise.ts +192 -192
  253. package/src/index.ts +140 -140
  254. package/src/intermediaries/Intermediary.ts +280 -280
  255. package/src/intermediaries/IntermediaryDiscovery.ts +541 -541
  256. package/src/intermediaries/apis/IntermediaryAPI.ts +963 -963
  257. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -257
  258. package/src/lnurl/LNURL.ts +402 -402
  259. package/src/prices/RedundantSwapPrice.ts +264 -264
  260. package/src/prices/SingleSwapPrice.ts +50 -50
  261. package/src/prices/SwapPriceWithChain.ts +194 -194
  262. package/src/prices/abstract/ICachedSwapPrice.ts +85 -85
  263. package/src/prices/abstract/IPriceProvider.ts +127 -127
  264. package/src/prices/abstract/ISwapPrice.ts +390 -390
  265. package/src/prices/providers/BinancePriceProvider.ts +48 -48
  266. package/src/prices/providers/CoinGeckoPriceProvider.ts +46 -46
  267. package/src/prices/providers/CoinPaprikaPriceProvider.ts +49 -49
  268. package/src/prices/providers/CustomPriceProvider.ts +40 -40
  269. package/src/prices/providers/KrakenPriceProvider.ts +83 -83
  270. package/src/prices/providers/OKXPriceProvider.ts +59 -59
  271. package/src/prices/providers/abstract/ExchangePriceProvider.ts +31 -31
  272. package/src/prices/providers/abstract/HttpPriceProvider.ts +14 -14
  273. package/src/storage/IUnifiedStorage.ts +95 -95
  274. package/src/storage/UnifiedSwapStorage.ts +141 -141
  275. package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
  276. package/src/storage-browser/LocalStorageManager.ts +106 -106
  277. package/src/swapper/Swapper.ts +2488 -2488
  278. package/src/swapper/SwapperFactory.ts +307 -307
  279. package/src/swapper/SwapperUtils.ts +570 -570
  280. package/src/swapper/SwapperWithChain.ts +707 -707
  281. package/src/swapper/SwapperWithSigner.ts +511 -511
  282. package/src/swaps/IAddressSwap.ts +30 -30
  283. package/src/swaps/IBTCWalletSwap.ts +92 -92
  284. package/src/swaps/IClaimableSwap.ts +65 -65
  285. package/src/swaps/IClaimableSwapWrapper.ts +17 -17
  286. package/src/swaps/IRefundableSwap.ts +58 -58
  287. package/src/swaps/ISwap.ts +703 -703
  288. package/src/swaps/ISwapWithGasDrop.ts +25 -25
  289. package/src/swaps/ISwapWrapper.ts +539 -539
  290. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
  291. package/src/swaps/escrow_swaps/IEscrowSwap.ts +269 -269
  292. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +282 -282
  293. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +169 -169
  294. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +300 -300
  295. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +107 -107
  296. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1473 -1474
  297. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +601 -601
  298. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1582 -1582
  299. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +750 -750
  300. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1299 -1299
  301. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +610 -610
  302. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1096 -1096
  303. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +138 -138
  304. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +304 -304
  305. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +786 -786
  306. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
  307. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +401 -401
  308. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1812 -1812
  309. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1236 -1236
  310. package/src/swaps/trusted/ln/LnForGasSwap.ts +589 -589
  311. package/src/swaps/trusted/ln/LnForGasWrapper.ts +91 -91
  312. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +862 -862
  313. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +131 -131
  314. package/src/types/AmountData.ts +9 -9
  315. package/src/types/CustomPriceFunction.ts +11 -11
  316. package/src/types/PriceInfoType.ts +66 -66
  317. package/src/types/SwapExecutionAction.ts +99 -99
  318. package/src/types/SwapStateInfo.ts +6 -6
  319. package/src/types/SwapWithSigner.ts +61 -61
  320. package/src/types/Token.ts +163 -163
  321. package/src/types/TokenAmount.ts +132 -132
  322. package/src/types/fees/Fee.ts +56 -56
  323. package/src/types/fees/FeeBreakdown.ts +11 -11
  324. package/src/types/fees/PercentagePPM.ts +26 -26
  325. package/src/types/lnurl/LNURLPay.ts +79 -79
  326. package/src/types/lnurl/LNURLWithdraw.ts +61 -61
  327. package/src/types/wallets/LightningInvoiceCreateService.ts +30 -30
  328. package/src/types/wallets/MinimalBitcoinWalletInterface.ts +21 -21
  329. package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +9 -9
  330. package/src/utils/AutomaticClockDriftCorrection.ts +71 -71
  331. package/src/utils/BitcoinUtils.ts +132 -132
  332. package/src/utils/BitcoinWalletUtils.ts +15 -15
  333. package/src/utils/Logger.ts +14 -14
  334. package/src/utils/RetryUtils.ts +78 -78
  335. package/src/utils/SwapUtils.ts +99 -99
  336. package/src/utils/TimeoutUtils.ts +49 -49
  337. package/src/utils/TokenUtils.ts +33 -33
  338. package/src/utils/TypeUtils.ts +8 -8
  339. package/src/utils/Utils.ts +212 -212
@@ -1,1448 +1,1448 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SpvFromBTCSwap = exports.isSpvFromBTCSwapInit = exports.SpvFromBTCSwapState = void 0;
4
- const ISwap_1 = require("../ISwap");
5
- const base_1 = require("@atomiqlabs/base");
6
- const SwapType_1 = require("../../enums/SwapType");
7
- const Utils_1 = require("../../utils/Utils");
8
- const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
9
- const btc_signer_1 = require("@scure/btc-signer");
10
- const buffer_1 = require("buffer");
11
- const IBitcoinWallet_1 = require("../../bitcoin/wallet/IBitcoinWallet");
12
- const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
13
- const FeeType_1 = require("../../enums/FeeType");
14
- const PercentagePPM_1 = require("../../types/fees/PercentagePPM");
15
- const TokenAmount_1 = require("../../types/TokenAmount");
16
- const Token_1 = require("../../types/Token");
17
- const Logger_1 = require("../../utils/Logger");
18
- const TimeoutUtils_1 = require("../../utils/TimeoutUtils");
19
- const PriceInfoType_1 = require("../../types/PriceInfoType");
20
- const BitcoinWalletUtils_1 = require("../../utils/BitcoinWalletUtils");
21
- /**
22
- * State enum for SPV vault (UTXO-controlled vault) based swaps
23
- * @category Swaps/Bitcoin → Smart chain
24
- */
25
- var SpvFromBTCSwapState;
26
- (function (SpvFromBTCSwapState) {
27
- /**
28
- * Catastrophic failure has occurred when processing the swap on the smart chain side,
29
- * this implies a bug in the smart contract code or the user and intermediary deliberately
30
- * creating a bitcoin transaction with invalid format unparsable by the smart contract.
31
- */
32
- SpvFromBTCSwapState[SpvFromBTCSwapState["CLOSED"] = -5] = "CLOSED";
33
- /**
34
- * Some of the bitcoin swap transaction inputs were double-spent, this means the swap
35
- * has failed and no BTC was sent
36
- */
37
- SpvFromBTCSwapState[SpvFromBTCSwapState["FAILED"] = -4] = "FAILED";
38
- /**
39
- * The intermediary (LP) declined to co-sign the submitted PSBT, hence the swap failed
40
- */
41
- SpvFromBTCSwapState[SpvFromBTCSwapState["DECLINED"] = -3] = "DECLINED";
42
- /**
43
- * Swap has expired for good and there is no way how it can be executed anymore
44
- */
45
- SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
46
- /**
47
- * A swap is almost expired, and it should be presented to the user as expired, though
48
- * there is still a chance that it will be processed
49
- */
50
- SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
51
- /**
52
- * Swap was created, use the {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.getPsbt} functions
53
- * to get the bitcoin swap PSBT that should be signed by the user's wallet and then submitted via the
54
- * {@link SpvFromBTCSwap.submitPsbt} function.
55
- */
56
- SpvFromBTCSwapState[SpvFromBTCSwapState["CREATED"] = 0] = "CREATED";
57
- /**
58
- * Swap bitcoin PSBT was submitted by the client to the SDK
59
- */
60
- SpvFromBTCSwapState[SpvFromBTCSwapState["SIGNED"] = 1] = "SIGNED";
61
- /**
62
- * Swap bitcoin PSBT sent to the intermediary (LP), waiting for the intermediary co-sign
63
- * it and broadcast. You can use the {@link SpvFromBTCSwap.waitTillClaimedOrFronted}
64
- * function to wait till the intermediary broadcasts the transaction and the transaction
65
- * confirms.
66
- */
67
- SpvFromBTCSwapState[SpvFromBTCSwapState["POSTED"] = 2] = "POSTED";
68
- /**
69
- * Intermediary (LP) has co-signed and broadcasted the bitcoin transaction. You can use the
70
- * {@link SpvFromBTCSwap.waitTillClaimedOrFronted} function to wait till the transaction
71
- * confirms.
72
- */
73
- SpvFromBTCSwapState[SpvFromBTCSwapState["BROADCASTED"] = 3] = "BROADCASTED";
74
- /**
75
- * Settlement on the destination smart chain was fronted and funds were already received
76
- * by the user, even before the final settlement.
77
- */
78
- SpvFromBTCSwapState[SpvFromBTCSwapState["FRONTED"] = 4] = "FRONTED";
79
- /**
80
- * Bitcoin transaction confirmed with necessary amount of confirmations, wait for automatic
81
- * settlement by the watchtower with the {@link waitTillClaimedOrFronted} function, or settle manually
82
- * using the {@link FromBTCSwap.claim} or {@link FromBTCSwap.txsClaim} function.
83
- */
84
- SpvFromBTCSwapState[SpvFromBTCSwapState["BTC_TX_CONFIRMED"] = 5] = "BTC_TX_CONFIRMED";
85
- /**
86
- * Swap settled on the smart chain and funds received
87
- */
88
- SpvFromBTCSwapState[SpvFromBTCSwapState["CLAIMED"] = 6] = "CLAIMED";
89
- })(SpvFromBTCSwapState = exports.SpvFromBTCSwapState || (exports.SpvFromBTCSwapState = {}));
90
- const SpvFromBTCSwapStateDescription = {
91
- [SpvFromBTCSwapState.CLOSED]: "Catastrophic failure has occurred when processing the swap on the smart chain side, this implies a bug in the smart contract code or the user and intermediary deliberately creating a bitcoin transaction with invalid format unparsable by the smart contract.",
92
- [SpvFromBTCSwapState.FAILED]: "Some of the bitcoin swap transaction inputs were double-spent, this means the swap has failed and no BTC was sent",
93
- [SpvFromBTCSwapState.DECLINED]: "The intermediary (LP) declined to co-sign the submitted PSBT, hence the swap failed",
94
- [SpvFromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
95
- [SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is almost expired, and it should be presented to the user as expired, though there is still a chance that it will be processed",
96
- [SpvFromBTCSwapState.CREATED]: "Swap was created, get the bitcoin swap PSBT that should be signed by the user's wallet and then submit it back to the SDK.",
97
- [SpvFromBTCSwapState.SIGNED]: "Swap bitcoin PSBT was submitted by the client to the SDK",
98
- [SpvFromBTCSwapState.POSTED]: "Swap bitcoin PSBT sent to the intermediary (LP), waiting for the intermediary co-sign it and broadcast.",
99
- [SpvFromBTCSwapState.BROADCASTED]: "Intermediary (LP) has co-signed and broadcasted the bitcoin transaction.",
100
- [SpvFromBTCSwapState.FRONTED]: "Settlement on the destination smart chain was fronted and funds were already received by the user, even before the final settlement.",
101
- [SpvFromBTCSwapState.BTC_TX_CONFIRMED]: "Bitcoin transaction confirmed with necessary amount of confirmations, wait for automatic settlement by the watchtower or settle manually.",
102
- [SpvFromBTCSwapState.CLAIMED]: "Swap settled on the smart chain and funds received"
103
- };
104
- function isSpvFromBTCSwapInit(obj) {
105
- return typeof obj === "object" &&
106
- typeof (obj.quoteId) === "string" &&
107
- typeof (obj.recipient) === "string" &&
108
- typeof (obj.vaultOwner) === "string" &&
109
- typeof (obj.vaultId) === "bigint" &&
110
- typeof (obj.vaultRequiredConfirmations) === "number" &&
111
- Array.isArray(obj.vaultTokenMultipliers) && obj.vaultTokenMultipliers.reduce((prev, curr) => prev && typeof (curr) === "bigint", true) &&
112
- typeof (obj.vaultBtcAddress) === "string" &&
113
- typeof (obj.vaultUtxo) === "string" &&
114
- typeof (obj.vaultUtxoValue) === "bigint" &&
115
- typeof (obj.btcDestinationAddress) === "string" &&
116
- typeof (obj.btcAmount) === "bigint" &&
117
- typeof (obj.btcAmountSwap) === "bigint" &&
118
- typeof (obj.btcAmountGas) === "bigint" &&
119
- typeof (obj.minimumBtcFeeRate) === "number" &&
120
- typeof (obj.outputTotalSwap) === "bigint" &&
121
- typeof (obj.outputSwapToken) === "string" &&
122
- typeof (obj.outputTotalGas) === "bigint" &&
123
- typeof (obj.outputGasToken) === "string" &&
124
- typeof (obj.gasSwapFeeBtc) === "bigint" &&
125
- typeof (obj.gasSwapFee) === "bigint" &&
126
- typeof (obj.callerFeeShare) === "bigint" &&
127
- typeof (obj.frontingFeeShare) === "bigint" &&
128
- typeof (obj.executionFeeShare) === "bigint" &&
129
- typeof (obj.genesisSmartChainBlockHeight) === "number" &&
130
- (obj.gasPricingInfo == null || (0, PriceInfoType_1.isPriceInfoType)(obj.gasPricingInfo)) &&
131
- (0, ISwap_1.isISwapInit)(obj);
132
- }
133
- exports.isSpvFromBTCSwapInit = isSpvFromBTCSwapInit;
134
- /**
135
- * New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
136
- * any initiation on the destination chain, and with the added possibility for the user to receive
137
- * a native token on the destination chain as part of the swap (a "gas drop" feature).
138
- *
139
- * @category Swaps/Bitcoin → Smart chain
140
- */
141
- class SpvFromBTCSwap extends ISwap_1.ISwap {
142
- constructor(wrapper, initOrObject) {
143
- if (isSpvFromBTCSwapInit(initOrObject) && initOrObject.url != null)
144
- initOrObject.url += "/frombtc_spv";
145
- super(wrapper, initOrObject);
146
- this.currentVersion = 2;
147
- this.TYPE = SwapType_1.SwapType.SPV_VAULT_FROM_BTC;
148
- /**
149
- * @internal
150
- */
151
- this.swapStateDescription = SpvFromBTCSwapStateDescription;
152
- /**
153
- * @internal
154
- */
155
- this.swapStateName = (state) => SpvFromBTCSwapState[state];
156
- if (isSpvFromBTCSwapInit(initOrObject)) {
157
- this._state = SpvFromBTCSwapState.CREATED;
158
- this.quoteId = initOrObject.quoteId;
159
- this.recipient = initOrObject.recipient;
160
- this.vaultOwner = initOrObject.vaultOwner;
161
- this.vaultId = initOrObject.vaultId;
162
- this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
163
- this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers;
164
- this.vaultBtcAddress = initOrObject.vaultBtcAddress;
165
- this.vaultUtxo = initOrObject.vaultUtxo;
166
- this.vaultUtxoValue = initOrObject.vaultUtxoValue;
167
- this.btcDestinationAddress = initOrObject.btcDestinationAddress;
168
- this.btcAmount = initOrObject.btcAmount;
169
- this.btcAmountSwap = initOrObject.btcAmountSwap;
170
- this.btcAmountGas = initOrObject.btcAmountGas;
171
- this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
172
- this.outputTotalSwap = initOrObject.outputTotalSwap;
173
- this.outputSwapToken = initOrObject.outputSwapToken;
174
- this.outputTotalGas = initOrObject.outputTotalGas;
175
- this.outputGasToken = initOrObject.outputGasToken;
176
- this.gasSwapFeeBtc = initOrObject.gasSwapFeeBtc;
177
- this.gasSwapFee = initOrObject.gasSwapFee;
178
- this.callerFeeShare = initOrObject.callerFeeShare;
179
- this.frontingFeeShare = initOrObject.frontingFeeShare;
180
- this.executionFeeShare = initOrObject.executionFeeShare;
181
- this._genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
182
- this.gasPricingInfo = initOrObject.gasPricingInfo;
183
- const vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)((0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress));
184
- if (vaultAddressType !== "p2tr" && vaultAddressType !== "p2wpkh" && vaultAddressType !== "p2wsh")
185
- throw new Error("Vault address type must be of witness type: p2tr, p2wpkh, p2wsh");
186
- }
187
- else {
188
- this.quoteId = initOrObject.quoteId;
189
- this.recipient = initOrObject.recipient;
190
- this.vaultOwner = initOrObject.vaultOwner;
191
- this.vaultId = BigInt(initOrObject.vaultId);
192
- this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
193
- this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers.map((val) => BigInt(val));
194
- this.vaultBtcAddress = initOrObject.vaultBtcAddress;
195
- this.vaultUtxo = initOrObject.vaultUtxo;
196
- this.vaultUtxoValue = BigInt(initOrObject.vaultUtxoValue);
197
- this.btcDestinationAddress = initOrObject.btcDestinationAddress;
198
- this.btcAmount = BigInt(initOrObject.btcAmount);
199
- this.btcAmountSwap = BigInt(initOrObject.btcAmountSwap);
200
- this.btcAmountGas = BigInt(initOrObject.btcAmountGas);
201
- this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
202
- this.outputTotalSwap = BigInt(initOrObject.outputTotalSwap);
203
- this.outputSwapToken = initOrObject.outputSwapToken;
204
- this.outputTotalGas = BigInt(initOrObject.outputTotalGas);
205
- this.outputGasToken = initOrObject.outputGasToken;
206
- this.gasSwapFeeBtc = BigInt(initOrObject.gasSwapFeeBtc);
207
- this.gasSwapFee = BigInt(initOrObject.gasSwapFee);
208
- this.callerFeeShare = BigInt(initOrObject.callerFeeShare);
209
- this.frontingFeeShare = BigInt(initOrObject.frontingFeeShare);
210
- this.executionFeeShare = BigInt(initOrObject.executionFeeShare);
211
- this._genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
212
- this._senderAddress = initOrObject.senderAddress;
213
- this._claimTxId = initOrObject.claimTxId;
214
- this._frontTxId = initOrObject.frontTxId;
215
- this.gasPricingInfo = (0, PriceInfoType_1.deserializePriceInfoType)(initOrObject.gasPricingInfo);
216
- this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
217
- this.posted = initOrObject.posted;
218
- if (initOrObject.data != null)
219
- this._data = new (this.wrapper._spvWithdrawalDataDeserializer(this._contractVersion))(initOrObject.data);
220
- }
221
- this.tryCalculateSwapFee();
222
- this.logger = (0, Logger_1.getLogger)("SPVFromBTC(" + this.getId() + "): ");
223
- this._contract = wrapper._contract(this._contractVersion);
224
- }
225
- /**
226
- * @inheritDoc
227
- * @internal
228
- */
229
- upgradeVersion() {
230
- if (this.version === 1) {
231
- this.posted = this.initiated && this._data != null;
232
- this.version = 2;
233
- }
234
- }
235
- /**
236
- * @inheritDoc
237
- * @internal
238
- */
239
- tryCalculateSwapFee() {
240
- if (this.swapFeeBtc == null && this.swapFee != null) {
241
- this.swapFeeBtc = this.swapFee * this.btcAmountSwap / this.getOutputWithoutFee().rawAmount;
242
- }
243
- if (this.pricingInfo != null && this.pricingInfo.swapPriceUSatPerToken == null) {
244
- const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
245
- this.pricingInfo = this.wrapper._prices.recomputePriceInfoReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken);
246
- this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
247
- }
248
- }
249
- //////////////////////////////
250
- //// Pricing
251
- /**
252
- * @inheritDoc
253
- */
254
- async refreshPriceData() {
255
- if (this.pricingInfo == null)
256
- return;
257
- const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
258
- this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken, undefined, undefined, this.swapFeeBtc);
259
- this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
260
- }
261
- //////////////////////////////
262
- //// Getters & utils
263
- /**
264
- * @inheritDoc
265
- * @internal
266
- */
267
- _getInitiator() {
268
- return this.recipient;
269
- }
270
- /**
271
- * @inheritDoc
272
- * @internal
273
- */
274
- _getEscrowHash() {
275
- return this._data?.btcTx?.txid ?? null;
276
- }
277
- /**
278
- * @inheritDoc
279
- */
280
- getId() {
281
- return this.quoteId + this._randomNonce;
282
- }
283
- /**
284
- * @inheritDoc
285
- */
286
- getQuoteExpiry() {
287
- return this.expiry - 20 * 1000;
288
- }
289
- /**
290
- * @inheritDoc
291
- * @internal
292
- */
293
- _verifyQuoteDefinitelyExpired() {
294
- return Promise.resolve(this.expiry < Date.now());
295
- }
296
- /**
297
- * @inheritDoc
298
- * @internal
299
- */
300
- _verifyQuoteValid() {
301
- return Promise.resolve(this.expiry > Date.now() && (this._state === SpvFromBTCSwapState.CREATED || this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED));
302
- }
303
- /**
304
- * @inheritDoc
305
- */
306
- getOutputAddress() {
307
- return this.recipient;
308
- }
309
- /**
310
- * @inheritDoc
311
- */
312
- getOutputTxId() {
313
- return this._frontTxId ?? this._claimTxId ?? null;
314
- }
315
- /**
316
- * @inheritDoc
317
- */
318
- getInputAddress() {
319
- return this._senderAddress ?? null;
320
- }
321
- /**
322
- * @inheritDoc
323
- */
324
- getInputTxId() {
325
- return this._data?.btcTx?.txid ?? null;
326
- }
327
- /**
328
- * @inheritDoc
329
- */
330
- requiresAction() {
331
- return this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
332
- }
333
- /**
334
- * @inheritDoc
335
- */
336
- isFinished() {
337
- return this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.QUOTE_EXPIRED || this._state === SpvFromBTCSwapState.FAILED;
338
- }
339
- /**
340
- * @inheritDoc
341
- */
342
- isClaimable() {
343
- return this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
344
- }
345
- /**
346
- * @inheritDoc
347
- */
348
- isSuccessful() {
349
- return this._state === SpvFromBTCSwapState.FRONTED || this._state === SpvFromBTCSwapState.CLAIMED;
350
- }
351
- /**
352
- * @inheritDoc
353
- */
354
- isFailed() {
355
- return this._state === SpvFromBTCSwapState.FAILED || this._state === SpvFromBTCSwapState.DECLINED || this._state === SpvFromBTCSwapState.CLOSED;
356
- }
357
- /**
358
- * @inheritDoc
359
- */
360
- isInProgress() {
361
- return this._state === SpvFromBTCSwapState.POSTED ||
362
- this._state === SpvFromBTCSwapState.BROADCASTED ||
363
- this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
364
- }
365
- /**
366
- * @inheritDoc
367
- */
368
- isQuoteExpired() {
369
- return this._state === SpvFromBTCSwapState.QUOTE_EXPIRED;
370
- }
371
- /**
372
- * @inheritDoc
373
- */
374
- isQuoteSoftExpired() {
375
- return this._state === SpvFromBTCSwapState.QUOTE_EXPIRED || this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
376
- }
377
- /**
378
- * Returns the data about used spv vault (UTXO-controlled vault) to perform the swap
379
- */
380
- getSpvVaultData() {
381
- return {
382
- owner: this.vaultOwner,
383
- vaultId: this.vaultId,
384
- utxo: this.vaultUtxo
385
- };
386
- }
387
- //////////////////////////////
388
- //// Amounts & fees
389
- /**
390
- * Returns the input BTC amount in sats without any fees
391
- *
392
- * @internal
393
- */
394
- getInputSwapAmountWithoutFee() {
395
- return (this.btcAmountSwap - this.swapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare);
396
- }
397
- /**
398
- * Returns the input gas BTC amount in sats without any fees
399
- *
400
- * @internal
401
- */
402
- getInputGasAmountWithoutFee() {
403
- return (this.btcAmountGas - this.gasSwapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare);
404
- }
405
- /**
406
- * Returns to total input BTC amount in sats without any fees (this is BTC amount for the swap + BTC amount
407
- * for the gas drop).
408
- *
409
- * @internal
410
- */
411
- getInputAmountWithoutFee() {
412
- return this.getInputSwapAmountWithoutFee() + this.getInputGasAmountWithoutFee();
413
- }
414
- /**
415
- * Returns the swap output amount without any fees, this value is therefore always higher than
416
- * the actual received output.
417
- *
418
- * @internal
419
- */
420
- getOutputWithoutFee() {
421
- return (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare) / 100000n) + (this.swapFee ?? 0n), this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo);
422
- }
423
- /**
424
- * Returns the swap fee charged by the intermediary (LP) on this swap
425
- *
426
- * @internal
427
- */
428
- getSwapFee() {
429
- if (this.pricingInfo == null)
430
- throw new Error("No pricing info known, cannot estimate fee!");
431
- const outputToken = this.wrapper._tokens[this.outputSwapToken];
432
- const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
433
- * (10n ** BigInt(outputToken.decimals))
434
- * 1000000n
435
- / this.pricingInfo.swapPriceUSatPerToken;
436
- const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
437
- const swapFeePPM = feeWithoutBaseFee * 1000000n / (this.btcAmount - this.swapFeeBtc - this.gasSwapFeeBtc);
438
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(this.swapFeeBtc + this.gasSwapFeeBtc, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
439
- return {
440
- amountInSrcToken,
441
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
442
- currentUsdValue: amountInSrcToken.currentUsdValue,
443
- usdValue: amountInSrcToken.usdValue,
444
- pastUsdValue: amountInSrcToken.pastUsdValue,
445
- composition: {
446
- base: (0, TokenAmount_1.toTokenAmount)(this.pricingInfo.satsBaseFee, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo),
447
- percentage: (0, PercentagePPM_1.ppmToPercentage)(swapFeePPM)
448
- }
449
- };
450
- }
451
- /**
452
- * Returns the fee to be paid to watchtowers on the destination chain to automatically
453
- * process and settle this swap without requiring any user interaction
454
- *
455
- * @internal
456
- */
457
- getWatchtowerFee() {
458
- if (this.pricingInfo == null)
459
- throw new Error("No pricing info known, cannot estimate fee!");
460
- const totalFeeShare = this.callerFeeShare + this.frontingFeeShare;
461
- const outputToken = this.wrapper._tokens[this.outputSwapToken];
462
- const watchtowerFeeInOutputToken = this.getInputGasAmountWithoutFee() * totalFeeShare
463
- * (10n ** BigInt(outputToken.decimals))
464
- * 1000000n
465
- / this.pricingInfo.swapPriceUSatPerToken
466
- / 100000n;
467
- const feeBtc = this.getInputAmountWithoutFee() * (totalFeeShare + this.executionFeeShare) / 100000n;
468
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(feeBtc, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
469
- return {
470
- amountInSrcToken,
471
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (totalFeeShare + this.executionFeeShare) / 100000n) + watchtowerFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
472
- currentUsdValue: amountInSrcToken.currentUsdValue,
473
- usdValue: amountInSrcToken.usdValue,
474
- pastUsdValue: amountInSrcToken.pastUsdValue
475
- };
476
- }
477
- /**
478
- * @inheritDoc
479
- */
480
- getFee() {
481
- const swapFee = this.getSwapFee();
482
- const watchtowerFee = this.getWatchtowerFee();
483
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
484
- return {
485
- amountInSrcToken,
486
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)(swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount, this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo),
487
- currentUsdValue: amountInSrcToken.currentUsdValue,
488
- usdValue: amountInSrcToken.usdValue,
489
- pastUsdValue: amountInSrcToken.pastUsdValue
490
- };
491
- }
492
- /**
493
- * @inheritDoc
494
- */
495
- getFeeBreakdown() {
496
- return [
497
- {
498
- type: FeeType_1.FeeType.SWAP,
499
- fee: this.getSwapFee()
500
- },
501
- {
502
- type: FeeType_1.FeeType.NETWORK_OUTPUT,
503
- fee: this.getWatchtowerFee()
504
- }
505
- ];
506
- }
507
- /**
508
- * @inheritDoc
509
- */
510
- getOutputToken() {
511
- return this.wrapper._tokens[this.outputSwapToken];
512
- }
513
- /**
514
- * @inheritDoc
515
- */
516
- getOutput() {
517
- return (0, TokenAmount_1.toTokenAmount)(this.outputTotalSwap, this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo);
518
- }
519
- /**
520
- * @inheritDoc
521
- */
522
- getGasDropOutput() {
523
- return (0, TokenAmount_1.toTokenAmount)(this.outputTotalGas, this.wrapper._tokens[this.outputGasToken], this.wrapper._prices, this.gasPricingInfo);
524
- }
525
- /**
526
- * @inheritDoc
527
- */
528
- getInputWithoutFee() {
529
- return (0, TokenAmount_1.toTokenAmount)(this.getInputAmountWithoutFee(), Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
530
- }
531
- /**
532
- * @inheritDoc
533
- */
534
- getInputToken() {
535
- return Token_1.BitcoinTokens.BTC;
536
- }
537
- /**
538
- * @inheritDoc
539
- */
540
- getInput() {
541
- return (0, TokenAmount_1.toTokenAmount)(this.btcAmount, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
542
- }
543
- //////////////////////////////
544
- //// Bitcoin tx
545
- /**
546
- * @inheritDoc
547
- */
548
- getRequiredConfirmationsCount() {
549
- return this.vaultRequiredConfirmations;
550
- }
551
- /**
552
- * Returns raw transaction details that can be used to manually create a swap PSBT. It is better to use
553
- * the {@link getPsbt} or {@link getFundedPsbt} function retrieve an already prepared PSBT.
554
- */
555
- async getTransactionDetails() {
556
- const [txId, voutStr] = this.vaultUtxo.split(":");
557
- const vaultScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress);
558
- const out2script = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.btcDestinationAddress);
559
- const opReturnData = this._contract.toOpReturnData(this.recipient, [
560
- this.outputTotalSwap / this.vaultTokenMultipliers[0],
561
- this.outputTotalGas / this.vaultTokenMultipliers[1]
562
- ]);
563
- const out1script = buffer_1.Buffer.concat([
564
- opReturnData.length > 75 ? buffer_1.Buffer.from([0x6a, 0x4c, opReturnData.length]) : buffer_1.Buffer.from([0x6a, opReturnData.length]),
565
- opReturnData
566
- ]);
567
- if (this.callerFeeShare < 0n || this.callerFeeShare > 0xfffffn)
568
- throw new Error("Caller fee out of bounds!");
569
- if (this.frontingFeeShare < 0n || this.frontingFeeShare > 0xfffffn)
570
- throw new Error("Fronting fee out of bounds!");
571
- if (this.executionFeeShare < 0n || this.executionFeeShare > 0xfffffn)
572
- throw new Error("Execution fee out of bounds!");
573
- const nSequence0 = 0x80000000n | (this.callerFeeShare & 0xfffffn) | (this.frontingFeeShare & 1047552n) << 10n;
574
- const nSequence1 = 0x80000000n | (this.executionFeeShare & 0xfffffn) | (this.frontingFeeShare & 1023n) << 20n;
575
- return {
576
- in0txid: txId,
577
- in0vout: parseInt(voutStr),
578
- in0sequence: Number(nSequence0),
579
- vaultAmount: this.vaultUtxoValue,
580
- vaultScript,
581
- in1sequence: Number(nSequence1),
582
- out1script,
583
- out2amount: this.btcAmount,
584
- out2script,
585
- locktime: 500000000 + Math.floor(Math.random() * 1000000000) //Use this as a random salt to make the btc txId unique!
586
- };
587
- }
588
- /**
589
- * Returns the raw PSBT (not funded), the wallet should fund the PSBT (add its inputs) and importantly **set the nSequence field of the
590
- * 2nd input** (input 1 - indexing from 0) to the value returned in `in1sequence`, sign the PSBT and then pass
591
- * it back to the swap with {@link submitPsbt} function.
592
- */
593
- async getPsbt() {
594
- const res = await this.getTransactionDetails();
595
- const psbt = new btc_signer_1.Transaction({
596
- allowUnknownOutputs: true,
597
- allowLegacyWitnessUtxo: true,
598
- lockTime: res.locktime
599
- });
600
- psbt.addInput({
601
- txid: res.in0txid,
602
- index: res.in0vout,
603
- witnessUtxo: {
604
- amount: res.vaultAmount,
605
- script: res.vaultScript
606
- },
607
- sequence: res.in0sequence
608
- });
609
- psbt.addOutput({
610
- amount: res.vaultAmount,
611
- script: res.vaultScript
612
- });
613
- psbt.addOutput({
614
- amount: 0n,
615
- script: res.out1script
616
- });
617
- psbt.addOutput({
618
- amount: res.out2amount,
619
- script: res.out2script
620
- });
621
- const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
622
- return {
623
- psbt,
624
- psbtHex: serializedPsbt.toString("hex"),
625
- psbtBase64: serializedPsbt.toString("base64"),
626
- in1sequence: res.in1sequence
627
- };
628
- }
629
- /**
630
- * Returns the PSBT that is already funded with wallet's UTXOs (runs a coin-selection algorithm to choose UTXOs to use),
631
- * also returns inputs indices that need to be signed by the wallet before submitting the PSBT back to the SDK with
632
- * {@link submitPsbt}
633
- *
634
- * @remarks
635
- * Note that when passing the `feeRate` argument, the fee must be at least {@link minimumBtcFeeRate} sats/vB.
636
- *
637
- * @param _bitcoinWallet Sender's bitcoin wallet
638
- * @param feeRate Optional fee rate in sats/vB for the transaction
639
- * @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
640
- * @param utxos Pre-fetched list of UTXOs to spend from
641
- * @param spendFully Instructs the wallet to spend all the passed UTXOs in the transaction without creating any
642
- * change output, if the `feeRate` is passed, it will also enforce that the feeRate in sats/vB for the resulting
643
- * transaction is not more than 50% and 10 sats/vB larger (considering also the CPFP adjustments)
644
- */
645
- async getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs, utxos, spendFully) {
646
- const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
647
- if (feeRate != null) {
648
- if (feeRate < this.minimumBtcFeeRate)
649
- throw new Error("Bitcoin tx fee needs to be at least " + this.minimumBtcFeeRate + " sats/vB");
650
- }
651
- else {
652
- feeRate = Math.max(this.minimumBtcFeeRate, await bitcoinWallet.getFeeRate());
653
- }
654
- let { psbt, in1sequence } = await this.getPsbt();
655
- if (additionalOutputs != null)
656
- additionalOutputs.forEach(output => {
657
- psbt.addOutput({
658
- amount: output.amount,
659
- script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
660
- });
661
- });
662
- psbt = await bitcoinWallet.fundPsbt(psbt, feeRate, utxos, spendFully);
663
- psbt.updateInput(1, { sequence: in1sequence });
664
- //Sign every input except the first one
665
- const signInputs = [];
666
- for (let i = 1; i < psbt.inputsLength; i++) {
667
- signInputs.push(i);
668
- }
669
- const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
670
- return {
671
- psbt,
672
- psbtHex: serializedPsbt.toString("hex"),
673
- psbtBase64: serializedPsbt.toString("base64"),
674
- signInputs
675
- };
676
- }
677
- /**
678
- * @inheritDoc
679
- */
680
- async submitPsbt(_psbt) {
681
- const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
682
- //Ensure not expired
683
- if (this.expiry < Date.now()) {
684
- throw new Error("Quote expired!");
685
- }
686
- //Ensure valid state
687
- if (this._state !== SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this._state !== SpvFromBTCSwapState.CREATED) {
688
- throw new Error("Invalid swap state!");
689
- }
690
- if (this.url == null)
691
- throw new Error("LP URL not known, cannot submit PSBT!");
692
- //Ensure all inputs except the 1st are finalized
693
- for (let i = 1; i < psbt.inputsLength; i++) {
694
- if ((0, btc_signer_1.getInputType)(psbt.getInput(i)).txType === "legacy")
695
- throw new Error("Legacy (non-segwit) inputs are not allowed in the transaction!");
696
- psbt.finalizeIdx(i);
697
- }
698
- const btcTx = await this.wrapper._btcRpc.parseTransaction(buffer_1.Buffer.from(psbt.toBytes(true)).toString("hex"));
699
- const data = await this._contract.getWithdrawalData(btcTx);
700
- this.logger.debug("submitPsbt(): parsed withdrawal data: ", data);
701
- //Verify correct withdrawal data
702
- if (!data.isRecipient(this.recipient) ||
703
- data.rawAmounts[0] * this.vaultTokenMultipliers[0] !== this.outputTotalSwap ||
704
- (data.rawAmounts[1] ?? 0n) * this.vaultTokenMultipliers[1] !== this.outputTotalGas ||
705
- data.callerFeeRate !== this.callerFeeShare ||
706
- data.frontingFeeRate !== this.frontingFeeShare ||
707
- data.executionFeeRate !== this.executionFeeShare ||
708
- data.getSpentVaultUtxo() !== this.vaultUtxo ||
709
- BigInt(data.getNewVaultBtcAmount()) !== this.vaultUtxoValue ||
710
- !data.getNewVaultScript().equals((0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress)) ||
711
- data.getExecutionData() != null) {
712
- throw new Error("Invalid withdrawal tx data submitted!");
713
- }
714
- //Verify correct LP output
715
- const lpOutput = psbt.getOutput(2);
716
- if (lpOutput.script == null ||
717
- lpOutput.amount !== this.btcAmount ||
718
- !(0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.btcDestinationAddress).equals(buffer_1.Buffer.from(lpOutput.script))) {
719
- throw new Error("Invalid LP bitcoin output in transaction!");
720
- }
721
- //Verify vault utxo not spent yet
722
- if (await this.wrapper._btcRpc.isSpent(this.vaultUtxo)) {
723
- throw new Error("Vault UTXO already spent, please create new swap!");
724
- }
725
- //Verify tx is parsable by the contract
726
- try {
727
- await this._contract.checkWithdrawalTx(data);
728
- }
729
- catch (e) {
730
- throw new Error("Transaction not parsable by the contract: " + (e.message ?? e.toString()));
731
- }
732
- //Ensure still not expired
733
- if (this.expiry < Date.now()) {
734
- throw new Error("Quote expired!");
735
- }
736
- this._data = data;
737
- this.initiated = true;
738
- this.posted = true;
739
- await this._saveAndEmit(SpvFromBTCSwapState.SIGNED);
740
- try {
741
- await IntermediaryAPI_1.IntermediaryAPI.initSpvFromBTC(this.chainIdentifier, this.url, {
742
- quoteId: this.quoteId,
743
- psbtHex: buffer_1.Buffer.from(psbt.toPSBT(0)).toString("hex")
744
- });
745
- await this._saveAndEmit(SpvFromBTCSwapState.POSTED);
746
- }
747
- catch (e) {
748
- await this._saveAndEmit(SpvFromBTCSwapState.DECLINED);
749
- throw e;
750
- }
751
- return this._data.getTxId();
752
- }
753
- /**
754
- * @inheritDoc
755
- */
756
- async estimateBitcoinFee(_bitcoinWallet, feeRate) {
757
- const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
758
- const txFee = await bitcoinWallet.getFundedPsbtFee((await this.getPsbt()).psbt, feeRate);
759
- if (txFee == null)
760
- return null;
761
- return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
762
- }
763
- /**
764
- * @inheritDoc
765
- */
766
- async sendBitcoinTransaction(wallet, feeRate, utxos, spendFully) {
767
- const { psbt, psbtBase64, psbtHex, signInputs } = await this.getFundedPsbt(wallet, feeRate, undefined, utxos, spendFully);
768
- let signedPsbt;
769
- if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
770
- signedPsbt = await wallet.signPsbt(psbt, signInputs);
771
- }
772
- else {
773
- signedPsbt = await wallet.signPsbt({
774
- psbt, psbtHex, psbtBase64
775
- }, signInputs);
776
- }
777
- return await this.submitPsbt(signedPsbt);
778
- }
779
- /**
780
- * Executes the swap with the provided bitcoin wallet
781
- *
782
- * @param wallet Bitcoin wallet to use to sign the bitcoin transaction
783
- * @param callbacks Callbacks to track the progress of the swap
784
- * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
785
- *
786
- * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
787
- * user, in case `false` is returned the user should call the {@link claim} function to settle the swap on the
788
- * destination manually
789
- */
790
- async execute(wallet, callbacks, options) {
791
- if (this._state === SpvFromBTCSwapState.CLOSED)
792
- throw new Error("Swap encountered a catastrophic failure!");
793
- if (this._state === SpvFromBTCSwapState.FAILED)
794
- throw new Error("Swap failed!");
795
- if (this._state === SpvFromBTCSwapState.DECLINED)
796
- throw new Error("Swap execution already declined by the LP!");
797
- if (this._state === SpvFromBTCSwapState.QUOTE_EXPIRED)
798
- throw new Error("Swap quote expired!");
799
- if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
800
- throw new Error("Swap already settled or fronted!");
801
- if (this._state === SpvFromBTCSwapState.CREATED) {
802
- const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate, options?.utxos, options?.spendFully);
803
- if (callbacks?.onSourceTransactionSent != null)
804
- callbacks.onSourceTransactionSent(txId);
805
- }
806
- if (this._state === SpvFromBTCSwapState.POSTED || this._state === SpvFromBTCSwapState.BROADCASTED) {
807
- const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
808
- if (callbacks?.onSourceTransactionConfirmed != null)
809
- callbacks.onSourceTransactionConfirmed(txId);
810
- }
811
- // @ts-ignore
812
- if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
813
- return true;
814
- if (this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
815
- const success = await this.waitTillClaimedOrFronted(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
816
- if (success && callbacks?.onSwapSettled != null)
817
- callbacks.onSwapSettled(this.getOutputTxId());
818
- return success;
819
- }
820
- throw new Error("Unexpected state reached!");
821
- }
822
- /**
823
- * @inheritDoc
824
- *
825
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
826
- * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
827
- * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
828
- * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
829
- * indexing from 0) to the value returned in `in1sequence`
830
- */
831
- async txsExecute(options) {
832
- if (this._state === SpvFromBTCSwapState.CREATED) {
833
- if (!await this._verifyQuoteValid())
834
- throw new Error("Quote already expired or close to expiry!");
835
- return [
836
- {
837
- name: "Payment",
838
- description: "Send funds to the bitcoin swap address",
839
- chain: "BITCOIN",
840
- txs: [
841
- options?.bitcoinWallet == null
842
- ? { ...await this.getPsbt(), type: "RAW_PSBT" }
843
- : { ...await this.getFundedPsbt(options.bitcoinWallet, options?.bitcoinFeeRate), type: "FUNDED_PSBT" }
844
- ]
845
- }
846
- ];
847
- }
848
- throw new Error("Invalid swap state to obtain execution txns, required CREATED");
849
- }
850
- /**
851
- * @inheritDoc
852
- *
853
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
854
- * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
855
- * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
856
- * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
857
- * indexing from 0) to the value returned in `in1sequence`
858
- * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
859
- * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
860
- * the bitcoin transaction is confirmed (defaults to 60 seconds)
861
- */
862
- async getCurrentActions(options) {
863
- if (this._state === SpvFromBTCSwapState.CREATED) {
864
- try {
865
- return await this.txsExecute(options);
866
- }
867
- catch (e) { }
868
- }
869
- if (this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
870
- if (this.btcTxConfirmedAt == null ||
871
- options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
872
- (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
873
- return [{
874
- name: "Claim",
875
- description: "Manually settle (claim) the swap on the destination smart chain",
876
- chain: this.chainIdentifier,
877
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
878
- }];
879
- }
880
- }
881
- return [];
882
- }
883
- //////////////////////////////
884
- //// Bitcoin tx listener
885
- /**
886
- * Checks whether a bitcoin payment was already made, returns the payment or null when no payment has been made.
887
- * @internal
888
- */
889
- async getBitcoinPayment() {
890
- if (this._data?.btcTx?.txid == null)
891
- return null;
892
- const result = await this.wrapper._btcRpc.getTransaction(this._data?.btcTx?.txid);
893
- if (result == null)
894
- return null;
895
- return {
896
- txId: result.txid,
897
- confirmations: result.confirmations ?? 0,
898
- targetConfirmations: this.vaultRequiredConfirmations,
899
- inputAddresses: result.inputAddresses
900
- };
901
- }
902
- /**
903
- * @inheritDoc
904
- *
905
- * @throws {Error} if in invalid state (must be {@link SpvFromBTCSwapState.POSTED} or
906
- * {@link SpvFromBTCSwapState.BROADCASTED} states)
907
- */
908
- async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
909
- if (this._state !== SpvFromBTCSwapState.POSTED &&
910
- this._state !== SpvFromBTCSwapState.BROADCASTED &&
911
- !(this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this.posted))
912
- throw new Error("Must be in POSTED or BROADCASTED state!");
913
- if (this._data == null)
914
- throw new Error("Expected swap to have withdrawal data filled!");
915
- const result = await this.wrapper._btcRpc.waitForTransaction(this._data.btcTx.txid, this.vaultRequiredConfirmations, (btcTx, txEtaMs) => {
916
- if (updateCallback != null)
917
- updateCallback(btcTx?.txid, btcTx?.confirmations, this.vaultRequiredConfirmations, txEtaMs);
918
- if (btcTx == null)
919
- return;
920
- let save = false;
921
- if (btcTx.inputAddresses != null && this._senderAddress == null) {
922
- this._senderAddress = btcTx.inputAddresses[1];
923
- save = true;
924
- }
925
- if (this._state === SpvFromBTCSwapState.POSTED || this._state == SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED) {
926
- this._state = SpvFromBTCSwapState.BROADCASTED;
927
- save = true;
928
- }
929
- if (save)
930
- this._saveAndEmit();
931
- }, abortSignal, checkIntervalSeconds);
932
- if (abortSignal != null)
933
- abortSignal.throwIfAborted();
934
- let save = false;
935
- if (result.inputAddresses != null && this._senderAddress == null) {
936
- this._senderAddress = result.inputAddresses[1];
937
- save = true;
938
- }
939
- if (this._state !== SpvFromBTCSwapState.FRONTED &&
940
- this._state !== SpvFromBTCSwapState.CLAIMED) {
941
- this.btcTxConfirmedAt ??= Date.now();
942
- this._state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
943
- save = true;
944
- }
945
- if (save)
946
- await this._saveAndEmit();
947
- return result.txid;
948
- }
949
- //////////////////////////////
950
- //// Claim
951
- /**
952
- * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
953
- * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
954
- * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
955
- *
956
- * @remarks
957
- * Might also return transactions necessary to sync the bitcoin light client.
958
- *
959
- * @param _signer Address of the signer to create the claim transactions for, can also be different to the recipient
960
- *
961
- * @throws {Error} If the swap is in invalid state (must be {@link SpvFromBTCSwapState.BTC_TX_CONFIRMED})
962
- */
963
- async txsClaim(_signer) {
964
- let address = undefined;
965
- if (_signer != null) {
966
- if (typeof (_signer) === "string") {
967
- address = _signer;
968
- }
969
- else if ((0, base_1.isAbstractSigner)(_signer)) {
970
- address = _signer.getAddress();
971
- }
972
- else {
973
- address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
974
- }
975
- }
976
- if (!this.isClaimable())
977
- throw new Error("Must be in BTC_TX_CONFIRMED state!");
978
- if (this._data == null)
979
- throw new Error("Expected swap to have withdrawal data filled!");
980
- const vaultData = await this._contract.getVaultData(this.vaultOwner, this.vaultId);
981
- if (vaultData == null)
982
- throw new Error(`Vault data for ${this.vaultOwner}:${this.vaultId.toString(10)} not found (already closed???)!`);
983
- const btcTx = await this.wrapper._btcRpc.getTransaction(this._data.btcTx.txid);
984
- if (btcTx == null)
985
- throw new Error(`Bitcoin transaction ${this._data.btcTx.txid} not found!`);
986
- const txs = [btcTx];
987
- //Trace back from current tx to the vaultData-specified UTXO
988
- const vaultUtxo = vaultData.getUtxo();
989
- while (txs[0].ins[0].txid + ":" + txs[0].ins[0].vout !== vaultUtxo) {
990
- const btcTx = await this.wrapper._btcRpc.getTransaction(txs[0].ins[0].txid);
991
- if (btcTx == null)
992
- throw new Error(`Prior withdrawal bitcoin transaction ${this._data.btcTx.txid} not found!`);
993
- txs.unshift(btcTx);
994
- }
995
- //Parse transactions to withdrawal data
996
- const withdrawalData = [];
997
- for (let tx of txs) {
998
- withdrawalData.push(await this._contract.getWithdrawalData(tx));
999
- }
1000
- return await this._contract.txsClaim(address ?? this._getInitiator(), vaultData, withdrawalData.map(tx => { return { tx }; }), this.wrapper._synchronizer(this._contractVersion), true);
1001
- }
1002
- /**
1003
- * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
1004
- * check so with isClaimable.
1005
- *
1006
- * @remarks
1007
- * Might also sync the bitcoin light client during the process.
1008
- *
1009
- * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
1010
- * @param abortSignal Abort signal
1011
- * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
1012
- *
1013
- * @throws {Error} If the swap is in invalid state (must be {@link SpvFromBTCSwapState.BTC_TX_CONFIRMED})
1014
- */
1015
- async claim(_signer, abortSignal, onBeforeTxSent) {
1016
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1017
- let txIds;
1018
- try {
1019
- let txCount = 0;
1020
- const txs = await this.txsClaim(signer);
1021
- txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1022
- txCount++;
1023
- if (onBeforeTxSent != null && txCount === txs.length)
1024
- onBeforeTxSent(txId);
1025
- return Promise.resolve();
1026
- });
1027
- }
1028
- catch (e) {
1029
- if (this._data == null)
1030
- throw e;
1031
- this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
1032
- if (this._state === SpvFromBTCSwapState.CLAIMED) {
1033
- this.logger.info("claim(): Transaction state is CLAIMED, swap was successfully claimed by the watchtower");
1034
- return this._claimTxId;
1035
- }
1036
- const withdrawalState = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1037
- if (withdrawalState != null && withdrawalState.type === base_1.SpvWithdrawalStateType.CLAIMED) {
1038
- this.logger.info("claim(): Transaction status is CLAIMED, swap was successfully claimed by the watchtower");
1039
- this._claimTxId = withdrawalState.txId;
1040
- await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1041
- return withdrawalState.txId;
1042
- }
1043
- throw e;
1044
- }
1045
- this._claimTxId = txIds[0];
1046
- if (this._state === SpvFromBTCSwapState.POSTED || this._state === SpvFromBTCSwapState.BROADCASTED ||
1047
- this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED || this._state === SpvFromBTCSwapState.FAILED ||
1048
- this._state === SpvFromBTCSwapState.FRONTED) {
1049
- await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1050
- }
1051
- return txIds[0];
1052
- }
1053
- /**
1054
- * Periodically checks the chain to see whether the swap was finished (claimed or refunded)
1055
- *
1056
- * @param abortSignal
1057
- * @param interval How often to check (in seconds), default to 5s
1058
- * @internal
1059
- */
1060
- async watchdogWaitTillResult(abortSignal, interval = 5) {
1061
- if (this._data == null)
1062
- throw new Error("Cannot await the result before the btc transaction is sent!");
1063
- let status = { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
1064
- while (status.type === base_1.SpvWithdrawalStateType.NOT_FOUND) {
1065
- await (0, TimeoutUtils_1.timeoutPromise)(interval * 1000, abortSignal);
1066
- try {
1067
- //Be smart about checking withdrawal state
1068
- if (await this._shouldCheckWithdrawalState()) {
1069
- status = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight) ?? { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
1070
- }
1071
- }
1072
- catch (e) {
1073
- this.logger.error("watchdogWaitTillResult(): Error when fetching commit status: ", e);
1074
- }
1075
- }
1076
- if (abortSignal != null)
1077
- abortSignal.throwIfAborted();
1078
- return status;
1079
- }
1080
- /**
1081
- * Waits till the swap is successfully settled (claimed), should be called after sending the claim (settlement)
1082
- * transactions manually to wait till the SDK processes the settlement and updates the swap state accordingly.
1083
- *
1084
- * @remarks
1085
- * This is an alias for the {@link waitTillClaimedOrFronted} function and will also resolve if the swap has
1086
- * been fronted (not necessarily claimed)
1087
- *
1088
- * @param maxWaitTimeSeconds – Maximum time in seconds to wait for the swap to be settled
1089
- * @param abortSignal – AbortSignal
1090
- *
1091
- * @returns Whether the swap was claimed in time or not
1092
- */
1093
- waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
1094
- return this.waitTillClaimedOrFronted(maxWaitTimeSeconds, abortSignal);
1095
- }
1096
- /**
1097
- * Waits till the swap is successfully fronted or settled on the destination chain
1098
- *
1099
- * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled (by default
1100
- * it waits indefinitely)
1101
- * @param abortSignal Abort signal
1102
- *
1103
- * @returns {boolean} whether the swap was claimed or fronted automatically or not, if the swap was not claimed
1104
- * the user can claim manually through the {@link claim} function
1105
- */
1106
- async waitTillClaimedOrFronted(maxWaitTimeSeconds, abortSignal) {
1107
- if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
1108
- return Promise.resolve(true);
1109
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1110
- let timedOut = false;
1111
- if (maxWaitTimeSeconds != null) {
1112
- const timeout = setTimeout(() => {
1113
- timedOut = true;
1114
- abortController.abort();
1115
- }, maxWaitTimeSeconds * 1000);
1116
- abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1117
- }
1118
- let res;
1119
- try {
1120
- res = await Promise.race([
1121
- this.watchdogWaitTillResult(abortController.signal),
1122
- this.waitTillState(SpvFromBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 0),
1123
- this.waitTillState(SpvFromBTCSwapState.FRONTED, "eq", abortController.signal).then(() => 1),
1124
- this.waitTillState(SpvFromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 2),
1125
- ]);
1126
- abortController.abort();
1127
- }
1128
- catch (e) {
1129
- abortController.abort();
1130
- if (timedOut)
1131
- return false;
1132
- throw e;
1133
- }
1134
- if (typeof (res) === "number") {
1135
- if (res === 0) {
1136
- this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (CLAIMED)");
1137
- return true;
1138
- }
1139
- if (res === 1) {
1140
- this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FRONTED)");
1141
- return true;
1142
- }
1143
- if (res === 2) {
1144
- this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FAILED)");
1145
- throw new Error("Swap failed while waiting for claim or front");
1146
- }
1147
- throw new Error("Invalid numeric response, this should never happen!");
1148
- }
1149
- this.logger.debug("waitTillClaimedOrFronted(): Resolved from watchdog");
1150
- if (res.type === base_1.SpvWithdrawalStateType.FRONTED) {
1151
- if (this._state !== SpvFromBTCSwapState.FRONTED ||
1152
- this._state !== SpvFromBTCSwapState.CLAIMED) {
1153
- this._frontTxId = res.txId;
1154
- await this._saveAndEmit(SpvFromBTCSwapState.FRONTED);
1155
- }
1156
- }
1157
- if (res.type === base_1.SpvWithdrawalStateType.CLAIMED) {
1158
- if (this._state !== SpvFromBTCSwapState.CLAIMED) {
1159
- this._claimTxId = res.txId;
1160
- await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1161
- }
1162
- }
1163
- if (res.type === base_1.SpvWithdrawalStateType.CLOSED) {
1164
- if (this._state !== SpvFromBTCSwapState.CLOSED)
1165
- await this._saveAndEmit(SpvFromBTCSwapState.CLOSED);
1166
- throw new Error("Swap failed with catastrophic error!");
1167
- }
1168
- return true;
1169
- }
1170
- /**
1171
- * Waits till the bitcoin transaction confirms and swap settled on the destination chain
1172
- *
1173
- * @param updateCallback Callback called when txId is found, and also called with subsequent confirmations
1174
- * @param checkIntervalSeconds How often to check the bitcoin transaction (5 seconds by default)
1175
- * @param abortSignal Abort signal
1176
- *
1177
- * @throws {Error} if in invalid state (must be {@link SpvFromBTCSwapState.POSTED} or
1178
- * {@link SpvFromBTCSwapState.BROADCASTED} states)
1179
- */
1180
- async waitTillExecuted(updateCallback, checkIntervalSeconds, abortSignal) {
1181
- await this.waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal);
1182
- await this.waitTillClaimedOrFronted(undefined, abortSignal);
1183
- }
1184
- //////////////////////////////
1185
- //// Storage
1186
- /**
1187
- * @inheritDoc
1188
- */
1189
- serialize() {
1190
- return {
1191
- ...super.serialize(),
1192
- quoteId: this.quoteId,
1193
- recipient: this.recipient,
1194
- vaultOwner: this.vaultOwner,
1195
- vaultId: this.vaultId.toString(10),
1196
- vaultRequiredConfirmations: this.vaultRequiredConfirmations,
1197
- vaultTokenMultipliers: this.vaultTokenMultipliers.map(val => val.toString(10)),
1198
- vaultBtcAddress: this.vaultBtcAddress,
1199
- vaultUtxo: this.vaultUtxo,
1200
- vaultUtxoValue: this.vaultUtxoValue.toString(10),
1201
- btcDestinationAddress: this.btcDestinationAddress,
1202
- btcAmount: this.btcAmount.toString(10),
1203
- btcAmountSwap: this.btcAmountSwap.toString(10),
1204
- btcAmountGas: this.btcAmountGas.toString(10),
1205
- minimumBtcFeeRate: this.minimumBtcFeeRate,
1206
- outputTotalSwap: this.outputTotalSwap.toString(10),
1207
- outputSwapToken: this.outputSwapToken,
1208
- outputTotalGas: this.outputTotalGas.toString(10),
1209
- outputGasToken: this.outputGasToken,
1210
- gasSwapFeeBtc: this.gasSwapFeeBtc.toString(10),
1211
- gasSwapFee: this.gasSwapFee.toString(10),
1212
- callerFeeShare: this.callerFeeShare.toString(10),
1213
- frontingFeeShare: this.frontingFeeShare.toString(10),
1214
- executionFeeShare: this.executionFeeShare.toString(10),
1215
- genesisSmartChainBlockHeight: this._genesisSmartChainBlockHeight,
1216
- gasPricingInfo: (0, PriceInfoType_1.serializePriceInfoType)(this.gasPricingInfo),
1217
- posted: this.posted,
1218
- senderAddress: this._senderAddress,
1219
- claimTxId: this._claimTxId,
1220
- frontTxId: this._frontTxId,
1221
- data: this._data?.serialize(),
1222
- btcTxConfirmedAt: this.btcTxConfirmedAt
1223
- };
1224
- }
1225
- //////////////////////////////
1226
- //// Swap ticks & sync
1227
- /**
1228
- * Used to set the txId of the bitcoin payment from the on-chain events listener
1229
- *
1230
- * @param txId
1231
- * @internal
1232
- */
1233
- async _setBitcoinTxId(txId) {
1234
- if (this._data == null)
1235
- return;
1236
- if (txId != this._data.btcTx.txid)
1237
- return;
1238
- if (this._senderAddress != null)
1239
- return;
1240
- const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
1241
- if (btcTx == null || btcTx.inputAddresses == null)
1242
- return;
1243
- this._senderAddress = btcTx.inputAddresses[1];
1244
- }
1245
- /**
1246
- * @internal
1247
- */
1248
- async _syncStateFromBitcoin(save) {
1249
- if (this._data?.btcTx == null)
1250
- return false;
1251
- //Check if bitcoin payment was confirmed
1252
- this.btcTxLastChecked = Date.now();
1253
- const res = await this.getBitcoinPayment();
1254
- if (res == null) {
1255
- //Check inputs double-spent
1256
- for (let input of this._data.btcTx.ins) {
1257
- if (await this.wrapper._btcRpc.isSpent(input.txid + ":" + input.vout, true)) {
1258
- if (this._state === SpvFromBTCSwapState.SIGNED ||
1259
- this._state === SpvFromBTCSwapState.POSTED ||
1260
- this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1261
- this._state === SpvFromBTCSwapState.DECLINED) {
1262
- //One of the inputs was double-spent
1263
- this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1264
- }
1265
- else {
1266
- //One of the inputs was double-spent
1267
- this._state = SpvFromBTCSwapState.FAILED;
1268
- }
1269
- if (save)
1270
- await this._saveAndEmit();
1271
- return true;
1272
- }
1273
- }
1274
- }
1275
- else {
1276
- let needsSave = false;
1277
- if (res.inputAddresses != null && this._senderAddress == null) {
1278
- this._senderAddress = res.inputAddresses[1];
1279
- needsSave = true;
1280
- }
1281
- if (res.confirmations >= this.vaultRequiredConfirmations) {
1282
- if (this._state !== SpvFromBTCSwapState.BTC_TX_CONFIRMED &&
1283
- this._state !== SpvFromBTCSwapState.FRONTED &&
1284
- this._state !== SpvFromBTCSwapState.CLAIMED) {
1285
- this.btcTxConfirmedAt ??= Date.now();
1286
- this._state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
1287
- needsSave = true;
1288
- }
1289
- }
1290
- else if (this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1291
- this._state === SpvFromBTCSwapState.POSTED ||
1292
- this._state === SpvFromBTCSwapState.SIGNED ||
1293
- this._state === SpvFromBTCSwapState.DECLINED) {
1294
- this._state = SpvFromBTCSwapState.BROADCASTED;
1295
- needsSave = true;
1296
- }
1297
- if (needsSave && save)
1298
- await this._saveAndEmit();
1299
- return needsSave;
1300
- }
1301
- return false;
1302
- }
1303
- /**
1304
- * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1305
- * data
1306
- */
1307
- async syncStateFromChain() {
1308
- let changed = false;
1309
- if (this._state === SpvFromBTCSwapState.SIGNED ||
1310
- this._state === SpvFromBTCSwapState.POSTED ||
1311
- this._state === SpvFromBTCSwapState.BROADCASTED ||
1312
- this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1313
- this._state === SpvFromBTCSwapState.DECLINED ||
1314
- this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1315
- //Check BTC transaction
1316
- if (await this._syncStateFromBitcoin(false))
1317
- changed ||= true;
1318
- }
1319
- if (this._state === SpvFromBTCSwapState.BROADCASTED || this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1320
- if (await this._shouldCheckWithdrawalState()) {
1321
- const status = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1322
- this.logger.debug("syncStateFromChain(): status of " + this._data.btcTx.txid, status);
1323
- switch (status?.type) {
1324
- case base_1.SpvWithdrawalStateType.FRONTED:
1325
- this._frontTxId = status.txId;
1326
- this._state = SpvFromBTCSwapState.FRONTED;
1327
- changed ||= true;
1328
- break;
1329
- case base_1.SpvWithdrawalStateType.CLAIMED:
1330
- this._claimTxId = status.txId;
1331
- this._state = SpvFromBTCSwapState.CLAIMED;
1332
- changed ||= true;
1333
- break;
1334
- case base_1.SpvWithdrawalStateType.CLOSED:
1335
- this._state = SpvFromBTCSwapState.CLOSED;
1336
- changed ||= true;
1337
- break;
1338
- }
1339
- }
1340
- }
1341
- if (this._state === SpvFromBTCSwapState.CREATED ||
1342
- this._state === SpvFromBTCSwapState.SIGNED ||
1343
- this._state === SpvFromBTCSwapState.POSTED) {
1344
- if (this.expiry < Date.now()) {
1345
- if (this._state === SpvFromBTCSwapState.CREATED) {
1346
- this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1347
- }
1348
- else {
1349
- this._state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
1350
- }
1351
- changed ||= true;
1352
- }
1353
- }
1354
- return changed;
1355
- }
1356
- /**
1357
- * @inheritDoc
1358
- * @internal
1359
- */
1360
- async _sync(save) {
1361
- const changed = await this.syncStateFromChain();
1362
- if (changed && save)
1363
- await this._saveAndEmit();
1364
- return changed;
1365
- }
1366
- /**
1367
- * @inheritDoc
1368
- * @internal
1369
- */
1370
- async _tick(save) {
1371
- if (this._state === SpvFromBTCSwapState.CREATED ||
1372
- this._state === SpvFromBTCSwapState.SIGNED) {
1373
- if (this.getQuoteExpiry() < Date.now()) {
1374
- this._state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
1375
- if (save)
1376
- await this._saveAndEmit();
1377
- return true;
1378
- }
1379
- }
1380
- if (this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && !this.posted) {
1381
- if (this.expiry < Date.now()) {
1382
- this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1383
- if (save)
1384
- await this._saveAndEmit();
1385
- return true;
1386
- }
1387
- }
1388
- if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120000) {
1389
- if (this._state === SpvFromBTCSwapState.POSTED ||
1390
- this._state === SpvFromBTCSwapState.BROADCASTED) {
1391
- try {
1392
- //Check if bitcoin payment was confirmed
1393
- return await this._syncStateFromBitcoin(save);
1394
- }
1395
- catch (e) {
1396
- this.logger.error("tickSwap(" + this.getId() + "): ", e);
1397
- }
1398
- }
1399
- }
1400
- return false;
1401
- }
1402
- /**
1403
- * Checks whether an on-chain withdrawal state should be fetched for this specific swap
1404
- *
1405
- * @internal
1406
- */
1407
- async _shouldCheckWithdrawalState(frontingAddress, vaultDataUtxo) {
1408
- if (frontingAddress === undefined)
1409
- frontingAddress = await this._contract.getFronterAddress(this.vaultOwner, this.vaultId, this._data);
1410
- if (vaultDataUtxo === undefined)
1411
- vaultDataUtxo = await this._contract.getVaultLatestUtxo(this.vaultOwner, this.vaultId);
1412
- if (frontingAddress != null)
1413
- return true; //In case the swap is fronted there will for sure be a fronted event
1414
- if (vaultDataUtxo == null)
1415
- return true; //Vault UTXO is null (the vault closed)
1416
- const [txId, _] = vaultDataUtxo.split(":");
1417
- //Don't check both txns if their txId is equal
1418
- if (this._data.btcTx.txid === txId)
1419
- return true;
1420
- const [btcTx, latestVaultTx] = await Promise.all([
1421
- this.wrapper._btcRpc.getTransaction(this._data.btcTx.txid),
1422
- this.wrapper._btcRpc.getTransaction(txId)
1423
- ]);
1424
- if (latestVaultTx == null || latestVaultTx.blockheight == null) {
1425
- //Something must've gone horribly wrong, the latest vault utxo tx of the vault either
1426
- // cannot be found on bitcoin network or is not even confirmed yet
1427
- this.logger.debug(`_shouldCheckWithdrawalState(): Latest vault utxo not found or not confirmed on bitcoin ${txId}`);
1428
- return false;
1429
- }
1430
- if (btcTx != null) {
1431
- const btcTxHeight = btcTx.blockheight;
1432
- const latestVaultTxHeight = latestVaultTx.blockheight;
1433
- //We also need to cover the case where bitcoin tx isn't confirmed yet (hence btxTxHeight==null)
1434
- if (btcTxHeight == null || latestVaultTxHeight < btcTxHeight) {
1435
- //Definitely not claimed!
1436
- this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, latestVaultTxHeight: ${latestVaultTx.blockheight}, btcTxHeight: ${btcTxHeight} and not fronted!`);
1437
- return false;
1438
- }
1439
- }
1440
- else {
1441
- //Definitely not claimed because the transaction was probably double-spent (or evicted from mempool)
1442
- this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, btc tx probably replaced or evicted: ${this._data.btcTx.txid} and not fronted`);
1443
- return false;
1444
- }
1445
- return true;
1446
- }
1447
- }
1448
- exports.SpvFromBTCSwap = SpvFromBTCSwap;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpvFromBTCSwap = exports.isSpvFromBTCSwapInit = exports.SpvFromBTCSwapState = void 0;
4
+ const ISwap_1 = require("../ISwap");
5
+ const base_1 = require("@atomiqlabs/base");
6
+ const SwapType_1 = require("../../enums/SwapType");
7
+ const Utils_1 = require("../../utils/Utils");
8
+ const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
9
+ const btc_signer_1 = require("@scure/btc-signer");
10
+ const buffer_1 = require("buffer");
11
+ const IBitcoinWallet_1 = require("../../bitcoin/wallet/IBitcoinWallet");
12
+ const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
13
+ const FeeType_1 = require("../../enums/FeeType");
14
+ const PercentagePPM_1 = require("../../types/fees/PercentagePPM");
15
+ const TokenAmount_1 = require("../../types/TokenAmount");
16
+ const Token_1 = require("../../types/Token");
17
+ const Logger_1 = require("../../utils/Logger");
18
+ const TimeoutUtils_1 = require("../../utils/TimeoutUtils");
19
+ const PriceInfoType_1 = require("../../types/PriceInfoType");
20
+ const BitcoinWalletUtils_1 = require("../../utils/BitcoinWalletUtils");
21
+ /**
22
+ * State enum for SPV vault (UTXO-controlled vault) based swaps
23
+ * @category Swaps/Bitcoin → Smart chain
24
+ */
25
+ var SpvFromBTCSwapState;
26
+ (function (SpvFromBTCSwapState) {
27
+ /**
28
+ * Catastrophic failure has occurred when processing the swap on the smart chain side,
29
+ * this implies a bug in the smart contract code or the user and intermediary deliberately
30
+ * creating a bitcoin transaction with invalid format unparsable by the smart contract.
31
+ */
32
+ SpvFromBTCSwapState[SpvFromBTCSwapState["CLOSED"] = -5] = "CLOSED";
33
+ /**
34
+ * Some of the bitcoin swap transaction inputs were double-spent, this means the swap
35
+ * has failed and no BTC was sent
36
+ */
37
+ SpvFromBTCSwapState[SpvFromBTCSwapState["FAILED"] = -4] = "FAILED";
38
+ /**
39
+ * The intermediary (LP) declined to co-sign the submitted PSBT, hence the swap failed
40
+ */
41
+ SpvFromBTCSwapState[SpvFromBTCSwapState["DECLINED"] = -3] = "DECLINED";
42
+ /**
43
+ * Swap has expired for good and there is no way how it can be executed anymore
44
+ */
45
+ SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
46
+ /**
47
+ * A swap is almost expired, and it should be presented to the user as expired, though
48
+ * there is still a chance that it will be processed
49
+ */
50
+ SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
51
+ /**
52
+ * Swap was created, use the {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.getPsbt} functions
53
+ * to get the bitcoin swap PSBT that should be signed by the user's wallet and then submitted via the
54
+ * {@link SpvFromBTCSwap.submitPsbt} function.
55
+ */
56
+ SpvFromBTCSwapState[SpvFromBTCSwapState["CREATED"] = 0] = "CREATED";
57
+ /**
58
+ * Swap bitcoin PSBT was submitted by the client to the SDK
59
+ */
60
+ SpvFromBTCSwapState[SpvFromBTCSwapState["SIGNED"] = 1] = "SIGNED";
61
+ /**
62
+ * Swap bitcoin PSBT sent to the intermediary (LP), waiting for the intermediary co-sign
63
+ * it and broadcast. You can use the {@link SpvFromBTCSwap.waitTillClaimedOrFronted}
64
+ * function to wait till the intermediary broadcasts the transaction and the transaction
65
+ * confirms.
66
+ */
67
+ SpvFromBTCSwapState[SpvFromBTCSwapState["POSTED"] = 2] = "POSTED";
68
+ /**
69
+ * Intermediary (LP) has co-signed and broadcasted the bitcoin transaction. You can use the
70
+ * {@link SpvFromBTCSwap.waitTillClaimedOrFronted} function to wait till the transaction
71
+ * confirms.
72
+ */
73
+ SpvFromBTCSwapState[SpvFromBTCSwapState["BROADCASTED"] = 3] = "BROADCASTED";
74
+ /**
75
+ * Settlement on the destination smart chain was fronted and funds were already received
76
+ * by the user, even before the final settlement.
77
+ */
78
+ SpvFromBTCSwapState[SpvFromBTCSwapState["FRONTED"] = 4] = "FRONTED";
79
+ /**
80
+ * Bitcoin transaction confirmed with necessary amount of confirmations, wait for automatic
81
+ * settlement by the watchtower with the {@link waitTillClaimedOrFronted} function, or settle manually
82
+ * using the {@link FromBTCSwap.claim} or {@link FromBTCSwap.txsClaim} function.
83
+ */
84
+ SpvFromBTCSwapState[SpvFromBTCSwapState["BTC_TX_CONFIRMED"] = 5] = "BTC_TX_CONFIRMED";
85
+ /**
86
+ * Swap settled on the smart chain and funds received
87
+ */
88
+ SpvFromBTCSwapState[SpvFromBTCSwapState["CLAIMED"] = 6] = "CLAIMED";
89
+ })(SpvFromBTCSwapState = exports.SpvFromBTCSwapState || (exports.SpvFromBTCSwapState = {}));
90
+ const SpvFromBTCSwapStateDescription = {
91
+ [SpvFromBTCSwapState.CLOSED]: "Catastrophic failure has occurred when processing the swap on the smart chain side, this implies a bug in the smart contract code or the user and intermediary deliberately creating a bitcoin transaction with invalid format unparsable by the smart contract.",
92
+ [SpvFromBTCSwapState.FAILED]: "Some of the bitcoin swap transaction inputs were double-spent, this means the swap has failed and no BTC was sent",
93
+ [SpvFromBTCSwapState.DECLINED]: "The intermediary (LP) declined to co-sign the submitted PSBT, hence the swap failed",
94
+ [SpvFromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
95
+ [SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is almost expired, and it should be presented to the user as expired, though there is still a chance that it will be processed",
96
+ [SpvFromBTCSwapState.CREATED]: "Swap was created, get the bitcoin swap PSBT that should be signed by the user's wallet and then submit it back to the SDK.",
97
+ [SpvFromBTCSwapState.SIGNED]: "Swap bitcoin PSBT was submitted by the client to the SDK",
98
+ [SpvFromBTCSwapState.POSTED]: "Swap bitcoin PSBT sent to the intermediary (LP), waiting for the intermediary co-sign it and broadcast.",
99
+ [SpvFromBTCSwapState.BROADCASTED]: "Intermediary (LP) has co-signed and broadcasted the bitcoin transaction.",
100
+ [SpvFromBTCSwapState.FRONTED]: "Settlement on the destination smart chain was fronted and funds were already received by the user, even before the final settlement.",
101
+ [SpvFromBTCSwapState.BTC_TX_CONFIRMED]: "Bitcoin transaction confirmed with necessary amount of confirmations, wait for automatic settlement by the watchtower or settle manually.",
102
+ [SpvFromBTCSwapState.CLAIMED]: "Swap settled on the smart chain and funds received"
103
+ };
104
+ function isSpvFromBTCSwapInit(obj) {
105
+ return typeof obj === "object" &&
106
+ typeof (obj.quoteId) === "string" &&
107
+ typeof (obj.recipient) === "string" &&
108
+ typeof (obj.vaultOwner) === "string" &&
109
+ typeof (obj.vaultId) === "bigint" &&
110
+ typeof (obj.vaultRequiredConfirmations) === "number" &&
111
+ Array.isArray(obj.vaultTokenMultipliers) && obj.vaultTokenMultipliers.reduce((prev, curr) => prev && typeof (curr) === "bigint", true) &&
112
+ typeof (obj.vaultBtcAddress) === "string" &&
113
+ typeof (obj.vaultUtxo) === "string" &&
114
+ typeof (obj.vaultUtxoValue) === "bigint" &&
115
+ typeof (obj.btcDestinationAddress) === "string" &&
116
+ typeof (obj.btcAmount) === "bigint" &&
117
+ typeof (obj.btcAmountSwap) === "bigint" &&
118
+ typeof (obj.btcAmountGas) === "bigint" &&
119
+ typeof (obj.minimumBtcFeeRate) === "number" &&
120
+ typeof (obj.outputTotalSwap) === "bigint" &&
121
+ typeof (obj.outputSwapToken) === "string" &&
122
+ typeof (obj.outputTotalGas) === "bigint" &&
123
+ typeof (obj.outputGasToken) === "string" &&
124
+ typeof (obj.gasSwapFeeBtc) === "bigint" &&
125
+ typeof (obj.gasSwapFee) === "bigint" &&
126
+ typeof (obj.callerFeeShare) === "bigint" &&
127
+ typeof (obj.frontingFeeShare) === "bigint" &&
128
+ typeof (obj.executionFeeShare) === "bigint" &&
129
+ typeof (obj.genesisSmartChainBlockHeight) === "number" &&
130
+ (obj.gasPricingInfo == null || (0, PriceInfoType_1.isPriceInfoType)(obj.gasPricingInfo)) &&
131
+ (0, ISwap_1.isISwapInit)(obj);
132
+ }
133
+ exports.isSpvFromBTCSwapInit = isSpvFromBTCSwapInit;
134
+ /**
135
+ * New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
136
+ * any initiation on the destination chain, and with the added possibility for the user to receive
137
+ * a native token on the destination chain as part of the swap (a "gas drop" feature).
138
+ *
139
+ * @category Swaps/Bitcoin → Smart chain
140
+ */
141
+ class SpvFromBTCSwap extends ISwap_1.ISwap {
142
+ constructor(wrapper, initOrObject) {
143
+ if (isSpvFromBTCSwapInit(initOrObject) && initOrObject.url != null)
144
+ initOrObject.url += "/frombtc_spv";
145
+ super(wrapper, initOrObject);
146
+ this.currentVersion = 2;
147
+ this.TYPE = SwapType_1.SwapType.SPV_VAULT_FROM_BTC;
148
+ /**
149
+ * @internal
150
+ */
151
+ this.swapStateDescription = SpvFromBTCSwapStateDescription;
152
+ /**
153
+ * @internal
154
+ */
155
+ this.swapStateName = (state) => SpvFromBTCSwapState[state];
156
+ if (isSpvFromBTCSwapInit(initOrObject)) {
157
+ this._state = SpvFromBTCSwapState.CREATED;
158
+ this.quoteId = initOrObject.quoteId;
159
+ this.recipient = initOrObject.recipient;
160
+ this.vaultOwner = initOrObject.vaultOwner;
161
+ this.vaultId = initOrObject.vaultId;
162
+ this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
163
+ this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers;
164
+ this.vaultBtcAddress = initOrObject.vaultBtcAddress;
165
+ this.vaultUtxo = initOrObject.vaultUtxo;
166
+ this.vaultUtxoValue = initOrObject.vaultUtxoValue;
167
+ this.btcDestinationAddress = initOrObject.btcDestinationAddress;
168
+ this.btcAmount = initOrObject.btcAmount;
169
+ this.btcAmountSwap = initOrObject.btcAmountSwap;
170
+ this.btcAmountGas = initOrObject.btcAmountGas;
171
+ this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
172
+ this.outputTotalSwap = initOrObject.outputTotalSwap;
173
+ this.outputSwapToken = initOrObject.outputSwapToken;
174
+ this.outputTotalGas = initOrObject.outputTotalGas;
175
+ this.outputGasToken = initOrObject.outputGasToken;
176
+ this.gasSwapFeeBtc = initOrObject.gasSwapFeeBtc;
177
+ this.gasSwapFee = initOrObject.gasSwapFee;
178
+ this.callerFeeShare = initOrObject.callerFeeShare;
179
+ this.frontingFeeShare = initOrObject.frontingFeeShare;
180
+ this.executionFeeShare = initOrObject.executionFeeShare;
181
+ this._genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
182
+ this.gasPricingInfo = initOrObject.gasPricingInfo;
183
+ const vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)((0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress));
184
+ if (vaultAddressType !== "p2tr" && vaultAddressType !== "p2wpkh" && vaultAddressType !== "p2wsh")
185
+ throw new Error("Vault address type must be of witness type: p2tr, p2wpkh, p2wsh");
186
+ }
187
+ else {
188
+ this.quoteId = initOrObject.quoteId;
189
+ this.recipient = initOrObject.recipient;
190
+ this.vaultOwner = initOrObject.vaultOwner;
191
+ this.vaultId = BigInt(initOrObject.vaultId);
192
+ this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
193
+ this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers.map((val) => BigInt(val));
194
+ this.vaultBtcAddress = initOrObject.vaultBtcAddress;
195
+ this.vaultUtxo = initOrObject.vaultUtxo;
196
+ this.vaultUtxoValue = BigInt(initOrObject.vaultUtxoValue);
197
+ this.btcDestinationAddress = initOrObject.btcDestinationAddress;
198
+ this.btcAmount = BigInt(initOrObject.btcAmount);
199
+ this.btcAmountSwap = BigInt(initOrObject.btcAmountSwap);
200
+ this.btcAmountGas = BigInt(initOrObject.btcAmountGas);
201
+ this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
202
+ this.outputTotalSwap = BigInt(initOrObject.outputTotalSwap);
203
+ this.outputSwapToken = initOrObject.outputSwapToken;
204
+ this.outputTotalGas = BigInt(initOrObject.outputTotalGas);
205
+ this.outputGasToken = initOrObject.outputGasToken;
206
+ this.gasSwapFeeBtc = BigInt(initOrObject.gasSwapFeeBtc);
207
+ this.gasSwapFee = BigInt(initOrObject.gasSwapFee);
208
+ this.callerFeeShare = BigInt(initOrObject.callerFeeShare);
209
+ this.frontingFeeShare = BigInt(initOrObject.frontingFeeShare);
210
+ this.executionFeeShare = BigInt(initOrObject.executionFeeShare);
211
+ this._genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
212
+ this._senderAddress = initOrObject.senderAddress;
213
+ this._claimTxId = initOrObject.claimTxId;
214
+ this._frontTxId = initOrObject.frontTxId;
215
+ this.gasPricingInfo = (0, PriceInfoType_1.deserializePriceInfoType)(initOrObject.gasPricingInfo);
216
+ this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
217
+ this.posted = initOrObject.posted;
218
+ if (initOrObject.data != null)
219
+ this._data = new (this.wrapper._spvWithdrawalDataDeserializer(this._contractVersion))(initOrObject.data);
220
+ }
221
+ this.tryCalculateSwapFee();
222
+ this.logger = (0, Logger_1.getLogger)("SPVFromBTC(" + this.getId() + "): ");
223
+ this._contract = wrapper._contract(this._contractVersion);
224
+ }
225
+ /**
226
+ * @inheritDoc
227
+ * @internal
228
+ */
229
+ upgradeVersion() {
230
+ if (this.version === 1) {
231
+ this.posted = this.initiated && this._data != null;
232
+ this.version = 2;
233
+ }
234
+ }
235
+ /**
236
+ * @inheritDoc
237
+ * @internal
238
+ */
239
+ tryCalculateSwapFee() {
240
+ if (this.swapFeeBtc == null && this.swapFee != null) {
241
+ this.swapFeeBtc = this.swapFee * this.btcAmountSwap / this.getOutputWithoutFee().rawAmount;
242
+ }
243
+ if (this.pricingInfo != null && this.pricingInfo.swapPriceUSatPerToken == null) {
244
+ const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
245
+ this.pricingInfo = this.wrapper._prices.recomputePriceInfoReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken);
246
+ this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
247
+ }
248
+ }
249
+ //////////////////////////////
250
+ //// Pricing
251
+ /**
252
+ * @inheritDoc
253
+ */
254
+ async refreshPriceData() {
255
+ if (this.pricingInfo == null)
256
+ return;
257
+ const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
258
+ this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken, undefined, undefined, this.swapFeeBtc);
259
+ this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
260
+ }
261
+ //////////////////////////////
262
+ //// Getters & utils
263
+ /**
264
+ * @inheritDoc
265
+ * @internal
266
+ */
267
+ _getInitiator() {
268
+ return this.recipient;
269
+ }
270
+ /**
271
+ * @inheritDoc
272
+ * @internal
273
+ */
274
+ _getEscrowHash() {
275
+ return this._data?.btcTx?.txid ?? null;
276
+ }
277
+ /**
278
+ * @inheritDoc
279
+ */
280
+ getId() {
281
+ return this.quoteId + this._randomNonce;
282
+ }
283
+ /**
284
+ * @inheritDoc
285
+ */
286
+ getQuoteExpiry() {
287
+ return this.expiry - 20 * 1000;
288
+ }
289
+ /**
290
+ * @inheritDoc
291
+ * @internal
292
+ */
293
+ _verifyQuoteDefinitelyExpired() {
294
+ return Promise.resolve(this.expiry < Date.now());
295
+ }
296
+ /**
297
+ * @inheritDoc
298
+ * @internal
299
+ */
300
+ _verifyQuoteValid() {
301
+ return Promise.resolve(this.expiry > Date.now() && (this._state === SpvFromBTCSwapState.CREATED || this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED));
302
+ }
303
+ /**
304
+ * @inheritDoc
305
+ */
306
+ getOutputAddress() {
307
+ return this.recipient;
308
+ }
309
+ /**
310
+ * @inheritDoc
311
+ */
312
+ getOutputTxId() {
313
+ return this._frontTxId ?? this._claimTxId ?? null;
314
+ }
315
+ /**
316
+ * @inheritDoc
317
+ */
318
+ getInputAddress() {
319
+ return this._senderAddress ?? null;
320
+ }
321
+ /**
322
+ * @inheritDoc
323
+ */
324
+ getInputTxId() {
325
+ return this._data?.btcTx?.txid ?? null;
326
+ }
327
+ /**
328
+ * @inheritDoc
329
+ */
330
+ requiresAction() {
331
+ return this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
332
+ }
333
+ /**
334
+ * @inheritDoc
335
+ */
336
+ isFinished() {
337
+ return this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.QUOTE_EXPIRED || this._state === SpvFromBTCSwapState.FAILED;
338
+ }
339
+ /**
340
+ * @inheritDoc
341
+ */
342
+ isClaimable() {
343
+ return this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
344
+ }
345
+ /**
346
+ * @inheritDoc
347
+ */
348
+ isSuccessful() {
349
+ return this._state === SpvFromBTCSwapState.FRONTED || this._state === SpvFromBTCSwapState.CLAIMED;
350
+ }
351
+ /**
352
+ * @inheritDoc
353
+ */
354
+ isFailed() {
355
+ return this._state === SpvFromBTCSwapState.FAILED || this._state === SpvFromBTCSwapState.DECLINED || this._state === SpvFromBTCSwapState.CLOSED;
356
+ }
357
+ /**
358
+ * @inheritDoc
359
+ */
360
+ isInProgress() {
361
+ return this._state === SpvFromBTCSwapState.POSTED ||
362
+ this._state === SpvFromBTCSwapState.BROADCASTED ||
363
+ this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
364
+ }
365
+ /**
366
+ * @inheritDoc
367
+ */
368
+ isQuoteExpired() {
369
+ return this._state === SpvFromBTCSwapState.QUOTE_EXPIRED;
370
+ }
371
+ /**
372
+ * @inheritDoc
373
+ */
374
+ isQuoteSoftExpired() {
375
+ return this._state === SpvFromBTCSwapState.QUOTE_EXPIRED || this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
376
+ }
377
+ /**
378
+ * Returns the data about used spv vault (UTXO-controlled vault) to perform the swap
379
+ */
380
+ getSpvVaultData() {
381
+ return {
382
+ owner: this.vaultOwner,
383
+ vaultId: this.vaultId,
384
+ utxo: this.vaultUtxo
385
+ };
386
+ }
387
+ //////////////////////////////
388
+ //// Amounts & fees
389
+ /**
390
+ * Returns the input BTC amount in sats without any fees
391
+ *
392
+ * @internal
393
+ */
394
+ getInputSwapAmountWithoutFee() {
395
+ return (this.btcAmountSwap - this.swapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare);
396
+ }
397
+ /**
398
+ * Returns the input gas BTC amount in sats without any fees
399
+ *
400
+ * @internal
401
+ */
402
+ getInputGasAmountWithoutFee() {
403
+ return (this.btcAmountGas - this.gasSwapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare);
404
+ }
405
+ /**
406
+ * Returns to total input BTC amount in sats without any fees (this is BTC amount for the swap + BTC amount
407
+ * for the gas drop).
408
+ *
409
+ * @internal
410
+ */
411
+ getInputAmountWithoutFee() {
412
+ return this.getInputSwapAmountWithoutFee() + this.getInputGasAmountWithoutFee();
413
+ }
414
+ /**
415
+ * Returns the swap output amount without any fees, this value is therefore always higher than
416
+ * the actual received output.
417
+ *
418
+ * @internal
419
+ */
420
+ getOutputWithoutFee() {
421
+ return (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare) / 100000n) + (this.swapFee ?? 0n), this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo);
422
+ }
423
+ /**
424
+ * Returns the swap fee charged by the intermediary (LP) on this swap
425
+ *
426
+ * @internal
427
+ */
428
+ getSwapFee() {
429
+ if (this.pricingInfo == null)
430
+ throw new Error("No pricing info known, cannot estimate fee!");
431
+ const outputToken = this.wrapper._tokens[this.outputSwapToken];
432
+ const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
433
+ * (10n ** BigInt(outputToken.decimals))
434
+ * 1000000n
435
+ / this.pricingInfo.swapPriceUSatPerToken;
436
+ const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
437
+ const swapFeePPM = feeWithoutBaseFee * 1000000n / (this.btcAmount - this.swapFeeBtc - this.gasSwapFeeBtc);
438
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(this.swapFeeBtc + this.gasSwapFeeBtc, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
439
+ return {
440
+ amountInSrcToken,
441
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
442
+ currentUsdValue: amountInSrcToken.currentUsdValue,
443
+ usdValue: amountInSrcToken.usdValue,
444
+ pastUsdValue: amountInSrcToken.pastUsdValue,
445
+ composition: {
446
+ base: (0, TokenAmount_1.toTokenAmount)(this.pricingInfo.satsBaseFee, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo),
447
+ percentage: (0, PercentagePPM_1.ppmToPercentage)(swapFeePPM)
448
+ }
449
+ };
450
+ }
451
+ /**
452
+ * Returns the fee to be paid to watchtowers on the destination chain to automatically
453
+ * process and settle this swap without requiring any user interaction
454
+ *
455
+ * @internal
456
+ */
457
+ getWatchtowerFee() {
458
+ if (this.pricingInfo == null)
459
+ throw new Error("No pricing info known, cannot estimate fee!");
460
+ const totalFeeShare = this.callerFeeShare + this.frontingFeeShare;
461
+ const outputToken = this.wrapper._tokens[this.outputSwapToken];
462
+ const watchtowerFeeInOutputToken = this.getInputGasAmountWithoutFee() * totalFeeShare
463
+ * (10n ** BigInt(outputToken.decimals))
464
+ * 1000000n
465
+ / this.pricingInfo.swapPriceUSatPerToken
466
+ / 100000n;
467
+ const feeBtc = this.getInputAmountWithoutFee() * (totalFeeShare + this.executionFeeShare) / 100000n;
468
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(feeBtc, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
469
+ return {
470
+ amountInSrcToken,
471
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (totalFeeShare + this.executionFeeShare) / 100000n) + watchtowerFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
472
+ currentUsdValue: amountInSrcToken.currentUsdValue,
473
+ usdValue: amountInSrcToken.usdValue,
474
+ pastUsdValue: amountInSrcToken.pastUsdValue
475
+ };
476
+ }
477
+ /**
478
+ * @inheritDoc
479
+ */
480
+ getFee() {
481
+ const swapFee = this.getSwapFee();
482
+ const watchtowerFee = this.getWatchtowerFee();
483
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
484
+ return {
485
+ amountInSrcToken,
486
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)(swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount, this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo),
487
+ currentUsdValue: amountInSrcToken.currentUsdValue,
488
+ usdValue: amountInSrcToken.usdValue,
489
+ pastUsdValue: amountInSrcToken.pastUsdValue
490
+ };
491
+ }
492
+ /**
493
+ * @inheritDoc
494
+ */
495
+ getFeeBreakdown() {
496
+ return [
497
+ {
498
+ type: FeeType_1.FeeType.SWAP,
499
+ fee: this.getSwapFee()
500
+ },
501
+ {
502
+ type: FeeType_1.FeeType.NETWORK_OUTPUT,
503
+ fee: this.getWatchtowerFee()
504
+ }
505
+ ];
506
+ }
507
+ /**
508
+ * @inheritDoc
509
+ */
510
+ getOutputToken() {
511
+ return this.wrapper._tokens[this.outputSwapToken];
512
+ }
513
+ /**
514
+ * @inheritDoc
515
+ */
516
+ getOutput() {
517
+ return (0, TokenAmount_1.toTokenAmount)(this.outputTotalSwap, this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo);
518
+ }
519
+ /**
520
+ * @inheritDoc
521
+ */
522
+ getGasDropOutput() {
523
+ return (0, TokenAmount_1.toTokenAmount)(this.outputTotalGas, this.wrapper._tokens[this.outputGasToken], this.wrapper._prices, this.gasPricingInfo);
524
+ }
525
+ /**
526
+ * @inheritDoc
527
+ */
528
+ getInputWithoutFee() {
529
+ return (0, TokenAmount_1.toTokenAmount)(this.getInputAmountWithoutFee(), Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
530
+ }
531
+ /**
532
+ * @inheritDoc
533
+ */
534
+ getInputToken() {
535
+ return Token_1.BitcoinTokens.BTC;
536
+ }
537
+ /**
538
+ * @inheritDoc
539
+ */
540
+ getInput() {
541
+ return (0, TokenAmount_1.toTokenAmount)(this.btcAmount, Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
542
+ }
543
+ //////////////////////////////
544
+ //// Bitcoin tx
545
+ /**
546
+ * @inheritDoc
547
+ */
548
+ getRequiredConfirmationsCount() {
549
+ return this.vaultRequiredConfirmations;
550
+ }
551
+ /**
552
+ * Returns raw transaction details that can be used to manually create a swap PSBT. It is better to use
553
+ * the {@link getPsbt} or {@link getFundedPsbt} function retrieve an already prepared PSBT.
554
+ */
555
+ async getTransactionDetails() {
556
+ const [txId, voutStr] = this.vaultUtxo.split(":");
557
+ const vaultScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress);
558
+ const out2script = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.btcDestinationAddress);
559
+ const opReturnData = this._contract.toOpReturnData(this.recipient, [
560
+ this.outputTotalSwap / this.vaultTokenMultipliers[0],
561
+ this.outputTotalGas / this.vaultTokenMultipliers[1]
562
+ ]);
563
+ const out1script = buffer_1.Buffer.concat([
564
+ opReturnData.length > 75 ? buffer_1.Buffer.from([0x6a, 0x4c, opReturnData.length]) : buffer_1.Buffer.from([0x6a, opReturnData.length]),
565
+ opReturnData
566
+ ]);
567
+ if (this.callerFeeShare < 0n || this.callerFeeShare > 0xfffffn)
568
+ throw new Error("Caller fee out of bounds!");
569
+ if (this.frontingFeeShare < 0n || this.frontingFeeShare > 0xfffffn)
570
+ throw new Error("Fronting fee out of bounds!");
571
+ if (this.executionFeeShare < 0n || this.executionFeeShare > 0xfffffn)
572
+ throw new Error("Execution fee out of bounds!");
573
+ const nSequence0 = 0x80000000n | (this.callerFeeShare & 0xfffffn) | (this.frontingFeeShare & 1047552n) << 10n;
574
+ const nSequence1 = 0x80000000n | (this.executionFeeShare & 0xfffffn) | (this.frontingFeeShare & 1023n) << 20n;
575
+ return {
576
+ in0txid: txId,
577
+ in0vout: parseInt(voutStr),
578
+ in0sequence: Number(nSequence0),
579
+ vaultAmount: this.vaultUtxoValue,
580
+ vaultScript,
581
+ in1sequence: Number(nSequence1),
582
+ out1script,
583
+ out2amount: this.btcAmount,
584
+ out2script,
585
+ locktime: 500000000 + Math.floor(Math.random() * 1000000000) //Use this as a random salt to make the btc txId unique!
586
+ };
587
+ }
588
+ /**
589
+ * Returns the raw PSBT (not funded), the wallet should fund the PSBT (add its inputs) and importantly **set the nSequence field of the
590
+ * 2nd input** (input 1 - indexing from 0) to the value returned in `in1sequence`, sign the PSBT and then pass
591
+ * it back to the swap with {@link submitPsbt} function.
592
+ */
593
+ async getPsbt() {
594
+ const res = await this.getTransactionDetails();
595
+ const psbt = new btc_signer_1.Transaction({
596
+ allowUnknownOutputs: true,
597
+ allowLegacyWitnessUtxo: true,
598
+ lockTime: res.locktime
599
+ });
600
+ psbt.addInput({
601
+ txid: res.in0txid,
602
+ index: res.in0vout,
603
+ witnessUtxo: {
604
+ amount: res.vaultAmount,
605
+ script: res.vaultScript
606
+ },
607
+ sequence: res.in0sequence
608
+ });
609
+ psbt.addOutput({
610
+ amount: res.vaultAmount,
611
+ script: res.vaultScript
612
+ });
613
+ psbt.addOutput({
614
+ amount: 0n,
615
+ script: res.out1script
616
+ });
617
+ psbt.addOutput({
618
+ amount: res.out2amount,
619
+ script: res.out2script
620
+ });
621
+ const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
622
+ return {
623
+ psbt,
624
+ psbtHex: serializedPsbt.toString("hex"),
625
+ psbtBase64: serializedPsbt.toString("base64"),
626
+ in1sequence: res.in1sequence
627
+ };
628
+ }
629
+ /**
630
+ * Returns the PSBT that is already funded with wallet's UTXOs (runs a coin-selection algorithm to choose UTXOs to use),
631
+ * also returns inputs indices that need to be signed by the wallet before submitting the PSBT back to the SDK with
632
+ * {@link submitPsbt}
633
+ *
634
+ * @remarks
635
+ * Note that when passing the `feeRate` argument, the fee must be at least {@link minimumBtcFeeRate} sats/vB.
636
+ *
637
+ * @param _bitcoinWallet Sender's bitcoin wallet
638
+ * @param feeRate Optional fee rate in sats/vB for the transaction
639
+ * @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
640
+ * @param utxos Pre-fetched list of UTXOs to spend from
641
+ * @param spendFully Instructs the wallet to spend all the passed UTXOs in the transaction without creating any
642
+ * change output, if the `feeRate` is passed, it will also enforce that the feeRate in sats/vB for the resulting
643
+ * transaction is not more than 50% and 10 sats/vB larger (considering also the CPFP adjustments)
644
+ */
645
+ async getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs, utxos, spendFully) {
646
+ const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
647
+ if (feeRate != null) {
648
+ if (feeRate < this.minimumBtcFeeRate)
649
+ throw new Error("Bitcoin tx fee needs to be at least " + this.minimumBtcFeeRate + " sats/vB");
650
+ }
651
+ else {
652
+ feeRate = Math.max(this.minimumBtcFeeRate, await bitcoinWallet.getFeeRate());
653
+ }
654
+ let { psbt, in1sequence } = await this.getPsbt();
655
+ if (additionalOutputs != null)
656
+ additionalOutputs.forEach(output => {
657
+ psbt.addOutput({
658
+ amount: output.amount,
659
+ script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
660
+ });
661
+ });
662
+ psbt = await bitcoinWallet.fundPsbt(psbt, feeRate, utxos, spendFully);
663
+ psbt.updateInput(1, { sequence: in1sequence });
664
+ //Sign every input except the first one
665
+ const signInputs = [];
666
+ for (let i = 1; i < psbt.inputsLength; i++) {
667
+ signInputs.push(i);
668
+ }
669
+ const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
670
+ return {
671
+ psbt,
672
+ psbtHex: serializedPsbt.toString("hex"),
673
+ psbtBase64: serializedPsbt.toString("base64"),
674
+ signInputs
675
+ };
676
+ }
677
+ /**
678
+ * @inheritDoc
679
+ */
680
+ async submitPsbt(_psbt) {
681
+ const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
682
+ //Ensure not expired
683
+ if (this.expiry < Date.now()) {
684
+ throw new Error("Quote expired!");
685
+ }
686
+ //Ensure valid state
687
+ if (this._state !== SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this._state !== SpvFromBTCSwapState.CREATED) {
688
+ throw new Error("Invalid swap state!");
689
+ }
690
+ if (this.url == null)
691
+ throw new Error("LP URL not known, cannot submit PSBT!");
692
+ //Ensure all inputs except the 1st are finalized
693
+ for (let i = 1; i < psbt.inputsLength; i++) {
694
+ if ((0, btc_signer_1.getInputType)(psbt.getInput(i)).txType === "legacy")
695
+ throw new Error("Legacy (non-segwit) inputs are not allowed in the transaction!");
696
+ psbt.finalizeIdx(i);
697
+ }
698
+ const btcTx = await this.wrapper._btcRpc.parseTransaction(buffer_1.Buffer.from(psbt.toBytes(true)).toString("hex"));
699
+ const data = await this._contract.getWithdrawalData(btcTx);
700
+ this.logger.debug("submitPsbt(): parsed withdrawal data: ", data);
701
+ //Verify correct withdrawal data
702
+ if (!data.isRecipient(this.recipient) ||
703
+ data.rawAmounts[0] * this.vaultTokenMultipliers[0] !== this.outputTotalSwap ||
704
+ (data.rawAmounts[1] ?? 0n) * this.vaultTokenMultipliers[1] !== this.outputTotalGas ||
705
+ data.callerFeeRate !== this.callerFeeShare ||
706
+ data.frontingFeeRate !== this.frontingFeeShare ||
707
+ data.executionFeeRate !== this.executionFeeShare ||
708
+ data.getSpentVaultUtxo() !== this.vaultUtxo ||
709
+ BigInt(data.getNewVaultBtcAmount()) !== this.vaultUtxoValue ||
710
+ !data.getNewVaultScript().equals((0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.vaultBtcAddress)) ||
711
+ data.getExecutionData() != null) {
712
+ throw new Error("Invalid withdrawal tx data submitted!");
713
+ }
714
+ //Verify correct LP output
715
+ const lpOutput = psbt.getOutput(2);
716
+ if (lpOutput.script == null ||
717
+ lpOutput.amount !== this.btcAmount ||
718
+ !(0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.btcDestinationAddress).equals(buffer_1.Buffer.from(lpOutput.script))) {
719
+ throw new Error("Invalid LP bitcoin output in transaction!");
720
+ }
721
+ //Verify vault utxo not spent yet
722
+ if (await this.wrapper._btcRpc.isSpent(this.vaultUtxo)) {
723
+ throw new Error("Vault UTXO already spent, please create new swap!");
724
+ }
725
+ //Verify tx is parsable by the contract
726
+ try {
727
+ await this._contract.checkWithdrawalTx(data);
728
+ }
729
+ catch (e) {
730
+ throw new Error("Transaction not parsable by the contract: " + (e.message ?? e.toString()));
731
+ }
732
+ //Ensure still not expired
733
+ if (this.expiry < Date.now()) {
734
+ throw new Error("Quote expired!");
735
+ }
736
+ this._data = data;
737
+ this.initiated = true;
738
+ this.posted = true;
739
+ await this._saveAndEmit(SpvFromBTCSwapState.SIGNED);
740
+ try {
741
+ await IntermediaryAPI_1.IntermediaryAPI.initSpvFromBTC(this.chainIdentifier, this.url, {
742
+ quoteId: this.quoteId,
743
+ psbtHex: buffer_1.Buffer.from(psbt.toPSBT(0)).toString("hex")
744
+ });
745
+ await this._saveAndEmit(SpvFromBTCSwapState.POSTED);
746
+ }
747
+ catch (e) {
748
+ await this._saveAndEmit(SpvFromBTCSwapState.DECLINED);
749
+ throw e;
750
+ }
751
+ return this._data.getTxId();
752
+ }
753
+ /**
754
+ * @inheritDoc
755
+ */
756
+ async estimateBitcoinFee(_bitcoinWallet, feeRate) {
757
+ const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
758
+ const txFee = await bitcoinWallet.getFundedPsbtFee((await this.getPsbt()).psbt, feeRate);
759
+ if (txFee == null)
760
+ return null;
761
+ return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
762
+ }
763
+ /**
764
+ * @inheritDoc
765
+ */
766
+ async sendBitcoinTransaction(wallet, feeRate, utxos, spendFully) {
767
+ const { psbt, psbtBase64, psbtHex, signInputs } = await this.getFundedPsbt(wallet, feeRate, undefined, utxos, spendFully);
768
+ let signedPsbt;
769
+ if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
770
+ signedPsbt = await wallet.signPsbt(psbt, signInputs);
771
+ }
772
+ else {
773
+ signedPsbt = await wallet.signPsbt({
774
+ psbt, psbtHex, psbtBase64
775
+ }, signInputs);
776
+ }
777
+ return await this.submitPsbt(signedPsbt);
778
+ }
779
+ /**
780
+ * Executes the swap with the provided bitcoin wallet
781
+ *
782
+ * @param wallet Bitcoin wallet to use to sign the bitcoin transaction
783
+ * @param callbacks Callbacks to track the progress of the swap
784
+ * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
785
+ *
786
+ * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
787
+ * user, in case `false` is returned the user should call the {@link claim} function to settle the swap on the
788
+ * destination manually
789
+ */
790
+ async execute(wallet, callbacks, options) {
791
+ if (this._state === SpvFromBTCSwapState.CLOSED)
792
+ throw new Error("Swap encountered a catastrophic failure!");
793
+ if (this._state === SpvFromBTCSwapState.FAILED)
794
+ throw new Error("Swap failed!");
795
+ if (this._state === SpvFromBTCSwapState.DECLINED)
796
+ throw new Error("Swap execution already declined by the LP!");
797
+ if (this._state === SpvFromBTCSwapState.QUOTE_EXPIRED)
798
+ throw new Error("Swap quote expired!");
799
+ if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
800
+ throw new Error("Swap already settled or fronted!");
801
+ if (this._state === SpvFromBTCSwapState.CREATED) {
802
+ const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate, options?.utxos, options?.spendFully);
803
+ if (callbacks?.onSourceTransactionSent != null)
804
+ callbacks.onSourceTransactionSent(txId);
805
+ }
806
+ if (this._state === SpvFromBTCSwapState.POSTED || this._state === SpvFromBTCSwapState.BROADCASTED) {
807
+ const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
808
+ if (callbacks?.onSourceTransactionConfirmed != null)
809
+ callbacks.onSourceTransactionConfirmed(txId);
810
+ }
811
+ // @ts-ignore
812
+ if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
813
+ return true;
814
+ if (this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
815
+ const success = await this.waitTillClaimedOrFronted(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
816
+ if (success && callbacks?.onSwapSettled != null)
817
+ callbacks.onSwapSettled(this.getOutputTxId());
818
+ return success;
819
+ }
820
+ throw new Error("Unexpected state reached!");
821
+ }
822
+ /**
823
+ * @inheritDoc
824
+ *
825
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
826
+ * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
827
+ * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
828
+ * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
829
+ * indexing from 0) to the value returned in `in1sequence`
830
+ */
831
+ async txsExecute(options) {
832
+ if (this._state === SpvFromBTCSwapState.CREATED) {
833
+ if (!await this._verifyQuoteValid())
834
+ throw new Error("Quote already expired or close to expiry!");
835
+ return [
836
+ {
837
+ name: "Payment",
838
+ description: "Send funds to the bitcoin swap address",
839
+ chain: "BITCOIN",
840
+ txs: [
841
+ options?.bitcoinWallet == null
842
+ ? { ...await this.getPsbt(), type: "RAW_PSBT" }
843
+ : { ...await this.getFundedPsbt(options.bitcoinWallet, options?.bitcoinFeeRate), type: "FUNDED_PSBT" }
844
+ ]
845
+ }
846
+ ];
847
+ }
848
+ throw new Error("Invalid swap state to obtain execution txns, required CREATED");
849
+ }
850
+ /**
851
+ * @inheritDoc
852
+ *
853
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
854
+ * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
855
+ * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
856
+ * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
857
+ * indexing from 0) to the value returned in `in1sequence`
858
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
859
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
860
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
861
+ */
862
+ async getCurrentActions(options) {
863
+ if (this._state === SpvFromBTCSwapState.CREATED) {
864
+ try {
865
+ return await this.txsExecute(options);
866
+ }
867
+ catch (e) { }
868
+ }
869
+ if (this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
870
+ if (this.btcTxConfirmedAt == null ||
871
+ options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
872
+ (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
873
+ return [{
874
+ name: "Claim",
875
+ description: "Manually settle (claim) the swap on the destination smart chain",
876
+ chain: this.chainIdentifier,
877
+ txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
878
+ }];
879
+ }
880
+ }
881
+ return [];
882
+ }
883
+ //////////////////////////////
884
+ //// Bitcoin tx listener
885
+ /**
886
+ * Checks whether a bitcoin payment was already made, returns the payment or null when no payment has been made.
887
+ * @internal
888
+ */
889
+ async getBitcoinPayment() {
890
+ if (this._data?.btcTx?.txid == null)
891
+ return null;
892
+ const result = await this.wrapper._btcRpc.getTransaction(this._data?.btcTx?.txid);
893
+ if (result == null)
894
+ return null;
895
+ return {
896
+ txId: result.txid,
897
+ confirmations: result.confirmations ?? 0,
898
+ targetConfirmations: this.vaultRequiredConfirmations,
899
+ inputAddresses: result.inputAddresses
900
+ };
901
+ }
902
+ /**
903
+ * @inheritDoc
904
+ *
905
+ * @throws {Error} if in invalid state (must be {@link SpvFromBTCSwapState.POSTED} or
906
+ * {@link SpvFromBTCSwapState.BROADCASTED} states)
907
+ */
908
+ async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
909
+ if (this._state !== SpvFromBTCSwapState.POSTED &&
910
+ this._state !== SpvFromBTCSwapState.BROADCASTED &&
911
+ !(this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this.posted))
912
+ throw new Error("Must be in POSTED or BROADCASTED state!");
913
+ if (this._data == null)
914
+ throw new Error("Expected swap to have withdrawal data filled!");
915
+ const result = await this.wrapper._btcRpc.waitForTransaction(this._data.btcTx.txid, this.vaultRequiredConfirmations, (btcTx, txEtaMs) => {
916
+ if (updateCallback != null)
917
+ updateCallback(btcTx?.txid, btcTx?.confirmations, this.vaultRequiredConfirmations, txEtaMs);
918
+ if (btcTx == null)
919
+ return;
920
+ let save = false;
921
+ if (btcTx.inputAddresses != null && this._senderAddress == null) {
922
+ this._senderAddress = btcTx.inputAddresses[1];
923
+ save = true;
924
+ }
925
+ if (this._state === SpvFromBTCSwapState.POSTED || this._state == SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED) {
926
+ this._state = SpvFromBTCSwapState.BROADCASTED;
927
+ save = true;
928
+ }
929
+ if (save)
930
+ this._saveAndEmit();
931
+ }, abortSignal, checkIntervalSeconds);
932
+ if (abortSignal != null)
933
+ abortSignal.throwIfAborted();
934
+ let save = false;
935
+ if (result.inputAddresses != null && this._senderAddress == null) {
936
+ this._senderAddress = result.inputAddresses[1];
937
+ save = true;
938
+ }
939
+ if (this._state !== SpvFromBTCSwapState.FRONTED &&
940
+ this._state !== SpvFromBTCSwapState.CLAIMED) {
941
+ this.btcTxConfirmedAt ??= Date.now();
942
+ this._state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
943
+ save = true;
944
+ }
945
+ if (save)
946
+ await this._saveAndEmit();
947
+ return result.txid;
948
+ }
949
+ //////////////////////////////
950
+ //// Claim
951
+ /**
952
+ * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
953
+ * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
954
+ * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
955
+ *
956
+ * @remarks
957
+ * Might also return transactions necessary to sync the bitcoin light client.
958
+ *
959
+ * @param _signer Address of the signer to create the claim transactions for, can also be different to the recipient
960
+ *
961
+ * @throws {Error} If the swap is in invalid state (must be {@link SpvFromBTCSwapState.BTC_TX_CONFIRMED})
962
+ */
963
+ async txsClaim(_signer) {
964
+ let address = undefined;
965
+ if (_signer != null) {
966
+ if (typeof (_signer) === "string") {
967
+ address = _signer;
968
+ }
969
+ else if ((0, base_1.isAbstractSigner)(_signer)) {
970
+ address = _signer.getAddress();
971
+ }
972
+ else {
973
+ address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
974
+ }
975
+ }
976
+ if (!this.isClaimable())
977
+ throw new Error("Must be in BTC_TX_CONFIRMED state!");
978
+ if (this._data == null)
979
+ throw new Error("Expected swap to have withdrawal data filled!");
980
+ const vaultData = await this._contract.getVaultData(this.vaultOwner, this.vaultId);
981
+ if (vaultData == null)
982
+ throw new Error(`Vault data for ${this.vaultOwner}:${this.vaultId.toString(10)} not found (already closed???)!`);
983
+ const btcTx = await this.wrapper._btcRpc.getTransaction(this._data.btcTx.txid);
984
+ if (btcTx == null)
985
+ throw new Error(`Bitcoin transaction ${this._data.btcTx.txid} not found!`);
986
+ const txs = [btcTx];
987
+ //Trace back from current tx to the vaultData-specified UTXO
988
+ const vaultUtxo = vaultData.getUtxo();
989
+ while (txs[0].ins[0].txid + ":" + txs[0].ins[0].vout !== vaultUtxo) {
990
+ const btcTx = await this.wrapper._btcRpc.getTransaction(txs[0].ins[0].txid);
991
+ if (btcTx == null)
992
+ throw new Error(`Prior withdrawal bitcoin transaction ${this._data.btcTx.txid} not found!`);
993
+ txs.unshift(btcTx);
994
+ }
995
+ //Parse transactions to withdrawal data
996
+ const withdrawalData = [];
997
+ for (let tx of txs) {
998
+ withdrawalData.push(await this._contract.getWithdrawalData(tx));
999
+ }
1000
+ return await this._contract.txsClaim(address ?? this._getInitiator(), vaultData, withdrawalData.map(tx => { return { tx }; }), this.wrapper._synchronizer(this._contractVersion), true);
1001
+ }
1002
+ /**
1003
+ * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
1004
+ * check so with isClaimable.
1005
+ *
1006
+ * @remarks
1007
+ * Might also sync the bitcoin light client during the process.
1008
+ *
1009
+ * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
1010
+ * @param abortSignal Abort signal
1011
+ * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
1012
+ *
1013
+ * @throws {Error} If the swap is in invalid state (must be {@link SpvFromBTCSwapState.BTC_TX_CONFIRMED})
1014
+ */
1015
+ async claim(_signer, abortSignal, onBeforeTxSent) {
1016
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1017
+ let txIds;
1018
+ try {
1019
+ let txCount = 0;
1020
+ const txs = await this.txsClaim(signer);
1021
+ txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1022
+ txCount++;
1023
+ if (onBeforeTxSent != null && txCount === txs.length)
1024
+ onBeforeTxSent(txId);
1025
+ return Promise.resolve();
1026
+ });
1027
+ }
1028
+ catch (e) {
1029
+ if (this._data == null)
1030
+ throw e;
1031
+ this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
1032
+ if (this._state === SpvFromBTCSwapState.CLAIMED) {
1033
+ this.logger.info("claim(): Transaction state is CLAIMED, swap was successfully claimed by the watchtower");
1034
+ return this._claimTxId;
1035
+ }
1036
+ const withdrawalState = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1037
+ if (withdrawalState != null && withdrawalState.type === base_1.SpvWithdrawalStateType.CLAIMED) {
1038
+ this.logger.info("claim(): Transaction status is CLAIMED, swap was successfully claimed by the watchtower");
1039
+ this._claimTxId = withdrawalState.txId;
1040
+ await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1041
+ return withdrawalState.txId;
1042
+ }
1043
+ throw e;
1044
+ }
1045
+ this._claimTxId = txIds[0];
1046
+ if (this._state === SpvFromBTCSwapState.POSTED || this._state === SpvFromBTCSwapState.BROADCASTED ||
1047
+ this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED || this._state === SpvFromBTCSwapState.FAILED ||
1048
+ this._state === SpvFromBTCSwapState.FRONTED) {
1049
+ await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1050
+ }
1051
+ return txIds[0];
1052
+ }
1053
+ /**
1054
+ * Periodically checks the chain to see whether the swap was finished (claimed or refunded)
1055
+ *
1056
+ * @param abortSignal
1057
+ * @param interval How often to check (in seconds), default to 5s
1058
+ * @internal
1059
+ */
1060
+ async watchdogWaitTillResult(abortSignal, interval = 5) {
1061
+ if (this._data == null)
1062
+ throw new Error("Cannot await the result before the btc transaction is sent!");
1063
+ let status = { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
1064
+ while (status.type === base_1.SpvWithdrawalStateType.NOT_FOUND) {
1065
+ await (0, TimeoutUtils_1.timeoutPromise)(interval * 1000, abortSignal);
1066
+ try {
1067
+ //Be smart about checking withdrawal state
1068
+ if (await this._shouldCheckWithdrawalState()) {
1069
+ status = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight) ?? { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
1070
+ }
1071
+ }
1072
+ catch (e) {
1073
+ this.logger.error("watchdogWaitTillResult(): Error when fetching commit status: ", e);
1074
+ }
1075
+ }
1076
+ if (abortSignal != null)
1077
+ abortSignal.throwIfAborted();
1078
+ return status;
1079
+ }
1080
+ /**
1081
+ * Waits till the swap is successfully settled (claimed), should be called after sending the claim (settlement)
1082
+ * transactions manually to wait till the SDK processes the settlement and updates the swap state accordingly.
1083
+ *
1084
+ * @remarks
1085
+ * This is an alias for the {@link waitTillClaimedOrFronted} function and will also resolve if the swap has
1086
+ * been fronted (not necessarily claimed)
1087
+ *
1088
+ * @param maxWaitTimeSeconds – Maximum time in seconds to wait for the swap to be settled
1089
+ * @param abortSignal – AbortSignal
1090
+ *
1091
+ * @returns Whether the swap was claimed in time or not
1092
+ */
1093
+ waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
1094
+ return this.waitTillClaimedOrFronted(maxWaitTimeSeconds, abortSignal);
1095
+ }
1096
+ /**
1097
+ * Waits till the swap is successfully fronted or settled on the destination chain
1098
+ *
1099
+ * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled (by default
1100
+ * it waits indefinitely)
1101
+ * @param abortSignal Abort signal
1102
+ *
1103
+ * @returns {boolean} whether the swap was claimed or fronted automatically or not, if the swap was not claimed
1104
+ * the user can claim manually through the {@link claim} function
1105
+ */
1106
+ async waitTillClaimedOrFronted(maxWaitTimeSeconds, abortSignal) {
1107
+ if (this._state === SpvFromBTCSwapState.CLAIMED || this._state === SpvFromBTCSwapState.FRONTED)
1108
+ return Promise.resolve(true);
1109
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1110
+ let timedOut = false;
1111
+ if (maxWaitTimeSeconds != null) {
1112
+ const timeout = setTimeout(() => {
1113
+ timedOut = true;
1114
+ abortController.abort();
1115
+ }, maxWaitTimeSeconds * 1000);
1116
+ abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1117
+ }
1118
+ let res;
1119
+ try {
1120
+ res = await Promise.race([
1121
+ this.watchdogWaitTillResult(abortController.signal),
1122
+ this.waitTillState(SpvFromBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 0),
1123
+ this.waitTillState(SpvFromBTCSwapState.FRONTED, "eq", abortController.signal).then(() => 1),
1124
+ this.waitTillState(SpvFromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 2),
1125
+ ]);
1126
+ abortController.abort();
1127
+ }
1128
+ catch (e) {
1129
+ abortController.abort();
1130
+ if (timedOut)
1131
+ return false;
1132
+ throw e;
1133
+ }
1134
+ if (typeof (res) === "number") {
1135
+ if (res === 0) {
1136
+ this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (CLAIMED)");
1137
+ return true;
1138
+ }
1139
+ if (res === 1) {
1140
+ this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FRONTED)");
1141
+ return true;
1142
+ }
1143
+ if (res === 2) {
1144
+ this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FAILED)");
1145
+ throw new Error("Swap failed while waiting for claim or front");
1146
+ }
1147
+ throw new Error("Invalid numeric response, this should never happen!");
1148
+ }
1149
+ this.logger.debug("waitTillClaimedOrFronted(): Resolved from watchdog");
1150
+ if (res.type === base_1.SpvWithdrawalStateType.FRONTED) {
1151
+ if (this._state !== SpvFromBTCSwapState.FRONTED ||
1152
+ this._state !== SpvFromBTCSwapState.CLAIMED) {
1153
+ this._frontTxId = res.txId;
1154
+ await this._saveAndEmit(SpvFromBTCSwapState.FRONTED);
1155
+ }
1156
+ }
1157
+ if (res.type === base_1.SpvWithdrawalStateType.CLAIMED) {
1158
+ if (this._state !== SpvFromBTCSwapState.CLAIMED) {
1159
+ this._claimTxId = res.txId;
1160
+ await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
1161
+ }
1162
+ }
1163
+ if (res.type === base_1.SpvWithdrawalStateType.CLOSED) {
1164
+ if (this._state !== SpvFromBTCSwapState.CLOSED)
1165
+ await this._saveAndEmit(SpvFromBTCSwapState.CLOSED);
1166
+ throw new Error("Swap failed with catastrophic error!");
1167
+ }
1168
+ return true;
1169
+ }
1170
+ /**
1171
+ * Waits till the bitcoin transaction confirms and swap settled on the destination chain
1172
+ *
1173
+ * @param updateCallback Callback called when txId is found, and also called with subsequent confirmations
1174
+ * @param checkIntervalSeconds How often to check the bitcoin transaction (5 seconds by default)
1175
+ * @param abortSignal Abort signal
1176
+ *
1177
+ * @throws {Error} if in invalid state (must be {@link SpvFromBTCSwapState.POSTED} or
1178
+ * {@link SpvFromBTCSwapState.BROADCASTED} states)
1179
+ */
1180
+ async waitTillExecuted(updateCallback, checkIntervalSeconds, abortSignal) {
1181
+ await this.waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal);
1182
+ await this.waitTillClaimedOrFronted(undefined, abortSignal);
1183
+ }
1184
+ //////////////////////////////
1185
+ //// Storage
1186
+ /**
1187
+ * @inheritDoc
1188
+ */
1189
+ serialize() {
1190
+ return {
1191
+ ...super.serialize(),
1192
+ quoteId: this.quoteId,
1193
+ recipient: this.recipient,
1194
+ vaultOwner: this.vaultOwner,
1195
+ vaultId: this.vaultId.toString(10),
1196
+ vaultRequiredConfirmations: this.vaultRequiredConfirmations,
1197
+ vaultTokenMultipliers: this.vaultTokenMultipliers.map(val => val.toString(10)),
1198
+ vaultBtcAddress: this.vaultBtcAddress,
1199
+ vaultUtxo: this.vaultUtxo,
1200
+ vaultUtxoValue: this.vaultUtxoValue.toString(10),
1201
+ btcDestinationAddress: this.btcDestinationAddress,
1202
+ btcAmount: this.btcAmount.toString(10),
1203
+ btcAmountSwap: this.btcAmountSwap.toString(10),
1204
+ btcAmountGas: this.btcAmountGas.toString(10),
1205
+ minimumBtcFeeRate: this.minimumBtcFeeRate,
1206
+ outputTotalSwap: this.outputTotalSwap.toString(10),
1207
+ outputSwapToken: this.outputSwapToken,
1208
+ outputTotalGas: this.outputTotalGas.toString(10),
1209
+ outputGasToken: this.outputGasToken,
1210
+ gasSwapFeeBtc: this.gasSwapFeeBtc.toString(10),
1211
+ gasSwapFee: this.gasSwapFee.toString(10),
1212
+ callerFeeShare: this.callerFeeShare.toString(10),
1213
+ frontingFeeShare: this.frontingFeeShare.toString(10),
1214
+ executionFeeShare: this.executionFeeShare.toString(10),
1215
+ genesisSmartChainBlockHeight: this._genesisSmartChainBlockHeight,
1216
+ gasPricingInfo: (0, PriceInfoType_1.serializePriceInfoType)(this.gasPricingInfo),
1217
+ posted: this.posted,
1218
+ senderAddress: this._senderAddress,
1219
+ claimTxId: this._claimTxId,
1220
+ frontTxId: this._frontTxId,
1221
+ data: this._data?.serialize(),
1222
+ btcTxConfirmedAt: this.btcTxConfirmedAt
1223
+ };
1224
+ }
1225
+ //////////////////////////////
1226
+ //// Swap ticks & sync
1227
+ /**
1228
+ * Used to set the txId of the bitcoin payment from the on-chain events listener
1229
+ *
1230
+ * @param txId
1231
+ * @internal
1232
+ */
1233
+ async _setBitcoinTxId(txId) {
1234
+ if (this._data == null)
1235
+ return;
1236
+ if (txId != this._data.btcTx.txid)
1237
+ return;
1238
+ if (this._senderAddress != null)
1239
+ return;
1240
+ const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
1241
+ if (btcTx == null || btcTx.inputAddresses == null)
1242
+ return;
1243
+ this._senderAddress = btcTx.inputAddresses[1];
1244
+ }
1245
+ /**
1246
+ * @internal
1247
+ */
1248
+ async _syncStateFromBitcoin(save) {
1249
+ if (this._data?.btcTx == null)
1250
+ return false;
1251
+ //Check if bitcoin payment was confirmed
1252
+ this.btcTxLastChecked = Date.now();
1253
+ const res = await this.getBitcoinPayment();
1254
+ if (res == null) {
1255
+ //Check inputs double-spent
1256
+ for (let input of this._data.btcTx.ins) {
1257
+ if (await this.wrapper._btcRpc.isSpent(input.txid + ":" + input.vout, true)) {
1258
+ if (this._state === SpvFromBTCSwapState.SIGNED ||
1259
+ this._state === SpvFromBTCSwapState.POSTED ||
1260
+ this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1261
+ this._state === SpvFromBTCSwapState.DECLINED) {
1262
+ //One of the inputs was double-spent
1263
+ this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1264
+ }
1265
+ else {
1266
+ //One of the inputs was double-spent
1267
+ this._state = SpvFromBTCSwapState.FAILED;
1268
+ }
1269
+ if (save)
1270
+ await this._saveAndEmit();
1271
+ return true;
1272
+ }
1273
+ }
1274
+ }
1275
+ else {
1276
+ let needsSave = false;
1277
+ if (res.inputAddresses != null && this._senderAddress == null) {
1278
+ this._senderAddress = res.inputAddresses[1];
1279
+ needsSave = true;
1280
+ }
1281
+ if (res.confirmations >= this.vaultRequiredConfirmations) {
1282
+ if (this._state !== SpvFromBTCSwapState.BTC_TX_CONFIRMED &&
1283
+ this._state !== SpvFromBTCSwapState.FRONTED &&
1284
+ this._state !== SpvFromBTCSwapState.CLAIMED) {
1285
+ this.btcTxConfirmedAt ??= Date.now();
1286
+ this._state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
1287
+ needsSave = true;
1288
+ }
1289
+ }
1290
+ else if (this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1291
+ this._state === SpvFromBTCSwapState.POSTED ||
1292
+ this._state === SpvFromBTCSwapState.SIGNED ||
1293
+ this._state === SpvFromBTCSwapState.DECLINED) {
1294
+ this._state = SpvFromBTCSwapState.BROADCASTED;
1295
+ needsSave = true;
1296
+ }
1297
+ if (needsSave && save)
1298
+ await this._saveAndEmit();
1299
+ return needsSave;
1300
+ }
1301
+ return false;
1302
+ }
1303
+ /**
1304
+ * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1305
+ * data
1306
+ */
1307
+ async syncStateFromChain() {
1308
+ let changed = false;
1309
+ if (this._state === SpvFromBTCSwapState.SIGNED ||
1310
+ this._state === SpvFromBTCSwapState.POSTED ||
1311
+ this._state === SpvFromBTCSwapState.BROADCASTED ||
1312
+ this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1313
+ this._state === SpvFromBTCSwapState.DECLINED ||
1314
+ this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1315
+ //Check BTC transaction
1316
+ if (await this._syncStateFromBitcoin(false))
1317
+ changed ||= true;
1318
+ }
1319
+ if (this._state === SpvFromBTCSwapState.BROADCASTED || this._state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1320
+ if (await this._shouldCheckWithdrawalState()) {
1321
+ const status = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1322
+ this.logger.debug("syncStateFromChain(): status of " + this._data.btcTx.txid, status);
1323
+ switch (status?.type) {
1324
+ case base_1.SpvWithdrawalStateType.FRONTED:
1325
+ this._frontTxId = status.txId;
1326
+ this._state = SpvFromBTCSwapState.FRONTED;
1327
+ changed ||= true;
1328
+ break;
1329
+ case base_1.SpvWithdrawalStateType.CLAIMED:
1330
+ this._claimTxId = status.txId;
1331
+ this._state = SpvFromBTCSwapState.CLAIMED;
1332
+ changed ||= true;
1333
+ break;
1334
+ case base_1.SpvWithdrawalStateType.CLOSED:
1335
+ this._state = SpvFromBTCSwapState.CLOSED;
1336
+ changed ||= true;
1337
+ break;
1338
+ }
1339
+ }
1340
+ }
1341
+ if (this._state === SpvFromBTCSwapState.CREATED ||
1342
+ this._state === SpvFromBTCSwapState.SIGNED ||
1343
+ this._state === SpvFromBTCSwapState.POSTED) {
1344
+ if (this.expiry < Date.now()) {
1345
+ if (this._state === SpvFromBTCSwapState.CREATED) {
1346
+ this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1347
+ }
1348
+ else {
1349
+ this._state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
1350
+ }
1351
+ changed ||= true;
1352
+ }
1353
+ }
1354
+ return changed;
1355
+ }
1356
+ /**
1357
+ * @inheritDoc
1358
+ * @internal
1359
+ */
1360
+ async _sync(save) {
1361
+ const changed = await this.syncStateFromChain();
1362
+ if (changed && save)
1363
+ await this._saveAndEmit();
1364
+ return changed;
1365
+ }
1366
+ /**
1367
+ * @inheritDoc
1368
+ * @internal
1369
+ */
1370
+ async _tick(save) {
1371
+ if (this._state === SpvFromBTCSwapState.CREATED ||
1372
+ this._state === SpvFromBTCSwapState.SIGNED) {
1373
+ if (this.getQuoteExpiry() < Date.now()) {
1374
+ this._state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
1375
+ if (save)
1376
+ await this._saveAndEmit();
1377
+ return true;
1378
+ }
1379
+ }
1380
+ if (this._state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && !this.posted) {
1381
+ if (this.expiry < Date.now()) {
1382
+ this._state = SpvFromBTCSwapState.QUOTE_EXPIRED;
1383
+ if (save)
1384
+ await this._saveAndEmit();
1385
+ return true;
1386
+ }
1387
+ }
1388
+ if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120000) {
1389
+ if (this._state === SpvFromBTCSwapState.POSTED ||
1390
+ this._state === SpvFromBTCSwapState.BROADCASTED) {
1391
+ try {
1392
+ //Check if bitcoin payment was confirmed
1393
+ return await this._syncStateFromBitcoin(save);
1394
+ }
1395
+ catch (e) {
1396
+ this.logger.error("tickSwap(" + this.getId() + "): ", e);
1397
+ }
1398
+ }
1399
+ }
1400
+ return false;
1401
+ }
1402
+ /**
1403
+ * Checks whether an on-chain withdrawal state should be fetched for this specific swap
1404
+ *
1405
+ * @internal
1406
+ */
1407
+ async _shouldCheckWithdrawalState(frontingAddress, vaultDataUtxo) {
1408
+ if (frontingAddress === undefined)
1409
+ frontingAddress = await this._contract.getFronterAddress(this.vaultOwner, this.vaultId, this._data);
1410
+ if (vaultDataUtxo === undefined)
1411
+ vaultDataUtxo = await this._contract.getVaultLatestUtxo(this.vaultOwner, this.vaultId);
1412
+ if (frontingAddress != null)
1413
+ return true; //In case the swap is fronted there will for sure be a fronted event
1414
+ if (vaultDataUtxo == null)
1415
+ return true; //Vault UTXO is null (the vault closed)
1416
+ const [txId, _] = vaultDataUtxo.split(":");
1417
+ //Don't check both txns if their txId is equal
1418
+ if (this._data.btcTx.txid === txId)
1419
+ return true;
1420
+ const [btcTx, latestVaultTx] = await Promise.all([
1421
+ this.wrapper._btcRpc.getTransaction(this._data.btcTx.txid),
1422
+ this.wrapper._btcRpc.getTransaction(txId)
1423
+ ]);
1424
+ if (latestVaultTx == null || latestVaultTx.blockheight == null) {
1425
+ //Something must've gone horribly wrong, the latest vault utxo tx of the vault either
1426
+ // cannot be found on bitcoin network or is not even confirmed yet
1427
+ this.logger.debug(`_shouldCheckWithdrawalState(): Latest vault utxo not found or not confirmed on bitcoin ${txId}`);
1428
+ return false;
1429
+ }
1430
+ if (btcTx != null) {
1431
+ const btcTxHeight = btcTx.blockheight;
1432
+ const latestVaultTxHeight = latestVaultTx.blockheight;
1433
+ //We also need to cover the case where bitcoin tx isn't confirmed yet (hence btxTxHeight==null)
1434
+ if (btcTxHeight == null || latestVaultTxHeight < btcTxHeight) {
1435
+ //Definitely not claimed!
1436
+ this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, latestVaultTxHeight: ${latestVaultTx.blockheight}, btcTxHeight: ${btcTxHeight} and not fronted!`);
1437
+ return false;
1438
+ }
1439
+ }
1440
+ else {
1441
+ //Definitely not claimed because the transaction was probably double-spent (or evicted from mempool)
1442
+ this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, btc tx probably replaced or evicted: ${this._data.btcTx.txid} and not fronted`);
1443
+ return false;
1444
+ }
1445
+ return true;
1446
+ }
1447
+ }
1448
+ exports.SpvFromBTCSwap = SpvFromBTCSwap;