@atomiqlabs/sdk 8.8.3 → 8.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +1760 -1760
  3. package/dist/SmartChainAssets.d.ts +181 -181
  4. package/dist/SmartChainAssets.js +181 -181
  5. package/dist/bitcoin/coinselect2/accumulative.d.ts +7 -7
  6. package/dist/bitcoin/coinselect2/accumulative.js +52 -52
  7. package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -7
  8. package/dist/bitcoin/coinselect2/blackjack.js +38 -38
  9. package/dist/bitcoin/coinselect2/index.d.ts +20 -20
  10. package/dist/bitcoin/coinselect2/index.js +69 -69
  11. package/dist/bitcoin/coinselect2/utils.d.ts +82 -82
  12. package/dist/bitcoin/coinselect2/utils.js +158 -158
  13. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -113
  14. package/dist/bitcoin/wallet/BitcoinWallet.js +335 -335
  15. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -116
  16. package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
  17. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -106
  18. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -196
  19. package/dist/enums/FeeType.d.ts +15 -15
  20. package/dist/enums/FeeType.js +19 -19
  21. package/dist/enums/SwapAmountType.d.ts +15 -15
  22. package/dist/enums/SwapAmountType.js +19 -19
  23. package/dist/enums/SwapDirection.d.ts +15 -15
  24. package/dist/enums/SwapDirection.js +19 -19
  25. package/dist/enums/SwapSide.d.ts +15 -15
  26. package/dist/enums/SwapSide.js +19 -19
  27. package/dist/enums/SwapType.d.ts +75 -75
  28. package/dist/enums/SwapType.js +79 -79
  29. package/dist/errors/IntermediaryError.d.ts +13 -13
  30. package/dist/errors/IntermediaryError.js +27 -27
  31. package/dist/errors/RequestError.d.ts +32 -32
  32. package/dist/errors/RequestError.js +54 -54
  33. package/dist/errors/UserError.d.ts +8 -8
  34. package/dist/errors/UserError.js +16 -16
  35. package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
  36. package/dist/events/UnifiedSwapEventListener.js +132 -132
  37. package/dist/http/HttpUtils.d.ts +27 -27
  38. package/dist/http/HttpUtils.js +91 -91
  39. package/dist/http/paramcoders/IParamReader.d.ts +8 -8
  40. package/dist/http/paramcoders/IParamReader.js +2 -2
  41. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -44
  42. package/dist/http/paramcoders/ParamDecoder.js +137 -137
  43. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -20
  44. package/dist/http/paramcoders/ParamEncoder.js +36 -36
  45. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
  46. package/dist/http/paramcoders/SchemaVerifier.js +145 -145
  47. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
  48. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
  49. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -13
  50. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
  51. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
  52. package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -174
  53. package/dist/index.d.ts +85 -85
  54. package/dist/index.js +158 -158
  55. package/dist/intermediaries/Intermediary.d.ts +178 -178
  56. package/dist/intermediaries/Intermediary.js +166 -166
  57. package/dist/intermediaries/IntermediaryDiscovery.d.ts +211 -211
  58. package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
  59. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +450 -450
  60. package/dist/intermediaries/apis/IntermediaryAPI.js +618 -618
  61. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
  62. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
  63. package/dist/lnurl/LNURL.d.ts +102 -102
  64. package/dist/lnurl/LNURL.js +321 -321
  65. package/dist/prices/RedundantSwapPrice.d.ts +110 -110
  66. package/dist/prices/RedundantSwapPrice.js +222 -222
  67. package/dist/prices/SingleSwapPrice.d.ts +34 -34
  68. package/dist/prices/SingleSwapPrice.js +44 -44
  69. package/dist/prices/SwapPriceWithChain.d.ts +107 -107
  70. package/dist/prices/SwapPriceWithChain.js +128 -128
  71. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
  72. package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
  73. package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
  74. package/dist/prices/abstract/IPriceProvider.js +74 -74
  75. package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
  76. package/dist/prices/abstract/ISwapPrice.js +279 -279
  77. package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
  78. package/dist/prices/providers/BinancePriceProvider.js +30 -30
  79. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
  80. package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
  81. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
  82. package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
  83. package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
  84. package/dist/prices/providers/CustomPriceProvider.js +35 -35
  85. package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
  86. package/dist/prices/providers/KrakenPriceProvider.js +45 -45
  87. package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
  88. package/dist/prices/providers/OKXPriceProvider.js +29 -29
  89. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
  90. package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
  91. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
  92. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
  93. package/dist/storage/IUnifiedStorage.d.ts +85 -85
  94. package/dist/storage/IUnifiedStorage.js +2 -2
  95. package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
  96. package/dist/storage/UnifiedSwapStorage.js +116 -116
  97. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
  98. package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
  99. package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
  100. package/dist/storage-browser/LocalStorageManager.js +93 -93
  101. package/dist/swapper/Swapper.d.ts +732 -732
  102. package/dist/swapper/Swapper.js +1713 -1713
  103. package/dist/swapper/SwapperFactory.d.ts +135 -135
  104. package/dist/swapper/SwapperFactory.js +162 -162
  105. package/dist/swapper/SwapperUtils.d.ts +206 -206
  106. package/dist/swapper/SwapperUtils.js +481 -481
  107. package/dist/swapper/SwapperWithChain.d.ts +404 -404
  108. package/dist/swapper/SwapperWithChain.js +469 -469
  109. package/dist/swapper/SwapperWithSigner.d.ts +322 -322
  110. package/dist/swapper/SwapperWithSigner.js +318 -318
  111. package/dist/swaps/IAddressSwap.d.ts +22 -22
  112. package/dist/swaps/IAddressSwap.js +14 -14
  113. package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
  114. package/dist/swaps/IBTCWalletSwap.js +18 -18
  115. package/dist/swaps/IClaimableSwap.d.ts +49 -49
  116. package/dist/swaps/IClaimableSwap.js +15 -15
  117. package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
  118. package/dist/swaps/IClaimableSwapWrapper.js +2 -2
  119. package/dist/swaps/IRefundableSwap.d.ts +43 -43
  120. package/dist/swaps/IRefundableSwap.js +14 -14
  121. package/dist/swaps/ISwap.d.ts +392 -392
  122. package/dist/swaps/ISwap.js +349 -349
  123. package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
  124. package/dist/swaps/ISwapWithGasDrop.js +12 -12
  125. package/dist/swaps/ISwapWrapper.d.ts +285 -285
  126. package/dist/swaps/ISwapWrapper.js +353 -353
  127. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
  128. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
  129. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +139 -139
  130. package/dist/swaps/escrow_swaps/IEscrowSwap.js +170 -170
  131. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +128 -128
  132. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
  133. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +105 -105
  134. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +129 -129
  135. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
  136. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
  137. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +64 -64
  138. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +82 -82
  139. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +531 -531
  140. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
  141. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +190 -190
  142. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
  143. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +583 -583
  144. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
  145. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +235 -235
  146. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
  147. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
  148. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
  149. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +202 -202
  150. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
  151. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
  152. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
  153. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +68 -68
  154. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +117 -117
  155. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -127
  156. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
  157. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +251 -251
  158. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +536 -536
  159. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
  160. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
  161. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +132 -132
  162. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
  163. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +637 -637
  164. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1448 -1448
  165. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +257 -257
  166. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -947
  167. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
  168. package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
  169. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
  170. package/dist/swaps/trusted/ln/LnForGasWrapper.js +83 -83
  171. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
  172. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
  173. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +69 -69
  174. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +93 -93
  175. package/dist/types/AmountData.d.ts +10 -10
  176. package/dist/types/AmountData.js +2 -2
  177. package/dist/types/CustomPriceFunction.d.ts +11 -11
  178. package/dist/types/CustomPriceFunction.js +2 -2
  179. package/dist/types/PriceInfoType.d.ts +28 -28
  180. package/dist/types/PriceInfoType.js +57 -57
  181. package/dist/types/SwapExecutionAction.d.ts +88 -88
  182. package/dist/types/SwapExecutionAction.js +2 -2
  183. package/dist/types/SwapStateInfo.d.ts +5 -5
  184. package/dist/types/SwapStateInfo.js +2 -2
  185. package/dist/types/SwapWithSigner.d.ts +17 -17
  186. package/dist/types/SwapWithSigner.js +43 -43
  187. package/dist/types/Token.d.ts +99 -99
  188. package/dist/types/Token.js +76 -76
  189. package/dist/types/TokenAmount.d.ts +69 -69
  190. package/dist/types/TokenAmount.js +60 -60
  191. package/dist/types/fees/Fee.d.ts +50 -50
  192. package/dist/types/fees/Fee.js +2 -2
  193. package/dist/types/fees/FeeBreakdown.d.ts +11 -11
  194. package/dist/types/fees/FeeBreakdown.js +2 -2
  195. package/dist/types/fees/PercentagePPM.d.ts +17 -17
  196. package/dist/types/fees/PercentagePPM.js +18 -18
  197. package/dist/types/lnurl/LNURLPay.d.ts +61 -61
  198. package/dist/types/lnurl/LNURLPay.js +31 -31
  199. package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
  200. package/dist/types/lnurl/LNURLWithdraw.js +27 -27
  201. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
  202. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -15
  203. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
  204. package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
  205. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
  206. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
  207. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
  208. package/dist/utils/AutomaticClockDriftCorrection.js +70 -70
  209. package/dist/utils/BitcoinUtils.d.ts +16 -16
  210. package/dist/utils/BitcoinUtils.js +141 -141
  211. package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
  212. package/dist/utils/BitcoinWalletUtils.js +14 -14
  213. package/dist/utils/Logger.d.ts +7 -7
  214. package/dist/utils/Logger.js +12 -12
  215. package/dist/utils/RetryUtils.d.ts +22 -22
  216. package/dist/utils/RetryUtils.js +67 -67
  217. package/dist/utils/SwapUtils.d.ts +88 -88
  218. package/dist/utils/SwapUtils.js +72 -72
  219. package/dist/utils/TimeoutUtils.d.ts +17 -17
  220. package/dist/utils/TimeoutUtils.js +55 -55
  221. package/dist/utils/TokenUtils.d.ts +19 -19
  222. package/dist/utils/TokenUtils.js +37 -37
  223. package/dist/utils/TypeUtils.d.ts +7 -7
  224. package/dist/utils/TypeUtils.js +2 -2
  225. package/dist/utils/Utils.d.ts +67 -67
  226. package/dist/utils/Utils.js +208 -208
  227. package/package.json +43 -43
  228. package/src/SmartChainAssets.ts +186 -186
  229. package/src/bitcoin/coinselect2/accumulative.ts +69 -69
  230. package/src/bitcoin/coinselect2/blackjack.ts +50 -50
  231. package/src/bitcoin/coinselect2/index.ts +93 -93
  232. package/src/bitcoin/coinselect2/utils.ts +236 -236
  233. package/src/bitcoin/wallet/BitcoinWallet.ts +439 -439
  234. package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -140
  235. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -225
  236. package/src/enums/FeeType.ts +15 -15
  237. package/src/enums/SwapAmountType.ts +16 -16
  238. package/src/enums/SwapDirection.ts +15 -15
  239. package/src/enums/SwapSide.ts +16 -16
  240. package/src/enums/SwapType.ts +75 -75
  241. package/src/errors/IntermediaryError.ts +28 -28
  242. package/src/errors/RequestError.ts +64 -64
  243. package/src/errors/UserError.ts +15 -15
  244. package/src/events/UnifiedSwapEventListener.ts +173 -173
  245. package/src/http/HttpUtils.ts +91 -91
  246. package/src/http/paramcoders/IParamReader.ts +9 -9
  247. package/src/http/paramcoders/ParamDecoder.ts +145 -145
  248. package/src/http/paramcoders/ParamEncoder.ts +40 -40
  249. package/src/http/paramcoders/SchemaVerifier.ts +153 -153
  250. package/src/http/paramcoders/client/ResponseParamDecoder.ts +57 -57
  251. package/src/http/paramcoders/client/StreamParamEncoder.ts +28 -28
  252. package/src/http/paramcoders/client/StreamingFetchPromise.ts +192 -192
  253. package/src/index.ts +140 -140
  254. package/src/intermediaries/Intermediary.ts +280 -280
  255. package/src/intermediaries/IntermediaryDiscovery.ts +541 -541
  256. package/src/intermediaries/apis/IntermediaryAPI.ts +963 -963
  257. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -257
  258. package/src/lnurl/LNURL.ts +402 -402
  259. package/src/prices/RedundantSwapPrice.ts +264 -264
  260. package/src/prices/SingleSwapPrice.ts +50 -50
  261. package/src/prices/SwapPriceWithChain.ts +194 -194
  262. package/src/prices/abstract/ICachedSwapPrice.ts +85 -85
  263. package/src/prices/abstract/IPriceProvider.ts +127 -127
  264. package/src/prices/abstract/ISwapPrice.ts +390 -390
  265. package/src/prices/providers/BinancePriceProvider.ts +48 -48
  266. package/src/prices/providers/CoinGeckoPriceProvider.ts +46 -46
  267. package/src/prices/providers/CoinPaprikaPriceProvider.ts +49 -49
  268. package/src/prices/providers/CustomPriceProvider.ts +40 -40
  269. package/src/prices/providers/KrakenPriceProvider.ts +83 -83
  270. package/src/prices/providers/OKXPriceProvider.ts +59 -59
  271. package/src/prices/providers/abstract/ExchangePriceProvider.ts +31 -31
  272. package/src/prices/providers/abstract/HttpPriceProvider.ts +14 -14
  273. package/src/storage/IUnifiedStorage.ts +95 -95
  274. package/src/storage/UnifiedSwapStorage.ts +141 -141
  275. package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
  276. package/src/storage-browser/LocalStorageManager.ts +106 -106
  277. package/src/swapper/Swapper.ts +2488 -2488
  278. package/src/swapper/SwapperFactory.ts +307 -307
  279. package/src/swapper/SwapperUtils.ts +570 -570
  280. package/src/swapper/SwapperWithChain.ts +707 -707
  281. package/src/swapper/SwapperWithSigner.ts +511 -511
  282. package/src/swaps/IAddressSwap.ts +30 -30
  283. package/src/swaps/IBTCWalletSwap.ts +92 -92
  284. package/src/swaps/IClaimableSwap.ts +65 -65
  285. package/src/swaps/IClaimableSwapWrapper.ts +17 -17
  286. package/src/swaps/IRefundableSwap.ts +58 -58
  287. package/src/swaps/ISwap.ts +703 -703
  288. package/src/swaps/ISwapWithGasDrop.ts +25 -25
  289. package/src/swaps/ISwapWrapper.ts +539 -539
  290. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
  291. package/src/swaps/escrow_swaps/IEscrowSwap.ts +269 -269
  292. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +282 -282
  293. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +169 -169
  294. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +300 -300
  295. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +107 -107
  296. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1473 -1474
  297. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +601 -601
  298. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1582 -1582
  299. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +750 -750
  300. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1299 -1299
  301. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +610 -610
  302. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1096 -1096
  303. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +138 -138
  304. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +304 -304
  305. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +786 -786
  306. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
  307. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +401 -401
  308. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1812 -1812
  309. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1236 -1236
  310. package/src/swaps/trusted/ln/LnForGasSwap.ts +589 -589
  311. package/src/swaps/trusted/ln/LnForGasWrapper.ts +91 -91
  312. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +862 -862
  313. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +131 -131
  314. package/src/types/AmountData.ts +9 -9
  315. package/src/types/CustomPriceFunction.ts +11 -11
  316. package/src/types/PriceInfoType.ts +66 -66
  317. package/src/types/SwapExecutionAction.ts +99 -99
  318. package/src/types/SwapStateInfo.ts +6 -6
  319. package/src/types/SwapWithSigner.ts +61 -61
  320. package/src/types/Token.ts +163 -163
  321. package/src/types/TokenAmount.ts +132 -132
  322. package/src/types/fees/Fee.ts +56 -56
  323. package/src/types/fees/FeeBreakdown.ts +11 -11
  324. package/src/types/fees/PercentagePPM.ts +26 -26
  325. package/src/types/lnurl/LNURLPay.ts +79 -79
  326. package/src/types/lnurl/LNURLWithdraw.ts +61 -61
  327. package/src/types/wallets/LightningInvoiceCreateService.ts +30 -30
  328. package/src/types/wallets/MinimalBitcoinWalletInterface.ts +21 -21
  329. package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +9 -9
  330. package/src/utils/AutomaticClockDriftCorrection.ts +71 -71
  331. package/src/utils/BitcoinUtils.ts +132 -132
  332. package/src/utils/BitcoinWalletUtils.ts +15 -15
  333. package/src/utils/Logger.ts +14 -14
  334. package/src/utils/RetryUtils.ts +78 -78
  335. package/src/utils/SwapUtils.ts +99 -99
  336. package/src/utils/TimeoutUtils.ts +49 -49
  337. package/src/utils/TokenUtils.ts +33 -33
  338. package/src/utils/TypeUtils.ts +8 -8
  339. package/src/utils/Utils.ts +212 -212
@@ -1,2488 +1,2488 @@
1
- import {ISwapPrice} from "../prices/abstract/ISwapPrice";
2
- import {
3
- BitcoinNetwork, BitcoinRpc, BitcoinRpcWithAddressIndex, BtcBlock,
4
- BtcRelay,
5
- ChainData,
6
- ChainSwapType,
7
- ChainType, LightningNetworkApi,
8
- Messenger,
9
- RelaySynchronizer, SpvWithdrawalClaimedState, SpvWithdrawalFrontedState, SwapCommitState, SwapContract, SwapData
10
- } from "@atomiqlabs/base";
11
- import {
12
- ToBTCLNOptions,
13
- ToBTCLNWrapper
14
- } from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper";
15
- import {ToBTCOptions, ToBTCWrapper} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper";
16
- import {FromBTCLNOptions, FromBTCLNWrapper} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper";
17
- import {FromBTCOptions, FromBTCWrapper} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper";
18
- import {IntermediaryDiscovery, MultichainSwapBounds, SwapBounds} from "../intermediaries/IntermediaryDiscovery";
19
- import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
20
- import {ISwap} from "../swaps/ISwap";
21
- import {IntermediaryError} from "../errors/IntermediaryError";
22
- import {SwapType} from "../enums/SwapType";
23
- import {FromBTCLNSwap} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap";
24
- import {FromBTCSwap} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCSwap";
25
- import {ToBTCLNSwap} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap";
26
- import {ToBTCSwap} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCSwap";
27
- import {LnForGasWrapper} from "../swaps/trusted/ln/LnForGasWrapper";
28
- import {LnForGasSwap} from "../swaps/trusted/ln/LnForGasSwap";
29
- import {EventEmitter} from "events";
30
- import {Intermediary} from "../intermediaries/Intermediary";
31
- import {ISwapWrapper, WrapperCtorTokens} from "../swaps/ISwapWrapper";
32
- import {bigIntCompare, bigIntMax, bigIntMin, fromDecimal, objectMap, randomBytes} from "../utils/Utils";
33
- import {OutOfBoundsError} from "../errors/RequestError";
34
- import {SwapperWithChain} from "./SwapperWithChain";
35
- import {OnchainForGasSwap} from "../swaps/trusted/onchain/OnchainForGasSwap";
36
- import {OnchainForGasWrapper} from "../swaps/trusted/onchain/OnchainForGasWrapper";
37
- import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils";
38
- import {IUnifiedStorage, QueryParams} from "../storage/IUnifiedStorage";
39
- import {
40
- UnifiedSwapStorage,
41
- UnifiedSwapStorageCompositeIndexes,
42
- UnifiedSwapStorageIndexes
43
- } from "../storage/UnifiedSwapStorage";
44
- import {UnifiedSwapEventListener} from "../events/UnifiedSwapEventListener";
45
- import {IToBTCSwap} from "../swaps/escrow_swaps/tobtc/IToBTCSwap";
46
- import {SpvFromBTCOptions, SpvFromBTCWrapper} from "../swaps/spv_swaps/SpvFromBTCWrapper";
47
- import {SpvFromBTCSwap} from "../swaps/spv_swaps/SpvFromBTCSwap";
48
- import {SwapperUtils} from "./SwapperUtils";
49
- import {FromBTCLNAutoOptions, FromBTCLNAutoWrapper} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper";
50
- import {FromBTCLNAutoSwap} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap";
51
- import {UserError} from "../errors/UserError";
52
- import {SwapAmountType} from "../enums/SwapAmountType";
53
- import {IClaimableSwap} from "../swaps/IClaimableSwap";
54
- import {correctClock} from "../utils/AutomaticClockDriftCorrection";
55
- import {isSwapType, SwapProtocolInfo, SwapTypeMapping} from "../utils/SwapUtils";
56
- import {IndexedDBUnifiedStorage} from "../storage-browser/IndexedDBUnifiedStorage";
57
- import {TokenAmount, toTokenAmount} from "../types/TokenAmount";
58
- import {BitcoinTokens, BtcToken, isBtcToken, isSCToken, SCToken, Token} from "../types/Token";
59
- import {AmountData} from "../types/AmountData";
60
- import {getLogger} from "../utils/Logger";
61
- import {isLNURLWithdraw, LNURLWithdraw} from "../types/lnurl/LNURLWithdraw";
62
- import {isLNURLPay, LNURLPay} from "../types/lnurl/LNURLPay";
63
- import {tryWithRetries} from "../utils/RetryUtils";
64
- import {NotNever} from "../utils/TypeUtils";
65
- import {IEscrowSwap} from "../swaps/escrow_swaps/IEscrowSwap";
66
- import {LightningInvoiceCreateService, isLightningInvoiceCreateService} from "../types/wallets/LightningInvoiceCreateService";
67
- import {SwapSide} from "../enums/SwapSide";
68
- import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "../bitcoin/wallet/IBitcoinWallet";
69
- import {MinimalBitcoinWalletInterface} from "../types/wallets/MinimalBitcoinWalletInterface";
70
- import {toBitcoinWallet} from "../utils/BitcoinWalletUtils";
71
-
72
- /**
73
- * Configuration options for the Swapper
74
- * @category Core
75
- */
76
- export type SwapperOptions = {
77
- /**
78
- * Manual override for the intermediary (LP) URLs for the SDK to use, by default these are fetched automatically
79
- * from the registry
80
- */
81
- intermediaryUrl?: string | string[],
82
- /**
83
- * Registry URL for where to look for active intermediary (LP) endpoint URLs
84
- */
85
- registryUrl?: string,
86
-
87
- /**
88
- * Bitcoin network to use for the swaps,
89
- */
90
- bitcoinNetwork?: BitcoinNetwork,
91
-
92
- /**
93
- * Timeout (in milliseconds) for HTTP GET requests done by the SDK
94
- */
95
- getRequestTimeout?: number,
96
- /**
97
- * Timeout (in milliseconds) for HTTP POST requests done by the SDK
98
- */
99
- postRequestTimeout?: number,
100
- /**
101
- * Additional parameters to be sent to the intermediaries (LPs), when requesting quotes from them
102
- */
103
- defaultAdditionalParameters?: {[key: string]: any},
104
- /**
105
- * Optional name prefix to use when creating a swap storage, you can use this to create separate storage
106
- * instances that don't overlap.
107
- */
108
- storagePrefix?: string,
109
- /**
110
- * Sets the default intermediary (LP) to use for the trusted gas swaps, if not set the SDK uses a default one
111
- */
112
- defaultTrustedIntermediaryUrl?: string,
113
-
114
- /**
115
- * A function callback to retrieve a specific named storage container for swap persistency. If not present, the
116
- * default IndexedDB storage adapter is used. When you use the SDK in non-browser based environments you need to
117
- * provide this callback such that the SDK is able to use a custom storage adapter.
118
- *
119
- * @param storageName Name of the container to retrieve
120
- */
121
- swapStorage?: (storageName: string) => IUnifiedStorage<UnifiedSwapStorageIndexes, UnifiedSwapStorageCompositeIndexes>,
122
-
123
- /**
124
- * By setting this flag, the swapper doesn't schedule automatic tick timers. To make sure the swap states are
125
- * properly updated (e.g. the expired swaps properly move to the expired state), you should call the
126
- * {@link Swapper._syncSwaps} function periodically. This flag should be set when you run an environment that
127
- * doesn't support long-running timers - e.g. serverless environments like Azure Function Apps or AWS Lambda
128
- */
129
- noTimers?: boolean,
130
- /**
131
- * By setting this flag, the swapper doesn't subscribe to on-chain events. To make sure the swap states are
132
- * properly updated you should call the {@link Swapper._syncSwaps} function periodically. This flag should be
133
- * set when you run an environment that doesn't support long-running timers and websocket connections - e.g.
134
- * serverless environments like Azure Function Apps or AWS Lambda
135
- */
136
- noEvents?: boolean,
137
- /**
138
- * By setting this flag, the swap objects will not be cached in the SDK and instead will always be loaded from
139
- * the persistent storage. By default, the SDK uses a `WeakRef` mapping of swaps, to ensure that when the same
140
- * swap is loaded concurrently, it returns the same object reference to both, making the changes on the object
141
- * atomic. This flag should be set to `true` when running in an environment where multiple instances of the SDK
142
- * access the same swap database - e.g. serverless environments like Azure Function Apps or AWS Lambda
143
- */
144
- noSwapCache?: boolean,
145
- /**
146
- * Skip checking past swaps when the swapper is initiated with {@link Swapper.init}, you can call the
147
- * {@link Swapper._syncSwaps} function later, to check the swaps. By default, the SDK checks the state
148
- * of all the known swaps during init.
149
- */
150
- dontCheckPastSwaps?: boolean,
151
- /**
152
- * Skip fetching the LPs when the swapper is initiated with {@link Swapper.init}, this means the list of available
153
- * tokens and swap limits won't be available immediately. LPs will be fetched automatically later, when a swap
154
- * is requested
155
- */
156
- dontFetchLPs?: boolean,
157
- /**
158
- * Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
159
- * `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
160
- * want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
161
- *
162
- * Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
163
- * `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
164
- * might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
165
- * frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
166
- * necessitating the saving of the swap data to the persistent storage until it is actually initiated.
167
- */
168
- saveUninitializedSwaps?: boolean,
169
- /**
170
- * Automatically checks system time on initialize, if the system time drifts too far from the actual time
171
- * (as checked from multiple server sources) it adjusts the `Date.now()` function to return proper actual time.
172
- */
173
- automaticClockDriftCorrection?: boolean
174
- };
175
-
176
- /**
177
- * Type representing multiple blockchain configurations
178
- * @category Core
179
- */
180
- export type MultiChain = {
181
- [chainIdentifier in string]: ChainType;
182
- };
183
-
184
- type ChainSpecificData<T extends ChainType> = {
185
- wrappers: {
186
- [SwapType.TO_BTCLN]: ToBTCLNWrapper<T>,
187
- [SwapType.TO_BTC]: ToBTCWrapper<T>,
188
- [SwapType.FROM_BTCLN]: FromBTCLNWrapper<T>,
189
- [SwapType.FROM_BTC]: FromBTCWrapper<T>,
190
- [SwapType.TRUSTED_FROM_BTCLN]: LnForGasWrapper<T>,
191
- [SwapType.TRUSTED_FROM_BTC]: OnchainForGasWrapper<T>,
192
- [SwapType.SPV_VAULT_FROM_BTC]: SpvFromBTCWrapper<T>,
193
- [SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>
194
- }
195
- chainEvents: T["Events"],
196
- chainInterface: T["ChainInterface"],
197
- unifiedChainEvents: UnifiedSwapEventListener<T>,
198
- unifiedSwapStorage: UnifiedSwapStorage<T>,
199
- reviver: (val: any) => ISwap<T>,
200
- defaultVersion: string,
201
-
202
- versionedContracts: {
203
- [contractVersion: string]: {
204
- swapContract: T["Contract"],
205
- spvVaultContract: T["SpvVaultContract"],
206
- btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
207
- synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
208
- }
209
- }
210
- };
211
-
212
- type MultiChainData<T extends MultiChain> = {
213
- [chainIdentifier in keyof T]: ChainSpecificData<T[chainIdentifier]>
214
- };
215
-
216
- type CtorMultiChainData<T extends MultiChain> = {
217
- [chainIdentifier in keyof T]: ChainData<T[chainIdentifier]>
218
- };
219
-
220
- type SwapperCtorTokens<T extends MultiChain = MultiChain> = {
221
- ticker: string,
222
- name: string,
223
- chains: {[chainId in ChainIds<T>]?: {
224
- address: string,
225
- decimals: number,
226
- displayDecimals?: number
227
- }}
228
- }[];
229
-
230
- /**
231
- * Type extracting chain identifiers from a MultiChain type
232
- * @category Core
233
- */
234
- export type ChainIds<T extends MultiChain> = keyof T & string;
235
-
236
- /**
237
- * Type helper to check if a chain supports a specific swap type
238
- * @category Core
239
- */
240
- export type SupportsSwapType<
241
- C extends ChainType,
242
- Type extends SwapType
243
- > = Type extends SwapType.SPV_VAULT_FROM_BTC ?
244
- NotNever<C["SpvVaultContract"]> :
245
- Type extends (SwapType.TRUSTED_FROM_BTCLN | SwapType.TRUSTED_FROM_BTC) ?
246
- true :
247
- Type extends SwapType.FROM_BTCLN_AUTO ? (C["Contract"]["supportsInitWithoutClaimer"] extends true ? true : false) :
248
- NotNever<C["Contract"]>;
249
-
250
- /**
251
- * Core orchestrator for all atomiq swap operations
252
- *
253
- * @category Core
254
- */
255
- export class Swapper<T extends MultiChain> extends EventEmitter<{
256
- lpsRemoved: [Intermediary[]],
257
- lpsAdded: [Intermediary[]],
258
- swapState: [ISwap],
259
- swapLimitsChanged: []
260
- }> {
261
- private readonly logger = getLogger(this.constructor.name+": ");
262
- private readonly swapStateListener: (swap: ISwap) => void;
263
-
264
- private defaultTrustedIntermediary?: Intermediary;
265
- private readonly bitcoinNetwork: BitcoinNetwork;
266
- private readonly options: SwapperOptions;
267
- /**
268
- * Data propagation layer used for broadcasting messages to watchtowers
269
- */
270
- private readonly messenger: Messenger;
271
-
272
- /**
273
- * A dictionary of smart chains used by the SDK
274
- * @internal
275
- */
276
- readonly _chains: MultiChainData<T>;
277
- /**
278
- * Bitcoin RPC for fetching bitcoin chain data
279
- * @internal
280
- */
281
- readonly _bitcoinRpc: BitcoinRpcWithAddressIndex<any>;
282
- /**
283
- * Bitcoin network specification
284
- * @internal
285
- */
286
- readonly _btcNetwork: BTC_NETWORK;
287
- /**
288
- * Token data indexed by chain identifier and token addresses
289
- * @internal
290
- */
291
- readonly _tokens: {
292
- [chainId: string]: {
293
- [tokenAddress: string]: SCToken
294
- }
295
- };
296
- /**
297
- * Token data indexed by chain identifier and token tickers
298
- * @internal
299
- */
300
- readonly _tokensByTicker: {
301
- [chainId: string]: {
302
- [tokenTicker: string]: SCToken
303
- }
304
- };
305
-
306
- /**
307
- * Pricing API used by the SDK
308
- */
309
- readonly prices: ISwapPrice<T>;
310
- /**
311
- * Intermediary discovery instance
312
- */
313
- readonly intermediaryDiscovery: IntermediaryDiscovery;
314
- /**
315
- * Miscellaneous utility functions
316
- */
317
- readonly Utils: SwapperUtils<T>;
318
-
319
- /**
320
- * @internal
321
- */
322
- constructor(
323
- bitcoinRpc: BitcoinRpcWithAddressIndex<any>,
324
- lightningApi: LightningNetworkApi,
325
- bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
326
- chainsData: CtorMultiChainData<T>,
327
- pricing: ISwapPrice<T>,
328
- tokens: SCToken[],
329
- messenger: Messenger,
330
- options?: SwapperOptions
331
- ) {
332
- super();
333
- const storagePrefix = options?.storagePrefix ?? "atomiq-";
334
-
335
- options ??= {};
336
- options.saveUninitializedSwaps ??= true;
337
- options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
338
- const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
339
-
340
- this.options = options;
341
-
342
- this.bitcoinNetwork = options.bitcoinNetwork;
343
- this._btcNetwork = options.bitcoinNetwork===BitcoinNetwork.MAINNET ? NETWORK :
344
- (options.bitcoinNetwork===BitcoinNetwork.TESTNET || options.bitcoinNetwork===BitcoinNetwork.TESTNET4) ? TEST_NETWORK : {
345
- bech32: 'bcrt',
346
- pubKeyHash: 111,
347
- scriptHash: 196,
348
- wif: 239
349
- };
350
- this.Utils = new SwapperUtils(this);
351
-
352
- this.prices = pricing;
353
- this._bitcoinRpc = bitcoinRpc;
354
- this.messenger = messenger;
355
-
356
- this._tokens = {};
357
- this._tokensByTicker = {};
358
- for(let tokenData of tokens) {
359
- const chainId = tokenData.chainId;
360
- this._tokens[chainId] ??= {};
361
- this._tokensByTicker[chainId] ??= {};
362
- this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
363
- }
364
-
365
- this.swapStateListener = (swap: ISwap) => {
366
- this.emit("swapState", swap);
367
- };
368
-
369
- this._chains = objectMap<CtorMultiChainData<T>, MultiChainData<T>>(chainsData, <InputKey extends keyof CtorMultiChainData<T>>(chainData: CtorMultiChainData<T>[InputKey], key: string) => {
370
- let {
371
- chainInterface, chainEvents, chainId,
372
- btcRelay,
373
- swapContract, swapDataConstructor,
374
- spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor,
375
- defaultVersion, versions
376
- } = chainData;
377
-
378
- defaultVersion ??= "v1";
379
-
380
- if(versions==null) {
381
- versions = {
382
- [defaultVersion]: {
383
- btcRelay,
384
- swapContract,
385
- swapDataConstructor,
386
- spvVaultContract,
387
- spvVaultDataConstructor,
388
- spvVaultWithdrawalDataConstructor
389
- }
390
- }
391
- }
392
-
393
- const versionedContracts = objectMap(versions, (value, key) => {
394
- return {
395
- swapContract: value.swapContract,
396
- spvVaultContract: value.spvVaultContract,
397
- btcRelay: value.btcRelay,
398
- synchronizer: bitcoinSynchronizer(value.btcRelay)
399
- };
400
- });
401
-
402
- const storageHandler = swapStorage(storagePrefix + chainId);
403
- const unifiedSwapStorage = new UnifiedSwapStorage<T[InputKey]>(storageHandler, this.options.noSwapCache);
404
- const unifiedChainEvents = new UnifiedSwapEventListener<T[InputKey]>(unifiedSwapStorage, chainEvents);
405
-
406
- const wrappers: any = {};
407
-
408
- wrappers[SwapType.TO_BTCLN] = new ToBTCLNWrapper<T[InputKey]>(
409
- key,
410
- unifiedSwapStorage,
411
- unifiedChainEvents,
412
- chainInterface,
413
- pricing,
414
- this._tokens[chainId],
415
- versions,
416
- {
417
- getRequestTimeout: this.options.getRequestTimeout,
418
- postRequestTimeout: this.options.postRequestTimeout,
419
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
420
- }
421
- );
422
- wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
423
- key,
424
- unifiedSwapStorage,
425
- unifiedChainEvents,
426
- chainInterface,
427
- pricing,
428
- this._tokens[chainId],
429
- versions,
430
- this._bitcoinRpc,
431
- {
432
- getRequestTimeout: this.options.getRequestTimeout,
433
- postRequestTimeout: this.options.postRequestTimeout,
434
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
435
- bitcoinNetwork: this._btcNetwork
436
- }
437
- );
438
- wrappers[SwapType.FROM_BTCLN] = new FromBTCLNWrapper<T[InputKey]>(
439
- key,
440
- unifiedSwapStorage,
441
- unifiedChainEvents,
442
- chainInterface,
443
- pricing,
444
- this._tokens[chainId],
445
- versions,
446
- lightningApi,
447
- {
448
- getRequestTimeout: this.options.getRequestTimeout,
449
- postRequestTimeout: this.options.postRequestTimeout,
450
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
451
- unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
452
- }
453
- );
454
- wrappers[SwapType.FROM_BTC] = new FromBTCWrapper<T[InputKey]>(
455
- key,
456
- unifiedSwapStorage,
457
- unifiedChainEvents,
458
- chainInterface,
459
- pricing,
460
- this._tokens[chainId],
461
- versions,
462
- versionedContracts,
463
- this._bitcoinRpc,
464
- {
465
- getRequestTimeout: this.options.getRequestTimeout,
466
- postRequestTimeout: this.options.postRequestTimeout,
467
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
468
- bitcoinNetwork: this._btcNetwork
469
- }
470
- );
471
- wrappers[SwapType.TRUSTED_FROM_BTCLN] = new LnForGasWrapper<T[InputKey]>(
472
- key,
473
- unifiedSwapStorage,
474
- unifiedChainEvents,
475
- chainInterface,
476
- pricing,
477
- this._tokens[chainId],
478
- {
479
- getRequestTimeout: this.options.getRequestTimeout,
480
- postRequestTimeout: this.options.postRequestTimeout,
481
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
482
- }
483
- );
484
- wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
485
- key,
486
- unifiedSwapStorage,
487
- unifiedChainEvents,
488
- chainInterface,
489
- pricing,
490
- this._tokens[chainId],
491
- bitcoinRpc,
492
- {
493
- getRequestTimeout: this.options.getRequestTimeout,
494
- postRequestTimeout: this.options.postRequestTimeout,
495
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
496
- bitcoinNetwork: this._btcNetwork
497
- }
498
- );
499
-
500
- // This is gated on the default version of the contracts
501
- if(spvVaultContract!=null) {
502
- wrappers[SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper<T[InputKey]>(
503
- key,
504
- unifiedSwapStorage,
505
- unifiedChainEvents,
506
- chainInterface,
507
- pricing,
508
- this._tokens[chainId],
509
- versions,
510
- versionedContracts,
511
- bitcoinRpc,
512
- {
513
- getRequestTimeout: this.options.getRequestTimeout,
514
- postRequestTimeout: this.options.postRequestTimeout,
515
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
516
- bitcoinNetwork: this._btcNetwork
517
- }
518
- );
519
- }
520
-
521
- // This is gated on the default version of the contracts
522
- if(swapContract.supportsInitWithoutClaimer) {
523
- wrappers[SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper<T[InputKey]>(
524
- key,
525
- unifiedSwapStorage,
526
- unifiedChainEvents,
527
- chainInterface,
528
- pricing,
529
- this._tokens[chainId],
530
- versions,
531
- lightningApi,
532
- this.messenger,
533
- {
534
- getRequestTimeout: this.options.getRequestTimeout,
535
- postRequestTimeout: this.options.postRequestTimeout,
536
- saveUninitializedSwaps: this.options.saveUninitializedSwaps,
537
- unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
538
- }
539
- );
540
- }
541
-
542
- Object.keys(wrappers).forEach(key => wrappers[key].events.on("swapState", this.swapStateListener));
543
-
544
- const reviver = (val: any) => {
545
- const wrapper: ISwapWrapper<any, any> = wrappers[val.type];
546
- if(wrapper==null) return null;
547
- return new wrapper._swapDeserializer(wrapper, val);
548
- };
549
-
550
- return {
551
- chainEvents,
552
- chainInterface,
553
-
554
- wrappers,
555
-
556
- unifiedChainEvents,
557
- unifiedSwapStorage,
558
-
559
- defaultVersion,
560
-
561
- reviver,
562
-
563
- versionedContracts
564
- }
565
- });
566
-
567
- const contracts = objectMap(chainsData, (data) => data.versions ?? {[data.defaultVersion ?? "v1"]: {swapContract: data.swapContract, spvVaultContract: data.spvVaultContract}});
568
- if(options.intermediaryUrl!=null) {
569
- this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
570
- } else {
571
- this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, undefined, options.getRequestTimeout);
572
- }
573
-
574
- this.intermediaryDiscovery.on("removed", (intermediaries: Intermediary[]) => {
575
- this.emit("lpsRemoved", intermediaries);
576
- });
577
-
578
- this.intermediaryDiscovery.on("added", (intermediaries: Intermediary[]) => {
579
- this.emit("lpsAdded", intermediaries);
580
- });
581
- }
582
-
583
- private async _init(): Promise<void> {
584
- this.logger.debug("init(): Initializing swapper...");
585
-
586
- const abortController = new AbortController();
587
-
588
- const promises: Promise<void>[] = [];
589
- let automaticClockDriftCorrectionPromise: Promise<void> | undefined = undefined;
590
- if(this.options.automaticClockDriftCorrection) {
591
- promises.push(automaticClockDriftCorrectionPromise = tryWithRetries(correctClock, undefined, undefined, abortController.signal).catch((err) => {
592
- abortController.abort(err);
593
- }));
594
- }
595
-
596
- this.logger.debug("init(): Initializing intermediary discovery");
597
- if(!this.options.dontFetchLPs) promises.push(this.intermediaryDiscovery.init(abortController.signal).catch(err => {
598
- if(abortController.signal.aborted) return;
599
- this.logger.error("init(): Failed to fetch intermediaries/LPs: ", err);
600
- }));
601
-
602
- if(this.options.defaultTrustedIntermediaryUrl!=null) {
603
- promises.push(
604
- this.intermediaryDiscovery.getIntermediary(this.options.defaultTrustedIntermediaryUrl, abortController.signal)
605
- .then(val => {
606
- if(val==null) throw new Error("Cannot get trusted LP");
607
- this.defaultTrustedIntermediary = val;
608
- })
609
- .catch(err => {
610
- if(abortController.signal.aborted) return;
611
- this.logger.error("init(): Failed to contact trusted LP url: ", err);
612
- })
613
- );
614
- }
615
-
616
- if(automaticClockDriftCorrectionPromise!=null) {
617
- //We should await the promises here before checking the swaps
618
- await automaticClockDriftCorrectionPromise;
619
- }
620
-
621
- const chainPromises = [];
622
- for(let chainIdentifier in this._chains) {
623
- chainPromises.push((async() => {
624
- const {
625
- chainInterface,
626
- versionedContracts,
627
- unifiedChainEvents,
628
- unifiedSwapStorage,
629
- wrappers,
630
- reviver
631
- } = this._chains[chainIdentifier];
632
-
633
- const _chainInterface: any = chainInterface;
634
- if(_chainInterface.verifyNetwork!=null) {
635
- await _chainInterface.verifyNetwork(this.bitcoinNetwork);
636
- }
637
-
638
- for(let contractVersion in versionedContracts) {
639
- await versionedContracts[contractVersion].swapContract.start();
640
- this.logger.debug("init(): Intialized swap contract: "+chainIdentifier+` version: ${contractVersion}`);
641
- }
642
-
643
- await unifiedSwapStorage.init();
644
- if(unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage) {
645
- //Try to migrate the data here
646
- const storagePrefix = chainIdentifier==="SOLANA" ?
647
- "SOLv4-"+this.bitcoinNetwork+"-Swaps-" :
648
- "atomiqsdk-"+this.bitcoinNetwork+chainIdentifier+"-Swaps-";
649
- await unifiedSwapStorage.storage.tryMigrate(
650
- [
651
- [storagePrefix+"FromBTC", SwapType.FROM_BTC],
652
- [storagePrefix+"FromBTCLN", SwapType.FROM_BTCLN],
653
- [storagePrefix+"ToBTC", SwapType.TO_BTC],
654
- [storagePrefix+"ToBTCLN", SwapType.TO_BTCLN]
655
- ],
656
- (obj: any) => {
657
- const swap = reviver(obj);
658
- if(swap._randomNonce==null) {
659
- const oldIdentifierHash = swap.getId();
660
- swap._randomNonce = randomBytes(16).toString("hex");
661
- const newIdentifierHash = swap.getId();
662
- this.logger.info("init(): Found older swap version without randomNonce, replacing, old hash: "+oldIdentifierHash+
663
- " new hash: "+newIdentifierHash);
664
- }
665
- return swap;
666
- }
667
- )
668
- }
669
-
670
- if(!this.options.noEvents) await unifiedChainEvents.start();
671
- this.logger.debug("init(): Intialized events: "+chainIdentifier);
672
-
673
- for(let key in wrappers) {
674
- // this.logger.debug("init(): Initializing "+SwapType[key]+": "+chainIdentifier);
675
- await wrappers[key as unknown as SwapType].init(this.options.noTimers, this.options.dontCheckPastSwaps);
676
- }
677
- })());
678
- }
679
- await Promise.all(chainPromises);
680
-
681
- await Promise.all(promises);
682
-
683
- this.logger.debug("init(): Initializing messenger");
684
- await this.messenger.init();
685
- }
686
-
687
- private initPromise?: Promise<void>;
688
- private initialized: boolean = false;
689
-
690
- /**
691
- * Initializes the swap storage and loads existing swaps, needs to be called before any other action
692
- */
693
- async init(): Promise<void> {
694
- if(this.initialized) return;
695
- if(this.initPromise!=null) {
696
- await this.initPromise;
697
- return;
698
- }
699
-
700
- try {
701
- const promise = this._init();
702
- this.initPromise = promise;
703
- await promise;
704
- delete this.initPromise;
705
- this.initialized = true;
706
- } catch (e) {
707
- delete this.initPromise;
708
- throw e;
709
- }
710
- }
711
-
712
- /**
713
- * Stops listening for onchain events and closes this Swapper instance
714
- */
715
- async stop() {
716
- if(this.initPromise) await this.initPromise;
717
- for(let chainIdentifier in this._chains) {
718
- const {
719
- wrappers,
720
- unifiedChainEvents
721
- } = this._chains[chainIdentifier];
722
- for(let key in wrappers) {
723
- const wrapper = wrappers[key as unknown as SwapType];
724
- wrapper.events.removeListener("swapState", this.swapStateListener);
725
- await wrapper.stop();
726
- }
727
- await unifiedChainEvents.stop();
728
- await this.messenger.stop();
729
- }
730
- this.initialized = false;
731
- }
732
-
733
- /**
734
- * Creates swap & handles intermediary, quote selection
735
- *
736
- * @param chainIdentifier
737
- * @param create Callback to create the
738
- * @param amountData Amount data as passed to the function
739
- * @param swapType Swap type of the execution
740
- * @param maxWaitTimeMS Maximum waiting time after the first intermediary returns the quote
741
- * @private
742
- * @throws {Error} when no intermediary was found
743
- * @throws {Error} if the chain with the provided identifier cannot be found
744
- */
745
- private async createSwap<ChainIdentifier extends ChainIds<T>, S extends ISwap<T[ChainIdentifier]>>(
746
- chainIdentifier: ChainIdentifier,
747
- create: (candidates: Intermediary[], abortSignal: AbortSignal, chain: ChainSpecificData<T[ChainIdentifier]>) => Promise<{
748
- quote: Promise<S>,
749
- intermediary: Intermediary
750
- }[]>,
751
- amountData: { amount?: bigint, token: string, exactIn: boolean },
752
- swapType: SwapType,
753
- maxWaitTimeMS: number = 2000
754
- ): Promise<S> {
755
- if(!this.initialized) throw new Error("Swapper not initialized, init first with swapper.init()!");
756
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
757
- let candidates: Intermediary[];
758
-
759
- const inBtc: boolean = swapType===SwapType.TO_BTCLN || swapType===SwapType.TO_BTC ? !amountData.exactIn : amountData.exactIn;
760
-
761
- if(!inBtc || amountData.amount==null) {
762
- //Get candidates not based on the amount
763
- candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
764
- } else {
765
- candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
766
- }
767
-
768
- let swapLimitsChanged = false;
769
-
770
- if(candidates.length===0) {
771
- this.logger.warn("createSwap(): No valid intermediary found to execute the swap with, reloading intermediary database...");
772
- await this.intermediaryDiscovery.reloadIntermediaries();
773
- swapLimitsChanged = true;
774
-
775
- if(!inBtc || amountData.amount==null) {
776
- //Get candidates not based on the amount
777
- candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
778
- } else {
779
- candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
780
-
781
- if(candidates.length===0) {
782
- const min = this.intermediaryDiscovery.getSwapMinimum(chainIdentifier, swapType, amountData.token);
783
- const max = this.intermediaryDiscovery.getSwapMaximum(chainIdentifier, swapType, amountData.token);
784
- if(min!=null && max!=null) {
785
- if(amountData.amount < BigInt(min)) throw new OutOfBoundsError("Swap amount too low! Try swapping a higher amount.", 200, BigInt(min), BigInt(max));
786
- if(amountData.amount > BigInt(max)) throw new OutOfBoundsError("Swap amount too high! Try swapping a lower amount.", 200, BigInt(min), BigInt(max));
787
- }
788
- }
789
- }
790
-
791
- if(candidates.length===0) throw new Error("No intermediary found for the requested pair and amount! You can try swapping different pair or higher/lower amount.");
792
- }
793
-
794
- const abortController = new AbortController();
795
- this.logger.debug("createSwap() Swap candidates: ", candidates.map(lp => lp.url).join());
796
- const quotePromises: {quote: Promise<S>, intermediary: Intermediary}[] = await create(candidates, abortController.signal, this._chains[chainIdentifier]);
797
-
798
- const promiseAll = new Promise<{
799
- quote: S,
800
- intermediary: Intermediary
801
- }[]>((resolve, reject) => {
802
- let min: bigint;
803
- let max: bigint;
804
- let error: Error;
805
- let numResolved = 0;
806
- let quotes: {
807
- quote: S,
808
- intermediary: Intermediary
809
- }[] = [];
810
- let timeout: NodeJS.Timeout;
811
-
812
- quotePromises.forEach(data => {
813
- data.quote.then(quote => {
814
- if(numResolved===0) {
815
- timeout = setTimeout(() => {
816
- abortController.abort(new Error("Timed out waiting for quote!"));
817
- resolve(quotes);
818
- }, maxWaitTimeMS);
819
- }
820
- numResolved++;
821
- quotes.push({
822
- quote,
823
- intermediary: data.intermediary
824
- });
825
- if(numResolved===quotePromises.length) {
826
- clearTimeout(timeout);
827
- resolve(quotes);
828
- return;
829
- }
830
- }).catch(e => {
831
- numResolved++;
832
- if(e instanceof IntermediaryError) {
833
- //Blacklist that node
834
- this.intermediaryDiscovery.removeIntermediary(data.intermediary);
835
- swapLimitsChanged = true;
836
- } else if(e instanceof OutOfBoundsError) {
837
- if(min==null || max==null) {
838
- min = e.min;
839
- max = e.max;
840
- } else {
841
- min = bigIntMin(min, e.min);
842
- max = bigIntMax(max, e.max);
843
- }
844
- data.intermediary.swapBounds[swapType] ??= {};
845
- data.intermediary.swapBounds[swapType]![chainIdentifier] ??= {};
846
- const tokenBoundsData = (data.intermediary.swapBounds[swapType]![chainIdentifier]![amountData.token] ??= {input: {}, output: {}});
847
- if(amountData.exactIn) {
848
- tokenBoundsData.input = {min: e.min, max: e.max};
849
- } else {
850
- tokenBoundsData.output = {min: e.min, max: e.max};
851
- }
852
- swapLimitsChanged = true;
853
- }
854
- this.logger.warn("createSwap(): Intermediary "+data.intermediary.url+" error: ", e);
855
- error = e;
856
-
857
- if(numResolved===quotePromises.length) {
858
- if(timeout!=null) clearTimeout(timeout);
859
- if(quotes.length>0) {
860
- resolve(quotes);
861
- return;
862
- }
863
- if(min!=null && max!=null) {
864
- let msg = "Swap amount too high or too low! Try swapping a different amount.";
865
- if(amountData.amount!=null) {
866
- if(min > amountData.amount) msg = "Swap amount too low! Try swapping a higher amount.";
867
- if(max < amountData.amount) msg = "Swap amount too high! Try swapping a lower amount.";
868
- }
869
- reject(new OutOfBoundsError(msg, 400, min, max));
870
- return;
871
- }
872
- reject(error);
873
- }
874
- });
875
- });
876
- });
877
-
878
- try {
879
- const quotes = await promiseAll;
880
-
881
- //TODO: Intermediary's reputation is not taken into account!
882
- quotes.sort((a, b) => {
883
- if(amountData.exactIn) {
884
- //Compare outputs
885
- return bigIntCompare(b.quote.getOutput().rawAmount!, a.quote.getOutput().rawAmount!);
886
- } else {
887
- //Compare inputs
888
- return bigIntCompare(a.quote.getInput().rawAmount!, b.quote.getInput().rawAmount!);
889
- }
890
- });
891
-
892
- this.logger.debug("createSwap(): Sorted quotes, best price to worst: ", quotes);
893
-
894
- if(swapLimitsChanged) this.emit("swapLimitsChanged");
895
-
896
- const quote = quotes[0].quote;
897
- await quote._save();
898
- return quote;
899
- } catch (e) {
900
- if(swapLimitsChanged) this.emit("swapLimitsChanged");
901
- throw e;
902
- }
903
- }
904
-
905
- /**
906
- * Creates Smart chain -> Bitcoin ({@link SwapType.TO_BTC}) swap
907
- *
908
- * @param chainIdentifier Chain identifier string of the source smart chain
909
- * @param signer Signer's address on the source chain
910
- * @param tokenAddress Token address to pay with
911
- * @param address Recipient's bitcoin address
912
- * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
913
- * @param exactIn Whether to use exact in instead of exact out
914
- * @param additionalParams Additional parameters sent to the LP when creating the swap
915
- * @param options Additional options for the swap
916
- */
917
- createToBTCSwap<ChainIdentifier extends ChainIds<T>>(
918
- chainIdentifier: ChainIdentifier,
919
- signer: string,
920
- tokenAddress: string,
921
- address: string,
922
- amount: bigint,
923
- exactIn: boolean = false,
924
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
925
- options?: ToBTCOptions
926
- ): Promise<ToBTCSwap<T[ChainIdentifier]>> {
927
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
928
- if(address.startsWith("bitcoin:")) {
929
- address = address.substring(8).split("?")[0];
930
- }
931
- if(!this.Utils.isValidBitcoinAddress(address)) throw new Error("Invalid bitcoin address");
932
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
933
- signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
934
- const amountData = {
935
- amount,
936
- token: tokenAddress,
937
- exactIn
938
- };
939
- return this.createSwap(
940
- chainIdentifier as ChainIdentifier,
941
- (candidates: Intermediary[], abortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.TO_BTC].create(
942
- signer,
943
- address,
944
- amountData,
945
- candidates,
946
- options,
947
- additionalParams,
948
- abortSignal
949
- )),
950
- amountData,
951
- SwapType.TO_BTC
952
- );
953
- }
954
-
955
- /**
956
- * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap
957
- *
958
- * @param chainIdentifier Chain identifier string of the source smart chain
959
- * @param signer Signer's address on the source chain
960
- * @param tokenAddress Token address to pay with
961
- * @param paymentRequest BOLT11 lightning network invoice to be paid (needs to have a fixed amount), and the swap
962
- * amount is taken from this fixed amount, hence only exact output swaps are supported
963
- * @param additionalParams Additional parameters sent to the LP when creating the swap
964
- * @param options Additional options for the swap
965
- */
966
- async createToBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
967
- chainIdentifier: ChainIdentifier,
968
- signer: string,
969
- tokenAddress: string,
970
- paymentRequest: string,
971
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
972
- options?: ToBTCLNOptions
973
- ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
974
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
975
- if(paymentRequest.startsWith("lightning:")) paymentRequest = paymentRequest.substring(10);
976
- if(!this.Utils.isValidLightningInvoice(paymentRequest)) throw new Error("Invalid lightning network invoice");
977
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
978
- signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
979
- const parsedPR = bolt11Decode(paymentRequest);
980
- if(parsedPR.millisatoshis==null) throw new Error("Invalid lightning network invoice, no msat value field!");
981
- const amountData = {
982
- amount: (BigInt(parsedPR.millisatoshis) + 999n) / 1000n,
983
- token: tokenAddress,
984
- exactIn: false as const
985
- };
986
- return this.createSwap(
987
- chainIdentifier as ChainIdentifier,
988
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].create(
989
- signer,
990
- paymentRequest,
991
- amountData,
992
- candidates,
993
- options,
994
- additionalParams,
995
- abortSignal
996
- ),
997
- amountData,
998
- SwapType.TO_BTCLN
999
- );
1000
- }
1001
-
1002
- /**
1003
- * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via LNURL-pay link
1004
- *
1005
- * @param chainIdentifier Chain identifier string of the source smart chain
1006
- * @param signer Signer's address on the source chain
1007
- * @param tokenAddress Token address to pay with
1008
- * @param lnurlPay LNURL-pay link to use for the payment
1009
- * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
1010
- * @param exactIn Whether to do an exact in swap instead of exact out
1011
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1012
- * @param options Additional options for the swap
1013
- */
1014
- async createToBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
1015
- chainIdentifier: ChainIdentifier,
1016
- signer: string,
1017
- tokenAddress: string,
1018
- lnurlPay: string | LNURLPay,
1019
- amount: bigint,
1020
- exactIn: boolean = false,
1021
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1022
- options?: ToBTCLNOptions & {comment?: string}
1023
- ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
1024
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1025
- if(typeof(lnurlPay)==="string" && !this.Utils.isValidLNURL(lnurlPay)) throw new Error("Invalid LNURL-pay link");
1026
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
1027
- signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
1028
- const amountData = {
1029
- amount,
1030
- token: tokenAddress,
1031
- exactIn
1032
- };
1033
- return this.createSwap(
1034
- chainIdentifier as ChainIdentifier,
1035
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaLNURL(
1036
- signer,
1037
- typeof(lnurlPay)==="string" ? (lnurlPay.startsWith("lightning:") ? lnurlPay.substring(10): lnurlPay) : lnurlPay.params,
1038
- amountData,
1039
- candidates,
1040
- options,
1041
- additionalParams,
1042
- abortSignal
1043
- ),
1044
- amountData,
1045
- SwapType.TO_BTCLN
1046
- );
1047
- }
1048
-
1049
- /**
1050
- * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via {@link LightningInvoiceCreateService}
1051
- *
1052
- * @param chainIdentifier Chain identifier string of the source smart chain
1053
- * @param signer Signer's address on the source chain
1054
- * @param tokenAddress Token address to pay with
1055
- * @param service Invoice create service object which facilitates the creation of fixed amount LN invoices
1056
- * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
1057
- * @param exactIn Whether to do an exact in swap instead of exact out
1058
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1059
- * @param options Additional options for the swap
1060
- */
1061
- async createToBTCLNSwapViaInvoiceCreateService<ChainIdentifier extends ChainIds<T>>(
1062
- chainIdentifier: ChainIdentifier,
1063
- signer: string,
1064
- tokenAddress: string,
1065
- service: LightningInvoiceCreateService,
1066
- amount: bigint,
1067
- exactIn: boolean = false,
1068
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1069
- options?: ToBTCLNOptions
1070
- ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
1071
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1072
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
1073
- signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
1074
- const amountData = {
1075
- amount,
1076
- token: tokenAddress,
1077
- exactIn
1078
- };
1079
- return this.createSwap(
1080
- chainIdentifier as ChainIdentifier,
1081
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaInvoiceCreateService(
1082
- signer,
1083
- Promise.resolve(service),
1084
- amountData,
1085
- candidates,
1086
- options,
1087
- additionalParams,
1088
- abortSignal
1089
- ),
1090
- amountData,
1091
- SwapType.TO_BTCLN
1092
- );
1093
- }
1094
-
1095
- /**
1096
- * Creates Bitcoin -> Smart chain ({@link SwapType.SPV_VAULT_FROM_BTC}) swap
1097
- *
1098
- * @param chainIdentifier Chain identifier string of the destination smart chain
1099
- * @param recipient Recipient address on the destination chain
1100
- * @param tokenAddress Token address to receive
1101
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1102
- * @param exactOut Whether to use a exact out instead of exact in
1103
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1104
- * @param options Additional options for the swap
1105
- */
1106
- async createFromBTCSwapNew<ChainIdentifier extends ChainIds<T>>(
1107
- chainIdentifier: ChainIdentifier,
1108
- recipient: string,
1109
- tokenAddress: string,
1110
- amount: bigint | null,
1111
- exactOut: boolean = false,
1112
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1113
- options?: SpvFromBTCOptions
1114
- ): Promise<SpvFromBTCSwap<T[ChainIdentifier]>> {
1115
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1116
- if(this._chains[chainIdentifier].wrappers[SwapType.SPV_VAULT_FROM_BTC]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new BTC swap protocol (spv vault swaps)!");
1117
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1118
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1119
- const amountData = {
1120
- amount: amount ?? undefined,
1121
- token: tokenAddress,
1122
- exactIn: !exactOut
1123
- };
1124
- return this.createSwap(
1125
- chainIdentifier as ChainIdentifier,
1126
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.SPV_VAULT_FROM_BTC].create(
1127
- recipient,
1128
- amountData,
1129
- candidates,
1130
- options,
1131
- additionalParams,
1132
- abortSignal
1133
- )),
1134
- amountData,
1135
- SwapType.SPV_VAULT_FROM_BTC
1136
- );
1137
- }
1138
-
1139
- /**
1140
- * Creates LEGACY Bitcoin -> Smart chain ({@link SwapType.FROM_BTC}) swap
1141
- *
1142
- * @param chainIdentifier Chain identifier string of the destination smart chain
1143
- * @param recipient Recipient address on the destination chain
1144
- * @param tokenAddress Token address to receive
1145
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1146
- * @param exactOut Whether to use a exact out instead of exact in
1147
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1148
- * @param options Additional options for the swap
1149
- */
1150
- async createFromBTCSwap<ChainIdentifier extends ChainIds<T>>(
1151
- chainIdentifier: ChainIdentifier,
1152
- recipient: string,
1153
- tokenAddress: string,
1154
- amount: bigint,
1155
- exactOut: boolean = false,
1156
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1157
- options?: FromBTCOptions
1158
- ): Promise<FromBTCSwap<T[ChainIdentifier]>> {
1159
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1160
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1161
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1162
- const amountData = {
1163
- amount,
1164
- token: tokenAddress,
1165
- exactIn: !exactOut
1166
- };
1167
- return this.createSwap(
1168
- chainIdentifier as ChainIdentifier,
1169
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTC].create(
1170
- recipient,
1171
- amountData,
1172
- candidates,
1173
- options,
1174
- additionalParams,
1175
- abortSignal
1176
- )),
1177
- amountData,
1178
- SwapType.FROM_BTC
1179
- );
1180
- }
1181
-
1182
- /**
1183
- * Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap
1184
- *
1185
- * @param chainIdentifier Chain identifier string of the destination smart chain
1186
- * @param recipient Recipient address on the destination chain
1187
- * @param tokenAddress Token address to receive
1188
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1189
- * @param exactOut Whether to use a exact out instead of exact in
1190
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1191
- * @param options Additional options for the swap
1192
- */
1193
- async createFromBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
1194
- chainIdentifier: ChainIdentifier,
1195
- recipient: string,
1196
- tokenAddress: string,
1197
- amount: bigint,
1198
- exactOut: boolean = false,
1199
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1200
- options?: FromBTCLNOptions
1201
- ): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
1202
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1203
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1204
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1205
- const amountData = {
1206
- amount,
1207
- token: tokenAddress,
1208
- exactIn: !exactOut
1209
- };
1210
- return this.createSwap(
1211
- chainIdentifier as ChainIdentifier,
1212
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN].create(
1213
- recipient,
1214
- amountData,
1215
- candidates,
1216
- options,
1217
- additionalParams,
1218
- abortSignal
1219
- )),
1220
- amountData,
1221
- SwapType.FROM_BTCLN
1222
- );
1223
- }
1224
-
1225
- /**
1226
- * Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap, withdrawing from
1227
- * an LNURL-withdraw link
1228
- *
1229
- * @param chainIdentifier Chain identifier string of the destination smart chain
1230
- * @param recipient Recipient address on the destination chain
1231
- * @param tokenAddress Token address to receive
1232
- * @param lnurl LNURL-withdraw link to pull the funds from
1233
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1234
- * @param exactOut Whether to use a exact out instead of exact in
1235
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1236
- * @param options Additional options for the swap
1237
- */
1238
- async createFromBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
1239
- chainIdentifier: ChainIdentifier,
1240
- recipient: string,
1241
- tokenAddress: string,
1242
- lnurl: string | LNURLWithdraw,
1243
- amount: bigint,
1244
- exactOut: boolean = false,
1245
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1246
- options?: FromBTCLNOptions
1247
- ): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
1248
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1249
- if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
1250
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1251
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1252
- const amountData = {
1253
- amount,
1254
- token: tokenAddress,
1255
- exactIn: !exactOut
1256
- };
1257
- return this.createSwap(
1258
- chainIdentifier as ChainIdentifier,
1259
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN].createViaLNURL(
1260
- recipient,
1261
- typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
1262
- amountData,
1263
- candidates,
1264
- options,
1265
- additionalParams,
1266
- abortSignal
1267
- ),
1268
- amountData,
1269
- SwapType.FROM_BTCLN
1270
- );
1271
- }
1272
-
1273
- /**
1274
- * Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap
1275
- *
1276
- * @param chainIdentifier Chain identifier string of the destination smart chain
1277
- * @param recipient Recipient address on the destination chain
1278
- * @param tokenAddress Token address to receive
1279
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1280
- * @param exactOut Whether to use a exact out instead of exact in
1281
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1282
- * @param options Additional options for the swap
1283
- */
1284
- async createFromBTCLNSwapNew<ChainIdentifier extends ChainIds<T>>(
1285
- chainIdentifier: ChainIdentifier,
1286
- recipient: string,
1287
- tokenAddress: string,
1288
- amount: bigint,
1289
- exactOut: boolean = false,
1290
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1291
- options?: FromBTCLNAutoOptions
1292
- ): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
1293
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1294
- if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
1295
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1296
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1297
- const amountData = {
1298
- amount,
1299
- token: tokenAddress,
1300
- exactIn: !exactOut
1301
- };
1302
- return this.createSwap(
1303
- chainIdentifier as ChainIdentifier,
1304
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN_AUTO].create(
1305
- recipient,
1306
- amountData,
1307
- candidates,
1308
- options,
1309
- additionalParams,
1310
- abortSignal
1311
- )),
1312
- amountData,
1313
- SwapType.FROM_BTCLN_AUTO
1314
- );
1315
- }
1316
-
1317
- /**
1318
- * Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap, withdrawing from
1319
- * an LNURL-withdraw link
1320
- *
1321
- * @param chainIdentifier Chain identifier string of the destination smart chain
1322
- * @param recipient Recipient address on the destination chain
1323
- * @param tokenAddress Token address to receive
1324
- * @param lnurl LNURL-withdraw link to pull the funds from
1325
- * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1326
- * @param exactOut Whether to use a exact out instead of exact in
1327
- * @param additionalParams Additional parameters sent to the LP when creating the swap
1328
- * @param options Additional options for the swap
1329
- */
1330
- async createFromBTCLNSwapNewViaLNURL<ChainIdentifier extends ChainIds<T>>(
1331
- chainIdentifier: ChainIdentifier,
1332
- recipient: string,
1333
- tokenAddress: string,
1334
- lnurl: string | LNURLWithdraw,
1335
- amount: bigint,
1336
- exactOut: boolean = false,
1337
- additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1338
- options?: FromBTCLNAutoOptions
1339
- ): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
1340
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1341
- if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
1342
- if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
1343
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1344
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1345
- const amountData = {
1346
- amount,
1347
- token: tokenAddress,
1348
- exactIn: !exactOut
1349
- };
1350
- return this.createSwap(
1351
- chainIdentifier as ChainIdentifier,
1352
- (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN_AUTO].createViaLNURL(
1353
- recipient,
1354
- typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
1355
- amountData,
1356
- candidates,
1357
- options,
1358
- additionalParams,
1359
- abortSignal
1360
- ),
1361
- amountData,
1362
- SwapType.FROM_BTCLN_AUTO
1363
- );
1364
- }
1365
-
1366
- /**
1367
- * Creates a trusted Bitcoin Lightning -> Smart chain ({@link SwapType.TRUSTED_FROM_BTCLN}) gas swap
1368
- *
1369
- * @param chainIdentifier Chain identifier string of the destination smart chain
1370
- * @param recipient Recipient address on the destination chain
1371
- * @param amount Amount of native token to receive, in base units
1372
- * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1373
- * @throws {Error} If no trusted intermediary specified
1374
- */
1375
- async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
1376
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1377
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1378
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1379
- const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1380
- if(useUrl==null) throw new Error("No trusted intermediary specified!");
1381
- const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
1382
- await swap._save();
1383
- return swap;
1384
- }
1385
-
1386
- /**
1387
- * Creates a trusted Bitcoin -> Smart chain ({@link SwapType.TRUSTED_FROM_BTC}) gas swap
1388
- *
1389
- * @param chainIdentifier Chain identifier string of the destination smart chain
1390
- * @param recipient Recipient address on the destination chain
1391
- * @param amount Amount of native token to receive, in base units
1392
- * @param refundAddress Bitcoin refund address, in case the swap fails the funds are refunded here
1393
- * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1394
- * @throws {Error} If no trusted intermediary specified
1395
- */
1396
- async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1397
- chainIdentifier: C, recipient: string,
1398
- amount: bigint, refundAddress?: string,
1399
- trustedIntermediaryOrUrl?: Intermediary | string
1400
- ): Promise<OnchainForGasSwap<T[C]>> {
1401
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1402
- if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1403
- recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1404
- const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1405
- if(useUrl==null) throw new Error("No trusted intermediary specified!");
1406
- const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
1407
- await swap._save();
1408
- return swap;
1409
- }
1410
-
1411
- /**
1412
- * @internal
1413
- */
1414
- create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<true>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean, lnurlWithdraw?: string | LNURLWithdraw): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
1415
- /**
1416
- * @internal
1417
- */
1418
- create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<false>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
1419
- /**
1420
- * @internal
1421
- */
1422
- create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<false>, amount: bigint, exactIn: boolean, address: string): Promise<ToBTCSwap<T[C]>>;
1423
- /**
1424
- * @internal
1425
- */
1426
- create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: bigint, exactIn: boolean, lnurlPay: string | LNURLPay): Promise<ToBTCLNSwap<T[C]>>;
1427
- /**
1428
- * @internal
1429
- */
1430
- create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: undefined, exactIn: false, lightningInvoice: string): Promise<ToBTCLNSwap<T[C]>>;
1431
- /**
1432
- * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
1433
- * or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
1434
- * invoice's pre-set amount is used instead.
1435
- * @deprecated Use {@link swap} instead
1436
- *
1437
- * @param signer Smartchain (Solana, Starknet, etc.) address of the user
1438
- * @param srcToken Source token of the swap, user pays this token
1439
- * @param dstToken Destination token of the swap, user receives this token
1440
- * @param amount Amount of the swap
1441
- * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1442
- * @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
1443
- * LNURL-withdrawal to withdraw money from
1444
- */
1445
- create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>>;
1446
- /**
1447
- * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
1448
- * or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
1449
- * invoice's pre-set amount is used instead.
1450
- * @deprecated Use {@link swap} instead
1451
- *
1452
- * @param signer Smartchain (Solana, Starknet, etc.) address of the user
1453
- * @param srcToken Source token of the swap, user pays this token
1454
- * @param dstToken Destination token of the swap, user receives this token
1455
- * @param amount Amount of the swap
1456
- * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1457
- * @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
1458
- * LNURL-withdrawal to withdraw money from
1459
- */
1460
- create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>> {
1461
- if(srcToken.chain==="BTC") {
1462
- return this.swap(srcToken, dstToken, amount, exactIn, addressLnurlLightningInvoice as any, signer);
1463
- } else {
1464
- return this.swap(srcToken, dstToken, amount, exactIn, signer, addressLnurlLightningInvoice as any);
1465
- }
1466
- }
1467
-
1468
- /**
1469
- * @internal
1470
- */
1471
- swap<C extends ChainIds<T>>(srcToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoOptions : FromBTCLNOptions)): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
1472
- /**
1473
- * @internal
1474
- */
1475
- swap<C extends ChainIds<T>>(srcToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCOptions : FromBTCOptions)): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
1476
- /**
1477
- * @internal
1478
- */
1479
- swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstAddress: string, options?: ToBTCOptions): Promise<ToBTCSwap<T[C]>>;
1480
- /**
1481
- * @internal
1482
- */
1483
- swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstLnurlPayOrInvoiceCreateService: string | LNURLPay | LightningInvoiceCreateService, options?: ToBTCLNOptions & {comment?: string}): Promise<ToBTCLNSwap<T[C]>>;
1484
- /**
1485
- * @internal
1486
- */
1487
- swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: undefined, exactIn: false | SwapAmountType.EXACT_OUT, src: string, dstLightningInvoice: string, options?: ToBTCLNOptions): Promise<ToBTCLNSwap<T[C]>>;
1488
- /**
1489
- * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
1490
- * or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
1491
- * invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
1492
- *
1493
- * @param srcToken Source token of the swap, user pays this token
1494
- * @param dstToken Destination token of the swap, user receives this token
1495
- * @param amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
1496
- * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1497
- * @param src Source wallet/lnurl-withdraw of the swap
1498
- * @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
1499
- * @param options Options for the swap
1500
- */
1501
- swap<C extends ChainIds<T>>(srcToken: Token<C> | string, dstToken: Token<C> | string, amount: bigint | string | undefined, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dst: string | LNURLPay | LightningInvoiceCreateService, options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions): Promise<ISwap<T[C]>>;
1502
- /**
1503
- * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
1504
- * or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
1505
- * invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
1506
- *
1507
- * @param _srcToken Source token of the swap, user pays this token
1508
- * @param _dstToken Destination token of the swap, user receives this token
1509
- * @param _amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
1510
- * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1511
- * @param src Source wallet/lnurl-withdraw of the swap
1512
- * @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
1513
- * @param options Options for the swap
1514
- */
1515
- swap<C extends ChainIds<T>>(
1516
- _srcToken: Token<C> | string,
1517
- _dstToken: Token<C> | string,
1518
- _amount: bigint | string | undefined,
1519
- exactIn: boolean | SwapAmountType,
1520
- src: undefined | string | LNURLWithdraw,
1521
- dst: string | LNURLPay | LightningInvoiceCreateService,
1522
- options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions
1523
- ): Promise<ISwap<T[C]>> {
1524
- const srcToken = typeof(_srcToken)==="string" ? this.getToken(_srcToken) as Token<C> : _srcToken;
1525
- const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
1526
- const amount = _amount==null ? null : (typeof(_amount)==="bigint" ? _amount : fromDecimal(_amount, exactIn ? srcToken.decimals : dstToken.decimals));
1527
- if(isBtcToken(srcToken)) {
1528
- if(isSCToken<C>(dstToken)) {
1529
- if(typeof(dst)!=="string") throw new Error("Destination for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
1530
- if(amount==null) throw new Error("Amount cannot be null for from btc swaps!");
1531
- if(srcToken.lightning) {
1532
- //FROM_BTCLN
1533
- if(src!=null) {
1534
- if(typeof(src)!=="string" && !isLNURLWithdraw(src)) throw new Error("LNURL must be a string or LNURLWithdraw object!");
1535
- return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
1536
- this.createFromBTCLNSwapNewViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any) :
1537
- this.createFromBTCLNSwapViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any);
1538
- } else {
1539
- return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
1540
- this.createFromBTCLNSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any):
1541
- this.createFromBTCLNSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1542
- }
1543
- } else {
1544
- //FROM_BTC
1545
- if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
1546
- return this.createFromBTCSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1547
- } else {
1548
- return this.createFromBTCSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1549
- }
1550
- }
1551
- }
1552
- } else if(isSCToken<C>(srcToken)) {
1553
- if(isBtcToken(dstToken)) {
1554
- if(typeof(src)!=="string") throw new Error("Source address for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
1555
- if(dstToken.lightning) {
1556
- //TO_BTCLN
1557
- if(typeof(dst)!=="string" && !isLNURLPay(dst)) throw new Error("Destination LNURL link/lightning invoice must be a string or LNURLPay object!");
1558
- if(isLNURLPay(dst) || this.Utils.isValidLNURL(dst)) {
1559
- if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via LNURL-pay!");
1560
- return this.createToBTCLNSwapViaLNURL(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1561
- } else if(isLightningInvoiceCreateService(dst)) {
1562
- if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via InvoiceCreateService!");
1563
- return this.createToBTCLNSwapViaInvoiceCreateService(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1564
- } else if(this.Utils.isLightningInvoice(dst)) {
1565
- if(!this.Utils.isValidLightningInvoice(dst))
1566
- throw new Error("Invalid lightning invoice specified, lightning invoice MUST contain pre-set amount!");
1567
- if(exactIn)
1568
- throw new Error("Only exact out swaps are possible with lightning invoices, use LNURL links for exact in lightning swaps!");
1569
- return this.createToBTCLNSwap(srcToken.chainId, src, srcToken.address, dst, undefined, options as any);
1570
- } else {
1571
- throw new Error("Supplied parameter is not LNURL link nor lightning invoice (bolt11)!");
1572
- }
1573
- } else {
1574
- //TO_BTC
1575
- if(typeof(dst)!=="string") throw new Error("Destination bitcoin address must be a string!");
1576
- if(amount==null) throw new Error("Amount cannot be null for to btc swaps!");
1577
- return this.createToBTCSwap(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1578
- }
1579
- }
1580
- }
1581
- throw new Error("Unsupported swap type");
1582
- }
1583
-
1584
- /**
1585
- * A helper function to sweep all the funds from a given wallet in a single swap, after getting the quote you can
1586
- * execute the swap by passing the returned `feeRate` and `utxos` to the {@link SpvFromBTCSwap.execute},
1587
- * {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.sendBitcoinTransaction} functions along
1588
- * with `spendFully=true`.
1589
- *
1590
- * @example
1591
- * Create the swap first using this function
1592
- * ```ts
1593
- * const {swap, utxos, btcFeeRate} = await swapper.sweepBitcoinWallet(wallet, Tokens.CITREA.CBTC, dstAddress);
1594
- * ```
1595
- * Then execute it using one of these execution paths - ensure that you supply the returned `utxos`, `btcFeeRate`
1596
- * params and also set `spendFully` to `true`!
1597
- *
1598
- * a) Execute and pass the returned utxos and btcFeeRate:
1599
- * ```ts
1600
- * await swap.execute(wallet, undefined, {feeRate: btcFeeRate, utxos: utxos, spendFully: true});
1601
- * ```
1602
- *
1603
- * b) Get funded PSBT to sign externally:
1604
- * ```ts
1605
- * const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt(wallet, btcFeeRate, undefined, utxos, true);
1606
- * // Sign the psbt at the specified signInputs indices
1607
- * const signedPsbt = ...;
1608
- * // Then submit back to the SDK
1609
- * await swap.submitPsbt(signedPsbt);
1610
- * ```
1611
- *
1612
- * c) Only sign and send the signed PSBT with the provided wallet:
1613
- * ```ts
1614
- * await swap.sendBitcoinTransaction(wallet, btcFeeRate, utxos, true);
1615
- * ```
1616
- */
1617
- async sweepBitcoinWallet<C extends ChainIds<T>>(
1618
- srcWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
1619
- _dstToken: SCToken<C> | string,
1620
- dstAddress: string,
1621
- options?: SpvFromBTCOptions
1622
- ): Promise<{
1623
- swap: SpvFromBTCSwap<T[C]>,
1624
- utxos: BitcoinWalletUtxo[],
1625
- btcFeeRate: number
1626
- }> {
1627
- const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
1628
- if(!isSCToken<C>(dstToken)) throw new Error("Destination token must be a smart chain token!");
1629
-
1630
- const wallet = toBitcoinWallet(srcWallet, this._bitcoinRpc, this.bitcoinNetwork);
1631
- if(wallet.getUtxoPool==null) throw new Error("Wallet needs to support the `getUtxoPool()` function!");
1632
-
1633
- const walletUtxosPromise = wallet.getUtxoPool();
1634
- const bitcoinFeeRatePromise = options?.bitcoinFeeRate ?? wallet.getFeeRate();
1635
-
1636
- const swap = await this.createFromBTCSwapNew(
1637
- dstToken.chainId, dstAddress, dstToken.address, null, false, undefined, {
1638
- ...options,
1639
- sourceWalletUtxos: walletUtxosPromise,
1640
- bitcoinFeeRate: bitcoinFeeRatePromise
1641
- }
1642
- );
1643
-
1644
- return {
1645
- swap,
1646
- utxos: await walletUtxosPromise,
1647
- btcFeeRate: Math.max(swap.minimumBtcFeeRate, await bitcoinFeeRatePromise)
1648
- };
1649
- }
1650
-
1651
- /**
1652
- * Returns all swaps
1653
- */
1654
- getAllSwaps(): Promise<ISwap[]>;
1655
- /**
1656
- * Returns all swaps for the specific chain, and optionally also for a specific signer's address
1657
- */
1658
- getAllSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
1659
- async getAllSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
1660
- const queryParams: QueryParams[] = [];
1661
- if(signer!=null) queryParams.push({key: "initiator", value: signer});
1662
-
1663
- if(chainId==null) {
1664
- const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1665
- const {unifiedSwapStorage, reviver} = this._chains[chainId];
1666
- return unifiedSwapStorage.query([queryParams], reviver);
1667
- }));
1668
- return res.flat();
1669
- } else {
1670
- const {unifiedSwapStorage, reviver} = this._chains[chainId];
1671
- return await unifiedSwapStorage.query([queryParams], reviver);
1672
- }
1673
- }
1674
-
1675
- /**
1676
- * Returns all swaps where an action is required (either claim or refund)
1677
- */
1678
- getActionableSwaps(): Promise<ISwap[]>;
1679
- /**
1680
- * Returns swaps where an action is required (either claim or refund) for the specific chain, and optionally also for a specific signer's address
1681
- */
1682
- getActionableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
1683
- async getActionableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
1684
- if(chainId==null) {
1685
- const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1686
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1687
- const queryParams: Array<QueryParams[]> = [];
1688
- for(let key in wrappers) {
1689
- const wrapper = wrappers[key as unknown as SwapType];
1690
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1691
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1692
- swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1693
- queryParams.push(swapTypeQueryParams);
1694
- }
1695
- return unifiedSwapStorage.query(queryParams, reviver);
1696
- }));
1697
- return res.flat().filter(swap => swap.requiresAction());
1698
- } else {
1699
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1700
- const queryParams: Array<QueryParams[]> = [];
1701
- for(let key in wrappers) {
1702
- const wrapper = wrappers[key as unknown as SwapType];
1703
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1704
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1705
- swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1706
- queryParams.push(swapTypeQueryParams);
1707
- }
1708
- return (await unifiedSwapStorage.query(queryParams, reviver)).filter(swap => swap.requiresAction());
1709
- }
1710
- }
1711
-
1712
- /**
1713
- * Returns all swaps that are refundable
1714
- */
1715
- getRefundableSwaps(): Promise<IToBTCSwap[]>;
1716
- /**
1717
- * Returns swaps which are refundable for the specific chain, and optionally also for a specific signer's address
1718
- */
1719
- getRefundableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IToBTCSwap<T[C]>[]>;
1720
- async getRefundableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IToBTCSwap[]> {
1721
- if(chainId==null) {
1722
- const res: IToBTCSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1723
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1724
- const queryParams: Array<QueryParams[]> = [];
1725
- for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
1726
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1727
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1728
- swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
1729
- queryParams.push(swapTypeQueryParams);
1730
- }
1731
- return unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
1732
- }));
1733
- return res.flat().filter(swap => swap.isRefundable());
1734
- } else {
1735
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1736
- const queryParams: Array<QueryParams[]> = [];
1737
- for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
1738
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1739
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1740
- swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
1741
- queryParams.push(swapTypeQueryParams);
1742
- }
1743
- const result = await unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
1744
- return result.filter(swap => swap.isRefundable());
1745
- }
1746
- }
1747
-
1748
- /**
1749
- * Returns all swaps that are manually claimable
1750
- */
1751
- getClaimableSwaps(): Promise<IClaimableSwap[]>;
1752
- /**
1753
- * Returns all swaps that are manually claimable for the specific chain, and optionally also for a specific signer's address
1754
- */
1755
- getClaimableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IClaimableSwap<T[C]>[]>;
1756
- async getClaimableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IClaimableSwap[]> {
1757
- if(chainId==null) {
1758
- const res: IClaimableSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1759
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1760
- const queryParams: Array<QueryParams[]> = [];
1761
- for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
1762
- if(wrapper==null) continue;
1763
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1764
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1765
- swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
1766
- queryParams.push(swapTypeQueryParams);
1767
- }
1768
- return unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
1769
- }));
1770
- return res.flat().filter(swap => swap.isClaimable());
1771
- } else {
1772
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1773
- const queryParams: Array<QueryParams[]> = [];
1774
- for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
1775
- if(wrapper==null) continue;
1776
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1777
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1778
- swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
1779
- queryParams.push(swapTypeQueryParams);
1780
- }
1781
- const result = await unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
1782
- return result.filter(swap => swap.isClaimable());
1783
- }
1784
- }
1785
-
1786
- /**
1787
- * Returns swap with a specific id (identifier)
1788
- */
1789
- getSwapById(id: string): Promise<ISwap>;
1790
- /**
1791
- * Returns swap with a specific id (identifier) on a specific chain and optionally with a signer
1792
- */
1793
- getSwapById<C extends ChainIds<T>>(id: string, chainId: C, signer?: string): Promise<ISwap<T[C]>>;
1794
- async getSwapById<C extends ChainIds<T>>(id: string, chainId?: C, signer?: string): Promise<ISwap> {
1795
- //Check in pending swaps first
1796
- if(chainId!=null) {
1797
- for(let key in this._chains[chainId].wrappers) {
1798
- const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
1799
- const result = wrapper._getPendingSwap(id);
1800
- if(result!=null) {
1801
- if (signer != null) {
1802
- if (result._getInitiator() === signer) return result;
1803
- } else {
1804
- return result;
1805
- }
1806
- }
1807
- }
1808
- } else {
1809
- for(let chainId in this._chains) {
1810
- for(let key in this._chains[chainId].wrappers) {
1811
- const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
1812
- const result = wrapper._getPendingSwap(id);
1813
- if(result!=null) {
1814
- if(signer!=null) {
1815
- if(result._getInitiator()===signer) return result;
1816
- } else {
1817
- return result;
1818
- }
1819
- }
1820
- }
1821
- }
1822
- }
1823
-
1824
- const queryParams: QueryParams[] = [];
1825
- if(signer!=null) queryParams.push({key: "initiator", value: signer});
1826
- queryParams.push({key: "id", value: id});
1827
- if(chainId==null) {
1828
- const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1829
- const {unifiedSwapStorage, reviver} = this._chains[chainId];
1830
- return unifiedSwapStorage.query([queryParams], reviver);
1831
- }));
1832
- return res.flat()[0];
1833
- } else {
1834
- const {unifiedSwapStorage, reviver} = this._chains[chainId];
1835
- return (await unifiedSwapStorage.query([queryParams], reviver))[0];
1836
- }
1837
- }
1838
-
1839
- /**
1840
- * Returns the swap with a proper return type, or `undefined` if not found or has wrong type
1841
- *
1842
- * @param id An ID of the swap ({@link ISwap.getId})
1843
- * @param chainId Chain identifier of the smart chain where the swap was initiated
1844
- * @param swapType Type of the swap
1845
- * @param signer An optional required smart chain signer address to fetch the swap for
1846
- */
1847
- async getTypedSwapById<C extends ChainIds<T>, S extends SwapType>(id: string, chainId: C, swapType: S, signer?: string): Promise<SwapTypeMapping<T[C]>[S] | undefined> {
1848
- let _swapType: SwapType = swapType;
1849
- if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC))
1850
- _swapType = SwapType.SPV_VAULT_FROM_BTC;
1851
- if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO))
1852
- _swapType = SwapType.FROM_BTCLN_AUTO;
1853
-
1854
- const wrapper = this._chains[chainId].wrappers[_swapType];
1855
- if(wrapper==null) return;
1856
-
1857
- const result = wrapper._getPendingSwap(id);
1858
- if(result!=null) {
1859
- if (signer != null) {
1860
- if (result._getInitiator() === signer) return result as any;
1861
- } else {
1862
- return result as any;
1863
- }
1864
- }
1865
-
1866
- const queryParams: QueryParams[] = [];
1867
- if(signer!=null) queryParams.push({key: "initiator", value: signer});
1868
- queryParams.push({key: "id", value: id});
1869
- const {unifiedSwapStorage, reviver} = this._chains[chainId];
1870
- const swap = (await unifiedSwapStorage.query([queryParams], reviver))[0];
1871
- if(isSwapType(swap, swapType)) return swap;
1872
- }
1873
-
1874
- private async syncSwapsForChain<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<void> {
1875
- const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1876
- const queryParams: Array<QueryParams[]> = [];
1877
- for(let key in wrappers) {
1878
- const wrapper = wrappers[key as unknown as SwapType];
1879
- const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1880
- if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1881
- swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1882
- queryParams.push(swapTypeQueryParams);
1883
- }
1884
- this.logger.debug("_syncSwaps(): Querying swaps swaps for chain "+chainId+"!");
1885
- const swaps = await unifiedSwapStorage.query(queryParams, reviver);
1886
- this.logger.debug("_syncSwaps(): Syncing "+swaps.length+" swaps!");
1887
-
1888
- const changedSwaps: ISwap<T[C]>[] = [];
1889
- const removeSwaps: ISwap<T[C]>[] = [];
1890
-
1891
- const assortedSwaps: {[swapType in SwapType]?: ISwap<T[string]>[]} = {};
1892
- swaps.forEach(swap => {
1893
- (assortedSwaps[swap.getType()] ??= []).push(swap);
1894
- });
1895
-
1896
- for(let key in assortedSwaps) {
1897
- const swapType = key as unknown as SwapType;
1898
- const wrapperSwaps = assortedSwaps[swapType];
1899
- const wrapper: ISwapWrapper<T[C], any> = wrappers[swapType];
1900
- const result = await wrapper.checkPastSwaps(wrapperSwaps, true);
1901
- changedSwaps.push(...result.changedSwaps);
1902
- removeSwaps.push(...result.removeSwaps);
1903
- }
1904
-
1905
- this.logger.debug("_syncSwaps(): Done syncing "+swaps.length+" swaps, saving "+changedSwaps.length+" changed swaps, removing "+removeSwaps.length+" swaps!");
1906
- await unifiedSwapStorage.saveAll(changedSwaps);
1907
- await unifiedSwapStorage.removeAll(removeSwaps);
1908
-
1909
- changedSwaps.forEach(swap => swap._emitEvent());
1910
- removeSwaps.forEach(swap => swap._emitEvent());
1911
- }
1912
-
1913
- /**
1914
- * Deletes the swaps from the persistent storage backend. Note that some data (like lightning network
1915
- * amounts and bolt11 invoices) are purely off-chain and can never be recovered later just from
1916
- * on-chain data!
1917
- *
1918
- * @param chainId Optional, to only delete swaps for this smart chain
1919
- * @param signer Optional, to only delete swaps for this smart chain signer (`chainId` param must be
1920
- * set to delete only signer's swaps)
1921
- */
1922
- async wipeStorage<C extends ChainIds<T>>(chainId?: C, signer?: string) {
1923
- if(chainId==null) {
1924
- const swaps = await this.getAllSwaps();
1925
- const chainSwaps: {
1926
- [chainId: string]: ISwap<any>[]
1927
- } = {};
1928
- swaps.forEach(swap => (chainSwaps[swap.chainIdentifier] ??= []).push(swap));
1929
- for(let chainId in chainSwaps) {
1930
- const currentChainSwaps = chainSwaps[chainId];
1931
- if(this._chains[chainId]==null) {
1932
- this.logger.warn(`wipeStorage(): Attempted to remove ${currentChainSwaps.length} swaps on ${chainId}, but smart chain not known!`);
1933
- continue;
1934
- }
1935
- await this._chains[chainId].unifiedSwapStorage.removeAll(currentChainSwaps);
1936
- this.logger.debug(`wipeStorage(): Successfully removed ${currentChainSwaps.length} swaps on ${chainId}!`);
1937
- }
1938
- } else {
1939
- if(this._chains[chainId]==null) throw new Error(`wipeStorage(): Smart chain with identifier ${chainId} not found!`);
1940
- const swaps = await this.getAllSwaps(chainId, signer);
1941
- await this._chains[chainId].unifiedSwapStorage.removeAll(swaps);
1942
- this.logger.debug(`wipeStorage(): Successfully removed ${swaps.length} swaps on ${chainId}!`);
1943
- }
1944
- }
1945
-
1946
- /**
1947
- * Synchronizes swaps from on-chain, this is ran automatically when SDK is initialized, hence
1948
- * should only be ran manually when `dontCheckPastSwaps=true` is passed in the swapper options,
1949
- * also deletes expired quotes
1950
- *
1951
- * @param chainId Optional chain identifier to only run swap sync for a single smart chain
1952
- * @param signer Optional signer to only run swap sync for swaps initiated by this signer
1953
- */
1954
- async _syncSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<void> {
1955
- if(chainId==null) {
1956
- await Promise.all(Object.keys(this._chains).map((chainId) => {
1957
- return this.syncSwapsForChain(chainId, signer);
1958
- }));
1959
- } else {
1960
- await this.syncSwapsForChain(chainId, signer);
1961
- }
1962
- }
1963
-
1964
- /**
1965
- * Recovers swaps from on-chain historical data.
1966
- *
1967
- * Please note that the recovered swaps might not be complete (i.e. missing amounts or addresses), as some
1968
- * of the swap data is purely off-chain and can never be recovered purely from on-chain data. This
1969
- * functions tries to recover as much swap data as possible.
1970
- *
1971
- * @param chainId Smart chain identifier string to recover the swaps from
1972
- * @param signer Signer address to recover the swaps for
1973
- * @param startBlockheight Optional starting blockheight for swap data recovery, will only check swaps
1974
- * initiated after this blockheight
1975
- */
1976
- async recoverSwaps<C extends ChainIds<T>>(chainId: C, signer: string, startBlockheight?: number): Promise<ISwap<T[C]>[]> {
1977
- //TODO: Recover swaps from all the known contract versions
1978
- const {versionedContracts, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1979
-
1980
- const recoveredSwaps: ISwap<T[C]>[] = [];
1981
- let someVersionSupportsRecovery = false;
1982
- const recoveredEscrowStates: {
1983
- [p: string]: {
1984
- init?: {
1985
- data: SwapData
1986
- getInitTxId: () => Promise<string>
1987
- getTxBlock: () => Promise<{
1988
- blockTime: number
1989
- blockHeight: number
1990
- }>
1991
- },
1992
- state: SwapCommitState
1993
- contractVersion: string
1994
- }
1995
- } = {};
1996
- const recoveredSpvStates: {
1997
- [contractVersion: string]: {
1998
- [escrowHash: string]: SpvWithdrawalClaimedState | SpvWithdrawalFrontedState
1999
- }
2000
- } = {};
2001
-
2002
- for(let contractVersion in versionedContracts) {
2003
- const {swapContract, spvVaultContract} = versionedContracts[contractVersion];
2004
-
2005
- if(
2006
- swapContract.getHistoricalSwaps==null ||
2007
- (spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
2008
- ) {
2009
- this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
2010
- continue;
2011
- }
2012
-
2013
- someVersionSupportsRecovery = true;
2014
-
2015
- const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
2016
- const spvVaultData = wrappers[SwapType.SPV_VAULT_FROM_BTC]==null
2017
- ? undefined
2018
- : await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
2019
-
2020
- for(let key in swaps) recoveredEscrowStates[key] = {...swaps[key], contractVersion};
2021
- if(spvVaultData!=null) for(let key in spvVaultData.withdrawals) (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
2022
- }
2023
- if(!someVersionSupportsRecovery) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
2024
-
2025
- const escrowHashes = Object.keys(recoveredEscrowStates);
2026
- for(let contractVersion in recoveredSpvStates) Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
2027
- this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
2028
- this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
2029
- const knownSwapsArray = await unifiedSwapStorage.query([[{key: "escrowHash", value: escrowHashes}]], reviver);
2030
- const knownSwaps: {[escrowHash: string]: ISwap<T[C]>} = {};
2031
- knownSwapsArray.forEach(val => {
2032
- const escrowHash = val._getEscrowHash();
2033
- if(escrowHash!=null) knownSwaps[escrowHash] = val;
2034
- });
2035
- this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
2036
-
2037
- for(let escrowHash in recoveredEscrowStates) {
2038
- const {init, state, contractVersion} = recoveredEscrowStates[escrowHash];
2039
- const knownSwap = knownSwaps[escrowHash];
2040
- const { swapContract } = versionedContracts[contractVersion];
2041
-
2042
- if(knownSwap==null) {
2043
- if(init==null) {
2044
- this.logger.warn(`recoverSwaps(escrow): Fetched ${escrowHash} swap state, but swap not found locally!`);
2045
- continue;
2046
- }
2047
- } else if(knownSwap instanceof IEscrowSwap) {
2048
- this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
2049
- if((knownSwap._contractVersion ?? "v1")!==contractVersion) {
2050
- this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
2051
- continue;
2052
- }
2053
- if(await knownSwap._forciblySetOnchainState(state)) {
2054
- await knownSwap._save();
2055
- }
2056
- continue;
2057
- } else {
2058
- this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap already known and in local storage!`);
2059
- continue;
2060
- }
2061
-
2062
- const data = init.data;
2063
-
2064
- //Classify swap
2065
- let swap: ISwap<T[C]> | undefined | null;
2066
- let typeIdentified: boolean = false;
2067
- if(data.getType()===ChainSwapType.HTLC) {
2068
- if(data.isOfferer(signer)) {
2069
- //To BTCLN
2070
- typeIdentified = true;
2071
- const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
2072
- swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2073
- } else if(data.isClaimer(signer)) {
2074
- //From BTCLN
2075
- typeIdentified = true;
2076
- const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
2077
- if(swapContract.supportsInitWithoutClaimer && wrappers[SwapType.FROM_BTCLN_AUTO]!=null) {
2078
- swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2079
- } else {
2080
- swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2081
- }
2082
- }
2083
- } else if(data.getType()===ChainSwapType.CHAIN_NONCED) {
2084
- //To BTC
2085
- typeIdentified = true;
2086
- const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
2087
- swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2088
- } else if(data.getType()===ChainSwapType.CHAIN) {
2089
- //From BTC
2090
- typeIdentified = true;
2091
- const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
2092
- swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2093
- }
2094
-
2095
- if(swap!=null) {
2096
- recoveredSwaps.push(swap);
2097
- } else {
2098
- if(typeIdentified) this.logger.debug(`recoverSwaps(escrow): Swap data type correctly identified but swap returned is null for swap ${escrowHash}`);
2099
- }
2100
- }
2101
-
2102
- for(let contractVersion in recoveredSpvStates) {
2103
- const { spvVaultContract } = versionedContracts[contractVersion];
2104
- const spvVaultData = recoveredSpvStates[contractVersion];
2105
-
2106
- const vaultsData = await spvVaultContract.getMultipleVaultData(
2107
- Object.keys(spvVaultData)
2108
- .map(btcTxId => ({
2109
- owner: spvVaultData[btcTxId].owner,
2110
- vaultId: spvVaultData[btcTxId].vaultId
2111
- }))
2112
- );
2113
-
2114
- for(let btcTxId in spvVaultData) {
2115
- const state = spvVaultData[btcTxId];
2116
- const knownSwap = knownSwaps[btcTxId];
2117
-
2118
- if(knownSwap!=null) {
2119
- if(knownSwap instanceof SpvFromBTCSwap) {
2120
- this.logger.debug(`recoverSwaps(spv_vault): Forcibly updating ${btcTxId} swap: swap already known and in local storage!`);
2121
- //TODO: Forcibly set on-chain state to the swap
2122
- // if(await knownSwap._forciblySetOnchainState(state)) {
2123
- // await knownSwap._save();
2124
- // }
2125
- continue;
2126
- } else {
2127
- this.logger.debug(`recoverSwaps(spv_vault): Skipping ${btcTxId} swap: swap already known and in local storage!`);
2128
- continue;
2129
- }
2130
- }
2131
-
2132
- const lp = this.intermediaryDiscovery.intermediaries.find(
2133
- val => val.supportsChain(chainId) && state.owner.toLowerCase()===val.getAddress(chainId).toLowerCase()
2134
- );
2135
- const swap = await wrappers[SwapType.SPV_VAULT_FROM_BTC].recoverFromState(
2136
- state,
2137
- contractVersion,
2138
- vaultsData[state.owner]?.[state.vaultId.toString(10)],
2139
- lp
2140
- );
2141
- if(swap!=null) {
2142
- recoveredSwaps.push(swap);
2143
- } else {
2144
- this.logger.debug(`recoverSwaps(spv_vault): Swap data type correctly identified but swap returned is null for swap ${btcTxId}`);
2145
- }
2146
- }
2147
- }
2148
-
2149
- this.logger.debug(`recoverSwaps(): Successfully recovered ${recoveredSwaps.length} swaps!`);
2150
-
2151
- return recoveredSwaps;
2152
- }
2153
-
2154
- getToken(ticker: "BTC" | "BITCOIN-BTC"): BtcToken<false>;
2155
- getToken(ticker: "BTCLN" | "BTC-LN" | "LIGHTNING-BTC"): BtcToken<true>;
2156
- getToken<ChainIdentifier extends ChainIds<T>>(ticker: `${ChainIdentifier}-${string}`): SCToken<ChainIdentifier>;
2157
- getToken(tickerOrAddress: string): Token<ChainIds<T>>;
2158
- /**
2159
- * Returns the {@link Token} object for a given token
2160
- *
2161
- * @param tickerOrAddress Token to return the object for, can use multiple formats:
2162
- * - a) token ticker, such as `"BTC"`, `"SOL"`, etc.
2163
- * - b) token ticker prefixed with smart chain identifier, such as `"SOLANA-SOL"`, `"SOLANA-USDC"`, etc.
2164
- * - c) token address
2165
- */
2166
- getToken(tickerOrAddress: string): Token<ChainIds<T>> {
2167
- //Btc tokens - BTC, BTCLN, BTC-LN
2168
- if(tickerOrAddress==="BTC" || tickerOrAddress==="BITCOIN-BTC") return BitcoinTokens.BTC;
2169
- if(tickerOrAddress==="BTCLN" || tickerOrAddress==="BTC-LN" || tickerOrAddress==="LIGHTNING-BTC") return BitcoinTokens.BTCLN;
2170
-
2171
- //Check if the ticker is in format <chainId>-<ticker>, i.e. SOLANA-USDC, STARKNET-WBTC
2172
- if(tickerOrAddress.includes("-")) {
2173
- const [chainId, ticker] = tickerOrAddress.split("-");
2174
- const token = this._tokensByTicker[chainId]?.[ticker];
2175
- if(token==null) throw new UserError(`Not found ticker: ${ticker} for chainId: ${chainId}`);
2176
- return token;
2177
- }
2178
-
2179
- const possibleTokens: SCToken[] = [];
2180
- for(let chainId in this._chains) {
2181
- const chain = this._chains[chainId];
2182
- if(chain.chainInterface.isValidToken(tickerOrAddress)) {
2183
- //Try to find in known token addresses
2184
- const token = this._tokens[chainId]?.[tickerOrAddress];
2185
- if(token!=null) return token;
2186
- } else {
2187
- //Check in known tickers
2188
- const token = this._tokensByTicker[chainId]?.[tickerOrAddress];
2189
- if(token!=null) possibleTokens.push(token);
2190
- }
2191
- }
2192
-
2193
- if(possibleTokens.length===0) throw new UserError(`Specified token address or ticker ${tickerOrAddress} not found!`);
2194
- //In case we've found the token in multiple chains
2195
- if(possibleTokens.length>1) throw new UserError(`A ticker ${tickerOrAddress} has been found in multiple chains, narrow it down by using <chainId>-${tickerOrAddress} notation`)
2196
- return possibleTokens[0];
2197
- }
2198
-
2199
- /**
2200
- * Creates a child swapper instance with a given smart chain
2201
- *
2202
- * @param chainIdentifier Smart chain identifier for the created child swapper instance
2203
- */
2204
- withChain<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier): SwapperWithChain<T, ChainIdentifier> {
2205
- if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
2206
- return new SwapperWithChain<T, ChainIdentifier>(this, chainIdentifier as ChainIdentifier);
2207
- }
2208
-
2209
- /**
2210
- * Returns an array of all the supported smart chains
2211
- */
2212
- getSmartChains(): ChainIds<T>[] {
2213
- return Object.keys(this._chains);
2214
- }
2215
-
2216
- /**
2217
- * Returns whether the SDK supports a given swap type on a given chain based on currently known LPs
2218
- *
2219
- * @param chainId Smart chain identifier string
2220
- * @param swapType Swap protocol type
2221
- */
2222
- supportsSwapType<
2223
- ChainIdentifier extends ChainIds<T>,
2224
- Type extends SwapType
2225
- >(chainId: ChainIdentifier, swapType: Type): SupportsSwapType<T[ChainIdentifier], Type> {
2226
- return (this._chains[chainId]?.wrappers[swapType] != null) as any;
2227
- }
2228
-
2229
- /**
2230
- * @internal
2231
- */
2232
- getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<true>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN);
2233
- /**
2234
- * @internal
2235
- */
2236
- getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<false>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC);
2237
- /**
2238
- * @internal
2239
- */
2240
- getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<false>): SwapType.TO_BTC;
2241
- /**
2242
- * @internal
2243
- */
2244
- getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<true>): SwapType.TO_BTCLN;
2245
- /**
2246
- * Returns type of the swap based on input and output tokens specified
2247
- *
2248
- * @param srcToken Source token
2249
- * @param dstToken Destination token
2250
- */
2251
- getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN;
2252
- /**
2253
- * Returns type of the swap based on input and output tokens specified
2254
- *
2255
- * @param srcToken Source token
2256
- * @param dstToken Destination token
2257
- */
2258
- getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN {
2259
- if(isSCToken(srcToken)) {
2260
- if(!isBtcToken(dstToken)) throw new Error("Swap not supported");
2261
- if(dstToken.lightning) {
2262
- return SwapType.TO_BTCLN;
2263
- } else {
2264
- return SwapType.TO_BTC;
2265
- }
2266
- } else if(isBtcToken(srcToken)) {
2267
- if(!isSCToken(dstToken)) throw new Error("Swap not supported");
2268
- if(srcToken.lightning) {
2269
- if(this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO)) {
2270
- return SwapType.FROM_BTCLN_AUTO;
2271
- } else {
2272
- return SwapType.FROM_BTCLN;
2273
- }
2274
- } else {
2275
- if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
2276
- return SwapType.SPV_VAULT_FROM_BTC;
2277
- } else {
2278
- return SwapType.FROM_BTC;
2279
- }
2280
- }
2281
- }
2282
- throw new Error("Swap not supported");
2283
- }
2284
-
2285
- /**
2286
- * Helper information about various swap protocol and their features:
2287
- * - `requiresInputWallet`: Whether a swap requires a connected wallet on the input chain able to sign
2288
- * arbitrary transaction
2289
- * - `requiresOutputWallet`: Whether a swap requires a connected wallet on the output chain able to sign
2290
- * arbitrary transactions
2291
- * - `supportsGasDrop`: Whether a swap supports the "gas drop" feature, allowing to user to receive a small
2292
- * amount of native token as part of the swap when swapping to smart chains
2293
- *
2294
- * Uses a `Record` type here, use the {@link SwapProtocolInfo} import for a literal readonly type, with
2295
- * pre-filled exact values in the type.
2296
- */
2297
- readonly SwapTypeInfo = SwapProtocolInfo as Record<SwapType, {
2298
- requiresInputWallet: boolean,
2299
- requiresOutputWallet: boolean,
2300
- supportsGasDrop: boolean
2301
- }>;
2302
-
2303
- /**
2304
- * Returns minimum/maximum limits for inputs and outputs for a swap between given tokens
2305
- *
2306
- * @param srcToken Source token
2307
- * @param dstToken Destination token
2308
- */
2309
- getSwapLimits<C extends ChainIds<T>, A extends Token<C>, B extends Token<C>>(srcToken: A, dstToken: B): {
2310
- input: {min: TokenAmount<A>, max?: TokenAmount<A>},
2311
- output: {min: TokenAmount<B>, max?: TokenAmount<B>}
2312
- } {
2313
- const swapType = this.getSwapType(srcToken, dstToken);
2314
- const scToken = isSCToken(srcToken) ? srcToken : isSCToken(dstToken) ? dstToken : null;
2315
- if(scToken==null) throw new Error("At least one token needs to be a smart chain token!");
2316
- const result: {input: {min?: bigint, max?: bigint}, output: {min?: bigint, max?: bigint}} = {
2317
- input: {},
2318
- output: {}
2319
- };
2320
- for(let lp of this.intermediaryDiscovery.intermediaries) {
2321
- const lpMinMax = lp.getSwapLimits(swapType, scToken.chainId, scToken.address);
2322
- if(lpMinMax==null) continue;
2323
- result.input.min = result.input.min==null ? lpMinMax.input.min : bigIntMin(result.input.min, lpMinMax.input.min);
2324
- result.input.max = result.input.max==null ? lpMinMax.input.max : bigIntMax(result.input.max, lpMinMax.input.max);
2325
- result.output.min = result.output.min==null ? lpMinMax.output.min : bigIntMin(result.output.min, lpMinMax.output.min);
2326
- result.output.max = result.output.max==null ? lpMinMax.output.max : bigIntMax(result.output.max, lpMinMax.output.max);
2327
- }
2328
- return {
2329
- input: {
2330
- min: toTokenAmount(result.input.min ?? 1n, srcToken, this.prices),
2331
- max: result.input.max==null ? undefined : toTokenAmount(result.input.max, srcToken, this.prices),
2332
- },
2333
- output: {
2334
- min: toTokenAmount(result.output.min ?? 1n, dstToken, this.prices),
2335
- max: result.output.max==null ? undefined : toTokenAmount(result.output.max, dstToken, this.prices),
2336
- }
2337
- }
2338
- }
2339
-
2340
- /**
2341
- * Returns an array of supported tokens either on the input or on the output of a swap
2342
- *
2343
- * @param input Whether to return input tokens or output tokens
2344
- */
2345
- getSupportedTokens(input: SwapSide | boolean): Token[] {
2346
- const tokens: {[chainId: string]: Set<string>} = {};
2347
- let lightning = false;
2348
- let btc = false;
2349
- this.intermediaryDiscovery.intermediaries.forEach(lp => {
2350
- for(let swapType of [SwapType.TO_BTC, SwapType.TO_BTCLN, SwapType.FROM_BTC, SwapType.FROM_BTCLN, SwapType.SPV_VAULT_FROM_BTC, SwapType.FROM_BTCLN_AUTO]) {
2351
- if(lp.services[swapType]?.chainTokens==null) continue;
2352
- for(let chainId of this.getSmartChains()) {
2353
- if(this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC) ? swapType===SwapType.FROM_BTC : swapType===SwapType.SPV_VAULT_FROM_BTC) continue;
2354
- if(this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO) ? swapType===SwapType.FROM_BTCLN : swapType===SwapType.FROM_BTCLN_AUTO) continue;
2355
- const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
2356
- if(chainTokens==null) continue;
2357
- for (let tokenAddress of chainTokens) {
2358
- if(input) {
2359
- if(swapType===SwapType.TO_BTC || swapType===SwapType.TO_BTCLN) {
2360
- tokens[chainId] ??= new Set();
2361
- tokens[chainId].add(tokenAddress);
2362
- }
2363
- if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTCLN_AUTO) {
2364
- lightning = true;
2365
- }
2366
- if(swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC) {
2367
- btc = true;
2368
- }
2369
- } else {
2370
- if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC || swapType===SwapType.FROM_BTCLN_AUTO) {
2371
- tokens[chainId] ??= new Set();
2372
- tokens[chainId].add(tokenAddress);
2373
- }
2374
- if(swapType===SwapType.TO_BTCLN) {
2375
- lightning = true;
2376
- }
2377
- if(swapType===SwapType.TO_BTC) {
2378
- btc = true;
2379
- }
2380
- }
2381
- }
2382
- }
2383
- }
2384
- });
2385
- const output: Token[] = [];
2386
- if(lightning) output.push(BitcoinTokens.BTCLN);
2387
- if(btc) output.push(BitcoinTokens.BTC);
2388
- for(let chainId in tokens) {
2389
- tokens[chainId].forEach(tokenAddress => {
2390
- const token = this._tokens?.[chainId]?.[tokenAddress];
2391
- if(token!=null) output.push(token);
2392
- })
2393
- }
2394
- return output;
2395
- }
2396
-
2397
- /**
2398
- * Returns a set of supported tokens by all the intermediaries offering a specific swap service
2399
- *
2400
- * @param _swapType Swap service type to check supported tokens for
2401
- */
2402
- private getSupportedTokensForSwapType(_swapType: SwapType): SCToken[] {
2403
- const tokens: {[chainId: string]: Set<string>} = {};
2404
- this.intermediaryDiscovery.intermediaries.forEach(lp => {
2405
- for(let chainId of this.getSmartChains()) {
2406
- let swapType = _swapType;
2407
- if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC)) swapType = SwapType.SPV_VAULT_FROM_BTC;
2408
- if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO)) swapType = SwapType.FROM_BTCLN_AUTO;
2409
- if(lp.services[swapType]?.chainTokens==null) break;
2410
- const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
2411
- if(chainTokens==null) continue;
2412
- for(let tokenAddress of chainTokens) {
2413
- tokens[chainId] ??= new Set();
2414
- tokens[chainId].add(tokenAddress);
2415
- }
2416
- }
2417
- });
2418
- const output: SCToken[] = [];
2419
- for(let chainId in tokens) {
2420
- tokens[chainId].forEach(tokenAddress => {
2421
- const token = this._tokens?.[chainId]?.[tokenAddress];
2422
- if(token!=null) output.push(token);
2423
- })
2424
- }
2425
- return output;
2426
- }
2427
-
2428
- /**
2429
- * Returns the set of supported token addresses by all the intermediaries we know of offering a specific swapType service
2430
- *
2431
- * @param chainIdentifier Chain identifier string
2432
- * @param swapType Specific swap type for which to obtain supported tokens
2433
- */
2434
- private getSupportedTokenAddresses<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier, swapType: SwapType): Set<string> {
2435
- const set = new Set<string>();
2436
- this.intermediaryDiscovery.intermediaries.forEach(lp => {
2437
- const chainTokens = lp.services[swapType]?.chainTokens?.[chainIdentifier];
2438
- if(chainTokens==null) return;
2439
- chainTokens.forEach(token => set.add(token));
2440
- });
2441
- return set;
2442
- }
2443
-
2444
- /**
2445
- * Returns tokens that you can swap to (if input=true) from a given token,
2446
- * or tokens that you can swap from (if input=false) to a given token
2447
- */
2448
- getSwapCounterTokens(token: Token, input: SwapSide | boolean): Token[] {
2449
- if(isSCToken(token)) {
2450
- const result: Token[] = [];
2451
- if(input) {
2452
- //TO_BTC or TO_BTCLN
2453
- if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTCLN).has(token.address)) {
2454
- result.push(BitcoinTokens.BTCLN);
2455
- }
2456
- if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTC).has(token.address)) {
2457
- result.push(BitcoinTokens.BTC);
2458
- }
2459
- } else {
2460
- //FROM_BTC or FROM_BTCLN
2461
- const fromLightningSwapType = this.supportsSwapType(token.chainId, SwapType.FROM_BTCLN_AUTO) ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN;
2462
- if(this.getSupportedTokenAddresses(token.chainId, fromLightningSwapType).has(token.address)) {
2463
- result.push(BitcoinTokens.BTCLN);
2464
- }
2465
- const fromOnchainSwapType = this.supportsSwapType(token.chainId, SwapType.SPV_VAULT_FROM_BTC) ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC;
2466
- if(this.getSupportedTokenAddresses(token.chainId, fromOnchainSwapType).has(token.address)) {
2467
- result.push(BitcoinTokens.BTC);
2468
- }
2469
- }
2470
- return result;
2471
- } else {
2472
- if(input) {
2473
- if(token.lightning) {
2474
- return this.getSupportedTokensForSwapType(SwapType.FROM_BTCLN);
2475
- } else {
2476
- return this.getSupportedTokensForSwapType(SwapType.FROM_BTC);
2477
- }
2478
- } else {
2479
- if(token.lightning) {
2480
- return this.getSupportedTokensForSwapType(SwapType.TO_BTCLN);
2481
- } else {
2482
- return this.getSupportedTokensForSwapType(SwapType.TO_BTC);
2483
- }
2484
- }
2485
- }
2486
- }
2487
-
2488
- }
1
+ import {ISwapPrice} from "../prices/abstract/ISwapPrice";
2
+ import {
3
+ BitcoinNetwork, BitcoinRpc, BitcoinRpcWithAddressIndex, BtcBlock,
4
+ BtcRelay,
5
+ ChainData,
6
+ ChainSwapType,
7
+ ChainType, LightningNetworkApi,
8
+ Messenger,
9
+ RelaySynchronizer, SpvWithdrawalClaimedState, SpvWithdrawalFrontedState, SwapCommitState, SwapContract, SwapData
10
+ } from "@atomiqlabs/base";
11
+ import {
12
+ ToBTCLNOptions,
13
+ ToBTCLNWrapper
14
+ } from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper";
15
+ import {ToBTCOptions, ToBTCWrapper} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper";
16
+ import {FromBTCLNOptions, FromBTCLNWrapper} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper";
17
+ import {FromBTCOptions, FromBTCWrapper} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper";
18
+ import {IntermediaryDiscovery, MultichainSwapBounds, SwapBounds} from "../intermediaries/IntermediaryDiscovery";
19
+ import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
20
+ import {ISwap} from "../swaps/ISwap";
21
+ import {IntermediaryError} from "../errors/IntermediaryError";
22
+ import {SwapType} from "../enums/SwapType";
23
+ import {FromBTCLNSwap} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap";
24
+ import {FromBTCSwap} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCSwap";
25
+ import {ToBTCLNSwap} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap";
26
+ import {ToBTCSwap} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCSwap";
27
+ import {LnForGasWrapper} from "../swaps/trusted/ln/LnForGasWrapper";
28
+ import {LnForGasSwap} from "../swaps/trusted/ln/LnForGasSwap";
29
+ import {EventEmitter} from "events";
30
+ import {Intermediary} from "../intermediaries/Intermediary";
31
+ import {ISwapWrapper, WrapperCtorTokens} from "../swaps/ISwapWrapper";
32
+ import {bigIntCompare, bigIntMax, bigIntMin, fromDecimal, objectMap, randomBytes} from "../utils/Utils";
33
+ import {OutOfBoundsError} from "../errors/RequestError";
34
+ import {SwapperWithChain} from "./SwapperWithChain";
35
+ import {OnchainForGasSwap} from "../swaps/trusted/onchain/OnchainForGasSwap";
36
+ import {OnchainForGasWrapper} from "../swaps/trusted/onchain/OnchainForGasWrapper";
37
+ import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils";
38
+ import {IUnifiedStorage, QueryParams} from "../storage/IUnifiedStorage";
39
+ import {
40
+ UnifiedSwapStorage,
41
+ UnifiedSwapStorageCompositeIndexes,
42
+ UnifiedSwapStorageIndexes
43
+ } from "../storage/UnifiedSwapStorage";
44
+ import {UnifiedSwapEventListener} from "../events/UnifiedSwapEventListener";
45
+ import {IToBTCSwap} from "../swaps/escrow_swaps/tobtc/IToBTCSwap";
46
+ import {SpvFromBTCOptions, SpvFromBTCWrapper} from "../swaps/spv_swaps/SpvFromBTCWrapper";
47
+ import {SpvFromBTCSwap} from "../swaps/spv_swaps/SpvFromBTCSwap";
48
+ import {SwapperUtils} from "./SwapperUtils";
49
+ import {FromBTCLNAutoOptions, FromBTCLNAutoWrapper} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper";
50
+ import {FromBTCLNAutoSwap} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap";
51
+ import {UserError} from "../errors/UserError";
52
+ import {SwapAmountType} from "../enums/SwapAmountType";
53
+ import {IClaimableSwap} from "../swaps/IClaimableSwap";
54
+ import {correctClock} from "../utils/AutomaticClockDriftCorrection";
55
+ import {isSwapType, SwapProtocolInfo, SwapTypeMapping} from "../utils/SwapUtils";
56
+ import {IndexedDBUnifiedStorage} from "../storage-browser/IndexedDBUnifiedStorage";
57
+ import {TokenAmount, toTokenAmount} from "../types/TokenAmount";
58
+ import {BitcoinTokens, BtcToken, isBtcToken, isSCToken, SCToken, Token} from "../types/Token";
59
+ import {AmountData} from "../types/AmountData";
60
+ import {getLogger} from "../utils/Logger";
61
+ import {isLNURLWithdraw, LNURLWithdraw} from "../types/lnurl/LNURLWithdraw";
62
+ import {isLNURLPay, LNURLPay} from "../types/lnurl/LNURLPay";
63
+ import {tryWithRetries} from "../utils/RetryUtils";
64
+ import {NotNever} from "../utils/TypeUtils";
65
+ import {IEscrowSwap} from "../swaps/escrow_swaps/IEscrowSwap";
66
+ import {LightningInvoiceCreateService, isLightningInvoiceCreateService} from "../types/wallets/LightningInvoiceCreateService";
67
+ import {SwapSide} from "../enums/SwapSide";
68
+ import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "../bitcoin/wallet/IBitcoinWallet";
69
+ import {MinimalBitcoinWalletInterface} from "../types/wallets/MinimalBitcoinWalletInterface";
70
+ import {toBitcoinWallet} from "../utils/BitcoinWalletUtils";
71
+
72
+ /**
73
+ * Configuration options for the Swapper
74
+ * @category Core
75
+ */
76
+ export type SwapperOptions = {
77
+ /**
78
+ * Manual override for the intermediary (LP) URLs for the SDK to use, by default these are fetched automatically
79
+ * from the registry
80
+ */
81
+ intermediaryUrl?: string | string[],
82
+ /**
83
+ * Registry URL for where to look for active intermediary (LP) endpoint URLs
84
+ */
85
+ registryUrl?: string,
86
+
87
+ /**
88
+ * Bitcoin network to use for the swaps,
89
+ */
90
+ bitcoinNetwork?: BitcoinNetwork,
91
+
92
+ /**
93
+ * Timeout (in milliseconds) for HTTP GET requests done by the SDK
94
+ */
95
+ getRequestTimeout?: number,
96
+ /**
97
+ * Timeout (in milliseconds) for HTTP POST requests done by the SDK
98
+ */
99
+ postRequestTimeout?: number,
100
+ /**
101
+ * Additional parameters to be sent to the intermediaries (LPs), when requesting quotes from them
102
+ */
103
+ defaultAdditionalParameters?: {[key: string]: any},
104
+ /**
105
+ * Optional name prefix to use when creating a swap storage, you can use this to create separate storage
106
+ * instances that don't overlap.
107
+ */
108
+ storagePrefix?: string,
109
+ /**
110
+ * Sets the default intermediary (LP) to use for the trusted gas swaps, if not set the SDK uses a default one
111
+ */
112
+ defaultTrustedIntermediaryUrl?: string,
113
+
114
+ /**
115
+ * A function callback to retrieve a specific named storage container for swap persistency. If not present, the
116
+ * default IndexedDB storage adapter is used. When you use the SDK in non-browser based environments you need to
117
+ * provide this callback such that the SDK is able to use a custom storage adapter.
118
+ *
119
+ * @param storageName Name of the container to retrieve
120
+ */
121
+ swapStorage?: (storageName: string) => IUnifiedStorage<UnifiedSwapStorageIndexes, UnifiedSwapStorageCompositeIndexes>,
122
+
123
+ /**
124
+ * By setting this flag, the swapper doesn't schedule automatic tick timers. To make sure the swap states are
125
+ * properly updated (e.g. the expired swaps properly move to the expired state), you should call the
126
+ * {@link Swapper._syncSwaps} function periodically. This flag should be set when you run an environment that
127
+ * doesn't support long-running timers - e.g. serverless environments like Azure Function Apps or AWS Lambda
128
+ */
129
+ noTimers?: boolean,
130
+ /**
131
+ * By setting this flag, the swapper doesn't subscribe to on-chain events. To make sure the swap states are
132
+ * properly updated you should call the {@link Swapper._syncSwaps} function periodically. This flag should be
133
+ * set when you run an environment that doesn't support long-running timers and websocket connections - e.g.
134
+ * serverless environments like Azure Function Apps or AWS Lambda
135
+ */
136
+ noEvents?: boolean,
137
+ /**
138
+ * By setting this flag, the swap objects will not be cached in the SDK and instead will always be loaded from
139
+ * the persistent storage. By default, the SDK uses a `WeakRef` mapping of swaps, to ensure that when the same
140
+ * swap is loaded concurrently, it returns the same object reference to both, making the changes on the object
141
+ * atomic. This flag should be set to `true` when running in an environment where multiple instances of the SDK
142
+ * access the same swap database - e.g. serverless environments like Azure Function Apps or AWS Lambda
143
+ */
144
+ noSwapCache?: boolean,
145
+ /**
146
+ * Skip checking past swaps when the swapper is initiated with {@link Swapper.init}, you can call the
147
+ * {@link Swapper._syncSwaps} function later, to check the swaps. By default, the SDK checks the state
148
+ * of all the known swaps during init.
149
+ */
150
+ dontCheckPastSwaps?: boolean,
151
+ /**
152
+ * Skip fetching the LPs when the swapper is initiated with {@link Swapper.init}, this means the list of available
153
+ * tokens and swap limits won't be available immediately. LPs will be fetched automatically later, when a swap
154
+ * is requested
155
+ */
156
+ dontFetchLPs?: boolean,
157
+ /**
158
+ * Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
159
+ * `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
160
+ * want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
161
+ *
162
+ * Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
163
+ * `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
164
+ * might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
165
+ * frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
166
+ * necessitating the saving of the swap data to the persistent storage until it is actually initiated.
167
+ */
168
+ saveUninitializedSwaps?: boolean,
169
+ /**
170
+ * Automatically checks system time on initialize, if the system time drifts too far from the actual time
171
+ * (as checked from multiple server sources) it adjusts the `Date.now()` function to return proper actual time.
172
+ */
173
+ automaticClockDriftCorrection?: boolean
174
+ };
175
+
176
+ /**
177
+ * Type representing multiple blockchain configurations
178
+ * @category Core
179
+ */
180
+ export type MultiChain = {
181
+ [chainIdentifier in string]: ChainType;
182
+ };
183
+
184
+ type ChainSpecificData<T extends ChainType> = {
185
+ wrappers: {
186
+ [SwapType.TO_BTCLN]: ToBTCLNWrapper<T>,
187
+ [SwapType.TO_BTC]: ToBTCWrapper<T>,
188
+ [SwapType.FROM_BTCLN]: FromBTCLNWrapper<T>,
189
+ [SwapType.FROM_BTC]: FromBTCWrapper<T>,
190
+ [SwapType.TRUSTED_FROM_BTCLN]: LnForGasWrapper<T>,
191
+ [SwapType.TRUSTED_FROM_BTC]: OnchainForGasWrapper<T>,
192
+ [SwapType.SPV_VAULT_FROM_BTC]: SpvFromBTCWrapper<T>,
193
+ [SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>
194
+ }
195
+ chainEvents: T["Events"],
196
+ chainInterface: T["ChainInterface"],
197
+ unifiedChainEvents: UnifiedSwapEventListener<T>,
198
+ unifiedSwapStorage: UnifiedSwapStorage<T>,
199
+ reviver: (val: any) => ISwap<T>,
200
+ defaultVersion: string,
201
+
202
+ versionedContracts: {
203
+ [contractVersion: string]: {
204
+ swapContract: T["Contract"],
205
+ spvVaultContract: T["SpvVaultContract"],
206
+ btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
207
+ synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
208
+ }
209
+ }
210
+ };
211
+
212
+ type MultiChainData<T extends MultiChain> = {
213
+ [chainIdentifier in keyof T]: ChainSpecificData<T[chainIdentifier]>
214
+ };
215
+
216
+ type CtorMultiChainData<T extends MultiChain> = {
217
+ [chainIdentifier in keyof T]: ChainData<T[chainIdentifier]>
218
+ };
219
+
220
+ type SwapperCtorTokens<T extends MultiChain = MultiChain> = {
221
+ ticker: string,
222
+ name: string,
223
+ chains: {[chainId in ChainIds<T>]?: {
224
+ address: string,
225
+ decimals: number,
226
+ displayDecimals?: number
227
+ }}
228
+ }[];
229
+
230
+ /**
231
+ * Type extracting chain identifiers from a MultiChain type
232
+ * @category Core
233
+ */
234
+ export type ChainIds<T extends MultiChain> = keyof T & string;
235
+
236
+ /**
237
+ * Type helper to check if a chain supports a specific swap type
238
+ * @category Core
239
+ */
240
+ export type SupportsSwapType<
241
+ C extends ChainType,
242
+ Type extends SwapType
243
+ > = Type extends SwapType.SPV_VAULT_FROM_BTC ?
244
+ NotNever<C["SpvVaultContract"]> :
245
+ Type extends (SwapType.TRUSTED_FROM_BTCLN | SwapType.TRUSTED_FROM_BTC) ?
246
+ true :
247
+ Type extends SwapType.FROM_BTCLN_AUTO ? (C["Contract"]["supportsInitWithoutClaimer"] extends true ? true : false) :
248
+ NotNever<C["Contract"]>;
249
+
250
+ /**
251
+ * Core orchestrator for all atomiq swap operations
252
+ *
253
+ * @category Core
254
+ */
255
+ export class Swapper<T extends MultiChain> extends EventEmitter<{
256
+ lpsRemoved: [Intermediary[]],
257
+ lpsAdded: [Intermediary[]],
258
+ swapState: [ISwap],
259
+ swapLimitsChanged: []
260
+ }> {
261
+ private readonly logger = getLogger(this.constructor.name+": ");
262
+ private readonly swapStateListener: (swap: ISwap) => void;
263
+
264
+ private defaultTrustedIntermediary?: Intermediary;
265
+ private readonly bitcoinNetwork: BitcoinNetwork;
266
+ private readonly options: SwapperOptions;
267
+ /**
268
+ * Data propagation layer used for broadcasting messages to watchtowers
269
+ */
270
+ private readonly messenger: Messenger;
271
+
272
+ /**
273
+ * A dictionary of smart chains used by the SDK
274
+ * @internal
275
+ */
276
+ readonly _chains: MultiChainData<T>;
277
+ /**
278
+ * Bitcoin RPC for fetching bitcoin chain data
279
+ * @internal
280
+ */
281
+ readonly _bitcoinRpc: BitcoinRpcWithAddressIndex<any>;
282
+ /**
283
+ * Bitcoin network specification
284
+ * @internal
285
+ */
286
+ readonly _btcNetwork: BTC_NETWORK;
287
+ /**
288
+ * Token data indexed by chain identifier and token addresses
289
+ * @internal
290
+ */
291
+ readonly _tokens: {
292
+ [chainId: string]: {
293
+ [tokenAddress: string]: SCToken
294
+ }
295
+ };
296
+ /**
297
+ * Token data indexed by chain identifier and token tickers
298
+ * @internal
299
+ */
300
+ readonly _tokensByTicker: {
301
+ [chainId: string]: {
302
+ [tokenTicker: string]: SCToken
303
+ }
304
+ };
305
+
306
+ /**
307
+ * Pricing API used by the SDK
308
+ */
309
+ readonly prices: ISwapPrice<T>;
310
+ /**
311
+ * Intermediary discovery instance
312
+ */
313
+ readonly intermediaryDiscovery: IntermediaryDiscovery;
314
+ /**
315
+ * Miscellaneous utility functions
316
+ */
317
+ readonly Utils: SwapperUtils<T>;
318
+
319
+ /**
320
+ * @internal
321
+ */
322
+ constructor(
323
+ bitcoinRpc: BitcoinRpcWithAddressIndex<any>,
324
+ lightningApi: LightningNetworkApi,
325
+ bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
326
+ chainsData: CtorMultiChainData<T>,
327
+ pricing: ISwapPrice<T>,
328
+ tokens: SCToken[],
329
+ messenger: Messenger,
330
+ options?: SwapperOptions
331
+ ) {
332
+ super();
333
+ const storagePrefix = options?.storagePrefix ?? "atomiq-";
334
+
335
+ options ??= {};
336
+ options.saveUninitializedSwaps ??= true;
337
+ options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
338
+ const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
339
+
340
+ this.options = options;
341
+
342
+ this.bitcoinNetwork = options.bitcoinNetwork;
343
+ this._btcNetwork = options.bitcoinNetwork===BitcoinNetwork.MAINNET ? NETWORK :
344
+ (options.bitcoinNetwork===BitcoinNetwork.TESTNET || options.bitcoinNetwork===BitcoinNetwork.TESTNET4) ? TEST_NETWORK : {
345
+ bech32: 'bcrt',
346
+ pubKeyHash: 111,
347
+ scriptHash: 196,
348
+ wif: 239
349
+ };
350
+ this.Utils = new SwapperUtils(this);
351
+
352
+ this.prices = pricing;
353
+ this._bitcoinRpc = bitcoinRpc;
354
+ this.messenger = messenger;
355
+
356
+ this._tokens = {};
357
+ this._tokensByTicker = {};
358
+ for(let tokenData of tokens) {
359
+ const chainId = tokenData.chainId;
360
+ this._tokens[chainId] ??= {};
361
+ this._tokensByTicker[chainId] ??= {};
362
+ this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
363
+ }
364
+
365
+ this.swapStateListener = (swap: ISwap) => {
366
+ this.emit("swapState", swap);
367
+ };
368
+
369
+ this._chains = objectMap<CtorMultiChainData<T>, MultiChainData<T>>(chainsData, <InputKey extends keyof CtorMultiChainData<T>>(chainData: CtorMultiChainData<T>[InputKey], key: string) => {
370
+ let {
371
+ chainInterface, chainEvents, chainId,
372
+ btcRelay,
373
+ swapContract, swapDataConstructor,
374
+ spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor,
375
+ defaultVersion, versions
376
+ } = chainData;
377
+
378
+ defaultVersion ??= "v1";
379
+
380
+ if(versions==null) {
381
+ versions = {
382
+ [defaultVersion]: {
383
+ btcRelay,
384
+ swapContract,
385
+ swapDataConstructor,
386
+ spvVaultContract,
387
+ spvVaultDataConstructor,
388
+ spvVaultWithdrawalDataConstructor
389
+ }
390
+ }
391
+ }
392
+
393
+ const versionedContracts = objectMap(versions, (value, key) => {
394
+ return {
395
+ swapContract: value.swapContract,
396
+ spvVaultContract: value.spvVaultContract,
397
+ btcRelay: value.btcRelay,
398
+ synchronizer: bitcoinSynchronizer(value.btcRelay)
399
+ };
400
+ });
401
+
402
+ const storageHandler = swapStorage(storagePrefix + chainId);
403
+ const unifiedSwapStorage = new UnifiedSwapStorage<T[InputKey]>(storageHandler, this.options.noSwapCache);
404
+ const unifiedChainEvents = new UnifiedSwapEventListener<T[InputKey]>(unifiedSwapStorage, chainEvents);
405
+
406
+ const wrappers: any = {};
407
+
408
+ wrappers[SwapType.TO_BTCLN] = new ToBTCLNWrapper<T[InputKey]>(
409
+ key,
410
+ unifiedSwapStorage,
411
+ unifiedChainEvents,
412
+ chainInterface,
413
+ pricing,
414
+ this._tokens[chainId],
415
+ versions,
416
+ {
417
+ getRequestTimeout: this.options.getRequestTimeout,
418
+ postRequestTimeout: this.options.postRequestTimeout,
419
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
420
+ }
421
+ );
422
+ wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
423
+ key,
424
+ unifiedSwapStorage,
425
+ unifiedChainEvents,
426
+ chainInterface,
427
+ pricing,
428
+ this._tokens[chainId],
429
+ versions,
430
+ this._bitcoinRpc,
431
+ {
432
+ getRequestTimeout: this.options.getRequestTimeout,
433
+ postRequestTimeout: this.options.postRequestTimeout,
434
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
435
+ bitcoinNetwork: this._btcNetwork
436
+ }
437
+ );
438
+ wrappers[SwapType.FROM_BTCLN] = new FromBTCLNWrapper<T[InputKey]>(
439
+ key,
440
+ unifiedSwapStorage,
441
+ unifiedChainEvents,
442
+ chainInterface,
443
+ pricing,
444
+ this._tokens[chainId],
445
+ versions,
446
+ lightningApi,
447
+ {
448
+ getRequestTimeout: this.options.getRequestTimeout,
449
+ postRequestTimeout: this.options.postRequestTimeout,
450
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
451
+ unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
452
+ }
453
+ );
454
+ wrappers[SwapType.FROM_BTC] = new FromBTCWrapper<T[InputKey]>(
455
+ key,
456
+ unifiedSwapStorage,
457
+ unifiedChainEvents,
458
+ chainInterface,
459
+ pricing,
460
+ this._tokens[chainId],
461
+ versions,
462
+ versionedContracts,
463
+ this._bitcoinRpc,
464
+ {
465
+ getRequestTimeout: this.options.getRequestTimeout,
466
+ postRequestTimeout: this.options.postRequestTimeout,
467
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
468
+ bitcoinNetwork: this._btcNetwork
469
+ }
470
+ );
471
+ wrappers[SwapType.TRUSTED_FROM_BTCLN] = new LnForGasWrapper<T[InputKey]>(
472
+ key,
473
+ unifiedSwapStorage,
474
+ unifiedChainEvents,
475
+ chainInterface,
476
+ pricing,
477
+ this._tokens[chainId],
478
+ {
479
+ getRequestTimeout: this.options.getRequestTimeout,
480
+ postRequestTimeout: this.options.postRequestTimeout,
481
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
482
+ }
483
+ );
484
+ wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
485
+ key,
486
+ unifiedSwapStorage,
487
+ unifiedChainEvents,
488
+ chainInterface,
489
+ pricing,
490
+ this._tokens[chainId],
491
+ bitcoinRpc,
492
+ {
493
+ getRequestTimeout: this.options.getRequestTimeout,
494
+ postRequestTimeout: this.options.postRequestTimeout,
495
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
496
+ bitcoinNetwork: this._btcNetwork
497
+ }
498
+ );
499
+
500
+ // This is gated on the default version of the contracts
501
+ if(spvVaultContract!=null) {
502
+ wrappers[SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper<T[InputKey]>(
503
+ key,
504
+ unifiedSwapStorage,
505
+ unifiedChainEvents,
506
+ chainInterface,
507
+ pricing,
508
+ this._tokens[chainId],
509
+ versions,
510
+ versionedContracts,
511
+ bitcoinRpc,
512
+ {
513
+ getRequestTimeout: this.options.getRequestTimeout,
514
+ postRequestTimeout: this.options.postRequestTimeout,
515
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
516
+ bitcoinNetwork: this._btcNetwork
517
+ }
518
+ );
519
+ }
520
+
521
+ // This is gated on the default version of the contracts
522
+ if(swapContract.supportsInitWithoutClaimer) {
523
+ wrappers[SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper<T[InputKey]>(
524
+ key,
525
+ unifiedSwapStorage,
526
+ unifiedChainEvents,
527
+ chainInterface,
528
+ pricing,
529
+ this._tokens[chainId],
530
+ versions,
531
+ lightningApi,
532
+ this.messenger,
533
+ {
534
+ getRequestTimeout: this.options.getRequestTimeout,
535
+ postRequestTimeout: this.options.postRequestTimeout,
536
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
537
+ unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
538
+ }
539
+ );
540
+ }
541
+
542
+ Object.keys(wrappers).forEach(key => wrappers[key].events.on("swapState", this.swapStateListener));
543
+
544
+ const reviver = (val: any) => {
545
+ const wrapper: ISwapWrapper<any, any> = wrappers[val.type];
546
+ if(wrapper==null) return null;
547
+ return new wrapper._swapDeserializer(wrapper, val);
548
+ };
549
+
550
+ return {
551
+ chainEvents,
552
+ chainInterface,
553
+
554
+ wrappers,
555
+
556
+ unifiedChainEvents,
557
+ unifiedSwapStorage,
558
+
559
+ defaultVersion,
560
+
561
+ reviver,
562
+
563
+ versionedContracts
564
+ }
565
+ });
566
+
567
+ const contracts = objectMap(chainsData, (data) => data.versions ?? {[data.defaultVersion ?? "v1"]: {swapContract: data.swapContract, spvVaultContract: data.spvVaultContract}});
568
+ if(options.intermediaryUrl!=null) {
569
+ this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
570
+ } else {
571
+ this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, undefined, options.getRequestTimeout);
572
+ }
573
+
574
+ this.intermediaryDiscovery.on("removed", (intermediaries: Intermediary[]) => {
575
+ this.emit("lpsRemoved", intermediaries);
576
+ });
577
+
578
+ this.intermediaryDiscovery.on("added", (intermediaries: Intermediary[]) => {
579
+ this.emit("lpsAdded", intermediaries);
580
+ });
581
+ }
582
+
583
+ private async _init(): Promise<void> {
584
+ this.logger.debug("init(): Initializing swapper...");
585
+
586
+ const abortController = new AbortController();
587
+
588
+ const promises: Promise<void>[] = [];
589
+ let automaticClockDriftCorrectionPromise: Promise<void> | undefined = undefined;
590
+ if(this.options.automaticClockDriftCorrection) {
591
+ promises.push(automaticClockDriftCorrectionPromise = tryWithRetries(correctClock, undefined, undefined, abortController.signal).catch((err) => {
592
+ abortController.abort(err);
593
+ }));
594
+ }
595
+
596
+ this.logger.debug("init(): Initializing intermediary discovery");
597
+ if(!this.options.dontFetchLPs) promises.push(this.intermediaryDiscovery.init(abortController.signal).catch(err => {
598
+ if(abortController.signal.aborted) return;
599
+ this.logger.error("init(): Failed to fetch intermediaries/LPs: ", err);
600
+ }));
601
+
602
+ if(this.options.defaultTrustedIntermediaryUrl!=null) {
603
+ promises.push(
604
+ this.intermediaryDiscovery.getIntermediary(this.options.defaultTrustedIntermediaryUrl, abortController.signal)
605
+ .then(val => {
606
+ if(val==null) throw new Error("Cannot get trusted LP");
607
+ this.defaultTrustedIntermediary = val;
608
+ })
609
+ .catch(err => {
610
+ if(abortController.signal.aborted) return;
611
+ this.logger.error("init(): Failed to contact trusted LP url: ", err);
612
+ })
613
+ );
614
+ }
615
+
616
+ if(automaticClockDriftCorrectionPromise!=null) {
617
+ //We should await the promises here before checking the swaps
618
+ await automaticClockDriftCorrectionPromise;
619
+ }
620
+
621
+ const chainPromises = [];
622
+ for(let chainIdentifier in this._chains) {
623
+ chainPromises.push((async() => {
624
+ const {
625
+ chainInterface,
626
+ versionedContracts,
627
+ unifiedChainEvents,
628
+ unifiedSwapStorage,
629
+ wrappers,
630
+ reviver
631
+ } = this._chains[chainIdentifier];
632
+
633
+ const _chainInterface: any = chainInterface;
634
+ if(_chainInterface.verifyNetwork!=null) {
635
+ await _chainInterface.verifyNetwork(this.bitcoinNetwork);
636
+ }
637
+
638
+ for(let contractVersion in versionedContracts) {
639
+ await versionedContracts[contractVersion].swapContract.start();
640
+ this.logger.debug("init(): Intialized swap contract: "+chainIdentifier+` version: ${contractVersion}`);
641
+ }
642
+
643
+ await unifiedSwapStorage.init();
644
+ if(unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage) {
645
+ //Try to migrate the data here
646
+ const storagePrefix = chainIdentifier==="SOLANA" ?
647
+ "SOLv4-"+this.bitcoinNetwork+"-Swaps-" :
648
+ "atomiqsdk-"+this.bitcoinNetwork+chainIdentifier+"-Swaps-";
649
+ await unifiedSwapStorage.storage.tryMigrate(
650
+ [
651
+ [storagePrefix+"FromBTC", SwapType.FROM_BTC],
652
+ [storagePrefix+"FromBTCLN", SwapType.FROM_BTCLN],
653
+ [storagePrefix+"ToBTC", SwapType.TO_BTC],
654
+ [storagePrefix+"ToBTCLN", SwapType.TO_BTCLN]
655
+ ],
656
+ (obj: any) => {
657
+ const swap = reviver(obj);
658
+ if(swap._randomNonce==null) {
659
+ const oldIdentifierHash = swap.getId();
660
+ swap._randomNonce = randomBytes(16).toString("hex");
661
+ const newIdentifierHash = swap.getId();
662
+ this.logger.info("init(): Found older swap version without randomNonce, replacing, old hash: "+oldIdentifierHash+
663
+ " new hash: "+newIdentifierHash);
664
+ }
665
+ return swap;
666
+ }
667
+ )
668
+ }
669
+
670
+ if(!this.options.noEvents) await unifiedChainEvents.start();
671
+ this.logger.debug("init(): Intialized events: "+chainIdentifier);
672
+
673
+ for(let key in wrappers) {
674
+ // this.logger.debug("init(): Initializing "+SwapType[key]+": "+chainIdentifier);
675
+ await wrappers[key as unknown as SwapType].init(this.options.noTimers, this.options.dontCheckPastSwaps);
676
+ }
677
+ })());
678
+ }
679
+ await Promise.all(chainPromises);
680
+
681
+ await Promise.all(promises);
682
+
683
+ this.logger.debug("init(): Initializing messenger");
684
+ await this.messenger.init();
685
+ }
686
+
687
+ private initPromise?: Promise<void>;
688
+ private initialized: boolean = false;
689
+
690
+ /**
691
+ * Initializes the swap storage and loads existing swaps, needs to be called before any other action
692
+ */
693
+ async init(): Promise<void> {
694
+ if(this.initialized) return;
695
+ if(this.initPromise!=null) {
696
+ await this.initPromise;
697
+ return;
698
+ }
699
+
700
+ try {
701
+ const promise = this._init();
702
+ this.initPromise = promise;
703
+ await promise;
704
+ delete this.initPromise;
705
+ this.initialized = true;
706
+ } catch (e) {
707
+ delete this.initPromise;
708
+ throw e;
709
+ }
710
+ }
711
+
712
+ /**
713
+ * Stops listening for onchain events and closes this Swapper instance
714
+ */
715
+ async stop() {
716
+ if(this.initPromise) await this.initPromise;
717
+ for(let chainIdentifier in this._chains) {
718
+ const {
719
+ wrappers,
720
+ unifiedChainEvents
721
+ } = this._chains[chainIdentifier];
722
+ for(let key in wrappers) {
723
+ const wrapper = wrappers[key as unknown as SwapType];
724
+ wrapper.events.removeListener("swapState", this.swapStateListener);
725
+ await wrapper.stop();
726
+ }
727
+ await unifiedChainEvents.stop();
728
+ await this.messenger.stop();
729
+ }
730
+ this.initialized = false;
731
+ }
732
+
733
+ /**
734
+ * Creates swap & handles intermediary, quote selection
735
+ *
736
+ * @param chainIdentifier
737
+ * @param create Callback to create the
738
+ * @param amountData Amount data as passed to the function
739
+ * @param swapType Swap type of the execution
740
+ * @param maxWaitTimeMS Maximum waiting time after the first intermediary returns the quote
741
+ * @private
742
+ * @throws {Error} when no intermediary was found
743
+ * @throws {Error} if the chain with the provided identifier cannot be found
744
+ */
745
+ private async createSwap<ChainIdentifier extends ChainIds<T>, S extends ISwap<T[ChainIdentifier]>>(
746
+ chainIdentifier: ChainIdentifier,
747
+ create: (candidates: Intermediary[], abortSignal: AbortSignal, chain: ChainSpecificData<T[ChainIdentifier]>) => Promise<{
748
+ quote: Promise<S>,
749
+ intermediary: Intermediary
750
+ }[]>,
751
+ amountData: { amount?: bigint, token: string, exactIn: boolean },
752
+ swapType: SwapType,
753
+ maxWaitTimeMS: number = 2000
754
+ ): Promise<S> {
755
+ if(!this.initialized) throw new Error("Swapper not initialized, init first with swapper.init()!");
756
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
757
+ let candidates: Intermediary[];
758
+
759
+ const inBtc: boolean = swapType===SwapType.TO_BTCLN || swapType===SwapType.TO_BTC ? !amountData.exactIn : amountData.exactIn;
760
+
761
+ if(!inBtc || amountData.amount==null) {
762
+ //Get candidates not based on the amount
763
+ candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
764
+ } else {
765
+ candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
766
+ }
767
+
768
+ let swapLimitsChanged = false;
769
+
770
+ if(candidates.length===0) {
771
+ this.logger.warn("createSwap(): No valid intermediary found to execute the swap with, reloading intermediary database...");
772
+ await this.intermediaryDiscovery.reloadIntermediaries();
773
+ swapLimitsChanged = true;
774
+
775
+ if(!inBtc || amountData.amount==null) {
776
+ //Get candidates not based on the amount
777
+ candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
778
+ } else {
779
+ candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
780
+
781
+ if(candidates.length===0) {
782
+ const min = this.intermediaryDiscovery.getSwapMinimum(chainIdentifier, swapType, amountData.token);
783
+ const max = this.intermediaryDiscovery.getSwapMaximum(chainIdentifier, swapType, amountData.token);
784
+ if(min!=null && max!=null) {
785
+ if(amountData.amount < BigInt(min)) throw new OutOfBoundsError("Swap amount too low! Try swapping a higher amount.", 200, BigInt(min), BigInt(max));
786
+ if(amountData.amount > BigInt(max)) throw new OutOfBoundsError("Swap amount too high! Try swapping a lower amount.", 200, BigInt(min), BigInt(max));
787
+ }
788
+ }
789
+ }
790
+
791
+ if(candidates.length===0) throw new Error("No intermediary found for the requested pair and amount! You can try swapping different pair or higher/lower amount.");
792
+ }
793
+
794
+ const abortController = new AbortController();
795
+ this.logger.debug("createSwap() Swap candidates: ", candidates.map(lp => lp.url).join());
796
+ const quotePromises: {quote: Promise<S>, intermediary: Intermediary}[] = await create(candidates, abortController.signal, this._chains[chainIdentifier]);
797
+
798
+ const promiseAll = new Promise<{
799
+ quote: S,
800
+ intermediary: Intermediary
801
+ }[]>((resolve, reject) => {
802
+ let min: bigint;
803
+ let max: bigint;
804
+ let error: Error;
805
+ let numResolved = 0;
806
+ let quotes: {
807
+ quote: S,
808
+ intermediary: Intermediary
809
+ }[] = [];
810
+ let timeout: NodeJS.Timeout;
811
+
812
+ quotePromises.forEach(data => {
813
+ data.quote.then(quote => {
814
+ if(numResolved===0) {
815
+ timeout = setTimeout(() => {
816
+ abortController.abort(new Error("Timed out waiting for quote!"));
817
+ resolve(quotes);
818
+ }, maxWaitTimeMS);
819
+ }
820
+ numResolved++;
821
+ quotes.push({
822
+ quote,
823
+ intermediary: data.intermediary
824
+ });
825
+ if(numResolved===quotePromises.length) {
826
+ clearTimeout(timeout);
827
+ resolve(quotes);
828
+ return;
829
+ }
830
+ }).catch(e => {
831
+ numResolved++;
832
+ if(e instanceof IntermediaryError) {
833
+ //Blacklist that node
834
+ this.intermediaryDiscovery.removeIntermediary(data.intermediary);
835
+ swapLimitsChanged = true;
836
+ } else if(e instanceof OutOfBoundsError) {
837
+ if(min==null || max==null) {
838
+ min = e.min;
839
+ max = e.max;
840
+ } else {
841
+ min = bigIntMin(min, e.min);
842
+ max = bigIntMax(max, e.max);
843
+ }
844
+ data.intermediary.swapBounds[swapType] ??= {};
845
+ data.intermediary.swapBounds[swapType]![chainIdentifier] ??= {};
846
+ const tokenBoundsData = (data.intermediary.swapBounds[swapType]![chainIdentifier]![amountData.token] ??= {input: {}, output: {}});
847
+ if(amountData.exactIn) {
848
+ tokenBoundsData.input = {min: e.min, max: e.max};
849
+ } else {
850
+ tokenBoundsData.output = {min: e.min, max: e.max};
851
+ }
852
+ swapLimitsChanged = true;
853
+ }
854
+ this.logger.warn("createSwap(): Intermediary "+data.intermediary.url+" error: ", e);
855
+ error = e;
856
+
857
+ if(numResolved===quotePromises.length) {
858
+ if(timeout!=null) clearTimeout(timeout);
859
+ if(quotes.length>0) {
860
+ resolve(quotes);
861
+ return;
862
+ }
863
+ if(min!=null && max!=null) {
864
+ let msg = "Swap amount too high or too low! Try swapping a different amount.";
865
+ if(amountData.amount!=null) {
866
+ if(min > amountData.amount) msg = "Swap amount too low! Try swapping a higher amount.";
867
+ if(max < amountData.amount) msg = "Swap amount too high! Try swapping a lower amount.";
868
+ }
869
+ reject(new OutOfBoundsError(msg, 400, min, max));
870
+ return;
871
+ }
872
+ reject(error);
873
+ }
874
+ });
875
+ });
876
+ });
877
+
878
+ try {
879
+ const quotes = await promiseAll;
880
+
881
+ //TODO: Intermediary's reputation is not taken into account!
882
+ quotes.sort((a, b) => {
883
+ if(amountData.exactIn) {
884
+ //Compare outputs
885
+ return bigIntCompare(b.quote.getOutput().rawAmount!, a.quote.getOutput().rawAmount!);
886
+ } else {
887
+ //Compare inputs
888
+ return bigIntCompare(a.quote.getInput().rawAmount!, b.quote.getInput().rawAmount!);
889
+ }
890
+ });
891
+
892
+ this.logger.debug("createSwap(): Sorted quotes, best price to worst: ", quotes);
893
+
894
+ if(swapLimitsChanged) this.emit("swapLimitsChanged");
895
+
896
+ const quote = quotes[0].quote;
897
+ await quote._save();
898
+ return quote;
899
+ } catch (e) {
900
+ if(swapLimitsChanged) this.emit("swapLimitsChanged");
901
+ throw e;
902
+ }
903
+ }
904
+
905
+ /**
906
+ * Creates Smart chain -> Bitcoin ({@link SwapType.TO_BTC}) swap
907
+ *
908
+ * @param chainIdentifier Chain identifier string of the source smart chain
909
+ * @param signer Signer's address on the source chain
910
+ * @param tokenAddress Token address to pay with
911
+ * @param address Recipient's bitcoin address
912
+ * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
913
+ * @param exactIn Whether to use exact in instead of exact out
914
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
915
+ * @param options Additional options for the swap
916
+ */
917
+ createToBTCSwap<ChainIdentifier extends ChainIds<T>>(
918
+ chainIdentifier: ChainIdentifier,
919
+ signer: string,
920
+ tokenAddress: string,
921
+ address: string,
922
+ amount: bigint,
923
+ exactIn: boolean = false,
924
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
925
+ options?: ToBTCOptions
926
+ ): Promise<ToBTCSwap<T[ChainIdentifier]>> {
927
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
928
+ if(address.startsWith("bitcoin:")) {
929
+ address = address.substring(8).split("?")[0];
930
+ }
931
+ if(!this.Utils.isValidBitcoinAddress(address)) throw new Error("Invalid bitcoin address");
932
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
933
+ signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
934
+ const amountData = {
935
+ amount,
936
+ token: tokenAddress,
937
+ exactIn
938
+ };
939
+ return this.createSwap(
940
+ chainIdentifier as ChainIdentifier,
941
+ (candidates: Intermediary[], abortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.TO_BTC].create(
942
+ signer,
943
+ address,
944
+ amountData,
945
+ candidates,
946
+ options,
947
+ additionalParams,
948
+ abortSignal
949
+ )),
950
+ amountData,
951
+ SwapType.TO_BTC
952
+ );
953
+ }
954
+
955
+ /**
956
+ * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap
957
+ *
958
+ * @param chainIdentifier Chain identifier string of the source smart chain
959
+ * @param signer Signer's address on the source chain
960
+ * @param tokenAddress Token address to pay with
961
+ * @param paymentRequest BOLT11 lightning network invoice to be paid (needs to have a fixed amount), and the swap
962
+ * amount is taken from this fixed amount, hence only exact output swaps are supported
963
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
964
+ * @param options Additional options for the swap
965
+ */
966
+ async createToBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
967
+ chainIdentifier: ChainIdentifier,
968
+ signer: string,
969
+ tokenAddress: string,
970
+ paymentRequest: string,
971
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
972
+ options?: ToBTCLNOptions
973
+ ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
974
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
975
+ if(paymentRequest.startsWith("lightning:")) paymentRequest = paymentRequest.substring(10);
976
+ if(!this.Utils.isValidLightningInvoice(paymentRequest)) throw new Error("Invalid lightning network invoice");
977
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
978
+ signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
979
+ const parsedPR = bolt11Decode(paymentRequest);
980
+ if(parsedPR.millisatoshis==null) throw new Error("Invalid lightning network invoice, no msat value field!");
981
+ const amountData = {
982
+ amount: (BigInt(parsedPR.millisatoshis) + 999n) / 1000n,
983
+ token: tokenAddress,
984
+ exactIn: false as const
985
+ };
986
+ return this.createSwap(
987
+ chainIdentifier as ChainIdentifier,
988
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].create(
989
+ signer,
990
+ paymentRequest,
991
+ amountData,
992
+ candidates,
993
+ options,
994
+ additionalParams,
995
+ abortSignal
996
+ ),
997
+ amountData,
998
+ SwapType.TO_BTCLN
999
+ );
1000
+ }
1001
+
1002
+ /**
1003
+ * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via LNURL-pay link
1004
+ *
1005
+ * @param chainIdentifier Chain identifier string of the source smart chain
1006
+ * @param signer Signer's address on the source chain
1007
+ * @param tokenAddress Token address to pay with
1008
+ * @param lnurlPay LNURL-pay link to use for the payment
1009
+ * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
1010
+ * @param exactIn Whether to do an exact in swap instead of exact out
1011
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1012
+ * @param options Additional options for the swap
1013
+ */
1014
+ async createToBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
1015
+ chainIdentifier: ChainIdentifier,
1016
+ signer: string,
1017
+ tokenAddress: string,
1018
+ lnurlPay: string | LNURLPay,
1019
+ amount: bigint,
1020
+ exactIn: boolean = false,
1021
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1022
+ options?: ToBTCLNOptions & {comment?: string}
1023
+ ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
1024
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1025
+ if(typeof(lnurlPay)==="string" && !this.Utils.isValidLNURL(lnurlPay)) throw new Error("Invalid LNURL-pay link");
1026
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
1027
+ signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
1028
+ const amountData = {
1029
+ amount,
1030
+ token: tokenAddress,
1031
+ exactIn
1032
+ };
1033
+ return this.createSwap(
1034
+ chainIdentifier as ChainIdentifier,
1035
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaLNURL(
1036
+ signer,
1037
+ typeof(lnurlPay)==="string" ? (lnurlPay.startsWith("lightning:") ? lnurlPay.substring(10): lnurlPay) : lnurlPay.params,
1038
+ amountData,
1039
+ candidates,
1040
+ options,
1041
+ additionalParams,
1042
+ abortSignal
1043
+ ),
1044
+ amountData,
1045
+ SwapType.TO_BTCLN
1046
+ );
1047
+ }
1048
+
1049
+ /**
1050
+ * Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via {@link LightningInvoiceCreateService}
1051
+ *
1052
+ * @param chainIdentifier Chain identifier string of the source smart chain
1053
+ * @param signer Signer's address on the source chain
1054
+ * @param tokenAddress Token address to pay with
1055
+ * @param service Invoice create service object which facilitates the creation of fixed amount LN invoices
1056
+ * @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
1057
+ * @param exactIn Whether to do an exact in swap instead of exact out
1058
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1059
+ * @param options Additional options for the swap
1060
+ */
1061
+ async createToBTCLNSwapViaInvoiceCreateService<ChainIdentifier extends ChainIds<T>>(
1062
+ chainIdentifier: ChainIdentifier,
1063
+ signer: string,
1064
+ tokenAddress: string,
1065
+ service: LightningInvoiceCreateService,
1066
+ amount: bigint,
1067
+ exactIn: boolean = false,
1068
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1069
+ options?: ToBTCLNOptions
1070
+ ): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
1071
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1072
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
1073
+ signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
1074
+ const amountData = {
1075
+ amount,
1076
+ token: tokenAddress,
1077
+ exactIn
1078
+ };
1079
+ return this.createSwap(
1080
+ chainIdentifier as ChainIdentifier,
1081
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaInvoiceCreateService(
1082
+ signer,
1083
+ Promise.resolve(service),
1084
+ amountData,
1085
+ candidates,
1086
+ options,
1087
+ additionalParams,
1088
+ abortSignal
1089
+ ),
1090
+ amountData,
1091
+ SwapType.TO_BTCLN
1092
+ );
1093
+ }
1094
+
1095
+ /**
1096
+ * Creates Bitcoin -> Smart chain ({@link SwapType.SPV_VAULT_FROM_BTC}) swap
1097
+ *
1098
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1099
+ * @param recipient Recipient address on the destination chain
1100
+ * @param tokenAddress Token address to receive
1101
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1102
+ * @param exactOut Whether to use a exact out instead of exact in
1103
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1104
+ * @param options Additional options for the swap
1105
+ */
1106
+ async createFromBTCSwapNew<ChainIdentifier extends ChainIds<T>>(
1107
+ chainIdentifier: ChainIdentifier,
1108
+ recipient: string,
1109
+ tokenAddress: string,
1110
+ amount: bigint | null,
1111
+ exactOut: boolean = false,
1112
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1113
+ options?: SpvFromBTCOptions
1114
+ ): Promise<SpvFromBTCSwap<T[ChainIdentifier]>> {
1115
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1116
+ if(this._chains[chainIdentifier].wrappers[SwapType.SPV_VAULT_FROM_BTC]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new BTC swap protocol (spv vault swaps)!");
1117
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1118
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1119
+ const amountData = {
1120
+ amount: amount ?? undefined,
1121
+ token: tokenAddress,
1122
+ exactIn: !exactOut
1123
+ };
1124
+ return this.createSwap(
1125
+ chainIdentifier as ChainIdentifier,
1126
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.SPV_VAULT_FROM_BTC].create(
1127
+ recipient,
1128
+ amountData,
1129
+ candidates,
1130
+ options,
1131
+ additionalParams,
1132
+ abortSignal
1133
+ )),
1134
+ amountData,
1135
+ SwapType.SPV_VAULT_FROM_BTC
1136
+ );
1137
+ }
1138
+
1139
+ /**
1140
+ * Creates LEGACY Bitcoin -> Smart chain ({@link SwapType.FROM_BTC}) swap
1141
+ *
1142
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1143
+ * @param recipient Recipient address on the destination chain
1144
+ * @param tokenAddress Token address to receive
1145
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1146
+ * @param exactOut Whether to use a exact out instead of exact in
1147
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1148
+ * @param options Additional options for the swap
1149
+ */
1150
+ async createFromBTCSwap<ChainIdentifier extends ChainIds<T>>(
1151
+ chainIdentifier: ChainIdentifier,
1152
+ recipient: string,
1153
+ tokenAddress: string,
1154
+ amount: bigint,
1155
+ exactOut: boolean = false,
1156
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1157
+ options?: FromBTCOptions
1158
+ ): Promise<FromBTCSwap<T[ChainIdentifier]>> {
1159
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1160
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1161
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1162
+ const amountData = {
1163
+ amount,
1164
+ token: tokenAddress,
1165
+ exactIn: !exactOut
1166
+ };
1167
+ return this.createSwap(
1168
+ chainIdentifier as ChainIdentifier,
1169
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTC].create(
1170
+ recipient,
1171
+ amountData,
1172
+ candidates,
1173
+ options,
1174
+ additionalParams,
1175
+ abortSignal
1176
+ )),
1177
+ amountData,
1178
+ SwapType.FROM_BTC
1179
+ );
1180
+ }
1181
+
1182
+ /**
1183
+ * Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap
1184
+ *
1185
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1186
+ * @param recipient Recipient address on the destination chain
1187
+ * @param tokenAddress Token address to receive
1188
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1189
+ * @param exactOut Whether to use a exact out instead of exact in
1190
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1191
+ * @param options Additional options for the swap
1192
+ */
1193
+ async createFromBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
1194
+ chainIdentifier: ChainIdentifier,
1195
+ recipient: string,
1196
+ tokenAddress: string,
1197
+ amount: bigint,
1198
+ exactOut: boolean = false,
1199
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1200
+ options?: FromBTCLNOptions
1201
+ ): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
1202
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1203
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1204
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1205
+ const amountData = {
1206
+ amount,
1207
+ token: tokenAddress,
1208
+ exactIn: !exactOut
1209
+ };
1210
+ return this.createSwap(
1211
+ chainIdentifier as ChainIdentifier,
1212
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN].create(
1213
+ recipient,
1214
+ amountData,
1215
+ candidates,
1216
+ options,
1217
+ additionalParams,
1218
+ abortSignal
1219
+ )),
1220
+ amountData,
1221
+ SwapType.FROM_BTCLN
1222
+ );
1223
+ }
1224
+
1225
+ /**
1226
+ * Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap, withdrawing from
1227
+ * an LNURL-withdraw link
1228
+ *
1229
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1230
+ * @param recipient Recipient address on the destination chain
1231
+ * @param tokenAddress Token address to receive
1232
+ * @param lnurl LNURL-withdraw link to pull the funds from
1233
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1234
+ * @param exactOut Whether to use a exact out instead of exact in
1235
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1236
+ * @param options Additional options for the swap
1237
+ */
1238
+ async createFromBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
1239
+ chainIdentifier: ChainIdentifier,
1240
+ recipient: string,
1241
+ tokenAddress: string,
1242
+ lnurl: string | LNURLWithdraw,
1243
+ amount: bigint,
1244
+ exactOut: boolean = false,
1245
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1246
+ options?: FromBTCLNOptions
1247
+ ): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
1248
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1249
+ if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
1250
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1251
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1252
+ const amountData = {
1253
+ amount,
1254
+ token: tokenAddress,
1255
+ exactIn: !exactOut
1256
+ };
1257
+ return this.createSwap(
1258
+ chainIdentifier as ChainIdentifier,
1259
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN].createViaLNURL(
1260
+ recipient,
1261
+ typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
1262
+ amountData,
1263
+ candidates,
1264
+ options,
1265
+ additionalParams,
1266
+ abortSignal
1267
+ ),
1268
+ amountData,
1269
+ SwapType.FROM_BTCLN
1270
+ );
1271
+ }
1272
+
1273
+ /**
1274
+ * Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap
1275
+ *
1276
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1277
+ * @param recipient Recipient address on the destination chain
1278
+ * @param tokenAddress Token address to receive
1279
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1280
+ * @param exactOut Whether to use a exact out instead of exact in
1281
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1282
+ * @param options Additional options for the swap
1283
+ */
1284
+ async createFromBTCLNSwapNew<ChainIdentifier extends ChainIds<T>>(
1285
+ chainIdentifier: ChainIdentifier,
1286
+ recipient: string,
1287
+ tokenAddress: string,
1288
+ amount: bigint,
1289
+ exactOut: boolean = false,
1290
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1291
+ options?: FromBTCLNAutoOptions
1292
+ ): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
1293
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1294
+ if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
1295
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1296
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1297
+ const amountData = {
1298
+ amount,
1299
+ token: tokenAddress,
1300
+ exactIn: !exactOut
1301
+ };
1302
+ return this.createSwap(
1303
+ chainIdentifier as ChainIdentifier,
1304
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN_AUTO].create(
1305
+ recipient,
1306
+ amountData,
1307
+ candidates,
1308
+ options,
1309
+ additionalParams,
1310
+ abortSignal
1311
+ )),
1312
+ amountData,
1313
+ SwapType.FROM_BTCLN_AUTO
1314
+ );
1315
+ }
1316
+
1317
+ /**
1318
+ * Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap, withdrawing from
1319
+ * an LNURL-withdraw link
1320
+ *
1321
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1322
+ * @param recipient Recipient address on the destination chain
1323
+ * @param tokenAddress Token address to receive
1324
+ * @param lnurl LNURL-withdraw link to pull the funds from
1325
+ * @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
1326
+ * @param exactOut Whether to use a exact out instead of exact in
1327
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
1328
+ * @param options Additional options for the swap
1329
+ */
1330
+ async createFromBTCLNSwapNewViaLNURL<ChainIdentifier extends ChainIds<T>>(
1331
+ chainIdentifier: ChainIdentifier,
1332
+ recipient: string,
1333
+ tokenAddress: string,
1334
+ lnurl: string | LNURLWithdraw,
1335
+ amount: bigint,
1336
+ exactOut: boolean = false,
1337
+ additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
1338
+ options?: FromBTCLNAutoOptions
1339
+ ): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
1340
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1341
+ if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
1342
+ if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
1343
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1344
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1345
+ const amountData = {
1346
+ amount,
1347
+ token: tokenAddress,
1348
+ exactIn: !exactOut
1349
+ };
1350
+ return this.createSwap(
1351
+ chainIdentifier as ChainIdentifier,
1352
+ (candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN_AUTO].createViaLNURL(
1353
+ recipient,
1354
+ typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
1355
+ amountData,
1356
+ candidates,
1357
+ options,
1358
+ additionalParams,
1359
+ abortSignal
1360
+ ),
1361
+ amountData,
1362
+ SwapType.FROM_BTCLN_AUTO
1363
+ );
1364
+ }
1365
+
1366
+ /**
1367
+ * Creates a trusted Bitcoin Lightning -> Smart chain ({@link SwapType.TRUSTED_FROM_BTCLN}) gas swap
1368
+ *
1369
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1370
+ * @param recipient Recipient address on the destination chain
1371
+ * @param amount Amount of native token to receive, in base units
1372
+ * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1373
+ * @throws {Error} If no trusted intermediary specified
1374
+ */
1375
+ async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
1376
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1377
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1378
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1379
+ const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1380
+ if(useUrl==null) throw new Error("No trusted intermediary specified!");
1381
+ const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
1382
+ await swap._save();
1383
+ return swap;
1384
+ }
1385
+
1386
+ /**
1387
+ * Creates a trusted Bitcoin -> Smart chain ({@link SwapType.TRUSTED_FROM_BTC}) gas swap
1388
+ *
1389
+ * @param chainIdentifier Chain identifier string of the destination smart chain
1390
+ * @param recipient Recipient address on the destination chain
1391
+ * @param amount Amount of native token to receive, in base units
1392
+ * @param refundAddress Bitcoin refund address, in case the swap fails the funds are refunded here
1393
+ * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1394
+ * @throws {Error} If no trusted intermediary specified
1395
+ */
1396
+ async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1397
+ chainIdentifier: C, recipient: string,
1398
+ amount: bigint, refundAddress?: string,
1399
+ trustedIntermediaryOrUrl?: Intermediary | string
1400
+ ): Promise<OnchainForGasSwap<T[C]>> {
1401
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1402
+ if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1403
+ recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1404
+ const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1405
+ if(useUrl==null) throw new Error("No trusted intermediary specified!");
1406
+ const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
1407
+ await swap._save();
1408
+ return swap;
1409
+ }
1410
+
1411
+ /**
1412
+ * @internal
1413
+ */
1414
+ create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<true>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean, lnurlWithdraw?: string | LNURLWithdraw): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
1415
+ /**
1416
+ * @internal
1417
+ */
1418
+ create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<false>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
1419
+ /**
1420
+ * @internal
1421
+ */
1422
+ create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<false>, amount: bigint, exactIn: boolean, address: string): Promise<ToBTCSwap<T[C]>>;
1423
+ /**
1424
+ * @internal
1425
+ */
1426
+ create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: bigint, exactIn: boolean, lnurlPay: string | LNURLPay): Promise<ToBTCLNSwap<T[C]>>;
1427
+ /**
1428
+ * @internal
1429
+ */
1430
+ create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: undefined, exactIn: false, lightningInvoice: string): Promise<ToBTCLNSwap<T[C]>>;
1431
+ /**
1432
+ * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
1433
+ * or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
1434
+ * invoice's pre-set amount is used instead.
1435
+ * @deprecated Use {@link swap} instead
1436
+ *
1437
+ * @param signer Smartchain (Solana, Starknet, etc.) address of the user
1438
+ * @param srcToken Source token of the swap, user pays this token
1439
+ * @param dstToken Destination token of the swap, user receives this token
1440
+ * @param amount Amount of the swap
1441
+ * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1442
+ * @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
1443
+ * LNURL-withdrawal to withdraw money from
1444
+ */
1445
+ create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>>;
1446
+ /**
1447
+ * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
1448
+ * or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
1449
+ * invoice's pre-set amount is used instead.
1450
+ * @deprecated Use {@link swap} instead
1451
+ *
1452
+ * @param signer Smartchain (Solana, Starknet, etc.) address of the user
1453
+ * @param srcToken Source token of the swap, user pays this token
1454
+ * @param dstToken Destination token of the swap, user receives this token
1455
+ * @param amount Amount of the swap
1456
+ * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1457
+ * @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
1458
+ * LNURL-withdrawal to withdraw money from
1459
+ */
1460
+ create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>> {
1461
+ if(srcToken.chain==="BTC") {
1462
+ return this.swap(srcToken, dstToken, amount, exactIn, addressLnurlLightningInvoice as any, signer);
1463
+ } else {
1464
+ return this.swap(srcToken, dstToken, amount, exactIn, signer, addressLnurlLightningInvoice as any);
1465
+ }
1466
+ }
1467
+
1468
+ /**
1469
+ * @internal
1470
+ */
1471
+ swap<C extends ChainIds<T>>(srcToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoOptions : FromBTCLNOptions)): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
1472
+ /**
1473
+ * @internal
1474
+ */
1475
+ swap<C extends ChainIds<T>>(srcToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCOptions : FromBTCOptions)): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
1476
+ /**
1477
+ * @internal
1478
+ */
1479
+ swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstAddress: string, options?: ToBTCOptions): Promise<ToBTCSwap<T[C]>>;
1480
+ /**
1481
+ * @internal
1482
+ */
1483
+ swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstLnurlPayOrInvoiceCreateService: string | LNURLPay | LightningInvoiceCreateService, options?: ToBTCLNOptions & {comment?: string}): Promise<ToBTCLNSwap<T[C]>>;
1484
+ /**
1485
+ * @internal
1486
+ */
1487
+ swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: undefined, exactIn: false | SwapAmountType.EXACT_OUT, src: string, dstLightningInvoice: string, options?: ToBTCLNOptions): Promise<ToBTCLNSwap<T[C]>>;
1488
+ /**
1489
+ * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
1490
+ * or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
1491
+ * invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
1492
+ *
1493
+ * @param srcToken Source token of the swap, user pays this token
1494
+ * @param dstToken Destination token of the swap, user receives this token
1495
+ * @param amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
1496
+ * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1497
+ * @param src Source wallet/lnurl-withdraw of the swap
1498
+ * @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
1499
+ * @param options Options for the swap
1500
+ */
1501
+ swap<C extends ChainIds<T>>(srcToken: Token<C> | string, dstToken: Token<C> | string, amount: bigint | string | undefined, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dst: string | LNURLPay | LightningInvoiceCreateService, options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions): Promise<ISwap<T[C]>>;
1502
+ /**
1503
+ * Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
1504
+ * or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
1505
+ * invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
1506
+ *
1507
+ * @param _srcToken Source token of the swap, user pays this token
1508
+ * @param _dstToken Destination token of the swap, user receives this token
1509
+ * @param _amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
1510
+ * @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
1511
+ * @param src Source wallet/lnurl-withdraw of the swap
1512
+ * @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
1513
+ * @param options Options for the swap
1514
+ */
1515
+ swap<C extends ChainIds<T>>(
1516
+ _srcToken: Token<C> | string,
1517
+ _dstToken: Token<C> | string,
1518
+ _amount: bigint | string | undefined,
1519
+ exactIn: boolean | SwapAmountType,
1520
+ src: undefined | string | LNURLWithdraw,
1521
+ dst: string | LNURLPay | LightningInvoiceCreateService,
1522
+ options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions
1523
+ ): Promise<ISwap<T[C]>> {
1524
+ const srcToken = typeof(_srcToken)==="string" ? this.getToken(_srcToken) as Token<C> : _srcToken;
1525
+ const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
1526
+ const amount = _amount==null ? null : (typeof(_amount)==="bigint" ? _amount : fromDecimal(_amount, exactIn ? srcToken.decimals : dstToken.decimals));
1527
+ if(isBtcToken(srcToken)) {
1528
+ if(isSCToken<C>(dstToken)) {
1529
+ if(typeof(dst)!=="string") throw new Error("Destination for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
1530
+ if(amount==null) throw new Error("Amount cannot be null for from btc swaps!");
1531
+ if(srcToken.lightning) {
1532
+ //FROM_BTCLN
1533
+ if(src!=null) {
1534
+ if(typeof(src)!=="string" && !isLNURLWithdraw(src)) throw new Error("LNURL must be a string or LNURLWithdraw object!");
1535
+ return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
1536
+ this.createFromBTCLNSwapNewViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any) :
1537
+ this.createFromBTCLNSwapViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any);
1538
+ } else {
1539
+ return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
1540
+ this.createFromBTCLNSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any):
1541
+ this.createFromBTCLNSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1542
+ }
1543
+ } else {
1544
+ //FROM_BTC
1545
+ if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
1546
+ return this.createFromBTCSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1547
+ } else {
1548
+ return this.createFromBTCSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
1549
+ }
1550
+ }
1551
+ }
1552
+ } else if(isSCToken<C>(srcToken)) {
1553
+ if(isBtcToken(dstToken)) {
1554
+ if(typeof(src)!=="string") throw new Error("Source address for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
1555
+ if(dstToken.lightning) {
1556
+ //TO_BTCLN
1557
+ if(typeof(dst)!=="string" && !isLNURLPay(dst)) throw new Error("Destination LNURL link/lightning invoice must be a string or LNURLPay object!");
1558
+ if(isLNURLPay(dst) || this.Utils.isValidLNURL(dst)) {
1559
+ if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via LNURL-pay!");
1560
+ return this.createToBTCLNSwapViaLNURL(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1561
+ } else if(isLightningInvoiceCreateService(dst)) {
1562
+ if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via InvoiceCreateService!");
1563
+ return this.createToBTCLNSwapViaInvoiceCreateService(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1564
+ } else if(this.Utils.isLightningInvoice(dst)) {
1565
+ if(!this.Utils.isValidLightningInvoice(dst))
1566
+ throw new Error("Invalid lightning invoice specified, lightning invoice MUST contain pre-set amount!");
1567
+ if(exactIn)
1568
+ throw new Error("Only exact out swaps are possible with lightning invoices, use LNURL links for exact in lightning swaps!");
1569
+ return this.createToBTCLNSwap(srcToken.chainId, src, srcToken.address, dst, undefined, options as any);
1570
+ } else {
1571
+ throw new Error("Supplied parameter is not LNURL link nor lightning invoice (bolt11)!");
1572
+ }
1573
+ } else {
1574
+ //TO_BTC
1575
+ if(typeof(dst)!=="string") throw new Error("Destination bitcoin address must be a string!");
1576
+ if(amount==null) throw new Error("Amount cannot be null for to btc swaps!");
1577
+ return this.createToBTCSwap(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
1578
+ }
1579
+ }
1580
+ }
1581
+ throw new Error("Unsupported swap type");
1582
+ }
1583
+
1584
+ /**
1585
+ * A helper function to sweep all the funds from a given wallet in a single swap, after getting the quote you can
1586
+ * execute the swap by passing the returned `feeRate` and `utxos` to the {@link SpvFromBTCSwap.execute},
1587
+ * {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.sendBitcoinTransaction} functions along
1588
+ * with `spendFully=true`.
1589
+ *
1590
+ * @example
1591
+ * Create the swap first using this function
1592
+ * ```ts
1593
+ * const {swap, utxos, btcFeeRate} = await swapper.sweepBitcoinWallet(wallet, Tokens.CITREA.CBTC, dstAddress);
1594
+ * ```
1595
+ * Then execute it using one of these execution paths - ensure that you supply the returned `utxos`, `btcFeeRate`
1596
+ * params and also set `spendFully` to `true`!
1597
+ *
1598
+ * a) Execute and pass the returned utxos and btcFeeRate:
1599
+ * ```ts
1600
+ * await swap.execute(wallet, undefined, {feeRate: btcFeeRate, utxos: utxos, spendFully: true});
1601
+ * ```
1602
+ *
1603
+ * b) Get funded PSBT to sign externally:
1604
+ * ```ts
1605
+ * const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt(wallet, btcFeeRate, undefined, utxos, true);
1606
+ * // Sign the psbt at the specified signInputs indices
1607
+ * const signedPsbt = ...;
1608
+ * // Then submit back to the SDK
1609
+ * await swap.submitPsbt(signedPsbt);
1610
+ * ```
1611
+ *
1612
+ * c) Only sign and send the signed PSBT with the provided wallet:
1613
+ * ```ts
1614
+ * await swap.sendBitcoinTransaction(wallet, btcFeeRate, utxos, true);
1615
+ * ```
1616
+ */
1617
+ async sweepBitcoinWallet<C extends ChainIds<T>>(
1618
+ srcWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
1619
+ _dstToken: SCToken<C> | string,
1620
+ dstAddress: string,
1621
+ options?: SpvFromBTCOptions
1622
+ ): Promise<{
1623
+ swap: SpvFromBTCSwap<T[C]>,
1624
+ utxos: BitcoinWalletUtxo[],
1625
+ btcFeeRate: number
1626
+ }> {
1627
+ const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
1628
+ if(!isSCToken<C>(dstToken)) throw new Error("Destination token must be a smart chain token!");
1629
+
1630
+ const wallet = toBitcoinWallet(srcWallet, this._bitcoinRpc, this.bitcoinNetwork);
1631
+ if(wallet.getUtxoPool==null) throw new Error("Wallet needs to support the `getUtxoPool()` function!");
1632
+
1633
+ const walletUtxosPromise = wallet.getUtxoPool();
1634
+ const bitcoinFeeRatePromise = options?.bitcoinFeeRate ?? wallet.getFeeRate();
1635
+
1636
+ const swap = await this.createFromBTCSwapNew(
1637
+ dstToken.chainId, dstAddress, dstToken.address, null, false, undefined, {
1638
+ ...options,
1639
+ sourceWalletUtxos: walletUtxosPromise,
1640
+ bitcoinFeeRate: bitcoinFeeRatePromise
1641
+ }
1642
+ );
1643
+
1644
+ return {
1645
+ swap,
1646
+ utxos: await walletUtxosPromise,
1647
+ btcFeeRate: Math.max(swap.minimumBtcFeeRate, await bitcoinFeeRatePromise)
1648
+ };
1649
+ }
1650
+
1651
+ /**
1652
+ * Returns all swaps
1653
+ */
1654
+ getAllSwaps(): Promise<ISwap[]>;
1655
+ /**
1656
+ * Returns all swaps for the specific chain, and optionally also for a specific signer's address
1657
+ */
1658
+ getAllSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
1659
+ async getAllSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
1660
+ const queryParams: QueryParams[] = [];
1661
+ if(signer!=null) queryParams.push({key: "initiator", value: signer});
1662
+
1663
+ if(chainId==null) {
1664
+ const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1665
+ const {unifiedSwapStorage, reviver} = this._chains[chainId];
1666
+ return unifiedSwapStorage.query([queryParams], reviver);
1667
+ }));
1668
+ return res.flat();
1669
+ } else {
1670
+ const {unifiedSwapStorage, reviver} = this._chains[chainId];
1671
+ return await unifiedSwapStorage.query([queryParams], reviver);
1672
+ }
1673
+ }
1674
+
1675
+ /**
1676
+ * Returns all swaps where an action is required (either claim or refund)
1677
+ */
1678
+ getActionableSwaps(): Promise<ISwap[]>;
1679
+ /**
1680
+ * Returns swaps where an action is required (either claim or refund) for the specific chain, and optionally also for a specific signer's address
1681
+ */
1682
+ getActionableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
1683
+ async getActionableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
1684
+ if(chainId==null) {
1685
+ const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1686
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1687
+ const queryParams: Array<QueryParams[]> = [];
1688
+ for(let key in wrappers) {
1689
+ const wrapper = wrappers[key as unknown as SwapType];
1690
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1691
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1692
+ swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1693
+ queryParams.push(swapTypeQueryParams);
1694
+ }
1695
+ return unifiedSwapStorage.query(queryParams, reviver);
1696
+ }));
1697
+ return res.flat().filter(swap => swap.requiresAction());
1698
+ } else {
1699
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1700
+ const queryParams: Array<QueryParams[]> = [];
1701
+ for(let key in wrappers) {
1702
+ const wrapper = wrappers[key as unknown as SwapType];
1703
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1704
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1705
+ swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1706
+ queryParams.push(swapTypeQueryParams);
1707
+ }
1708
+ return (await unifiedSwapStorage.query(queryParams, reviver)).filter(swap => swap.requiresAction());
1709
+ }
1710
+ }
1711
+
1712
+ /**
1713
+ * Returns all swaps that are refundable
1714
+ */
1715
+ getRefundableSwaps(): Promise<IToBTCSwap[]>;
1716
+ /**
1717
+ * Returns swaps which are refundable for the specific chain, and optionally also for a specific signer's address
1718
+ */
1719
+ getRefundableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IToBTCSwap<T[C]>[]>;
1720
+ async getRefundableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IToBTCSwap[]> {
1721
+ if(chainId==null) {
1722
+ const res: IToBTCSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1723
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1724
+ const queryParams: Array<QueryParams[]> = [];
1725
+ for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
1726
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1727
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1728
+ swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
1729
+ queryParams.push(swapTypeQueryParams);
1730
+ }
1731
+ return unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
1732
+ }));
1733
+ return res.flat().filter(swap => swap.isRefundable());
1734
+ } else {
1735
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1736
+ const queryParams: Array<QueryParams[]> = [];
1737
+ for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
1738
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1739
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1740
+ swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
1741
+ queryParams.push(swapTypeQueryParams);
1742
+ }
1743
+ const result = await unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
1744
+ return result.filter(swap => swap.isRefundable());
1745
+ }
1746
+ }
1747
+
1748
+ /**
1749
+ * Returns all swaps that are manually claimable
1750
+ */
1751
+ getClaimableSwaps(): Promise<IClaimableSwap[]>;
1752
+ /**
1753
+ * Returns all swaps that are manually claimable for the specific chain, and optionally also for a specific signer's address
1754
+ */
1755
+ getClaimableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IClaimableSwap<T[C]>[]>;
1756
+ async getClaimableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IClaimableSwap[]> {
1757
+ if(chainId==null) {
1758
+ const res: IClaimableSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1759
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1760
+ const queryParams: Array<QueryParams[]> = [];
1761
+ for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
1762
+ if(wrapper==null) continue;
1763
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1764
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1765
+ swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
1766
+ queryParams.push(swapTypeQueryParams);
1767
+ }
1768
+ return unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
1769
+ }));
1770
+ return res.flat().filter(swap => swap.isClaimable());
1771
+ } else {
1772
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1773
+ const queryParams: Array<QueryParams[]> = [];
1774
+ for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
1775
+ if(wrapper==null) continue;
1776
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1777
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1778
+ swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
1779
+ queryParams.push(swapTypeQueryParams);
1780
+ }
1781
+ const result = await unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
1782
+ return result.filter(swap => swap.isClaimable());
1783
+ }
1784
+ }
1785
+
1786
+ /**
1787
+ * Returns swap with a specific id (identifier)
1788
+ */
1789
+ getSwapById(id: string): Promise<ISwap>;
1790
+ /**
1791
+ * Returns swap with a specific id (identifier) on a specific chain and optionally with a signer
1792
+ */
1793
+ getSwapById<C extends ChainIds<T>>(id: string, chainId: C, signer?: string): Promise<ISwap<T[C]>>;
1794
+ async getSwapById<C extends ChainIds<T>>(id: string, chainId?: C, signer?: string): Promise<ISwap> {
1795
+ //Check in pending swaps first
1796
+ if(chainId!=null) {
1797
+ for(let key in this._chains[chainId].wrappers) {
1798
+ const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
1799
+ const result = wrapper._getPendingSwap(id);
1800
+ if(result!=null) {
1801
+ if (signer != null) {
1802
+ if (result._getInitiator() === signer) return result;
1803
+ } else {
1804
+ return result;
1805
+ }
1806
+ }
1807
+ }
1808
+ } else {
1809
+ for(let chainId in this._chains) {
1810
+ for(let key in this._chains[chainId].wrappers) {
1811
+ const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
1812
+ const result = wrapper._getPendingSwap(id);
1813
+ if(result!=null) {
1814
+ if(signer!=null) {
1815
+ if(result._getInitiator()===signer) return result;
1816
+ } else {
1817
+ return result;
1818
+ }
1819
+ }
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ const queryParams: QueryParams[] = [];
1825
+ if(signer!=null) queryParams.push({key: "initiator", value: signer});
1826
+ queryParams.push({key: "id", value: id});
1827
+ if(chainId==null) {
1828
+ const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
1829
+ const {unifiedSwapStorage, reviver} = this._chains[chainId];
1830
+ return unifiedSwapStorage.query([queryParams], reviver);
1831
+ }));
1832
+ return res.flat()[0];
1833
+ } else {
1834
+ const {unifiedSwapStorage, reviver} = this._chains[chainId];
1835
+ return (await unifiedSwapStorage.query([queryParams], reviver))[0];
1836
+ }
1837
+ }
1838
+
1839
+ /**
1840
+ * Returns the swap with a proper return type, or `undefined` if not found or has wrong type
1841
+ *
1842
+ * @param id An ID of the swap ({@link ISwap.getId})
1843
+ * @param chainId Chain identifier of the smart chain where the swap was initiated
1844
+ * @param swapType Type of the swap
1845
+ * @param signer An optional required smart chain signer address to fetch the swap for
1846
+ */
1847
+ async getTypedSwapById<C extends ChainIds<T>, S extends SwapType>(id: string, chainId: C, swapType: S, signer?: string): Promise<SwapTypeMapping<T[C]>[S] | undefined> {
1848
+ let _swapType: SwapType = swapType;
1849
+ if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC))
1850
+ _swapType = SwapType.SPV_VAULT_FROM_BTC;
1851
+ if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO))
1852
+ _swapType = SwapType.FROM_BTCLN_AUTO;
1853
+
1854
+ const wrapper = this._chains[chainId].wrappers[_swapType];
1855
+ if(wrapper==null) return;
1856
+
1857
+ const result = wrapper._getPendingSwap(id);
1858
+ if(result!=null) {
1859
+ if (signer != null) {
1860
+ if (result._getInitiator() === signer) return result as any;
1861
+ } else {
1862
+ return result as any;
1863
+ }
1864
+ }
1865
+
1866
+ const queryParams: QueryParams[] = [];
1867
+ if(signer!=null) queryParams.push({key: "initiator", value: signer});
1868
+ queryParams.push({key: "id", value: id});
1869
+ const {unifiedSwapStorage, reviver} = this._chains[chainId];
1870
+ const swap = (await unifiedSwapStorage.query([queryParams], reviver))[0];
1871
+ if(isSwapType(swap, swapType)) return swap;
1872
+ }
1873
+
1874
+ private async syncSwapsForChain<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<void> {
1875
+ const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1876
+ const queryParams: Array<QueryParams[]> = [];
1877
+ for(let key in wrappers) {
1878
+ const wrapper = wrappers[key as unknown as SwapType];
1879
+ const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
1880
+ if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
1881
+ swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
1882
+ queryParams.push(swapTypeQueryParams);
1883
+ }
1884
+ this.logger.debug("_syncSwaps(): Querying swaps swaps for chain "+chainId+"!");
1885
+ const swaps = await unifiedSwapStorage.query(queryParams, reviver);
1886
+ this.logger.debug("_syncSwaps(): Syncing "+swaps.length+" swaps!");
1887
+
1888
+ const changedSwaps: ISwap<T[C]>[] = [];
1889
+ const removeSwaps: ISwap<T[C]>[] = [];
1890
+
1891
+ const assortedSwaps: {[swapType in SwapType]?: ISwap<T[string]>[]} = {};
1892
+ swaps.forEach(swap => {
1893
+ (assortedSwaps[swap.getType()] ??= []).push(swap);
1894
+ });
1895
+
1896
+ for(let key in assortedSwaps) {
1897
+ const swapType = key as unknown as SwapType;
1898
+ const wrapperSwaps = assortedSwaps[swapType];
1899
+ const wrapper: ISwapWrapper<T[C], any> = wrappers[swapType];
1900
+ const result = await wrapper.checkPastSwaps(wrapperSwaps, true);
1901
+ changedSwaps.push(...result.changedSwaps);
1902
+ removeSwaps.push(...result.removeSwaps);
1903
+ }
1904
+
1905
+ this.logger.debug("_syncSwaps(): Done syncing "+swaps.length+" swaps, saving "+changedSwaps.length+" changed swaps, removing "+removeSwaps.length+" swaps!");
1906
+ await unifiedSwapStorage.saveAll(changedSwaps);
1907
+ await unifiedSwapStorage.removeAll(removeSwaps);
1908
+
1909
+ changedSwaps.forEach(swap => swap._emitEvent());
1910
+ removeSwaps.forEach(swap => swap._emitEvent());
1911
+ }
1912
+
1913
+ /**
1914
+ * Deletes the swaps from the persistent storage backend. Note that some data (like lightning network
1915
+ * amounts and bolt11 invoices) are purely off-chain and can never be recovered later just from
1916
+ * on-chain data!
1917
+ *
1918
+ * @param chainId Optional, to only delete swaps for this smart chain
1919
+ * @param signer Optional, to only delete swaps for this smart chain signer (`chainId` param must be
1920
+ * set to delete only signer's swaps)
1921
+ */
1922
+ async wipeStorage<C extends ChainIds<T>>(chainId?: C, signer?: string) {
1923
+ if(chainId==null) {
1924
+ const swaps = await this.getAllSwaps();
1925
+ const chainSwaps: {
1926
+ [chainId: string]: ISwap<any>[]
1927
+ } = {};
1928
+ swaps.forEach(swap => (chainSwaps[swap.chainIdentifier] ??= []).push(swap));
1929
+ for(let chainId in chainSwaps) {
1930
+ const currentChainSwaps = chainSwaps[chainId];
1931
+ if(this._chains[chainId]==null) {
1932
+ this.logger.warn(`wipeStorage(): Attempted to remove ${currentChainSwaps.length} swaps on ${chainId}, but smart chain not known!`);
1933
+ continue;
1934
+ }
1935
+ await this._chains[chainId].unifiedSwapStorage.removeAll(currentChainSwaps);
1936
+ this.logger.debug(`wipeStorage(): Successfully removed ${currentChainSwaps.length} swaps on ${chainId}!`);
1937
+ }
1938
+ } else {
1939
+ if(this._chains[chainId]==null) throw new Error(`wipeStorage(): Smart chain with identifier ${chainId} not found!`);
1940
+ const swaps = await this.getAllSwaps(chainId, signer);
1941
+ await this._chains[chainId].unifiedSwapStorage.removeAll(swaps);
1942
+ this.logger.debug(`wipeStorage(): Successfully removed ${swaps.length} swaps on ${chainId}!`);
1943
+ }
1944
+ }
1945
+
1946
+ /**
1947
+ * Synchronizes swaps from on-chain, this is ran automatically when SDK is initialized, hence
1948
+ * should only be ran manually when `dontCheckPastSwaps=true` is passed in the swapper options,
1949
+ * also deletes expired quotes
1950
+ *
1951
+ * @param chainId Optional chain identifier to only run swap sync for a single smart chain
1952
+ * @param signer Optional signer to only run swap sync for swaps initiated by this signer
1953
+ */
1954
+ async _syncSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<void> {
1955
+ if(chainId==null) {
1956
+ await Promise.all(Object.keys(this._chains).map((chainId) => {
1957
+ return this.syncSwapsForChain(chainId, signer);
1958
+ }));
1959
+ } else {
1960
+ await this.syncSwapsForChain(chainId, signer);
1961
+ }
1962
+ }
1963
+
1964
+ /**
1965
+ * Recovers swaps from on-chain historical data.
1966
+ *
1967
+ * Please note that the recovered swaps might not be complete (i.e. missing amounts or addresses), as some
1968
+ * of the swap data is purely off-chain and can never be recovered purely from on-chain data. This
1969
+ * functions tries to recover as much swap data as possible.
1970
+ *
1971
+ * @param chainId Smart chain identifier string to recover the swaps from
1972
+ * @param signer Signer address to recover the swaps for
1973
+ * @param startBlockheight Optional starting blockheight for swap data recovery, will only check swaps
1974
+ * initiated after this blockheight
1975
+ */
1976
+ async recoverSwaps<C extends ChainIds<T>>(chainId: C, signer: string, startBlockheight?: number): Promise<ISwap<T[C]>[]> {
1977
+ //TODO: Recover swaps from all the known contract versions
1978
+ const {versionedContracts, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1979
+
1980
+ const recoveredSwaps: ISwap<T[C]>[] = [];
1981
+ let someVersionSupportsRecovery = false;
1982
+ const recoveredEscrowStates: {
1983
+ [p: string]: {
1984
+ init?: {
1985
+ data: SwapData
1986
+ getInitTxId: () => Promise<string>
1987
+ getTxBlock: () => Promise<{
1988
+ blockTime: number
1989
+ blockHeight: number
1990
+ }>
1991
+ },
1992
+ state: SwapCommitState
1993
+ contractVersion: string
1994
+ }
1995
+ } = {};
1996
+ const recoveredSpvStates: {
1997
+ [contractVersion: string]: {
1998
+ [escrowHash: string]: SpvWithdrawalClaimedState | SpvWithdrawalFrontedState
1999
+ }
2000
+ } = {};
2001
+
2002
+ for(let contractVersion in versionedContracts) {
2003
+ const {swapContract, spvVaultContract} = versionedContracts[contractVersion];
2004
+
2005
+ if(
2006
+ swapContract.getHistoricalSwaps==null ||
2007
+ (spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
2008
+ ) {
2009
+ this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
2010
+ continue;
2011
+ }
2012
+
2013
+ someVersionSupportsRecovery = true;
2014
+
2015
+ const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
2016
+ const spvVaultData = wrappers[SwapType.SPV_VAULT_FROM_BTC]==null
2017
+ ? undefined
2018
+ : await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
2019
+
2020
+ for(let key in swaps) recoveredEscrowStates[key] = {...swaps[key], contractVersion};
2021
+ if(spvVaultData!=null) for(let key in spvVaultData.withdrawals) (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
2022
+ }
2023
+ if(!someVersionSupportsRecovery) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
2024
+
2025
+ const escrowHashes = Object.keys(recoveredEscrowStates);
2026
+ for(let contractVersion in recoveredSpvStates) Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
2027
+ this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
2028
+ this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
2029
+ const knownSwapsArray = await unifiedSwapStorage.query([[{key: "escrowHash", value: escrowHashes}]], reviver);
2030
+ const knownSwaps: {[escrowHash: string]: ISwap<T[C]>} = {};
2031
+ knownSwapsArray.forEach(val => {
2032
+ const escrowHash = val._getEscrowHash();
2033
+ if(escrowHash!=null) knownSwaps[escrowHash] = val;
2034
+ });
2035
+ this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
2036
+
2037
+ for(let escrowHash in recoveredEscrowStates) {
2038
+ const {init, state, contractVersion} = recoveredEscrowStates[escrowHash];
2039
+ const knownSwap = knownSwaps[escrowHash];
2040
+ const { swapContract } = versionedContracts[contractVersion];
2041
+
2042
+ if(knownSwap==null) {
2043
+ if(init==null) {
2044
+ this.logger.warn(`recoverSwaps(escrow): Fetched ${escrowHash} swap state, but swap not found locally!`);
2045
+ continue;
2046
+ }
2047
+ } else if(knownSwap instanceof IEscrowSwap) {
2048
+ this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
2049
+ if((knownSwap._contractVersion ?? "v1")!==contractVersion) {
2050
+ this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
2051
+ continue;
2052
+ }
2053
+ if(await knownSwap._forciblySetOnchainState(state)) {
2054
+ await knownSwap._save();
2055
+ }
2056
+ continue;
2057
+ } else {
2058
+ this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap already known and in local storage!`);
2059
+ continue;
2060
+ }
2061
+
2062
+ const data = init.data;
2063
+
2064
+ //Classify swap
2065
+ let swap: ISwap<T[C]> | undefined | null;
2066
+ let typeIdentified: boolean = false;
2067
+ if(data.getType()===ChainSwapType.HTLC) {
2068
+ if(data.isOfferer(signer)) {
2069
+ //To BTCLN
2070
+ typeIdentified = true;
2071
+ const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
2072
+ swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2073
+ } else if(data.isClaimer(signer)) {
2074
+ //From BTCLN
2075
+ typeIdentified = true;
2076
+ const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
2077
+ if(swapContract.supportsInitWithoutClaimer && wrappers[SwapType.FROM_BTCLN_AUTO]!=null) {
2078
+ swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2079
+ } else {
2080
+ swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2081
+ }
2082
+ }
2083
+ } else if(data.getType()===ChainSwapType.CHAIN_NONCED) {
2084
+ //To BTC
2085
+ typeIdentified = true;
2086
+ const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
2087
+ swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2088
+ } else if(data.getType()===ChainSwapType.CHAIN) {
2089
+ //From BTC
2090
+ typeIdentified = true;
2091
+ const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
2092
+ swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
2093
+ }
2094
+
2095
+ if(swap!=null) {
2096
+ recoveredSwaps.push(swap);
2097
+ } else {
2098
+ if(typeIdentified) this.logger.debug(`recoverSwaps(escrow): Swap data type correctly identified but swap returned is null for swap ${escrowHash}`);
2099
+ }
2100
+ }
2101
+
2102
+ for(let contractVersion in recoveredSpvStates) {
2103
+ const { spvVaultContract } = versionedContracts[contractVersion];
2104
+ const spvVaultData = recoveredSpvStates[contractVersion];
2105
+
2106
+ const vaultsData = await spvVaultContract.getMultipleVaultData(
2107
+ Object.keys(spvVaultData)
2108
+ .map(btcTxId => ({
2109
+ owner: spvVaultData[btcTxId].owner,
2110
+ vaultId: spvVaultData[btcTxId].vaultId
2111
+ }))
2112
+ );
2113
+
2114
+ for(let btcTxId in spvVaultData) {
2115
+ const state = spvVaultData[btcTxId];
2116
+ const knownSwap = knownSwaps[btcTxId];
2117
+
2118
+ if(knownSwap!=null) {
2119
+ if(knownSwap instanceof SpvFromBTCSwap) {
2120
+ this.logger.debug(`recoverSwaps(spv_vault): Forcibly updating ${btcTxId} swap: swap already known and in local storage!`);
2121
+ //TODO: Forcibly set on-chain state to the swap
2122
+ // if(await knownSwap._forciblySetOnchainState(state)) {
2123
+ // await knownSwap._save();
2124
+ // }
2125
+ continue;
2126
+ } else {
2127
+ this.logger.debug(`recoverSwaps(spv_vault): Skipping ${btcTxId} swap: swap already known and in local storage!`);
2128
+ continue;
2129
+ }
2130
+ }
2131
+
2132
+ const lp = this.intermediaryDiscovery.intermediaries.find(
2133
+ val => val.supportsChain(chainId) && state.owner.toLowerCase()===val.getAddress(chainId).toLowerCase()
2134
+ );
2135
+ const swap = await wrappers[SwapType.SPV_VAULT_FROM_BTC].recoverFromState(
2136
+ state,
2137
+ contractVersion,
2138
+ vaultsData[state.owner]?.[state.vaultId.toString(10)],
2139
+ lp
2140
+ );
2141
+ if(swap!=null) {
2142
+ recoveredSwaps.push(swap);
2143
+ } else {
2144
+ this.logger.debug(`recoverSwaps(spv_vault): Swap data type correctly identified but swap returned is null for swap ${btcTxId}`);
2145
+ }
2146
+ }
2147
+ }
2148
+
2149
+ this.logger.debug(`recoverSwaps(): Successfully recovered ${recoveredSwaps.length} swaps!`);
2150
+
2151
+ return recoveredSwaps;
2152
+ }
2153
+
2154
+ getToken(ticker: "BTC" | "BITCOIN-BTC"): BtcToken<false>;
2155
+ getToken(ticker: "BTCLN" | "BTC-LN" | "LIGHTNING-BTC"): BtcToken<true>;
2156
+ getToken<ChainIdentifier extends ChainIds<T>>(ticker: `${ChainIdentifier}-${string}`): SCToken<ChainIdentifier>;
2157
+ getToken(tickerOrAddress: string): Token<ChainIds<T>>;
2158
+ /**
2159
+ * Returns the {@link Token} object for a given token
2160
+ *
2161
+ * @param tickerOrAddress Token to return the object for, can use multiple formats:
2162
+ * - a) token ticker, such as `"BTC"`, `"SOL"`, etc.
2163
+ * - b) token ticker prefixed with smart chain identifier, such as `"SOLANA-SOL"`, `"SOLANA-USDC"`, etc.
2164
+ * - c) token address
2165
+ */
2166
+ getToken(tickerOrAddress: string): Token<ChainIds<T>> {
2167
+ //Btc tokens - BTC, BTCLN, BTC-LN
2168
+ if(tickerOrAddress==="BTC" || tickerOrAddress==="BITCOIN-BTC") return BitcoinTokens.BTC;
2169
+ if(tickerOrAddress==="BTCLN" || tickerOrAddress==="BTC-LN" || tickerOrAddress==="LIGHTNING-BTC") return BitcoinTokens.BTCLN;
2170
+
2171
+ //Check if the ticker is in format <chainId>-<ticker>, i.e. SOLANA-USDC, STARKNET-WBTC
2172
+ if(tickerOrAddress.includes("-")) {
2173
+ const [chainId, ticker] = tickerOrAddress.split("-");
2174
+ const token = this._tokensByTicker[chainId]?.[ticker];
2175
+ if(token==null) throw new UserError(`Not found ticker: ${ticker} for chainId: ${chainId}`);
2176
+ return token;
2177
+ }
2178
+
2179
+ const possibleTokens: SCToken[] = [];
2180
+ for(let chainId in this._chains) {
2181
+ const chain = this._chains[chainId];
2182
+ if(chain.chainInterface.isValidToken(tickerOrAddress)) {
2183
+ //Try to find in known token addresses
2184
+ const token = this._tokens[chainId]?.[tickerOrAddress];
2185
+ if(token!=null) return token;
2186
+ } else {
2187
+ //Check in known tickers
2188
+ const token = this._tokensByTicker[chainId]?.[tickerOrAddress];
2189
+ if(token!=null) possibleTokens.push(token);
2190
+ }
2191
+ }
2192
+
2193
+ if(possibleTokens.length===0) throw new UserError(`Specified token address or ticker ${tickerOrAddress} not found!`);
2194
+ //In case we've found the token in multiple chains
2195
+ if(possibleTokens.length>1) throw new UserError(`A ticker ${tickerOrAddress} has been found in multiple chains, narrow it down by using <chainId>-${tickerOrAddress} notation`)
2196
+ return possibleTokens[0];
2197
+ }
2198
+
2199
+ /**
2200
+ * Creates a child swapper instance with a given smart chain
2201
+ *
2202
+ * @param chainIdentifier Smart chain identifier for the created child swapper instance
2203
+ */
2204
+ withChain<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier): SwapperWithChain<T, ChainIdentifier> {
2205
+ if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
2206
+ return new SwapperWithChain<T, ChainIdentifier>(this, chainIdentifier as ChainIdentifier);
2207
+ }
2208
+
2209
+ /**
2210
+ * Returns an array of all the supported smart chains
2211
+ */
2212
+ getSmartChains(): ChainIds<T>[] {
2213
+ return Object.keys(this._chains);
2214
+ }
2215
+
2216
+ /**
2217
+ * Returns whether the SDK supports a given swap type on a given chain based on currently known LPs
2218
+ *
2219
+ * @param chainId Smart chain identifier string
2220
+ * @param swapType Swap protocol type
2221
+ */
2222
+ supportsSwapType<
2223
+ ChainIdentifier extends ChainIds<T>,
2224
+ Type extends SwapType
2225
+ >(chainId: ChainIdentifier, swapType: Type): SupportsSwapType<T[ChainIdentifier], Type> {
2226
+ return (this._chains[chainId]?.wrappers[swapType] != null) as any;
2227
+ }
2228
+
2229
+ /**
2230
+ * @internal
2231
+ */
2232
+ getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<true>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN);
2233
+ /**
2234
+ * @internal
2235
+ */
2236
+ getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<false>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC);
2237
+ /**
2238
+ * @internal
2239
+ */
2240
+ getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<false>): SwapType.TO_BTC;
2241
+ /**
2242
+ * @internal
2243
+ */
2244
+ getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<true>): SwapType.TO_BTCLN;
2245
+ /**
2246
+ * Returns type of the swap based on input and output tokens specified
2247
+ *
2248
+ * @param srcToken Source token
2249
+ * @param dstToken Destination token
2250
+ */
2251
+ getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN;
2252
+ /**
2253
+ * Returns type of the swap based on input and output tokens specified
2254
+ *
2255
+ * @param srcToken Source token
2256
+ * @param dstToken Destination token
2257
+ */
2258
+ getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN {
2259
+ if(isSCToken(srcToken)) {
2260
+ if(!isBtcToken(dstToken)) throw new Error("Swap not supported");
2261
+ if(dstToken.lightning) {
2262
+ return SwapType.TO_BTCLN;
2263
+ } else {
2264
+ return SwapType.TO_BTC;
2265
+ }
2266
+ } else if(isBtcToken(srcToken)) {
2267
+ if(!isSCToken(dstToken)) throw new Error("Swap not supported");
2268
+ if(srcToken.lightning) {
2269
+ if(this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO)) {
2270
+ return SwapType.FROM_BTCLN_AUTO;
2271
+ } else {
2272
+ return SwapType.FROM_BTCLN;
2273
+ }
2274
+ } else {
2275
+ if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
2276
+ return SwapType.SPV_VAULT_FROM_BTC;
2277
+ } else {
2278
+ return SwapType.FROM_BTC;
2279
+ }
2280
+ }
2281
+ }
2282
+ throw new Error("Swap not supported");
2283
+ }
2284
+
2285
+ /**
2286
+ * Helper information about various swap protocol and their features:
2287
+ * - `requiresInputWallet`: Whether a swap requires a connected wallet on the input chain able to sign
2288
+ * arbitrary transaction
2289
+ * - `requiresOutputWallet`: Whether a swap requires a connected wallet on the output chain able to sign
2290
+ * arbitrary transactions
2291
+ * - `supportsGasDrop`: Whether a swap supports the "gas drop" feature, allowing to user to receive a small
2292
+ * amount of native token as part of the swap when swapping to smart chains
2293
+ *
2294
+ * Uses a `Record` type here, use the {@link SwapProtocolInfo} import for a literal readonly type, with
2295
+ * pre-filled exact values in the type.
2296
+ */
2297
+ readonly SwapTypeInfo = SwapProtocolInfo as Record<SwapType, {
2298
+ requiresInputWallet: boolean,
2299
+ requiresOutputWallet: boolean,
2300
+ supportsGasDrop: boolean
2301
+ }>;
2302
+
2303
+ /**
2304
+ * Returns minimum/maximum limits for inputs and outputs for a swap between given tokens
2305
+ *
2306
+ * @param srcToken Source token
2307
+ * @param dstToken Destination token
2308
+ */
2309
+ getSwapLimits<C extends ChainIds<T>, A extends Token<C>, B extends Token<C>>(srcToken: A, dstToken: B): {
2310
+ input: {min: TokenAmount<A>, max?: TokenAmount<A>},
2311
+ output: {min: TokenAmount<B>, max?: TokenAmount<B>}
2312
+ } {
2313
+ const swapType = this.getSwapType(srcToken, dstToken);
2314
+ const scToken = isSCToken(srcToken) ? srcToken : isSCToken(dstToken) ? dstToken : null;
2315
+ if(scToken==null) throw new Error("At least one token needs to be a smart chain token!");
2316
+ const result: {input: {min?: bigint, max?: bigint}, output: {min?: bigint, max?: bigint}} = {
2317
+ input: {},
2318
+ output: {}
2319
+ };
2320
+ for(let lp of this.intermediaryDiscovery.intermediaries) {
2321
+ const lpMinMax = lp.getSwapLimits(swapType, scToken.chainId, scToken.address);
2322
+ if(lpMinMax==null) continue;
2323
+ result.input.min = result.input.min==null ? lpMinMax.input.min : bigIntMin(result.input.min, lpMinMax.input.min);
2324
+ result.input.max = result.input.max==null ? lpMinMax.input.max : bigIntMax(result.input.max, lpMinMax.input.max);
2325
+ result.output.min = result.output.min==null ? lpMinMax.output.min : bigIntMin(result.output.min, lpMinMax.output.min);
2326
+ result.output.max = result.output.max==null ? lpMinMax.output.max : bigIntMax(result.output.max, lpMinMax.output.max);
2327
+ }
2328
+ return {
2329
+ input: {
2330
+ min: toTokenAmount(result.input.min ?? 1n, srcToken, this.prices),
2331
+ max: result.input.max==null ? undefined : toTokenAmount(result.input.max, srcToken, this.prices),
2332
+ },
2333
+ output: {
2334
+ min: toTokenAmount(result.output.min ?? 1n, dstToken, this.prices),
2335
+ max: result.output.max==null ? undefined : toTokenAmount(result.output.max, dstToken, this.prices),
2336
+ }
2337
+ }
2338
+ }
2339
+
2340
+ /**
2341
+ * Returns an array of supported tokens either on the input or on the output of a swap
2342
+ *
2343
+ * @param input Whether to return input tokens or output tokens
2344
+ */
2345
+ getSupportedTokens(input: SwapSide | boolean): Token[] {
2346
+ const tokens: {[chainId: string]: Set<string>} = {};
2347
+ let lightning = false;
2348
+ let btc = false;
2349
+ this.intermediaryDiscovery.intermediaries.forEach(lp => {
2350
+ for(let swapType of [SwapType.TO_BTC, SwapType.TO_BTCLN, SwapType.FROM_BTC, SwapType.FROM_BTCLN, SwapType.SPV_VAULT_FROM_BTC, SwapType.FROM_BTCLN_AUTO]) {
2351
+ if(lp.services[swapType]?.chainTokens==null) continue;
2352
+ for(let chainId of this.getSmartChains()) {
2353
+ if(this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC) ? swapType===SwapType.FROM_BTC : swapType===SwapType.SPV_VAULT_FROM_BTC) continue;
2354
+ if(this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO) ? swapType===SwapType.FROM_BTCLN : swapType===SwapType.FROM_BTCLN_AUTO) continue;
2355
+ const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
2356
+ if(chainTokens==null) continue;
2357
+ for (let tokenAddress of chainTokens) {
2358
+ if(input) {
2359
+ if(swapType===SwapType.TO_BTC || swapType===SwapType.TO_BTCLN) {
2360
+ tokens[chainId] ??= new Set();
2361
+ tokens[chainId].add(tokenAddress);
2362
+ }
2363
+ if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTCLN_AUTO) {
2364
+ lightning = true;
2365
+ }
2366
+ if(swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC) {
2367
+ btc = true;
2368
+ }
2369
+ } else {
2370
+ if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC || swapType===SwapType.FROM_BTCLN_AUTO) {
2371
+ tokens[chainId] ??= new Set();
2372
+ tokens[chainId].add(tokenAddress);
2373
+ }
2374
+ if(swapType===SwapType.TO_BTCLN) {
2375
+ lightning = true;
2376
+ }
2377
+ if(swapType===SwapType.TO_BTC) {
2378
+ btc = true;
2379
+ }
2380
+ }
2381
+ }
2382
+ }
2383
+ }
2384
+ });
2385
+ const output: Token[] = [];
2386
+ if(lightning) output.push(BitcoinTokens.BTCLN);
2387
+ if(btc) output.push(BitcoinTokens.BTC);
2388
+ for(let chainId in tokens) {
2389
+ tokens[chainId].forEach(tokenAddress => {
2390
+ const token = this._tokens?.[chainId]?.[tokenAddress];
2391
+ if(token!=null) output.push(token);
2392
+ })
2393
+ }
2394
+ return output;
2395
+ }
2396
+
2397
+ /**
2398
+ * Returns a set of supported tokens by all the intermediaries offering a specific swap service
2399
+ *
2400
+ * @param _swapType Swap service type to check supported tokens for
2401
+ */
2402
+ private getSupportedTokensForSwapType(_swapType: SwapType): SCToken[] {
2403
+ const tokens: {[chainId: string]: Set<string>} = {};
2404
+ this.intermediaryDiscovery.intermediaries.forEach(lp => {
2405
+ for(let chainId of this.getSmartChains()) {
2406
+ let swapType = _swapType;
2407
+ if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC)) swapType = SwapType.SPV_VAULT_FROM_BTC;
2408
+ if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO)) swapType = SwapType.FROM_BTCLN_AUTO;
2409
+ if(lp.services[swapType]?.chainTokens==null) break;
2410
+ const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
2411
+ if(chainTokens==null) continue;
2412
+ for(let tokenAddress of chainTokens) {
2413
+ tokens[chainId] ??= new Set();
2414
+ tokens[chainId].add(tokenAddress);
2415
+ }
2416
+ }
2417
+ });
2418
+ const output: SCToken[] = [];
2419
+ for(let chainId in tokens) {
2420
+ tokens[chainId].forEach(tokenAddress => {
2421
+ const token = this._tokens?.[chainId]?.[tokenAddress];
2422
+ if(token!=null) output.push(token);
2423
+ })
2424
+ }
2425
+ return output;
2426
+ }
2427
+
2428
+ /**
2429
+ * Returns the set of supported token addresses by all the intermediaries we know of offering a specific swapType service
2430
+ *
2431
+ * @param chainIdentifier Chain identifier string
2432
+ * @param swapType Specific swap type for which to obtain supported tokens
2433
+ */
2434
+ private getSupportedTokenAddresses<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier, swapType: SwapType): Set<string> {
2435
+ const set = new Set<string>();
2436
+ this.intermediaryDiscovery.intermediaries.forEach(lp => {
2437
+ const chainTokens = lp.services[swapType]?.chainTokens?.[chainIdentifier];
2438
+ if(chainTokens==null) return;
2439
+ chainTokens.forEach(token => set.add(token));
2440
+ });
2441
+ return set;
2442
+ }
2443
+
2444
+ /**
2445
+ * Returns tokens that you can swap to (if input=true) from a given token,
2446
+ * or tokens that you can swap from (if input=false) to a given token
2447
+ */
2448
+ getSwapCounterTokens(token: Token, input: SwapSide | boolean): Token[] {
2449
+ if(isSCToken(token)) {
2450
+ const result: Token[] = [];
2451
+ if(input) {
2452
+ //TO_BTC or TO_BTCLN
2453
+ if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTCLN).has(token.address)) {
2454
+ result.push(BitcoinTokens.BTCLN);
2455
+ }
2456
+ if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTC).has(token.address)) {
2457
+ result.push(BitcoinTokens.BTC);
2458
+ }
2459
+ } else {
2460
+ //FROM_BTC or FROM_BTCLN
2461
+ const fromLightningSwapType = this.supportsSwapType(token.chainId, SwapType.FROM_BTCLN_AUTO) ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN;
2462
+ if(this.getSupportedTokenAddresses(token.chainId, fromLightningSwapType).has(token.address)) {
2463
+ result.push(BitcoinTokens.BTCLN);
2464
+ }
2465
+ const fromOnchainSwapType = this.supportsSwapType(token.chainId, SwapType.SPV_VAULT_FROM_BTC) ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC;
2466
+ if(this.getSupportedTokenAddresses(token.chainId, fromOnchainSwapType).has(token.address)) {
2467
+ result.push(BitcoinTokens.BTC);
2468
+ }
2469
+ }
2470
+ return result;
2471
+ } else {
2472
+ if(input) {
2473
+ if(token.lightning) {
2474
+ return this.getSupportedTokensForSwapType(SwapType.FROM_BTCLN);
2475
+ } else {
2476
+ return this.getSupportedTokensForSwapType(SwapType.FROM_BTC);
2477
+ }
2478
+ } else {
2479
+ if(token.lightning) {
2480
+ return this.getSupportedTokensForSwapType(SwapType.TO_BTCLN);
2481
+ } else {
2482
+ return this.getSupportedTokensForSwapType(SwapType.TO_BTC);
2483
+ }
2484
+ }
2485
+ }
2486
+ }
2487
+
2488
+ }