@atomiqlabs/sdk 8.9.1 → 8.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +1760 -1760
  3. package/api/index.d.ts +1 -1
  4. package/api/index.js +3 -3
  5. package/dist/ApiList.d.ts +37 -37
  6. package/dist/ApiList.js +30 -30
  7. package/dist/SmartChainAssets.d.ts +181 -181
  8. package/dist/SmartChainAssets.js +181 -181
  9. package/dist/api/ApiEndpoints.d.ts +393 -393
  10. package/dist/api/ApiEndpoints.js +2 -2
  11. package/dist/api/ApiParser.d.ts +10 -10
  12. package/dist/api/ApiParser.js +134 -134
  13. package/dist/api/ApiTypes.d.ts +157 -157
  14. package/dist/api/ApiTypes.js +75 -75
  15. package/dist/api/SerializedAction.d.ts +40 -40
  16. package/dist/api/SerializedAction.js +59 -59
  17. package/dist/api/SwapperApi.d.ts +50 -50
  18. package/dist/api/SwapperApi.js +431 -431
  19. package/dist/api/index.d.ts +5 -5
  20. package/dist/api/index.js +24 -24
  21. package/dist/bitcoin/coinselect2/accumulative.d.ts +7 -7
  22. package/dist/bitcoin/coinselect2/accumulative.js +52 -52
  23. package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -7
  24. package/dist/bitcoin/coinselect2/blackjack.js +38 -38
  25. package/dist/bitcoin/coinselect2/index.d.ts +20 -20
  26. package/dist/bitcoin/coinselect2/index.js +69 -69
  27. package/dist/bitcoin/coinselect2/utils.d.ts +82 -82
  28. package/dist/bitcoin/coinselect2/utils.js +158 -158
  29. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -113
  30. package/dist/bitcoin/wallet/BitcoinWallet.js +335 -335
  31. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -116
  32. package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
  33. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -106
  34. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -196
  35. package/dist/enums/FeeType.d.ts +15 -15
  36. package/dist/enums/FeeType.js +19 -19
  37. package/dist/enums/SwapAmountType.d.ts +15 -15
  38. package/dist/enums/SwapAmountType.js +19 -19
  39. package/dist/enums/SwapDirection.d.ts +15 -15
  40. package/dist/enums/SwapDirection.js +19 -19
  41. package/dist/enums/SwapSide.d.ts +15 -15
  42. package/dist/enums/SwapSide.js +19 -19
  43. package/dist/enums/SwapType.d.ts +75 -75
  44. package/dist/enums/SwapType.js +79 -79
  45. package/dist/errors/IntermediaryError.d.ts +13 -13
  46. package/dist/errors/IntermediaryError.js +27 -27
  47. package/dist/errors/RequestError.d.ts +32 -32
  48. package/dist/errors/RequestError.js +54 -54
  49. package/dist/errors/UserError.d.ts +8 -8
  50. package/dist/errors/UserError.js +16 -16
  51. package/dist/events/UnifiedSwapEventListener.d.ts +24 -24
  52. package/dist/events/UnifiedSwapEventListener.js +138 -138
  53. package/dist/http/HttpUtils.d.ts +29 -29
  54. package/dist/http/HttpUtils.js +97 -97
  55. package/dist/http/paramcoders/IParamReader.d.ts +8 -8
  56. package/dist/http/paramcoders/IParamReader.js +2 -2
  57. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -44
  58. package/dist/http/paramcoders/ParamDecoder.js +137 -137
  59. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -20
  60. package/dist/http/paramcoders/ParamEncoder.js +36 -36
  61. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
  62. package/dist/http/paramcoders/SchemaVerifier.js +145 -145
  63. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
  64. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
  65. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -13
  66. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
  67. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +17 -17
  68. package/dist/http/paramcoders/client/StreamingFetchPromise.js +175 -175
  69. package/dist/index.d.ts +86 -86
  70. package/dist/index.js +159 -159
  71. package/dist/intermediaries/Intermediary.d.ts +178 -178
  72. package/dist/intermediaries/Intermediary.js +166 -166
  73. package/dist/intermediaries/IntermediaryDiscovery.d.ts +216 -216
  74. package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
  75. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +607 -607
  76. package/dist/intermediaries/apis/IntermediaryAPI.js +764 -764
  77. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
  78. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
  79. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -14
  80. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -68
  81. package/dist/lnurl/LNURL.d.ts +102 -102
  82. package/dist/lnurl/LNURL.js +321 -321
  83. package/dist/prices/RedundantSwapPrice.d.ts +110 -110
  84. package/dist/prices/RedundantSwapPrice.js +222 -222
  85. package/dist/prices/SingleSwapPrice.d.ts +34 -34
  86. package/dist/prices/SingleSwapPrice.js +44 -44
  87. package/dist/prices/SwapPriceWithChain.d.ts +107 -107
  88. package/dist/prices/SwapPriceWithChain.js +128 -128
  89. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
  90. package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
  91. package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
  92. package/dist/prices/abstract/IPriceProvider.js +74 -74
  93. package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
  94. package/dist/prices/abstract/ISwapPrice.js +279 -279
  95. package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
  96. package/dist/prices/providers/BinancePriceProvider.js +30 -30
  97. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
  98. package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
  99. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
  100. package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
  101. package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
  102. package/dist/prices/providers/CustomPriceProvider.js +35 -35
  103. package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
  104. package/dist/prices/providers/KrakenPriceProvider.js +45 -45
  105. package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
  106. package/dist/prices/providers/OKXPriceProvider.js +29 -29
  107. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
  108. package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
  109. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
  110. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
  111. package/dist/storage/IUnifiedStorage.d.ts +127 -127
  112. package/dist/storage/IUnifiedStorage.js +2 -2
  113. package/dist/storage/UnifiedSwapStorage.d.ts +120 -120
  114. package/dist/storage/UnifiedSwapStorage.js +154 -154
  115. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
  116. package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
  117. package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
  118. package/dist/storage-browser/LocalStorageManager.js +93 -93
  119. package/dist/swapper/Swapper.d.ts +765 -770
  120. package/dist/swapper/Swapper.js +1749 -1758
  121. package/dist/swapper/SwapperFactory.d.ts +135 -135
  122. package/dist/swapper/SwapperFactory.js +162 -162
  123. package/dist/swapper/SwapperUtils.d.ts +222 -222
  124. package/dist/swapper/SwapperUtils.js +519 -519
  125. package/dist/swapper/SwapperWithChain.d.ts +404 -404
  126. package/dist/swapper/SwapperWithChain.js +469 -469
  127. package/dist/swapper/SwapperWithSigner.d.ts +322 -322
  128. package/dist/swapper/SwapperWithSigner.js +318 -318
  129. package/dist/swaps/IAddressSwap.d.ts +22 -22
  130. package/dist/swaps/IAddressSwap.js +14 -14
  131. package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
  132. package/dist/swaps/IBTCWalletSwap.js +18 -18
  133. package/dist/swaps/IClaimableSwap.d.ts +49 -49
  134. package/dist/swaps/IClaimableSwap.js +15 -15
  135. package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
  136. package/dist/swaps/IClaimableSwapWrapper.js +2 -2
  137. package/dist/swaps/IRefundableSwap.d.ts +43 -43
  138. package/dist/swaps/IRefundableSwap.js +14 -14
  139. package/dist/swaps/ISwap.d.ts +453 -453
  140. package/dist/swaps/ISwap.js +371 -371
  141. package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
  142. package/dist/swaps/ISwapWithGasDrop.js +12 -12
  143. package/dist/swaps/ISwapWrapper.d.ts +295 -295
  144. package/dist/swaps/ISwapWrapper.js +373 -373
  145. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
  146. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
  147. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +139 -139
  148. package/dist/swaps/escrow_swaps/IEscrowSwap.js +172 -172
  149. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +129 -129
  150. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
  151. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +107 -107
  152. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +130 -130
  153. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
  154. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
  155. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +64 -64
  156. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +82 -82
  157. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +547 -547
  158. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1419 -1419
  159. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +192 -192
  160. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
  161. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +650 -650
  162. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1577 -1577
  163. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +237 -237
  164. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
  165. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +491 -491
  166. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1463 -1463
  167. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +204 -204
  168. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
  169. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +446 -446
  170. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +1097 -1097
  171. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +68 -68
  172. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +117 -117
  173. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -127
  174. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
  175. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +252 -252
  176. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +535 -535
  177. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
  178. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
  179. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +134 -134
  180. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
  181. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +694 -694
  182. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1687 -1687
  183. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +259 -259
  184. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -947
  185. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +302 -302
  186. package/dist/swaps/trusted/ln/LnForGasSwap.js +625 -625
  187. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
  188. package/dist/swaps/trusted/ln/LnForGasWrapper.js +82 -82
  189. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +343 -343
  190. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +698 -698
  191. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +71 -71
  192. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +93 -93
  193. package/dist/types/AmountData.d.ts +10 -10
  194. package/dist/types/AmountData.js +2 -2
  195. package/dist/types/CustomPriceFunction.d.ts +11 -11
  196. package/dist/types/CustomPriceFunction.js +2 -2
  197. package/dist/types/PriceInfoType.d.ts +28 -28
  198. package/dist/types/PriceInfoType.js +57 -57
  199. package/dist/types/SwapExecutionAction.d.ts +195 -195
  200. package/dist/types/SwapExecutionAction.js +106 -106
  201. package/dist/types/SwapExecutionStep.d.ts +144 -144
  202. package/dist/types/SwapExecutionStep.js +87 -87
  203. package/dist/types/SwapStateInfo.d.ts +5 -5
  204. package/dist/types/SwapStateInfo.js +2 -2
  205. package/dist/types/SwapWithSigner.d.ts +17 -17
  206. package/dist/types/SwapWithSigner.js +43 -43
  207. package/dist/types/Token.d.ts +99 -99
  208. package/dist/types/Token.js +76 -76
  209. package/dist/types/TokenAmount.d.ts +75 -75
  210. package/dist/types/TokenAmount.js +85 -85
  211. package/dist/types/fees/Fee.d.ts +50 -50
  212. package/dist/types/fees/Fee.js +2 -2
  213. package/dist/types/fees/FeeBreakdown.d.ts +11 -11
  214. package/dist/types/fees/FeeBreakdown.js +2 -2
  215. package/dist/types/fees/PercentagePPM.d.ts +17 -17
  216. package/dist/types/fees/PercentagePPM.js +18 -18
  217. package/dist/types/lnurl/LNURLPay.d.ts +61 -61
  218. package/dist/types/lnurl/LNURLPay.js +31 -31
  219. package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
  220. package/dist/types/lnurl/LNURLWithdraw.js +27 -27
  221. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
  222. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -15
  223. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
  224. package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
  225. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
  226. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
  227. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
  228. package/dist/utils/AutomaticClockDriftCorrection.js +70 -70
  229. package/dist/utils/BitcoinUtils.d.ts +18 -18
  230. package/dist/utils/BitcoinUtils.js +174 -174
  231. package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
  232. package/dist/utils/BitcoinWalletUtils.js +14 -14
  233. package/dist/utils/Logger.d.ts +7 -7
  234. package/dist/utils/Logger.js +12 -12
  235. package/dist/utils/RetryUtils.d.ts +22 -22
  236. package/dist/utils/RetryUtils.js +67 -67
  237. package/dist/utils/SwapUtils.d.ts +88 -88
  238. package/dist/utils/SwapUtils.js +72 -72
  239. package/dist/utils/TimeoutUtils.d.ts +17 -17
  240. package/dist/utils/TimeoutUtils.js +55 -55
  241. package/dist/utils/TokenUtils.d.ts +19 -19
  242. package/dist/utils/TokenUtils.js +37 -37
  243. package/dist/utils/TypeUtils.d.ts +7 -7
  244. package/dist/utils/TypeUtils.js +2 -2
  245. package/dist/utils/Utils.d.ts +69 -69
  246. package/dist/utils/Utils.js +214 -214
  247. package/package.json +46 -46
  248. package/src/SmartChainAssets.ts +186 -186
  249. package/src/api/ApiEndpoints.ts +427 -427
  250. package/src/api/ApiParser.ts +138 -138
  251. package/src/api/ApiTypes.ts +229 -229
  252. package/src/api/SerializedAction.ts +97 -97
  253. package/src/api/SwapperApi.ts +545 -545
  254. package/src/api/index.ts +5 -5
  255. package/src/bitcoin/coinselect2/accumulative.ts +69 -69
  256. package/src/bitcoin/coinselect2/blackjack.ts +50 -50
  257. package/src/bitcoin/coinselect2/index.ts +93 -93
  258. package/src/bitcoin/coinselect2/utils.ts +236 -236
  259. package/src/bitcoin/wallet/BitcoinWallet.ts +439 -439
  260. package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -140
  261. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -225
  262. package/src/enums/FeeType.ts +15 -15
  263. package/src/enums/SwapAmountType.ts +16 -16
  264. package/src/enums/SwapDirection.ts +15 -15
  265. package/src/enums/SwapSide.ts +16 -16
  266. package/src/enums/SwapType.ts +75 -75
  267. package/src/errors/IntermediaryError.ts +28 -28
  268. package/src/errors/RequestError.ts +64 -64
  269. package/src/errors/UserError.ts +15 -15
  270. package/src/events/UnifiedSwapEventListener.ts +181 -181
  271. package/src/http/HttpUtils.ts +97 -97
  272. package/src/http/paramcoders/IParamReader.ts +9 -9
  273. package/src/http/paramcoders/ParamDecoder.ts +145 -145
  274. package/src/http/paramcoders/ParamEncoder.ts +40 -40
  275. package/src/http/paramcoders/SchemaVerifier.ts +153 -153
  276. package/src/http/paramcoders/client/ResponseParamDecoder.ts +57 -57
  277. package/src/http/paramcoders/client/StreamParamEncoder.ts +28 -28
  278. package/src/http/paramcoders/client/StreamingFetchPromise.ts +194 -194
  279. package/src/index.ts +141 -141
  280. package/src/intermediaries/Intermediary.ts +280 -280
  281. package/src/intermediaries/IntermediaryDiscovery.ts +548 -548
  282. package/src/intermediaries/apis/IntermediaryAPI.ts +1247 -1247
  283. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -69
  284. package/src/lnurl/LNURL.ts +402 -402
  285. package/src/prices/RedundantSwapPrice.ts +264 -264
  286. package/src/prices/SingleSwapPrice.ts +50 -50
  287. package/src/prices/SwapPriceWithChain.ts +194 -194
  288. package/src/prices/abstract/ICachedSwapPrice.ts +85 -85
  289. package/src/prices/abstract/IPriceProvider.ts +127 -127
  290. package/src/prices/abstract/ISwapPrice.ts +390 -390
  291. package/src/prices/providers/BinancePriceProvider.ts +48 -48
  292. package/src/prices/providers/CoinGeckoPriceProvider.ts +46 -46
  293. package/src/prices/providers/CoinPaprikaPriceProvider.ts +49 -49
  294. package/src/prices/providers/CustomPriceProvider.ts +40 -40
  295. package/src/prices/providers/KrakenPriceProvider.ts +83 -83
  296. package/src/prices/providers/OKXPriceProvider.ts +59 -59
  297. package/src/prices/providers/abstract/ExchangePriceProvider.ts +31 -31
  298. package/src/prices/providers/abstract/HttpPriceProvider.ts +14 -14
  299. package/src/storage/IUnifiedStorage.ts +136 -136
  300. package/src/storage/UnifiedSwapStorage.ts +175 -175
  301. package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
  302. package/src/storage-browser/LocalStorageManager.ts +106 -106
  303. package/src/swapper/Swapper.ts +2557 -2570
  304. package/src/swapper/SwapperFactory.ts +307 -307
  305. package/src/swapper/SwapperUtils.ts +610 -610
  306. package/src/swapper/SwapperWithChain.ts +707 -707
  307. package/src/swapper/SwapperWithSigner.ts +511 -511
  308. package/src/swaps/IAddressSwap.ts +30 -30
  309. package/src/swaps/IBTCWalletSwap.ts +92 -92
  310. package/src/swaps/IClaimableSwap.ts +65 -65
  311. package/src/swaps/IClaimableSwapWrapper.ts +17 -17
  312. package/src/swaps/IRefundableSwap.ts +58 -58
  313. package/src/swaps/ISwap.ts +775 -775
  314. package/src/swaps/ISwapWithGasDrop.ts +25 -25
  315. package/src/swaps/ISwapWrapper.ts +564 -564
  316. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
  317. package/src/swaps/escrow_swaps/IEscrowSwap.ts +271 -271
  318. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +284 -284
  319. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +172 -172
  320. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +300 -300
  321. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +107 -107
  322. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1670 -1671
  323. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +603 -603
  324. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1883 -1883
  325. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +752 -752
  326. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1753 -1753
  327. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +612 -612
  328. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1327 -1327
  329. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +138 -138
  330. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +304 -304
  331. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +787 -787
  332. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
  333. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +403 -403
  334. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +2148 -2148
  335. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1238 -1238
  336. package/src/swaps/trusted/ln/LnForGasSwap.ts +753 -753
  337. package/src/swaps/trusted/ln/LnForGasWrapper.ts +90 -90
  338. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +843 -843
  339. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +133 -133
  340. package/src/types/AmountData.ts +9 -9
  341. package/src/types/CustomPriceFunction.ts +11 -11
  342. package/src/types/PriceInfoType.ts +66 -66
  343. package/src/types/SwapExecutionAction.ts +323 -323
  344. package/src/types/SwapExecutionStep.ts +224 -224
  345. package/src/types/SwapStateInfo.ts +6 -6
  346. package/src/types/SwapWithSigner.ts +61 -61
  347. package/src/types/Token.ts +163 -163
  348. package/src/types/TokenAmount.ts +167 -167
  349. package/src/types/fees/Fee.ts +56 -56
  350. package/src/types/fees/FeeBreakdown.ts +11 -11
  351. package/src/types/fees/PercentagePPM.ts +26 -26
  352. package/src/types/lnurl/LNURLPay.ts +79 -79
  353. package/src/types/lnurl/LNURLWithdraw.ts +61 -61
  354. package/src/types/wallets/LightningInvoiceCreateService.ts +30 -30
  355. package/src/types/wallets/MinimalBitcoinWalletInterface.ts +21 -21
  356. package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +9 -9
  357. package/src/utils/AutomaticClockDriftCorrection.ts +71 -71
  358. package/src/utils/BitcoinUtils.ts +164 -164
  359. package/src/utils/BitcoinWalletUtils.ts +15 -15
  360. package/src/utils/Logger.ts +14 -14
  361. package/src/utils/RetryUtils.ts +78 -78
  362. package/src/utils/SwapUtils.ts +99 -99
  363. package/src/utils/TimeoutUtils.ts +49 -49
  364. package/src/utils/TokenUtils.ts +33 -33
  365. package/src/utils/TypeUtils.ts +8 -8
  366. package/src/utils/Utils.ts +221 -221
@@ -1,1463 +1,1463 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FromBTCSwap = exports.isFromBTCSwapInit = exports.FromBTCSwapState = void 0;
4
- const IFromBTCSelfInitSwap_1 = require("../IFromBTCSelfInitSwap");
5
- const SwapType_1 = require("../../../../enums/SwapType");
6
- const base_1 = require("@atomiqlabs/base");
7
- const buffer_1 = require("buffer");
8
- const Utils_1 = require("../../../../utils/Utils");
9
- const BitcoinUtils_1 = require("../../../../utils/BitcoinUtils");
10
- const IBitcoinWallet_1 = require("../../../../bitcoin/wallet/IBitcoinWallet");
11
- const btc_signer_1 = require("@scure/btc-signer");
12
- const SingleAddressBitcoinWallet_1 = require("../../../../bitcoin/wallet/SingleAddressBitcoinWallet");
13
- const IEscrowSelfInitSwap_1 = require("../../IEscrowSelfInitSwap");
14
- const TokenAmount_1 = require("../../../../types/TokenAmount");
15
- const Token_1 = require("../../../../types/Token");
16
- const Logger_1 = require("../../../../utils/Logger");
17
- const BitcoinWalletUtils_1 = require("../../../../utils/BitcoinWalletUtils");
18
- /**
19
- * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
20
- *
21
- * @category Swaps/Legacy/Bitcoin → Smart chain
22
- */
23
- var FromBTCSwapState;
24
- (function (FromBTCSwapState) {
25
- /**
26
- * Bitcoin swap address has expired and the intermediary (LP) has already refunded
27
- * its funds. No BTC should be sent anymore!
28
- */
29
- FromBTCSwapState[FromBTCSwapState["FAILED"] = -4] = "FAILED";
30
- /**
31
- * Bitcoin swap address has expired, user should not send any BTC anymore! Though
32
- * the intermediary (LP) hasn't refunded yet. So if there is a transaction already
33
- * in-flight the swap might still succeed.
34
- */
35
- FromBTCSwapState[FromBTCSwapState["EXPIRED"] = -3] = "EXPIRED";
36
- /**
37
- * Swap has expired for good and there is no way how it can be executed anymore
38
- */
39
- FromBTCSwapState[FromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
40
- /**
41
- * A swap is almost expired, and it should be presented to the user as expired, though
42
- * there is still a chance that it will be processed
43
- */
44
- FromBTCSwapState[FromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
45
- /**
46
- * Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
47
- * to initiate it by creating the swap escrow on the destination smart chain
48
- */
49
- FromBTCSwapState[FromBTCSwapState["PR_CREATED"] = 0] = "PR_CREATED";
50
- /**
51
- * Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
52
- * swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
53
- * {@link FromBTCSwap.getHyperlink} functions.
54
- */
55
- FromBTCSwapState[FromBTCSwapState["CLAIM_COMMITED"] = 1] = "CLAIM_COMMITED";
56
- /**
57
- * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtowers
58
- * using the {@link FromBTCSwap.waitTillClaimed} function or settle manually using the {@link FromBTCSwap.claim}
59
- * or {@link FromBTCSwap.txsClaim} function.
60
- */
61
- FromBTCSwapState[FromBTCSwapState["BTC_TX_CONFIRMED"] = 2] = "BTC_TX_CONFIRMED";
62
- /**
63
- * Swap successfully settled and funds received on the destination chain
64
- */
65
- FromBTCSwapState[FromBTCSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
66
- })(FromBTCSwapState = exports.FromBTCSwapState || (exports.FromBTCSwapState = {}));
67
- const FromBTCSwapStateDescription = {
68
- [FromBTCSwapState.FAILED]: "Bitcoin swap address has expired and the intermediary (LP) has already refunded its funds. No BTC should be sent anymore!",
69
- [FromBTCSwapState.EXPIRED]: "Bitcoin swap address has expired, user should not send any BTC anymore! Though the intermediary (LP) hasn't refunded yet. So if there is a transaction already in-flight the swap might still succeed.",
70
- [FromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
71
- [FromBTCSwapState.QUOTE_SOFT_EXPIRED]: "The swap is expired, though there is still a chance that it will be processed",
72
- [FromBTCSwapState.PR_CREATED]: "Swap quote was created, initiate it by creating the swap escrow on the destination smart chain",
73
- [FromBTCSwapState.CLAIM_COMMITED]: "Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the Bitcoin swap address.",
74
- [FromBTCSwapState.BTC_TX_CONFIRMED]: "Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower or settle manually.",
75
- [FromBTCSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
76
- };
77
- function isFromBTCSwapInit(obj) {
78
- return typeof (obj.data) === "object" &&
79
- (obj.address == null || typeof (obj.address) === "string") &&
80
- (obj.amount == null || typeof (obj.amount) === "bigint") &&
81
- (obj.requiredConfirmations == null || typeof (obj.requiredConfirmations) === "number") &&
82
- (0, IEscrowSelfInitSwap_1.isIEscrowSelfInitSwapInit)(obj);
83
- }
84
- exports.isFromBTCSwapInit = isFromBTCSwapInit;
85
- /**
86
- * Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
87
- * of the swap escrow on the destination chain.
88
- *
89
- * @category Swaps/Legacy/Bitcoin → Smart chain
90
- */
91
- class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
92
- constructor(wrapper, initOrObject) {
93
- if (isFromBTCSwapInit(initOrObject) && initOrObject.url != null)
94
- initOrObject.url += "/frombtc";
95
- super(wrapper, initOrObject);
96
- this.TYPE = SwapType_1.SwapType.FROM_BTC;
97
- /**
98
- * @internal
99
- */
100
- this.swapStateName = (state) => FromBTCSwapState[state];
101
- /**
102
- * @internal
103
- */
104
- this.swapStateDescription = FromBTCSwapStateDescription;
105
- /**
106
- * @internal
107
- */
108
- this.inputToken = Token_1.BitcoinTokens.BTC;
109
- if (isFromBTCSwapInit(initOrObject)) {
110
- this._state = FromBTCSwapState.PR_CREATED;
111
- this._data = initOrObject.data;
112
- this.feeRate = initOrObject.feeRate;
113
- this.address = initOrObject.address;
114
- this.amount = initOrObject.amount;
115
- this.requiredConfirmations = initOrObject.requiredConfirmations;
116
- }
117
- else {
118
- this.address = initOrObject.address;
119
- this.amount = (0, Utils_1.toBigInt)(initOrObject.amount);
120
- this.senderAddress = initOrObject.senderAddress;
121
- this.txId = initOrObject.txId;
122
- this.vout = initOrObject.vout;
123
- this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
124
- this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
125
- }
126
- this.tryRecomputeSwapPrice();
127
- this.logger = (0, Logger_1.getLogger)("FromBTC(" + this.getIdentifierHashString() + "): ");
128
- }
129
- /**
130
- * @inheritDoc
131
- * @internal
132
- */
133
- getSwapData() {
134
- return this._data;
135
- }
136
- /**
137
- * @inheritDoc
138
- * @internal
139
- */
140
- upgradeVersion() {
141
- if (this.version == null) {
142
- switch (this._state) {
143
- case -2:
144
- this._state = FromBTCSwapState.FAILED;
145
- break;
146
- case -1:
147
- this._state = FromBTCSwapState.QUOTE_EXPIRED;
148
- break;
149
- case 0:
150
- this._state = FromBTCSwapState.PR_CREATED;
151
- break;
152
- case 1:
153
- this._state = FromBTCSwapState.CLAIM_COMMITED;
154
- break;
155
- case 2:
156
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
157
- break;
158
- case 3:
159
- this._state = FromBTCSwapState.CLAIM_CLAIMED;
160
- break;
161
- }
162
- this.version = 1;
163
- }
164
- }
165
- //////////////////////////////
166
- //// Getters & utils
167
- /**
168
- * Returns bitcoin address where the on-chain BTC should be sent to
169
- */
170
- getAddress() {
171
- if (this._state === FromBTCSwapState.PR_CREATED)
172
- throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
173
- return this.address ?? "";
174
- }
175
- /**
176
- * Unsafe bitcoin hyperlink getter, returns the address even before the swap is committed!
177
- *
178
- * @private
179
- */
180
- _getHyperlink() {
181
- return this.address == null || this.amount == null ? "" : "bitcoin:" + this.address + "?amount=" + encodeURIComponent((Number(this.amount) / 100000000).toString(10));
182
- }
183
- /**
184
- * @inheritDoc
185
- */
186
- getHyperlink() {
187
- if (this._state === FromBTCSwapState.PR_CREATED)
188
- throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
189
- return this._getHyperlink();
190
- }
191
- /**
192
- * @inheritDoc
193
- */
194
- getInputAddress() {
195
- return this.senderAddress ?? null;
196
- }
197
- /**
198
- * @inheritDoc
199
- */
200
- getInputTxId() {
201
- return this.txId ?? null;
202
- }
203
- async _setSubmittedBitcoinTx(txId, psbt) {
204
- let changed = false;
205
- if (this.txId !== txId) {
206
- this.txId = txId;
207
- changed = true;
208
- }
209
- const submittedVout = this.address == null || this.amount == null || psbt == null
210
- ? undefined
211
- : (0, BitcoinUtils_1.getVoutIndex)(psbt, this.wrapper._options.bitcoinNetwork, this.address, this.amount);
212
- if (submittedVout != null && this.vout !== submittedVout) {
213
- this.vout = submittedVout;
214
- changed = true;
215
- }
216
- const submittedSenderAddress = psbt == null
217
- ? undefined
218
- : (0, BitcoinUtils_1.getSenderAddress)(psbt, this.wrapper._options.bitcoinNetwork);
219
- if (submittedSenderAddress != null && this.senderAddress !== submittedSenderAddress) {
220
- this.senderAddress = submittedSenderAddress;
221
- changed = true;
222
- }
223
- if (changed)
224
- await this._saveAndEmit();
225
- }
226
- /**
227
- * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
228
- * to that address anymore
229
- */
230
- getTimeoutTime() {
231
- return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
232
- }
233
- /**
234
- * @inheritDoc
235
- */
236
- requiresAction() {
237
- return this.isClaimable() || (this._state === FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime() > Date.now() && this.txId == null);
238
- }
239
- /**
240
- * @inheritDoc
241
- */
242
- isFinished() {
243
- return this._state === FromBTCSwapState.CLAIM_CLAIMED || this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.FAILED;
244
- }
245
- /**
246
- * @inheritDoc
247
- */
248
- isClaimable() {
249
- return this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
250
- }
251
- /**
252
- * @inheritDoc
253
- */
254
- isSuccessful() {
255
- return this._state === FromBTCSwapState.CLAIM_CLAIMED;
256
- }
257
- /**
258
- * @inheritDoc
259
- */
260
- isFailed() {
261
- return this._state === FromBTCSwapState.FAILED || this._state === FromBTCSwapState.EXPIRED;
262
- }
263
- /**
264
- * @inheritDoc
265
- */
266
- isInProgress() {
267
- return this._state === FromBTCSwapState.CLAIM_COMMITED ||
268
- this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
269
- }
270
- /**
271
- * @inheritDoc
272
- */
273
- isQuoteExpired() {
274
- return this._state === FromBTCSwapState.QUOTE_EXPIRED;
275
- }
276
- /**
277
- * @inheritDoc
278
- */
279
- isQuoteSoftExpired() {
280
- return this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
281
- }
282
- /**
283
- * @inheritDoc
284
- * @internal
285
- */
286
- canCommit(skipQuoteExpiryChecks) {
287
- if (this._state !== FromBTCSwapState.PR_CREATED && (!skipQuoteExpiryChecks || this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED))
288
- return false;
289
- if (this.requiredConfirmations == null)
290
- return false;
291
- const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
292
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
293
- return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
294
- }
295
- //////////////////////////////
296
- //// Amounts & fees
297
- /**
298
- * @inheritDoc
299
- */
300
- getInputToken() {
301
- return Token_1.BitcoinTokens.BTC;
302
- }
303
- /**
304
- * @inheritDoc
305
- */
306
- getInput() {
307
- return (0, TokenAmount_1.toTokenAmount)(this.amount ?? null, this.inputToken, this.wrapper._prices);
308
- }
309
- /**
310
- * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
311
- * this amount is pre-funded by the user on the destination chain when the swap escrow
312
- * is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
313
- */
314
- getClaimerBounty() {
315
- return (0, TokenAmount_1.toTokenAmount)(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
316
- }
317
- //////////////////////////////
318
- //// Bitcoin tx
319
- /**
320
- * If the required number of confirmations is not known, this function tries to infer it by looping through
321
- * possible confirmation targets and comparing the claim hashes
322
- *
323
- * @param btcTx Bitcoin transaction
324
- * @param vout Output index of the desired output in the bitcoin transaction
325
- *
326
- * @private
327
- */
328
- inferRequiredConfirmationsCount(btcTx, vout) {
329
- const txOut = btcTx.outs[vout];
330
- for (let i = 1; i <= 20; i++) {
331
- const computedClaimHash = this._contract.getHashForOnchain(buffer_1.Buffer.from(txOut.scriptPubKey.hex, "hex"), BigInt(txOut.value), i);
332
- if (computedClaimHash.toString("hex") === this._data.getClaimHash()) {
333
- return i;
334
- }
335
- }
336
- }
337
- /**
338
- * @inheritDoc
339
- */
340
- getRequiredConfirmationsCount() {
341
- return this.requiredConfirmations ?? NaN;
342
- }
343
- /**
344
- * Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
345
- *
346
- * @internal
347
- */
348
- async getBitcoinPayment() {
349
- const txoHashHint = this._data.getTxoHashHint();
350
- if (txoHashHint == null)
351
- throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
352
- if (this.address == null)
353
- throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
354
- const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, buffer_1.Buffer.from(txoHashHint, "hex"));
355
- if (result == null)
356
- return null;
357
- if (this.requiredConfirmations == null) {
358
- this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
359
- }
360
- return {
361
- inputAddresses: result.tx.inputAddresses,
362
- txId: result.tx.txid,
363
- vout: result.vout,
364
- confirmations: result.tx.confirmations ?? 0,
365
- targetConfirmations: this.getRequiredConfirmationsCount()
366
- };
367
- }
368
- /**
369
- * Used to set the txId of the bitcoin payment from the on-chain events listener
370
- *
371
- * @param txId Transaction ID that settled the swap on the smart chain
372
- *
373
- * @internal
374
- */
375
- async _setBitcoinTxId(txId) {
376
- if (this.txId !== txId || this.address == null || this.vout == null || this.senderAddress == null || this.amount == null) {
377
- const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
378
- if (btcTx == null)
379
- return;
380
- const txoHashHint = this._data.getTxoHashHint();
381
- if (txoHashHint != null) {
382
- const expectedTxoHash = buffer_1.Buffer.from(txoHashHint, "hex");
383
- const vout = btcTx.outs.findIndex(out => (0, Utils_1.getTxoHash)(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
384
- if (vout !== -1) {
385
- this.vout = vout;
386
- //If amount or address are not known, parse them from the bitcoin tx
387
- // this can happen if the swap is recovered from on-chain data and
388
- // hence doesn't contain the address and amount data
389
- if (this.amount == null)
390
- this.amount = BigInt(btcTx.outs[vout].value);
391
- if (this.address == null)
392
- try {
393
- this.address = (0, BitcoinUtils_1.fromOutputScript)(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
394
- }
395
- catch (e) {
396
- this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
397
- }
398
- if (this.requiredConfirmations == null) {
399
- this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
400
- }
401
- }
402
- }
403
- if (btcTx.inputAddresses != null) {
404
- this.senderAddress = btcTx.inputAddresses[0];
405
- }
406
- }
407
- this.txId = txId;
408
- }
409
- /**
410
- * @inheritDoc
411
- *
412
- * @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
413
- */
414
- async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
415
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.EXPIRED)
416
- throw new Error("Must be in COMMITED state!");
417
- const txoHashHint = this._data.getTxoHashHint();
418
- if (txoHashHint == null)
419
- throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
420
- if (this.address == null)
421
- throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
422
- let abortedDueToEnoughConfirmationsResult;
423
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
424
- const result = await this.wrapper._btcRpc.waitForAddressTxo(this.address, buffer_1.Buffer.from(txoHashHint, "hex"), this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
425
- (btcTx, vout, txEtaMs) => {
426
- let requiredConfirmations = this.requiredConfirmations;
427
- if (btcTx != null && vout != null && requiredConfirmations == null) {
428
- requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
429
- }
430
- if (btcTx != null && (btcTx.txid !== this.txId ||
431
- this.vout == null ||
432
- this.senderAddress == null ||
433
- (this.requiredConfirmations == null && requiredConfirmations != null))) {
434
- this.txId = btcTx.txid;
435
- this.vout = vout;
436
- this.requiredConfirmations = requiredConfirmations;
437
- if (btcTx.inputAddresses != null)
438
- this.senderAddress = btcTx.inputAddresses[0];
439
- this._saveAndEmit().catch(e => {
440
- this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e);
441
- });
442
- }
443
- //Abort the loop as soon as the transaction gets enough confirmations, this is required in case
444
- // we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
445
- // target from the prior block
446
- if (btcTx?.confirmations != null && requiredConfirmations != null && requiredConfirmations <= btcTx.confirmations && vout != null) {
447
- abortedDueToEnoughConfirmationsResult = {
448
- tx: btcTx,
449
- vout
450
- };
451
- abortController.abort();
452
- return;
453
- }
454
- if (updateCallback != null)
455
- updateCallback(btcTx?.txid, btcTx == null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
456
- }, abortController.signal, checkIntervalSeconds).catch(e => {
457
- //We catch the case when the loop was aborted due to the transaction getting enough confirmations
458
- if (abortedDueToEnoughConfirmationsResult != null)
459
- return abortedDueToEnoughConfirmationsResult;
460
- throw e;
461
- });
462
- if (abortSignal != null)
463
- abortSignal.throwIfAborted();
464
- this.txId = result.tx.txid;
465
- this.vout = result.vout;
466
- if (result.tx.inputAddresses != null)
467
- this.senderAddress = result.tx.inputAddresses[0];
468
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
469
- this._state !== FromBTCSwapState.FAILED) {
470
- this.btcTxConfirmedAt ??= Date.now();
471
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
472
- }
473
- await this._saveAndEmit();
474
- return result.tx.txid;
475
- }
476
- /**
477
- * Private getter of the funded PSBT that doesn't check current state
478
- *
479
- * @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
480
- * @param feeRate Optional bitcoin fee rate in sats/vB
481
- * @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
482
- *
483
- * @private
484
- */
485
- async _getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
486
- if (this.address == null)
487
- throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
488
- let bitcoinWallet;
489
- if ((0, IBitcoinWallet_1.isIBitcoinWallet)(_bitcoinWallet)) {
490
- bitcoinWallet = _bitcoinWallet;
491
- }
492
- else {
493
- bitcoinWallet = new SingleAddressBitcoinWallet_1.SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
494
- }
495
- //TODO: Maybe re-introduce fee rate check here if passed from the user
496
- if (feeRate == null) {
497
- feeRate = await bitcoinWallet.getFeeRate();
498
- }
499
- const basePsbt = new btc_signer_1.Transaction({
500
- allowUnknownOutputs: true,
501
- allowLegacyWitnessUtxo: true
502
- });
503
- basePsbt.addOutput({
504
- amount: this.amount,
505
- script: (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address)
506
- });
507
- if (additionalOutputs != null)
508
- additionalOutputs.forEach(output => {
509
- basePsbt.addOutput({
510
- amount: output.amount,
511
- script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
512
- });
513
- });
514
- const psbt = await bitcoinWallet.fundPsbt(basePsbt, feeRate);
515
- //Sign every input
516
- const signInputs = [];
517
- for (let i = 0; i < psbt.inputsLength; i++) {
518
- signInputs.push(i);
519
- }
520
- const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
521
- return {
522
- psbt,
523
- psbtHex: serializedPsbt.toString("hex"),
524
- psbtBase64: serializedPsbt.toString("base64"),
525
- signInputs,
526
- feeRate
527
- };
528
- }
529
- /**
530
- * @inheritDoc
531
- */
532
- getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
533
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
534
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
535
- if (this.txId != null)
536
- throw new Error("Bitcoin transaction already submitted for this swap!");
537
- return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
538
- }
539
- /**
540
- * @inheritDoc
541
- *
542
- * @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
543
- * the swap bitcoin address already expired.
544
- */
545
- async submitPsbt(_psbt) {
546
- const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
547
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
548
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
549
- if (this.txId != null)
550
- throw new Error("Bitcoin transaction already submitted for this swap!");
551
- //Ensure not expired
552
- if (this.getTimeoutTime() < Date.now()) {
553
- throw new Error("Swap address expired!");
554
- }
555
- const output0 = psbt.getOutput(0);
556
- if (this.amount != null && output0.amount !== this.amount)
557
- throw new Error("PSBT output amount invalid, expected: " + this.amount + " got: " + output0.amount);
558
- if (this.address != null) {
559
- const expectedOutputScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address);
560
- if (output0.script == null || !expectedOutputScript.equals(output0.script))
561
- throw new Error("PSBT output script invalid!");
562
- }
563
- if (!psbt.isFinal)
564
- psbt.finalize();
565
- const txId = await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
566
- await this._setSubmittedBitcoinTx(txId, psbt);
567
- return txId;
568
- }
569
- /**
570
- * @inheritDoc
571
- */
572
- async estimateBitcoinFee(_bitcoinWallet, feeRate) {
573
- if (this.address == null || this.amount == null)
574
- return null;
575
- const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
576
- const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
577
- if (txFee == null)
578
- return null;
579
- return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices);
580
- }
581
- /**
582
- * @inheritDoc
583
- */
584
- async sendBitcoinTransaction(wallet, feeRate) {
585
- if (this.address == null || this.amount == null)
586
- throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
587
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
588
- throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
589
- if (this.txId != null)
590
- throw new Error("Bitcoin transaction already submitted for this swap!");
591
- //Ensure not expired
592
- if (this.getTimeoutTime() < Date.now()) {
593
- throw new Error("Swap address expired!");
594
- }
595
- if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
596
- const txId = await wallet.sendTransaction(this.address, this.amount, feeRate);
597
- await this._setSubmittedBitcoinTx(txId);
598
- return txId;
599
- }
600
- else {
601
- const { psbt, psbtHex, psbtBase64, signInputs } = await this.getFundedPsbt(wallet, feeRate);
602
- const signedPsbt = await wallet.signPsbt({
603
- psbt, psbtHex, psbtBase64
604
- }, signInputs);
605
- return await this.submitPsbt(signedPsbt);
606
- }
607
- }
608
- //////////////////////////////
609
- //// Execution
610
- /**
611
- * Executes the swap with the provided bitcoin wallet,
612
- *
613
- * @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
614
- * quote was created, this is required for legacy swaps because the destination wallet needs to actively open
615
- * a bitcoin swap address to which the BTC is then sent, this means that the address also needs to have enough
616
- * native tokens to pay for gas on the destination network
617
- * @param wallet Bitcoin wallet to use to sign the bitcoin transaction, can also be null - then the execution waits
618
- * till a transaction is received from an external wallet
619
- * @param callbacks Callbacks to track the progress of the swap
620
- * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
621
- *
622
- * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
623
- * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
624
- */
625
- async execute(dstSigner, wallet, callbacks, options) {
626
- if (this._state === FromBTCSwapState.FAILED)
627
- throw new Error("Swap failed!");
628
- if (this._state === FromBTCSwapState.EXPIRED)
629
- throw new Error("Swap address expired!");
630
- if (this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED)
631
- throw new Error("Swap quote expired!");
632
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
633
- throw new Error("Swap already settled!");
634
- if (this._state === FromBTCSwapState.PR_CREATED) {
635
- await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
636
- }
637
- if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
638
- if (wallet != null) {
639
- const bitcoinPaymentSent = await this.getBitcoinPayment();
640
- if (bitcoinPaymentSent == null && this.txId == null) {
641
- //Send btc tx
642
- const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
643
- if (callbacks?.onSourceTransactionSent != null)
644
- callbacks.onSourceTransactionSent(txId);
645
- }
646
- }
647
- const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
648
- if (callbacks?.onSourceTransactionConfirmed != null)
649
- callbacks.onSourceTransactionConfirmed(txId);
650
- }
651
- // @ts-ignore
652
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
653
- return true;
654
- if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
655
- const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
656
- if (success && callbacks?.onSwapSettled != null)
657
- callbacks.onSwapSettled(this.getOutputTxId());
658
- return success;
659
- }
660
- throw new Error("Invalid state reached!");
661
- }
662
- /**
663
- * @internal
664
- */
665
- async _getExecutionStatus(options) {
666
- const state = this._state;
667
- const now = Date.now();
668
- const timeoutTime = this.getTimeoutTime();
669
- let confirmations;
670
- let bitcoinTxId;
671
- let destinationSetupStatus = "awaiting";
672
- let bitcoinPaymentStatus = "inactive";
673
- let destinationSettlementStatus = "inactive";
674
- let buildCurrentAction = async () => undefined;
675
- switch (state) {
676
- case FromBTCSwapState.PR_CREATED: {
677
- const quoteValid = await this._verifyQuoteValid();
678
- destinationSetupStatus = quoteValid && timeoutTime >= now ? "awaiting" : "soft_expired";
679
- if (quoteValid && timeoutTime >= now) {
680
- buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
681
- }
682
- break;
683
- }
684
- case FromBTCSwapState.QUOTE_SOFT_EXPIRED:
685
- destinationSetupStatus = "soft_expired";
686
- break;
687
- case FromBTCSwapState.QUOTE_EXPIRED:
688
- destinationSetupStatus = "expired";
689
- break;
690
- case FromBTCSwapState.CLAIM_COMMITED:
691
- case FromBTCSwapState.EXPIRED:
692
- case FromBTCSwapState.FAILED:
693
- const bitcoinPayment = this.address == null ? null : await this.getBitcoinPayment();
694
- bitcoinTxId = bitcoinPayment?.txId;
695
- let bitcoinConfirmationDelay;
696
- if (bitcoinPayment != null && bitcoinPayment.confirmations < bitcoinPayment.targetConfirmations) {
697
- const tx = await this.wrapper._btcRpc.getTransaction(bitcoinPayment.txId);
698
- const result = tx == null
699
- ? null
700
- : await this.wrapper._btcRpc.getConfirmationDelay(tx, bitcoinPayment.targetConfirmations);
701
- bitcoinConfirmationDelay = result ?? -1;
702
- }
703
- destinationSetupStatus = "completed";
704
- if (bitcoinPayment == null) {
705
- if (this.txId != null) {
706
- bitcoinPaymentStatus = state === FromBTCSwapState.FAILED ? "expired" : "received";
707
- if (state !== FromBTCSwapState.FAILED) {
708
- buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, -1, "Wait for bitcoin transaction to be picked up by the RPC and confirmed.");
709
- }
710
- }
711
- else {
712
- bitcoinPaymentStatus = "awaiting";
713
- if (state === FromBTCSwapState.EXPIRED)
714
- bitcoinPaymentStatus = "soft_expired";
715
- if (state === FromBTCSwapState.FAILED)
716
- bitcoinPaymentStatus = "expired";
717
- if (state === FromBTCSwapState.CLAIM_COMMITED && timeoutTime >= now &&
718
- this.address != null && this.amount != null) {
719
- buildCurrentAction = this._buildSendToAddressOrSignPsbtAction.bind(this);
720
- }
721
- }
722
- }
723
- else if (bitcoinPayment.confirmations >= bitcoinPayment.targetConfirmations) {
724
- bitcoinPaymentStatus = "confirmed";
725
- if (state !== FromBTCSwapState.FAILED) {
726
- buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
727
- }
728
- }
729
- else {
730
- bitcoinPaymentStatus = "received";
731
- confirmations = {
732
- current: bitcoinPayment.confirmations,
733
- target: bitcoinPayment.targetConfirmations,
734
- etaSeconds: bitcoinConfirmationDelay ?? -1
735
- };
736
- if (state !== FromBTCSwapState.FAILED) {
737
- buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
738
- }
739
- }
740
- destinationSettlementStatus = state === FromBTCSwapState.FAILED ? "expired" : "inactive";
741
- break;
742
- case FromBTCSwapState.BTC_TX_CONFIRMED:
743
- destinationSetupStatus = "completed";
744
- bitcoinPaymentStatus = "confirmed";
745
- if (this.btcTxConfirmedAt == null ||
746
- options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
747
- (now - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
748
- destinationSettlementStatus = "awaiting_manual";
749
- buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
750
- }
751
- else {
752
- destinationSettlementStatus = "awaiting_automatic";
753
- buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
754
- }
755
- break;
756
- case FromBTCSwapState.CLAIM_CLAIMED:
757
- destinationSetupStatus = "completed";
758
- bitcoinPaymentStatus = "confirmed";
759
- destinationSettlementStatus = "settled";
760
- break;
761
- }
762
- if (bitcoinPaymentStatus === "confirmed") {
763
- const requiredConfirmations = this.getRequiredConfirmationsCount();
764
- if (!Number.isNaN(requiredConfirmations)) {
765
- confirmations = {
766
- current: requiredConfirmations,
767
- target: requiredConfirmations,
768
- etaSeconds: 0
769
- };
770
- }
771
- }
772
- return {
773
- steps: [
774
- {
775
- type: "Setup",
776
- side: "destination",
777
- chain: this.chainIdentifier,
778
- title: "Open Bitcoin swap address",
779
- description: `Create the escrow on the ${this.chainIdentifier} side to open the Bitcoin swap address`,
780
- status: destinationSetupStatus,
781
- setupTxId: this._commitTxId
782
- },
783
- {
784
- type: "Payment",
785
- side: "source",
786
- chain: "BITCOIN",
787
- title: "Bitcoin payment",
788
- description: "Send Bitcoin to the swap address and wait for the transaction to confirm",
789
- status: bitcoinPaymentStatus,
790
- confirmations,
791
- initTxId: this.txId ?? bitcoinTxId,
792
- settleTxId: this.txId
793
- },
794
- {
795
- type: "Settlement",
796
- side: "destination",
797
- chain: this.chainIdentifier,
798
- title: "Destination settlement",
799
- description: `Wait for automatic settlement on the ${this.chainIdentifier} side, or settle manually if it takes too long`,
800
- status: destinationSettlementStatus,
801
- initTxId: this._commitTxId,
802
- settleTxId: this._claimTxId
803
- }
804
- ],
805
- buildCurrentAction,
806
- state
807
- };
808
- }
809
- /**
810
- * @inheritDoc
811
- * @internal
812
- */
813
- async _submitExecutionTransactions(txs, abortSignal, requiredStates, idempotent) {
814
- if (txs.length === 0)
815
- throw new Error("Need to submit at least 1 transaction in the array, submitted empty array of transactions!");
816
- if (idempotent) {
817
- // Handle idempotent calls
818
- let idempotencyTriggered = false;
819
- const txIds = [];
820
- for (let tx of txs) {
821
- let parsedTx;
822
- if (typeof (tx) === "string") {
823
- try {
824
- parsedTx = await this.wrapper._chain.deserializeSignedTx(tx);
825
- }
826
- catch (e) { }
827
- try {
828
- parsedTx = (0, BitcoinUtils_1.parsePsbtTransaction)(tx);
829
- }
830
- catch (e) { }
831
- }
832
- else {
833
- parsedTx = tx;
834
- }
835
- if (parsedTx == null) {
836
- this.logger.debug("_submitExecutionTransactions(): Failed to parse provided execution transaction: ", tx);
837
- continue;
838
- }
839
- if (parsedTx instanceof btc_signer_1.Transaction) {
840
- // Bitcoin tx
841
- const btcTx = await this.wrapper._btcRpc.parseTransaction(buffer_1.Buffer.from(parsedTx.toBytes(true)).toString("hex"));
842
- if (btcTx.txid === this.txId)
843
- idempotencyTriggered = true;
844
- txIds.push(btcTx.txid);
845
- }
846
- else {
847
- // SC tx
848
- if (this.wrapper._chain.getTxId != null) {
849
- const txId = await this.wrapper._chain.getTxId(parsedTx);
850
- if (this._commitTxId === txId || this._claimTxId === txId)
851
- idempotencyTriggered = true;
852
- txIds.push(txId);
853
- }
854
- }
855
- }
856
- if (idempotencyTriggered)
857
- return txIds;
858
- }
859
- if (requiredStates != null && !requiredStates.includes(this._state))
860
- throw new Error("Swap state has changed before transactions were submitted!");
861
- if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
862
- let psbt;
863
- if (txs.length !== 1)
864
- throw new Error("Need to submit exactly 1 signed PSBT!");
865
- if (typeof (txs[0]) !== "string" && !(txs[0] instanceof btc_signer_1.Transaction))
866
- throw new Error("Must submit a valid PSBT as hex/base64 string or `@scure/btc-signer` Transaction object!");
867
- psbt = txs[0];
868
- return [await this.submitPsbt(psbt)];
869
- }
870
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
871
- if (!await this._verifyQuoteValid())
872
- throw new Error("Quote is already expired!");
873
- if (this.getTimeoutTime() < Date.now())
874
- throw new Error("Swap address already expired or close to expiry!");
875
- const parsedTxs = [];
876
- for (let tx of txs) {
877
- parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
878
- }
879
- const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
880
- await this.waitTillCommited(abortSignal);
881
- return txIds;
882
- }
883
- if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
884
- const parsedTxs = [];
885
- for (let tx of txs) {
886
- parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
887
- }
888
- const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
889
- await this.waitTillClaimed(undefined, abortSignal);
890
- return txIds;
891
- }
892
- throw new Error("Invalid swap state for transaction submission!");
893
- }
894
- /**
895
- * @internal
896
- */
897
- async _buildSendToAddressOrSignPsbtAction(actionOptions) {
898
- if (this.address == null)
899
- throw new Error("Bitcoin swap address not known!");
900
- if (this.amount == null)
901
- throw new Error("Bitcoin swap amount not known!");
902
- if (actionOptions?.bitcoinWallet == null) {
903
- return {
904
- type: "SendToAddress",
905
- name: "Deposit on Bitcoin",
906
- description: "Send funds to the bitcoin swap address",
907
- chain: "BITCOIN",
908
- txs: [{
909
- type: "BITCOIN_ADDRESS",
910
- address: this.address,
911
- hyperlink: this._getHyperlink(),
912
- amount: (0, TokenAmount_1.toTokenAmount)(this.amount, Token_1.BitcoinTokens.BTC, this.wrapper._prices)
913
- }],
914
- waitForTransactions: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
915
- let btcTxId;
916
- const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction");
917
- try {
918
- return await this.waitForBitcoinTransaction((txId) => {
919
- btcTxId = txId;
920
- abortController.abort();
921
- }, pollIntervalSeconds, abortController.signal);
922
- }
923
- catch (e) {
924
- if (btcTxId != null)
925
- return btcTxId;
926
- throw e;
927
- }
928
- }
929
- };
930
- }
931
- return {
932
- type: "SignPSBT",
933
- name: "Deposit on Bitcoin",
934
- description: "Send funds to the bitcoin swap address",
935
- chain: "BITCOIN",
936
- txs: [{
937
- ...await this.getFundedPsbt(actionOptions.bitcoinWallet, actionOptions?.bitcoinFeeRate),
938
- type: "FUNDED_PSBT"
939
- }],
940
- submitPsbt: async (signedPsbt, idempotent) => {
941
- return this._submitExecutionTransactions(Array.isArray(signedPsbt) ? signedPsbt : [signedPsbt], undefined, [FromBTCSwapState.CLAIM_COMMITED], idempotent);
942
- }
943
- };
944
- }
945
- /**
946
- * @internal
947
- */
948
- async _buildWaitBitcoinConfirmationsAction(confirmationDelay, description) {
949
- return {
950
- type: "Wait",
951
- name: "Bitcoin confirmations",
952
- description: description ?? "Wait for bitcoin transaction to confirm",
953
- pollTimeSeconds: 10,
954
- expectedTimeSeconds: confirmationDelay === -1 ? -1 : Math.floor(confirmationDelay / 1000),
955
- wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal, btcConfirmationsCallback) => {
956
- const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction to confirm");
957
- await this.waitForBitcoinTransaction(btcConfirmationsCallback, pollIntervalSeconds, abortController.signal);
958
- }
959
- };
960
- }
961
- /**
962
- * @internal
963
- */
964
- async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds) {
965
- return {
966
- type: "Wait",
967
- name: "Automatic settlement",
968
- description: "Wait for automatic settlement by the watchtower",
969
- pollTimeSeconds: 5,
970
- expectedTimeSeconds: 10,
971
- wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
972
- await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, pollIntervalSeconds);
973
- }
974
- };
975
- }
976
- /**
977
- * @internal
978
- */
979
- async _buildInitSmartChainTxAction(actionOptions) {
980
- return {
981
- type: "SignSmartChainTransaction",
982
- name: "Initiate swap",
983
- description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
984
- chain: this.chainIdentifier,
985
- txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
986
- submitTransactions: async (txs, abortSignal, idempotent) => {
987
- return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.PR_CREATED, FromBTCSwapState.QUOTE_SOFT_EXPIRED], idempotent);
988
- },
989
- requiredSigner: this._getInitiator()
990
- };
991
- }
992
- /**
993
- * @inheritDoc
994
- * @internal
995
- */
996
- async _buildClaimSmartChainTxAction(actionOptions) {
997
- const signerAddress = await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
998
- return {
999
- type: "SignSmartChainTransaction",
1000
- name: "Settle manually",
1001
- description: "Manually settle (claim) the swap on the destination smart chain",
1002
- chain: this.chainIdentifier,
1003
- txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner)),
1004
- submitTransactions: async (txs, abortSignal, idempotent) => {
1005
- return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.BTC_TX_CONFIRMED], idempotent);
1006
- },
1007
- requiredSigner: signerAddress ?? this._getInitiator()
1008
- };
1009
- }
1010
- /**
1011
- * @inheritDoc
1012
- *
1013
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
1014
- * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
1015
- * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
1016
- * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
1017
- * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
1018
- * can use `skipChecks=true`)
1019
- * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
1020
- * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
1021
- * the bitcoin transaction is confirmed (defaults to 60 seconds)
1022
- */
1023
- async getExecutionAction(options) {
1024
- const executionStatus = await this._getExecutionStatus(options);
1025
- return executionStatus.buildCurrentAction(options);
1026
- }
1027
- /**
1028
- * @inheritDoc
1029
- */
1030
- async getExecutionStatus(options) {
1031
- const executionStatus = await this._getExecutionStatus(options);
1032
- return {
1033
- steps: executionStatus.steps,
1034
- currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1035
- stateInfo: this._getStateInfo(executionStatus.state)
1036
- };
1037
- }
1038
- /**
1039
- * @inheritDoc
1040
- */
1041
- async getExecutionSteps(options) {
1042
- return (await this._getExecutionStatus(options)).steps;
1043
- }
1044
- //////////////////////////////
1045
- //// Commit
1046
- /**
1047
- * @inheritDoc
1048
- *
1049
- * @throws {Error} If invalid signer is provided that doesn't match the swap data
1050
- */
1051
- async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
1052
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1053
- this.checkSigner(signer);
1054
- let txCount = 0;
1055
- const txs = await this.txsCommit(skipChecks);
1056
- const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1057
- txCount++;
1058
- if (onBeforeTxSent != null && txCount === txs.length)
1059
- onBeforeTxSent(txId);
1060
- return Promise.resolve();
1061
- });
1062
- this._commitTxId = result[result.length - 1];
1063
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCSwapState.QUOTE_EXPIRED) {
1064
- await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
1065
- }
1066
- return this._commitTxId;
1067
- }
1068
- /**
1069
- * @inheritDoc
1070
- */
1071
- async waitTillCommited(abortSignal) {
1072
- if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.CLAIM_CLAIMED)
1073
- return Promise.resolve();
1074
- if (this._state !== FromBTCSwapState.PR_CREATED && this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED)
1075
- throw new Error("Invalid state");
1076
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1077
- const result = await Promise.race([
1078
- this.watchdogWaitTillCommited(undefined, abortController.signal),
1079
- this.waitTillState(FromBTCSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
1080
- ]);
1081
- abortController.abort();
1082
- if (result === 0) {
1083
- this.logger.debug("waitTillCommited(): Resolved from state changed");
1084
- }
1085
- else if (result != null) {
1086
- this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1087
- }
1088
- if (result === null) {
1089
- this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
1090
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1091
- await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
1092
- }
1093
- return;
1094
- }
1095
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1096
- if (typeof (result) === "object" && result.getInitTxId != null && this._commitTxId == null)
1097
- this._commitTxId = await result.getInitTxId();
1098
- await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
1099
- }
1100
- }
1101
- //////////////////////////////
1102
- //// Claim
1103
- /**
1104
- * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
1105
- * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
1106
- * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
1107
- *
1108
- * @remarks
1109
- * Might also return transactions necessary to sync the bitcoin light client.
1110
- *
1111
- * @param _signer Address of the signer to create the claim transactions for
1112
- *
1113
- * @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
1114
- */
1115
- async txsClaim(_signer) {
1116
- let signer = undefined;
1117
- if (_signer != null) {
1118
- if (typeof (_signer) === "string") {
1119
- signer = _signer;
1120
- }
1121
- else if ((0, base_1.isAbstractSigner)(_signer)) {
1122
- signer = _signer;
1123
- }
1124
- else {
1125
- signer = await this.wrapper._chain.wrapSigner(_signer);
1126
- }
1127
- }
1128
- if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
1129
- throw new Error("Must be in BTC_TX_CONFIRMED state!");
1130
- if (this.txId == null || this.vout == null)
1131
- throw new Error("Bitcoin transaction ID not known!");
1132
- const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
1133
- if (tx == null)
1134
- throw new Error("Bitcoin transaction not found on the network!");
1135
- this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
1136
- if (this.requiredConfirmations == null)
1137
- throw new Error("Cannot create claim transaction, because required confirmations are not known and cannot be infered! This can happen after a swap is recovered.");
1138
- if (tx.blockhash == null || tx.confirmations == null || tx.blockheight == null || tx.confirmations < this.requiredConfirmations)
1139
- throw new Error("Bitcoin transaction not confirmed yet!");
1140
- return await this._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
1141
- blockhash: tx.blockhash,
1142
- confirmations: tx.confirmations,
1143
- txid: tx.txid,
1144
- hex: tx.hex,
1145
- height: tx.blockheight
1146
- }, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer(this._contractVersion), true);
1147
- }
1148
- /**
1149
- * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
1150
- * check so with isClaimable.
1151
- *
1152
- * @remarks
1153
- * Might also sync the bitcoin light client during the process.
1154
- *
1155
- * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
1156
- * @param abortSignal Abort signal
1157
- * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
1158
- *
1159
- * @returns Transaction ID of the settlement (claim) transaction on the destination smart chain
1160
- */
1161
- async claim(_signer, abortSignal, onBeforeTxSent) {
1162
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1163
- let txIds;
1164
- try {
1165
- let txCount = 0;
1166
- const txs = await this.txsClaim(signer);
1167
- txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1168
- txCount++;
1169
- if (onBeforeTxSent != null && txCount === txs.length)
1170
- onBeforeTxSent(txId);
1171
- return Promise.resolve();
1172
- });
1173
- }
1174
- catch (e) {
1175
- this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
1176
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED) {
1177
- this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
1178
- return this._claimTxId;
1179
- }
1180
- const status = await this._contract.getCommitStatus(this._getInitiator(), this._data);
1181
- if (status?.type === base_1.SwapCommitStateType.PAID) {
1182
- this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
1183
- if (this._claimTxId == null)
1184
- this._claimTxId = await status.getClaimTxId();
1185
- const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1186
- await this._setBitcoinTxId(txId);
1187
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1188
- return this._claimTxId;
1189
- }
1190
- throw e;
1191
- }
1192
- this._claimTxId = txIds[txIds.length - 1];
1193
- if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1194
- this._state === FromBTCSwapState.EXPIRED || this._state === FromBTCSwapState.FAILED) {
1195
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1196
- }
1197
- return txIds[txIds.length - 1];
1198
- }
1199
- /**
1200
- * @inheritDoc
1201
- *
1202
- * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
1203
- * @throws {Error} If the LP refunded sooner than we were able to claim
1204
- */
1205
- async waitTillClaimed(maxWaitTimeSeconds, abortSignal, pollIntervalSeconds) {
1206
- if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
1207
- return Promise.resolve(true);
1208
- if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
1209
- throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
1210
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1211
- let timedOut = false;
1212
- if (maxWaitTimeSeconds != null) {
1213
- const timeout = setTimeout(() => {
1214
- timedOut = true;
1215
- abortController.abort();
1216
- }, maxWaitTimeSeconds * 1000);
1217
- abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1218
- }
1219
- let res;
1220
- try {
1221
- res = await Promise.race([
1222
- this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1223
- this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
1224
- this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1),
1225
- ]);
1226
- abortController.abort();
1227
- }
1228
- catch (e) {
1229
- abortController.abort();
1230
- if (timedOut)
1231
- return false;
1232
- throw e;
1233
- }
1234
- if (res === 0) {
1235
- this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
1236
- return true;
1237
- }
1238
- if (res === 1) {
1239
- this.logger.debug("waitTillClaimed(): Resolved from state change (FAILED)");
1240
- throw new Error("Offerer refunded during claiming");
1241
- }
1242
- this.logger.debug("waitTillClaimed(): Resolved from watchdog");
1243
- if (res?.type === base_1.SwapCommitStateType.PAID) {
1244
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED) {
1245
- if (this._claimTxId == null)
1246
- this._claimTxId = await res.getClaimTxId();
1247
- const txId = buffer_1.Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
1248
- await this._setBitcoinTxId(txId);
1249
- await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1250
- }
1251
- }
1252
- if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
1253
- if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
1254
- this._state !== FromBTCSwapState.FAILED) {
1255
- if (res.getRefundTxId != null)
1256
- this._refundTxId = await res.getRefundTxId();
1257
- await this._saveAndEmit(FromBTCSwapState.FAILED);
1258
- }
1259
- throw new Error("Swap expired while waiting for claim!");
1260
- }
1261
- return true;
1262
- }
1263
- //////////////////////////////
1264
- //// Storage
1265
- /**
1266
- * @inheritDoc
1267
- */
1268
- serialize() {
1269
- return {
1270
- ...super.serialize(),
1271
- address: this.address,
1272
- amount: this.amount == null ? null : this.amount.toString(10),
1273
- requiredConfirmations: this.requiredConfirmations,
1274
- senderAddress: this.senderAddress,
1275
- txId: this.txId,
1276
- vout: this.vout,
1277
- btcTxConfirmedAt: this.btcTxConfirmedAt
1278
- };
1279
- }
1280
- //////////////////////////////
1281
- //// Swap ticks & sync
1282
- /**
1283
- * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1284
- * data
1285
- *
1286
- * @private
1287
- */
1288
- async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
1289
- if (this._state === FromBTCSwapState.PR_CREATED ||
1290
- this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1291
- this._state === FromBTCSwapState.CLAIM_COMMITED ||
1292
- this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1293
- this._state === FromBTCSwapState.EXPIRED) {
1294
- let quoteExpired = false;
1295
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1296
- quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
1297
- }
1298
- const status = commitStatus ?? await this._contract.getCommitStatus(this._getInitiator(), this._data);
1299
- if (status != null && await this._forciblySetOnchainState(status))
1300
- return true;
1301
- if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1302
- if (quoteExpired) {
1303
- this._state = FromBTCSwapState.QUOTE_EXPIRED;
1304
- return true;
1305
- }
1306
- }
1307
- }
1308
- return false;
1309
- }
1310
- /**
1311
- * @inheritDoc
1312
- * @internal
1313
- */
1314
- _shouldFetchOnchainState() {
1315
- return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1316
- this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1317
- this._state === FromBTCSwapState.EXPIRED;
1318
- }
1319
- /**
1320
- * @inheritDoc
1321
- * @internal
1322
- */
1323
- _shouldFetchExpiryStatus() {
1324
- return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1325
- }
1326
- /**
1327
- * @inheritDoc
1328
- * @internal
1329
- */
1330
- async _sync(save, quoteDefinitelyExpired, commitStatus) {
1331
- const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1332
- if (changed && save)
1333
- await this._saveAndEmit();
1334
- return changed;
1335
- }
1336
- /**
1337
- * @inheritDoc
1338
- * @internal
1339
- */
1340
- async _forciblySetOnchainState(status) {
1341
- switch (status.type) {
1342
- case base_1.SwapCommitStateType.PAID:
1343
- if (this._commitTxId == null && status.getInitTxId != null)
1344
- this._commitTxId = await status.getInitTxId();
1345
- if (this._claimTxId == null)
1346
- this._claimTxId = await status.getClaimTxId();
1347
- const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1348
- await this._setBitcoinTxId(txId);
1349
- this._state = FromBTCSwapState.CLAIM_CLAIMED;
1350
- return true;
1351
- case base_1.SwapCommitStateType.NOT_COMMITED:
1352
- let changed = false;
1353
- if (this._commitTxId == null && status.getInitTxId != null) {
1354
- this._commitTxId = await status.getInitTxId();
1355
- changed = true;
1356
- }
1357
- if (this._refundTxId == null && status.getRefundTxId) {
1358
- this._refundTxId = await status.getRefundTxId();
1359
- changed = true;
1360
- }
1361
- if (this._refundTxId != null) {
1362
- this._state = FromBTCSwapState.FAILED;
1363
- changed = true;
1364
- }
1365
- return changed;
1366
- case base_1.SwapCommitStateType.EXPIRED:
1367
- if (this._commitTxId == null && status.getInitTxId != null)
1368
- this._commitTxId = await status.getInitTxId();
1369
- if (this._refundTxId == null && status.getRefundTxId)
1370
- this._refundTxId = await status.getRefundTxId();
1371
- this._state = this._refundTxId == null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1372
- return true;
1373
- case base_1.SwapCommitStateType.COMMITED:
1374
- let save = false;
1375
- if (this._commitTxId == null && status.getInitTxId != null) {
1376
- this._commitTxId = await status.getInitTxId();
1377
- save = true;
1378
- }
1379
- if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.BTC_TX_CONFIRMED && this._state !== FromBTCSwapState.EXPIRED) {
1380
- this._state = FromBTCSwapState.CLAIM_COMMITED;
1381
- save = true;
1382
- }
1383
- if (this.address == null)
1384
- return save;
1385
- this.btcTxLastChecked = Date.now();
1386
- const res = await this.getBitcoinPayment();
1387
- if (res != null) {
1388
- if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1389
- if (res.inputAddresses != null)
1390
- this.senderAddress = res.inputAddresses[0];
1391
- this.txId = res.txId;
1392
- this.vout = res.vout;
1393
- save = true;
1394
- }
1395
- if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1396
- this.btcTxConfirmedAt ??= Date.now();
1397
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1398
- save = true;
1399
- }
1400
- }
1401
- return save;
1402
- }
1403
- return false;
1404
- }
1405
- /**
1406
- * @inheritDoc
1407
- * @internal
1408
- */
1409
- async _tick(save) {
1410
- switch (this._state) {
1411
- case FromBTCSwapState.PR_CREATED:
1412
- if (this.expiry < Date.now()) {
1413
- this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1414
- if (save)
1415
- await this._saveAndEmit();
1416
- return true;
1417
- }
1418
- break;
1419
- case FromBTCSwapState.CLAIM_COMMITED:
1420
- if (this.getTimeoutTime() < Date.now()) {
1421
- this._state = FromBTCSwapState.EXPIRED;
1422
- if (save)
1423
- await this._saveAndEmit();
1424
- return true;
1425
- }
1426
- case FromBTCSwapState.EXPIRED:
1427
- //Check if bitcoin payment was received at least every 2 minutes
1428
- if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120000) {
1429
- if (this.address != null)
1430
- try {
1431
- this.btcTxLastChecked = Date.now();
1432
- const res = await this.getBitcoinPayment();
1433
- if (res != null) {
1434
- let shouldSave = false;
1435
- if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1436
- this.txId = res.txId;
1437
- this.vout = res.vout;
1438
- if (res.inputAddresses != null)
1439
- this.senderAddress = res.inputAddresses[0];
1440
- shouldSave = true;
1441
- }
1442
- if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1443
- this.btcTxConfirmedAt ??= Date.now();
1444
- this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1445
- if (save)
1446
- await this._saveAndEmit();
1447
- shouldSave = true;
1448
- }
1449
- if (shouldSave && save)
1450
- await this._saveAndEmit();
1451
- return shouldSave;
1452
- }
1453
- }
1454
- catch (e) {
1455
- this.logger.warn("tickSwap(" + this.getIdentifierHashString() + "): ", e);
1456
- }
1457
- }
1458
- break;
1459
- }
1460
- return false;
1461
- }
1462
- }
1463
- exports.FromBTCSwap = FromBTCSwap;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FromBTCSwap = exports.isFromBTCSwapInit = exports.FromBTCSwapState = void 0;
4
+ const IFromBTCSelfInitSwap_1 = require("../IFromBTCSelfInitSwap");
5
+ const SwapType_1 = require("../../../../enums/SwapType");
6
+ const base_1 = require("@atomiqlabs/base");
7
+ const buffer_1 = require("buffer");
8
+ const Utils_1 = require("../../../../utils/Utils");
9
+ const BitcoinUtils_1 = require("../../../../utils/BitcoinUtils");
10
+ const IBitcoinWallet_1 = require("../../../../bitcoin/wallet/IBitcoinWallet");
11
+ const btc_signer_1 = require("@scure/btc-signer");
12
+ const SingleAddressBitcoinWallet_1 = require("../../../../bitcoin/wallet/SingleAddressBitcoinWallet");
13
+ const IEscrowSelfInitSwap_1 = require("../../IEscrowSelfInitSwap");
14
+ const TokenAmount_1 = require("../../../../types/TokenAmount");
15
+ const Token_1 = require("../../../../types/Token");
16
+ const Logger_1 = require("../../../../utils/Logger");
17
+ const BitcoinWalletUtils_1 = require("../../../../utils/BitcoinWalletUtils");
18
+ /**
19
+ * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
20
+ *
21
+ * @category Swaps/Legacy/Bitcoin → Smart chain
22
+ */
23
+ var FromBTCSwapState;
24
+ (function (FromBTCSwapState) {
25
+ /**
26
+ * Bitcoin swap address has expired and the intermediary (LP) has already refunded
27
+ * its funds. No BTC should be sent anymore!
28
+ */
29
+ FromBTCSwapState[FromBTCSwapState["FAILED"] = -4] = "FAILED";
30
+ /**
31
+ * Bitcoin swap address has expired, user should not send any BTC anymore! Though
32
+ * the intermediary (LP) hasn't refunded yet. So if there is a transaction already
33
+ * in-flight the swap might still succeed.
34
+ */
35
+ FromBTCSwapState[FromBTCSwapState["EXPIRED"] = -3] = "EXPIRED";
36
+ /**
37
+ * Swap has expired for good and there is no way how it can be executed anymore
38
+ */
39
+ FromBTCSwapState[FromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
40
+ /**
41
+ * A swap is almost expired, and it should be presented to the user as expired, though
42
+ * there is still a chance that it will be processed
43
+ */
44
+ FromBTCSwapState[FromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
45
+ /**
46
+ * Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
47
+ * to initiate it by creating the swap escrow on the destination smart chain
48
+ */
49
+ FromBTCSwapState[FromBTCSwapState["PR_CREATED"] = 0] = "PR_CREATED";
50
+ /**
51
+ * Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
52
+ * swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
53
+ * {@link FromBTCSwap.getHyperlink} functions.
54
+ */
55
+ FromBTCSwapState[FromBTCSwapState["CLAIM_COMMITED"] = 1] = "CLAIM_COMMITED";
56
+ /**
57
+ * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtowers
58
+ * using the {@link FromBTCSwap.waitTillClaimed} function or settle manually using the {@link FromBTCSwap.claim}
59
+ * or {@link FromBTCSwap.txsClaim} function.
60
+ */
61
+ FromBTCSwapState[FromBTCSwapState["BTC_TX_CONFIRMED"] = 2] = "BTC_TX_CONFIRMED";
62
+ /**
63
+ * Swap successfully settled and funds received on the destination chain
64
+ */
65
+ FromBTCSwapState[FromBTCSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
66
+ })(FromBTCSwapState = exports.FromBTCSwapState || (exports.FromBTCSwapState = {}));
67
+ const FromBTCSwapStateDescription = {
68
+ [FromBTCSwapState.FAILED]: "Bitcoin swap address has expired and the intermediary (LP) has already refunded its funds. No BTC should be sent anymore!",
69
+ [FromBTCSwapState.EXPIRED]: "Bitcoin swap address has expired, user should not send any BTC anymore! Though the intermediary (LP) hasn't refunded yet. So if there is a transaction already in-flight the swap might still succeed.",
70
+ [FromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
71
+ [FromBTCSwapState.QUOTE_SOFT_EXPIRED]: "The swap is expired, though there is still a chance that it will be processed",
72
+ [FromBTCSwapState.PR_CREATED]: "Swap quote was created, initiate it by creating the swap escrow on the destination smart chain",
73
+ [FromBTCSwapState.CLAIM_COMMITED]: "Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the Bitcoin swap address.",
74
+ [FromBTCSwapState.BTC_TX_CONFIRMED]: "Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower or settle manually.",
75
+ [FromBTCSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
76
+ };
77
+ function isFromBTCSwapInit(obj) {
78
+ return typeof (obj.data) === "object" &&
79
+ (obj.address == null || typeof (obj.address) === "string") &&
80
+ (obj.amount == null || typeof (obj.amount) === "bigint") &&
81
+ (obj.requiredConfirmations == null || typeof (obj.requiredConfirmations) === "number") &&
82
+ (0, IEscrowSelfInitSwap_1.isIEscrowSelfInitSwapInit)(obj);
83
+ }
84
+ exports.isFromBTCSwapInit = isFromBTCSwapInit;
85
+ /**
86
+ * Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
87
+ * of the swap escrow on the destination chain.
88
+ *
89
+ * @category Swaps/Legacy/Bitcoin → Smart chain
90
+ */
91
+ class FromBTCSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
92
+ constructor(wrapper, initOrObject) {
93
+ if (isFromBTCSwapInit(initOrObject) && initOrObject.url != null)
94
+ initOrObject.url += "/frombtc";
95
+ super(wrapper, initOrObject);
96
+ this.TYPE = SwapType_1.SwapType.FROM_BTC;
97
+ /**
98
+ * @internal
99
+ */
100
+ this.swapStateName = (state) => FromBTCSwapState[state];
101
+ /**
102
+ * @internal
103
+ */
104
+ this.swapStateDescription = FromBTCSwapStateDescription;
105
+ /**
106
+ * @internal
107
+ */
108
+ this.inputToken = Token_1.BitcoinTokens.BTC;
109
+ if (isFromBTCSwapInit(initOrObject)) {
110
+ this._state = FromBTCSwapState.PR_CREATED;
111
+ this._data = initOrObject.data;
112
+ this.feeRate = initOrObject.feeRate;
113
+ this.address = initOrObject.address;
114
+ this.amount = initOrObject.amount;
115
+ this.requiredConfirmations = initOrObject.requiredConfirmations;
116
+ }
117
+ else {
118
+ this.address = initOrObject.address;
119
+ this.amount = (0, Utils_1.toBigInt)(initOrObject.amount);
120
+ this.senderAddress = initOrObject.senderAddress;
121
+ this.txId = initOrObject.txId;
122
+ this.vout = initOrObject.vout;
123
+ this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
124
+ this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
125
+ }
126
+ this.tryRecomputeSwapPrice();
127
+ this.logger = (0, Logger_1.getLogger)("FromBTC(" + this.getIdentifierHashString() + "): ");
128
+ }
129
+ /**
130
+ * @inheritDoc
131
+ * @internal
132
+ */
133
+ getSwapData() {
134
+ return this._data;
135
+ }
136
+ /**
137
+ * @inheritDoc
138
+ * @internal
139
+ */
140
+ upgradeVersion() {
141
+ if (this.version == null) {
142
+ switch (this._state) {
143
+ case -2:
144
+ this._state = FromBTCSwapState.FAILED;
145
+ break;
146
+ case -1:
147
+ this._state = FromBTCSwapState.QUOTE_EXPIRED;
148
+ break;
149
+ case 0:
150
+ this._state = FromBTCSwapState.PR_CREATED;
151
+ break;
152
+ case 1:
153
+ this._state = FromBTCSwapState.CLAIM_COMMITED;
154
+ break;
155
+ case 2:
156
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
157
+ break;
158
+ case 3:
159
+ this._state = FromBTCSwapState.CLAIM_CLAIMED;
160
+ break;
161
+ }
162
+ this.version = 1;
163
+ }
164
+ }
165
+ //////////////////////////////
166
+ //// Getters & utils
167
+ /**
168
+ * Returns bitcoin address where the on-chain BTC should be sent to
169
+ */
170
+ getAddress() {
171
+ if (this._state === FromBTCSwapState.PR_CREATED)
172
+ throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
173
+ return this.address ?? "";
174
+ }
175
+ /**
176
+ * Unsafe bitcoin hyperlink getter, returns the address even before the swap is committed!
177
+ *
178
+ * @private
179
+ */
180
+ _getHyperlink() {
181
+ return this.address == null || this.amount == null ? "" : "bitcoin:" + this.address + "?amount=" + encodeURIComponent((Number(this.amount) / 100000000).toString(10));
182
+ }
183
+ /**
184
+ * @inheritDoc
185
+ */
186
+ getHyperlink() {
187
+ if (this._state === FromBTCSwapState.PR_CREATED)
188
+ throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
189
+ return this._getHyperlink();
190
+ }
191
+ /**
192
+ * @inheritDoc
193
+ */
194
+ getInputAddress() {
195
+ return this.senderAddress ?? null;
196
+ }
197
+ /**
198
+ * @inheritDoc
199
+ */
200
+ getInputTxId() {
201
+ return this.txId ?? null;
202
+ }
203
+ async _setSubmittedBitcoinTx(txId, psbt) {
204
+ let changed = false;
205
+ if (this.txId !== txId) {
206
+ this.txId = txId;
207
+ changed = true;
208
+ }
209
+ const submittedVout = this.address == null || this.amount == null || psbt == null
210
+ ? undefined
211
+ : (0, BitcoinUtils_1.getVoutIndex)(psbt, this.wrapper._options.bitcoinNetwork, this.address, this.amount);
212
+ if (submittedVout != null && this.vout !== submittedVout) {
213
+ this.vout = submittedVout;
214
+ changed = true;
215
+ }
216
+ const submittedSenderAddress = psbt == null
217
+ ? undefined
218
+ : (0, BitcoinUtils_1.getSenderAddress)(psbt, this.wrapper._options.bitcoinNetwork);
219
+ if (submittedSenderAddress != null && this.senderAddress !== submittedSenderAddress) {
220
+ this.senderAddress = submittedSenderAddress;
221
+ changed = true;
222
+ }
223
+ if (changed)
224
+ await this._saveAndEmit();
225
+ }
226
+ /**
227
+ * Returns timeout time (in UNIX milliseconds) when the on-chain address will expire and no funds should be sent
228
+ * to that address anymore
229
+ */
230
+ getTimeoutTime() {
231
+ return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
232
+ }
233
+ /**
234
+ * @inheritDoc
235
+ */
236
+ requiresAction() {
237
+ return this.isClaimable() || (this._state === FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime() > Date.now() && this.txId == null);
238
+ }
239
+ /**
240
+ * @inheritDoc
241
+ */
242
+ isFinished() {
243
+ return this._state === FromBTCSwapState.CLAIM_CLAIMED || this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.FAILED;
244
+ }
245
+ /**
246
+ * @inheritDoc
247
+ */
248
+ isClaimable() {
249
+ return this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
250
+ }
251
+ /**
252
+ * @inheritDoc
253
+ */
254
+ isSuccessful() {
255
+ return this._state === FromBTCSwapState.CLAIM_CLAIMED;
256
+ }
257
+ /**
258
+ * @inheritDoc
259
+ */
260
+ isFailed() {
261
+ return this._state === FromBTCSwapState.FAILED || this._state === FromBTCSwapState.EXPIRED;
262
+ }
263
+ /**
264
+ * @inheritDoc
265
+ */
266
+ isInProgress() {
267
+ return this._state === FromBTCSwapState.CLAIM_COMMITED ||
268
+ this._state === FromBTCSwapState.BTC_TX_CONFIRMED;
269
+ }
270
+ /**
271
+ * @inheritDoc
272
+ */
273
+ isQuoteExpired() {
274
+ return this._state === FromBTCSwapState.QUOTE_EXPIRED;
275
+ }
276
+ /**
277
+ * @inheritDoc
278
+ */
279
+ isQuoteSoftExpired() {
280
+ return this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
281
+ }
282
+ /**
283
+ * @inheritDoc
284
+ * @internal
285
+ */
286
+ canCommit(skipQuoteExpiryChecks) {
287
+ if (this._state !== FromBTCSwapState.PR_CREATED && (!skipQuoteExpiryChecks || this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED))
288
+ return false;
289
+ if (this.requiredConfirmations == null)
290
+ return false;
291
+ const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
292
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
293
+ return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
294
+ }
295
+ //////////////////////////////
296
+ //// Amounts & fees
297
+ /**
298
+ * @inheritDoc
299
+ */
300
+ getInputToken() {
301
+ return Token_1.BitcoinTokens.BTC;
302
+ }
303
+ /**
304
+ * @inheritDoc
305
+ */
306
+ getInput() {
307
+ return (0, TokenAmount_1.toTokenAmount)(this.amount ?? null, this.inputToken, this.wrapper._prices);
308
+ }
309
+ /**
310
+ * Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
311
+ * this amount is pre-funded by the user on the destination chain when the swap escrow
312
+ * is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
313
+ */
314
+ getClaimerBounty() {
315
+ return (0, TokenAmount_1.toTokenAmount)(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
316
+ }
317
+ //////////////////////////////
318
+ //// Bitcoin tx
319
+ /**
320
+ * If the required number of confirmations is not known, this function tries to infer it by looping through
321
+ * possible confirmation targets and comparing the claim hashes
322
+ *
323
+ * @param btcTx Bitcoin transaction
324
+ * @param vout Output index of the desired output in the bitcoin transaction
325
+ *
326
+ * @private
327
+ */
328
+ inferRequiredConfirmationsCount(btcTx, vout) {
329
+ const txOut = btcTx.outs[vout];
330
+ for (let i = 1; i <= 20; i++) {
331
+ const computedClaimHash = this._contract.getHashForOnchain(buffer_1.Buffer.from(txOut.scriptPubKey.hex, "hex"), BigInt(txOut.value), i);
332
+ if (computedClaimHash.toString("hex") === this._data.getClaimHash()) {
333
+ return i;
334
+ }
335
+ }
336
+ }
337
+ /**
338
+ * @inheritDoc
339
+ */
340
+ getRequiredConfirmationsCount() {
341
+ return this.requiredConfirmations ?? NaN;
342
+ }
343
+ /**
344
+ * Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
345
+ *
346
+ * @internal
347
+ */
348
+ async getBitcoinPayment() {
349
+ const txoHashHint = this._data.getTxoHashHint();
350
+ if (txoHashHint == null)
351
+ throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
352
+ if (this.address == null)
353
+ throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
354
+ const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, buffer_1.Buffer.from(txoHashHint, "hex"));
355
+ if (result == null)
356
+ return null;
357
+ if (this.requiredConfirmations == null) {
358
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
359
+ }
360
+ return {
361
+ inputAddresses: result.tx.inputAddresses,
362
+ txId: result.tx.txid,
363
+ vout: result.vout,
364
+ confirmations: result.tx.confirmations ?? 0,
365
+ targetConfirmations: this.getRequiredConfirmationsCount()
366
+ };
367
+ }
368
+ /**
369
+ * Used to set the txId of the bitcoin payment from the on-chain events listener
370
+ *
371
+ * @param txId Transaction ID that settled the swap on the smart chain
372
+ *
373
+ * @internal
374
+ */
375
+ async _setBitcoinTxId(txId) {
376
+ if (this.txId !== txId || this.address == null || this.vout == null || this.senderAddress == null || this.amount == null) {
377
+ const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
378
+ if (btcTx == null)
379
+ return;
380
+ const txoHashHint = this._data.getTxoHashHint();
381
+ if (txoHashHint != null) {
382
+ const expectedTxoHash = buffer_1.Buffer.from(txoHashHint, "hex");
383
+ const vout = btcTx.outs.findIndex(out => (0, Utils_1.getTxoHash)(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
384
+ if (vout !== -1) {
385
+ this.vout = vout;
386
+ //If amount or address are not known, parse them from the bitcoin tx
387
+ // this can happen if the swap is recovered from on-chain data and
388
+ // hence doesn't contain the address and amount data
389
+ if (this.amount == null)
390
+ this.amount = BigInt(btcTx.outs[vout].value);
391
+ if (this.address == null)
392
+ try {
393
+ this.address = (0, BitcoinUtils_1.fromOutputScript)(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
394
+ }
395
+ catch (e) {
396
+ this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
397
+ }
398
+ if (this.requiredConfirmations == null) {
399
+ this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
400
+ }
401
+ }
402
+ }
403
+ if (btcTx.inputAddresses != null) {
404
+ this.senderAddress = btcTx.inputAddresses[0];
405
+ }
406
+ }
407
+ this.txId = txId;
408
+ }
409
+ /**
410
+ * @inheritDoc
411
+ *
412
+ * @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
413
+ */
414
+ async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
415
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.EXPIRED)
416
+ throw new Error("Must be in COMMITED state!");
417
+ const txoHashHint = this._data.getTxoHashHint();
418
+ if (txoHashHint == null)
419
+ throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
420
+ if (this.address == null)
421
+ throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
422
+ let abortedDueToEnoughConfirmationsResult;
423
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
424
+ const result = await this.wrapper._btcRpc.waitForAddressTxo(this.address, buffer_1.Buffer.from(txoHashHint, "hex"), this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
425
+ (btcTx, vout, txEtaMs) => {
426
+ let requiredConfirmations = this.requiredConfirmations;
427
+ if (btcTx != null && vout != null && requiredConfirmations == null) {
428
+ requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
429
+ }
430
+ if (btcTx != null && (btcTx.txid !== this.txId ||
431
+ this.vout == null ||
432
+ this.senderAddress == null ||
433
+ (this.requiredConfirmations == null && requiredConfirmations != null))) {
434
+ this.txId = btcTx.txid;
435
+ this.vout = vout;
436
+ this.requiredConfirmations = requiredConfirmations;
437
+ if (btcTx.inputAddresses != null)
438
+ this.senderAddress = btcTx.inputAddresses[0];
439
+ this._saveAndEmit().catch(e => {
440
+ this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e);
441
+ });
442
+ }
443
+ //Abort the loop as soon as the transaction gets enough confirmations, this is required in case
444
+ // we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
445
+ // target from the prior block
446
+ if (btcTx?.confirmations != null && requiredConfirmations != null && requiredConfirmations <= btcTx.confirmations && vout != null) {
447
+ abortedDueToEnoughConfirmationsResult = {
448
+ tx: btcTx,
449
+ vout
450
+ };
451
+ abortController.abort();
452
+ return;
453
+ }
454
+ if (updateCallback != null)
455
+ updateCallback(btcTx?.txid, btcTx == null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
456
+ }, abortController.signal, checkIntervalSeconds).catch(e => {
457
+ //We catch the case when the loop was aborted due to the transaction getting enough confirmations
458
+ if (abortedDueToEnoughConfirmationsResult != null)
459
+ return abortedDueToEnoughConfirmationsResult;
460
+ throw e;
461
+ });
462
+ if (abortSignal != null)
463
+ abortSignal.throwIfAborted();
464
+ this.txId = result.tx.txid;
465
+ this.vout = result.vout;
466
+ if (result.tx.inputAddresses != null)
467
+ this.senderAddress = result.tx.inputAddresses[0];
468
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
469
+ this._state !== FromBTCSwapState.FAILED) {
470
+ this.btcTxConfirmedAt ??= Date.now();
471
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
472
+ }
473
+ await this._saveAndEmit();
474
+ return result.tx.txid;
475
+ }
476
+ /**
477
+ * Private getter of the funded PSBT that doesn't check current state
478
+ *
479
+ * @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
480
+ * @param feeRate Optional bitcoin fee rate in sats/vB
481
+ * @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
482
+ *
483
+ * @private
484
+ */
485
+ async _getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
486
+ if (this.address == null)
487
+ throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
488
+ let bitcoinWallet;
489
+ if ((0, IBitcoinWallet_1.isIBitcoinWallet)(_bitcoinWallet)) {
490
+ bitcoinWallet = _bitcoinWallet;
491
+ }
492
+ else {
493
+ bitcoinWallet = new SingleAddressBitcoinWallet_1.SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
494
+ }
495
+ //TODO: Maybe re-introduce fee rate check here if passed from the user
496
+ if (feeRate == null) {
497
+ feeRate = await bitcoinWallet.getFeeRate();
498
+ }
499
+ const basePsbt = new btc_signer_1.Transaction({
500
+ allowUnknownOutputs: true,
501
+ allowLegacyWitnessUtxo: true
502
+ });
503
+ basePsbt.addOutput({
504
+ amount: this.amount,
505
+ script: (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address)
506
+ });
507
+ if (additionalOutputs != null)
508
+ additionalOutputs.forEach(output => {
509
+ basePsbt.addOutput({
510
+ amount: output.amount,
511
+ script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, output.address)
512
+ });
513
+ });
514
+ const psbt = await bitcoinWallet.fundPsbt(basePsbt, feeRate);
515
+ //Sign every input
516
+ const signInputs = [];
517
+ for (let i = 0; i < psbt.inputsLength; i++) {
518
+ signInputs.push(i);
519
+ }
520
+ const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
521
+ return {
522
+ psbt,
523
+ psbtHex: serializedPsbt.toString("hex"),
524
+ psbtBase64: serializedPsbt.toString("base64"),
525
+ signInputs,
526
+ feeRate
527
+ };
528
+ }
529
+ /**
530
+ * @inheritDoc
531
+ */
532
+ getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
533
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
534
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
535
+ if (this.txId != null)
536
+ throw new Error("Bitcoin transaction already submitted for this swap!");
537
+ return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
538
+ }
539
+ /**
540
+ * @inheritDoc
541
+ *
542
+ * @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
543
+ * the swap bitcoin address already expired.
544
+ */
545
+ async submitPsbt(_psbt) {
546
+ const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
547
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
548
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
549
+ if (this.txId != null)
550
+ throw new Error("Bitcoin transaction already submitted for this swap!");
551
+ //Ensure not expired
552
+ if (this.getTimeoutTime() < Date.now()) {
553
+ throw new Error("Swap address expired!");
554
+ }
555
+ const output0 = psbt.getOutput(0);
556
+ if (this.amount != null && output0.amount !== this.amount)
557
+ throw new Error("PSBT output amount invalid, expected: " + this.amount + " got: " + output0.amount);
558
+ if (this.address != null) {
559
+ const expectedOutputScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper._options.bitcoinNetwork, this.address);
560
+ if (output0.script == null || !expectedOutputScript.equals(output0.script))
561
+ throw new Error("PSBT output script invalid!");
562
+ }
563
+ if (!psbt.isFinal)
564
+ psbt.finalize();
565
+ const txId = await this.wrapper._btcRpc.sendRawTransaction(buffer_1.Buffer.from(psbt.toBytes(true, true)).toString("hex"));
566
+ await this._setSubmittedBitcoinTx(txId, psbt);
567
+ return txId;
568
+ }
569
+ /**
570
+ * @inheritDoc
571
+ */
572
+ async estimateBitcoinFee(_bitcoinWallet, feeRate) {
573
+ if (this.address == null || this.amount == null)
574
+ return null;
575
+ const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
576
+ const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
577
+ if (txFee == null)
578
+ return null;
579
+ return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper._prices);
580
+ }
581
+ /**
582
+ * @inheritDoc
583
+ */
584
+ async sendBitcoinTransaction(wallet, feeRate) {
585
+ if (this.address == null || this.amount == null)
586
+ throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
587
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED)
588
+ throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
589
+ if (this.txId != null)
590
+ throw new Error("Bitcoin transaction already submitted for this swap!");
591
+ //Ensure not expired
592
+ if (this.getTimeoutTime() < Date.now()) {
593
+ throw new Error("Swap address expired!");
594
+ }
595
+ if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
596
+ const txId = await wallet.sendTransaction(this.address, this.amount, feeRate);
597
+ await this._setSubmittedBitcoinTx(txId);
598
+ return txId;
599
+ }
600
+ else {
601
+ const { psbt, psbtHex, psbtBase64, signInputs } = await this.getFundedPsbt(wallet, feeRate);
602
+ const signedPsbt = await wallet.signPsbt({
603
+ psbt, psbtHex, psbtBase64
604
+ }, signInputs);
605
+ return await this.submitPsbt(signedPsbt);
606
+ }
607
+ }
608
+ //////////////////////////////
609
+ //// Execution
610
+ /**
611
+ * Executes the swap with the provided bitcoin wallet,
612
+ *
613
+ * @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
614
+ * quote was created, this is required for legacy swaps because the destination wallet needs to actively open
615
+ * a bitcoin swap address to which the BTC is then sent, this means that the address also needs to have enough
616
+ * native tokens to pay for gas on the destination network
617
+ * @param wallet Bitcoin wallet to use to sign the bitcoin transaction, can also be null - then the execution waits
618
+ * till a transaction is received from an external wallet
619
+ * @param callbacks Callbacks to track the progress of the swap
620
+ * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
621
+ *
622
+ * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
623
+ * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
624
+ */
625
+ async execute(dstSigner, wallet, callbacks, options) {
626
+ if (this._state === FromBTCSwapState.FAILED)
627
+ throw new Error("Swap failed!");
628
+ if (this._state === FromBTCSwapState.EXPIRED)
629
+ throw new Error("Swap address expired!");
630
+ if (this._state === FromBTCSwapState.QUOTE_EXPIRED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED)
631
+ throw new Error("Swap quote expired!");
632
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
633
+ throw new Error("Swap already settled!");
634
+ if (this._state === FromBTCSwapState.PR_CREATED) {
635
+ await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
636
+ }
637
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
638
+ if (wallet != null) {
639
+ const bitcoinPaymentSent = await this.getBitcoinPayment();
640
+ if (bitcoinPaymentSent == null && this.txId == null) {
641
+ //Send btc tx
642
+ const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
643
+ if (callbacks?.onSourceTransactionSent != null)
644
+ callbacks.onSourceTransactionSent(txId);
645
+ }
646
+ }
647
+ const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
648
+ if (callbacks?.onSourceTransactionConfirmed != null)
649
+ callbacks.onSourceTransactionConfirmed(txId);
650
+ }
651
+ // @ts-ignore
652
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
653
+ return true;
654
+ if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
655
+ const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
656
+ if (success && callbacks?.onSwapSettled != null)
657
+ callbacks.onSwapSettled(this.getOutputTxId());
658
+ return success;
659
+ }
660
+ throw new Error("Invalid state reached!");
661
+ }
662
+ /**
663
+ * @internal
664
+ */
665
+ async _getExecutionStatus(options) {
666
+ const state = this._state;
667
+ const now = Date.now();
668
+ const timeoutTime = this.getTimeoutTime();
669
+ let confirmations;
670
+ let bitcoinTxId;
671
+ let destinationSetupStatus = "awaiting";
672
+ let bitcoinPaymentStatus = "inactive";
673
+ let destinationSettlementStatus = "inactive";
674
+ let buildCurrentAction = async () => undefined;
675
+ switch (state) {
676
+ case FromBTCSwapState.PR_CREATED: {
677
+ const quoteValid = await this._verifyQuoteValid();
678
+ destinationSetupStatus = quoteValid && timeoutTime >= now ? "awaiting" : "soft_expired";
679
+ if (quoteValid && timeoutTime >= now) {
680
+ buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
681
+ }
682
+ break;
683
+ }
684
+ case FromBTCSwapState.QUOTE_SOFT_EXPIRED:
685
+ destinationSetupStatus = "soft_expired";
686
+ break;
687
+ case FromBTCSwapState.QUOTE_EXPIRED:
688
+ destinationSetupStatus = "expired";
689
+ break;
690
+ case FromBTCSwapState.CLAIM_COMMITED:
691
+ case FromBTCSwapState.EXPIRED:
692
+ case FromBTCSwapState.FAILED:
693
+ const bitcoinPayment = this.address == null ? null : await this.getBitcoinPayment();
694
+ bitcoinTxId = bitcoinPayment?.txId;
695
+ let bitcoinConfirmationDelay;
696
+ if (bitcoinPayment != null && bitcoinPayment.confirmations < bitcoinPayment.targetConfirmations) {
697
+ const tx = await this.wrapper._btcRpc.getTransaction(bitcoinPayment.txId);
698
+ const result = tx == null
699
+ ? null
700
+ : await this.wrapper._btcRpc.getConfirmationDelay(tx, bitcoinPayment.targetConfirmations);
701
+ bitcoinConfirmationDelay = result ?? -1;
702
+ }
703
+ destinationSetupStatus = "completed";
704
+ if (bitcoinPayment == null) {
705
+ if (this.txId != null) {
706
+ bitcoinPaymentStatus = state === FromBTCSwapState.FAILED ? "expired" : "received";
707
+ if (state !== FromBTCSwapState.FAILED) {
708
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, -1, "Wait for bitcoin transaction to be picked up by the RPC and confirmed.");
709
+ }
710
+ }
711
+ else {
712
+ bitcoinPaymentStatus = "awaiting";
713
+ if (state === FromBTCSwapState.EXPIRED)
714
+ bitcoinPaymentStatus = "soft_expired";
715
+ if (state === FromBTCSwapState.FAILED)
716
+ bitcoinPaymentStatus = "expired";
717
+ if (state === FromBTCSwapState.CLAIM_COMMITED && timeoutTime >= now &&
718
+ this.address != null && this.amount != null) {
719
+ buildCurrentAction = this._buildSendToAddressOrSignPsbtAction.bind(this);
720
+ }
721
+ }
722
+ }
723
+ else if (bitcoinPayment.confirmations >= bitcoinPayment.targetConfirmations) {
724
+ bitcoinPaymentStatus = "confirmed";
725
+ if (state !== FromBTCSwapState.FAILED) {
726
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
727
+ }
728
+ }
729
+ else {
730
+ bitcoinPaymentStatus = "received";
731
+ confirmations = {
732
+ current: bitcoinPayment.confirmations,
733
+ target: bitcoinPayment.targetConfirmations,
734
+ etaSeconds: bitcoinConfirmationDelay ?? -1
735
+ };
736
+ if (state !== FromBTCSwapState.FAILED) {
737
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay ?? -1, undefined);
738
+ }
739
+ }
740
+ destinationSettlementStatus = state === FromBTCSwapState.FAILED ? "expired" : "inactive";
741
+ break;
742
+ case FromBTCSwapState.BTC_TX_CONFIRMED:
743
+ destinationSetupStatus = "completed";
744
+ bitcoinPaymentStatus = "confirmed";
745
+ if (this.btcTxConfirmedAt == null ||
746
+ options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
747
+ (now - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
748
+ destinationSettlementStatus = "awaiting_manual";
749
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
750
+ }
751
+ else {
752
+ destinationSettlementStatus = "awaiting_automatic";
753
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
754
+ }
755
+ break;
756
+ case FromBTCSwapState.CLAIM_CLAIMED:
757
+ destinationSetupStatus = "completed";
758
+ bitcoinPaymentStatus = "confirmed";
759
+ destinationSettlementStatus = "settled";
760
+ break;
761
+ }
762
+ if (bitcoinPaymentStatus === "confirmed") {
763
+ const requiredConfirmations = this.getRequiredConfirmationsCount();
764
+ if (!Number.isNaN(requiredConfirmations)) {
765
+ confirmations = {
766
+ current: requiredConfirmations,
767
+ target: requiredConfirmations,
768
+ etaSeconds: 0
769
+ };
770
+ }
771
+ }
772
+ return {
773
+ steps: [
774
+ {
775
+ type: "Setup",
776
+ side: "destination",
777
+ chain: this.chainIdentifier,
778
+ title: "Open Bitcoin swap address",
779
+ description: `Create the escrow on the ${this.chainIdentifier} side to open the Bitcoin swap address`,
780
+ status: destinationSetupStatus,
781
+ setupTxId: this._commitTxId
782
+ },
783
+ {
784
+ type: "Payment",
785
+ side: "source",
786
+ chain: "BITCOIN",
787
+ title: "Bitcoin payment",
788
+ description: "Send Bitcoin to the swap address and wait for the transaction to confirm",
789
+ status: bitcoinPaymentStatus,
790
+ confirmations,
791
+ initTxId: this.txId ?? bitcoinTxId,
792
+ settleTxId: this.txId
793
+ },
794
+ {
795
+ type: "Settlement",
796
+ side: "destination",
797
+ chain: this.chainIdentifier,
798
+ title: "Destination settlement",
799
+ description: `Wait for automatic settlement on the ${this.chainIdentifier} side, or settle manually if it takes too long`,
800
+ status: destinationSettlementStatus,
801
+ initTxId: this._commitTxId,
802
+ settleTxId: this._claimTxId
803
+ }
804
+ ],
805
+ buildCurrentAction,
806
+ state
807
+ };
808
+ }
809
+ /**
810
+ * @inheritDoc
811
+ * @internal
812
+ */
813
+ async _submitExecutionTransactions(txs, abortSignal, requiredStates, idempotent) {
814
+ if (txs.length === 0)
815
+ throw new Error("Need to submit at least 1 transaction in the array, submitted empty array of transactions!");
816
+ if (idempotent) {
817
+ // Handle idempotent calls
818
+ let idempotencyTriggered = false;
819
+ const txIds = [];
820
+ for (let tx of txs) {
821
+ let parsedTx;
822
+ if (typeof (tx) === "string") {
823
+ try {
824
+ parsedTx = await this.wrapper._chain.deserializeSignedTx(tx);
825
+ }
826
+ catch (e) { }
827
+ try {
828
+ parsedTx = (0, BitcoinUtils_1.parsePsbtTransaction)(tx);
829
+ }
830
+ catch (e) { }
831
+ }
832
+ else {
833
+ parsedTx = tx;
834
+ }
835
+ if (parsedTx == null) {
836
+ this.logger.debug("_submitExecutionTransactions(): Failed to parse provided execution transaction: ", tx);
837
+ continue;
838
+ }
839
+ if (parsedTx instanceof btc_signer_1.Transaction) {
840
+ // Bitcoin tx
841
+ const btcTx = await this.wrapper._btcRpc.parseTransaction(buffer_1.Buffer.from(parsedTx.toBytes(true)).toString("hex"));
842
+ if (btcTx.txid === this.txId)
843
+ idempotencyTriggered = true;
844
+ txIds.push(btcTx.txid);
845
+ }
846
+ else {
847
+ // SC tx
848
+ if (this.wrapper._chain.getTxId != null) {
849
+ const txId = await this.wrapper._chain.getTxId(parsedTx);
850
+ if (this._commitTxId === txId || this._claimTxId === txId)
851
+ idempotencyTriggered = true;
852
+ txIds.push(txId);
853
+ }
854
+ }
855
+ }
856
+ if (idempotencyTriggered)
857
+ return txIds;
858
+ }
859
+ if (requiredStates != null && !requiredStates.includes(this._state))
860
+ throw new Error("Swap state has changed before transactions were submitted!");
861
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED) {
862
+ let psbt;
863
+ if (txs.length !== 1)
864
+ throw new Error("Need to submit exactly 1 signed PSBT!");
865
+ if (typeof (txs[0]) !== "string" && !(txs[0] instanceof btc_signer_1.Transaction))
866
+ throw new Error("Must submit a valid PSBT as hex/base64 string or `@scure/btc-signer` Transaction object!");
867
+ psbt = txs[0];
868
+ return [await this.submitPsbt(psbt)];
869
+ }
870
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
871
+ if (!await this._verifyQuoteValid())
872
+ throw new Error("Quote is already expired!");
873
+ if (this.getTimeoutTime() < Date.now())
874
+ throw new Error("Swap address already expired or close to expiry!");
875
+ const parsedTxs = [];
876
+ for (let tx of txs) {
877
+ parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
878
+ }
879
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
880
+ await this.waitTillCommited(abortSignal);
881
+ return txIds;
882
+ }
883
+ if (this._state === FromBTCSwapState.BTC_TX_CONFIRMED) {
884
+ const parsedTxs = [];
885
+ for (let tx of txs) {
886
+ parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
887
+ }
888
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
889
+ await this.waitTillClaimed(undefined, abortSignal);
890
+ return txIds;
891
+ }
892
+ throw new Error("Invalid swap state for transaction submission!");
893
+ }
894
+ /**
895
+ * @internal
896
+ */
897
+ async _buildSendToAddressOrSignPsbtAction(actionOptions) {
898
+ if (this.address == null)
899
+ throw new Error("Bitcoin swap address not known!");
900
+ if (this.amount == null)
901
+ throw new Error("Bitcoin swap amount not known!");
902
+ if (actionOptions?.bitcoinWallet == null) {
903
+ return {
904
+ type: "SendToAddress",
905
+ name: "Deposit on Bitcoin",
906
+ description: "Send funds to the bitcoin swap address",
907
+ chain: "BITCOIN",
908
+ txs: [{
909
+ type: "BITCOIN_ADDRESS",
910
+ address: this.address,
911
+ hyperlink: this._getHyperlink(),
912
+ amount: (0, TokenAmount_1.toTokenAmount)(this.amount, Token_1.BitcoinTokens.BTC, this.wrapper._prices)
913
+ }],
914
+ waitForTransactions: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
915
+ let btcTxId;
916
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction");
917
+ try {
918
+ return await this.waitForBitcoinTransaction((txId) => {
919
+ btcTxId = txId;
920
+ abortController.abort();
921
+ }, pollIntervalSeconds, abortController.signal);
922
+ }
923
+ catch (e) {
924
+ if (btcTxId != null)
925
+ return btcTxId;
926
+ throw e;
927
+ }
928
+ }
929
+ };
930
+ }
931
+ return {
932
+ type: "SignPSBT",
933
+ name: "Deposit on Bitcoin",
934
+ description: "Send funds to the bitcoin swap address",
935
+ chain: "BITCOIN",
936
+ txs: [{
937
+ ...await this.getFundedPsbt(actionOptions.bitcoinWallet, actionOptions?.bitcoinFeeRate),
938
+ type: "FUNDED_PSBT"
939
+ }],
940
+ submitPsbt: async (signedPsbt, idempotent) => {
941
+ return this._submitExecutionTransactions(Array.isArray(signedPsbt) ? signedPsbt : [signedPsbt], undefined, [FromBTCSwapState.CLAIM_COMMITED], idempotent);
942
+ }
943
+ };
944
+ }
945
+ /**
946
+ * @internal
947
+ */
948
+ async _buildWaitBitcoinConfirmationsAction(confirmationDelay, description) {
949
+ return {
950
+ type: "Wait",
951
+ name: "Bitcoin confirmations",
952
+ description: description ?? "Wait for bitcoin transaction to confirm",
953
+ pollTimeSeconds: 10,
954
+ expectedTimeSeconds: confirmationDelay === -1 ? -1 : Math.floor(confirmationDelay / 1000),
955
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal, btcConfirmationsCallback) => {
956
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction to confirm");
957
+ await this.waitForBitcoinTransaction(btcConfirmationsCallback, pollIntervalSeconds, abortController.signal);
958
+ }
959
+ };
960
+ }
961
+ /**
962
+ * @internal
963
+ */
964
+ async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds) {
965
+ return {
966
+ type: "Wait",
967
+ name: "Automatic settlement",
968
+ description: "Wait for automatic settlement by the watchtower",
969
+ pollTimeSeconds: 5,
970
+ expectedTimeSeconds: 10,
971
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
972
+ await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, pollIntervalSeconds);
973
+ }
974
+ };
975
+ }
976
+ /**
977
+ * @internal
978
+ */
979
+ async _buildInitSmartChainTxAction(actionOptions) {
980
+ return {
981
+ type: "SignSmartChainTransaction",
982
+ name: "Initiate swap",
983
+ description: `Opens up the bitcoin swap address on the ${this.chainIdentifier} side`,
984
+ chain: this.chainIdentifier,
985
+ txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
986
+ submitTransactions: async (txs, abortSignal, idempotent) => {
987
+ return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.PR_CREATED, FromBTCSwapState.QUOTE_SOFT_EXPIRED], idempotent);
988
+ },
989
+ requiredSigner: this._getInitiator()
990
+ };
991
+ }
992
+ /**
993
+ * @inheritDoc
994
+ * @internal
995
+ */
996
+ async _buildClaimSmartChainTxAction(actionOptions) {
997
+ const signerAddress = await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
998
+ return {
999
+ type: "SignSmartChainTransaction",
1000
+ name: "Settle manually",
1001
+ description: "Manually settle (claim) the swap on the destination smart chain",
1002
+ chain: this.chainIdentifier,
1003
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner)),
1004
+ submitTransactions: async (txs, abortSignal, idempotent) => {
1005
+ return this._submitExecutionTransactions(txs, abortSignal, [FromBTCSwapState.BTC_TX_CONFIRMED], idempotent);
1006
+ },
1007
+ requiredSigner: signerAddress ?? this._getInitiator()
1008
+ };
1009
+ }
1010
+ /**
1011
+ * @inheritDoc
1012
+ *
1013
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
1014
+ * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
1015
+ * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
1016
+ * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
1017
+ * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
1018
+ * can use `skipChecks=true`)
1019
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
1020
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
1021
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
1022
+ */
1023
+ async getExecutionAction(options) {
1024
+ const executionStatus = await this._getExecutionStatus(options);
1025
+ return executionStatus.buildCurrentAction(options);
1026
+ }
1027
+ /**
1028
+ * @inheritDoc
1029
+ */
1030
+ async getExecutionStatus(options) {
1031
+ const executionStatus = await this._getExecutionStatus(options);
1032
+ return {
1033
+ steps: executionStatus.steps,
1034
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1035
+ stateInfo: this._getStateInfo(executionStatus.state)
1036
+ };
1037
+ }
1038
+ /**
1039
+ * @inheritDoc
1040
+ */
1041
+ async getExecutionSteps(options) {
1042
+ return (await this._getExecutionStatus(options)).steps;
1043
+ }
1044
+ //////////////////////////////
1045
+ //// Commit
1046
+ /**
1047
+ * @inheritDoc
1048
+ *
1049
+ * @throws {Error} If invalid signer is provided that doesn't match the swap data
1050
+ */
1051
+ async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
1052
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1053
+ this.checkSigner(signer);
1054
+ let txCount = 0;
1055
+ const txs = await this.txsCommit(skipChecks);
1056
+ const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1057
+ txCount++;
1058
+ if (onBeforeTxSent != null && txCount === txs.length)
1059
+ onBeforeTxSent(txId);
1060
+ return Promise.resolve();
1061
+ });
1062
+ this._commitTxId = result[result.length - 1];
1063
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCSwapState.QUOTE_EXPIRED) {
1064
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
1065
+ }
1066
+ return this._commitTxId;
1067
+ }
1068
+ /**
1069
+ * @inheritDoc
1070
+ */
1071
+ async waitTillCommited(abortSignal) {
1072
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.CLAIM_CLAIMED)
1073
+ return Promise.resolve();
1074
+ if (this._state !== FromBTCSwapState.PR_CREATED && this._state !== FromBTCSwapState.QUOTE_SOFT_EXPIRED)
1075
+ throw new Error("Invalid state");
1076
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1077
+ const result = await Promise.race([
1078
+ this.watchdogWaitTillCommited(undefined, abortController.signal),
1079
+ this.waitTillState(FromBTCSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
1080
+ ]);
1081
+ abortController.abort();
1082
+ if (result === 0) {
1083
+ this.logger.debug("waitTillCommited(): Resolved from state changed");
1084
+ }
1085
+ else if (result != null) {
1086
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1087
+ }
1088
+ if (result === null) {
1089
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
1090
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1091
+ await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
1092
+ }
1093
+ return;
1094
+ }
1095
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1096
+ if (typeof (result) === "object" && result.getInitTxId != null && this._commitTxId == null)
1097
+ this._commitTxId = await result.getInitTxId();
1098
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
1099
+ }
1100
+ }
1101
+ //////////////////////////////
1102
+ //// Claim
1103
+ /**
1104
+ * Returns transactions for settling (claiming) the swap if the swap requires manual settlement, you can check so
1105
+ * with isClaimable. After sending the transaction manually be sure to call the waitTillClaimed function to wait
1106
+ * till the claim transaction is observed, processed by the SDK and state of the swap properly updated.
1107
+ *
1108
+ * @remarks
1109
+ * Might also return transactions necessary to sync the bitcoin light client.
1110
+ *
1111
+ * @param _signer Address of the signer to create the claim transactions for
1112
+ *
1113
+ * @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
1114
+ */
1115
+ async txsClaim(_signer) {
1116
+ let signer = undefined;
1117
+ if (_signer != null) {
1118
+ if (typeof (_signer) === "string") {
1119
+ signer = _signer;
1120
+ }
1121
+ else if ((0, base_1.isAbstractSigner)(_signer)) {
1122
+ signer = _signer;
1123
+ }
1124
+ else {
1125
+ signer = await this.wrapper._chain.wrapSigner(_signer);
1126
+ }
1127
+ }
1128
+ if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
1129
+ throw new Error("Must be in BTC_TX_CONFIRMED state!");
1130
+ if (this.txId == null || this.vout == null)
1131
+ throw new Error("Bitcoin transaction ID not known!");
1132
+ const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
1133
+ if (tx == null)
1134
+ throw new Error("Bitcoin transaction not found on the network!");
1135
+ this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
1136
+ if (this.requiredConfirmations == null)
1137
+ throw new Error("Cannot create claim transaction, because required confirmations are not known and cannot be infered! This can happen after a swap is recovered.");
1138
+ if (tx.blockhash == null || tx.confirmations == null || tx.blockheight == null || tx.confirmations < this.requiredConfirmations)
1139
+ throw new Error("Bitcoin transaction not confirmed yet!");
1140
+ return await this._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
1141
+ blockhash: tx.blockhash,
1142
+ confirmations: tx.confirmations,
1143
+ txid: tx.txid,
1144
+ hex: tx.hex,
1145
+ height: tx.blockheight
1146
+ }, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer(this._contractVersion), true);
1147
+ }
1148
+ /**
1149
+ * Settles the swap by claiming the funds on the destination chain if the swap requires manual settlement, you can
1150
+ * check so with isClaimable.
1151
+ *
1152
+ * @remarks
1153
+ * Might also sync the bitcoin light client during the process.
1154
+ *
1155
+ * @param _signer Signer to use for signing the settlement transactions, can also be different to the recipient
1156
+ * @param abortSignal Abort signal
1157
+ * @param onBeforeTxSent Optional callback triggered before the claim transaction is broadcasted
1158
+ *
1159
+ * @returns Transaction ID of the settlement (claim) transaction on the destination smart chain
1160
+ */
1161
+ async claim(_signer, abortSignal, onBeforeTxSent) {
1162
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1163
+ let txIds;
1164
+ try {
1165
+ let txCount = 0;
1166
+ const txs = await this.txsClaim(signer);
1167
+ txIds = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1168
+ txCount++;
1169
+ if (onBeforeTxSent != null && txCount === txs.length)
1170
+ onBeforeTxSent(txId);
1171
+ return Promise.resolve();
1172
+ });
1173
+ }
1174
+ catch (e) {
1175
+ this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
1176
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED) {
1177
+ this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
1178
+ return this._claimTxId;
1179
+ }
1180
+ const status = await this._contract.getCommitStatus(this._getInitiator(), this._data);
1181
+ if (status?.type === base_1.SwapCommitStateType.PAID) {
1182
+ this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
1183
+ if (this._claimTxId == null)
1184
+ this._claimTxId = await status.getClaimTxId();
1185
+ const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1186
+ await this._setBitcoinTxId(txId);
1187
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1188
+ return this._claimTxId;
1189
+ }
1190
+ throw e;
1191
+ }
1192
+ this._claimTxId = txIds[txIds.length - 1];
1193
+ if (this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1194
+ this._state === FromBTCSwapState.EXPIRED || this._state === FromBTCSwapState.FAILED) {
1195
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1196
+ }
1197
+ return txIds[txIds.length - 1];
1198
+ }
1199
+ /**
1200
+ * @inheritDoc
1201
+ *
1202
+ * @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
1203
+ * @throws {Error} If the LP refunded sooner than we were able to claim
1204
+ */
1205
+ async waitTillClaimed(maxWaitTimeSeconds, abortSignal, pollIntervalSeconds) {
1206
+ if (this._state === FromBTCSwapState.CLAIM_CLAIMED)
1207
+ return Promise.resolve(true);
1208
+ if (this._state !== FromBTCSwapState.BTC_TX_CONFIRMED)
1209
+ throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
1210
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1211
+ let timedOut = false;
1212
+ if (maxWaitTimeSeconds != null) {
1213
+ const timeout = setTimeout(() => {
1214
+ timedOut = true;
1215
+ abortController.abort();
1216
+ }, maxWaitTimeSeconds * 1000);
1217
+ abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1218
+ }
1219
+ let res;
1220
+ try {
1221
+ res = await Promise.race([
1222
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1223
+ this.waitTillState(FromBTCSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
1224
+ this.waitTillState(FromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 1),
1225
+ ]);
1226
+ abortController.abort();
1227
+ }
1228
+ catch (e) {
1229
+ abortController.abort();
1230
+ if (timedOut)
1231
+ return false;
1232
+ throw e;
1233
+ }
1234
+ if (res === 0) {
1235
+ this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
1236
+ return true;
1237
+ }
1238
+ if (res === 1) {
1239
+ this.logger.debug("waitTillClaimed(): Resolved from state change (FAILED)");
1240
+ throw new Error("Offerer refunded during claiming");
1241
+ }
1242
+ this.logger.debug("waitTillClaimed(): Resolved from watchdog");
1243
+ if (res?.type === base_1.SwapCommitStateType.PAID) {
1244
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED) {
1245
+ if (this._claimTxId == null)
1246
+ this._claimTxId = await res.getClaimTxId();
1247
+ const txId = buffer_1.Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
1248
+ await this._setBitcoinTxId(txId);
1249
+ await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
1250
+ }
1251
+ }
1252
+ if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
1253
+ if (this._state !== FromBTCSwapState.CLAIM_CLAIMED &&
1254
+ this._state !== FromBTCSwapState.FAILED) {
1255
+ if (res.getRefundTxId != null)
1256
+ this._refundTxId = await res.getRefundTxId();
1257
+ await this._saveAndEmit(FromBTCSwapState.FAILED);
1258
+ }
1259
+ throw new Error("Swap expired while waiting for claim!");
1260
+ }
1261
+ return true;
1262
+ }
1263
+ //////////////////////////////
1264
+ //// Storage
1265
+ /**
1266
+ * @inheritDoc
1267
+ */
1268
+ serialize() {
1269
+ return {
1270
+ ...super.serialize(),
1271
+ address: this.address,
1272
+ amount: this.amount == null ? null : this.amount.toString(10),
1273
+ requiredConfirmations: this.requiredConfirmations,
1274
+ senderAddress: this.senderAddress,
1275
+ txId: this.txId,
1276
+ vout: this.vout,
1277
+ btcTxConfirmedAt: this.btcTxConfirmedAt
1278
+ };
1279
+ }
1280
+ //////////////////////////////
1281
+ //// Swap ticks & sync
1282
+ /**
1283
+ * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1284
+ * data
1285
+ *
1286
+ * @private
1287
+ */
1288
+ async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
1289
+ if (this._state === FromBTCSwapState.PR_CREATED ||
1290
+ this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1291
+ this._state === FromBTCSwapState.CLAIM_COMMITED ||
1292
+ this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1293
+ this._state === FromBTCSwapState.EXPIRED) {
1294
+ let quoteExpired = false;
1295
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1296
+ quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
1297
+ }
1298
+ const status = commitStatus ?? await this._contract.getCommitStatus(this._getInitiator(), this._data);
1299
+ if (status != null && await this._forciblySetOnchainState(status))
1300
+ return true;
1301
+ if (this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1302
+ if (quoteExpired) {
1303
+ this._state = FromBTCSwapState.QUOTE_EXPIRED;
1304
+ return true;
1305
+ }
1306
+ }
1307
+ }
1308
+ return false;
1309
+ }
1310
+ /**
1311
+ * @inheritDoc
1312
+ * @internal
1313
+ */
1314
+ _shouldFetchOnchainState() {
1315
+ return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
1316
+ this._state === FromBTCSwapState.CLAIM_COMMITED || this._state === FromBTCSwapState.BTC_TX_CONFIRMED ||
1317
+ this._state === FromBTCSwapState.EXPIRED;
1318
+ }
1319
+ /**
1320
+ * @inheritDoc
1321
+ * @internal
1322
+ */
1323
+ _shouldFetchExpiryStatus() {
1324
+ return this._state === FromBTCSwapState.PR_CREATED || this._state === FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1325
+ }
1326
+ /**
1327
+ * @inheritDoc
1328
+ * @internal
1329
+ */
1330
+ async _sync(save, quoteDefinitelyExpired, commitStatus) {
1331
+ const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1332
+ if (changed && save)
1333
+ await this._saveAndEmit();
1334
+ return changed;
1335
+ }
1336
+ /**
1337
+ * @inheritDoc
1338
+ * @internal
1339
+ */
1340
+ async _forciblySetOnchainState(status) {
1341
+ switch (status.type) {
1342
+ case base_1.SwapCommitStateType.PAID:
1343
+ if (this._commitTxId == null && status.getInitTxId != null)
1344
+ this._commitTxId = await status.getInitTxId();
1345
+ if (this._claimTxId == null)
1346
+ this._claimTxId = await status.getClaimTxId();
1347
+ const txId = buffer_1.Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
1348
+ await this._setBitcoinTxId(txId);
1349
+ this._state = FromBTCSwapState.CLAIM_CLAIMED;
1350
+ return true;
1351
+ case base_1.SwapCommitStateType.NOT_COMMITED:
1352
+ let changed = false;
1353
+ if (this._commitTxId == null && status.getInitTxId != null) {
1354
+ this._commitTxId = await status.getInitTxId();
1355
+ changed = true;
1356
+ }
1357
+ if (this._refundTxId == null && status.getRefundTxId) {
1358
+ this._refundTxId = await status.getRefundTxId();
1359
+ changed = true;
1360
+ }
1361
+ if (this._refundTxId != null) {
1362
+ this._state = FromBTCSwapState.FAILED;
1363
+ changed = true;
1364
+ }
1365
+ return changed;
1366
+ case base_1.SwapCommitStateType.EXPIRED:
1367
+ if (this._commitTxId == null && status.getInitTxId != null)
1368
+ this._commitTxId = await status.getInitTxId();
1369
+ if (this._refundTxId == null && status.getRefundTxId)
1370
+ this._refundTxId = await status.getRefundTxId();
1371
+ this._state = this._refundTxId == null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
1372
+ return true;
1373
+ case base_1.SwapCommitStateType.COMMITED:
1374
+ let save = false;
1375
+ if (this._commitTxId == null && status.getInitTxId != null) {
1376
+ this._commitTxId = await status.getInitTxId();
1377
+ save = true;
1378
+ }
1379
+ if (this._state !== FromBTCSwapState.CLAIM_COMMITED && this._state !== FromBTCSwapState.BTC_TX_CONFIRMED && this._state !== FromBTCSwapState.EXPIRED) {
1380
+ this._state = FromBTCSwapState.CLAIM_COMMITED;
1381
+ save = true;
1382
+ }
1383
+ if (this.address == null)
1384
+ return save;
1385
+ this.btcTxLastChecked = Date.now();
1386
+ const res = await this.getBitcoinPayment();
1387
+ if (res != null) {
1388
+ if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1389
+ if (res.inputAddresses != null)
1390
+ this.senderAddress = res.inputAddresses[0];
1391
+ this.txId = res.txId;
1392
+ this.vout = res.vout;
1393
+ save = true;
1394
+ }
1395
+ if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1396
+ this.btcTxConfirmedAt ??= Date.now();
1397
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1398
+ save = true;
1399
+ }
1400
+ }
1401
+ return save;
1402
+ }
1403
+ return false;
1404
+ }
1405
+ /**
1406
+ * @inheritDoc
1407
+ * @internal
1408
+ */
1409
+ async _tick(save) {
1410
+ switch (this._state) {
1411
+ case FromBTCSwapState.PR_CREATED:
1412
+ if (this.expiry < Date.now()) {
1413
+ this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
1414
+ if (save)
1415
+ await this._saveAndEmit();
1416
+ return true;
1417
+ }
1418
+ break;
1419
+ case FromBTCSwapState.CLAIM_COMMITED:
1420
+ if (this.getTimeoutTime() < Date.now()) {
1421
+ this._state = FromBTCSwapState.EXPIRED;
1422
+ if (save)
1423
+ await this._saveAndEmit();
1424
+ return true;
1425
+ }
1426
+ case FromBTCSwapState.EXPIRED:
1427
+ //Check if bitcoin payment was received at least every 2 minutes
1428
+ if (this.btcTxLastChecked == null || Date.now() - this.btcTxLastChecked > 120000) {
1429
+ if (this.address != null)
1430
+ try {
1431
+ this.btcTxLastChecked = Date.now();
1432
+ const res = await this.getBitcoinPayment();
1433
+ if (res != null) {
1434
+ let shouldSave = false;
1435
+ if (this.txId !== res.txId || this.vout !== res.vout || (res.inputAddresses != null && this.senderAddress == null)) {
1436
+ this.txId = res.txId;
1437
+ this.vout = res.vout;
1438
+ if (res.inputAddresses != null)
1439
+ this.senderAddress = res.inputAddresses[0];
1440
+ shouldSave = true;
1441
+ }
1442
+ if (this.requiredConfirmations != null && res.confirmations >= this.requiredConfirmations) {
1443
+ this.btcTxConfirmedAt ??= Date.now();
1444
+ this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1445
+ if (save)
1446
+ await this._saveAndEmit();
1447
+ shouldSave = true;
1448
+ }
1449
+ if (shouldSave && save)
1450
+ await this._saveAndEmit();
1451
+ return shouldSave;
1452
+ }
1453
+ }
1454
+ catch (e) {
1455
+ this.logger.warn("tickSwap(" + this.getIdentifierHashString() + "): ", e);
1456
+ }
1457
+ }
1458
+ break;
1459
+ }
1460
+ return false;
1461
+ }
1462
+ }
1463
+ exports.FromBTCSwap = FromBTCSwap;