@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,787 +1,787 @@
1
- import {decode as bolt11Decode, PaymentRequestObject, TagsObject} from "@atomiqlabs/bolt11";
2
- import {ToBTCLNSwap, ToBTCLNSwapInit} from "./ToBTCLNSwap";
3
- import {IToBTCDefinition, IToBTCWrapper} from "../IToBTCWrapper";
4
- import {UserError} from "../../../../errors/UserError";
5
- import {ChainSwapType, ChainType, SwapCommitState, SwapCommitStateType} from "@atomiqlabs/base";
6
- import {Intermediary} from "../../../../intermediaries/Intermediary";
7
- import {ISwapWrapperOptions, WrapperCtorTokens} from "../../../ISwapWrapper";
8
- import {ISwapPrice} from "../../../../prices/abstract/ISwapPrice";
9
- import {EventEmitter} from "events";
10
- import {IntermediaryError} from "../../../../errors/IntermediaryError";
11
- import {SwapType} from "../../../../enums/SwapType";
12
- import {extendAbortController, mapArrayToObject, throwIfUndefined} from "../../../../utils/Utils";
13
- import {IntermediaryAPI, ToBTCLNResponseType} from "../../../../intermediaries/apis/IntermediaryAPI";
14
- import {RequestError} from "../../../../errors/RequestError";
15
- import {LNURL, LNURLPaySuccessAction} from "../../../../lnurl/LNURL";
16
- import {IToBTCSwapInit, ToBTCSwapState} from "../IToBTCSwap";
17
- import {UnifiedSwapEventListener} from "../../../../events/UnifiedSwapEventListener";
18
- import {UnifiedSwapStorage} from "../../../../storage/UnifiedSwapStorage";
19
- import {ISwap} from "../../../ISwap";
20
- import {sha256} from "@noble/hashes/sha2";
21
- import {AmountData} from "../../../../types/AmountData";
22
- import {LNURLPayParamsWithUrl} from "../../../../types/lnurl/LNURLPay";
23
- import {tryWithRetries} from "../../../../utils/RetryUtils";
24
- import {AllOptional} from "../../../../utils/TypeUtils";
25
- import {LightningInvoiceCreateService} from "../../../../types/wallets/LightningInvoiceCreateService";
26
-
27
- export type ToBTCLNOptions = {
28
- /**
29
- * HTLC expiration timeout in seconds to use when offering the HTLC to the LP. Larger expirations mean that more
30
- * lightning network payment paths can be considered (every hop in the lightning network payment adds additional
31
- * timeout requirement). On the other side, larger expiration also means that user's funds are locked for longer
32
- * in case of a non-cooperative LP.
33
- *
34
- * Uses 5 days as default.
35
- */
36
- expirySeconds?: number,
37
- /**
38
- * Maximum fee for routing the swap output payment through the lightning network. Higher fee percentages means that
39
- * more payment routes can be considered (every hop in the lightning network payment adds additional fee
40
- * requirements).
41
- *
42
- * The fee is express as percentage of the swap value, uses `0.2` by default which means the maximum
43
- * routing fee is capped at 0.2% of the swap value.
44
- *
45
- * The full fee also contains the base component (set by `maxRoutingBaseFee` option), the resulting maximum routing
46
- * fee rate is:
47
- *
48
- * `maxRoutingFee` = `maxRoutingBaseFee` sats + `value` * `maxRoutingFeePercentage`%
49
- */
50
- maxRoutingFeePercentage?: number,
51
- /**
52
- *
53
- * Maximum base fee (in sats) for routing the swap output payment through the lightning network. Higher fee
54
- * percentages means that more payment routes can be considered (every hop in the lightning network payment adds additional fee
55
- * requirements).
56
- *
57
- * Uses 10 sats as a default.
58
- *
59
- * The full fee also contains the value percentage component (set by `maxRoutingFeePercentage` option), the
60
- * resulting maximum routing fee rate is:
61
- *
62
- * `maxRoutingFee` = `maxRoutingBaseFee` sats + (`value` * `maxRoutingFeePercentage`%)
63
- */
64
- maxRoutingBaseFee?: bigint,
65
-
66
- /**
67
- * @deprecated Use `maxRoutingFeePercentage` and express the routing fee in percentage instead!
68
- */
69
- maxRoutingPPM?: bigint,
70
- /**
71
- * @deprecated Adjust fee with `maxRoutingFeePercentage` & `maxRoutingBaseFee` params!
72
- */
73
- maxFee?: bigint | Promise<bigint>,
74
- /**
75
- * @deprecated Pass desired HTLC expiration timeout as `expirySeconds`
76
- */
77
- expiryTimestamp?: bigint
78
- }
79
-
80
- export type ToBTCLNWrapperOptions = ISwapWrapperOptions & {
81
- lightningBaseFee: number,
82
- lightningFeePPM: number,
83
- paymentTimeoutSeconds: number
84
- };
85
-
86
- export type ToBTCLNDefinition<T extends ChainType> = IToBTCDefinition<T, ToBTCLNWrapper<T>, ToBTCLNSwap<T>>;
87
-
88
- /**
89
- * Escrow based (HTLC) swap for Smart chains -> Bitcoin lightning
90
- *
91
- * @category Swaps/Smart chain → Lightning
92
- */
93
- export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCLNDefinition<T>, ToBTCLNWrapperOptions> {
94
- public readonly TYPE: SwapType.TO_BTCLN = SwapType.TO_BTCLN;
95
- /**
96
- * @internal
97
- */
98
- readonly _swapDeserializer = ToBTCLNSwap;
99
-
100
- constructor(
101
- chainIdentifier: string,
102
- unifiedStorage: UnifiedSwapStorage<T>,
103
- unifiedChainEvents: UnifiedSwapEventListener<T>,
104
- chain: T["ChainInterface"],
105
- prices: ISwapPrice,
106
- tokens: WrapperCtorTokens,
107
- versionedContracts: {
108
- [version: string]: {
109
- swapContract: T["Contract"],
110
- swapDataConstructor: new (data: any) => T["Data"]
111
- }
112
- },
113
- lpApi: IntermediaryAPI,
114
- options?: AllOptional<ToBTCLNWrapperOptions>,
115
- events?: EventEmitter<{swapState: [ISwap]}>
116
- ) {
117
- super(
118
- chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, lpApi,
119
- {
120
- ...options,
121
- paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 5*24*60*60,
122
- lightningBaseFee: options?.lightningBaseFee ?? 10,
123
- lightningFeePPM: options?.lightningFeePPM ?? 2000
124
- },
125
- versionedContracts,
126
- events
127
- );
128
- }
129
-
130
- private toRequiredSwapOptions(amountData: AmountData, options?: ToBTCLNOptions, pricePreFetchPromise?: Promise<bigint | undefined>, abortSignal?: AbortSignal) {
131
- const expirySeconds = options?.expirySeconds ?? this._options.paymentTimeoutSeconds;
132
- const maxRoutingBaseFee = options?.maxRoutingBaseFee ?? BigInt(this._options.lightningBaseFee);
133
- const maxRoutingPPM = options?.maxRoutingFeePercentage!=null
134
- ? BigInt(Math.floor(options.maxRoutingFeePercentage * 10_000))
135
- : options?.maxRoutingPPM ?? BigInt(this._options.lightningFeePPM);
136
-
137
- let maxFee: bigint | Promise<bigint>;
138
- if(options?.maxFee!=null) {
139
- maxFee = options.maxFee;
140
- } else if(amountData.exactIn) {
141
- if(pricePreFetchPromise!=null) {
142
- maxFee = pricePreFetchPromise
143
- .then(val => this._prices.getFromBtcSwapAmount(this.chainIdentifier, maxRoutingBaseFee, amountData.token, abortSignal, val))
144
- .then(_maxBaseFee => this.calculateFeeForAmount(amountData.amount, _maxBaseFee, maxRoutingPPM))
145
- } else {
146
- maxFee = this._prices.getFromBtcSwapAmount(this.chainIdentifier, maxRoutingBaseFee, amountData.token, abortSignal)
147
- .then(_maxBaseFee => this.calculateFeeForAmount(amountData.amount, _maxBaseFee, maxRoutingPPM))
148
- }
149
- } else {
150
- maxFee = this.calculateFeeForAmount(amountData.amount, maxRoutingBaseFee, maxRoutingPPM)
151
- }
152
-
153
- return {
154
- expiryTimestamp: options?.expiryTimestamp ?? BigInt(Math.floor(Date.now()/1000)+expirySeconds),
155
- maxFee
156
- }
157
- }
158
-
159
- /**
160
- * Verifies whether a given payment hash was already paid by checking the local
161
- * storage of known swaps
162
- *
163
- * @param paymentHash Payment hash to check
164
- *
165
- * @private
166
- */
167
- private async checkPaymentHashWasPaid(paymentHash: string) {
168
- const swaps = await this.unifiedStorage.query(
169
- [[{key: "type", value: this.TYPE}, {key: "paymentHash", value: paymentHash}]],
170
- (obj: any) => new this._swapDeserializer(this, obj)
171
- );
172
-
173
- for(let value of swaps) {
174
- if(value._state===ToBTCSwapState.CLAIMED || value._state===ToBTCSwapState.SOFT_CLAIMED)
175
- throw new UserError("Lightning invoice was already paid!");
176
- }
177
- }
178
-
179
- /**
180
- * Calculates maximum lightning network routing fee based on amount
181
- *
182
- * @param amount BTC amount of the swap in satoshis
183
- * @param overrideBaseFee Override wrapper's default base fee
184
- * @param overrideFeePPM Override wrapper's default PPM
185
- *
186
- * @returns Maximum lightning routing fee in sats
187
- *
188
- * @private
189
- */
190
- private calculateFeeForAmount(amount: bigint, overrideBaseFee?: bigint, overrideFeePPM?: bigint) : bigint {
191
- return BigInt(overrideBaseFee ?? this._options.lightningBaseFee)
192
- + (amount * BigInt(overrideFeePPM ?? this._options.lightningFeePPM) / 1000000n);
193
- }
194
-
195
- /**
196
- * Verifies returned LP data
197
- *
198
- * @param signer
199
- * @param resp Response as returned by the LP
200
- * @param parsedPr Parsed bolt11 lightning invoice
201
- * @param token Smart chain token to be used in the swap
202
- * @param lp
203
- * @param calculatedOptions Swap options computed from the swap create options
204
- * @param data Parsed swap data returned by the LP
205
- * @param requiredTotal Required total to be paid on the input (for exactIn swaps)
206
- *
207
- * @throws {IntermediaryError} In case the response is not valid
208
- *
209
- * @private
210
- */
211
- private async verifyReturnedData(
212
- signer: string,
213
- resp: ToBTCLNResponseType,
214
- parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
215
- token: string,
216
- lp: Intermediary,
217
- calculatedOptions: {
218
- maxFee: bigint | Promise<bigint>,
219
- expiryTimestamp: bigint
220
- },
221
- data: T["Data"],
222
- requiredTotal?: bigint
223
- ): Promise<void> {
224
- if(resp.routingFeeSats > await calculatedOptions.maxFee) throw new IntermediaryError("Invalid max fee sats returned");
225
-
226
- if(requiredTotal!=null && resp.total !== requiredTotal)
227
- throw new IntermediaryError("Invalid data returned - total amount");
228
-
229
- if(parsedPr.tagsObject.payment_hash==null) throw new Error("Swap invoice doesn't contain payment hash field!");
230
- const claimHash = this._contract(lp.getContractVersion(this.chainIdentifier)).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash, "hex"));
231
-
232
- if(
233
- data.getAmount() !== resp.total ||
234
- !Buffer.from(data.getClaimHash(), "hex").equals(claimHash) ||
235
- data.getExpiry() !== calculatedOptions.expiryTimestamp ||
236
- data.getType()!==ChainSwapType.HTLC ||
237
- !data.isPayIn() ||
238
- !data.isToken(token) ||
239
- !data.isClaimer(lp.getAddress(this.chainIdentifier)) ||
240
- !data.isOfferer(signer) ||
241
- data.getTotalDeposit() !== 0n
242
- ) {
243
- throw new IntermediaryError("Invalid data returned");
244
- }
245
- }
246
-
247
- /**
248
- * Returns the quote/swap from a given intermediary
249
- *
250
- * @param signer Smartchain signer initiating the swap
251
- * @param amountData
252
- * @param lp Intermediary
253
- * @param pr bolt11 lightning network invoice
254
- * @param parsedPr Parsed bolt11 lightning network invoice
255
- * @param calculatedOptions Swap options computed from the swap create options
256
- * @param preFetches
257
- * @param abort Abort signal or controller, if AbortController is passed it is used as-is, when AbortSignal is passed
258
- * it is extended with extendAbortController and then used
259
- * @param additionalParams Additional params that should be sent to the LP
260
- *
261
- * @private
262
- */
263
- private async getIntermediaryQuote(
264
- signer: string,
265
- amountData: Omit<AmountData, "amount">,
266
- lp: Intermediary,
267
- pr: string,
268
- parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
269
- calculatedOptions: {
270
- maxFee: bigint | Promise<bigint>,
271
- expiryTimestamp: bigint
272
- },
273
- preFetches: {
274
- feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
275
- pricePreFetchPromise: Promise<bigint | undefined>,
276
- usdPricePrefetchPromise: Promise<number | undefined>,
277
- signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
278
- },
279
- abort: AbortSignal | AbortController,
280
- additionalParams?: Record<string, any>,
281
- ) {
282
- if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
283
- const version = lp.getContractVersion(this.chainIdentifier);
284
-
285
- const abortController = abort instanceof AbortController ? abort : extendAbortController(abort);
286
- const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
287
-
288
- try {
289
- const {signDataPromise, resp} = await tryWithRetries(async(retryCount: number) => {
290
- const {signDataPrefetch, response} = this._lpApi.initToBTCLN(this.chainIdentifier, lp.url, {
291
- offerer: signer,
292
- pr,
293
- maxFee: await calculatedOptions.maxFee,
294
- expiryTimestamp: calculatedOptions.expiryTimestamp,
295
- token: amountData.token,
296
- feeRate: throwIfUndefined(preFetches.feeRatePromise[version], "Network fee rate pre-fetch failed!"),
297
- additionalParams
298
- }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
299
-
300
- let signDataPromise = preFetches.signDataPrefetchPromise?.[version];
301
- if(signDataPromise==null) {
302
- signDataPromise = this.preFetchSignData(signDataPrefetch, version);
303
- } else signDataPrefetch.catch(() => {});
304
-
305
- return {
306
- signDataPromise,
307
- resp: await response
308
- };
309
- }, undefined, e => e instanceof RequestError, abortController.signal);
310
-
311
- if(parsedPr.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
312
- const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
313
- const totalFee: bigint = resp.swapFee + resp.maxFee;
314
- const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
315
- data.setOfferer(signer);
316
-
317
- await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
318
-
319
- const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
320
-
321
- const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
322
- this.verifyReturnedPrice(
323
- lp.services[SwapType.TO_BTCLN], true, amountOut, data.getAmount(),
324
- amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
325
- preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal
326
- ),
327
- this.verifyReturnedSignature(
328
- signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
329
- ),
330
- reputationPromise
331
- ]);
332
- abortController.signal.throwIfAborted();
333
-
334
- if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
335
-
336
- const quote = new ToBTCLNSwap<T>(this, {
337
- pricingInfo,
338
- url: lp.url,
339
- expiry: signatureExpiry,
340
- swapFee: resp.swapFee,
341
- swapFeeBtc,
342
- feeRate: (await preFetches.feeRatePromise[version])!,
343
- signatureData: resp,
344
- data,
345
- networkFee: resp.maxFee,
346
- networkFeeBtc: resp.routingFeeSats,
347
- confidence: resp.confidence,
348
- pr,
349
- exactIn: false,
350
- contractVersion: version
351
- } as IToBTCSwapInit<T["Data"]>);
352
- return quote;
353
- } catch (e) {
354
- abortController.abort(e);
355
- throw e;
356
- }
357
- }
358
-
359
- /**
360
- * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol,
361
- * the amount is parsed from the provided lightning network payment request (bolt11 invoice)
362
- *
363
- * @param signer Source chain signer address initiating the swap
364
- * @param recipient BOLT11 payment request (bitcoin lightning invoice) you wish to pay
365
- * @param amountData Token to swap
366
- * @param lps An array of intermediaries (LPs) to get the quotes from
367
- * @param options Optional additional quote options
368
- * @param additionalParams Optional additional parameters sent to the LP when creating the swap
369
- * @param abortSignal Abort signal
370
- * @param preFetches Optional existing pre-fetch promises for the swap (only used internally for LNURL swaps)
371
- */
372
- async create(
373
- signer: string,
374
- recipient: string,
375
- amountData: Omit<AmountData, "amount"> & {exactIn: false},
376
- lps: Intermediary[],
377
- options?: ToBTCLNOptions,
378
- additionalParams?: Record<string, any>,
379
- abortSignal?: AbortSignal,
380
- preFetches?: {
381
- feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
382
- pricePreFetchPromise: Promise<bigint | undefined>,
383
- usdPricePrefetchPromise: Promise<number | undefined>,
384
- signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
385
- }
386
- ): Promise<{
387
- quote: Promise<ToBTCLNSwap<T>>,
388
- intermediary: Intermediary
389
- }[]> {
390
- const parsedPr = bolt11Decode(recipient);
391
- if(parsedPr.millisatoshis==null) throw new UserError("Must be an invoice with amount");
392
- const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
393
-
394
- const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
395
-
396
- const _options = this.toRequiredSwapOptions({...amountData, amount: amountOut}, options);
397
-
398
- if(parsedPr.tagsObject.payment_hash==null) throw new Error("Provided lightning invoice doesn't contain payment hash field!");
399
- await this.checkPaymentHashWasPaid(parsedPr.tagsObject.payment_hash);
400
-
401
- const _hash = mapArrayToObject(lpVersions, (contractVersion: string) => {
402
- return this._contract(contractVersion).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash!, "hex")).toString("hex");
403
- });
404
-
405
- const _abortController = extendAbortController(abortSignal);
406
- const _preFetches = preFetches ?? {
407
- pricePreFetchPromise: this.preFetchPrice(amountData, _abortController.signal),
408
- feeRatePromise: this.preFetchFeeRate(signer, amountData, _hash, _abortController, lpVersions),
409
- usdPricePrefetchPromise: this.preFetchUsdPrice(_abortController.signal),
410
- signDataPrefetchPromise: mapArrayToObject(lpVersions, (contractVersion: string) => {
411
- return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
412
- this.preFetchSignData(Promise.resolve(true), contractVersion) :
413
- undefined;
414
- })
415
- };
416
-
417
- return lps.map(lp => {
418
- return {
419
- intermediary: lp,
420
- quote: this.getIntermediaryQuote(signer, amountData, lp, recipient, parsedPr, _options, _preFetches, _abortController.signal, additionalParams)
421
- }
422
- });
423
- }
424
-
425
- /**
426
- * Parses and fetches lnurl pay params from the specified lnurl
427
- *
428
- * @param lnurl LNURL to be parsed and fetched
429
- * @param abortSignal Abort signal
430
- * @throws {UserError} if the LNURL is invalid or if it's not a LNURL-pay
431
- *
432
- * @private
433
- */
434
- private async getLNURLPay(lnurl: string | LNURLPayParamsWithUrl, abortSignal: AbortSignal): Promise<LNURLPayParamsWithUrl> {
435
- if(typeof(lnurl)!=="string") return lnurl;
436
-
437
- const res = await LNURL.getLNURL(lnurl, true, this._options.getRequestTimeout, abortSignal);
438
- if(res==null) throw new UserError("Invalid LNURL");
439
- if(res.tag!=="payRequest") throw new UserError("Not a LNURL-pay");
440
- return res;
441
- }
442
-
443
- /**
444
- * Returns the quote/swap from the given LP
445
- *
446
- * @param signer Source chain signer address initiating the swap
447
- * @param amountData Token to swap
448
- * @param invoiceCreateService Service for creating fixed amount invoices
449
- * @param lp Intermediary (LPs) to get the quote from
450
- * @param dummyPr Dummy minimum value bolt11 lightning invoice returned from the LNURL-pay, used to estimate
451
- * network fees for an actual invoice
452
- * @param calculatedOptions Swap options computed from the swap create options
453
- * @param preFetches Optional existing pre-fetch promises for the swap (only used internally for LNURL swaps)
454
- * @param abortSignal Abort signal
455
- * @param additionalParams Additional params to be sent to the intermediary
456
- *
457
- * @private
458
- */
459
- private async getIntermediaryQuoteExactIn(
460
- signer: string,
461
- amountData: AmountData,
462
- invoiceCreateService: LightningInvoiceCreateService,
463
- lp: Intermediary,
464
- dummyPr: string,
465
- calculatedOptions: {
466
- maxFee: bigint | Promise<bigint>,
467
- expiryTimestamp: bigint
468
- },
469
- preFetches: {
470
- feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
471
- pricePreFetchPromise: Promise<bigint | undefined>,
472
- usdPricePrefetchPromise: Promise<number | undefined>
473
- },
474
- abortSignal: AbortSignal,
475
- additionalParams?: Record<string, any>,
476
- ) {
477
- if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
478
- const version = lp.getContractVersion(this.chainIdentifier);
479
-
480
- const abortController = extendAbortController(abortSignal);
481
- const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
482
-
483
- try {
484
- const {signDataPromise, prepareResp} = await tryWithRetries(async(retryCount: number) => {
485
- const {signDataPrefetch, response} = this._lpApi.prepareToBTCLNExactIn(this.chainIdentifier, lp.url, {
486
- token: amountData.token,
487
- offerer: signer,
488
- pr: dummyPr,
489
- amount: amountData.amount,
490
- maxFee: await calculatedOptions.maxFee,
491
- expiryTimestamp: calculatedOptions.expiryTimestamp,
492
- additionalParams
493
- }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
494
-
495
- return {
496
- signDataPromise: this.preFetchSignData(signDataPrefetch, version),
497
- prepareResp: await response
498
- };
499
- }, undefined, e => e instanceof RequestError, abortController.signal);
500
-
501
- if(prepareResp.amount <= 0n)
502
- throw new IntermediaryError("Invalid amount returned (zero or negative)");
503
-
504
- if(invoiceCreateService.minMsats!=null) {
505
- if(prepareResp.amount < invoiceCreateService.minMsats / 1000n) throw new UserError("Amount less than minimum");
506
- }
507
- if(invoiceCreateService.maxMSats!=null) {
508
- if(prepareResp.amount > invoiceCreateService.maxMSats / 1000n) throw new UserError("Amount more than maximum");
509
- }
510
-
511
- const invoice = await invoiceCreateService.getInvoice(Number(prepareResp.amount), abortController.signal);
512
- const parsedInvoice = bolt11Decode(invoice);
513
-
514
- const resp = await tryWithRetries(
515
- (retryCount: number) => this._lpApi.initToBTCLNExactIn(lp.url, {
516
- pr: invoice,
517
- reqId: prepareResp.reqId,
518
- feeRate: throwIfUndefined(preFetches.feeRatePromise[version], "Network fee rate pre-fetch failed!"),
519
- additionalParams
520
- }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined),
521
- undefined, RequestError, abortController.signal
522
- );
523
-
524
- if(parsedInvoice.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
525
- const amountOut: bigint = (BigInt(parsedInvoice.millisatoshis) + 999n) / 1000n;
526
- const totalFee: bigint = resp.swapFee + resp.maxFee;
527
- const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
528
- data.setOfferer(signer);
529
-
530
- await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
531
-
532
- const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
533
-
534
- const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
535
- this.verifyReturnedPrice(
536
- lp.services[SwapType.TO_BTCLN], true, prepareResp.amount, data.getAmount(),
537
- amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
538
- preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal
539
- ),
540
- this.verifyReturnedSignature(
541
- signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
542
- ),
543
- reputationPromise
544
- ]);
545
- abortController.signal.throwIfAborted();
546
-
547
- if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
548
-
549
- const quote = new ToBTCLNSwap<T>(this, {
550
- pricingInfo,
551
- url: lp.url,
552
- expiry: signatureExpiry,
553
- swapFee: resp.swapFee,
554
- swapFeeBtc,
555
- feeRate: (await preFetches.feeRatePromise[version])!,
556
- signatureData: resp,
557
- data,
558
- networkFee: resp.maxFee,
559
- networkFeeBtc: resp.routingFeeSats,
560
- confidence: resp.confidence,
561
- pr: invoice,
562
- exactIn: true,
563
- contractVersion: version
564
- } as IToBTCSwapInit<T["Data"]>);
565
- return quote;
566
- } catch (e) {
567
- abortController.abort(e);
568
- throw e;
569
- }
570
- }
571
-
572
- /**
573
- * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol via
574
- * invoice creation service. This allows exactIn swaps by requesting the desired fixed amount lightning
575
- * network invoice from the service.
576
- *
577
- * @param signer Source chain signer address initiating the swap
578
- * @param invoiceCreateServicePromise Service to request destination lightning network invoices from
579
- * @param amountData Amount, token and exact input/output data for to swap
580
- * @param lps An array of intermediaries (LPs) to get the quotes from
581
- * @param options Optional additional quote options
582
- * @param additionalParams Optional additional parameters sent to the LP when creating the swap
583
- * @param abortSignal Abort signal
584
- */
585
- async createViaInvoiceCreateService(
586
- signer: string,
587
- invoiceCreateServicePromise: Promise<LightningInvoiceCreateService>,
588
- amountData: AmountData,
589
- lps: Intermediary[],
590
- options?: ToBTCLNOptions,
591
- additionalParams?: Record<string, any>,
592
- abortSignal?: AbortSignal
593
- ): Promise<{
594
- quote: Promise<ToBTCLNSwap<T>>,
595
- intermediary: Intermediary
596
- }[]> {
597
- if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
598
-
599
- const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
600
-
601
- const _abortController = extendAbortController(abortSignal);
602
- const pricePreFetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
603
- const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
604
- const feeRatePromise = this.preFetchFeeRate(signer, amountData, undefined, _abortController, lpVersions);
605
- const signDataPrefetchPromise = mapArrayToObject(lpVersions, (contractVersion: string) => {
606
- return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
607
- this.preFetchSignData(Promise.resolve(true), contractVersion) :
608
- undefined;
609
- });
610
-
611
- const _options = this.toRequiredSwapOptions(amountData, options, pricePreFetchPromise, _abortController.signal);
612
-
613
- try {
614
- const invoiceCreateService = await invoiceCreateServicePromise;
615
-
616
- if(amountData.exactIn) {
617
- const dummyInvoice = await invoiceCreateService.getInvoice(
618
- invoiceCreateService.minMsats==null ? 1 : Number(invoiceCreateService.minMsats/1000n),
619
- _abortController.signal
620
- );
621
-
622
- return lps.map(lp => {
623
- return {
624
- quote: this.getIntermediaryQuoteExactIn(signer, amountData, invoiceCreateService, lp, dummyInvoice, _options, {
625
- pricePreFetchPromise,
626
- usdPricePrefetchPromise,
627
- feeRatePromise
628
- }, _abortController.signal, additionalParams),
629
- intermediary: lp
630
- }
631
- })
632
- } else {
633
- if(invoiceCreateService.minMsats!=null) {
634
- if(amountData.amount < invoiceCreateService.minMsats / 1000n) throw new UserError("Amount less than minimum");
635
- }
636
- if(invoiceCreateService.maxMSats!=null) {
637
- if(amountData.amount > invoiceCreateService.maxMSats / 1000n) throw new UserError("Amount more than maximum");
638
- }
639
-
640
- const invoice = await invoiceCreateService.getInvoice(Number(amountData.amount), _abortController.signal);
641
-
642
- return (await this.create(signer, invoice, {...amountData, exactIn: false}, lps, options, additionalParams, _abortController.signal, {
643
- feeRatePromise,
644
- pricePreFetchPromise,
645
- usdPricePrefetchPromise,
646
- signDataPrefetchPromise
647
- }));
648
- }
649
- } catch (e) {
650
- _abortController.abort(e);
651
- throw e;
652
- }
653
- }
654
-
655
- /**
656
- * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol. Pays to
657
- * an LNURL-pay link. This allows exactIn swaps by requesting the desired fixed amount lightning
658
- * network invoice from the LNURL service.
659
- *
660
- * @param signer Source chain signer address initiating the swap
661
- * @param lnurl LNURL-pay link of the recipient
662
- * @param amountData Amount, token and exact input/output data for to swap
663
- * @param lps An array of intermediaries (LPs) to get the quotes from
664
- * @param options Optional additional quote options
665
- * @param additionalParams Optional additional parameters sent to the LP when creating the swap
666
- * @param abortSignal Abort signal
667
- */
668
- async createViaLNURL(
669
- signer: string,
670
- lnurl: string | LNURLPayParamsWithUrl,
671
- amountData: AmountData,
672
- lps: Intermediary[],
673
- options?: ToBTCLNOptions & {comment?: string},
674
- additionalParams?: Record<string, any>,
675
- abortSignal?: AbortSignal
676
- ): Promise<{
677
- quote: Promise<ToBTCLNSwap<T>>,
678
- intermediary: Intermediary
679
- }[]> {
680
- let successActions: {[pr: string]: LNURLPaySuccessAction} = {};
681
-
682
- const _abortController = extendAbortController(abortSignal);
683
- const invoiceCreateService = (async() => {
684
- let payRequest: LNURLPayParamsWithUrl = await this.getLNURLPay(lnurl, _abortController.signal);
685
-
686
- if(
687
- options?.comment!=null &&
688
- (payRequest.commentAllowed==null || options.comment.length>payRequest.commentAllowed)
689
- ) throw new UserError("Comment not allowed or too long");
690
-
691
- return {
692
- getInvoice: async (amountSats: number, abortSignal?: AbortSignal) => {
693
- const {invoice, successAction} = await LNURL.useLNURLPay(
694
- payRequest, BigInt(amountSats), options?.comment,
695
- this._options.getRequestTimeout, abortSignal
696
- );
697
- if(successAction!=null) successActions[invoice] = successAction;
698
- return invoice;
699
- },
700
- minMsats: BigInt(payRequest.minSendable),
701
- maxMsats: BigInt(payRequest.maxSendable),
702
- url: payRequest.url
703
- }
704
- })();
705
-
706
- const quotes = await this.createViaInvoiceCreateService(
707
- signer,
708
- invoiceCreateService,
709
- amountData,
710
- lps,
711
- options,
712
- additionalParams,
713
- _abortController.signal
714
- );
715
- _abortController.signal.throwIfAborted();
716
-
717
- const resolved = await invoiceCreateService;
718
- _abortController.signal.throwIfAborted();
719
-
720
- return quotes.map(value => ({
721
- quote: value.quote.then(quote => {
722
- let _successAction: LNURLPaySuccessAction | undefined;
723
- const quoteAddress = quote.getOutputAddress();
724
- if(quoteAddress!=null) {
725
- const successAction = successActions[quoteAddress];
726
- if(successAction!=null) _successAction = successAction;
727
- }
728
- quote._setLNURLData(resolved.url, _successAction);
729
- return quote;
730
- }),
731
- intermediary: value.intermediary
732
- }));
733
- }
734
-
735
- /**
736
- * @inheritDoc
737
- */
738
- async recoverFromSwapDataAndState(
739
- init: {data: T["Data"], getInitTxId: () => Promise<string>, getTxBlock: () => Promise<{blockTime: number, blockHeight: number}>},
740
- state: SwapCommitState,
741
- contractVersion: string,
742
- lp?: Intermediary
743
- ): Promise<ToBTCLNSwap<T> | null> {
744
- const data = init.data;
745
-
746
- let paymentHash = data.getHTLCHashHint();
747
- let secret: string | undefined;
748
- if(state.type===SwapCommitStateType.PAID) {
749
- secret = await state.getClaimResult();
750
- paymentHash = Buffer.from(sha256(Buffer.from(secret, "hex"))).toString("hex");
751
- }
752
-
753
- const swapInit: ToBTCLNSwapInit<T["Data"]> = {
754
- pricingInfo: {
755
- isValid: true,
756
- satsBaseFee: 0n,
757
- swapPriceUSatPerToken: 100_000_000_000_000n,
758
- realPriceUSatPerToken: 100_000_000_000_000n,
759
- differencePPM: 0n,
760
- feePPM: 0n,
761
- },
762
- url: lp?.url,
763
- expiry: 0,
764
- swapFee: 0n,
765
- swapFeeBtc: 0n,
766
- feeRate: "",
767
- signatureData: undefined,
768
- data,
769
- networkFee: 0n,
770
- networkFeeBtc: 0n,
771
- confidence: 0,
772
- pr: paymentHash ?? undefined,
773
- exactIn: false,
774
- contractVersion
775
- };
776
- const swap = new ToBTCLNSwap(this, swapInit);
777
- swap._commitTxId = await init.getInitTxId();
778
- const blockData = await init.getTxBlock();
779
- swap.createdAt = blockData.blockTime * 1000;
780
- swap._setInitiated();
781
- swap._state = ToBTCSwapState.COMMITED;
782
- await swap._sync(false, false, state);
783
- await swap._save();
784
- return swap;
785
- }
786
-
787
- }
1
+ import {decode as bolt11Decode, PaymentRequestObject, TagsObject} from "@atomiqlabs/bolt11";
2
+ import {ToBTCLNSwap, ToBTCLNSwapInit} from "./ToBTCLNSwap";
3
+ import {IToBTCDefinition, IToBTCWrapper} from "../IToBTCWrapper";
4
+ import {UserError} from "../../../../errors/UserError";
5
+ import {ChainSwapType, ChainType, SwapCommitState, SwapCommitStateType} from "@atomiqlabs/base";
6
+ import {Intermediary} from "../../../../intermediaries/Intermediary";
7
+ import {ISwapWrapperOptions, WrapperCtorTokens} from "../../../ISwapWrapper";
8
+ import {ISwapPrice} from "../../../../prices/abstract/ISwapPrice";
9
+ import {EventEmitter} from "events";
10
+ import {IntermediaryError} from "../../../../errors/IntermediaryError";
11
+ import {SwapType} from "../../../../enums/SwapType";
12
+ import {extendAbortController, mapArrayToObject, throwIfUndefined} from "../../../../utils/Utils";
13
+ import {IntermediaryAPI, ToBTCLNResponseType} from "../../../../intermediaries/apis/IntermediaryAPI";
14
+ import {RequestError} from "../../../../errors/RequestError";
15
+ import {LNURL, LNURLPaySuccessAction} from "../../../../lnurl/LNURL";
16
+ import {IToBTCSwapInit, ToBTCSwapState} from "../IToBTCSwap";
17
+ import {UnifiedSwapEventListener} from "../../../../events/UnifiedSwapEventListener";
18
+ import {UnifiedSwapStorage} from "../../../../storage/UnifiedSwapStorage";
19
+ import {ISwap} from "../../../ISwap";
20
+ import {sha256} from "@noble/hashes/sha2";
21
+ import {AmountData} from "../../../../types/AmountData";
22
+ import {LNURLPayParamsWithUrl} from "../../../../types/lnurl/LNURLPay";
23
+ import {tryWithRetries} from "../../../../utils/RetryUtils";
24
+ import {AllOptional} from "../../../../utils/TypeUtils";
25
+ import {LightningInvoiceCreateService} from "../../../../types/wallets/LightningInvoiceCreateService";
26
+
27
+ export type ToBTCLNOptions = {
28
+ /**
29
+ * HTLC expiration timeout in seconds to use when offering the HTLC to the LP. Larger expirations mean that more
30
+ * lightning network payment paths can be considered (every hop in the lightning network payment adds additional
31
+ * timeout requirement). On the other side, larger expiration also means that user's funds are locked for longer
32
+ * in case of a non-cooperative LP.
33
+ *
34
+ * Uses 5 days as default.
35
+ */
36
+ expirySeconds?: number,
37
+ /**
38
+ * Maximum fee for routing the swap output payment through the lightning network. Higher fee percentages means that
39
+ * more payment routes can be considered (every hop in the lightning network payment adds additional fee
40
+ * requirements).
41
+ *
42
+ * The fee is express as percentage of the swap value, uses `0.2` by default which means the maximum
43
+ * routing fee is capped at 0.2% of the swap value.
44
+ *
45
+ * The full fee also contains the base component (set by `maxRoutingBaseFee` option), the resulting maximum routing
46
+ * fee rate is:
47
+ *
48
+ * `maxRoutingFee` = `maxRoutingBaseFee` sats + `value` * `maxRoutingFeePercentage`%
49
+ */
50
+ maxRoutingFeePercentage?: number,
51
+ /**
52
+ *
53
+ * Maximum base fee (in sats) for routing the swap output payment through the lightning network. Higher fee
54
+ * percentages means that more payment routes can be considered (every hop in the lightning network payment adds additional fee
55
+ * requirements).
56
+ *
57
+ * Uses 10 sats as a default.
58
+ *
59
+ * The full fee also contains the value percentage component (set by `maxRoutingFeePercentage` option), the
60
+ * resulting maximum routing fee rate is:
61
+ *
62
+ * `maxRoutingFee` = `maxRoutingBaseFee` sats + (`value` * `maxRoutingFeePercentage`%)
63
+ */
64
+ maxRoutingBaseFee?: bigint,
65
+
66
+ /**
67
+ * @deprecated Use `maxRoutingFeePercentage` and express the routing fee in percentage instead!
68
+ */
69
+ maxRoutingPPM?: bigint,
70
+ /**
71
+ * @deprecated Adjust fee with `maxRoutingFeePercentage` & `maxRoutingBaseFee` params!
72
+ */
73
+ maxFee?: bigint | Promise<bigint>,
74
+ /**
75
+ * @deprecated Pass desired HTLC expiration timeout as `expirySeconds`
76
+ */
77
+ expiryTimestamp?: bigint
78
+ }
79
+
80
+ export type ToBTCLNWrapperOptions = ISwapWrapperOptions & {
81
+ lightningBaseFee: number,
82
+ lightningFeePPM: number,
83
+ paymentTimeoutSeconds: number
84
+ };
85
+
86
+ export type ToBTCLNDefinition<T extends ChainType> = IToBTCDefinition<T, ToBTCLNWrapper<T>, ToBTCLNSwap<T>>;
87
+
88
+ /**
89
+ * Escrow based (HTLC) swap for Smart chains -> Bitcoin lightning
90
+ *
91
+ * @category Swaps/Smart chain → Lightning
92
+ */
93
+ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCLNDefinition<T>, ToBTCLNWrapperOptions> {
94
+ public readonly TYPE: SwapType.TO_BTCLN = SwapType.TO_BTCLN;
95
+ /**
96
+ * @internal
97
+ */
98
+ readonly _swapDeserializer = ToBTCLNSwap;
99
+
100
+ constructor(
101
+ chainIdentifier: string,
102
+ unifiedStorage: UnifiedSwapStorage<T>,
103
+ unifiedChainEvents: UnifiedSwapEventListener<T>,
104
+ chain: T["ChainInterface"],
105
+ prices: ISwapPrice,
106
+ tokens: WrapperCtorTokens,
107
+ versionedContracts: {
108
+ [version: string]: {
109
+ swapContract: T["Contract"],
110
+ swapDataConstructor: new (data: any) => T["Data"]
111
+ }
112
+ },
113
+ lpApi: IntermediaryAPI,
114
+ options?: AllOptional<ToBTCLNWrapperOptions>,
115
+ events?: EventEmitter<{swapState: [ISwap]}>
116
+ ) {
117
+ super(
118
+ chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, lpApi,
119
+ {
120
+ ...options,
121
+ paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 5*24*60*60,
122
+ lightningBaseFee: options?.lightningBaseFee ?? 10,
123
+ lightningFeePPM: options?.lightningFeePPM ?? 2000
124
+ },
125
+ versionedContracts,
126
+ events
127
+ );
128
+ }
129
+
130
+ private toRequiredSwapOptions(amountData: AmountData, options?: ToBTCLNOptions, pricePreFetchPromise?: Promise<bigint | undefined>, abortSignal?: AbortSignal) {
131
+ const expirySeconds = options?.expirySeconds ?? this._options.paymentTimeoutSeconds;
132
+ const maxRoutingBaseFee = options?.maxRoutingBaseFee ?? BigInt(this._options.lightningBaseFee);
133
+ const maxRoutingPPM = options?.maxRoutingFeePercentage!=null
134
+ ? BigInt(Math.floor(options.maxRoutingFeePercentage * 10_000))
135
+ : options?.maxRoutingPPM ?? BigInt(this._options.lightningFeePPM);
136
+
137
+ let maxFee: bigint | Promise<bigint>;
138
+ if(options?.maxFee!=null) {
139
+ maxFee = options.maxFee;
140
+ } else if(amountData.exactIn) {
141
+ if(pricePreFetchPromise!=null) {
142
+ maxFee = pricePreFetchPromise
143
+ .then(val => this._prices.getFromBtcSwapAmount(this.chainIdentifier, maxRoutingBaseFee, amountData.token, abortSignal, val))
144
+ .then(_maxBaseFee => this.calculateFeeForAmount(amountData.amount, _maxBaseFee, maxRoutingPPM))
145
+ } else {
146
+ maxFee = this._prices.getFromBtcSwapAmount(this.chainIdentifier, maxRoutingBaseFee, amountData.token, abortSignal)
147
+ .then(_maxBaseFee => this.calculateFeeForAmount(amountData.amount, _maxBaseFee, maxRoutingPPM))
148
+ }
149
+ } else {
150
+ maxFee = this.calculateFeeForAmount(amountData.amount, maxRoutingBaseFee, maxRoutingPPM)
151
+ }
152
+
153
+ return {
154
+ expiryTimestamp: options?.expiryTimestamp ?? BigInt(Math.floor(Date.now()/1000)+expirySeconds),
155
+ maxFee
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Verifies whether a given payment hash was already paid by checking the local
161
+ * storage of known swaps
162
+ *
163
+ * @param paymentHash Payment hash to check
164
+ *
165
+ * @private
166
+ */
167
+ private async checkPaymentHashWasPaid(paymentHash: string) {
168
+ const swaps = await this.unifiedStorage.query(
169
+ [[{key: "type", value: this.TYPE}, {key: "paymentHash", value: paymentHash}]],
170
+ (obj: any) => new this._swapDeserializer(this, obj)
171
+ );
172
+
173
+ for(let value of swaps) {
174
+ if(value._state===ToBTCSwapState.CLAIMED || value._state===ToBTCSwapState.SOFT_CLAIMED)
175
+ throw new UserError("Lightning invoice was already paid!");
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Calculates maximum lightning network routing fee based on amount
181
+ *
182
+ * @param amount BTC amount of the swap in satoshis
183
+ * @param overrideBaseFee Override wrapper's default base fee
184
+ * @param overrideFeePPM Override wrapper's default PPM
185
+ *
186
+ * @returns Maximum lightning routing fee in sats
187
+ *
188
+ * @private
189
+ */
190
+ private calculateFeeForAmount(amount: bigint, overrideBaseFee?: bigint, overrideFeePPM?: bigint) : bigint {
191
+ return BigInt(overrideBaseFee ?? this._options.lightningBaseFee)
192
+ + (amount * BigInt(overrideFeePPM ?? this._options.lightningFeePPM) / 1000000n);
193
+ }
194
+
195
+ /**
196
+ * Verifies returned LP data
197
+ *
198
+ * @param signer
199
+ * @param resp Response as returned by the LP
200
+ * @param parsedPr Parsed bolt11 lightning invoice
201
+ * @param token Smart chain token to be used in the swap
202
+ * @param lp
203
+ * @param calculatedOptions Swap options computed from the swap create options
204
+ * @param data Parsed swap data returned by the LP
205
+ * @param requiredTotal Required total to be paid on the input (for exactIn swaps)
206
+ *
207
+ * @throws {IntermediaryError} In case the response is not valid
208
+ *
209
+ * @private
210
+ */
211
+ private async verifyReturnedData(
212
+ signer: string,
213
+ resp: ToBTCLNResponseType,
214
+ parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
215
+ token: string,
216
+ lp: Intermediary,
217
+ calculatedOptions: {
218
+ maxFee: bigint | Promise<bigint>,
219
+ expiryTimestamp: bigint
220
+ },
221
+ data: T["Data"],
222
+ requiredTotal?: bigint
223
+ ): Promise<void> {
224
+ if(resp.routingFeeSats > await calculatedOptions.maxFee) throw new IntermediaryError("Invalid max fee sats returned");
225
+
226
+ if(requiredTotal!=null && resp.total !== requiredTotal)
227
+ throw new IntermediaryError("Invalid data returned - total amount");
228
+
229
+ if(parsedPr.tagsObject.payment_hash==null) throw new Error("Swap invoice doesn't contain payment hash field!");
230
+ const claimHash = this._contract(lp.getContractVersion(this.chainIdentifier)).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash, "hex"));
231
+
232
+ if(
233
+ data.getAmount() !== resp.total ||
234
+ !Buffer.from(data.getClaimHash(), "hex").equals(claimHash) ||
235
+ data.getExpiry() !== calculatedOptions.expiryTimestamp ||
236
+ data.getType()!==ChainSwapType.HTLC ||
237
+ !data.isPayIn() ||
238
+ !data.isToken(token) ||
239
+ !data.isClaimer(lp.getAddress(this.chainIdentifier)) ||
240
+ !data.isOfferer(signer) ||
241
+ data.getTotalDeposit() !== 0n
242
+ ) {
243
+ throw new IntermediaryError("Invalid data returned");
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Returns the quote/swap from a given intermediary
249
+ *
250
+ * @param signer Smartchain signer initiating the swap
251
+ * @param amountData
252
+ * @param lp Intermediary
253
+ * @param pr bolt11 lightning network invoice
254
+ * @param parsedPr Parsed bolt11 lightning network invoice
255
+ * @param calculatedOptions Swap options computed from the swap create options
256
+ * @param preFetches
257
+ * @param abort Abort signal or controller, if AbortController is passed it is used as-is, when AbortSignal is passed
258
+ * it is extended with extendAbortController and then used
259
+ * @param additionalParams Additional params that should be sent to the LP
260
+ *
261
+ * @private
262
+ */
263
+ private async getIntermediaryQuote(
264
+ signer: string,
265
+ amountData: Omit<AmountData, "amount">,
266
+ lp: Intermediary,
267
+ pr: string,
268
+ parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
269
+ calculatedOptions: {
270
+ maxFee: bigint | Promise<bigint>,
271
+ expiryTimestamp: bigint
272
+ },
273
+ preFetches: {
274
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
275
+ pricePreFetchPromise: Promise<bigint | undefined>,
276
+ usdPricePrefetchPromise: Promise<number | undefined>,
277
+ signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
278
+ },
279
+ abort: AbortSignal | AbortController,
280
+ additionalParams?: Record<string, any>,
281
+ ) {
282
+ if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
283
+ const version = lp.getContractVersion(this.chainIdentifier);
284
+
285
+ const abortController = abort instanceof AbortController ? abort : extendAbortController(abort);
286
+ const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
287
+
288
+ try {
289
+ const {signDataPromise, resp} = await tryWithRetries(async(retryCount: number) => {
290
+ const {signDataPrefetch, response} = this._lpApi.initToBTCLN(this.chainIdentifier, lp.url, {
291
+ offerer: signer,
292
+ pr,
293
+ maxFee: await calculatedOptions.maxFee,
294
+ expiryTimestamp: calculatedOptions.expiryTimestamp,
295
+ token: amountData.token,
296
+ feeRate: throwIfUndefined(preFetches.feeRatePromise[version], "Network fee rate pre-fetch failed!"),
297
+ additionalParams
298
+ }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
299
+
300
+ let signDataPromise = preFetches.signDataPrefetchPromise?.[version];
301
+ if(signDataPromise==null) {
302
+ signDataPromise = this.preFetchSignData(signDataPrefetch, version);
303
+ } else signDataPrefetch.catch(() => {});
304
+
305
+ return {
306
+ signDataPromise,
307
+ resp: await response
308
+ };
309
+ }, undefined, e => e instanceof RequestError, abortController.signal);
310
+
311
+ if(parsedPr.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
312
+ const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
313
+ const totalFee: bigint = resp.swapFee + resp.maxFee;
314
+ const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
315
+ data.setOfferer(signer);
316
+
317
+ await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
318
+
319
+ const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
320
+
321
+ const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
322
+ this.verifyReturnedPrice(
323
+ lp.services[SwapType.TO_BTCLN], true, amountOut, data.getAmount(),
324
+ amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
325
+ preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal
326
+ ),
327
+ this.verifyReturnedSignature(
328
+ signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
329
+ ),
330
+ reputationPromise
331
+ ]);
332
+ abortController.signal.throwIfAborted();
333
+
334
+ if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
335
+
336
+ const quote = new ToBTCLNSwap<T>(this, {
337
+ pricingInfo,
338
+ url: lp.url,
339
+ expiry: signatureExpiry,
340
+ swapFee: resp.swapFee,
341
+ swapFeeBtc,
342
+ feeRate: (await preFetches.feeRatePromise[version])!,
343
+ signatureData: resp,
344
+ data,
345
+ networkFee: resp.maxFee,
346
+ networkFeeBtc: resp.routingFeeSats,
347
+ confidence: resp.confidence,
348
+ pr,
349
+ exactIn: false,
350
+ contractVersion: version
351
+ } as IToBTCSwapInit<T["Data"]>);
352
+ return quote;
353
+ } catch (e) {
354
+ abortController.abort(e);
355
+ throw e;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol,
361
+ * the amount is parsed from the provided lightning network payment request (bolt11 invoice)
362
+ *
363
+ * @param signer Source chain signer address initiating the swap
364
+ * @param recipient BOLT11 payment request (bitcoin lightning invoice) you wish to pay
365
+ * @param amountData Token to swap
366
+ * @param lps An array of intermediaries (LPs) to get the quotes from
367
+ * @param options Optional additional quote options
368
+ * @param additionalParams Optional additional parameters sent to the LP when creating the swap
369
+ * @param abortSignal Abort signal
370
+ * @param preFetches Optional existing pre-fetch promises for the swap (only used internally for LNURL swaps)
371
+ */
372
+ async create(
373
+ signer: string,
374
+ recipient: string,
375
+ amountData: Omit<AmountData, "amount"> & {exactIn: false},
376
+ lps: Intermediary[],
377
+ options?: ToBTCLNOptions,
378
+ additionalParams?: Record<string, any>,
379
+ abortSignal?: AbortSignal,
380
+ preFetches?: {
381
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
382
+ pricePreFetchPromise: Promise<bigint | undefined>,
383
+ usdPricePrefetchPromise: Promise<number | undefined>,
384
+ signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
385
+ }
386
+ ): Promise<{
387
+ quote: Promise<ToBTCLNSwap<T>>,
388
+ intermediary: Intermediary
389
+ }[]> {
390
+ const parsedPr = bolt11Decode(recipient);
391
+ if(parsedPr.millisatoshis==null) throw new UserError("Must be an invoice with amount");
392
+ const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
393
+
394
+ const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
395
+
396
+ const _options = this.toRequiredSwapOptions({...amountData, amount: amountOut}, options);
397
+
398
+ if(parsedPr.tagsObject.payment_hash==null) throw new Error("Provided lightning invoice doesn't contain payment hash field!");
399
+ await this.checkPaymentHashWasPaid(parsedPr.tagsObject.payment_hash);
400
+
401
+ const _hash = mapArrayToObject(lpVersions, (contractVersion: string) => {
402
+ return this._contract(contractVersion).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash!, "hex")).toString("hex");
403
+ });
404
+
405
+ const _abortController = extendAbortController(abortSignal);
406
+ const _preFetches = preFetches ?? {
407
+ pricePreFetchPromise: this.preFetchPrice(amountData, _abortController.signal),
408
+ feeRatePromise: this.preFetchFeeRate(signer, amountData, _hash, _abortController, lpVersions),
409
+ usdPricePrefetchPromise: this.preFetchUsdPrice(_abortController.signal),
410
+ signDataPrefetchPromise: mapArrayToObject(lpVersions, (contractVersion: string) => {
411
+ return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
412
+ this.preFetchSignData(Promise.resolve(true), contractVersion) :
413
+ undefined;
414
+ })
415
+ };
416
+
417
+ return lps.map(lp => {
418
+ return {
419
+ intermediary: lp,
420
+ quote: this.getIntermediaryQuote(signer, amountData, lp, recipient, parsedPr, _options, _preFetches, _abortController.signal, additionalParams)
421
+ }
422
+ });
423
+ }
424
+
425
+ /**
426
+ * Parses and fetches lnurl pay params from the specified lnurl
427
+ *
428
+ * @param lnurl LNURL to be parsed and fetched
429
+ * @param abortSignal Abort signal
430
+ * @throws {UserError} if the LNURL is invalid or if it's not a LNURL-pay
431
+ *
432
+ * @private
433
+ */
434
+ private async getLNURLPay(lnurl: string | LNURLPayParamsWithUrl, abortSignal: AbortSignal): Promise<LNURLPayParamsWithUrl> {
435
+ if(typeof(lnurl)!=="string") return lnurl;
436
+
437
+ const res = await LNURL.getLNURL(lnurl, true, this._options.getRequestTimeout, abortSignal);
438
+ if(res==null) throw new UserError("Invalid LNURL");
439
+ if(res.tag!=="payRequest") throw new UserError("Not a LNURL-pay");
440
+ return res;
441
+ }
442
+
443
+ /**
444
+ * Returns the quote/swap from the given LP
445
+ *
446
+ * @param signer Source chain signer address initiating the swap
447
+ * @param amountData Token to swap
448
+ * @param invoiceCreateService Service for creating fixed amount invoices
449
+ * @param lp Intermediary (LPs) to get the quote from
450
+ * @param dummyPr Dummy minimum value bolt11 lightning invoice returned from the LNURL-pay, used to estimate
451
+ * network fees for an actual invoice
452
+ * @param calculatedOptions Swap options computed from the swap create options
453
+ * @param preFetches Optional existing pre-fetch promises for the swap (only used internally for LNURL swaps)
454
+ * @param abortSignal Abort signal
455
+ * @param additionalParams Additional params to be sent to the intermediary
456
+ *
457
+ * @private
458
+ */
459
+ private async getIntermediaryQuoteExactIn(
460
+ signer: string,
461
+ amountData: AmountData,
462
+ invoiceCreateService: LightningInvoiceCreateService,
463
+ lp: Intermediary,
464
+ dummyPr: string,
465
+ calculatedOptions: {
466
+ maxFee: bigint | Promise<bigint>,
467
+ expiryTimestamp: bigint
468
+ },
469
+ preFetches: {
470
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
471
+ pricePreFetchPromise: Promise<bigint | undefined>,
472
+ usdPricePrefetchPromise: Promise<number | undefined>
473
+ },
474
+ abortSignal: AbortSignal,
475
+ additionalParams?: Record<string, any>,
476
+ ) {
477
+ if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
478
+ const version = lp.getContractVersion(this.chainIdentifier);
479
+
480
+ const abortController = extendAbortController(abortSignal);
481
+ const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
482
+
483
+ try {
484
+ const {signDataPromise, prepareResp} = await tryWithRetries(async(retryCount: number) => {
485
+ const {signDataPrefetch, response} = this._lpApi.prepareToBTCLNExactIn(this.chainIdentifier, lp.url, {
486
+ token: amountData.token,
487
+ offerer: signer,
488
+ pr: dummyPr,
489
+ amount: amountData.amount,
490
+ maxFee: await calculatedOptions.maxFee,
491
+ expiryTimestamp: calculatedOptions.expiryTimestamp,
492
+ additionalParams
493
+ }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
494
+
495
+ return {
496
+ signDataPromise: this.preFetchSignData(signDataPrefetch, version),
497
+ prepareResp: await response
498
+ };
499
+ }, undefined, e => e instanceof RequestError, abortController.signal);
500
+
501
+ if(prepareResp.amount <= 0n)
502
+ throw new IntermediaryError("Invalid amount returned (zero or negative)");
503
+
504
+ if(invoiceCreateService.minMsats!=null) {
505
+ if(prepareResp.amount < invoiceCreateService.minMsats / 1000n) throw new UserError("Amount less than minimum");
506
+ }
507
+ if(invoiceCreateService.maxMSats!=null) {
508
+ if(prepareResp.amount > invoiceCreateService.maxMSats / 1000n) throw new UserError("Amount more than maximum");
509
+ }
510
+
511
+ const invoice = await invoiceCreateService.getInvoice(Number(prepareResp.amount), abortController.signal);
512
+ const parsedInvoice = bolt11Decode(invoice);
513
+
514
+ const resp = await tryWithRetries(
515
+ (retryCount: number) => this._lpApi.initToBTCLNExactIn(lp.url, {
516
+ pr: invoice,
517
+ reqId: prepareResp.reqId,
518
+ feeRate: throwIfUndefined(preFetches.feeRatePromise[version], "Network fee rate pre-fetch failed!"),
519
+ additionalParams
520
+ }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined),
521
+ undefined, RequestError, abortController.signal
522
+ );
523
+
524
+ if(parsedInvoice.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
525
+ const amountOut: bigint = (BigInt(parsedInvoice.millisatoshis) + 999n) / 1000n;
526
+ const totalFee: bigint = resp.swapFee + resp.maxFee;
527
+ const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
528
+ data.setOfferer(signer);
529
+
530
+ await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
531
+
532
+ const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
533
+
534
+ const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
535
+ this.verifyReturnedPrice(
536
+ lp.services[SwapType.TO_BTCLN], true, prepareResp.amount, data.getAmount(),
537
+ amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
538
+ preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal
539
+ ),
540
+ this.verifyReturnedSignature(
541
+ signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
542
+ ),
543
+ reputationPromise
544
+ ]);
545
+ abortController.signal.throwIfAborted();
546
+
547
+ if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
548
+
549
+ const quote = new ToBTCLNSwap<T>(this, {
550
+ pricingInfo,
551
+ url: lp.url,
552
+ expiry: signatureExpiry,
553
+ swapFee: resp.swapFee,
554
+ swapFeeBtc,
555
+ feeRate: (await preFetches.feeRatePromise[version])!,
556
+ signatureData: resp,
557
+ data,
558
+ networkFee: resp.maxFee,
559
+ networkFeeBtc: resp.routingFeeSats,
560
+ confidence: resp.confidence,
561
+ pr: invoice,
562
+ exactIn: true,
563
+ contractVersion: version
564
+ } as IToBTCSwapInit<T["Data"]>);
565
+ return quote;
566
+ } catch (e) {
567
+ abortController.abort(e);
568
+ throw e;
569
+ }
570
+ }
571
+
572
+ /**
573
+ * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol via
574
+ * invoice creation service. This allows exactIn swaps by requesting the desired fixed amount lightning
575
+ * network invoice from the service.
576
+ *
577
+ * @param signer Source chain signer address initiating the swap
578
+ * @param invoiceCreateServicePromise Service to request destination lightning network invoices from
579
+ * @param amountData Amount, token and exact input/output data for to swap
580
+ * @param lps An array of intermediaries (LPs) to get the quotes from
581
+ * @param options Optional additional quote options
582
+ * @param additionalParams Optional additional parameters sent to the LP when creating the swap
583
+ * @param abortSignal Abort signal
584
+ */
585
+ async createViaInvoiceCreateService(
586
+ signer: string,
587
+ invoiceCreateServicePromise: Promise<LightningInvoiceCreateService>,
588
+ amountData: AmountData,
589
+ lps: Intermediary[],
590
+ options?: ToBTCLNOptions,
591
+ additionalParams?: Record<string, any>,
592
+ abortSignal?: AbortSignal
593
+ ): Promise<{
594
+ quote: Promise<ToBTCLNSwap<T>>,
595
+ intermediary: Intermediary
596
+ }[]> {
597
+ if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
598
+
599
+ const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
600
+
601
+ const _abortController = extendAbortController(abortSignal);
602
+ const pricePreFetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
603
+ const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
604
+ const feeRatePromise = this.preFetchFeeRate(signer, amountData, undefined, _abortController, lpVersions);
605
+ const signDataPrefetchPromise = mapArrayToObject(lpVersions, (contractVersion: string) => {
606
+ return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
607
+ this.preFetchSignData(Promise.resolve(true), contractVersion) :
608
+ undefined;
609
+ });
610
+
611
+ const _options = this.toRequiredSwapOptions(amountData, options, pricePreFetchPromise, _abortController.signal);
612
+
613
+ try {
614
+ const invoiceCreateService = await invoiceCreateServicePromise;
615
+
616
+ if(amountData.exactIn) {
617
+ const dummyInvoice = await invoiceCreateService.getInvoice(
618
+ invoiceCreateService.minMsats==null ? 1 : Number(invoiceCreateService.minMsats/1000n),
619
+ _abortController.signal
620
+ );
621
+
622
+ return lps.map(lp => {
623
+ return {
624
+ quote: this.getIntermediaryQuoteExactIn(signer, amountData, invoiceCreateService, lp, dummyInvoice, _options, {
625
+ pricePreFetchPromise,
626
+ usdPricePrefetchPromise,
627
+ feeRatePromise
628
+ }, _abortController.signal, additionalParams),
629
+ intermediary: lp
630
+ }
631
+ })
632
+ } else {
633
+ if(invoiceCreateService.minMsats!=null) {
634
+ if(amountData.amount < invoiceCreateService.minMsats / 1000n) throw new UserError("Amount less than minimum");
635
+ }
636
+ if(invoiceCreateService.maxMSats!=null) {
637
+ if(amountData.amount > invoiceCreateService.maxMSats / 1000n) throw new UserError("Amount more than maximum");
638
+ }
639
+
640
+ const invoice = await invoiceCreateService.getInvoice(Number(amountData.amount), _abortController.signal);
641
+
642
+ return (await this.create(signer, invoice, {...amountData, exactIn: false}, lps, options, additionalParams, _abortController.signal, {
643
+ feeRatePromise,
644
+ pricePreFetchPromise,
645
+ usdPricePrefetchPromise,
646
+ signDataPrefetchPromise
647
+ }));
648
+ }
649
+ } catch (e) {
650
+ _abortController.abort(e);
651
+ throw e;
652
+ }
653
+ }
654
+
655
+ /**
656
+ * Returns a newly created Smart chain -> Lightning swap using the HTLC based escrow swap protocol. Pays to
657
+ * an LNURL-pay link. This allows exactIn swaps by requesting the desired fixed amount lightning
658
+ * network invoice from the LNURL service.
659
+ *
660
+ * @param signer Source chain signer address initiating the swap
661
+ * @param lnurl LNURL-pay link of the recipient
662
+ * @param amountData Amount, token and exact input/output data for to swap
663
+ * @param lps An array of intermediaries (LPs) to get the quotes from
664
+ * @param options Optional additional quote options
665
+ * @param additionalParams Optional additional parameters sent to the LP when creating the swap
666
+ * @param abortSignal Abort signal
667
+ */
668
+ async createViaLNURL(
669
+ signer: string,
670
+ lnurl: string | LNURLPayParamsWithUrl,
671
+ amountData: AmountData,
672
+ lps: Intermediary[],
673
+ options?: ToBTCLNOptions & {comment?: string},
674
+ additionalParams?: Record<string, any>,
675
+ abortSignal?: AbortSignal
676
+ ): Promise<{
677
+ quote: Promise<ToBTCLNSwap<T>>,
678
+ intermediary: Intermediary
679
+ }[]> {
680
+ let successActions: {[pr: string]: LNURLPaySuccessAction} = {};
681
+
682
+ const _abortController = extendAbortController(abortSignal);
683
+ const invoiceCreateService = (async() => {
684
+ let payRequest: LNURLPayParamsWithUrl = await this.getLNURLPay(lnurl, _abortController.signal);
685
+
686
+ if(
687
+ options?.comment!=null &&
688
+ (payRequest.commentAllowed==null || options.comment.length>payRequest.commentAllowed)
689
+ ) throw new UserError("Comment not allowed or too long");
690
+
691
+ return {
692
+ getInvoice: async (amountSats: number, abortSignal?: AbortSignal) => {
693
+ const {invoice, successAction} = await LNURL.useLNURLPay(
694
+ payRequest, BigInt(amountSats), options?.comment,
695
+ this._options.getRequestTimeout, abortSignal
696
+ );
697
+ if(successAction!=null) successActions[invoice] = successAction;
698
+ return invoice;
699
+ },
700
+ minMsats: BigInt(payRequest.minSendable),
701
+ maxMsats: BigInt(payRequest.maxSendable),
702
+ url: payRequest.url
703
+ }
704
+ })();
705
+
706
+ const quotes = await this.createViaInvoiceCreateService(
707
+ signer,
708
+ invoiceCreateService,
709
+ amountData,
710
+ lps,
711
+ options,
712
+ additionalParams,
713
+ _abortController.signal
714
+ );
715
+ _abortController.signal.throwIfAborted();
716
+
717
+ const resolved = await invoiceCreateService;
718
+ _abortController.signal.throwIfAborted();
719
+
720
+ return quotes.map(value => ({
721
+ quote: value.quote.then(quote => {
722
+ let _successAction: LNURLPaySuccessAction | undefined;
723
+ const quoteAddress = quote.getOutputAddress();
724
+ if(quoteAddress!=null) {
725
+ const successAction = successActions[quoteAddress];
726
+ if(successAction!=null) _successAction = successAction;
727
+ }
728
+ quote._setLNURLData(resolved.url, _successAction);
729
+ return quote;
730
+ }),
731
+ intermediary: value.intermediary
732
+ }));
733
+ }
734
+
735
+ /**
736
+ * @inheritDoc
737
+ */
738
+ async recoverFromSwapDataAndState(
739
+ init: {data: T["Data"], getInitTxId: () => Promise<string>, getTxBlock: () => Promise<{blockTime: number, blockHeight: number}>},
740
+ state: SwapCommitState,
741
+ contractVersion: string,
742
+ lp?: Intermediary
743
+ ): Promise<ToBTCLNSwap<T> | null> {
744
+ const data = init.data;
745
+
746
+ let paymentHash = data.getHTLCHashHint();
747
+ let secret: string | undefined;
748
+ if(state.type===SwapCommitStateType.PAID) {
749
+ secret = await state.getClaimResult();
750
+ paymentHash = Buffer.from(sha256(Buffer.from(secret, "hex"))).toString("hex");
751
+ }
752
+
753
+ const swapInit: ToBTCLNSwapInit<T["Data"]> = {
754
+ pricingInfo: {
755
+ isValid: true,
756
+ satsBaseFee: 0n,
757
+ swapPriceUSatPerToken: 100_000_000_000_000n,
758
+ realPriceUSatPerToken: 100_000_000_000_000n,
759
+ differencePPM: 0n,
760
+ feePPM: 0n,
761
+ },
762
+ url: lp?.url,
763
+ expiry: 0,
764
+ swapFee: 0n,
765
+ swapFeeBtc: 0n,
766
+ feeRate: "",
767
+ signatureData: undefined,
768
+ data,
769
+ networkFee: 0n,
770
+ networkFeeBtc: 0n,
771
+ confidence: 0,
772
+ pr: paymentHash ?? undefined,
773
+ exactIn: false,
774
+ contractVersion
775
+ };
776
+ const swap = new ToBTCLNSwap(this, swapInit);
777
+ swap._commitTxId = await init.getInitTxId();
778
+ const blockData = await init.getTxBlock();
779
+ swap.createdAt = blockData.blockTime * 1000;
780
+ swap._setInitiated();
781
+ swap._state = ToBTCSwapState.COMMITED;
782
+ await swap._sync(false, false, state);
783
+ await swap._save();
784
+ return swap;
785
+ }
786
+
787
+ }