@atomiqlabs/sdk 8.9.0 → 8.9.2

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