@atomiqlabs/sdk 8.9.1 → 8.9.3

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