@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,1327 +1,1327 @@
1
- import {IToBTCDefinition, IToBTCWrapper} from "./IToBTCWrapper";
2
- import {
3
- ChainType,
4
- isAbstractSigner,
5
- SignatureData,
6
- SignatureVerificationError,
7
- SwapCommitState,
8
- SwapCommitStateType,
9
- SwapData
10
- } from "@atomiqlabs/base";
11
- import {
12
- IntermediaryAPI,
13
- RefundAuthorizationResponse,
14
- RefundAuthorizationResponseCodes
15
- } from "../../../intermediaries/apis/IntermediaryAPI";
16
- import {IntermediaryError} from "../../../errors/IntermediaryError";
17
- import {extendAbortController, toBigInt} from "../../../utils/Utils";
18
- import {Fee} from "../../../types/fees/Fee";
19
- import {IEscrowSelfInitSwap, IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../IEscrowSelfInitSwap";
20
- import {IRefundableSwap} from "../../IRefundableSwap";
21
- import {FeeType} from "../../../enums/FeeType";
22
- import {ppmToPercentage} from "../../../types/fees/PercentagePPM";
23
- import {TokenAmount, toTokenAmount} from "../../../types/TokenAmount";
24
- import {BtcToken, SCToken} from "../../../types/Token";
25
- import {timeoutPromise} from "../../../utils/TimeoutUtils";
26
- import {SwapExecutionActionSignSmartChainTx, SwapExecutionActionWait} from "../../../types/SwapExecutionAction";
27
- import {
28
- SwapExecutionStep,
29
- SwapExecutionStepPayment,
30
- SwapExecutionStepRefund,
31
- SwapExecutionStepSettlement
32
- } from "../../../types/SwapExecutionStep";
33
- import {SwapStateInfo} from "../../../types/SwapStateInfo";
34
-
35
- export type IToBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
36
- signatureData?: SignatureData,
37
- data: T,
38
- networkFee: bigint,
39
- networkFeeBtc: bigint
40
- };
41
-
42
- export function isIToBTCSwapInit<T extends SwapData>(obj: any): obj is IToBTCSwapInit<T> {
43
- return typeof(obj.networkFee) === "bigint" &&
44
- typeof(obj.networkFeeBtc) === "bigint" &&
45
- (obj.signatureData==null || (
46
- typeof(obj.signatureData) === 'object' &&
47
- typeof(obj.signatureData.prefix)==="string" &&
48
- typeof(obj.signatureData.timeout)==="string" &&
49
- typeof(obj.signatureData.signature)==="string"
50
- )) &&
51
- typeof(obj.data) === 'object' &&
52
- isIEscrowSelfInitSwapInit<T>(obj);
53
- }
54
-
55
- /**
56
- * State enum for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
57
- *
58
- * @category Swaps/Smart chain → Bitcoin
59
- */
60
- export enum ToBTCSwapState {
61
- /**
62
- * Intermediary (LP) was unable to process the swap and the funds were refunded on the
63
- * source chain
64
- */
65
- REFUNDED = -3,
66
- /**
67
- * Swap has expired for good and there is no way how it can be executed anymore
68
- */
69
- QUOTE_EXPIRED = -2,
70
- /**
71
- * A swap is almost expired, and it should be presented to the user as expired, though
72
- * there is still a chance that it will be processed
73
- */
74
- QUOTE_SOFT_EXPIRED = -1,
75
- /**
76
- * Swap was created, use the {@link IToBTCSwap.commit} or {@link IToBTCSwap.txsCommit} to
77
- * initiate it by creating the swap escrow on the source chain
78
- */
79
- CREATED = 0,
80
- /**
81
- * Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will
82
- * now process the swap. You can wait till that happens with the {@link IToBTCSwap.waitForPayment}
83
- * function.
84
- */
85
- COMMITED = 1,
86
- /**
87
- * The intermediary (LP) has processed the transaction and sent out the funds on the destination chain,
88
- * but hasn't yet settled the escrow on the source chain.
89
- */
90
- SOFT_CLAIMED = 2,
91
- /**
92
- * Swap was successfully settled by the intermediary (LP) on the source chain
93
- */
94
- CLAIMED = 3,
95
- /**
96
- * Intermediary (LP) was unable to process the swap and the swap escrow on the source chain
97
- * is refundable, call {@link IToBTCSwap.refund} or {@link IToBTCSwap.txsRefund} to refund
98
- */
99
- REFUNDABLE = 4
100
- }
101
-
102
- const ToBTCSwapStateDescription = {
103
- [ToBTCSwapState.REFUNDED]: "Intermediary (LP) was unable to process the swap and the funds were refunded on the source chain",
104
- [ToBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
105
- [ToBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
106
- [ToBTCSwapState.CREATED]: "Swap was created, initiate it by creating the swap escrow on the source chain",
107
- [ToBTCSwapState.COMMITED]: "Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will now process the swap.",
108
- [ToBTCSwapState.SOFT_CLAIMED]: "The intermediary (LP) has processed the transaction and sent out the funds on the destination chain, but hasn't yet settled the escrow on the source chain.",
109
- [ToBTCSwapState.CLAIMED]: "Swap was successfully settled by the intermediary (LP) on the source chain",
110
- [ToBTCSwapState.REFUNDABLE]: "Intermediary (LP) was unable to process the swap and the swap escrow on the source chain is refundable."
111
- };
112
-
113
- /**
114
- * Base class for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
115
- *
116
- * @category Swaps/Smart chain → Bitcoin
117
- */
118
- export abstract class IToBTCSwap<
119
- T extends ChainType = ChainType,
120
- D extends IToBTCDefinition<T, IToBTCWrapper<T, D>, IToBTCSwap<T, D>> = IToBTCDefinition<T, IToBTCWrapper<T, any>, IToBTCSwap<T, any>>,
121
- > extends IEscrowSelfInitSwap<T, D, ToBTCSwapState> implements IRefundableSwap<T, D, ToBTCSwapState> {
122
-
123
- /**
124
- * @internal
125
- */
126
- protected readonly swapStateDescription = ToBTCSwapStateDescription;
127
- /**
128
- * @internal
129
- */
130
- protected readonly swapStateName = (state: number) => ToBTCSwapState[state];
131
- /**
132
- * @internal
133
- */
134
- protected readonly abstract outputToken: BtcToken;
135
- /**
136
- * @internal
137
- */
138
- protected readonly networkFee: bigint;
139
- /**
140
- * @internal
141
- */
142
- protected networkFeeBtc: bigint;
143
-
144
- /**
145
- * @internal
146
- */
147
- readonly _data!: T["Data"];
148
-
149
- protected constructor(wrapper: D["Wrapper"], serializedObject: any);
150
- protected constructor(wrapper: D["Wrapper"], init: IToBTCSwapInit<T["Data"]>);
151
- protected constructor(
152
- wrapper: D["Wrapper"],
153
- initOrObject: IToBTCSwapInit<T["Data"]> | any
154
- ) {
155
- super(wrapper, initOrObject);
156
- if(isIToBTCSwapInit<T["Data"]>(initOrObject)) {
157
- this._state = ToBTCSwapState.CREATED;
158
- this.networkFee = initOrObject.networkFee;
159
- this.networkFeeBtc = initOrObject.networkFeeBtc;
160
- this._data = initOrObject.data;
161
- this.signatureData = initOrObject.signatureData;
162
- } else {
163
- this.networkFee = toBigInt(initOrObject.networkFee);
164
- this.networkFeeBtc = toBigInt(initOrObject.networkFeeBtc);
165
- }
166
- }
167
-
168
- /**
169
- * @inheritDoc
170
- * @internal
171
- */
172
- protected getSwapData(): T["Data"] {
173
- return this._data;
174
- }
175
-
176
- /**
177
- * @inheritDoc
178
- * @internal
179
- */
180
- protected upgradeVersion() {
181
- if(this.version == null) {
182
- switch(this._state) {
183
- case -2:
184
- this._state = ToBTCSwapState.REFUNDED
185
- break;
186
- case -1:
187
- this._state = ToBTCSwapState.QUOTE_EXPIRED
188
- break;
189
- case 0:
190
- this._state = ToBTCSwapState.CREATED
191
- break;
192
- case 1:
193
- this._state = ToBTCSwapState.COMMITED
194
- break;
195
- case 2:
196
- this._state = ToBTCSwapState.CLAIMED
197
- break;
198
- case 3:
199
- this._state = ToBTCSwapState.REFUNDABLE
200
- break;
201
- }
202
- this.version = 1;
203
- }
204
- }
205
-
206
- /**
207
- * @inheritDoc
208
- * @internal
209
- */
210
- protected tryRecomputeSwapPrice() {
211
- const output = this.getOutput();
212
- if(output.rawAmount!=null) {
213
- if(this.swapFeeBtc==null) {
214
- this.swapFeeBtc = this.swapFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
215
- }
216
- if(this.networkFeeBtc==null) {
217
- this.networkFeeBtc = this.networkFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
218
- }
219
- }
220
- super.tryRecomputeSwapPrice();
221
- }
222
-
223
- /**
224
- * Returns the payment hash identifier to be sent to the LP for getStatus and getRefund
225
- * @internal
226
- */
227
- protected getLpIdentifier(): string {
228
- return this.getClaimHash();
229
- }
230
-
231
- /**
232
- * Sets the payment result for the swap, optionally also checking it (checking that tx exist or swap secret is valid)
233
- *
234
- * @param result Result returned by the LP
235
- * @param check Whether to check the passed result
236
- * @returns true if check passed, false if check failed with a soft error (e.g. tx not yet found in the mempool)
237
- * @throws {IntermediaryError} When the data returned by the intermediary isn't valid
238
- *
239
- * @internal
240
- */
241
- abstract _setPaymentResult(result: {secret?: string, txId?: string}, check?: boolean): Promise<boolean>;
242
-
243
-
244
- //////////////////////////////
245
- //// Getters & utils
246
-
247
- /**
248
- * @inheritDoc
249
- */
250
- getInputAddress(): string | null {
251
- return this._getInitiator();
252
- }
253
-
254
- /**
255
- * @inheritDoc
256
- */
257
- getInputTxId(): string | null {
258
- return this._commitTxId ?? null;
259
- }
260
-
261
- /**
262
- * @inheritDoc
263
- */
264
- requiresAction(): boolean {
265
- return this.isRefundable();
266
- }
267
-
268
- /**
269
- * @inheritDoc
270
- */
271
- isFinished(): boolean {
272
- return this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.REFUNDED || this._state===ToBTCSwapState.QUOTE_EXPIRED;
273
- }
274
-
275
- /**
276
- * @inheritDoc
277
- */
278
- isRefundable(): boolean {
279
- return this._state===ToBTCSwapState.REFUNDABLE;
280
- }
281
-
282
- /**
283
- * @inheritDoc
284
- */
285
- isQuoteExpired(): boolean {
286
- return this._state===ToBTCSwapState.QUOTE_EXPIRED;
287
- }
288
-
289
- /**
290
- * @inheritDoc
291
- */
292
- isQuoteSoftExpired(): boolean {
293
- return this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
294
- }
295
-
296
- /**
297
- * @inheritDoc
298
- */
299
- isSuccessful(): boolean {
300
- return this._state===ToBTCSwapState.CLAIMED;
301
- }
302
-
303
- /**
304
- * @inheritDoc
305
- */
306
- isFailed(): boolean {
307
- return this._state===ToBTCSwapState.REFUNDED;
308
- }
309
-
310
- /**
311
- * @inheritDoc
312
- */
313
- isInProgress(): boolean {
314
- return this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED;
315
- }
316
-
317
- /**
318
- * Returns the time (in UNIX milliseconds) at which the swap expires and the user is able to unilaterally
319
- * refund it with the {@link refund} or {@link txsRefund} function.
320
- */
321
- getExpiry(): number {
322
- return Number(this._data.getExpiry())*1000;
323
- }
324
-
325
- /**
326
- * @inheritDoc
327
- * @internal
328
- */
329
- _getInitiator(): string {
330
- return this._data.getOfferer();
331
- }
332
-
333
-
334
- //////////////////////////////
335
- //// Amounts & fees
336
-
337
- /**
338
- * Returns the swap fee charged by the intermediary (LP) on this swap
339
- *
340
- * @internal
341
- */
342
- protected getSwapFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
343
- if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
344
-
345
- const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
346
- const output = this.getOutput();
347
- const swapFeePPM = output.rawAmount==null ? 0n : feeWithoutBaseFee * 1000000n / output.rawAmount;
348
-
349
- const amountInDstToken = toTokenAmount(
350
- this.swapFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
351
- );
352
- return {
353
- amountInSrcToken: toTokenAmount(this.swapFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
354
- amountInDstToken,
355
- currentUsdValue: amountInDstToken.currentUsdValue,
356
- usdValue: amountInDstToken.usdValue,
357
- pastUsdValue: amountInDstToken.pastUsdValue,
358
- composition: {
359
- base: toTokenAmount(this.pricingInfo.satsBaseFee, this.outputToken, this.wrapper._prices, this.pricingInfo),
360
- percentage: ppmToPercentage(swapFeePPM)
361
- }
362
- };
363
- }
364
-
365
- /**
366
- * Returns network fee for on the destination chain for the swap
367
- *
368
- * @internal
369
- */
370
- protected getNetworkFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
371
- const amountInDstToken = toTokenAmount(
372
- this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
373
- );
374
- return {
375
- amountInSrcToken: toTokenAmount(
376
- this.networkFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
377
- ),
378
- amountInDstToken,
379
- currentUsdValue: amountInDstToken.currentUsdValue,
380
- usdValue: amountInDstToken.usdValue,
381
- pastUsdValue: amountInDstToken.pastUsdValue
382
- };
383
- }
384
-
385
- /**
386
- * @inheritDoc
387
- */
388
- getFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
389
- const amountInDstToken = toTokenAmount(
390
- this.swapFeeBtc + this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
391
- );
392
- return {
393
- amountInSrcToken: toTokenAmount(
394
- this.swapFee + this.networkFee, this.wrapper._tokens[this._data.getToken()],
395
- this.wrapper._prices, this.pricingInfo
396
- ),
397
- amountInDstToken,
398
- currentUsdValue: amountInDstToken.currentUsdValue,
399
- usdValue: amountInDstToken.usdValue,
400
- pastUsdValue: amountInDstToken.pastUsdValue
401
- }
402
- }
403
-
404
- /**
405
- * @inheritDoc
406
- */
407
- getFeeBreakdown(): [
408
- {type: FeeType.SWAP, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
409
- {type: FeeType.NETWORK_OUTPUT, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
410
- ] {
411
- return [
412
- {
413
- type: FeeType.SWAP,
414
- fee: this.getSwapFee()
415
- },
416
- {
417
- type: FeeType.NETWORK_OUTPUT,
418
- fee: this.getNetworkFee()
419
- }
420
- ];
421
- }
422
-
423
- /**
424
- * @inheritDoc
425
- */
426
- getInputToken(): SCToken<T["ChainId"]> {
427
- return this.wrapper._tokens[this._data.getToken()];
428
- }
429
-
430
- /**
431
- * @inheritDoc
432
- */
433
- getInput(): TokenAmount<SCToken<T["ChainId"]>, true> {
434
- return toTokenAmount(
435
- this._data.getAmount(), this.wrapper._tokens[this._data.getToken()],
436
- this.wrapper._prices, this.pricingInfo
437
- );
438
- }
439
-
440
- /**
441
- * @inheritDoc
442
- */
443
- getInputWithoutFee(): TokenAmount<SCToken<T["ChainId"]>, true> {
444
- return toTokenAmount(
445
- this._data.getAmount() - (this.swapFee + this.networkFee),
446
- this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
447
- );
448
- }
449
-
450
- /**
451
- * Checks if the initiator/sender on the source chain has enough balance to go through with the swap
452
- */
453
- async hasEnoughBalance(): Promise<{
454
- enoughBalance: boolean,
455
- balance: TokenAmount<SCToken<T["ChainId"]>, true>,
456
- required: TokenAmount<SCToken<T["ChainId"]>, true>
457
- }> {
458
- const [balance, commitFee] = await Promise.all([
459
- this._contract.getBalance(this._getInitiator(), this._data.getToken(), false),
460
- this._data.getToken()===this.wrapper._chain.getNativeCurrencyAddress() ? this.getCommitFee() : Promise.resolve(null)
461
- ]);
462
- let required = this._data.getAmount();
463
- if(commitFee!=null) required = required + commitFee;
464
- return {
465
- enoughBalance: balance >= required,
466
- balance: toTokenAmount(balance, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
467
- required: toTokenAmount(required, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo)
468
- };
469
- }
470
-
471
- /**
472
- * Checks if the initiator/sender on the source chain has enough native token balance
473
- * to cover the transaction fee of initiating the swap
474
- */
475
- async hasEnoughForTxFees(): Promise<{
476
- enoughBalance: boolean,
477
- balance: TokenAmount<SCToken<T["ChainId"]>, true>,
478
- required: TokenAmount<SCToken<T["ChainId"]>, true>
479
- }> {
480
- const [balance, commitFee] = await Promise.all([
481
- this._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
482
- this.getCommitFee()
483
- ]);
484
- return {
485
- enoughBalance: balance >= commitFee,
486
- balance: toTokenAmount(balance, this.wrapper._getNativeToken(), this.wrapper._prices),
487
- required: toTokenAmount(commitFee, this.wrapper._getNativeToken(), this.wrapper._prices)
488
- };
489
- }
490
-
491
-
492
- //////////////////////////////
493
- //// Execution
494
-
495
- /**
496
- * Executes the swap with the provided smart chain wallet/signer
497
- *
498
- * @param signer Smart chain wallet/signer to use to sign the transaction on the source chain
499
- * @param callbacks Callbacks to track the progress of the swap
500
- * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
501
- *
502
- * @returns {boolean} Whether the swap was successfully processed by the LP, in case `false` is returned
503
- * the user can refund their funds back on the source chain by calling {@link refund}
504
- */
505
- async execute(
506
- signer: T["Signer"] | T["NativeSigner"],
507
- callbacks?: {
508
- onSourceTransactionSent?: (sourceTxId: string) => void,
509
- onSourceTransactionConfirmed?: (sourceTxId: string) => void,
510
- onSwapSettled?: (destinationTxId: string) => void
511
- },
512
- options?: {
513
- abortSignal?: AbortSignal,
514
- paymentCheckIntervalSeconds?: number,
515
- maxWaitTillSwapProcessedSeconds?: number
516
- }
517
- ): Promise<boolean> {
518
- if(this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Quote expired");
519
- if(this._state===ToBTCSwapState.REFUNDED) throw new Error("Swap already refunded");
520
- if(this._state===ToBTCSwapState.REFUNDABLE) throw new Error("Swap refundable, refund with swap.refund()");
521
- if(this._state===ToBTCSwapState.SOFT_CLAIMED || this._state===ToBTCSwapState.CLAIMED) throw new Error("Swap already settled!");
522
-
523
- if(this._state===ToBTCSwapState.CREATED) {
524
- const txId = await this.commit(signer, options?.abortSignal, false, callbacks?.onSourceTransactionSent);
525
- if(callbacks?.onSourceTransactionConfirmed!=null) callbacks.onSourceTransactionConfirmed(txId);
526
- }
527
-
528
- // @ts-ignore
529
- if(this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.SOFT_CLAIMED) return true;
530
-
531
- if(this._state===ToBTCSwapState.COMMITED) {
532
- const success = await this.waitForPayment(options?.maxWaitTillSwapProcessedSeconds ?? 120, options?.paymentCheckIntervalSeconds, options?.abortSignal);
533
- if(success) {
534
- if(callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
535
- return true;
536
- } else {
537
- return false;
538
- }
539
- }
540
-
541
- throw new Error("Unexpected state reached!");
542
- }
543
-
544
- /**
545
- * @internal
546
- */
547
- protected async _getExecutionStatus() {
548
- const state = this._state;
549
-
550
- let sourcePaymentStatus: SwapExecutionStepPayment<T["ChainId"]>["status"] = "inactive";
551
- let destinationPayoutStatus: SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">["status"] = "inactive";
552
- let refundStatus: SwapExecutionStepRefund<T["ChainId"]>["status"] = "inactive";
553
- let buildCurrentAction: (actionOptions?: {
554
- skipChecks?: boolean,
555
- refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
556
- }) => Promise<
557
- SwapExecutionActionSignSmartChainTx<T> |
558
- SwapExecutionActionWait<"LP"> |
559
- undefined
560
- > = async () => undefined;
561
-
562
- switch(state) {
563
- case ToBTCSwapState.CREATED:
564
- const quoteValid = await this._verifyQuoteValid();
565
- sourcePaymentStatus = quoteValid ? "awaiting" : "soft_expired";
566
- if(this.signatureData!=null && quoteValid) {
567
- buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
568
- }
569
- break;
570
- case ToBTCSwapState.QUOTE_SOFT_EXPIRED:
571
- sourcePaymentStatus = "soft_expired";
572
- break;
573
- case ToBTCSwapState.QUOTE_EXPIRED:
574
- sourcePaymentStatus = "expired";
575
- break;
576
- case ToBTCSwapState.COMMITED:
577
- sourcePaymentStatus = "confirmed";
578
- destinationPayoutStatus = "waiting_lp";
579
- buildCurrentAction = this._buildWaitLpAction.bind(this);
580
- break;
581
- case ToBTCSwapState.SOFT_CLAIMED:
582
- sourcePaymentStatus = "confirmed";
583
- destinationPayoutStatus = "soft_settled";
584
- break;
585
- case ToBTCSwapState.CLAIMED:
586
- sourcePaymentStatus = "confirmed";
587
- destinationPayoutStatus = "settled";
588
- break;
589
- case ToBTCSwapState.REFUNDABLE:
590
- sourcePaymentStatus = "confirmed";
591
- destinationPayoutStatus = "expired";
592
- refundStatus = "awaiting";
593
- buildCurrentAction = this._buildRefundSmartChainTxAction.bind(this);
594
- break;
595
- case ToBTCSwapState.REFUNDED:
596
- sourcePaymentStatus = "confirmed";
597
- destinationPayoutStatus = "expired";
598
- refundStatus = "refunded";
599
- break;
600
- }
601
-
602
- return {
603
- steps: [
604
- {
605
- type: "Payment",
606
- side: "source",
607
- chain: this.chainIdentifier,
608
- title: "Source payment",
609
- description: `Initiate the swap by funding the escrow on the ${this.chainIdentifier} side`,
610
- status: sourcePaymentStatus,
611
- initTxId: this._commitTxId,
612
- settleTxId: this._claimTxId
613
- },
614
- {
615
- type: "Settlement",
616
- side: "destination",
617
- chain: this.outputToken.chainId,
618
- title: "Destination payout",
619
- description: `Wait for the LP to process the swap and send the payout on the ${this.outputToken.chainId} side`,
620
- status: destinationPayoutStatus,
621
- initTxId: destinationPayoutStatus==="settled" || destinationPayoutStatus==="soft_settled" ? this.getOutputTxId() : undefined,
622
- settleTxId: destinationPayoutStatus==="settled" ? this.getOutputTxId() : undefined,
623
- },
624
- {
625
- type: "Refund",
626
- side: "source",
627
- chain: this.chainIdentifier,
628
- title: "Source refund",
629
- description: `Refund escrowed funds on the ${this.chainIdentifier} side, after LP failed to execute`,
630
- status: refundStatus,
631
- refundTxId: this._refundTxId
632
- }
633
- ] as [
634
- SwapExecutionStepPayment<T["ChainId"]>,
635
- SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
636
- SwapExecutionStepRefund<T["ChainId"]>,
637
- ],
638
- buildCurrentAction,
639
- state
640
- };
641
- }
642
-
643
- /**
644
- * @inheritDoc
645
- * @internal
646
- */
647
- async _submitExecutionTransactions(txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, requiredStates?: ToBTCSwapState[], idempotent?: boolean): Promise<string[]> {
648
- const parsedTxs: T["SignedTXType"][] = [];
649
- for(let tx of txs) {
650
- parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
651
- }
652
-
653
- if(idempotent) {
654
- // Handle idempotent calls
655
- if(this.wrapper._chain.getTxId!=null) {
656
- const txIds = await Promise.all(parsedTxs.map(tx => this.wrapper._chain.getTxId!(tx)));
657
- const foundTxId = txIds.find(txId => this._commitTxId===txId || this._refundTxId===txId);
658
- if(foundTxId!=null) return txIds;
659
- }
660
- }
661
-
662
- if(requiredStates!=null && !requiredStates.includes(this._state)) throw new Error("Swap state has changed before transactions were submitted!");
663
-
664
- if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
665
- if(!await this._verifyQuoteValid()) throw new Error("Quote is already expired!");
666
- const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
667
- await this.waitTillCommited(abortSignal);
668
- return txIds;
669
- }
670
-
671
- if(this._state===ToBTCSwapState.REFUNDABLE) {
672
- const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
673
- await this.waitTillRefunded(abortSignal);
674
- return txIds;
675
- }
676
-
677
- throw new Error("Invalid swap state for transaction submission!");
678
- }
679
-
680
- /**
681
- * @internal
682
- */
683
- private async _buildInitSmartChainTxAction(actionOptions?: {
684
- skipChecks?: boolean
685
- }): Promise<SwapExecutionActionSignSmartChainTx> {
686
- return {
687
- type: "SignSmartChainTransaction",
688
- name: "Initiate swap",
689
- description: `Initiates the swap by commiting the funds to the escrow on the ${this.chainIdentifier} side`,
690
- chain: this.chainIdentifier,
691
- txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
692
- submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
693
- return this._submitExecutionTransactions(txs, abortSignal, [ToBTCSwapState.CREATED, ToBTCSwapState.QUOTE_SOFT_EXPIRED], idempotent);
694
- },
695
- requiredSigner: this._getInitiator()
696
- } as SwapExecutionActionSignSmartChainTx;
697
- }
698
-
699
- /**
700
- * @internal
701
- */
702
- private async _buildWaitLpAction(): Promise<SwapExecutionActionWait<"LP">> {
703
- return {
704
- type: "Wait",
705
- name: "Awaiting LP payout",
706
- description: "Wait for the intermediary to process the swap and either send the payout or make the swap refundable",
707
- pollTimeSeconds: 5,
708
- expectedTimeSeconds: 10,
709
- wait: async (
710
- maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
711
- ) => {
712
- await this.waitForPayment(maxWaitTimeSeconds, pollIntervalSeconds, abortSignal);
713
- }
714
- } as SwapExecutionActionWait<"LP">;
715
- }
716
-
717
- /**
718
- * @internal
719
- */
720
- private async _buildRefundSmartChainTxAction(actionOptions?: {
721
- refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
722
- }): Promise<SwapExecutionActionSignSmartChainTx> {
723
- const signerAddress =
724
- await this.wrapper._getSignerAddress(actionOptions?.refundSmartChainSigner);
725
-
726
- return {
727
- type: "SignSmartChainTransaction",
728
- name: "Refund",
729
- description: "Refund the swap after it failed to execute",
730
- chain: this.chainIdentifier,
731
- txs: await this.prepareTransactions(this.txsRefund(actionOptions?.refundSmartChainSigner)),
732
- submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
733
- return this._submitExecutionTransactions(txs, abortSignal, [ToBTCSwapState.REFUNDABLE], idempotent);
734
- },
735
- requiredSigner: signerAddress ?? this._getInitiator()
736
- } as SwapExecutionActionSignSmartChainTx;
737
- }
738
-
739
- /**
740
- * @inheritDoc
741
- *
742
- * @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
743
- * (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
744
- * @param options.refundSmartChainSigner Optional smart chain signer to use when creating refunds transactions
745
- */
746
- async getExecutionAction(options?: {
747
- skipChecks?: boolean,
748
- refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
749
- }): Promise<
750
- SwapExecutionActionSignSmartChainTx<T> |
751
- SwapExecutionActionWait<"LP"> |
752
- undefined
753
- > {
754
- const executionStatus = await this._getExecutionStatus();
755
- return executionStatus.buildCurrentAction(options);
756
- }
757
-
758
- /**
759
- * @inheritDoc
760
- */
761
- async getExecutionStatus(options?: {
762
- skipBuildingAction?: boolean,
763
- skipChecks?: boolean,
764
- refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
765
- }): Promise<{
766
- steps: [
767
- SwapExecutionStepPayment<T["ChainId"]>,
768
- SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
769
- SwapExecutionStepRefund<T["ChainId"]>,
770
- ],
771
- currentAction:
772
- SwapExecutionActionSignSmartChainTx<T> |
773
- SwapExecutionActionWait<"LP"> |
774
- undefined,
775
- stateInfo: SwapStateInfo<ToBTCSwapState>
776
- }> {
777
- const executionStatus = await this._getExecutionStatus();
778
- return {
779
- steps: executionStatus.steps,
780
- currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
781
- stateInfo: this._getStateInfo(executionStatus.state)
782
- };
783
- }
784
-
785
- /**
786
- * @inheritDoc
787
- */
788
- async getExecutionSteps(): Promise<[
789
- SwapExecutionStepPayment<T["ChainId"]>,
790
- SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
791
- SwapExecutionStepRefund<T["ChainId"]>,
792
- ]> {
793
- return (await this._getExecutionStatus()).steps;
794
- }
795
-
796
- //////////////////////////////
797
- //// Commit
798
-
799
- /**
800
- * @inheritDoc
801
- *
802
- * @throws {Error} When in invalid state (not {@link ToBTCSwapState.CREATED})
803
- */
804
- async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
805
- if(this._state!==ToBTCSwapState.CREATED && (!skipChecks || this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED)) throw new Error("Must be in CREATED state!");
806
- if(this.signatureData==null) throw new Error("Init signature data not known, cannot commit!");
807
-
808
- if(!this.initiated) {
809
- this.initiated = true;
810
- await this._saveAndEmit();
811
- }
812
-
813
- return await this._contract.txsInit(
814
- this._getInitiator(), this._data, this.signatureData, skipChecks, this.feeRate
815
- ).catch(e => Promise.reject(e instanceof SignatureVerificationError ? new Error("Request timed out") : e));
816
- }
817
-
818
- /**
819
- * @inheritDoc
820
- *
821
- * @throws {Error} If invalid signer is provided that doesn't match the swap data
822
- */
823
- async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
824
- const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
825
- this.checkSigner(signer);
826
- const txs = await this.txsCommit(skipChecks);
827
- let txCount = 0;
828
- const result = await this.wrapper._chain.sendAndConfirm(
829
- signer, txs, true, abortSignal, false, (txId, rawTx) => {
830
- txCount++;
831
- if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
832
- return Promise.resolve();
833
- }
834
- );
835
-
836
- this._commitTxId = result[result.length-1];
837
- if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
838
- await this._saveAndEmit(ToBTCSwapState.COMMITED);
839
- }
840
- return this._commitTxId;
841
- }
842
-
843
- /**
844
- * @inheritDoc
845
- *
846
- * @throws {Error} If swap is not in the correct state (must be {@link ToBTCSwapState.CREATED})
847
- */
848
- async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
849
- if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.CLAIMED) return Promise.resolve();
850
- if(this._state!==ToBTCSwapState.CREATED && this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state (not CREATED)");
851
-
852
- const abortController = extendAbortController(abortSignal);
853
- let result: SwapCommitState | number | null;
854
- try {
855
- result = await Promise.race([
856
- this.watchdogWaitTillCommited(undefined, abortController.signal),
857
- this.waitTillState(ToBTCSwapState.COMMITED, "gte", abortController.signal).then(() => 0)
858
- ]);
859
- abortController.abort();
860
- } catch (e) {
861
- abortController.abort();
862
- throw e;
863
- }
864
-
865
- if(result===0) {
866
- this.logger.debug("waitTillCommited(): Resolved from state change");
867
- } else if(result!=null) {
868
- this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
869
- }
870
-
871
- if(result===null) {
872
- this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expiry");
873
- if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED) {
874
- await this._saveAndEmit(ToBTCSwapState.QUOTE_EXPIRED);
875
- }
876
- throw new Error("Quote expired while waiting for transaction confirmation!");
877
- }
878
-
879
- if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
880
- if(typeof(result)==="object" && (result as any).getInitTxId!=null && this._commitTxId==null)
881
- this._commitTxId = await (result as any).getInitTxId();
882
- await this._saveAndEmit(ToBTCSwapState.COMMITED);
883
- }
884
- }
885
-
886
-
887
- //////////////////////////////
888
- //// Payment
889
-
890
- /**
891
- * Waits till the swap is processed by the intermediary (LP)
892
- *
893
- * @param checkIntervalSeconds How often to poll the intermediary for status (5 seconds default)
894
- * @param abortSignal Abort signal
895
- * @internal
896
- */
897
- protected async waitTillIntermediarySwapProcessed(
898
- checkIntervalSeconds?: number,
899
- abortSignal?: AbortSignal
900
- ): Promise<RefundAuthorizationResponse> {
901
- if(this.url==null) throw new Error("LP URL not specified!");
902
- checkIntervalSeconds ??= 5;
903
- let resp: RefundAuthorizationResponse = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
904
- while(!abortSignal?.aborted && (
905
- resp.code===RefundAuthorizationResponseCodes.PENDING || resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
906
- )) {
907
- resp = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
908
- if(resp.code===RefundAuthorizationResponseCodes.PAID) {
909
- const validResponse = await this._setPaymentResult(resp.data, true);
910
- if(validResponse) {
911
- if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE) {
912
- await this._saveAndEmit(ToBTCSwapState.SOFT_CLAIMED);
913
- }
914
- } else {
915
- resp = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
916
- }
917
- }
918
- if(
919
- resp.code===RefundAuthorizationResponseCodes.PENDING ||
920
- resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
921
- ) await timeoutPromise(checkIntervalSeconds*1000, abortSignal);
922
- }
923
- return resp;
924
- }
925
-
926
- /**
927
- * Checks whether the swap was already processed by the LP and is either successful (requires proof which is
928
- * either a HTLC pre-image for LN swaps or valid txId for on-chain swap) or failed and we can cooperatively
929
- * refund.
930
- *
931
- * @param save whether to save the data
932
- * @returns `true` if swap is processed, `false` if the swap is still ongoing
933
- *
934
- * @internal
935
- */
936
- protected async checkIntermediarySwapProcessed(save: boolean = true): Promise<boolean> {
937
- if(this._state===ToBTCSwapState.CREATED || this._state==ToBTCSwapState.QUOTE_EXPIRED || this.url==null) return false;
938
- if(this.isFinished() || this.isRefundable()) return true;
939
- //Check if that maybe already concluded according to the LP
940
- const resp = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
941
- switch(resp.code) {
942
- case RefundAuthorizationResponseCodes.PAID:
943
- const processed = await this._setPaymentResult(resp.data, true);
944
- if(processed) {
945
- this._state = ToBTCSwapState.SOFT_CLAIMED;
946
- if(save) await this._saveAndEmit();
947
- }
948
- return processed;
949
- case RefundAuthorizationResponseCodes.REFUND_DATA:
950
- await this._contract.isValidRefundAuthorization(this._data, resp.data);
951
- this._state = ToBTCSwapState.REFUNDABLE;
952
- if(save) await this._saveAndEmit();
953
- return true;
954
- default:
955
- return false;
956
- }
957
- }
958
-
959
- /**
960
- * A blocking promise resolving when swap was concluded by the intermediary (LP),
961
- * rejecting in case of failure
962
- *
963
- * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled, an error is thrown if the
964
- * swap is taking too long to claim
965
- * @param checkIntervalSeconds How often to poll the intermediary for answer
966
- * @param abortSignal Abort signal
967
- * @returns `true` if swap was successful, `false` if swap failed and we can refund
968
- *
969
- * @throws {IntermediaryError} If a swap is determined expired by the intermediary, but it is actually still valid
970
- * @throws {SignatureVerificationError} If the swap should be cooperatively refundable but the intermediary returned
971
- * invalid refund signature
972
- * @throws {Error} When swap expires or if the swap has invalid state (must be {@link ToBTCSwapState.COMMITED})
973
- */
974
- async waitForPayment(maxWaitTimeSeconds?: number, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
975
- if(this._state===ToBTCSwapState.CLAIMED) return Promise.resolve(true);
976
- if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.SOFT_CLAIMED) throw new Error("Invalid state (not COMMITED)");
977
-
978
- const abortController = extendAbortController(abortSignal);
979
-
980
- let timedOut: boolean = false;
981
- if(maxWaitTimeSeconds!=null) {
982
- const timeout = setTimeout(() => {
983
- timedOut = true;
984
- abortController.abort();
985
- }, maxWaitTimeSeconds * 1000);
986
- abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
987
- }
988
-
989
- let result: void | RefundAuthorizationResponse;
990
- try {
991
- result = await Promise.race([
992
- this.waitTillState(ToBTCSwapState.CLAIMED, "gte", abortController.signal),
993
- this.waitTillIntermediarySwapProcessed(checkIntervalSeconds, abortController.signal)
994
- ]);
995
- abortController.abort();
996
- } catch (e) {
997
- abortController.abort();
998
- if(timedOut) {
999
- throw new Error("Timed out while waiting for LP to process the swap, the LP might be unresponsive or offline!" +
1000
- ` Please check later or wait till ${new Date(Number(this._data.getExpiry())*1000).toLocaleString()} to refund unilaterally!`);
1001
- }
1002
- throw e;
1003
- }
1004
-
1005
- if(typeof result !== "object") {
1006
- if((this._state as ToBTCSwapState)===ToBTCSwapState.REFUNDABLE) throw new Error("Swap expired");
1007
- this.logger.debug("waitTillRefunded(): Resolved from state change");
1008
- return true;
1009
- }
1010
- this.logger.debug("waitTillRefunded(): Resolved from intermediary response");
1011
-
1012
- switch(result.code) {
1013
- case RefundAuthorizationResponseCodes.PAID:
1014
- return true;
1015
- case RefundAuthorizationResponseCodes.REFUND_DATA:
1016
- const resultData = result.data;
1017
- await this._contract.isValidRefundAuthorization(
1018
- this._data,
1019
- resultData
1020
- );
1021
- await this._saveAndEmit(ToBTCSwapState.REFUNDABLE);
1022
- return false;
1023
- case RefundAuthorizationResponseCodes.EXPIRED:
1024
- if(await this._contract.isExpired(this._getInitiator(), this._data)) throw new Error("Swap expired");
1025
- throw new IntermediaryError("Swap expired");
1026
- case RefundAuthorizationResponseCodes.NOT_FOUND:
1027
- if((this._state as ToBTCSwapState)===ToBTCSwapState.CLAIMED) return true;
1028
- throw new Error("LP swap not found");
1029
- }
1030
-
1031
- throw new Error("Invalid response code returned by the LP");
1032
- }
1033
-
1034
-
1035
- //////////////////////////////
1036
- //// Refund
1037
-
1038
- /**
1039
- * Get the estimated smart chain transaction fee of the refund transaction
1040
- */
1041
- async getRefundNetworkFee(): Promise<TokenAmount<SCToken<T["ChainId"]>, true>> {
1042
- const swapContract: T["Contract"] = this._contract;
1043
- return toTokenAmount(
1044
- await swapContract.getRefundFee(this._getInitiator(), this._data),
1045
- this.wrapper._getNativeToken(),
1046
- this.wrapper._prices
1047
- );
1048
- }
1049
-
1050
- /**
1051
- * @inheritDoc
1052
- *
1053
- * @throws {IntermediaryError} If intermediary returns invalid response in case cooperative refund should be used
1054
- * @throws {SignatureVerificationError} If intermediary returned invalid cooperative refund signature
1055
- * @throws {Error} When state is not refundable
1056
- */
1057
- async txsRefund(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
1058
- if(!this.isRefundable()) throw new Error("Must be in REFUNDABLE state or expired!");
1059
-
1060
- let signer: string;
1061
- if(_signer!=null) {
1062
- if (typeof (_signer) === "string") {
1063
- signer = _signer;
1064
- } else if (isAbstractSigner(_signer)) {
1065
- signer = _signer.getAddress();
1066
- } else {
1067
- signer = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
1068
- }
1069
- } else {
1070
- signer = this._getInitiator();
1071
- }
1072
-
1073
- if(await this._contract.isExpired(this._getInitiator(), this._data)) {
1074
- return await this._contract.txsRefund(signer, this._data, true, true);
1075
- } else {
1076
- if(this.url==null) throw new Error("LP URL not known, cannot get cooperative refund message, wait till expiry to refund!");
1077
- const res = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
1078
- if(res.code===RefundAuthorizationResponseCodes.REFUND_DATA) {
1079
- return await this._contract.txsRefundWithAuthorization(
1080
- signer,
1081
- this._data,
1082
- res.data,
1083
- true,
1084
- true
1085
- );
1086
- }
1087
- throw new IntermediaryError("Invalid intermediary cooperative message returned");
1088
- }
1089
- }
1090
-
1091
- /**
1092
- * @inheritDoc
1093
- *
1094
- * @throws {Error} If invalid signer is provided that doesn't match the swap data
1095
- */
1096
- async refund(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal): Promise<string> {
1097
- const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1098
- const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsRefund(signer.getAddress()), true, abortSignal)
1099
-
1100
- this._refundTxId = result[0];
1101
- if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE || this._state===ToBTCSwapState.SOFT_CLAIMED) {
1102
- await this._saveAndEmit(ToBTCSwapState.REFUNDED);
1103
- }
1104
- return result[0];
1105
- }
1106
-
1107
- /**
1108
- * @inheritDoc
1109
- *
1110
- * @throws {Error} When swap is not in a valid state (must be {@link ToBTCSwapState.COMMITED} or
1111
- * {@link ToBTCSwapState.REFUNDABLE})
1112
- * @throws {Error} If we tried to refund but claimer was able to claim first
1113
- */
1114
- async waitTillRefunded(abortSignal?: AbortSignal): Promise<void> {
1115
- if(this._state===ToBTCSwapState.REFUNDED) return Promise.resolve();
1116
- if(
1117
- this._state!==ToBTCSwapState.COMMITED &&
1118
- this._state!==ToBTCSwapState.SOFT_CLAIMED &&
1119
- this._state!==ToBTCSwapState.REFUNDABLE
1120
- ) throw new Error("Invalid state (not COMMITED)");
1121
-
1122
- const abortController = new AbortController();
1123
- if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
1124
- const res = await Promise.race([
1125
- this.watchdogWaitTillResult(undefined, abortController.signal),
1126
- this.waitTillState(ToBTCSwapState.REFUNDED, "eq", abortController.signal).then(() => 0 as const),
1127
- this.waitTillState(ToBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 1 as const),
1128
- ]);
1129
- abortController.abort();
1130
-
1131
- if(res===0) {
1132
- this.logger.debug("waitTillRefunded(): Resolved from state change (REFUNDED)");
1133
- return;
1134
- }
1135
- if(res===1) {
1136
- this.logger.debug("waitTillRefunded(): Resolved from state change (CLAIMED)");
1137
- throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
1138
- }
1139
- this.logger.debug("waitTillRefunded(): Resolved from watchdog");
1140
-
1141
- if(res?.type===SwapCommitStateType.PAID) {
1142
- if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
1143
- await this._saveAndEmit(ToBTCSwapState.CLAIMED);
1144
- throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
1145
- }
1146
- if(res?.type===SwapCommitStateType.NOT_COMMITED) {
1147
- if(this._refundTxId==null && res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
1148
- await this._saveAndEmit(ToBTCSwapState.REFUNDED);
1149
- }
1150
- }
1151
-
1152
-
1153
- //////////////////////////////
1154
- //// Storage
1155
-
1156
- /**
1157
- * @inheritDoc
1158
- */
1159
- serialize(): any {
1160
- const obj = super.serialize();
1161
- return {
1162
- ...obj,
1163
- networkFee: this.networkFee==null ? null : this.networkFee.toString(10),
1164
- networkFeeBtc: this.networkFeeBtc==null ? null : this.networkFeeBtc.toString(10)
1165
- };
1166
- }
1167
-
1168
-
1169
- //////////////////////////////
1170
- //// Swap ticks & sync
1171
-
1172
- /**
1173
- * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1174
- * data
1175
- *
1176
- * @private
1177
- */
1178
- private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
1179
- if(
1180
- this._state===ToBTCSwapState.CREATED ||
1181
- this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
1182
- this._state===ToBTCSwapState.COMMITED ||
1183
- this._state===ToBTCSwapState.SOFT_CLAIMED ||
1184
- this._state===ToBTCSwapState.REFUNDABLE
1185
- ) {
1186
- let quoteExpired = false;
1187
- if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
1188
- //Check if quote is still valid
1189
- quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
1190
- }
1191
-
1192
- commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
1193
- if(commitStatus!=null && await this._forciblySetOnchainState(commitStatus)) return true;
1194
-
1195
- if((this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED)) {
1196
- if(quoteExpired) {
1197
- this._state = ToBTCSwapState.QUOTE_EXPIRED;
1198
- return true;
1199
- }
1200
- }
1201
- }
1202
- return false;
1203
- }
1204
-
1205
- /**
1206
- * @inheritDoc
1207
- * @internal
1208
- */
1209
- _shouldFetchOnchainState(): boolean {
1210
- return this._state===ToBTCSwapState.CREATED ||
1211
- this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
1212
- this._state===ToBTCSwapState.COMMITED ||
1213
- this._state===ToBTCSwapState.SOFT_CLAIMED ||
1214
- this._state===ToBTCSwapState.REFUNDABLE;
1215
- }
1216
-
1217
- /**
1218
- * @inheritDoc
1219
- * @internal
1220
- */
1221
- _shouldFetchExpiryStatus(): boolean {
1222
- return this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
1223
- }
1224
-
1225
- /**
1226
- * @inheritDoc
1227
- * @internal
1228
- */
1229
- async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
1230
- let changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1231
-
1232
- if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED) {
1233
- //Check if that maybe already concluded
1234
- try {
1235
- if(await this.checkIntermediarySwapProcessed(false)) changed = true;
1236
- } catch (e) {
1237
- this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
1238
- }
1239
- }
1240
-
1241
- if(save && changed) await this._saveAndEmit();
1242
-
1243
- return changed;
1244
- }
1245
-
1246
- /**
1247
- * @inheritDoc
1248
- * @internal
1249
- */
1250
- async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
1251
- switch(commitStatus.type) {
1252
- case SwapCommitStateType.PAID:
1253
- if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1254
- if(this._claimTxId==null && commitStatus.getClaimTxId) this._claimTxId = await commitStatus.getClaimTxId();
1255
- const eventResult = await commitStatus.getClaimResult();
1256
- try {
1257
- await this._setPaymentResult({secret: eventResult, txId: Buffer.from(eventResult, "hex").reverse().toString("hex")});
1258
- } catch (e) {
1259
- this.logger.error(`Failed to set payment result ${eventResult} on the swap!`);
1260
- }
1261
- this._state = ToBTCSwapState.CLAIMED;
1262
- return true;
1263
- case SwapCommitStateType.REFUNDABLE:
1264
- if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1265
- this._state = ToBTCSwapState.REFUNDABLE;
1266
- return true;
1267
- case SwapCommitStateType.EXPIRED:
1268
- if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1269
- if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
1270
- this._state = this._refundTxId==null ? ToBTCSwapState.QUOTE_EXPIRED : ToBTCSwapState.REFUNDED;
1271
- return true;
1272
- case SwapCommitStateType.NOT_COMMITED:
1273
- let changed: boolean = false;
1274
- if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1275
- this._commitTxId = await (commitStatus as any).getInitTxId();
1276
- changed = true;
1277
- }
1278
- if(this._refundTxId==null && commitStatus.getRefundTxId) {
1279
- this._refundTxId = await commitStatus.getRefundTxId();
1280
- changed = true;
1281
- }
1282
- if(this._refundTxId!=null) {
1283
- this._state = ToBTCSwapState.REFUNDED;
1284
- changed = true;
1285
- }
1286
- return changed;
1287
- case SwapCommitStateType.COMMITED:
1288
- let save: boolean = false;
1289
- if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1290
- this._commitTxId = await (commitStatus as any).getInitTxId();
1291
- save = true;
1292
- }
1293
- if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.REFUNDABLE && this._state!==ToBTCSwapState.SOFT_CLAIMED) {
1294
- this._state = ToBTCSwapState.COMMITED;
1295
- save = true;
1296
- }
1297
- return save;
1298
- }
1299
- return false;
1300
- }
1301
-
1302
- /**
1303
- * @inheritDoc
1304
- * @internal
1305
- */
1306
- async _tick(save?: boolean): Promise<boolean> {
1307
- switch(this._state) {
1308
- case ToBTCSwapState.CREATED:
1309
- if(this.expiry<Date.now()) {
1310
- this._state = ToBTCSwapState.QUOTE_SOFT_EXPIRED;
1311
- if(save) await this._saveAndEmit();
1312
- return true;
1313
- }
1314
- break;
1315
- case ToBTCSwapState.COMMITED:
1316
- case ToBTCSwapState.SOFT_CLAIMED:
1317
- const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1318
- if(expired) {
1319
- this._state = ToBTCSwapState.REFUNDABLE;
1320
- if(save) await this._saveAndEmit();
1321
- return true;
1322
- }
1323
- break;
1324
- }
1325
- return false;
1326
- }
1327
- }
1
+ import {IToBTCDefinition, IToBTCWrapper} from "./IToBTCWrapper";
2
+ import {
3
+ ChainType,
4
+ isAbstractSigner,
5
+ SignatureData,
6
+ SignatureVerificationError,
7
+ SwapCommitState,
8
+ SwapCommitStateType,
9
+ SwapData
10
+ } from "@atomiqlabs/base";
11
+ import {
12
+ IntermediaryAPI,
13
+ RefundAuthorizationResponse,
14
+ RefundAuthorizationResponseCodes
15
+ } from "../../../intermediaries/apis/IntermediaryAPI";
16
+ import {IntermediaryError} from "../../../errors/IntermediaryError";
17
+ import {extendAbortController, toBigInt} from "../../../utils/Utils";
18
+ import {Fee} from "../../../types/fees/Fee";
19
+ import {IEscrowSelfInitSwap, IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../IEscrowSelfInitSwap";
20
+ import {IRefundableSwap} from "../../IRefundableSwap";
21
+ import {FeeType} from "../../../enums/FeeType";
22
+ import {ppmToPercentage} from "../../../types/fees/PercentagePPM";
23
+ import {TokenAmount, toTokenAmount} from "../../../types/TokenAmount";
24
+ import {BtcToken, SCToken} from "../../../types/Token";
25
+ import {timeoutPromise} from "../../../utils/TimeoutUtils";
26
+ import {SwapExecutionActionSignSmartChainTx, SwapExecutionActionWait} from "../../../types/SwapExecutionAction";
27
+ import {
28
+ SwapExecutionStep,
29
+ SwapExecutionStepPayment,
30
+ SwapExecutionStepRefund,
31
+ SwapExecutionStepSettlement
32
+ } from "../../../types/SwapExecutionStep";
33
+ import {SwapStateInfo} from "../../../types/SwapStateInfo";
34
+
35
+ export type IToBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
36
+ signatureData?: SignatureData,
37
+ data: T,
38
+ networkFee: bigint,
39
+ networkFeeBtc: bigint
40
+ };
41
+
42
+ export function isIToBTCSwapInit<T extends SwapData>(obj: any): obj is IToBTCSwapInit<T> {
43
+ return typeof(obj.networkFee) === "bigint" &&
44
+ typeof(obj.networkFeeBtc) === "bigint" &&
45
+ (obj.signatureData==null || (
46
+ typeof(obj.signatureData) === 'object' &&
47
+ typeof(obj.signatureData.prefix)==="string" &&
48
+ typeof(obj.signatureData.timeout)==="string" &&
49
+ typeof(obj.signatureData.signature)==="string"
50
+ )) &&
51
+ typeof(obj.data) === 'object' &&
52
+ isIEscrowSelfInitSwapInit<T>(obj);
53
+ }
54
+
55
+ /**
56
+ * State enum for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
57
+ *
58
+ * @category Swaps/Smart chain → Bitcoin
59
+ */
60
+ export enum ToBTCSwapState {
61
+ /**
62
+ * Intermediary (LP) was unable to process the swap and the funds were refunded on the
63
+ * source chain
64
+ */
65
+ REFUNDED = -3,
66
+ /**
67
+ * Swap has expired for good and there is no way how it can be executed anymore
68
+ */
69
+ QUOTE_EXPIRED = -2,
70
+ /**
71
+ * A swap is almost expired, and it should be presented to the user as expired, though
72
+ * there is still a chance that it will be processed
73
+ */
74
+ QUOTE_SOFT_EXPIRED = -1,
75
+ /**
76
+ * Swap was created, use the {@link IToBTCSwap.commit} or {@link IToBTCSwap.txsCommit} to
77
+ * initiate it by creating the swap escrow on the source chain
78
+ */
79
+ CREATED = 0,
80
+ /**
81
+ * Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will
82
+ * now process the swap. You can wait till that happens with the {@link IToBTCSwap.waitForPayment}
83
+ * function.
84
+ */
85
+ COMMITED = 1,
86
+ /**
87
+ * The intermediary (LP) has processed the transaction and sent out the funds on the destination chain,
88
+ * but hasn't yet settled the escrow on the source chain.
89
+ */
90
+ SOFT_CLAIMED = 2,
91
+ /**
92
+ * Swap was successfully settled by the intermediary (LP) on the source chain
93
+ */
94
+ CLAIMED = 3,
95
+ /**
96
+ * Intermediary (LP) was unable to process the swap and the swap escrow on the source chain
97
+ * is refundable, call {@link IToBTCSwap.refund} or {@link IToBTCSwap.txsRefund} to refund
98
+ */
99
+ REFUNDABLE = 4
100
+ }
101
+
102
+ const ToBTCSwapStateDescription = {
103
+ [ToBTCSwapState.REFUNDED]: "Intermediary (LP) was unable to process the swap and the funds were refunded on the source chain",
104
+ [ToBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
105
+ [ToBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
106
+ [ToBTCSwapState.CREATED]: "Swap was created, initiate it by creating the swap escrow on the source chain",
107
+ [ToBTCSwapState.COMMITED]: "Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will now process the swap.",
108
+ [ToBTCSwapState.SOFT_CLAIMED]: "The intermediary (LP) has processed the transaction and sent out the funds on the destination chain, but hasn't yet settled the escrow on the source chain.",
109
+ [ToBTCSwapState.CLAIMED]: "Swap was successfully settled by the intermediary (LP) on the source chain",
110
+ [ToBTCSwapState.REFUNDABLE]: "Intermediary (LP) was unable to process the swap and the swap escrow on the source chain is refundable."
111
+ };
112
+
113
+ /**
114
+ * Base class for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
115
+ *
116
+ * @category Swaps/Smart chain → Bitcoin
117
+ */
118
+ export abstract class IToBTCSwap<
119
+ T extends ChainType = ChainType,
120
+ D extends IToBTCDefinition<T, IToBTCWrapper<T, D>, IToBTCSwap<T, D>> = IToBTCDefinition<T, IToBTCWrapper<T, any>, IToBTCSwap<T, any>>,
121
+ > extends IEscrowSelfInitSwap<T, D, ToBTCSwapState> implements IRefundableSwap<T, D, ToBTCSwapState> {
122
+
123
+ /**
124
+ * @internal
125
+ */
126
+ protected readonly swapStateDescription = ToBTCSwapStateDescription;
127
+ /**
128
+ * @internal
129
+ */
130
+ protected readonly swapStateName = (state: number) => ToBTCSwapState[state];
131
+ /**
132
+ * @internal
133
+ */
134
+ protected readonly abstract outputToken: BtcToken;
135
+ /**
136
+ * @internal
137
+ */
138
+ protected readonly networkFee: bigint;
139
+ /**
140
+ * @internal
141
+ */
142
+ protected networkFeeBtc: bigint;
143
+
144
+ /**
145
+ * @internal
146
+ */
147
+ readonly _data!: T["Data"];
148
+
149
+ protected constructor(wrapper: D["Wrapper"], serializedObject: any);
150
+ protected constructor(wrapper: D["Wrapper"], init: IToBTCSwapInit<T["Data"]>);
151
+ protected constructor(
152
+ wrapper: D["Wrapper"],
153
+ initOrObject: IToBTCSwapInit<T["Data"]> | any
154
+ ) {
155
+ super(wrapper, initOrObject);
156
+ if(isIToBTCSwapInit<T["Data"]>(initOrObject)) {
157
+ this._state = ToBTCSwapState.CREATED;
158
+ this.networkFee = initOrObject.networkFee;
159
+ this.networkFeeBtc = initOrObject.networkFeeBtc;
160
+ this._data = initOrObject.data;
161
+ this.signatureData = initOrObject.signatureData;
162
+ } else {
163
+ this.networkFee = toBigInt(initOrObject.networkFee);
164
+ this.networkFeeBtc = toBigInt(initOrObject.networkFeeBtc);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * @inheritDoc
170
+ * @internal
171
+ */
172
+ protected getSwapData(): T["Data"] {
173
+ return this._data;
174
+ }
175
+
176
+ /**
177
+ * @inheritDoc
178
+ * @internal
179
+ */
180
+ protected upgradeVersion() {
181
+ if(this.version == null) {
182
+ switch(this._state) {
183
+ case -2:
184
+ this._state = ToBTCSwapState.REFUNDED
185
+ break;
186
+ case -1:
187
+ this._state = ToBTCSwapState.QUOTE_EXPIRED
188
+ break;
189
+ case 0:
190
+ this._state = ToBTCSwapState.CREATED
191
+ break;
192
+ case 1:
193
+ this._state = ToBTCSwapState.COMMITED
194
+ break;
195
+ case 2:
196
+ this._state = ToBTCSwapState.CLAIMED
197
+ break;
198
+ case 3:
199
+ this._state = ToBTCSwapState.REFUNDABLE
200
+ break;
201
+ }
202
+ this.version = 1;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * @inheritDoc
208
+ * @internal
209
+ */
210
+ protected tryRecomputeSwapPrice() {
211
+ const output = this.getOutput();
212
+ if(output.rawAmount!=null) {
213
+ if(this.swapFeeBtc==null) {
214
+ this.swapFeeBtc = this.swapFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
215
+ }
216
+ if(this.networkFeeBtc==null) {
217
+ this.networkFeeBtc = this.networkFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
218
+ }
219
+ }
220
+ super.tryRecomputeSwapPrice();
221
+ }
222
+
223
+ /**
224
+ * Returns the payment hash identifier to be sent to the LP for getStatus and getRefund
225
+ * @internal
226
+ */
227
+ protected getLpIdentifier(): string {
228
+ return this.getClaimHash();
229
+ }
230
+
231
+ /**
232
+ * Sets the payment result for the swap, optionally also checking it (checking that tx exist or swap secret is valid)
233
+ *
234
+ * @param result Result returned by the LP
235
+ * @param check Whether to check the passed result
236
+ * @returns true if check passed, false if check failed with a soft error (e.g. tx not yet found in the mempool)
237
+ * @throws {IntermediaryError} When the data returned by the intermediary isn't valid
238
+ *
239
+ * @internal
240
+ */
241
+ abstract _setPaymentResult(result: {secret?: string, txId?: string}, check?: boolean): Promise<boolean>;
242
+
243
+
244
+ //////////////////////////////
245
+ //// Getters & utils
246
+
247
+ /**
248
+ * @inheritDoc
249
+ */
250
+ getInputAddress(): string | null {
251
+ return this._getInitiator();
252
+ }
253
+
254
+ /**
255
+ * @inheritDoc
256
+ */
257
+ getInputTxId(): string | null {
258
+ return this._commitTxId ?? null;
259
+ }
260
+
261
+ /**
262
+ * @inheritDoc
263
+ */
264
+ requiresAction(): boolean {
265
+ return this.isRefundable();
266
+ }
267
+
268
+ /**
269
+ * @inheritDoc
270
+ */
271
+ isFinished(): boolean {
272
+ return this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.REFUNDED || this._state===ToBTCSwapState.QUOTE_EXPIRED;
273
+ }
274
+
275
+ /**
276
+ * @inheritDoc
277
+ */
278
+ isRefundable(): boolean {
279
+ return this._state===ToBTCSwapState.REFUNDABLE;
280
+ }
281
+
282
+ /**
283
+ * @inheritDoc
284
+ */
285
+ isQuoteExpired(): boolean {
286
+ return this._state===ToBTCSwapState.QUOTE_EXPIRED;
287
+ }
288
+
289
+ /**
290
+ * @inheritDoc
291
+ */
292
+ isQuoteSoftExpired(): boolean {
293
+ return this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
294
+ }
295
+
296
+ /**
297
+ * @inheritDoc
298
+ */
299
+ isSuccessful(): boolean {
300
+ return this._state===ToBTCSwapState.CLAIMED;
301
+ }
302
+
303
+ /**
304
+ * @inheritDoc
305
+ */
306
+ isFailed(): boolean {
307
+ return this._state===ToBTCSwapState.REFUNDED;
308
+ }
309
+
310
+ /**
311
+ * @inheritDoc
312
+ */
313
+ isInProgress(): boolean {
314
+ return this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED;
315
+ }
316
+
317
+ /**
318
+ * Returns the time (in UNIX milliseconds) at which the swap expires and the user is able to unilaterally
319
+ * refund it with the {@link refund} or {@link txsRefund} function.
320
+ */
321
+ getExpiry(): number {
322
+ return Number(this._data.getExpiry())*1000;
323
+ }
324
+
325
+ /**
326
+ * @inheritDoc
327
+ * @internal
328
+ */
329
+ _getInitiator(): string {
330
+ return this._data.getOfferer();
331
+ }
332
+
333
+
334
+ //////////////////////////////
335
+ //// Amounts & fees
336
+
337
+ /**
338
+ * Returns the swap fee charged by the intermediary (LP) on this swap
339
+ *
340
+ * @internal
341
+ */
342
+ protected getSwapFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
343
+ if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
344
+
345
+ const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
346
+ const output = this.getOutput();
347
+ const swapFeePPM = output.rawAmount==null ? 0n : feeWithoutBaseFee * 1000000n / output.rawAmount;
348
+
349
+ const amountInDstToken = toTokenAmount(
350
+ this.swapFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
351
+ );
352
+ return {
353
+ amountInSrcToken: toTokenAmount(this.swapFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
354
+ amountInDstToken,
355
+ currentUsdValue: amountInDstToken.currentUsdValue,
356
+ usdValue: amountInDstToken.usdValue,
357
+ pastUsdValue: amountInDstToken.pastUsdValue,
358
+ composition: {
359
+ base: toTokenAmount(this.pricingInfo.satsBaseFee, this.outputToken, this.wrapper._prices, this.pricingInfo),
360
+ percentage: ppmToPercentage(swapFeePPM)
361
+ }
362
+ };
363
+ }
364
+
365
+ /**
366
+ * Returns network fee for on the destination chain for the swap
367
+ *
368
+ * @internal
369
+ */
370
+ protected getNetworkFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
371
+ const amountInDstToken = toTokenAmount(
372
+ this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
373
+ );
374
+ return {
375
+ amountInSrcToken: toTokenAmount(
376
+ this.networkFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
377
+ ),
378
+ amountInDstToken,
379
+ currentUsdValue: amountInDstToken.currentUsdValue,
380
+ usdValue: amountInDstToken.usdValue,
381
+ pastUsdValue: amountInDstToken.pastUsdValue
382
+ };
383
+ }
384
+
385
+ /**
386
+ * @inheritDoc
387
+ */
388
+ getFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
389
+ const amountInDstToken = toTokenAmount(
390
+ this.swapFeeBtc + this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
391
+ );
392
+ return {
393
+ amountInSrcToken: toTokenAmount(
394
+ this.swapFee + this.networkFee, this.wrapper._tokens[this._data.getToken()],
395
+ this.wrapper._prices, this.pricingInfo
396
+ ),
397
+ amountInDstToken,
398
+ currentUsdValue: amountInDstToken.currentUsdValue,
399
+ usdValue: amountInDstToken.usdValue,
400
+ pastUsdValue: amountInDstToken.pastUsdValue
401
+ }
402
+ }
403
+
404
+ /**
405
+ * @inheritDoc
406
+ */
407
+ getFeeBreakdown(): [
408
+ {type: FeeType.SWAP, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
409
+ {type: FeeType.NETWORK_OUTPUT, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
410
+ ] {
411
+ return [
412
+ {
413
+ type: FeeType.SWAP,
414
+ fee: this.getSwapFee()
415
+ },
416
+ {
417
+ type: FeeType.NETWORK_OUTPUT,
418
+ fee: this.getNetworkFee()
419
+ }
420
+ ];
421
+ }
422
+
423
+ /**
424
+ * @inheritDoc
425
+ */
426
+ getInputToken(): SCToken<T["ChainId"]> {
427
+ return this.wrapper._tokens[this._data.getToken()];
428
+ }
429
+
430
+ /**
431
+ * @inheritDoc
432
+ */
433
+ getInput(): TokenAmount<SCToken<T["ChainId"]>, true> {
434
+ return toTokenAmount(
435
+ this._data.getAmount(), this.wrapper._tokens[this._data.getToken()],
436
+ this.wrapper._prices, this.pricingInfo
437
+ );
438
+ }
439
+
440
+ /**
441
+ * @inheritDoc
442
+ */
443
+ getInputWithoutFee(): TokenAmount<SCToken<T["ChainId"]>, true> {
444
+ return toTokenAmount(
445
+ this._data.getAmount() - (this.swapFee + this.networkFee),
446
+ this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
447
+ );
448
+ }
449
+
450
+ /**
451
+ * Checks if the initiator/sender on the source chain has enough balance to go through with the swap
452
+ */
453
+ async hasEnoughBalance(): Promise<{
454
+ enoughBalance: boolean,
455
+ balance: TokenAmount<SCToken<T["ChainId"]>, true>,
456
+ required: TokenAmount<SCToken<T["ChainId"]>, true>
457
+ }> {
458
+ const [balance, commitFee] = await Promise.all([
459
+ this._contract.getBalance(this._getInitiator(), this._data.getToken(), false),
460
+ this._data.getToken()===this.wrapper._chain.getNativeCurrencyAddress() ? this.getCommitFee() : Promise.resolve(null)
461
+ ]);
462
+ let required = this._data.getAmount();
463
+ if(commitFee!=null) required = required + commitFee;
464
+ return {
465
+ enoughBalance: balance >= required,
466
+ balance: toTokenAmount(balance, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
467
+ required: toTokenAmount(required, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo)
468
+ };
469
+ }
470
+
471
+ /**
472
+ * Checks if the initiator/sender on the source chain has enough native token balance
473
+ * to cover the transaction fee of initiating the swap
474
+ */
475
+ async hasEnoughForTxFees(): Promise<{
476
+ enoughBalance: boolean,
477
+ balance: TokenAmount<SCToken<T["ChainId"]>, true>,
478
+ required: TokenAmount<SCToken<T["ChainId"]>, true>
479
+ }> {
480
+ const [balance, commitFee] = await Promise.all([
481
+ this._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
482
+ this.getCommitFee()
483
+ ]);
484
+ return {
485
+ enoughBalance: balance >= commitFee,
486
+ balance: toTokenAmount(balance, this.wrapper._getNativeToken(), this.wrapper._prices),
487
+ required: toTokenAmount(commitFee, this.wrapper._getNativeToken(), this.wrapper._prices)
488
+ };
489
+ }
490
+
491
+
492
+ //////////////////////////////
493
+ //// Execution
494
+
495
+ /**
496
+ * Executes the swap with the provided smart chain wallet/signer
497
+ *
498
+ * @param signer Smart chain wallet/signer to use to sign the transaction on the source chain
499
+ * @param callbacks Callbacks to track the progress of the swap
500
+ * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
501
+ *
502
+ * @returns {boolean} Whether the swap was successfully processed by the LP, in case `false` is returned
503
+ * the user can refund their funds back on the source chain by calling {@link refund}
504
+ */
505
+ async execute(
506
+ signer: T["Signer"] | T["NativeSigner"],
507
+ callbacks?: {
508
+ onSourceTransactionSent?: (sourceTxId: string) => void,
509
+ onSourceTransactionConfirmed?: (sourceTxId: string) => void,
510
+ onSwapSettled?: (destinationTxId: string) => void
511
+ },
512
+ options?: {
513
+ abortSignal?: AbortSignal,
514
+ paymentCheckIntervalSeconds?: number,
515
+ maxWaitTillSwapProcessedSeconds?: number
516
+ }
517
+ ): Promise<boolean> {
518
+ if(this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Quote expired");
519
+ if(this._state===ToBTCSwapState.REFUNDED) throw new Error("Swap already refunded");
520
+ if(this._state===ToBTCSwapState.REFUNDABLE) throw new Error("Swap refundable, refund with swap.refund()");
521
+ if(this._state===ToBTCSwapState.SOFT_CLAIMED || this._state===ToBTCSwapState.CLAIMED) throw new Error("Swap already settled!");
522
+
523
+ if(this._state===ToBTCSwapState.CREATED) {
524
+ const txId = await this.commit(signer, options?.abortSignal, false, callbacks?.onSourceTransactionSent);
525
+ if(callbacks?.onSourceTransactionConfirmed!=null) callbacks.onSourceTransactionConfirmed(txId);
526
+ }
527
+
528
+ // @ts-ignore
529
+ if(this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.SOFT_CLAIMED) return true;
530
+
531
+ if(this._state===ToBTCSwapState.COMMITED) {
532
+ const success = await this.waitForPayment(options?.maxWaitTillSwapProcessedSeconds ?? 120, options?.paymentCheckIntervalSeconds, options?.abortSignal);
533
+ if(success) {
534
+ if(callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
535
+ return true;
536
+ } else {
537
+ return false;
538
+ }
539
+ }
540
+
541
+ throw new Error("Unexpected state reached!");
542
+ }
543
+
544
+ /**
545
+ * @internal
546
+ */
547
+ protected async _getExecutionStatus() {
548
+ const state = this._state;
549
+
550
+ let sourcePaymentStatus: SwapExecutionStepPayment<T["ChainId"]>["status"] = "inactive";
551
+ let destinationPayoutStatus: SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">["status"] = "inactive";
552
+ let refundStatus: SwapExecutionStepRefund<T["ChainId"]>["status"] = "inactive";
553
+ let buildCurrentAction: (actionOptions?: {
554
+ skipChecks?: boolean,
555
+ refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
556
+ }) => Promise<
557
+ SwapExecutionActionSignSmartChainTx<T> |
558
+ SwapExecutionActionWait<"LP"> |
559
+ undefined
560
+ > = async () => undefined;
561
+
562
+ switch(state) {
563
+ case ToBTCSwapState.CREATED:
564
+ const quoteValid = await this._verifyQuoteValid();
565
+ sourcePaymentStatus = quoteValid ? "awaiting" : "soft_expired";
566
+ if(this.signatureData!=null && quoteValid) {
567
+ buildCurrentAction = this._buildInitSmartChainTxAction.bind(this);
568
+ }
569
+ break;
570
+ case ToBTCSwapState.QUOTE_SOFT_EXPIRED:
571
+ sourcePaymentStatus = "soft_expired";
572
+ break;
573
+ case ToBTCSwapState.QUOTE_EXPIRED:
574
+ sourcePaymentStatus = "expired";
575
+ break;
576
+ case ToBTCSwapState.COMMITED:
577
+ sourcePaymentStatus = "confirmed";
578
+ destinationPayoutStatus = "waiting_lp";
579
+ buildCurrentAction = this._buildWaitLpAction.bind(this);
580
+ break;
581
+ case ToBTCSwapState.SOFT_CLAIMED:
582
+ sourcePaymentStatus = "confirmed";
583
+ destinationPayoutStatus = "soft_settled";
584
+ break;
585
+ case ToBTCSwapState.CLAIMED:
586
+ sourcePaymentStatus = "confirmed";
587
+ destinationPayoutStatus = "settled";
588
+ break;
589
+ case ToBTCSwapState.REFUNDABLE:
590
+ sourcePaymentStatus = "confirmed";
591
+ destinationPayoutStatus = "expired";
592
+ refundStatus = "awaiting";
593
+ buildCurrentAction = this._buildRefundSmartChainTxAction.bind(this);
594
+ break;
595
+ case ToBTCSwapState.REFUNDED:
596
+ sourcePaymentStatus = "confirmed";
597
+ destinationPayoutStatus = "expired";
598
+ refundStatus = "refunded";
599
+ break;
600
+ }
601
+
602
+ return {
603
+ steps: [
604
+ {
605
+ type: "Payment",
606
+ side: "source",
607
+ chain: this.chainIdentifier,
608
+ title: "Source payment",
609
+ description: `Initiate the swap by funding the escrow on the ${this.chainIdentifier} side`,
610
+ status: sourcePaymentStatus,
611
+ initTxId: this._commitTxId,
612
+ settleTxId: this._claimTxId
613
+ },
614
+ {
615
+ type: "Settlement",
616
+ side: "destination",
617
+ chain: this.outputToken.chainId,
618
+ title: "Destination payout",
619
+ description: `Wait for the LP to process the swap and send the payout on the ${this.outputToken.chainId} side`,
620
+ status: destinationPayoutStatus,
621
+ initTxId: destinationPayoutStatus==="settled" || destinationPayoutStatus==="soft_settled" ? this.getOutputTxId() : undefined,
622
+ settleTxId: destinationPayoutStatus==="settled" ? this.getOutputTxId() : undefined,
623
+ },
624
+ {
625
+ type: "Refund",
626
+ side: "source",
627
+ chain: this.chainIdentifier,
628
+ title: "Source refund",
629
+ description: `Refund escrowed funds on the ${this.chainIdentifier} side, after LP failed to execute`,
630
+ status: refundStatus,
631
+ refundTxId: this._refundTxId
632
+ }
633
+ ] as [
634
+ SwapExecutionStepPayment<T["ChainId"]>,
635
+ SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
636
+ SwapExecutionStepRefund<T["ChainId"]>,
637
+ ],
638
+ buildCurrentAction,
639
+ state
640
+ };
641
+ }
642
+
643
+ /**
644
+ * @inheritDoc
645
+ * @internal
646
+ */
647
+ async _submitExecutionTransactions(txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, requiredStates?: ToBTCSwapState[], idempotent?: boolean): Promise<string[]> {
648
+ const parsedTxs: T["SignedTXType"][] = [];
649
+ for(let tx of txs) {
650
+ parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
651
+ }
652
+
653
+ if(idempotent) {
654
+ // Handle idempotent calls
655
+ if(this.wrapper._chain.getTxId!=null) {
656
+ const txIds = await Promise.all(parsedTxs.map(tx => this.wrapper._chain.getTxId!(tx)));
657
+ const foundTxId = txIds.find(txId => this._commitTxId===txId || this._refundTxId===txId);
658
+ if(foundTxId!=null) return txIds;
659
+ }
660
+ }
661
+
662
+ if(requiredStates!=null && !requiredStates.includes(this._state)) throw new Error("Swap state has changed before transactions were submitted!");
663
+
664
+ if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
665
+ if(!await this._verifyQuoteValid()) throw new Error("Quote is already expired!");
666
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
667
+ await this.waitTillCommited(abortSignal);
668
+ return txIds;
669
+ }
670
+
671
+ if(this._state===ToBTCSwapState.REFUNDABLE) {
672
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
673
+ await this.waitTillRefunded(abortSignal);
674
+ return txIds;
675
+ }
676
+
677
+ throw new Error("Invalid swap state for transaction submission!");
678
+ }
679
+
680
+ /**
681
+ * @internal
682
+ */
683
+ private async _buildInitSmartChainTxAction(actionOptions?: {
684
+ skipChecks?: boolean
685
+ }): Promise<SwapExecutionActionSignSmartChainTx> {
686
+ return {
687
+ type: "SignSmartChainTransaction",
688
+ name: "Initiate swap",
689
+ description: `Initiates the swap by commiting the funds to the escrow on the ${this.chainIdentifier} side`,
690
+ chain: this.chainIdentifier,
691
+ txs: await this.prepareTransactions(this.txsCommit(actionOptions?.skipChecks)),
692
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
693
+ return this._submitExecutionTransactions(txs, abortSignal, [ToBTCSwapState.CREATED, ToBTCSwapState.QUOTE_SOFT_EXPIRED], idempotent);
694
+ },
695
+ requiredSigner: this._getInitiator()
696
+ } as SwapExecutionActionSignSmartChainTx;
697
+ }
698
+
699
+ /**
700
+ * @internal
701
+ */
702
+ private async _buildWaitLpAction(): Promise<SwapExecutionActionWait<"LP">> {
703
+ return {
704
+ type: "Wait",
705
+ name: "Awaiting LP payout",
706
+ description: "Wait for the intermediary to process the swap and either send the payout or make the swap refundable",
707
+ pollTimeSeconds: 5,
708
+ expectedTimeSeconds: 10,
709
+ wait: async (
710
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
711
+ ) => {
712
+ await this.waitForPayment(maxWaitTimeSeconds, pollIntervalSeconds, abortSignal);
713
+ }
714
+ } as SwapExecutionActionWait<"LP">;
715
+ }
716
+
717
+ /**
718
+ * @internal
719
+ */
720
+ private async _buildRefundSmartChainTxAction(actionOptions?: {
721
+ refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
722
+ }): Promise<SwapExecutionActionSignSmartChainTx> {
723
+ const signerAddress =
724
+ await this.wrapper._getSignerAddress(actionOptions?.refundSmartChainSigner);
725
+
726
+ return {
727
+ type: "SignSmartChainTransaction",
728
+ name: "Refund",
729
+ description: "Refund the swap after it failed to execute",
730
+ chain: this.chainIdentifier,
731
+ txs: await this.prepareTransactions(this.txsRefund(actionOptions?.refundSmartChainSigner)),
732
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
733
+ return this._submitExecutionTransactions(txs, abortSignal, [ToBTCSwapState.REFUNDABLE], idempotent);
734
+ },
735
+ requiredSigner: signerAddress ?? this._getInitiator()
736
+ } as SwapExecutionActionSignSmartChainTx;
737
+ }
738
+
739
+ /**
740
+ * @inheritDoc
741
+ *
742
+ * @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
743
+ * (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
744
+ * @param options.refundSmartChainSigner Optional smart chain signer to use when creating refunds transactions
745
+ */
746
+ async getExecutionAction(options?: {
747
+ skipChecks?: boolean,
748
+ refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
749
+ }): Promise<
750
+ SwapExecutionActionSignSmartChainTx<T> |
751
+ SwapExecutionActionWait<"LP"> |
752
+ undefined
753
+ > {
754
+ const executionStatus = await this._getExecutionStatus();
755
+ return executionStatus.buildCurrentAction(options);
756
+ }
757
+
758
+ /**
759
+ * @inheritDoc
760
+ */
761
+ async getExecutionStatus(options?: {
762
+ skipBuildingAction?: boolean,
763
+ skipChecks?: boolean,
764
+ refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
765
+ }): Promise<{
766
+ steps: [
767
+ SwapExecutionStepPayment<T["ChainId"]>,
768
+ SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
769
+ SwapExecutionStepRefund<T["ChainId"]>,
770
+ ],
771
+ currentAction:
772
+ SwapExecutionActionSignSmartChainTx<T> |
773
+ SwapExecutionActionWait<"LP"> |
774
+ undefined,
775
+ stateInfo: SwapStateInfo<ToBTCSwapState>
776
+ }> {
777
+ const executionStatus = await this._getExecutionStatus();
778
+ return {
779
+ steps: executionStatus.steps,
780
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
781
+ stateInfo: this._getStateInfo(executionStatus.state)
782
+ };
783
+ }
784
+
785
+ /**
786
+ * @inheritDoc
787
+ */
788
+ async getExecutionSteps(): Promise<[
789
+ SwapExecutionStepPayment<T["ChainId"]>,
790
+ SwapExecutionStepSettlement<"BITCOIN" | "LIGHTNING", "soft_settled">,
791
+ SwapExecutionStepRefund<T["ChainId"]>,
792
+ ]> {
793
+ return (await this._getExecutionStatus()).steps;
794
+ }
795
+
796
+ //////////////////////////////
797
+ //// Commit
798
+
799
+ /**
800
+ * @inheritDoc
801
+ *
802
+ * @throws {Error} When in invalid state (not {@link ToBTCSwapState.CREATED})
803
+ */
804
+ async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
805
+ if(this._state!==ToBTCSwapState.CREATED && (!skipChecks || this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED)) throw new Error("Must be in CREATED state!");
806
+ if(this.signatureData==null) throw new Error("Init signature data not known, cannot commit!");
807
+
808
+ if(!this.initiated) {
809
+ this.initiated = true;
810
+ await this._saveAndEmit();
811
+ }
812
+
813
+ return await this._contract.txsInit(
814
+ this._getInitiator(), this._data, this.signatureData, skipChecks, this.feeRate
815
+ ).catch(e => Promise.reject(e instanceof SignatureVerificationError ? new Error("Request timed out") : e));
816
+ }
817
+
818
+ /**
819
+ * @inheritDoc
820
+ *
821
+ * @throws {Error} If invalid signer is provided that doesn't match the swap data
822
+ */
823
+ async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
824
+ const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
825
+ this.checkSigner(signer);
826
+ const txs = await this.txsCommit(skipChecks);
827
+ let txCount = 0;
828
+ const result = await this.wrapper._chain.sendAndConfirm(
829
+ signer, txs, true, abortSignal, false, (txId, rawTx) => {
830
+ txCount++;
831
+ if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
832
+ return Promise.resolve();
833
+ }
834
+ );
835
+
836
+ this._commitTxId = result[result.length-1];
837
+ if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
838
+ await this._saveAndEmit(ToBTCSwapState.COMMITED);
839
+ }
840
+ return this._commitTxId;
841
+ }
842
+
843
+ /**
844
+ * @inheritDoc
845
+ *
846
+ * @throws {Error} If swap is not in the correct state (must be {@link ToBTCSwapState.CREATED})
847
+ */
848
+ async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
849
+ if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.CLAIMED) return Promise.resolve();
850
+ if(this._state!==ToBTCSwapState.CREATED && this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state (not CREATED)");
851
+
852
+ const abortController = extendAbortController(abortSignal);
853
+ let result: SwapCommitState | number | null;
854
+ try {
855
+ result = await Promise.race([
856
+ this.watchdogWaitTillCommited(undefined, abortController.signal),
857
+ this.waitTillState(ToBTCSwapState.COMMITED, "gte", abortController.signal).then(() => 0)
858
+ ]);
859
+ abortController.abort();
860
+ } catch (e) {
861
+ abortController.abort();
862
+ throw e;
863
+ }
864
+
865
+ if(result===0) {
866
+ this.logger.debug("waitTillCommited(): Resolved from state change");
867
+ } else if(result!=null) {
868
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
869
+ }
870
+
871
+ if(result===null) {
872
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expiry");
873
+ if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED) {
874
+ await this._saveAndEmit(ToBTCSwapState.QUOTE_EXPIRED);
875
+ }
876
+ throw new Error("Quote expired while waiting for transaction confirmation!");
877
+ }
878
+
879
+ if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
880
+ if(typeof(result)==="object" && (result as any).getInitTxId!=null && this._commitTxId==null)
881
+ this._commitTxId = await (result as any).getInitTxId();
882
+ await this._saveAndEmit(ToBTCSwapState.COMMITED);
883
+ }
884
+ }
885
+
886
+
887
+ //////////////////////////////
888
+ //// Payment
889
+
890
+ /**
891
+ * Waits till the swap is processed by the intermediary (LP)
892
+ *
893
+ * @param checkIntervalSeconds How often to poll the intermediary for status (5 seconds default)
894
+ * @param abortSignal Abort signal
895
+ * @internal
896
+ */
897
+ protected async waitTillIntermediarySwapProcessed(
898
+ checkIntervalSeconds?: number,
899
+ abortSignal?: AbortSignal
900
+ ): Promise<RefundAuthorizationResponse> {
901
+ if(this.url==null) throw new Error("LP URL not specified!");
902
+ checkIntervalSeconds ??= 5;
903
+ let resp: RefundAuthorizationResponse = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
904
+ while(!abortSignal?.aborted && (
905
+ resp.code===RefundAuthorizationResponseCodes.PENDING || resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
906
+ )) {
907
+ resp = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
908
+ if(resp.code===RefundAuthorizationResponseCodes.PAID) {
909
+ const validResponse = await this._setPaymentResult(resp.data, true);
910
+ if(validResponse) {
911
+ if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE) {
912
+ await this._saveAndEmit(ToBTCSwapState.SOFT_CLAIMED);
913
+ }
914
+ } else {
915
+ resp = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
916
+ }
917
+ }
918
+ if(
919
+ resp.code===RefundAuthorizationResponseCodes.PENDING ||
920
+ resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
921
+ ) await timeoutPromise(checkIntervalSeconds*1000, abortSignal);
922
+ }
923
+ return resp;
924
+ }
925
+
926
+ /**
927
+ * Checks whether the swap was already processed by the LP and is either successful (requires proof which is
928
+ * either a HTLC pre-image for LN swaps or valid txId for on-chain swap) or failed and we can cooperatively
929
+ * refund.
930
+ *
931
+ * @param save whether to save the data
932
+ * @returns `true` if swap is processed, `false` if the swap is still ongoing
933
+ *
934
+ * @internal
935
+ */
936
+ protected async checkIntermediarySwapProcessed(save: boolean = true): Promise<boolean> {
937
+ if(this._state===ToBTCSwapState.CREATED || this._state==ToBTCSwapState.QUOTE_EXPIRED || this.url==null) return false;
938
+ if(this.isFinished() || this.isRefundable()) return true;
939
+ //Check if that maybe already concluded according to the LP
940
+ const resp = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
941
+ switch(resp.code) {
942
+ case RefundAuthorizationResponseCodes.PAID:
943
+ const processed = await this._setPaymentResult(resp.data, true);
944
+ if(processed) {
945
+ this._state = ToBTCSwapState.SOFT_CLAIMED;
946
+ if(save) await this._saveAndEmit();
947
+ }
948
+ return processed;
949
+ case RefundAuthorizationResponseCodes.REFUND_DATA:
950
+ await this._contract.isValidRefundAuthorization(this._data, resp.data);
951
+ this._state = ToBTCSwapState.REFUNDABLE;
952
+ if(save) await this._saveAndEmit();
953
+ return true;
954
+ default:
955
+ return false;
956
+ }
957
+ }
958
+
959
+ /**
960
+ * A blocking promise resolving when swap was concluded by the intermediary (LP),
961
+ * rejecting in case of failure
962
+ *
963
+ * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled, an error is thrown if the
964
+ * swap is taking too long to claim
965
+ * @param checkIntervalSeconds How often to poll the intermediary for answer
966
+ * @param abortSignal Abort signal
967
+ * @returns `true` if swap was successful, `false` if swap failed and we can refund
968
+ *
969
+ * @throws {IntermediaryError} If a swap is determined expired by the intermediary, but it is actually still valid
970
+ * @throws {SignatureVerificationError} If the swap should be cooperatively refundable but the intermediary returned
971
+ * invalid refund signature
972
+ * @throws {Error} When swap expires or if the swap has invalid state (must be {@link ToBTCSwapState.COMMITED})
973
+ */
974
+ async waitForPayment(maxWaitTimeSeconds?: number, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
975
+ if(this._state===ToBTCSwapState.CLAIMED) return Promise.resolve(true);
976
+ if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.SOFT_CLAIMED) throw new Error("Invalid state (not COMMITED)");
977
+
978
+ const abortController = extendAbortController(abortSignal);
979
+
980
+ let timedOut: boolean = false;
981
+ if(maxWaitTimeSeconds!=null) {
982
+ const timeout = setTimeout(() => {
983
+ timedOut = true;
984
+ abortController.abort();
985
+ }, maxWaitTimeSeconds * 1000);
986
+ abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
987
+ }
988
+
989
+ let result: void | RefundAuthorizationResponse;
990
+ try {
991
+ result = await Promise.race([
992
+ this.waitTillState(ToBTCSwapState.CLAIMED, "gte", abortController.signal),
993
+ this.waitTillIntermediarySwapProcessed(checkIntervalSeconds, abortController.signal)
994
+ ]);
995
+ abortController.abort();
996
+ } catch (e) {
997
+ abortController.abort();
998
+ if(timedOut) {
999
+ throw new Error("Timed out while waiting for LP to process the swap, the LP might be unresponsive or offline!" +
1000
+ ` Please check later or wait till ${new Date(Number(this._data.getExpiry())*1000).toLocaleString()} to refund unilaterally!`);
1001
+ }
1002
+ throw e;
1003
+ }
1004
+
1005
+ if(typeof result !== "object") {
1006
+ if((this._state as ToBTCSwapState)===ToBTCSwapState.REFUNDABLE) throw new Error("Swap expired");
1007
+ this.logger.debug("waitTillRefunded(): Resolved from state change");
1008
+ return true;
1009
+ }
1010
+ this.logger.debug("waitTillRefunded(): Resolved from intermediary response");
1011
+
1012
+ switch(result.code) {
1013
+ case RefundAuthorizationResponseCodes.PAID:
1014
+ return true;
1015
+ case RefundAuthorizationResponseCodes.REFUND_DATA:
1016
+ const resultData = result.data;
1017
+ await this._contract.isValidRefundAuthorization(
1018
+ this._data,
1019
+ resultData
1020
+ );
1021
+ await this._saveAndEmit(ToBTCSwapState.REFUNDABLE);
1022
+ return false;
1023
+ case RefundAuthorizationResponseCodes.EXPIRED:
1024
+ if(await this._contract.isExpired(this._getInitiator(), this._data)) throw new Error("Swap expired");
1025
+ throw new IntermediaryError("Swap expired");
1026
+ case RefundAuthorizationResponseCodes.NOT_FOUND:
1027
+ if((this._state as ToBTCSwapState)===ToBTCSwapState.CLAIMED) return true;
1028
+ throw new Error("LP swap not found");
1029
+ }
1030
+
1031
+ throw new Error("Invalid response code returned by the LP");
1032
+ }
1033
+
1034
+
1035
+ //////////////////////////////
1036
+ //// Refund
1037
+
1038
+ /**
1039
+ * Get the estimated smart chain transaction fee of the refund transaction
1040
+ */
1041
+ async getRefundNetworkFee(): Promise<TokenAmount<SCToken<T["ChainId"]>, true>> {
1042
+ const swapContract: T["Contract"] = this._contract;
1043
+ return toTokenAmount(
1044
+ await swapContract.getRefundFee(this._getInitiator(), this._data),
1045
+ this.wrapper._getNativeToken(),
1046
+ this.wrapper._prices
1047
+ );
1048
+ }
1049
+
1050
+ /**
1051
+ * @inheritDoc
1052
+ *
1053
+ * @throws {IntermediaryError} If intermediary returns invalid response in case cooperative refund should be used
1054
+ * @throws {SignatureVerificationError} If intermediary returned invalid cooperative refund signature
1055
+ * @throws {Error} When state is not refundable
1056
+ */
1057
+ async txsRefund(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
1058
+ if(!this.isRefundable()) throw new Error("Must be in REFUNDABLE state or expired!");
1059
+
1060
+ let signer: string;
1061
+ if(_signer!=null) {
1062
+ if (typeof (_signer) === "string") {
1063
+ signer = _signer;
1064
+ } else if (isAbstractSigner(_signer)) {
1065
+ signer = _signer.getAddress();
1066
+ } else {
1067
+ signer = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
1068
+ }
1069
+ } else {
1070
+ signer = this._getInitiator();
1071
+ }
1072
+
1073
+ if(await this._contract.isExpired(this._getInitiator(), this._data)) {
1074
+ return await this._contract.txsRefund(signer, this._data, true, true);
1075
+ } else {
1076
+ if(this.url==null) throw new Error("LP URL not known, cannot get cooperative refund message, wait till expiry to refund!");
1077
+ const res = await this.wrapper._lpApi.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
1078
+ if(res.code===RefundAuthorizationResponseCodes.REFUND_DATA) {
1079
+ return await this._contract.txsRefundWithAuthorization(
1080
+ signer,
1081
+ this._data,
1082
+ res.data,
1083
+ true,
1084
+ true
1085
+ );
1086
+ }
1087
+ throw new IntermediaryError("Invalid intermediary cooperative message returned");
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * @inheritDoc
1093
+ *
1094
+ * @throws {Error} If invalid signer is provided that doesn't match the swap data
1095
+ */
1096
+ async refund(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal): Promise<string> {
1097
+ const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1098
+ const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsRefund(signer.getAddress()), true, abortSignal)
1099
+
1100
+ this._refundTxId = result[0];
1101
+ if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE || this._state===ToBTCSwapState.SOFT_CLAIMED) {
1102
+ await this._saveAndEmit(ToBTCSwapState.REFUNDED);
1103
+ }
1104
+ return result[0];
1105
+ }
1106
+
1107
+ /**
1108
+ * @inheritDoc
1109
+ *
1110
+ * @throws {Error} When swap is not in a valid state (must be {@link ToBTCSwapState.COMMITED} or
1111
+ * {@link ToBTCSwapState.REFUNDABLE})
1112
+ * @throws {Error} If we tried to refund but claimer was able to claim first
1113
+ */
1114
+ async waitTillRefunded(abortSignal?: AbortSignal): Promise<void> {
1115
+ if(this._state===ToBTCSwapState.REFUNDED) return Promise.resolve();
1116
+ if(
1117
+ this._state!==ToBTCSwapState.COMMITED &&
1118
+ this._state!==ToBTCSwapState.SOFT_CLAIMED &&
1119
+ this._state!==ToBTCSwapState.REFUNDABLE
1120
+ ) throw new Error("Invalid state (not COMMITED)");
1121
+
1122
+ const abortController = new AbortController();
1123
+ if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
1124
+ const res = await Promise.race([
1125
+ this.watchdogWaitTillResult(undefined, abortController.signal),
1126
+ this.waitTillState(ToBTCSwapState.REFUNDED, "eq", abortController.signal).then(() => 0 as const),
1127
+ this.waitTillState(ToBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 1 as const),
1128
+ ]);
1129
+ abortController.abort();
1130
+
1131
+ if(res===0) {
1132
+ this.logger.debug("waitTillRefunded(): Resolved from state change (REFUNDED)");
1133
+ return;
1134
+ }
1135
+ if(res===1) {
1136
+ this.logger.debug("waitTillRefunded(): Resolved from state change (CLAIMED)");
1137
+ throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
1138
+ }
1139
+ this.logger.debug("waitTillRefunded(): Resolved from watchdog");
1140
+
1141
+ if(res?.type===SwapCommitStateType.PAID) {
1142
+ if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
1143
+ await this._saveAndEmit(ToBTCSwapState.CLAIMED);
1144
+ throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
1145
+ }
1146
+ if(res?.type===SwapCommitStateType.NOT_COMMITED) {
1147
+ if(this._refundTxId==null && res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
1148
+ await this._saveAndEmit(ToBTCSwapState.REFUNDED);
1149
+ }
1150
+ }
1151
+
1152
+
1153
+ //////////////////////////////
1154
+ //// Storage
1155
+
1156
+ /**
1157
+ * @inheritDoc
1158
+ */
1159
+ serialize(): any {
1160
+ const obj = super.serialize();
1161
+ return {
1162
+ ...obj,
1163
+ networkFee: this.networkFee==null ? null : this.networkFee.toString(10),
1164
+ networkFeeBtc: this.networkFeeBtc==null ? null : this.networkFeeBtc.toString(10)
1165
+ };
1166
+ }
1167
+
1168
+
1169
+ //////////////////////////////
1170
+ //// Swap ticks & sync
1171
+
1172
+ /**
1173
+ * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1174
+ * data
1175
+ *
1176
+ * @private
1177
+ */
1178
+ private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
1179
+ if(
1180
+ this._state===ToBTCSwapState.CREATED ||
1181
+ this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
1182
+ this._state===ToBTCSwapState.COMMITED ||
1183
+ this._state===ToBTCSwapState.SOFT_CLAIMED ||
1184
+ this._state===ToBTCSwapState.REFUNDABLE
1185
+ ) {
1186
+ let quoteExpired = false;
1187
+ if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
1188
+ //Check if quote is still valid
1189
+ quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
1190
+ }
1191
+
1192
+ commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
1193
+ if(commitStatus!=null && await this._forciblySetOnchainState(commitStatus)) return true;
1194
+
1195
+ if((this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED)) {
1196
+ if(quoteExpired) {
1197
+ this._state = ToBTCSwapState.QUOTE_EXPIRED;
1198
+ return true;
1199
+ }
1200
+ }
1201
+ }
1202
+ return false;
1203
+ }
1204
+
1205
+ /**
1206
+ * @inheritDoc
1207
+ * @internal
1208
+ */
1209
+ _shouldFetchOnchainState(): boolean {
1210
+ return this._state===ToBTCSwapState.CREATED ||
1211
+ this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
1212
+ this._state===ToBTCSwapState.COMMITED ||
1213
+ this._state===ToBTCSwapState.SOFT_CLAIMED ||
1214
+ this._state===ToBTCSwapState.REFUNDABLE;
1215
+ }
1216
+
1217
+ /**
1218
+ * @inheritDoc
1219
+ * @internal
1220
+ */
1221
+ _shouldFetchExpiryStatus(): boolean {
1222
+ return this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
1223
+ }
1224
+
1225
+ /**
1226
+ * @inheritDoc
1227
+ * @internal
1228
+ */
1229
+ async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
1230
+ let changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
1231
+
1232
+ if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED) {
1233
+ //Check if that maybe already concluded
1234
+ try {
1235
+ if(await this.checkIntermediarySwapProcessed(false)) changed = true;
1236
+ } catch (e) {
1237
+ this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
1238
+ }
1239
+ }
1240
+
1241
+ if(save && changed) await this._saveAndEmit();
1242
+
1243
+ return changed;
1244
+ }
1245
+
1246
+ /**
1247
+ * @inheritDoc
1248
+ * @internal
1249
+ */
1250
+ async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
1251
+ switch(commitStatus.type) {
1252
+ case SwapCommitStateType.PAID:
1253
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1254
+ if(this._claimTxId==null && commitStatus.getClaimTxId) this._claimTxId = await commitStatus.getClaimTxId();
1255
+ const eventResult = await commitStatus.getClaimResult();
1256
+ try {
1257
+ await this._setPaymentResult({secret: eventResult, txId: Buffer.from(eventResult, "hex").reverse().toString("hex")});
1258
+ } catch (e) {
1259
+ this.logger.error(`Failed to set payment result ${eventResult} on the swap!`);
1260
+ }
1261
+ this._state = ToBTCSwapState.CLAIMED;
1262
+ return true;
1263
+ case SwapCommitStateType.REFUNDABLE:
1264
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1265
+ this._state = ToBTCSwapState.REFUNDABLE;
1266
+ return true;
1267
+ case SwapCommitStateType.EXPIRED:
1268
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1269
+ if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
1270
+ this._state = this._refundTxId==null ? ToBTCSwapState.QUOTE_EXPIRED : ToBTCSwapState.REFUNDED;
1271
+ return true;
1272
+ case SwapCommitStateType.NOT_COMMITED:
1273
+ let changed: boolean = false;
1274
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1275
+ this._commitTxId = await (commitStatus as any).getInitTxId();
1276
+ changed = true;
1277
+ }
1278
+ if(this._refundTxId==null && commitStatus.getRefundTxId) {
1279
+ this._refundTxId = await commitStatus.getRefundTxId();
1280
+ changed = true;
1281
+ }
1282
+ if(this._refundTxId!=null) {
1283
+ this._state = ToBTCSwapState.REFUNDED;
1284
+ changed = true;
1285
+ }
1286
+ return changed;
1287
+ case SwapCommitStateType.COMMITED:
1288
+ let save: boolean = false;
1289
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1290
+ this._commitTxId = await (commitStatus as any).getInitTxId();
1291
+ save = true;
1292
+ }
1293
+ if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.REFUNDABLE && this._state!==ToBTCSwapState.SOFT_CLAIMED) {
1294
+ this._state = ToBTCSwapState.COMMITED;
1295
+ save = true;
1296
+ }
1297
+ return save;
1298
+ }
1299
+ return false;
1300
+ }
1301
+
1302
+ /**
1303
+ * @inheritDoc
1304
+ * @internal
1305
+ */
1306
+ async _tick(save?: boolean): Promise<boolean> {
1307
+ switch(this._state) {
1308
+ case ToBTCSwapState.CREATED:
1309
+ if(this.expiry<Date.now()) {
1310
+ this._state = ToBTCSwapState.QUOTE_SOFT_EXPIRED;
1311
+ if(save) await this._saveAndEmit();
1312
+ return true;
1313
+ }
1314
+ break;
1315
+ case ToBTCSwapState.COMMITED:
1316
+ case ToBTCSwapState.SOFT_CLAIMED:
1317
+ const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1318
+ if(expired) {
1319
+ this._state = ToBTCSwapState.REFUNDABLE;
1320
+ if(save) await this._saveAndEmit();
1321
+ return true;
1322
+ }
1323
+ break;
1324
+ }
1325
+ return false;
1326
+ }
1327
+ }