@atomiqlabs/sdk 8.9.1 → 8.9.3

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 +770 -770
  120. package/dist/swapper/Swapper.js +1758 -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 +2570 -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,1577 +1,1577 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FromBTCLNAutoSwap = exports.isFromBTCLNAutoSwapInit = exports.FromBTCLNAutoSwapState = void 0;
4
- const bolt11_1 = require("@atomiqlabs/bolt11");
5
- const SwapType_1 = require("../../../../enums/SwapType");
6
- const base_1 = require("@atomiqlabs/base");
7
- const buffer_1 = require("buffer");
8
- const LNURL_1 = require("../../../../lnurl/LNURL");
9
- const UserError_1 = require("../../../../errors/UserError");
10
- const IntermediaryAPI_1 = require("../../../../intermediaries/apis/IntermediaryAPI");
11
- const IntermediaryError_1 = require("../../../../errors/IntermediaryError");
12
- const Utils_1 = require("../../../../utils/Utils");
13
- const IEscrowSwap_1 = require("../../IEscrowSwap");
14
- const FeeType_1 = require("../../../../enums/FeeType");
15
- const PercentagePPM_1 = require("../../../../types/fees/PercentagePPM");
16
- const TokenAmount_1 = require("../../../../types/TokenAmount");
17
- const Token_1 = require("../../../../types/Token");
18
- const Logger_1 = require("../../../../utils/Logger");
19
- const TimeoutUtils_1 = require("../../../../utils/TimeoutUtils");
20
- const LNURLWithdraw_1 = require("../../../../types/lnurl/LNURLWithdraw");
21
- const PriceInfoType_1 = require("../../../../types/PriceInfoType");
22
- const sha2_1 = require("@noble/hashes/sha2");
23
- /**
24
- * State enum for FromBTCLNAuto swaps
25
- * @category Swaps/Lightning → Smart chain
26
- */
27
- var FromBTCLNAutoSwapState;
28
- (function (FromBTCLNAutoSwapState) {
29
- /**
30
- * Swap has failed as the user didn't settle the HTLC on the destination before expiration
31
- */
32
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["FAILED"] = -4] = "FAILED";
33
- /**
34
- * Swap has expired for good and there is no way how it can be executed anymore
35
- */
36
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["QUOTE_EXPIRED"] = -3] = "QUOTE_EXPIRED";
37
- /**
38
- * A swap is almost expired, and it should be presented to the user as expired, though
39
- * there is still a chance that it will be processed
40
- */
41
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["QUOTE_SOFT_EXPIRED"] = -2] = "QUOTE_SOFT_EXPIRED";
42
- /**
43
- * Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the
44
- * swap on the destination smart chain.
45
- */
46
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["EXPIRED"] = -1] = "EXPIRED";
47
- /**
48
- * Swap quote was created, use {@link FromBTCLNAutoSwap.getAddress} or {@link FromBTCLNAutoSwap.getHyperlink}
49
- * to get the bolt11 lightning network invoice to pay to initiate the swap, then use the
50
- * {@link FromBTCLNAutoSwap.waitForPayment} to wait till the lightning network payment is received
51
- * by the intermediary (LP) and the destination HTLC escrow is created
52
- */
53
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["PR_CREATED"] = 0] = "PR_CREATED";
54
- /**
55
- * Lightning network payment has been received by the intermediary (LP), but the destination chain
56
- * HTLC escrow hasn't been created yet. Use {@link FromBTCLNAutoSwap.waitForPayment} to continue waiting
57
- * till the destination HTLC escrow is created.
58
- */
59
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["PR_PAID"] = 1] = "PR_PAID";
60
- /**
61
- * Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers
62
- * using the {@link FromBTCLNAutoSwap.waitTillClaimed} function or settle manually using the
63
- * {@link FromBTCLNAutoSwap.claim} or {@link FromBTCLNAutoSwap.txsClaim} function.
64
- */
65
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["CLAIM_COMMITED"] = 2] = "CLAIM_COMMITED";
66
- /**
67
- * Swap successfully settled and funds received on the destination chain
68
- */
69
- FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
70
- })(FromBTCLNAutoSwapState = exports.FromBTCLNAutoSwapState || (exports.FromBTCLNAutoSwapState = {}));
71
- const FromBTCLNAutoSwapStateDescription = {
72
- [FromBTCLNAutoSwapState.FAILED]: "Swap has failed as the user didn't settle the HTLC on the destination before expiration",
73
- [FromBTCLNAutoSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
74
- [FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
75
- [FromBTCLNAutoSwapState.EXPIRED]: "Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the swap on the destination smart chain.",
76
- [FromBTCLNAutoSwapState.PR_CREATED]: "Swap quote was created, pay the bolt11 lightning network invoice to initiate the swap, then wait till the lightning network payment is received by the intermediary (LP) and the destination HTLC escrow is created",
77
- [FromBTCLNAutoSwapState.PR_PAID]: "Lightning network payment has been received by the intermediary (LP), but the destination chain HTLC escrow hasn't been created yet. Continue waiting till the destination HTLC escrow is created.",
78
- [FromBTCLNAutoSwapState.CLAIM_COMMITED]: "Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers or settle manually.",
79
- [FromBTCLNAutoSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
80
- };
81
- function isFromBTCLNAutoSwapInit(obj) {
82
- return (obj.pr == null || typeof obj.pr === "string") &&
83
- (obj.secret == null || typeof obj.secret === "string") &&
84
- (obj.btcAmountSwap == null || typeof obj.btcAmountSwap === "bigint") &&
85
- (obj.btcAmountGas == null || typeof obj.btcAmountGas === "bigint") &&
86
- typeof obj.gasSwapFeeBtc === "bigint" &&
87
- typeof obj.gasSwapFee === "bigint" &&
88
- (obj.gasPricingInfo == null || (0, PriceInfoType_1.isPriceInfoType)(obj.gasPricingInfo)) &&
89
- (obj.lnurl == null || typeof (obj.lnurl) === "string") &&
90
- (obj.lnurlK1 == null || typeof (obj.lnurlK1) === "string") &&
91
- (obj.lnurlCallback == null || typeof (obj.lnurlCallback) === "string") &&
92
- (0, IEscrowSwap_1.isIEscrowSwapInit)(obj);
93
- }
94
- exports.isFromBTCLNAutoSwapInit = isFromBTCLNAutoSwapInit;
95
- /**
96
- * New escrow based (HTLC) swaps for Bitcoin Lightning -> Smart chain swaps not requiring manual settlement on
97
- * the destination by the user, and instead letting the LP initiate the escrow. Permissionless watchtower network
98
- * handles the claiming of HTLC, with the swap secret broadcasted over Nostr. Also adds a possibility for the user
99
- * to receive a native token on the destination chain as part of the swap (a "gas drop" feature).
100
- *
101
- * @category Swaps/Lightning → Smart chain
102
- */
103
- class FromBTCLNAutoSwap extends IEscrowSwap_1.IEscrowSwap {
104
- /**
105
- * Sets the LNURL data for the swap
106
- *
107
- * @internal
108
- */
109
- _setLNURLData(lnurl, lnurlK1, lnurlCallback) {
110
- this.lnurl = lnurl;
111
- this.lnurlK1 = lnurlK1;
112
- this.lnurlCallback = lnurlCallback;
113
- }
114
- constructor(wrapper, initOrObject) {
115
- if (isFromBTCLNAutoSwapInit(initOrObject) && initOrObject.url != null)
116
- initOrObject.url += "/frombtcln_auto";
117
- super(wrapper, initOrObject);
118
- this.TYPE = SwapType_1.SwapType.FROM_BTCLN_AUTO;
119
- /**
120
- * @internal
121
- */
122
- this.swapStateName = (state) => FromBTCLNAutoSwapState[state];
123
- /**
124
- * @internal
125
- */
126
- this.swapStateDescription = FromBTCLNAutoSwapStateDescription;
127
- /**
128
- * @internal
129
- */
130
- this.inputToken = Token_1.BitcoinTokens.BTCLN;
131
- this.lnurlFailSignal = new AbortController();
132
- this.prPosted = false;
133
- this.broadcastTickCounter = 0;
134
- if (isFromBTCLNAutoSwapInit(initOrObject)) {
135
- this._state = FromBTCLNAutoSwapState.PR_CREATED;
136
- this.pr = initOrObject.pr;
137
- this.secret = initOrObject.secret;
138
- this.initialSwapData = initOrObject.initialSwapData;
139
- this.btcAmountSwap = initOrObject.btcAmountSwap;
140
- this.btcAmountGas = initOrObject.btcAmountGas;
141
- this.gasSwapFeeBtc = initOrObject.gasSwapFeeBtc;
142
- this.gasSwapFee = initOrObject.gasSwapFee;
143
- this.gasPricingInfo = initOrObject.gasPricingInfo;
144
- this.lnurl = initOrObject.lnurl;
145
- this.lnurlK1 = initOrObject.lnurlK1;
146
- this.lnurlCallback = initOrObject.lnurlCallback;
147
- this.usesClaimHashAsId = true;
148
- }
149
- else {
150
- this.pr = initOrObject.pr;
151
- this.secret = initOrObject.secret;
152
- if (initOrObject.initialSwapData == null) {
153
- this.initialSwapData = this._data;
154
- }
155
- else {
156
- this.initialSwapData = base_1.SwapData.deserialize(initOrObject.initialSwapData);
157
- }
158
- this.btcAmountSwap = (0, Utils_1.toBigInt)(initOrObject.btcAmountSwap);
159
- this.btcAmountGas = (0, Utils_1.toBigInt)(initOrObject.btcAmountGas);
160
- this.gasSwapFeeBtc = (0, Utils_1.toBigInt)(initOrObject.gasSwapFeeBtc);
161
- this.gasSwapFee = (0, Utils_1.toBigInt)(initOrObject.gasSwapFee);
162
- this.gasPricingInfo = (0, PriceInfoType_1.deserializePriceInfoType)(initOrObject.gasPricingInfo);
163
- this._commitTxId = initOrObject.commitTxId;
164
- this._claimTxId = initOrObject.claimTxId;
165
- this._commitedAt = initOrObject.commitedAt;
166
- this.lnurl = initOrObject.lnurl;
167
- this.lnurlK1 = initOrObject.lnurlK1;
168
- this.lnurlCallback = initOrObject.lnurlCallback;
169
- this.prPosted = initOrObject.prPosted;
170
- this.usesClaimHashAsId = initOrObject.usesClaimHashAsId ?? false;
171
- }
172
- this.tryRecomputeSwapPrice();
173
- this.logger = (0, Logger_1.getLogger)("FromBTCLNAuto(" + this.getIdentifierHashString() + "): ");
174
- }
175
- /**
176
- * @inheritDoc
177
- * @internal
178
- */
179
- getSwapData() {
180
- return this._data ?? this.initialSwapData;
181
- }
182
- /**
183
- * @inheritDoc
184
- * @internal
185
- */
186
- upgradeVersion() { }
187
- /**
188
- * @inheritDoc
189
- * @internal
190
- */
191
- tryRecomputeSwapPrice() {
192
- if (this.pricingInfo == null || this.btcAmountSwap == null)
193
- return;
194
- if (this.pricingInfo.swapPriceUSatPerToken == null) {
195
- const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
196
- this.pricingInfo = this.wrapper._prices.recomputePriceInfoReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken());
197
- this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
198
- }
199
- }
200
- //////////////////////////////
201
- //// Pricing
202
- /**
203
- * @inheritDoc
204
- */
205
- async refreshPriceData() {
206
- if (this.pricingInfo == null || this.btcAmountSwap == null)
207
- return;
208
- const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
209
- this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken(), undefined, undefined, this.swapFeeBtc);
210
- this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
211
- }
212
- //////////////////////////////
213
- //// Getters & utils
214
- /**
215
- * @inheritDoc
216
- * @internal
217
- */
218
- _getEscrowHash() {
219
- //Use claim hash in case the data is not yet known
220
- return this._data == null ? this.initialSwapData?.getClaimHash() : this._data?.getEscrowHash();
221
- }
222
- /**
223
- * @inheritDoc
224
- * @internal
225
- */
226
- _getInitiator() {
227
- return this.getSwapData().getClaimer();
228
- }
229
- /**
230
- * @inheritDoc
231
- */
232
- getId() {
233
- return this.getIdentifierHashString();
234
- }
235
- /**
236
- * @inheritDoc
237
- */
238
- getOutputAddress() {
239
- return this._getInitiator();
240
- }
241
- /**
242
- * @inheritDoc
243
- */
244
- getOutputTxId() {
245
- return this._claimTxId ?? null;
246
- }
247
- /**
248
- * @inheritDoc
249
- */
250
- requiresAction() {
251
- return this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
252
- }
253
- /**
254
- * @inheritDoc
255
- * @internal
256
- */
257
- getIdentifierHashString() {
258
- const id = this.usesClaimHashAsId
259
- ? this.getClaimHash()
260
- : this.getPaymentHash().toString("hex");
261
- if (this._randomNonce == null)
262
- return id;
263
- return id + this._randomNonce;
264
- }
265
- /**
266
- * Returns the payment hash of the swap and lightning network invoice, or `null` if not known (i.e. if
267
- * the swap was recovered from on-chain data, the payment hash might not be known)
268
- *
269
- * @internal
270
- */
271
- getPaymentHash() {
272
- if (this.pr == null)
273
- return null;
274
- if (this.pr.toLowerCase().startsWith("ln")) {
275
- const parsed = (0, bolt11_1.decode)(this.pr);
276
- if (parsed.tagsObject.payment_hash == null)
277
- throw new Error("Swap invoice has no payment hash field!");
278
- return buffer_1.Buffer.from(parsed.tagsObject.payment_hash, "hex");
279
- }
280
- return buffer_1.Buffer.from(this.pr, "hex");
281
- }
282
- /**
283
- * @inheritDoc
284
- */
285
- getInputAddress() {
286
- return this.lnurl ?? this.pr ?? null;
287
- }
288
- /**
289
- * @inheritDoc
290
- */
291
- getInputTxId() {
292
- const paymentHash = this.getPaymentHash();
293
- if (paymentHash == null)
294
- return null;
295
- return paymentHash.toString("hex");
296
- }
297
- /**
298
- * Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap
299
- */
300
- getAddress() {
301
- return this.pr ?? "";
302
- }
303
- /**
304
- * @inheritDoc
305
- */
306
- getHyperlink() {
307
- return this.pr == null ? "" : "lightning:" + this.pr.toUpperCase();
308
- }
309
- /**
310
- * Returns the timeout time (in UNIX milliseconds) when the swap will definitelly be considered as expired
311
- * if the LP doesn't make it expired sooner
312
- */
313
- getDefinitiveExpiryTime() {
314
- if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
315
- return 0;
316
- const decoded = (0, bolt11_1.decode)(this.pr);
317
- if (decoded.tagsObject.min_final_cltv_expiry == null)
318
- throw new Error("Swap invoice doesn't contain final ctlv delta field!");
319
- if (decoded.timeExpireDate == null)
320
- throw new Error("Swap invoice doesn't contain expiry date field!");
321
- const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
322
- const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper._options.bitcoinBlocktime * this.wrapper._options.safetyFactor;
323
- return (decoded.timeExpireDate + finalCltvExpiryDelay) * 1000;
324
- }
325
- /**
326
- * Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
327
- */
328
- getHtlcTimeoutTime() {
329
- return this._data == null ? null : Number(this.wrapper._getHtlcTimeout(this._data)) * 1000;
330
- }
331
- /**
332
- * @inheritDoc
333
- */
334
- isFinished() {
335
- return this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED || this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state === FromBTCLNAutoSwapState.FAILED;
336
- }
337
- /**
338
- * @inheritDoc
339
- */
340
- isClaimable() {
341
- return this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
342
- }
343
- /**
344
- * @inheritDoc
345
- */
346
- isSuccessful() {
347
- return this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED;
348
- }
349
- /**
350
- * @inheritDoc
351
- */
352
- isFailed() {
353
- return this._state === FromBTCLNAutoSwapState.FAILED || this._state === FromBTCLNAutoSwapState.EXPIRED;
354
- }
355
- /**
356
- * @inheritDoc
357
- */
358
- isInProgress() {
359
- return (this._state === FromBTCLNAutoSwapState.PR_CREATED && this.initiated) ||
360
- (this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.initiated) ||
361
- this._state === FromBTCLNAutoSwapState.PR_PAID ||
362
- this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
363
- }
364
- /**
365
- * @inheritDoc
366
- */
367
- isQuoteExpired() {
368
- return this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED;
369
- }
370
- /**
371
- * @inheritDoc
372
- */
373
- isQuoteSoftExpired() {
374
- return this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED;
375
- }
376
- /**
377
- * @inheritDoc
378
- */
379
- _verifyQuoteDefinitelyExpired() {
380
- return Promise.resolve(this.getDefinitiveExpiryTime() < Date.now());
381
- }
382
- /**
383
- * @inheritDoc
384
- */
385
- _verifyQuoteValid() {
386
- return Promise.resolve(this.getQuoteExpiry() > Date.now());
387
- }
388
- //////////////////////////////
389
- //// Amounts & fees
390
- /**
391
- * Returns the satoshi amount of the lightning network invoice, or `null` if the lightning network
392
- * invoice is not known (i.e. when the swap was recovered from on-chain data, the paid invoice
393
- * cannot be recovered because it is purely off-chain)
394
- *
395
- * @internal
396
- */
397
- getLightningInvoiceSats() {
398
- if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
399
- return null;
400
- const parsed = (0, bolt11_1.decode)(this.pr);
401
- if (parsed.millisatoshis == null)
402
- throw new Error("Swap invoice doesn't contain msat amount field!");
403
- return (BigInt(parsed.millisatoshis) + 999n) / 1000n;
404
- }
405
- /**
406
- * Returns the watchtower fee paid in BTC satoshis, or null if known (i.e. if the swap was recovered from
407
- * on-chain data)
408
- *
409
- * @protected
410
- */
411
- getWatchtowerFeeAmountBtc() {
412
- if (this.btcAmountGas == null)
413
- return null;
414
- return (this.btcAmountGas - this.gasSwapFeeBtc) * this.getSwapData().getClaimerBounty() / this.getSwapData().getTotalDeposit();
415
- }
416
- /**
417
- * Returns the input amount for the actual swap (excluding the input amount used to cover the "gas drop"
418
- * part of the swap), excluding fees
419
- *
420
- * @internal
421
- */
422
- getInputSwapAmountWithoutFee() {
423
- if (this.btcAmountSwap == null)
424
- return null;
425
- return this.btcAmountSwap - this.swapFeeBtc;
426
- }
427
- /**
428
- * Returns the input amount purely for the "gas drop" part of the swap (this much BTC in sats will be
429
- * swapped into the native gas token on the destination chain), excluding fees
430
- *
431
- * @internal
432
- */
433
- getInputGasAmountWithoutFee() {
434
- if (this.btcAmountGas == null)
435
- return null;
436
- return this.btcAmountGas - this.gasSwapFeeBtc;
437
- }
438
- /**
439
- * Get total btc amount in sats on the input, excluding the swap fee and watchtower fee
440
- *
441
- * @internal
442
- */
443
- getInputAmountWithoutFee() {
444
- if (this.btcAmountGas == null || this.btcAmountSwap == null)
445
- return null;
446
- return this.getInputSwapAmountWithoutFee() + this.getInputGasAmountWithoutFee() - this.getWatchtowerFeeAmountBtc();
447
- }
448
- /**
449
- * Returns the "would be" output amount if the swap charged no swap fee
450
- *
451
- * @internal
452
- */
453
- getOutputAmountWithoutFee() {
454
- return this.getSwapData().getAmount() + this.swapFee;
455
- }
456
- /**
457
- * @inheritDoc
458
- */
459
- getInputToken() {
460
- return Token_1.BitcoinTokens.BTCLN;
461
- }
462
- /**
463
- * @inheritDoc
464
- */
465
- getInput() {
466
- return (0, TokenAmount_1.toTokenAmount)(this.getLightningInvoiceSats(), this.inputToken, this.wrapper._prices, this.pricingInfo);
467
- }
468
- /**
469
- * @inheritDoc
470
- */
471
- getInputWithoutFee() {
472
- return (0, TokenAmount_1.toTokenAmount)(this.getInputAmountWithoutFee(), this.inputToken, this.wrapper._prices, this.pricingInfo);
473
- }
474
- /**
475
- * @inheritDoc
476
- */
477
- getOutputToken() {
478
- return this.wrapper._tokens[this.getSwapData().getToken()];
479
- }
480
- /**
481
- * @inheritDoc
482
- */
483
- getOutput() {
484
- return (0, TokenAmount_1.toTokenAmount)(this.getSwapData().getAmount(), this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo);
485
- }
486
- /**
487
- * @inheritDoc
488
- */
489
- getGasDropOutput() {
490
- return (0, TokenAmount_1.toTokenAmount)(this.getSwapData().getSecurityDeposit() - this.getSwapData().getClaimerBounty(), this.wrapper._tokens[this.getSwapData().getDepositToken()], this.wrapper._prices, this.gasPricingInfo);
491
- }
492
- /**
493
- * Returns the swap fee charged by the intermediary (LP) on this swap
494
- *
495
- * @internal
496
- */
497
- getSwapFee() {
498
- if (this.pricingInfo == null)
499
- throw new Error("No pricing info known, cannot estimate fee!");
500
- const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
501
- const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
502
- * (10n ** BigInt(outputToken.decimals))
503
- * 1000000n
504
- / this.pricingInfo.swapPriceUSatPerToken;
505
- const feeWithoutBaseFee = this.gasSwapFeeBtc + this.swapFeeBtc - this.pricingInfo.satsBaseFee;
506
- const inputSats = this.getLightningInvoiceSats();
507
- const swapFeePPM = inputSats != null
508
- ? feeWithoutBaseFee * 1000000n / (inputSats - this.swapFeeBtc - this.gasSwapFeeBtc)
509
- : 0n;
510
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(this.swapFeeBtc + this.gasSwapFeeBtc, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
511
- return {
512
- amountInSrcToken,
513
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
514
- currentUsdValue: amountInSrcToken.currentUsdValue,
515
- pastUsdValue: amountInSrcToken.pastUsdValue,
516
- usdValue: amountInSrcToken.usdValue,
517
- composition: {
518
- base: (0, TokenAmount_1.toTokenAmount)(this.pricingInfo.satsBaseFee, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo),
519
- percentage: (0, PercentagePPM_1.ppmToPercentage)(swapFeePPM)
520
- }
521
- };
522
- }
523
- /**
524
- * Returns the fee to be paid to watchtowers on the destination chain to automatically
525
- * process and settle this swap without requiring any user interaction
526
- *
527
- * @internal
528
- */
529
- getWatchtowerFee() {
530
- if (this.pricingInfo == null)
531
- throw new Error("No pricing info known, cannot estimate fee!");
532
- const btcWatchtowerFee = this.getWatchtowerFeeAmountBtc();
533
- const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
534
- const watchtowerFeeInOutputToken = btcWatchtowerFee == null ? 0n : btcWatchtowerFee
535
- * (10n ** BigInt(outputToken.decimals))
536
- * 1000000n
537
- / this.pricingInfo.swapPriceUSatPerToken;
538
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(btcWatchtowerFee, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
539
- return {
540
- amountInSrcToken,
541
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)(watchtowerFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
542
- currentUsdValue: amountInSrcToken.currentUsdValue,
543
- usdValue: amountInSrcToken.usdValue,
544
- pastUsdValue: amountInSrcToken.pastUsdValue
545
- };
546
- }
547
- /**
548
- * @inheritDoc
549
- */
550
- getFee() {
551
- const swapFee = this.getSwapFee();
552
- const watchtowerFee = this.getWatchtowerFee();
553
- const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
554
- return {
555
- amountInSrcToken,
556
- amountInDstToken: (0, TokenAmount_1.toTokenAmount)(swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount, this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo),
557
- currentUsdValue: amountInSrcToken.currentUsdValue,
558
- usdValue: amountInSrcToken.usdValue,
559
- pastUsdValue: amountInSrcToken.pastUsdValue
560
- };
561
- }
562
- /**
563
- * @inheritDoc
564
- */
565
- getFeeBreakdown() {
566
- return [
567
- {
568
- type: FeeType_1.FeeType.SWAP,
569
- fee: this.getSwapFee()
570
- },
571
- {
572
- type: FeeType_1.FeeType.NETWORK_OUTPUT,
573
- fee: this.getWatchtowerFee()
574
- }
575
- ];
576
- }
577
- isValidSecretPreimage(secret) {
578
- const paymentHash = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex")));
579
- const claimHash = this._contract.getHashForHtlc(paymentHash).toString("hex");
580
- return this.getSwapData().getClaimHash() === claimHash;
581
- }
582
- /**
583
- * Sets the secret preimage for the swap, in case it is not known already
584
- *
585
- * @param secret Secret preimage that matches the expected payment hash
586
- *
587
- * @throws {Error} If an invalid secret preimage is provided
588
- */
589
- async setSecretPreimage(secret) {
590
- if (!this.isValidSecretPreimage(secret))
591
- throw new Error("Invalid secret preimage provided, hash doesn't match!");
592
- this.secret = secret;
593
- await this._broadcastSecret().catch(e => this.logger.error("setSecretPreimage(): Failed to broadcast swap secret: ", e));
594
- }
595
- /**
596
- * Returns whether the secret preimage for this swap is known
597
- */
598
- hasSecretPreimage() {
599
- return this.secret != null;
600
- }
601
- //////////////////////////////
602
- //// Execution
603
- /**
604
- * Executes the swap with the provided bitcoin lightning network wallet or LNURL
605
- *
606
- * @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
607
- * link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
608
- * @param callbacks Callbacks to track the progress of the swap
609
- * @param options Optional options for the swap like AbortSignal, and timeouts/intervals
610
- * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
611
- * was recovered from on-chain data, or the pre-image was generated outside the SDK
612
- *
613
- * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
614
- * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
615
- */
616
- async execute(walletOrLnurlWithdraw, callbacks, options) {
617
- if (this._state === FromBTCLNAutoSwapState.FAILED)
618
- throw new Error("Swap failed!");
619
- if (this._state === FromBTCLNAutoSwapState.EXPIRED)
620
- throw new Error("Swap HTLC expired!");
621
- if (this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
622
- throw new Error("Swap quote expired!");
623
- if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
624
- throw new Error("Swap already settled!");
625
- let abortSignal = options?.abortSignal;
626
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
627
- if (walletOrLnurlWithdraw != null && this.lnurl == null) {
628
- if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
629
- throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
630
- if (typeof (walletOrLnurlWithdraw) === "string" || (0, LNURLWithdraw_1.isLNURLWithdraw)(walletOrLnurlWithdraw)) {
631
- await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
632
- }
633
- else {
634
- const paymentPromise = walletOrLnurlWithdraw.payInvoice(this.pr);
635
- const abortController = new AbortController();
636
- paymentPromise.catch(e => abortController.abort(e));
637
- if (options?.abortSignal != null)
638
- options.abortSignal.addEventListener("abort", () => abortController.abort(options?.abortSignal?.reason));
639
- abortSignal = abortController.signal;
640
- }
641
- }
642
- }
643
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.PR_PAID) {
644
- const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
645
- if (!paymentSuccess)
646
- throw new Error("Failed to receive lightning network payment");
647
- }
648
- if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
649
- return true;
650
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
651
- if (this.secret == null && options?.secret == null)
652
- throw new Error("Tried to wait till settlement, but no secret pre-image is known, please pass the secret pre-image as an argument!");
653
- const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal, options?.secret);
654
- if (success && callbacks?.onSwapSettled != null)
655
- callbacks.onSwapSettled(this.getOutputTxId());
656
- return success;
657
- }
658
- throw new Error("Invalid state reached!");
659
- }
660
- /**
661
- * @internal
662
- */
663
- async _getExecutionStatus(options) {
664
- if (options?.secret != null)
665
- await this.setSecretPreimage(options.secret);
666
- const state = this._state;
667
- const now = Date.now();
668
- let lightningPaymentStatus = "inactive";
669
- let destinationSettlementStatus = "inactive";
670
- let buildCurrentAction = async () => undefined;
671
- switch (state) {
672
- case FromBTCLNAutoSwapState.PR_CREATED: {
673
- const quoteValid = await this._verifyQuoteValid();
674
- lightningPaymentStatus = quoteValid ? "awaiting" : "soft_expired";
675
- if (quoteValid && this.pr != null && this.pr.toLowerCase().startsWith("ln")) {
676
- buildCurrentAction = this._buildLightningPaymentAction.bind(this);
677
- }
678
- break;
679
- }
680
- case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
681
- lightningPaymentStatus = "soft_expired";
682
- break;
683
- case FromBTCLNAutoSwapState.QUOTE_EXPIRED:
684
- lightningPaymentStatus = "expired";
685
- break;
686
- case FromBTCLNAutoSwapState.PR_PAID:
687
- lightningPaymentStatus = "received";
688
- destinationSettlementStatus = "waiting_lp";
689
- buildCurrentAction = this._buildWaitLpAction.bind(this);
690
- break;
691
- case FromBTCLNAutoSwapState.CLAIM_COMMITED:
692
- lightningPaymentStatus = "received";
693
- if (this._commitedAt == null ||
694
- options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
695
- (now - this._commitedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
696
- destinationSettlementStatus = "awaiting_manual";
697
- if (this.hasSecretPreimage()) {
698
- //TODO: Maybe add an action that would prompt the user to reveal the pre-image
699
- buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
700
- }
701
- }
702
- else {
703
- destinationSettlementStatus = "awaiting_automatic";
704
- //TODO: Maybe add an action that would prompt the user to reveal the pre-image
705
- buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
706
- }
707
- break;
708
- case FromBTCLNAutoSwapState.CLAIM_CLAIMED:
709
- lightningPaymentStatus = "confirmed";
710
- destinationSettlementStatus = "settled";
711
- break;
712
- case FromBTCLNAutoSwapState.EXPIRED:
713
- case FromBTCLNAutoSwapState.FAILED:
714
- lightningPaymentStatus = "expired";
715
- destinationSettlementStatus = "expired";
716
- break;
717
- }
718
- return {
719
- steps: [
720
- {
721
- type: "Payment",
722
- side: "source",
723
- chain: "LIGHTNING",
724
- title: "Lightning payment",
725
- description: "Pay the Lightning network invoice to initiate the swap",
726
- status: lightningPaymentStatus,
727
- initTxId: this.getInputTxId(),
728
- settleTxId: lightningPaymentStatus === "confirmed" ? this.getInputTxId() : undefined
729
- },
730
- {
731
- type: "Settlement",
732
- side: "destination",
733
- chain: this.chainIdentifier,
734
- title: "Destination settlement",
735
- description: `Wait for the LP to initiate on the ${this.chainIdentifier} side, then wait for automatic settlement, or settle manually if it takes too long`,
736
- status: destinationSettlementStatus,
737
- initTxId: this._commitTxId,
738
- settleTxId: this._claimTxId
739
- }
740
- ],
741
- buildCurrentAction,
742
- state
743
- };
744
- }
745
- /**
746
- * @internal
747
- */
748
- async _buildLightningPaymentAction() {
749
- return {
750
- type: "SendToAddress",
751
- name: "Deposit on Lightning",
752
- description: "Pay the lightning network invoice to initiate the swap",
753
- chain: "LIGHTNING",
754
- txs: [{
755
- type: "BOLT11_PAYMENT_REQUEST",
756
- address: this.getAddress(),
757
- hyperlink: this.getHyperlink(),
758
- amount: this.getInput()
759
- }],
760
- waitForTransactions: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
761
- const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for lightning payment");
762
- const success = await this._waitForLpPaymentReceived(pollIntervalSeconds, abortController.signal);
763
- if (!success)
764
- throw new Error("Quote expired while waiting for Lightning payment");
765
- return this.getInputTxId();
766
- }
767
- };
768
- }
769
- /**
770
- * @internal
771
- */
772
- async _buildWaitLpAction() {
773
- return {
774
- type: "Wait",
775
- name: "Awaiting LP payout",
776
- description: "Wait for the LP to create the swap HTLC on the destination smart chain",
777
- pollTimeSeconds: 5,
778
- expectedTimeSeconds: 10,
779
- wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
780
- const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for LP payout");
781
- await this.waitTillCommited(pollIntervalSeconds, abortController.signal);
782
- }
783
- };
784
- }
785
- /**
786
- * @internal
787
- */
788
- async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds) {
789
- return {
790
- type: "Wait",
791
- name: "Automatic settlement",
792
- description: "Wait for automatic settlement by the watchtower",
793
- pollTimeSeconds: 5,
794
- expectedTimeSeconds: 10,
795
- wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
796
- await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, undefined, pollIntervalSeconds);
797
- }
798
- };
799
- }
800
- /**
801
- * @inheritDoc
802
- * @internal
803
- */
804
- async _submitExecutionTransactions(txs, abortSignal, requiredStates, idempotent) {
805
- const parsedTxs = [];
806
- for (let tx of txs) {
807
- parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
808
- }
809
- if (idempotent) {
810
- // Handle idempotent calls
811
- if (this.wrapper._chain.getTxId != null) {
812
- const txIds = await Promise.all(parsedTxs.map(tx => this.wrapper._chain.getTxId(tx)));
813
- const foundTxId = txIds.find(txId => this._claimTxId === txId);
814
- if (foundTxId != null)
815
- return txIds;
816
- }
817
- }
818
- if (requiredStates != null && !requiredStates.includes(this._state))
819
- throw new Error("Swap state has changed before transactions were submitted!");
820
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
821
- const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
822
- await this.waitTillClaimed(undefined, abortSignal);
823
- return txIds;
824
- }
825
- throw new Error("Invalid swap state for transaction submission!");
826
- }
827
- /**
828
- * @internal
829
- */
830
- async _buildClaimSmartChainTxAction(actionOptions) {
831
- const signerAddress = await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
832
- return {
833
- type: "SignSmartChainTransaction",
834
- name: "Settle manually",
835
- description: "Manually settle (claim) the swap on the destination smart chain",
836
- chain: this.chainIdentifier,
837
- txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner, actionOptions?.secret)),
838
- submitTransactions: async (txs, abortSignal, idempotent) => {
839
- return this._submitExecutionTransactions(txs, abortSignal, [FromBTCLNAutoSwapState.CLAIM_COMMITED], idempotent);
840
- },
841
- requiredSigner: signerAddress ?? this._getInitiator()
842
- };
843
- }
844
- /**
845
- *
846
- * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
847
- * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
848
- * the bitcoin transaction is confirmed (defaults to 60 seconds)
849
- * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
850
- * was recovered from on-chain data, or the pre-image was generated outside the SDK
851
- */
852
- async getExecutionAction(options) {
853
- const executionStatus = await this._getExecutionStatus(options);
854
- return executionStatus.buildCurrentAction(options);
855
- }
856
- /**
857
- * @inheritDoc
858
- */
859
- // TODO: Figure how we gonna trigger an LNURL-withdraw with the execution actions
860
- async getExecutionStatus(options) {
861
- const executionStatus = await this._getExecutionStatus(options);
862
- return {
863
- steps: executionStatus.steps,
864
- currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
865
- stateInfo: this._getStateInfo(executionStatus.state)
866
- };
867
- }
868
- /**
869
- * @inheritDoc
870
- */
871
- async getExecutionSteps(options) {
872
- return (await this._getExecutionStatus(options)).steps;
873
- }
874
- //////////////////////////////
875
- //// Payment
876
- /**
877
- * Checks whether the LP received the LN payment
878
- *
879
- * @param save If the new swap state should be saved
880
- *
881
- * @internal
882
- */
883
- async _checkIntermediaryPaymentReceived(save = true) {
884
- if (this._state === FromBTCLNAutoSwapState.PR_PAID ||
885
- this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED ||
886
- this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED ||
887
- this._state === FromBTCLNAutoSwapState.FAILED ||
888
- this._state === FromBTCLNAutoSwapState.EXPIRED)
889
- return true;
890
- if (this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED)
891
- return false;
892
- if (this.url == null)
893
- return false;
894
- const paymentHash = this.getPaymentHash();
895
- if (paymentHash == null)
896
- throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
897
- const resp = await this.wrapper._lpApi.getInvoiceStatus(this.url, paymentHash.toString("hex"));
898
- switch (resp.code) {
899
- case IntermediaryAPI_1.InvoiceStatusResponseCodes.PAID:
900
- const data = new (this.wrapper._swapDataDeserializer(this._contractVersion))(resp.data.data);
901
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
902
- try {
903
- await this._saveRealSwapData(data, save);
904
- return true;
905
- }
906
- catch (e) { }
907
- return null;
908
- case IntermediaryAPI_1.InvoiceStatusResponseCodes.EXPIRED:
909
- this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
910
- this.initiated = true;
911
- if (save)
912
- await this._saveAndEmit();
913
- return false;
914
- default:
915
- return null;
916
- }
917
- }
918
- /**
919
- * Checks and overrides the swap data for this swap. This is used to set the swap data from
920
- * on-chain events.
921
- *
922
- * @param data Swap data of the escrow swap
923
- * @param save If the new data should be saved
924
- *
925
- * @internal
926
- */
927
- async _saveRealSwapData(data, save) {
928
- await this.checkIntermediaryReturnedData(data);
929
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
930
- this._state = FromBTCLNAutoSwapState.PR_PAID;
931
- this._data = data;
932
- this.initiated = true;
933
- if (save)
934
- await this._saveAndEmit();
935
- return true;
936
- }
937
- return false;
938
- }
939
- /**
940
- * Waits till a lightning network payment is received by the intermediary, and the intermediary
941
- * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
942
- * for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
943
- * or settle manually using the {@link claim} or {@link txsClaim} functions.
944
- *
945
- * If this swap is using an LNURL-withdraw link as input, it automatically posts the
946
- * generated invoice to the LNURL service to pay it.
947
- *
948
- * @remarks For internal use, rather use {@link waitForPayment} which properly waits till the LP also
949
- * offers a swap HTLC.
950
- *
951
- * @param abortSignal Abort signal to stop waiting for payment
952
- * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
953
- *
954
- * @internal
955
- */
956
- async _waitForLpPaymentReceived(checkIntervalSeconds, abortSignal) {
957
- checkIntervalSeconds ??= 5;
958
- if (this._state >= FromBTCLNAutoSwapState.PR_PAID)
959
- return true;
960
- if (this._state !== FromBTCLNAutoSwapState.PR_CREATED)
961
- throw new Error("Must be in PR_CREATED state!");
962
- const abortController = new AbortController();
963
- if (abortSignal != null)
964
- abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
965
- let save = false;
966
- if (this.lnurl != null && this.lnurlK1 != null && this.lnurlCallback != null && !this.prPosted) {
967
- if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
968
- throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
969
- LNURL_1.LNURL.postInvoiceToLNURLWithdraw({ k1: this.lnurlK1, callback: this.lnurlCallback }, this.pr).catch(e => {
970
- this.lnurlFailSignal.abort(e);
971
- });
972
- this.prPosted = true;
973
- save ||= true;
974
- }
975
- if (!this.initiated) {
976
- this.initiated = true;
977
- save ||= true;
978
- }
979
- if (save)
980
- await this._saveAndEmit();
981
- let lnurlFailListener = () => abortController.abort(this.lnurlFailSignal.signal.reason);
982
- this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
983
- this.lnurlFailSignal.signal.throwIfAborted();
984
- const paymentHash = this.getPaymentHash();
985
- if (paymentHash == null)
986
- throw new Error("Swap payment hash not available, the swap was probably recovered!");
987
- if (this.wrapper._messenger.warmup != null)
988
- await this.wrapper._messenger.warmup().catch(e => {
989
- this.logger.warn("waitForPayment(): Failed to warmup messenger: ", e);
990
- });
991
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
992
- const promises = [
993
- this.waitTillState(FromBTCLNAutoSwapState.PR_PAID, "gte", abortController.signal).then(() => true)
994
- ];
995
- if (this.url != null)
996
- promises.push((async () => {
997
- let resp = { code: IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING, msg: "" };
998
- while (!abortController.signal.aborted && resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING) {
999
- resp = await this.wrapper._lpApi.getInvoiceStatus(this.url, paymentHash.toString("hex"));
1000
- if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING)
1001
- await (0, TimeoutUtils_1.timeoutPromise)(checkIntervalSeconds * 1000, abortController.signal);
1002
- }
1003
- this.lnurlFailSignal.signal.removeEventListener("abort", lnurlFailListener);
1004
- abortController.signal.throwIfAborted();
1005
- if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PAID) {
1006
- const swapData = new (this.wrapper._swapDataDeserializer(this._contractVersion))(resp.data.data);
1007
- return await this._saveRealSwapData(swapData, true);
1008
- }
1009
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1010
- if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.EXPIRED) {
1011
- await this._saveAndEmit(FromBTCLNAutoSwapState.QUOTE_EXPIRED);
1012
- }
1013
- return false;
1014
- }
1015
- })());
1016
- const paymentResult = await Promise.race(promises);
1017
- abortController.abort();
1018
- if (!paymentResult)
1019
- return false;
1020
- }
1021
- return this._state >= FromBTCLNAutoSwapState.PR_PAID;
1022
- }
1023
- /**
1024
- * Checks the data returned by the intermediary in the payment auth request
1025
- *
1026
- * @param data Parsed swap data as returned by the intermediary
1027
- *
1028
- * @throws {IntermediaryError} If the returned are not valid
1029
- * @throws {Error} If the swap is already committed on-chain
1030
- *
1031
- * @private
1032
- */
1033
- async checkIntermediaryReturnedData(data) {
1034
- if (!data.isPayOut())
1035
- throw new IntermediaryError_1.IntermediaryError("Invalid not pay out");
1036
- if (data.getType() !== base_1.ChainSwapType.HTLC)
1037
- throw new IntermediaryError_1.IntermediaryError("Invalid swap type");
1038
- if (!data.isOfferer(this.getSwapData().getOfferer()))
1039
- throw new IntermediaryError_1.IntermediaryError("Invalid offerer used");
1040
- if (!data.isClaimer(this._getInitiator()))
1041
- throw new IntermediaryError_1.IntermediaryError("Invalid claimer used");
1042
- if (!data.isToken(this.getSwapData().getToken()))
1043
- throw new IntermediaryError_1.IntermediaryError("Invalid token used");
1044
- if (data.getSecurityDeposit() !== this.getSwapData().getSecurityDeposit())
1045
- throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
1046
- if (data.getClaimerBounty() !== this.getSwapData().getClaimerBounty())
1047
- throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
1048
- if (data.getAmount() < this.getSwapData().getAmount())
1049
- throw new IntermediaryError_1.IntermediaryError("Invalid amount received!");
1050
- if (data.getClaimHash() !== this.getSwapData().getClaimHash())
1051
- throw new IntermediaryError_1.IntermediaryError("Invalid payment hash used!");
1052
- if (!data.isDepositToken(this.getSwapData().getDepositToken()))
1053
- throw new IntermediaryError_1.IntermediaryError("Invalid deposit token used!");
1054
- if (data.hasSuccessAction())
1055
- throw new IntermediaryError_1.IntermediaryError("Invalid has success action");
1056
- if (await this.wrapper._contract(this._contractVersion).isExpired(this._getInitiator(), data))
1057
- throw new IntermediaryError_1.IntermediaryError("Not enough time to claim!");
1058
- if (this.wrapper._getHtlcTimeout(data) <= (Date.now() / 1000))
1059
- throw new IntermediaryError_1.IntermediaryError("HTLC expires too soon!");
1060
- }
1061
- /**
1062
- * Waits till a lightning network payment is received by the intermediary, and the intermediary
1063
- * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
1064
- * for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
1065
- * or settle manually using the {@link claim} or {@link txsClaim} functions.
1066
- *
1067
- * If this swap is using an LNURL-withdraw link as input, it automatically posts the
1068
- * generated invoice to the LNURL service to pay it.
1069
- *
1070
- * @param onPaymentReceived Callback as for when the LP reports having received the ln payment
1071
- * @param abortSignal Abort signal to stop waiting for payment
1072
- * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
1073
- */
1074
- async waitForPayment(onPaymentReceived, checkIntervalSeconds, abortSignal) {
1075
- checkIntervalSeconds ??= 5;
1076
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1077
- await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1078
- }
1079
- if (this._state >= FromBTCLNAutoSwapState.CLAIM_COMMITED)
1080
- return true;
1081
- const success = await this._waitForLpPaymentReceived(checkIntervalSeconds, abortSignal);
1082
- if (!success)
1083
- return false;
1084
- if (onPaymentReceived != null)
1085
- onPaymentReceived(this.getInputTxId());
1086
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1087
- await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1088
- }
1089
- return this._state >= FromBTCLNAutoSwapState.CLAIM_COMMITED;
1090
- }
1091
- //////////////////////////////
1092
- //// Commit
1093
- /**
1094
- * Waits till the intermediary (LP) initiates the swap HTLC escrow on the destination smart chain side
1095
- *
1096
- * @param checkIntervalSeconds How often to check via a polling watchdog
1097
- * @param abortSignal Abort signal
1098
- *
1099
- * @internal
1100
- */
1101
- async waitTillCommited(checkIntervalSeconds, abortSignal) {
1102
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
1103
- return Promise.resolve();
1104
- if (this._state !== FromBTCLNAutoSwapState.PR_PAID)
1105
- throw new Error("Invalid state");
1106
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1107
- let result;
1108
- try {
1109
- result = await Promise.race([
1110
- this.watchdogWaitTillCommited(checkIntervalSeconds, abortController.signal),
1111
- this.waitTillState(FromBTCLNAutoSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
1112
- ]);
1113
- abortController.abort();
1114
- }
1115
- catch (e) {
1116
- abortController.abort();
1117
- throw e;
1118
- }
1119
- if (result === null) {
1120
- this.logger.debug("waitTillCommited(): Resolved from watchdog - HTLC expired");
1121
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1122
- await this._saveAndEmit(FromBTCLNAutoSwapState.EXPIRED);
1123
- }
1124
- return;
1125
- }
1126
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1127
- if (typeof (result) === "object" && result.getInitTxId != null && this._commitTxId == null)
1128
- this._commitTxId = await result.getInitTxId();
1129
- this._commitedAt ??= Date.now();
1130
- await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_COMMITED);
1131
- }
1132
- if (result === 0) {
1133
- this.logger.debug("waitTillCommited(): Resolved from state changed");
1134
- }
1135
- else if (result != null) {
1136
- this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1137
- if (this.secret != null)
1138
- await this._broadcastSecret().catch(e => {
1139
- this.logger.error("waitTillCommited(): Error broadcasting swap secret: ", e);
1140
- });
1141
- }
1142
- }
1143
- //////////////////////////////
1144
- //// Claim
1145
- /**
1146
- * @inheritDoc
1147
- *
1148
- * @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
1149
- * @param secret A swap secret to use for the claim transaction, generally only needed if the swap
1150
- * was recovered from on-chain data, or the pre-image was generated outside the SDK
1151
- *
1152
- * @throws {Error} If in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1153
- */
1154
- async txsClaim(_signer, secret) {
1155
- let address = undefined;
1156
- if (_signer != null) {
1157
- if (typeof (_signer) === "string") {
1158
- address = _signer;
1159
- }
1160
- else if ((0, base_1.isAbstractSigner)(_signer)) {
1161
- address = _signer.getAddress();
1162
- }
1163
- else {
1164
- address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
1165
- }
1166
- }
1167
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1168
- throw new Error("Must be in CLAIM_COMMITED state!");
1169
- if (this._data == null)
1170
- throw new Error("Unknown data, wrong state?");
1171
- const useSecret = secret ?? this.secret;
1172
- if (useSecret == null)
1173
- throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
1174
- if (!this.isValidSecretPreimage(useSecret))
1175
- throw new Error("Invalid swap secret pre-image provided!");
1176
- return await this._contract.txsClaimWithSecret(address ?? this._getInitiator(), this._data, useSecret, true, true);
1177
- }
1178
- /**
1179
- * @inheritDoc
1180
- *
1181
- * @param _signer Signer to sign the transactions with, can also be different to the initializer
1182
- * @param abortSignal Abort signal to stop waiting for transaction confirmation
1183
- * @param onBeforeTxSent
1184
- * @param secret A swap secret to use for the claim transaction, generally only needed if the swap
1185
- * was recovered from on-chain data, or the pre-image was generated outside the SDK
1186
- */
1187
- async claim(_signer, abortSignal, onBeforeTxSent, secret) {
1188
- const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1189
- let txCount = 0;
1190
- const txs = await this.txsClaim(_signer, secret);
1191
- const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1192
- txCount++;
1193
- if (onBeforeTxSent != null && txCount === 1)
1194
- onBeforeTxSent(txId);
1195
- return Promise.resolve();
1196
- });
1197
- this._claimTxId = result[0];
1198
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.EXPIRED || this._state === FromBTCLNAutoSwapState.FAILED) {
1199
- await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
1200
- }
1201
- return result[0];
1202
- }
1203
- /**
1204
- * Waits till the swap is successfully settled (claimed), should be called after sending the claim (settlement)
1205
- * transactions manually to wait till the SDK processes the settlement and updates the swap state accordingly.
1206
- *
1207
- * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
1208
- * @param abortSignal AbortSignal
1209
- * @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
1210
- * was recovered from on-chain data, or the pre-image was generated outside the SDK
1211
- * @param pollIntervalSeconds How often to poll via the watchdog
1212
- *
1213
- * @throws {Error} If swap is in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1214
- * @throws {Error} If the LP refunded sooner than we were able to claim
1215
- * @returns {boolean} whether the swap was claimed in time or not
1216
- */
1217
- async waitTillClaimed(maxWaitTimeSeconds, abortSignal, secret, pollIntervalSeconds) {
1218
- if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
1219
- return Promise.resolve(true);
1220
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1221
- throw new Error("Invalid state (not CLAIM_COMMITED)");
1222
- if (secret != null) {
1223
- if (!this.isValidSecretPreimage(secret))
1224
- throw new Error("Invalid swap secret pre-image provided!");
1225
- this.secret = secret;
1226
- }
1227
- const abortController = new AbortController();
1228
- if (abortSignal != null)
1229
- abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
1230
- let timedOut = false;
1231
- if (maxWaitTimeSeconds != null) {
1232
- const timeout = setTimeout(() => {
1233
- timedOut = true;
1234
- abortController.abort();
1235
- }, maxWaitTimeSeconds * 1000);
1236
- abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1237
- }
1238
- let res;
1239
- try {
1240
- res = await Promise.race([
1241
- this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1242
- this.waitTillState(FromBTCLNAutoSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
1243
- this.waitTillState(FromBTCLNAutoSwapState.EXPIRED, "eq", abortController.signal).then(() => 1),
1244
- ]);
1245
- abortController.abort();
1246
- }
1247
- catch (e) {
1248
- abortController.abort();
1249
- if (timedOut)
1250
- return false;
1251
- throw e;
1252
- }
1253
- if (res === 0) {
1254
- this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
1255
- return true;
1256
- }
1257
- if (res === 1) {
1258
- this.logger.debug("waitTillClaimed(): Resolved from state change (EXPIRED)");
1259
- throw new Error("Swap expired during claiming");
1260
- }
1261
- this.logger.debug("waitTillClaimed(): Resolved from watchdog");
1262
- if (res?.type === base_1.SwapCommitStateType.PAID) {
1263
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_CLAIMED) {
1264
- this._claimTxId = await res.getClaimTxId();
1265
- await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
1266
- }
1267
- }
1268
- if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
1269
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_CLAIMED &&
1270
- this._state !== FromBTCLNAutoSwapState.FAILED) {
1271
- await this._saveAndEmit(FromBTCLNAutoSwapState.FAILED);
1272
- }
1273
- throw new Error("Swap expired during claiming");
1274
- }
1275
- return true;
1276
- }
1277
- //////////////////////////////
1278
- //// LNURL
1279
- /**
1280
- * Whether this swap uses an LNURL-withdraw link
1281
- */
1282
- isLNURL() {
1283
- return this.lnurl != null;
1284
- }
1285
- /**
1286
- * Gets the used LNURL or `null` if this is not an LNURL-withdraw swap
1287
- */
1288
- getLNURL() {
1289
- return this.lnurl ?? null;
1290
- }
1291
- /**
1292
- * Pay the generated lightning network invoice with an LNURL-withdraw link, this
1293
- * is useful when you want to display a lightning payment QR code and also want to
1294
- * allow payments using LNURL-withdraw NFC cards.
1295
- *
1296
- * Note that the swap needs to be created **without** an LNURL to begin with for this function
1297
- * to work. If this swap is already using an LNURL-withdraw link, this function throws.
1298
- */
1299
- async settleWithLNURLWithdraw(lnurl) {
1300
- if (this._state !== FromBTCLNAutoSwapState.PR_CREATED &&
1301
- this._state !== FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
1302
- throw new Error("Must be in PR_CREATED state!");
1303
- if (this.lnurl != null)
1304
- throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
1305
- let lnurlParams;
1306
- if (typeof (lnurl) === "string") {
1307
- const parsedLNURL = await LNURL_1.LNURL.getLNURL(lnurl);
1308
- if (parsedLNURL == null || parsedLNURL.tag !== "withdrawRequest")
1309
- throw new UserError_1.UserError("Invalid LNURL-withdraw to settle the swap");
1310
- lnurlParams = parsedLNURL;
1311
- }
1312
- else {
1313
- lnurlParams = lnurl.params;
1314
- }
1315
- if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
1316
- throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
1317
- LNURL_1.LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
1318
- this.lnurl = lnurlParams.url;
1319
- this.lnurlCallback = lnurlParams.callback;
1320
- this.lnurlK1 = lnurlParams.k1;
1321
- this.prPosted = true;
1322
- await this._saveAndEmit();
1323
- }
1324
- //////////////////////////////
1325
- //// Storage
1326
- /**
1327
- * @inheritDoc
1328
- */
1329
- serialize() {
1330
- return {
1331
- ...super.serialize(),
1332
- data: this._data == null ? null : this._data.serialize(),
1333
- commitTxId: this._commitTxId,
1334
- claimTxId: this._claimTxId,
1335
- commitedAt: this._commitedAt,
1336
- btcAmountSwap: this.btcAmountSwap == null ? null : this.btcAmountSwap.toString(10),
1337
- btcAmountGas: this.btcAmountGas == null ? null : this.btcAmountGas.toString(10),
1338
- gasSwapFeeBtc: this.gasSwapFeeBtc == null ? null : this.gasSwapFeeBtc.toString(10),
1339
- gasSwapFee: this.gasSwapFee == null ? null : this.gasSwapFee.toString(10),
1340
- gasPricingInfo: (0, PriceInfoType_1.serializePriceInfoType)(this.gasPricingInfo),
1341
- pr: this.pr,
1342
- secret: this.secret,
1343
- lnurl: this.lnurl,
1344
- lnurlK1: this.lnurlK1,
1345
- lnurlCallback: this.lnurlCallback,
1346
- prPosted: this.prPosted,
1347
- initialSwapData: this.initialSwapData.serialize(),
1348
- usesClaimHashAsId: this.usesClaimHashAsId
1349
- };
1350
- }
1351
- //////////////////////////////
1352
- //// Swap ticks & sync
1353
- /**
1354
- * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1355
- * data
1356
- *
1357
- * @private
1358
- */
1359
- async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
1360
- if (this._state === FromBTCLNAutoSwapState.PR_PAID ||
1361
- this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED ||
1362
- this._state === FromBTCLNAutoSwapState.EXPIRED) {
1363
- //Check for expiry before the getCommitStatus to prevent race conditions
1364
- let quoteExpired = false;
1365
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1366
- quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
1367
- }
1368
- //Check if it's already successfully paid
1369
- commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
1370
- if (commitStatus != null && await this._forciblySetOnchainState(commitStatus))
1371
- return true;
1372
- if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1373
- if (quoteExpired) {
1374
- this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1375
- return true;
1376
- }
1377
- }
1378
- }
1379
- return false;
1380
- }
1381
- /**
1382
- * @inheritDoc
1383
- * @internal
1384
- */
1385
- _shouldFetchOnchainState() {
1386
- return this._state === FromBTCLNAutoSwapState.PR_PAID || this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.EXPIRED;
1387
- }
1388
- /**
1389
- * @inheritDoc
1390
- * @internal
1391
- */
1392
- _shouldFetchExpiryStatus() {
1393
- return this._state === FromBTCLNAutoSwapState.PR_PAID;
1394
- }
1395
- /**
1396
- * @inheritDoc
1397
- * @internal
1398
- */
1399
- _shouldCheckIntermediary() {
1400
- return this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1401
- }
1402
- /**
1403
- * @inheritDoc
1404
- * @internal
1405
- */
1406
- async _sync(save, quoteDefinitelyExpired, commitStatus, skipLpCheck) {
1407
- let changed = false;
1408
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1409
- if (this._state !== FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.getQuoteExpiry() < Date.now()) {
1410
- this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1411
- changed ||= true;
1412
- }
1413
- if (!skipLpCheck)
1414
- try {
1415
- const result = await this._checkIntermediaryPaymentReceived(false);
1416
- if (result !== null)
1417
- changed ||= true;
1418
- }
1419
- catch (e) {
1420
- this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
1421
- }
1422
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1423
- if (await this._verifyQuoteDefinitelyExpired()) {
1424
- this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1425
- changed ||= true;
1426
- }
1427
- }
1428
- }
1429
- if (await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus))
1430
- changed = true;
1431
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
1432
- const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1433
- if (expired) {
1434
- this._state = FromBTCLNAutoSwapState.EXPIRED;
1435
- changed = true;
1436
- }
1437
- }
1438
- if (save && changed)
1439
- await this._saveAndEmit();
1440
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED && this.secret != null)
1441
- await this._broadcastSecret().catch(e => {
1442
- this.logger.error("_sync(): Error when broadcasting swap secret: ", e);
1443
- });
1444
- return changed;
1445
- }
1446
- /**
1447
- * @inheritDoc
1448
- * @internal
1449
- */
1450
- async _forciblySetOnchainState(commitStatus) {
1451
- switch (commitStatus?.type) {
1452
- case base_1.SwapCommitStateType.PAID:
1453
- if (this._commitTxId == null && commitStatus.getInitTxId != null)
1454
- this._commitTxId = await commitStatus.getInitTxId();
1455
- if (this._claimTxId == null)
1456
- this._claimTxId = await commitStatus.getClaimTxId();
1457
- if (this.secret == null || this.pr == null)
1458
- this._setSwapSecret(await commitStatus.getClaimResult());
1459
- this._state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
1460
- return true;
1461
- case base_1.SwapCommitStateType.NOT_COMMITED:
1462
- let changed = false;
1463
- if (this._commitTxId == null && commitStatus.getInitTxId != null) {
1464
- this._commitTxId = await commitStatus.getInitTxId();
1465
- changed = true;
1466
- }
1467
- if (this._refundTxId == null && commitStatus.getRefundTxId != null) {
1468
- this._refundTxId = await commitStatus.getRefundTxId();
1469
- changed = true;
1470
- }
1471
- if (this._refundTxId != null) {
1472
- this._state = FromBTCLNAutoSwapState.FAILED;
1473
- changed = true;
1474
- }
1475
- return changed;
1476
- case base_1.SwapCommitStateType.EXPIRED:
1477
- if (this._commitTxId == null && commitStatus.getInitTxId != null)
1478
- this._commitTxId = await commitStatus.getInitTxId();
1479
- if (this._refundTxId == null && commitStatus.getRefundTxId != null)
1480
- this._refundTxId = await commitStatus.getRefundTxId();
1481
- this._state = this._refundTxId == null ? FromBTCLNAutoSwapState.QUOTE_EXPIRED : FromBTCLNAutoSwapState.FAILED;
1482
- return true;
1483
- case base_1.SwapCommitStateType.COMMITED:
1484
- let save = false;
1485
- if (this._commitTxId == null && commitStatus.getInitTxId != null) {
1486
- this._commitTxId = await commitStatus.getInitTxId();
1487
- save = true;
1488
- }
1489
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED && this._state !== FromBTCLNAutoSwapState.EXPIRED) {
1490
- this._commitedAt ??= Date.now();
1491
- this._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
1492
- save = true;
1493
- }
1494
- return save;
1495
- }
1496
- return false;
1497
- }
1498
- /**
1499
- * Broadcasts the swap secret to the underlying data propagation layer (e.g. Nostr by default)
1500
- *
1501
- * @param noCheckExpiry Whether a swap expiration check should be skipped broadcasting
1502
- * @param secret An optional secret pre-image for the swap to broadcast
1503
- *
1504
- * @internal
1505
- */
1506
- async _broadcastSecret(noCheckExpiry, secret) {
1507
- if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1508
- throw new Error("Must be in CLAIM_COMMITED state to broadcast swap secret!");
1509
- if (this._data == null)
1510
- throw new Error("Unknown data, wrong state?");
1511
- const useSecret = secret ?? this.secret;
1512
- if (useSecret == null)
1513
- throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
1514
- if (!this.isValidSecretPreimage(useSecret))
1515
- throw new Error("Invalid swap secret pre-image provided!");
1516
- if (!noCheckExpiry) {
1517
- if (await this._contract.isExpired(this._getInitiator(), this._data))
1518
- throw new Error("On-chain HTLC already expired!");
1519
- }
1520
- await this.wrapper._messenger.broadcast(new base_1.SwapClaimWitnessMessage(this._data, useSecret));
1521
- }
1522
- /**
1523
- * @inheritDoc
1524
- * @internal
1525
- */
1526
- async _tick(save) {
1527
- switch (this._state) {
1528
- case FromBTCLNAutoSwapState.PR_CREATED:
1529
- if (this.getQuoteExpiry() < Date.now()) {
1530
- this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1531
- if (save)
1532
- await this._saveAndEmit();
1533
- return true;
1534
- }
1535
- break;
1536
- case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
1537
- if (this.getDefinitiveExpiryTime() < Date.now()) {
1538
- this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1539
- if (save)
1540
- await this._saveAndEmit();
1541
- return true;
1542
- }
1543
- break;
1544
- case FromBTCLNAutoSwapState.PR_PAID:
1545
- case FromBTCLNAutoSwapState.CLAIM_COMMITED:
1546
- const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1547
- if (expired) {
1548
- this._state = FromBTCLNAutoSwapState.EXPIRED;
1549
- if (save)
1550
- await this._saveAndEmit();
1551
- return true;
1552
- }
1553
- if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
1554
- //Broadcast the secret over the provided messenger channel
1555
- if (this.broadcastTickCounter === 0 && this.secret != null)
1556
- await this._broadcastSecret(true).catch(e => {
1557
- this.logger.warn("_tick(): Error when broadcasting swap secret: ", e);
1558
- });
1559
- this.broadcastTickCounter = (this.broadcastTickCounter + 1) % 3; //Broadcast every 3rd tick
1560
- }
1561
- break;
1562
- }
1563
- return false;
1564
- }
1565
- /**
1566
- * Forcibly sets the swap secret pre-image from on-chain data
1567
- *
1568
- * @internal
1569
- */
1570
- _setSwapSecret(secret) {
1571
- this.secret = secret;
1572
- if (this.pr == null) {
1573
- this.pr = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex"))).toString("hex");
1574
- }
1575
- }
1576
- }
1577
- exports.FromBTCLNAutoSwap = FromBTCLNAutoSwap;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FromBTCLNAutoSwap = exports.isFromBTCLNAutoSwapInit = exports.FromBTCLNAutoSwapState = void 0;
4
+ const bolt11_1 = require("@atomiqlabs/bolt11");
5
+ const SwapType_1 = require("../../../../enums/SwapType");
6
+ const base_1 = require("@atomiqlabs/base");
7
+ const buffer_1 = require("buffer");
8
+ const LNURL_1 = require("../../../../lnurl/LNURL");
9
+ const UserError_1 = require("../../../../errors/UserError");
10
+ const IntermediaryAPI_1 = require("../../../../intermediaries/apis/IntermediaryAPI");
11
+ const IntermediaryError_1 = require("../../../../errors/IntermediaryError");
12
+ const Utils_1 = require("../../../../utils/Utils");
13
+ const IEscrowSwap_1 = require("../../IEscrowSwap");
14
+ const FeeType_1 = require("../../../../enums/FeeType");
15
+ const PercentagePPM_1 = require("../../../../types/fees/PercentagePPM");
16
+ const TokenAmount_1 = require("../../../../types/TokenAmount");
17
+ const Token_1 = require("../../../../types/Token");
18
+ const Logger_1 = require("../../../../utils/Logger");
19
+ const TimeoutUtils_1 = require("../../../../utils/TimeoutUtils");
20
+ const LNURLWithdraw_1 = require("../../../../types/lnurl/LNURLWithdraw");
21
+ const PriceInfoType_1 = require("../../../../types/PriceInfoType");
22
+ const sha2_1 = require("@noble/hashes/sha2");
23
+ /**
24
+ * State enum for FromBTCLNAuto swaps
25
+ * @category Swaps/Lightning → Smart chain
26
+ */
27
+ var FromBTCLNAutoSwapState;
28
+ (function (FromBTCLNAutoSwapState) {
29
+ /**
30
+ * Swap has failed as the user didn't settle the HTLC on the destination before expiration
31
+ */
32
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["FAILED"] = -4] = "FAILED";
33
+ /**
34
+ * Swap has expired for good and there is no way how it can be executed anymore
35
+ */
36
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["QUOTE_EXPIRED"] = -3] = "QUOTE_EXPIRED";
37
+ /**
38
+ * A swap is almost expired, and it should be presented to the user as expired, though
39
+ * there is still a chance that it will be processed
40
+ */
41
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["QUOTE_SOFT_EXPIRED"] = -2] = "QUOTE_SOFT_EXPIRED";
42
+ /**
43
+ * Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the
44
+ * swap on the destination smart chain.
45
+ */
46
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["EXPIRED"] = -1] = "EXPIRED";
47
+ /**
48
+ * Swap quote was created, use {@link FromBTCLNAutoSwap.getAddress} or {@link FromBTCLNAutoSwap.getHyperlink}
49
+ * to get the bolt11 lightning network invoice to pay to initiate the swap, then use the
50
+ * {@link FromBTCLNAutoSwap.waitForPayment} to wait till the lightning network payment is received
51
+ * by the intermediary (LP) and the destination HTLC escrow is created
52
+ */
53
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["PR_CREATED"] = 0] = "PR_CREATED";
54
+ /**
55
+ * Lightning network payment has been received by the intermediary (LP), but the destination chain
56
+ * HTLC escrow hasn't been created yet. Use {@link FromBTCLNAutoSwap.waitForPayment} to continue waiting
57
+ * till the destination HTLC escrow is created.
58
+ */
59
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["PR_PAID"] = 1] = "PR_PAID";
60
+ /**
61
+ * Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers
62
+ * using the {@link FromBTCLNAutoSwap.waitTillClaimed} function or settle manually using the
63
+ * {@link FromBTCLNAutoSwap.claim} or {@link FromBTCLNAutoSwap.txsClaim} function.
64
+ */
65
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["CLAIM_COMMITED"] = 2] = "CLAIM_COMMITED";
66
+ /**
67
+ * Swap successfully settled and funds received on the destination chain
68
+ */
69
+ FromBTCLNAutoSwapState[FromBTCLNAutoSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
70
+ })(FromBTCLNAutoSwapState = exports.FromBTCLNAutoSwapState || (exports.FromBTCLNAutoSwapState = {}));
71
+ const FromBTCLNAutoSwapStateDescription = {
72
+ [FromBTCLNAutoSwapState.FAILED]: "Swap has failed as the user didn't settle the HTLC on the destination before expiration",
73
+ [FromBTCLNAutoSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
74
+ [FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
75
+ [FromBTCLNAutoSwapState.EXPIRED]: "Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the swap on the destination smart chain.",
76
+ [FromBTCLNAutoSwapState.PR_CREATED]: "Swap quote was created, pay the bolt11 lightning network invoice to initiate the swap, then wait till the lightning network payment is received by the intermediary (LP) and the destination HTLC escrow is created",
77
+ [FromBTCLNAutoSwapState.PR_PAID]: "Lightning network payment has been received by the intermediary (LP), but the destination chain HTLC escrow hasn't been created yet. Continue waiting till the destination HTLC escrow is created.",
78
+ [FromBTCLNAutoSwapState.CLAIM_COMMITED]: "Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers or settle manually.",
79
+ [FromBTCLNAutoSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
80
+ };
81
+ function isFromBTCLNAutoSwapInit(obj) {
82
+ return (obj.pr == null || typeof obj.pr === "string") &&
83
+ (obj.secret == null || typeof obj.secret === "string") &&
84
+ (obj.btcAmountSwap == null || typeof obj.btcAmountSwap === "bigint") &&
85
+ (obj.btcAmountGas == null || typeof obj.btcAmountGas === "bigint") &&
86
+ typeof obj.gasSwapFeeBtc === "bigint" &&
87
+ typeof obj.gasSwapFee === "bigint" &&
88
+ (obj.gasPricingInfo == null || (0, PriceInfoType_1.isPriceInfoType)(obj.gasPricingInfo)) &&
89
+ (obj.lnurl == null || typeof (obj.lnurl) === "string") &&
90
+ (obj.lnurlK1 == null || typeof (obj.lnurlK1) === "string") &&
91
+ (obj.lnurlCallback == null || typeof (obj.lnurlCallback) === "string") &&
92
+ (0, IEscrowSwap_1.isIEscrowSwapInit)(obj);
93
+ }
94
+ exports.isFromBTCLNAutoSwapInit = isFromBTCLNAutoSwapInit;
95
+ /**
96
+ * New escrow based (HTLC) swaps for Bitcoin Lightning -> Smart chain swaps not requiring manual settlement on
97
+ * the destination by the user, and instead letting the LP initiate the escrow. Permissionless watchtower network
98
+ * handles the claiming of HTLC, with the swap secret broadcasted over Nostr. Also adds a possibility for the user
99
+ * to receive a native token on the destination chain as part of the swap (a "gas drop" feature).
100
+ *
101
+ * @category Swaps/Lightning → Smart chain
102
+ */
103
+ class FromBTCLNAutoSwap extends IEscrowSwap_1.IEscrowSwap {
104
+ /**
105
+ * Sets the LNURL data for the swap
106
+ *
107
+ * @internal
108
+ */
109
+ _setLNURLData(lnurl, lnurlK1, lnurlCallback) {
110
+ this.lnurl = lnurl;
111
+ this.lnurlK1 = lnurlK1;
112
+ this.lnurlCallback = lnurlCallback;
113
+ }
114
+ constructor(wrapper, initOrObject) {
115
+ if (isFromBTCLNAutoSwapInit(initOrObject) && initOrObject.url != null)
116
+ initOrObject.url += "/frombtcln_auto";
117
+ super(wrapper, initOrObject);
118
+ this.TYPE = SwapType_1.SwapType.FROM_BTCLN_AUTO;
119
+ /**
120
+ * @internal
121
+ */
122
+ this.swapStateName = (state) => FromBTCLNAutoSwapState[state];
123
+ /**
124
+ * @internal
125
+ */
126
+ this.swapStateDescription = FromBTCLNAutoSwapStateDescription;
127
+ /**
128
+ * @internal
129
+ */
130
+ this.inputToken = Token_1.BitcoinTokens.BTCLN;
131
+ this.lnurlFailSignal = new AbortController();
132
+ this.prPosted = false;
133
+ this.broadcastTickCounter = 0;
134
+ if (isFromBTCLNAutoSwapInit(initOrObject)) {
135
+ this._state = FromBTCLNAutoSwapState.PR_CREATED;
136
+ this.pr = initOrObject.pr;
137
+ this.secret = initOrObject.secret;
138
+ this.initialSwapData = initOrObject.initialSwapData;
139
+ this.btcAmountSwap = initOrObject.btcAmountSwap;
140
+ this.btcAmountGas = initOrObject.btcAmountGas;
141
+ this.gasSwapFeeBtc = initOrObject.gasSwapFeeBtc;
142
+ this.gasSwapFee = initOrObject.gasSwapFee;
143
+ this.gasPricingInfo = initOrObject.gasPricingInfo;
144
+ this.lnurl = initOrObject.lnurl;
145
+ this.lnurlK1 = initOrObject.lnurlK1;
146
+ this.lnurlCallback = initOrObject.lnurlCallback;
147
+ this.usesClaimHashAsId = true;
148
+ }
149
+ else {
150
+ this.pr = initOrObject.pr;
151
+ this.secret = initOrObject.secret;
152
+ if (initOrObject.initialSwapData == null) {
153
+ this.initialSwapData = this._data;
154
+ }
155
+ else {
156
+ this.initialSwapData = base_1.SwapData.deserialize(initOrObject.initialSwapData);
157
+ }
158
+ this.btcAmountSwap = (0, Utils_1.toBigInt)(initOrObject.btcAmountSwap);
159
+ this.btcAmountGas = (0, Utils_1.toBigInt)(initOrObject.btcAmountGas);
160
+ this.gasSwapFeeBtc = (0, Utils_1.toBigInt)(initOrObject.gasSwapFeeBtc);
161
+ this.gasSwapFee = (0, Utils_1.toBigInt)(initOrObject.gasSwapFee);
162
+ this.gasPricingInfo = (0, PriceInfoType_1.deserializePriceInfoType)(initOrObject.gasPricingInfo);
163
+ this._commitTxId = initOrObject.commitTxId;
164
+ this._claimTxId = initOrObject.claimTxId;
165
+ this._commitedAt = initOrObject.commitedAt;
166
+ this.lnurl = initOrObject.lnurl;
167
+ this.lnurlK1 = initOrObject.lnurlK1;
168
+ this.lnurlCallback = initOrObject.lnurlCallback;
169
+ this.prPosted = initOrObject.prPosted;
170
+ this.usesClaimHashAsId = initOrObject.usesClaimHashAsId ?? false;
171
+ }
172
+ this.tryRecomputeSwapPrice();
173
+ this.logger = (0, Logger_1.getLogger)("FromBTCLNAuto(" + this.getIdentifierHashString() + "): ");
174
+ }
175
+ /**
176
+ * @inheritDoc
177
+ * @internal
178
+ */
179
+ getSwapData() {
180
+ return this._data ?? this.initialSwapData;
181
+ }
182
+ /**
183
+ * @inheritDoc
184
+ * @internal
185
+ */
186
+ upgradeVersion() { }
187
+ /**
188
+ * @inheritDoc
189
+ * @internal
190
+ */
191
+ tryRecomputeSwapPrice() {
192
+ if (this.pricingInfo == null || this.btcAmountSwap == null)
193
+ return;
194
+ if (this.pricingInfo.swapPriceUSatPerToken == null) {
195
+ const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
196
+ this.pricingInfo = this.wrapper._prices.recomputePriceInfoReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken());
197
+ this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
198
+ }
199
+ }
200
+ //////////////////////////////
201
+ //// Pricing
202
+ /**
203
+ * @inheritDoc
204
+ */
205
+ async refreshPriceData() {
206
+ if (this.pricingInfo == null || this.btcAmountSwap == null)
207
+ return;
208
+ const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
209
+ this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken(), undefined, undefined, this.swapFeeBtc);
210
+ this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
211
+ }
212
+ //////////////////////////////
213
+ //// Getters & utils
214
+ /**
215
+ * @inheritDoc
216
+ * @internal
217
+ */
218
+ _getEscrowHash() {
219
+ //Use claim hash in case the data is not yet known
220
+ return this._data == null ? this.initialSwapData?.getClaimHash() : this._data?.getEscrowHash();
221
+ }
222
+ /**
223
+ * @inheritDoc
224
+ * @internal
225
+ */
226
+ _getInitiator() {
227
+ return this.getSwapData().getClaimer();
228
+ }
229
+ /**
230
+ * @inheritDoc
231
+ */
232
+ getId() {
233
+ return this.getIdentifierHashString();
234
+ }
235
+ /**
236
+ * @inheritDoc
237
+ */
238
+ getOutputAddress() {
239
+ return this._getInitiator();
240
+ }
241
+ /**
242
+ * @inheritDoc
243
+ */
244
+ getOutputTxId() {
245
+ return this._claimTxId ?? null;
246
+ }
247
+ /**
248
+ * @inheritDoc
249
+ */
250
+ requiresAction() {
251
+ return this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
252
+ }
253
+ /**
254
+ * @inheritDoc
255
+ * @internal
256
+ */
257
+ getIdentifierHashString() {
258
+ const id = this.usesClaimHashAsId
259
+ ? this.getClaimHash()
260
+ : this.getPaymentHash().toString("hex");
261
+ if (this._randomNonce == null)
262
+ return id;
263
+ return id + this._randomNonce;
264
+ }
265
+ /**
266
+ * Returns the payment hash of the swap and lightning network invoice, or `null` if not known (i.e. if
267
+ * the swap was recovered from on-chain data, the payment hash might not be known)
268
+ *
269
+ * @internal
270
+ */
271
+ getPaymentHash() {
272
+ if (this.pr == null)
273
+ return null;
274
+ if (this.pr.toLowerCase().startsWith("ln")) {
275
+ const parsed = (0, bolt11_1.decode)(this.pr);
276
+ if (parsed.tagsObject.payment_hash == null)
277
+ throw new Error("Swap invoice has no payment hash field!");
278
+ return buffer_1.Buffer.from(parsed.tagsObject.payment_hash, "hex");
279
+ }
280
+ return buffer_1.Buffer.from(this.pr, "hex");
281
+ }
282
+ /**
283
+ * @inheritDoc
284
+ */
285
+ getInputAddress() {
286
+ return this.lnurl ?? this.pr ?? null;
287
+ }
288
+ /**
289
+ * @inheritDoc
290
+ */
291
+ getInputTxId() {
292
+ const paymentHash = this.getPaymentHash();
293
+ if (paymentHash == null)
294
+ return null;
295
+ return paymentHash.toString("hex");
296
+ }
297
+ /**
298
+ * Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap
299
+ */
300
+ getAddress() {
301
+ return this.pr ?? "";
302
+ }
303
+ /**
304
+ * @inheritDoc
305
+ */
306
+ getHyperlink() {
307
+ return this.pr == null ? "" : "lightning:" + this.pr.toUpperCase();
308
+ }
309
+ /**
310
+ * Returns the timeout time (in UNIX milliseconds) when the swap will definitelly be considered as expired
311
+ * if the LP doesn't make it expired sooner
312
+ */
313
+ getDefinitiveExpiryTime() {
314
+ if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
315
+ return 0;
316
+ const decoded = (0, bolt11_1.decode)(this.pr);
317
+ if (decoded.tagsObject.min_final_cltv_expiry == null)
318
+ throw new Error("Swap invoice doesn't contain final ctlv delta field!");
319
+ if (decoded.timeExpireDate == null)
320
+ throw new Error("Swap invoice doesn't contain expiry date field!");
321
+ const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
322
+ const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper._options.bitcoinBlocktime * this.wrapper._options.safetyFactor;
323
+ return (decoded.timeExpireDate + finalCltvExpiryDelay) * 1000;
324
+ }
325
+ /**
326
+ * Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
327
+ */
328
+ getHtlcTimeoutTime() {
329
+ return this._data == null ? null : Number(this.wrapper._getHtlcTimeout(this._data)) * 1000;
330
+ }
331
+ /**
332
+ * @inheritDoc
333
+ */
334
+ isFinished() {
335
+ return this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED || this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state === FromBTCLNAutoSwapState.FAILED;
336
+ }
337
+ /**
338
+ * @inheritDoc
339
+ */
340
+ isClaimable() {
341
+ return this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
342
+ }
343
+ /**
344
+ * @inheritDoc
345
+ */
346
+ isSuccessful() {
347
+ return this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED;
348
+ }
349
+ /**
350
+ * @inheritDoc
351
+ */
352
+ isFailed() {
353
+ return this._state === FromBTCLNAutoSwapState.FAILED || this._state === FromBTCLNAutoSwapState.EXPIRED;
354
+ }
355
+ /**
356
+ * @inheritDoc
357
+ */
358
+ isInProgress() {
359
+ return (this._state === FromBTCLNAutoSwapState.PR_CREATED && this.initiated) ||
360
+ (this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.initiated) ||
361
+ this._state === FromBTCLNAutoSwapState.PR_PAID ||
362
+ this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED;
363
+ }
364
+ /**
365
+ * @inheritDoc
366
+ */
367
+ isQuoteExpired() {
368
+ return this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED;
369
+ }
370
+ /**
371
+ * @inheritDoc
372
+ */
373
+ isQuoteSoftExpired() {
374
+ return this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED;
375
+ }
376
+ /**
377
+ * @inheritDoc
378
+ */
379
+ _verifyQuoteDefinitelyExpired() {
380
+ return Promise.resolve(this.getDefinitiveExpiryTime() < Date.now());
381
+ }
382
+ /**
383
+ * @inheritDoc
384
+ */
385
+ _verifyQuoteValid() {
386
+ return Promise.resolve(this.getQuoteExpiry() > Date.now());
387
+ }
388
+ //////////////////////////////
389
+ //// Amounts & fees
390
+ /**
391
+ * Returns the satoshi amount of the lightning network invoice, or `null` if the lightning network
392
+ * invoice is not known (i.e. when the swap was recovered from on-chain data, the paid invoice
393
+ * cannot be recovered because it is purely off-chain)
394
+ *
395
+ * @internal
396
+ */
397
+ getLightningInvoiceSats() {
398
+ if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
399
+ return null;
400
+ const parsed = (0, bolt11_1.decode)(this.pr);
401
+ if (parsed.millisatoshis == null)
402
+ throw new Error("Swap invoice doesn't contain msat amount field!");
403
+ return (BigInt(parsed.millisatoshis) + 999n) / 1000n;
404
+ }
405
+ /**
406
+ * Returns the watchtower fee paid in BTC satoshis, or null if known (i.e. if the swap was recovered from
407
+ * on-chain data)
408
+ *
409
+ * @protected
410
+ */
411
+ getWatchtowerFeeAmountBtc() {
412
+ if (this.btcAmountGas == null)
413
+ return null;
414
+ return (this.btcAmountGas - this.gasSwapFeeBtc) * this.getSwapData().getClaimerBounty() / this.getSwapData().getTotalDeposit();
415
+ }
416
+ /**
417
+ * Returns the input amount for the actual swap (excluding the input amount used to cover the "gas drop"
418
+ * part of the swap), excluding fees
419
+ *
420
+ * @internal
421
+ */
422
+ getInputSwapAmountWithoutFee() {
423
+ if (this.btcAmountSwap == null)
424
+ return null;
425
+ return this.btcAmountSwap - this.swapFeeBtc;
426
+ }
427
+ /**
428
+ * Returns the input amount purely for the "gas drop" part of the swap (this much BTC in sats will be
429
+ * swapped into the native gas token on the destination chain), excluding fees
430
+ *
431
+ * @internal
432
+ */
433
+ getInputGasAmountWithoutFee() {
434
+ if (this.btcAmountGas == null)
435
+ return null;
436
+ return this.btcAmountGas - this.gasSwapFeeBtc;
437
+ }
438
+ /**
439
+ * Get total btc amount in sats on the input, excluding the swap fee and watchtower fee
440
+ *
441
+ * @internal
442
+ */
443
+ getInputAmountWithoutFee() {
444
+ if (this.btcAmountGas == null || this.btcAmountSwap == null)
445
+ return null;
446
+ return this.getInputSwapAmountWithoutFee() + this.getInputGasAmountWithoutFee() - this.getWatchtowerFeeAmountBtc();
447
+ }
448
+ /**
449
+ * Returns the "would be" output amount if the swap charged no swap fee
450
+ *
451
+ * @internal
452
+ */
453
+ getOutputAmountWithoutFee() {
454
+ return this.getSwapData().getAmount() + this.swapFee;
455
+ }
456
+ /**
457
+ * @inheritDoc
458
+ */
459
+ getInputToken() {
460
+ return Token_1.BitcoinTokens.BTCLN;
461
+ }
462
+ /**
463
+ * @inheritDoc
464
+ */
465
+ getInput() {
466
+ return (0, TokenAmount_1.toTokenAmount)(this.getLightningInvoiceSats(), this.inputToken, this.wrapper._prices, this.pricingInfo);
467
+ }
468
+ /**
469
+ * @inheritDoc
470
+ */
471
+ getInputWithoutFee() {
472
+ return (0, TokenAmount_1.toTokenAmount)(this.getInputAmountWithoutFee(), this.inputToken, this.wrapper._prices, this.pricingInfo);
473
+ }
474
+ /**
475
+ * @inheritDoc
476
+ */
477
+ getOutputToken() {
478
+ return this.wrapper._tokens[this.getSwapData().getToken()];
479
+ }
480
+ /**
481
+ * @inheritDoc
482
+ */
483
+ getOutput() {
484
+ return (0, TokenAmount_1.toTokenAmount)(this.getSwapData().getAmount(), this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo);
485
+ }
486
+ /**
487
+ * @inheritDoc
488
+ */
489
+ getGasDropOutput() {
490
+ return (0, TokenAmount_1.toTokenAmount)(this.getSwapData().getSecurityDeposit() - this.getSwapData().getClaimerBounty(), this.wrapper._tokens[this.getSwapData().getDepositToken()], this.wrapper._prices, this.gasPricingInfo);
491
+ }
492
+ /**
493
+ * Returns the swap fee charged by the intermediary (LP) on this swap
494
+ *
495
+ * @internal
496
+ */
497
+ getSwapFee() {
498
+ if (this.pricingInfo == null)
499
+ throw new Error("No pricing info known, cannot estimate fee!");
500
+ const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
501
+ const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
502
+ * (10n ** BigInt(outputToken.decimals))
503
+ * 1000000n
504
+ / this.pricingInfo.swapPriceUSatPerToken;
505
+ const feeWithoutBaseFee = this.gasSwapFeeBtc + this.swapFeeBtc - this.pricingInfo.satsBaseFee;
506
+ const inputSats = this.getLightningInvoiceSats();
507
+ const swapFeePPM = inputSats != null
508
+ ? feeWithoutBaseFee * 1000000n / (inputSats - this.swapFeeBtc - this.gasSwapFeeBtc)
509
+ : 0n;
510
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(this.swapFeeBtc + this.gasSwapFeeBtc, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
511
+ return {
512
+ amountInSrcToken,
513
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
514
+ currentUsdValue: amountInSrcToken.currentUsdValue,
515
+ pastUsdValue: amountInSrcToken.pastUsdValue,
516
+ usdValue: amountInSrcToken.usdValue,
517
+ composition: {
518
+ base: (0, TokenAmount_1.toTokenAmount)(this.pricingInfo.satsBaseFee, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo),
519
+ percentage: (0, PercentagePPM_1.ppmToPercentage)(swapFeePPM)
520
+ }
521
+ };
522
+ }
523
+ /**
524
+ * Returns the fee to be paid to watchtowers on the destination chain to automatically
525
+ * process and settle this swap without requiring any user interaction
526
+ *
527
+ * @internal
528
+ */
529
+ getWatchtowerFee() {
530
+ if (this.pricingInfo == null)
531
+ throw new Error("No pricing info known, cannot estimate fee!");
532
+ const btcWatchtowerFee = this.getWatchtowerFeeAmountBtc();
533
+ const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
534
+ const watchtowerFeeInOutputToken = btcWatchtowerFee == null ? 0n : btcWatchtowerFee
535
+ * (10n ** BigInt(outputToken.decimals))
536
+ * 1000000n
537
+ / this.pricingInfo.swapPriceUSatPerToken;
538
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(btcWatchtowerFee, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
539
+ return {
540
+ amountInSrcToken,
541
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)(watchtowerFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
542
+ currentUsdValue: amountInSrcToken.currentUsdValue,
543
+ usdValue: amountInSrcToken.usdValue,
544
+ pastUsdValue: amountInSrcToken.pastUsdValue
545
+ };
546
+ }
547
+ /**
548
+ * @inheritDoc
549
+ */
550
+ getFee() {
551
+ const swapFee = this.getSwapFee();
552
+ const watchtowerFee = this.getWatchtowerFee();
553
+ const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount, Token_1.BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
554
+ return {
555
+ amountInSrcToken,
556
+ amountInDstToken: (0, TokenAmount_1.toTokenAmount)(swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount, this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo),
557
+ currentUsdValue: amountInSrcToken.currentUsdValue,
558
+ usdValue: amountInSrcToken.usdValue,
559
+ pastUsdValue: amountInSrcToken.pastUsdValue
560
+ };
561
+ }
562
+ /**
563
+ * @inheritDoc
564
+ */
565
+ getFeeBreakdown() {
566
+ return [
567
+ {
568
+ type: FeeType_1.FeeType.SWAP,
569
+ fee: this.getSwapFee()
570
+ },
571
+ {
572
+ type: FeeType_1.FeeType.NETWORK_OUTPUT,
573
+ fee: this.getWatchtowerFee()
574
+ }
575
+ ];
576
+ }
577
+ isValidSecretPreimage(secret) {
578
+ const paymentHash = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex")));
579
+ const claimHash = this._contract.getHashForHtlc(paymentHash).toString("hex");
580
+ return this.getSwapData().getClaimHash() === claimHash;
581
+ }
582
+ /**
583
+ * Sets the secret preimage for the swap, in case it is not known already
584
+ *
585
+ * @param secret Secret preimage that matches the expected payment hash
586
+ *
587
+ * @throws {Error} If an invalid secret preimage is provided
588
+ */
589
+ async setSecretPreimage(secret) {
590
+ if (!this.isValidSecretPreimage(secret))
591
+ throw new Error("Invalid secret preimage provided, hash doesn't match!");
592
+ this.secret = secret;
593
+ await this._broadcastSecret().catch(e => this.logger.error("setSecretPreimage(): Failed to broadcast swap secret: ", e));
594
+ }
595
+ /**
596
+ * Returns whether the secret preimage for this swap is known
597
+ */
598
+ hasSecretPreimage() {
599
+ return this.secret != null;
600
+ }
601
+ //////////////////////////////
602
+ //// Execution
603
+ /**
604
+ * Executes the swap with the provided bitcoin lightning network wallet or LNURL
605
+ *
606
+ * @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
607
+ * link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
608
+ * @param callbacks Callbacks to track the progress of the swap
609
+ * @param options Optional options for the swap like AbortSignal, and timeouts/intervals
610
+ * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
611
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
612
+ *
613
+ * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
614
+ * user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
615
+ */
616
+ async execute(walletOrLnurlWithdraw, callbacks, options) {
617
+ if (this._state === FromBTCLNAutoSwapState.FAILED)
618
+ throw new Error("Swap failed!");
619
+ if (this._state === FromBTCLNAutoSwapState.EXPIRED)
620
+ throw new Error("Swap HTLC expired!");
621
+ if (this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
622
+ throw new Error("Swap quote expired!");
623
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
624
+ throw new Error("Swap already settled!");
625
+ let abortSignal = options?.abortSignal;
626
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
627
+ if (walletOrLnurlWithdraw != null && this.lnurl == null) {
628
+ if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
629
+ throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
630
+ if (typeof (walletOrLnurlWithdraw) === "string" || (0, LNURLWithdraw_1.isLNURLWithdraw)(walletOrLnurlWithdraw)) {
631
+ await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
632
+ }
633
+ else {
634
+ const paymentPromise = walletOrLnurlWithdraw.payInvoice(this.pr);
635
+ const abortController = new AbortController();
636
+ paymentPromise.catch(e => abortController.abort(e));
637
+ if (options?.abortSignal != null)
638
+ options.abortSignal.addEventListener("abort", () => abortController.abort(options?.abortSignal?.reason));
639
+ abortSignal = abortController.signal;
640
+ }
641
+ }
642
+ }
643
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.PR_PAID) {
644
+ const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
645
+ if (!paymentSuccess)
646
+ throw new Error("Failed to receive lightning network payment");
647
+ }
648
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
649
+ return true;
650
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
651
+ if (this.secret == null && options?.secret == null)
652
+ throw new Error("Tried to wait till settlement, but no secret pre-image is known, please pass the secret pre-image as an argument!");
653
+ const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal, options?.secret);
654
+ if (success && callbacks?.onSwapSettled != null)
655
+ callbacks.onSwapSettled(this.getOutputTxId());
656
+ return success;
657
+ }
658
+ throw new Error("Invalid state reached!");
659
+ }
660
+ /**
661
+ * @internal
662
+ */
663
+ async _getExecutionStatus(options) {
664
+ if (options?.secret != null)
665
+ await this.setSecretPreimage(options.secret);
666
+ const state = this._state;
667
+ const now = Date.now();
668
+ let lightningPaymentStatus = "inactive";
669
+ let destinationSettlementStatus = "inactive";
670
+ let buildCurrentAction = async () => undefined;
671
+ switch (state) {
672
+ case FromBTCLNAutoSwapState.PR_CREATED: {
673
+ const quoteValid = await this._verifyQuoteValid();
674
+ lightningPaymentStatus = quoteValid ? "awaiting" : "soft_expired";
675
+ if (quoteValid && this.pr != null && this.pr.toLowerCase().startsWith("ln")) {
676
+ buildCurrentAction = this._buildLightningPaymentAction.bind(this);
677
+ }
678
+ break;
679
+ }
680
+ case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
681
+ lightningPaymentStatus = "soft_expired";
682
+ break;
683
+ case FromBTCLNAutoSwapState.QUOTE_EXPIRED:
684
+ lightningPaymentStatus = "expired";
685
+ break;
686
+ case FromBTCLNAutoSwapState.PR_PAID:
687
+ lightningPaymentStatus = "received";
688
+ destinationSettlementStatus = "waiting_lp";
689
+ buildCurrentAction = this._buildWaitLpAction.bind(this);
690
+ break;
691
+ case FromBTCLNAutoSwapState.CLAIM_COMMITED:
692
+ lightningPaymentStatus = "received";
693
+ if (this._commitedAt == null ||
694
+ options?.maxWaitTillAutomaticSettlementSeconds === 0 ||
695
+ (now - this._commitedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60) * 1000) {
696
+ destinationSettlementStatus = "awaiting_manual";
697
+ if (this.hasSecretPreimage()) {
698
+ //TODO: Maybe add an action that would prompt the user to reveal the pre-image
699
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
700
+ }
701
+ }
702
+ else {
703
+ destinationSettlementStatus = "awaiting_automatic";
704
+ //TODO: Maybe add an action that would prompt the user to reveal the pre-image
705
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
706
+ }
707
+ break;
708
+ case FromBTCLNAutoSwapState.CLAIM_CLAIMED:
709
+ lightningPaymentStatus = "confirmed";
710
+ destinationSettlementStatus = "settled";
711
+ break;
712
+ case FromBTCLNAutoSwapState.EXPIRED:
713
+ case FromBTCLNAutoSwapState.FAILED:
714
+ lightningPaymentStatus = "expired";
715
+ destinationSettlementStatus = "expired";
716
+ break;
717
+ }
718
+ return {
719
+ steps: [
720
+ {
721
+ type: "Payment",
722
+ side: "source",
723
+ chain: "LIGHTNING",
724
+ title: "Lightning payment",
725
+ description: "Pay the Lightning network invoice to initiate the swap",
726
+ status: lightningPaymentStatus,
727
+ initTxId: this.getInputTxId(),
728
+ settleTxId: lightningPaymentStatus === "confirmed" ? this.getInputTxId() : undefined
729
+ },
730
+ {
731
+ type: "Settlement",
732
+ side: "destination",
733
+ chain: this.chainIdentifier,
734
+ title: "Destination settlement",
735
+ description: `Wait for the LP to initiate on the ${this.chainIdentifier} side, then wait for automatic settlement, or settle manually if it takes too long`,
736
+ status: destinationSettlementStatus,
737
+ initTxId: this._commitTxId,
738
+ settleTxId: this._claimTxId
739
+ }
740
+ ],
741
+ buildCurrentAction,
742
+ state
743
+ };
744
+ }
745
+ /**
746
+ * @internal
747
+ */
748
+ async _buildLightningPaymentAction() {
749
+ return {
750
+ type: "SendToAddress",
751
+ name: "Deposit on Lightning",
752
+ description: "Pay the lightning network invoice to initiate the swap",
753
+ chain: "LIGHTNING",
754
+ txs: [{
755
+ type: "BOLT11_PAYMENT_REQUEST",
756
+ address: this.getAddress(),
757
+ hyperlink: this.getHyperlink(),
758
+ amount: this.getInput()
759
+ }],
760
+ waitForTransactions: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
761
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for lightning payment");
762
+ const success = await this._waitForLpPaymentReceived(pollIntervalSeconds, abortController.signal);
763
+ if (!success)
764
+ throw new Error("Quote expired while waiting for Lightning payment");
765
+ return this.getInputTxId();
766
+ }
767
+ };
768
+ }
769
+ /**
770
+ * @internal
771
+ */
772
+ async _buildWaitLpAction() {
773
+ return {
774
+ type: "Wait",
775
+ name: "Awaiting LP payout",
776
+ description: "Wait for the LP to create the swap HTLC on the destination smart chain",
777
+ pollTimeSeconds: 5,
778
+ expectedTimeSeconds: 10,
779
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
780
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal, maxWaitTimeSeconds, "Timed out waiting for LP payout");
781
+ await this.waitTillCommited(pollIntervalSeconds, abortController.signal);
782
+ }
783
+ };
784
+ }
785
+ /**
786
+ * @internal
787
+ */
788
+ async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds) {
789
+ return {
790
+ type: "Wait",
791
+ name: "Automatic settlement",
792
+ description: "Wait for automatic settlement by the watchtower",
793
+ pollTimeSeconds: 5,
794
+ expectedTimeSeconds: 10,
795
+ wait: async (maxWaitTimeSeconds, pollIntervalSeconds, abortSignal) => {
796
+ await this.waitTillClaimed(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, undefined, pollIntervalSeconds);
797
+ }
798
+ };
799
+ }
800
+ /**
801
+ * @inheritDoc
802
+ * @internal
803
+ */
804
+ async _submitExecutionTransactions(txs, abortSignal, requiredStates, idempotent) {
805
+ const parsedTxs = [];
806
+ for (let tx of txs) {
807
+ parsedTxs.push(typeof (tx) === "string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
808
+ }
809
+ if (idempotent) {
810
+ // Handle idempotent calls
811
+ if (this.wrapper._chain.getTxId != null) {
812
+ const txIds = await Promise.all(parsedTxs.map(tx => this.wrapper._chain.getTxId(tx)));
813
+ const foundTxId = txIds.find(txId => this._claimTxId === txId);
814
+ if (foundTxId != null)
815
+ return txIds;
816
+ }
817
+ }
818
+ if (requiredStates != null && !requiredStates.includes(this._state))
819
+ throw new Error("Swap state has changed before transactions were submitted!");
820
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
821
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
822
+ await this.waitTillClaimed(undefined, abortSignal);
823
+ return txIds;
824
+ }
825
+ throw new Error("Invalid swap state for transaction submission!");
826
+ }
827
+ /**
828
+ * @internal
829
+ */
830
+ async _buildClaimSmartChainTxAction(actionOptions) {
831
+ const signerAddress = await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
832
+ return {
833
+ type: "SignSmartChainTransaction",
834
+ name: "Settle manually",
835
+ description: "Manually settle (claim) the swap on the destination smart chain",
836
+ chain: this.chainIdentifier,
837
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner, actionOptions?.secret)),
838
+ submitTransactions: async (txs, abortSignal, idempotent) => {
839
+ return this._submitExecutionTransactions(txs, abortSignal, [FromBTCLNAutoSwapState.CLAIM_COMMITED], idempotent);
840
+ },
841
+ requiredSigner: signerAddress ?? this._getInitiator()
842
+ };
843
+ }
844
+ /**
845
+ *
846
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
847
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
848
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
849
+ * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
850
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
851
+ */
852
+ async getExecutionAction(options) {
853
+ const executionStatus = await this._getExecutionStatus(options);
854
+ return executionStatus.buildCurrentAction(options);
855
+ }
856
+ /**
857
+ * @inheritDoc
858
+ */
859
+ // TODO: Figure how we gonna trigger an LNURL-withdraw with the execution actions
860
+ async getExecutionStatus(options) {
861
+ const executionStatus = await this._getExecutionStatus(options);
862
+ return {
863
+ steps: executionStatus.steps,
864
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
865
+ stateInfo: this._getStateInfo(executionStatus.state)
866
+ };
867
+ }
868
+ /**
869
+ * @inheritDoc
870
+ */
871
+ async getExecutionSteps(options) {
872
+ return (await this._getExecutionStatus(options)).steps;
873
+ }
874
+ //////////////////////////////
875
+ //// Payment
876
+ /**
877
+ * Checks whether the LP received the LN payment
878
+ *
879
+ * @param save If the new swap state should be saved
880
+ *
881
+ * @internal
882
+ */
883
+ async _checkIntermediaryPaymentReceived(save = true) {
884
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID ||
885
+ this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED ||
886
+ this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED ||
887
+ this._state === FromBTCLNAutoSwapState.FAILED ||
888
+ this._state === FromBTCLNAutoSwapState.EXPIRED)
889
+ return true;
890
+ if (this._state === FromBTCLNAutoSwapState.QUOTE_EXPIRED)
891
+ return false;
892
+ if (this.url == null)
893
+ return false;
894
+ const paymentHash = this.getPaymentHash();
895
+ if (paymentHash == null)
896
+ throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
897
+ const resp = await this.wrapper._lpApi.getInvoiceStatus(this.url, paymentHash.toString("hex"));
898
+ switch (resp.code) {
899
+ case IntermediaryAPI_1.InvoiceStatusResponseCodes.PAID:
900
+ const data = new (this.wrapper._swapDataDeserializer(this._contractVersion))(resp.data.data);
901
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
902
+ try {
903
+ await this._saveRealSwapData(data, save);
904
+ return true;
905
+ }
906
+ catch (e) { }
907
+ return null;
908
+ case IntermediaryAPI_1.InvoiceStatusResponseCodes.EXPIRED:
909
+ this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
910
+ this.initiated = true;
911
+ if (save)
912
+ await this._saveAndEmit();
913
+ return false;
914
+ default:
915
+ return null;
916
+ }
917
+ }
918
+ /**
919
+ * Checks and overrides the swap data for this swap. This is used to set the swap data from
920
+ * on-chain events.
921
+ *
922
+ * @param data Swap data of the escrow swap
923
+ * @param save If the new data should be saved
924
+ *
925
+ * @internal
926
+ */
927
+ async _saveRealSwapData(data, save) {
928
+ await this.checkIntermediaryReturnedData(data);
929
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
930
+ this._state = FromBTCLNAutoSwapState.PR_PAID;
931
+ this._data = data;
932
+ this.initiated = true;
933
+ if (save)
934
+ await this._saveAndEmit();
935
+ return true;
936
+ }
937
+ return false;
938
+ }
939
+ /**
940
+ * Waits till a lightning network payment is received by the intermediary, and the intermediary
941
+ * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
942
+ * for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
943
+ * or settle manually using the {@link claim} or {@link txsClaim} functions.
944
+ *
945
+ * If this swap is using an LNURL-withdraw link as input, it automatically posts the
946
+ * generated invoice to the LNURL service to pay it.
947
+ *
948
+ * @remarks For internal use, rather use {@link waitForPayment} which properly waits till the LP also
949
+ * offers a swap HTLC.
950
+ *
951
+ * @param abortSignal Abort signal to stop waiting for payment
952
+ * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
953
+ *
954
+ * @internal
955
+ */
956
+ async _waitForLpPaymentReceived(checkIntervalSeconds, abortSignal) {
957
+ checkIntervalSeconds ??= 5;
958
+ if (this._state >= FromBTCLNAutoSwapState.PR_PAID)
959
+ return true;
960
+ if (this._state !== FromBTCLNAutoSwapState.PR_CREATED)
961
+ throw new Error("Must be in PR_CREATED state!");
962
+ const abortController = new AbortController();
963
+ if (abortSignal != null)
964
+ abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
965
+ let save = false;
966
+ if (this.lnurl != null && this.lnurlK1 != null && this.lnurlCallback != null && !this.prPosted) {
967
+ if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
968
+ throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
969
+ LNURL_1.LNURL.postInvoiceToLNURLWithdraw({ k1: this.lnurlK1, callback: this.lnurlCallback }, this.pr).catch(e => {
970
+ this.lnurlFailSignal.abort(e);
971
+ });
972
+ this.prPosted = true;
973
+ save ||= true;
974
+ }
975
+ if (!this.initiated) {
976
+ this.initiated = true;
977
+ save ||= true;
978
+ }
979
+ if (save)
980
+ await this._saveAndEmit();
981
+ let lnurlFailListener = () => abortController.abort(this.lnurlFailSignal.signal.reason);
982
+ this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
983
+ this.lnurlFailSignal.signal.throwIfAborted();
984
+ const paymentHash = this.getPaymentHash();
985
+ if (paymentHash == null)
986
+ throw new Error("Swap payment hash not available, the swap was probably recovered!");
987
+ if (this.wrapper._messenger.warmup != null)
988
+ await this.wrapper._messenger.warmup().catch(e => {
989
+ this.logger.warn("waitForPayment(): Failed to warmup messenger: ", e);
990
+ });
991
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
992
+ const promises = [
993
+ this.waitTillState(FromBTCLNAutoSwapState.PR_PAID, "gte", abortController.signal).then(() => true)
994
+ ];
995
+ if (this.url != null)
996
+ promises.push((async () => {
997
+ let resp = { code: IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING, msg: "" };
998
+ while (!abortController.signal.aborted && resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING) {
999
+ resp = await this.wrapper._lpApi.getInvoiceStatus(this.url, paymentHash.toString("hex"));
1000
+ if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PENDING)
1001
+ await (0, TimeoutUtils_1.timeoutPromise)(checkIntervalSeconds * 1000, abortController.signal);
1002
+ }
1003
+ this.lnurlFailSignal.signal.removeEventListener("abort", lnurlFailListener);
1004
+ abortController.signal.throwIfAborted();
1005
+ if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.PAID) {
1006
+ const swapData = new (this.wrapper._swapDataDeserializer(this._contractVersion))(resp.data.data);
1007
+ return await this._saveRealSwapData(swapData, true);
1008
+ }
1009
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1010
+ if (resp.code === IntermediaryAPI_1.InvoiceStatusResponseCodes.EXPIRED) {
1011
+ await this._saveAndEmit(FromBTCLNAutoSwapState.QUOTE_EXPIRED);
1012
+ }
1013
+ return false;
1014
+ }
1015
+ })());
1016
+ const paymentResult = await Promise.race(promises);
1017
+ abortController.abort();
1018
+ if (!paymentResult)
1019
+ return false;
1020
+ }
1021
+ return this._state >= FromBTCLNAutoSwapState.PR_PAID;
1022
+ }
1023
+ /**
1024
+ * Checks the data returned by the intermediary in the payment auth request
1025
+ *
1026
+ * @param data Parsed swap data as returned by the intermediary
1027
+ *
1028
+ * @throws {IntermediaryError} If the returned are not valid
1029
+ * @throws {Error} If the swap is already committed on-chain
1030
+ *
1031
+ * @private
1032
+ */
1033
+ async checkIntermediaryReturnedData(data) {
1034
+ if (!data.isPayOut())
1035
+ throw new IntermediaryError_1.IntermediaryError("Invalid not pay out");
1036
+ if (data.getType() !== base_1.ChainSwapType.HTLC)
1037
+ throw new IntermediaryError_1.IntermediaryError("Invalid swap type");
1038
+ if (!data.isOfferer(this.getSwapData().getOfferer()))
1039
+ throw new IntermediaryError_1.IntermediaryError("Invalid offerer used");
1040
+ if (!data.isClaimer(this._getInitiator()))
1041
+ throw new IntermediaryError_1.IntermediaryError("Invalid claimer used");
1042
+ if (!data.isToken(this.getSwapData().getToken()))
1043
+ throw new IntermediaryError_1.IntermediaryError("Invalid token used");
1044
+ if (data.getSecurityDeposit() !== this.getSwapData().getSecurityDeposit())
1045
+ throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
1046
+ if (data.getClaimerBounty() !== this.getSwapData().getClaimerBounty())
1047
+ throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
1048
+ if (data.getAmount() < this.getSwapData().getAmount())
1049
+ throw new IntermediaryError_1.IntermediaryError("Invalid amount received!");
1050
+ if (data.getClaimHash() !== this.getSwapData().getClaimHash())
1051
+ throw new IntermediaryError_1.IntermediaryError("Invalid payment hash used!");
1052
+ if (!data.isDepositToken(this.getSwapData().getDepositToken()))
1053
+ throw new IntermediaryError_1.IntermediaryError("Invalid deposit token used!");
1054
+ if (data.hasSuccessAction())
1055
+ throw new IntermediaryError_1.IntermediaryError("Invalid has success action");
1056
+ if (await this.wrapper._contract(this._contractVersion).isExpired(this._getInitiator(), data))
1057
+ throw new IntermediaryError_1.IntermediaryError("Not enough time to claim!");
1058
+ if (this.wrapper._getHtlcTimeout(data) <= (Date.now() / 1000))
1059
+ throw new IntermediaryError_1.IntermediaryError("HTLC expires too soon!");
1060
+ }
1061
+ /**
1062
+ * Waits till a lightning network payment is received by the intermediary, and the intermediary
1063
+ * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
1064
+ * for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
1065
+ * or settle manually using the {@link claim} or {@link txsClaim} functions.
1066
+ *
1067
+ * If this swap is using an LNURL-withdraw link as input, it automatically posts the
1068
+ * generated invoice to the LNURL service to pay it.
1069
+ *
1070
+ * @param onPaymentReceived Callback as for when the LP reports having received the ln payment
1071
+ * @param abortSignal Abort signal to stop waiting for payment
1072
+ * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
1073
+ */
1074
+ async waitForPayment(onPaymentReceived, checkIntervalSeconds, abortSignal) {
1075
+ checkIntervalSeconds ??= 5;
1076
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1077
+ await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1078
+ }
1079
+ if (this._state >= FromBTCLNAutoSwapState.CLAIM_COMMITED)
1080
+ return true;
1081
+ const success = await this._waitForLpPaymentReceived(checkIntervalSeconds, abortSignal);
1082
+ if (!success)
1083
+ return false;
1084
+ if (onPaymentReceived != null)
1085
+ onPaymentReceived(this.getInputTxId());
1086
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1087
+ await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1088
+ }
1089
+ return this._state >= FromBTCLNAutoSwapState.CLAIM_COMMITED;
1090
+ }
1091
+ //////////////////////////////
1092
+ //// Commit
1093
+ /**
1094
+ * Waits till the intermediary (LP) initiates the swap HTLC escrow on the destination smart chain side
1095
+ *
1096
+ * @param checkIntervalSeconds How often to check via a polling watchdog
1097
+ * @param abortSignal Abort signal
1098
+ *
1099
+ * @internal
1100
+ */
1101
+ async waitTillCommited(checkIntervalSeconds, abortSignal) {
1102
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
1103
+ return Promise.resolve();
1104
+ if (this._state !== FromBTCLNAutoSwapState.PR_PAID)
1105
+ throw new Error("Invalid state");
1106
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
1107
+ let result;
1108
+ try {
1109
+ result = await Promise.race([
1110
+ this.watchdogWaitTillCommited(checkIntervalSeconds, abortController.signal),
1111
+ this.waitTillState(FromBTCLNAutoSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
1112
+ ]);
1113
+ abortController.abort();
1114
+ }
1115
+ catch (e) {
1116
+ abortController.abort();
1117
+ throw e;
1118
+ }
1119
+ if (result === null) {
1120
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - HTLC expired");
1121
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1122
+ await this._saveAndEmit(FromBTCLNAutoSwapState.EXPIRED);
1123
+ }
1124
+ return;
1125
+ }
1126
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1127
+ if (typeof (result) === "object" && result.getInitTxId != null && this._commitTxId == null)
1128
+ this._commitTxId = await result.getInitTxId();
1129
+ this._commitedAt ??= Date.now();
1130
+ await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_COMMITED);
1131
+ }
1132
+ if (result === 0) {
1133
+ this.logger.debug("waitTillCommited(): Resolved from state changed");
1134
+ }
1135
+ else if (result != null) {
1136
+ this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1137
+ if (this.secret != null)
1138
+ await this._broadcastSecret().catch(e => {
1139
+ this.logger.error("waitTillCommited(): Error broadcasting swap secret: ", e);
1140
+ });
1141
+ }
1142
+ }
1143
+ //////////////////////////////
1144
+ //// Claim
1145
+ /**
1146
+ * @inheritDoc
1147
+ *
1148
+ * @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
1149
+ * @param secret A swap secret to use for the claim transaction, generally only needed if the swap
1150
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
1151
+ *
1152
+ * @throws {Error} If in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1153
+ */
1154
+ async txsClaim(_signer, secret) {
1155
+ let address = undefined;
1156
+ if (_signer != null) {
1157
+ if (typeof (_signer) === "string") {
1158
+ address = _signer;
1159
+ }
1160
+ else if ((0, base_1.isAbstractSigner)(_signer)) {
1161
+ address = _signer.getAddress();
1162
+ }
1163
+ else {
1164
+ address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
1165
+ }
1166
+ }
1167
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1168
+ throw new Error("Must be in CLAIM_COMMITED state!");
1169
+ if (this._data == null)
1170
+ throw new Error("Unknown data, wrong state?");
1171
+ const useSecret = secret ?? this.secret;
1172
+ if (useSecret == null)
1173
+ throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
1174
+ if (!this.isValidSecretPreimage(useSecret))
1175
+ throw new Error("Invalid swap secret pre-image provided!");
1176
+ return await this._contract.txsClaimWithSecret(address ?? this._getInitiator(), this._data, useSecret, true, true);
1177
+ }
1178
+ /**
1179
+ * @inheritDoc
1180
+ *
1181
+ * @param _signer Signer to sign the transactions with, can also be different to the initializer
1182
+ * @param abortSignal Abort signal to stop waiting for transaction confirmation
1183
+ * @param onBeforeTxSent
1184
+ * @param secret A swap secret to use for the claim transaction, generally only needed if the swap
1185
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
1186
+ */
1187
+ async claim(_signer, abortSignal, onBeforeTxSent, secret) {
1188
+ const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
1189
+ let txCount = 0;
1190
+ const txs = await this.txsClaim(_signer, secret);
1191
+ const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
1192
+ txCount++;
1193
+ if (onBeforeTxSent != null && txCount === 1)
1194
+ onBeforeTxSent(txId);
1195
+ return Promise.resolve();
1196
+ });
1197
+ this._claimTxId = result[0];
1198
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.EXPIRED || this._state === FromBTCLNAutoSwapState.FAILED) {
1199
+ await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
1200
+ }
1201
+ return result[0];
1202
+ }
1203
+ /**
1204
+ * Waits till the swap is successfully settled (claimed), should be called after sending the claim (settlement)
1205
+ * transactions manually to wait till the SDK processes the settlement and updates the swap state accordingly.
1206
+ *
1207
+ * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
1208
+ * @param abortSignal AbortSignal
1209
+ * @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
1210
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
1211
+ * @param pollIntervalSeconds How often to poll via the watchdog
1212
+ *
1213
+ * @throws {Error} If swap is in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1214
+ * @throws {Error} If the LP refunded sooner than we were able to claim
1215
+ * @returns {boolean} whether the swap was claimed in time or not
1216
+ */
1217
+ async waitTillClaimed(maxWaitTimeSeconds, abortSignal, secret, pollIntervalSeconds) {
1218
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_CLAIMED)
1219
+ return Promise.resolve(true);
1220
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1221
+ throw new Error("Invalid state (not CLAIM_COMMITED)");
1222
+ if (secret != null) {
1223
+ if (!this.isValidSecretPreimage(secret))
1224
+ throw new Error("Invalid swap secret pre-image provided!");
1225
+ this.secret = secret;
1226
+ }
1227
+ const abortController = new AbortController();
1228
+ if (abortSignal != null)
1229
+ abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
1230
+ let timedOut = false;
1231
+ if (maxWaitTimeSeconds != null) {
1232
+ const timeout = setTimeout(() => {
1233
+ timedOut = true;
1234
+ abortController.abort();
1235
+ }, maxWaitTimeSeconds * 1000);
1236
+ abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
1237
+ }
1238
+ let res;
1239
+ try {
1240
+ res = await Promise.race([
1241
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1242
+ this.waitTillState(FromBTCLNAutoSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
1243
+ this.waitTillState(FromBTCLNAutoSwapState.EXPIRED, "eq", abortController.signal).then(() => 1),
1244
+ ]);
1245
+ abortController.abort();
1246
+ }
1247
+ catch (e) {
1248
+ abortController.abort();
1249
+ if (timedOut)
1250
+ return false;
1251
+ throw e;
1252
+ }
1253
+ if (res === 0) {
1254
+ this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
1255
+ return true;
1256
+ }
1257
+ if (res === 1) {
1258
+ this.logger.debug("waitTillClaimed(): Resolved from state change (EXPIRED)");
1259
+ throw new Error("Swap expired during claiming");
1260
+ }
1261
+ this.logger.debug("waitTillClaimed(): Resolved from watchdog");
1262
+ if (res?.type === base_1.SwapCommitStateType.PAID) {
1263
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_CLAIMED) {
1264
+ this._claimTxId = await res.getClaimTxId();
1265
+ await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
1266
+ }
1267
+ }
1268
+ if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
1269
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_CLAIMED &&
1270
+ this._state !== FromBTCLNAutoSwapState.FAILED) {
1271
+ await this._saveAndEmit(FromBTCLNAutoSwapState.FAILED);
1272
+ }
1273
+ throw new Error("Swap expired during claiming");
1274
+ }
1275
+ return true;
1276
+ }
1277
+ //////////////////////////////
1278
+ //// LNURL
1279
+ /**
1280
+ * Whether this swap uses an LNURL-withdraw link
1281
+ */
1282
+ isLNURL() {
1283
+ return this.lnurl != null;
1284
+ }
1285
+ /**
1286
+ * Gets the used LNURL or `null` if this is not an LNURL-withdraw swap
1287
+ */
1288
+ getLNURL() {
1289
+ return this.lnurl ?? null;
1290
+ }
1291
+ /**
1292
+ * Pay the generated lightning network invoice with an LNURL-withdraw link, this
1293
+ * is useful when you want to display a lightning payment QR code and also want to
1294
+ * allow payments using LNURL-withdraw NFC cards.
1295
+ *
1296
+ * Note that the swap needs to be created **without** an LNURL to begin with for this function
1297
+ * to work. If this swap is already using an LNURL-withdraw link, this function throws.
1298
+ */
1299
+ async settleWithLNURLWithdraw(lnurl) {
1300
+ if (this._state !== FromBTCLNAutoSwapState.PR_CREATED &&
1301
+ this._state !== FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED)
1302
+ throw new Error("Must be in PR_CREATED state!");
1303
+ if (this.lnurl != null)
1304
+ throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
1305
+ let lnurlParams;
1306
+ if (typeof (lnurl) === "string") {
1307
+ const parsedLNURL = await LNURL_1.LNURL.getLNURL(lnurl);
1308
+ if (parsedLNURL == null || parsedLNURL.tag !== "withdrawRequest")
1309
+ throw new UserError_1.UserError("Invalid LNURL-withdraw to settle the swap");
1310
+ lnurlParams = parsedLNURL;
1311
+ }
1312
+ else {
1313
+ lnurlParams = lnurl.params;
1314
+ }
1315
+ if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
1316
+ throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
1317
+ LNURL_1.LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
1318
+ this.lnurl = lnurlParams.url;
1319
+ this.lnurlCallback = lnurlParams.callback;
1320
+ this.lnurlK1 = lnurlParams.k1;
1321
+ this.prPosted = true;
1322
+ await this._saveAndEmit();
1323
+ }
1324
+ //////////////////////////////
1325
+ //// Storage
1326
+ /**
1327
+ * @inheritDoc
1328
+ */
1329
+ serialize() {
1330
+ return {
1331
+ ...super.serialize(),
1332
+ data: this._data == null ? null : this._data.serialize(),
1333
+ commitTxId: this._commitTxId,
1334
+ claimTxId: this._claimTxId,
1335
+ commitedAt: this._commitedAt,
1336
+ btcAmountSwap: this.btcAmountSwap == null ? null : this.btcAmountSwap.toString(10),
1337
+ btcAmountGas: this.btcAmountGas == null ? null : this.btcAmountGas.toString(10),
1338
+ gasSwapFeeBtc: this.gasSwapFeeBtc == null ? null : this.gasSwapFeeBtc.toString(10),
1339
+ gasSwapFee: this.gasSwapFee == null ? null : this.gasSwapFee.toString(10),
1340
+ gasPricingInfo: (0, PriceInfoType_1.serializePriceInfoType)(this.gasPricingInfo),
1341
+ pr: this.pr,
1342
+ secret: this.secret,
1343
+ lnurl: this.lnurl,
1344
+ lnurlK1: this.lnurlK1,
1345
+ lnurlCallback: this.lnurlCallback,
1346
+ prPosted: this.prPosted,
1347
+ initialSwapData: this.initialSwapData.serialize(),
1348
+ usesClaimHashAsId: this.usesClaimHashAsId
1349
+ };
1350
+ }
1351
+ //////////////////////////////
1352
+ //// Swap ticks & sync
1353
+ /**
1354
+ * Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
1355
+ * data
1356
+ *
1357
+ * @private
1358
+ */
1359
+ async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
1360
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID ||
1361
+ this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED ||
1362
+ this._state === FromBTCLNAutoSwapState.EXPIRED) {
1363
+ //Check for expiry before the getCommitStatus to prevent race conditions
1364
+ let quoteExpired = false;
1365
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1366
+ quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
1367
+ }
1368
+ //Check if it's already successfully paid
1369
+ commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
1370
+ if (commitStatus != null && await this._forciblySetOnchainState(commitStatus))
1371
+ return true;
1372
+ if (this._state === FromBTCLNAutoSwapState.PR_PAID) {
1373
+ if (quoteExpired) {
1374
+ this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1375
+ return true;
1376
+ }
1377
+ }
1378
+ }
1379
+ return false;
1380
+ }
1381
+ /**
1382
+ * @inheritDoc
1383
+ * @internal
1384
+ */
1385
+ _shouldFetchOnchainState() {
1386
+ return this._state === FromBTCLNAutoSwapState.PR_PAID || this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state === FromBTCLNAutoSwapState.EXPIRED;
1387
+ }
1388
+ /**
1389
+ * @inheritDoc
1390
+ * @internal
1391
+ */
1392
+ _shouldFetchExpiryStatus() {
1393
+ return this._state === FromBTCLNAutoSwapState.PR_PAID;
1394
+ }
1395
+ /**
1396
+ * @inheritDoc
1397
+ * @internal
1398
+ */
1399
+ _shouldCheckIntermediary() {
1400
+ return this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1401
+ }
1402
+ /**
1403
+ * @inheritDoc
1404
+ * @internal
1405
+ */
1406
+ async _sync(save, quoteDefinitelyExpired, commitStatus, skipLpCheck) {
1407
+ let changed = false;
1408
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1409
+ if (this._state !== FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.getQuoteExpiry() < Date.now()) {
1410
+ this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1411
+ changed ||= true;
1412
+ }
1413
+ if (!skipLpCheck)
1414
+ try {
1415
+ const result = await this._checkIntermediaryPaymentReceived(false);
1416
+ if (result !== null)
1417
+ changed ||= true;
1418
+ }
1419
+ catch (e) {
1420
+ this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
1421
+ }
1422
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED || this._state === FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
1423
+ if (await this._verifyQuoteDefinitelyExpired()) {
1424
+ this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1425
+ changed ||= true;
1426
+ }
1427
+ }
1428
+ }
1429
+ if (await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus))
1430
+ changed = true;
1431
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
1432
+ const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1433
+ if (expired) {
1434
+ this._state = FromBTCLNAutoSwapState.EXPIRED;
1435
+ changed = true;
1436
+ }
1437
+ }
1438
+ if (save && changed)
1439
+ await this._saveAndEmit();
1440
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED && this.secret != null)
1441
+ await this._broadcastSecret().catch(e => {
1442
+ this.logger.error("_sync(): Error when broadcasting swap secret: ", e);
1443
+ });
1444
+ return changed;
1445
+ }
1446
+ /**
1447
+ * @inheritDoc
1448
+ * @internal
1449
+ */
1450
+ async _forciblySetOnchainState(commitStatus) {
1451
+ switch (commitStatus?.type) {
1452
+ case base_1.SwapCommitStateType.PAID:
1453
+ if (this._commitTxId == null && commitStatus.getInitTxId != null)
1454
+ this._commitTxId = await commitStatus.getInitTxId();
1455
+ if (this._claimTxId == null)
1456
+ this._claimTxId = await commitStatus.getClaimTxId();
1457
+ if (this.secret == null || this.pr == null)
1458
+ this._setSwapSecret(await commitStatus.getClaimResult());
1459
+ this._state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
1460
+ return true;
1461
+ case base_1.SwapCommitStateType.NOT_COMMITED:
1462
+ let changed = false;
1463
+ if (this._commitTxId == null && commitStatus.getInitTxId != null) {
1464
+ this._commitTxId = await commitStatus.getInitTxId();
1465
+ changed = true;
1466
+ }
1467
+ if (this._refundTxId == null && commitStatus.getRefundTxId != null) {
1468
+ this._refundTxId = await commitStatus.getRefundTxId();
1469
+ changed = true;
1470
+ }
1471
+ if (this._refundTxId != null) {
1472
+ this._state = FromBTCLNAutoSwapState.FAILED;
1473
+ changed = true;
1474
+ }
1475
+ return changed;
1476
+ case base_1.SwapCommitStateType.EXPIRED:
1477
+ if (this._commitTxId == null && commitStatus.getInitTxId != null)
1478
+ this._commitTxId = await commitStatus.getInitTxId();
1479
+ if (this._refundTxId == null && commitStatus.getRefundTxId != null)
1480
+ this._refundTxId = await commitStatus.getRefundTxId();
1481
+ this._state = this._refundTxId == null ? FromBTCLNAutoSwapState.QUOTE_EXPIRED : FromBTCLNAutoSwapState.FAILED;
1482
+ return true;
1483
+ case base_1.SwapCommitStateType.COMMITED:
1484
+ let save = false;
1485
+ if (this._commitTxId == null && commitStatus.getInitTxId != null) {
1486
+ this._commitTxId = await commitStatus.getInitTxId();
1487
+ save = true;
1488
+ }
1489
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED && this._state !== FromBTCLNAutoSwapState.EXPIRED) {
1490
+ this._commitedAt ??= Date.now();
1491
+ this._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
1492
+ save = true;
1493
+ }
1494
+ return save;
1495
+ }
1496
+ return false;
1497
+ }
1498
+ /**
1499
+ * Broadcasts the swap secret to the underlying data propagation layer (e.g. Nostr by default)
1500
+ *
1501
+ * @param noCheckExpiry Whether a swap expiration check should be skipped broadcasting
1502
+ * @param secret An optional secret pre-image for the swap to broadcast
1503
+ *
1504
+ * @internal
1505
+ */
1506
+ async _broadcastSecret(noCheckExpiry, secret) {
1507
+ if (this._state !== FromBTCLNAutoSwapState.CLAIM_COMMITED)
1508
+ throw new Error("Must be in CLAIM_COMMITED state to broadcast swap secret!");
1509
+ if (this._data == null)
1510
+ throw new Error("Unknown data, wrong state?");
1511
+ const useSecret = secret ?? this.secret;
1512
+ if (useSecret == null)
1513
+ throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
1514
+ if (!this.isValidSecretPreimage(useSecret))
1515
+ throw new Error("Invalid swap secret pre-image provided!");
1516
+ if (!noCheckExpiry) {
1517
+ if (await this._contract.isExpired(this._getInitiator(), this._data))
1518
+ throw new Error("On-chain HTLC already expired!");
1519
+ }
1520
+ await this.wrapper._messenger.broadcast(new base_1.SwapClaimWitnessMessage(this._data, useSecret));
1521
+ }
1522
+ /**
1523
+ * @inheritDoc
1524
+ * @internal
1525
+ */
1526
+ async _tick(save) {
1527
+ switch (this._state) {
1528
+ case FromBTCLNAutoSwapState.PR_CREATED:
1529
+ if (this.getQuoteExpiry() < Date.now()) {
1530
+ this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
1531
+ if (save)
1532
+ await this._saveAndEmit();
1533
+ return true;
1534
+ }
1535
+ break;
1536
+ case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
1537
+ if (this.getDefinitiveExpiryTime() < Date.now()) {
1538
+ this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
1539
+ if (save)
1540
+ await this._saveAndEmit();
1541
+ return true;
1542
+ }
1543
+ break;
1544
+ case FromBTCLNAutoSwapState.PR_PAID:
1545
+ case FromBTCLNAutoSwapState.CLAIM_COMMITED:
1546
+ const expired = await this._contract.isExpired(this._getInitiator(), this._data);
1547
+ if (expired) {
1548
+ this._state = FromBTCLNAutoSwapState.EXPIRED;
1549
+ if (save)
1550
+ await this._saveAndEmit();
1551
+ return true;
1552
+ }
1553
+ if (this._state === FromBTCLNAutoSwapState.CLAIM_COMMITED) {
1554
+ //Broadcast the secret over the provided messenger channel
1555
+ if (this.broadcastTickCounter === 0 && this.secret != null)
1556
+ await this._broadcastSecret(true).catch(e => {
1557
+ this.logger.warn("_tick(): Error when broadcasting swap secret: ", e);
1558
+ });
1559
+ this.broadcastTickCounter = (this.broadcastTickCounter + 1) % 3; //Broadcast every 3rd tick
1560
+ }
1561
+ break;
1562
+ }
1563
+ return false;
1564
+ }
1565
+ /**
1566
+ * Forcibly sets the swap secret pre-image from on-chain data
1567
+ *
1568
+ * @internal
1569
+ */
1570
+ _setSwapSecret(secret) {
1571
+ this.secret = secret;
1572
+ if (this.pr == null) {
1573
+ this.pr = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex"))).toString("hex");
1574
+ }
1575
+ }
1576
+ }
1577
+ exports.FromBTCLNAutoSwap = FromBTCLNAutoSwap;