@atomiqlabs/sdk 7.0.12 → 8.0.7

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