@atomiqlabs/sdk 7.0.12 → 8.0.8

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 (364) hide show
  1. package/README.md +45 -29
  2. package/dist/SmartChainAssets.d.ts +11 -3
  3. package/dist/SmartChainAssets.js +7 -3
  4. package/dist/bitcoin/BitcoinRpcWithAddressIndex.d.ts +68 -0
  5. package/dist/bitcoin/BitcoinRpcWithAddressIndex.js +2 -0
  6. package/dist/bitcoin/LightningNetworkApi.d.ts +12 -0
  7. package/dist/bitcoin/LightningNetworkApi.js +2 -0
  8. package/dist/bitcoin/coinselect2/accumulative.d.ts +6 -0
  9. package/dist/bitcoin/coinselect2/accumulative.js +52 -0
  10. package/dist/bitcoin/coinselect2/blackjack.d.ts +6 -0
  11. package/dist/bitcoin/coinselect2/blackjack.js +38 -0
  12. package/dist/bitcoin/coinselect2/index.d.ts +19 -0
  13. package/dist/bitcoin/coinselect2/index.js +69 -0
  14. package/dist/bitcoin/coinselect2/utils.d.ts +71 -0
  15. package/dist/bitcoin/coinselect2/utils.js +123 -0
  16. package/dist/bitcoin/mempool/MempoolApi.d.ts +350 -0
  17. package/dist/bitcoin/mempool/MempoolApi.js +311 -0
  18. package/dist/bitcoin/mempool/MempoolBitcoinBlock.d.ts +44 -0
  19. package/dist/bitcoin/mempool/MempoolBitcoinBlock.js +48 -0
  20. package/dist/bitcoin/mempool/MempoolBitcoinRpc.d.ts +119 -0
  21. package/dist/bitcoin/mempool/MempoolBitcoinRpc.js +361 -0
  22. package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.d.ts +22 -0
  23. package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.js +105 -0
  24. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +93 -0
  25. package/dist/bitcoin/wallet/BitcoinWallet.js +273 -0
  26. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +28 -0
  27. package/dist/bitcoin/wallet/IBitcoinWallet.js +20 -0
  28. package/dist/bitcoin/wallet/MinimalBitcoinWalletInterface.d.ts +21 -0
  29. package/dist/bitcoin/wallet/MinimalBitcoinWalletInterface.js +2 -0
  30. package/dist/bitcoin/wallet/MinimalLightningNetworkWalletInterface.d.ts +7 -0
  31. package/dist/bitcoin/wallet/MinimalLightningNetworkWalletInterface.js +2 -0
  32. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +40 -0
  33. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +86 -0
  34. package/dist/enums/FeeType.d.ts +8 -0
  35. package/dist/enums/FeeType.js +12 -0
  36. package/dist/enums/SwapAmountType.d.ts +8 -0
  37. package/dist/enums/SwapAmountType.js +12 -0
  38. package/dist/enums/SwapDirection.d.ts +8 -0
  39. package/dist/enums/SwapDirection.js +12 -0
  40. package/dist/enums/SwapType.d.ts +14 -0
  41. package/dist/enums/SwapType.js +18 -0
  42. package/dist/errors/IntermediaryError.d.ts +9 -0
  43. package/dist/errors/IntermediaryError.js +26 -0
  44. package/dist/errors/PaymentAuthError.d.ts +11 -0
  45. package/dist/errors/PaymentAuthError.js +23 -0
  46. package/dist/errors/RequestError.d.ts +18 -0
  47. package/dist/errors/RequestError.js +46 -0
  48. package/dist/errors/UserError.d.ts +7 -0
  49. package/dist/errors/UserError.js +15 -0
  50. package/dist/events/UnifiedSwapEventListener.d.ts +23 -0
  51. package/dist/events/UnifiedSwapEventListener.js +130 -0
  52. package/dist/http/HttpUtils.d.ts +27 -0
  53. package/dist/http/HttpUtils.js +91 -0
  54. package/dist/http/paramcoders/IParamReader.d.ts +8 -0
  55. package/dist/http/paramcoders/IParamReader.js +2 -0
  56. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -0
  57. package/dist/http/paramcoders/ParamDecoder.js +132 -0
  58. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -0
  59. package/dist/http/paramcoders/ParamEncoder.js +31 -0
  60. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -0
  61. package/dist/http/paramcoders/SchemaVerifier.js +145 -0
  62. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -0
  63. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -0
  64. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -0
  65. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -0
  66. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -0
  67. package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -0
  68. package/dist/index.d.ts +82 -4
  69. package/dist/index.js +128 -4
  70. package/dist/intermediaries/Intermediary.d.ts +111 -0
  71. package/dist/intermediaries/Intermediary.js +115 -0
  72. package/dist/intermediaries/IntermediaryDiscovery.d.ts +166 -0
  73. package/dist/intermediaries/IntermediaryDiscovery.js +390 -0
  74. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +436 -0
  75. package/dist/intermediaries/apis/IntermediaryAPI.js +600 -0
  76. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +154 -0
  77. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +136 -0
  78. package/dist/lnurl/LNURL.d.ts +102 -0
  79. package/dist/lnurl/LNURL.js +321 -0
  80. package/dist/prices/RedundantSwapPrice.d.ts +89 -0
  81. package/dist/prices/RedundantSwapPrice.js +202 -0
  82. package/dist/prices/SingleSwapPrice.d.ts +31 -0
  83. package/dist/prices/SingleSwapPrice.js +41 -0
  84. package/dist/prices/SwapPriceWithChain.d.ts +70 -0
  85. package/dist/prices/SwapPriceWithChain.js +91 -0
  86. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -0
  87. package/dist/prices/abstract/ICachedSwapPrice.js +62 -0
  88. package/dist/prices/abstract/IPriceProvider.d.ts +81 -0
  89. package/dist/prices/abstract/IPriceProvider.js +74 -0
  90. package/dist/prices/abstract/ISwapPrice.d.ts +117 -0
  91. package/dist/prices/abstract/ISwapPrice.js +219 -0
  92. package/dist/prices/providers/BinancePriceProvider.d.ts +16 -0
  93. package/dist/prices/providers/BinancePriceProvider.js +23 -0
  94. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +17 -0
  95. package/dist/prices/providers/CoinGeckoPriceProvider.js +23 -0
  96. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +19 -0
  97. package/dist/prices/providers/CoinPaprikaPriceProvider.js +23 -0
  98. package/dist/prices/providers/CustomPriceProvider.d.ts +13 -0
  99. package/dist/prices/providers/CustomPriceProvider.js +24 -0
  100. package/dist/prices/providers/KrakenPriceProvider.d.ts +29 -0
  101. package/dist/prices/providers/KrakenPriceProvider.js +36 -0
  102. package/dist/prices/providers/OKXPriceProvider.d.ts +28 -0
  103. package/dist/prices/providers/OKXPriceProvider.js +23 -0
  104. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +14 -0
  105. package/dist/prices/providers/abstract/ExchangePriceProvider.js +18 -0
  106. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -0
  107. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -0
  108. package/dist/storage/IUnifiedStorage.d.ts +73 -0
  109. package/dist/storage/IUnifiedStorage.js +2 -0
  110. package/dist/storage/UnifiedSwapStorage.d.ts +82 -0
  111. package/dist/storage/UnifiedSwapStorage.js +83 -0
  112. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +39 -0
  113. package/dist/storage-browser/IndexedDBUnifiedStorage.js +275 -0
  114. package/dist/{storage → storage-browser}/LocalStorageManager.d.ts +1 -0
  115. package/dist/{storage → storage-browser}/LocalStorageManager.js +2 -1
  116. package/dist/swapper/Swapper.d.ts +533 -0
  117. package/dist/swapper/Swapper.js +1566 -0
  118. package/dist/swapper/SwapperFactory.d.ts +87 -0
  119. package/dist/{SwapperFactory.js → swapper/SwapperFactory.js} +37 -19
  120. package/dist/swapper/SwapperUtils.d.ts +153 -0
  121. package/dist/swapper/SwapperUtils.js +420 -0
  122. package/dist/swapper/SwapperWithChain.d.ts +214 -0
  123. package/dist/swapper/SwapperWithChain.js +315 -0
  124. package/dist/swapper/SwapperWithSigner.d.ts +178 -0
  125. package/dist/swapper/SwapperWithSigner.js +172 -0
  126. package/dist/swaps/IAddressSwap.d.ts +13 -0
  127. package/dist/swaps/IAddressSwap.js +13 -0
  128. package/dist/swaps/IBTCWalletSwap.d.ts +55 -0
  129. package/dist/swaps/IBTCWalletSwap.js +17 -0
  130. package/dist/swaps/IClaimableSwap.d.ts +17 -0
  131. package/dist/swaps/IClaimableSwap.js +14 -0
  132. package/dist/swaps/IClaimableSwapWrapper.d.ts +5 -0
  133. package/dist/swaps/IClaimableSwapWrapper.js +2 -0
  134. package/dist/swaps/IRefundableSwap.d.ts +17 -0
  135. package/dist/swaps/IRefundableSwap.js +13 -0
  136. package/dist/swaps/ISwap.d.ts +207 -0
  137. package/dist/swaps/ISwap.js +264 -0
  138. package/dist/swaps/ISwapWithGasDrop.d.ts +15 -0
  139. package/dist/swaps/ISwapWithGasDrop.js +11 -0
  140. package/dist/swaps/ISwapWrapper.d.ts +153 -0
  141. package/dist/swaps/ISwapWrapper.js +227 -0
  142. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +53 -0
  143. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +116 -0
  144. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +70 -0
  145. package/dist/swaps/escrow_swaps/IEscrowSwap.js +132 -0
  146. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +85 -0
  147. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +122 -0
  148. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +86 -0
  149. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +115 -0
  150. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +93 -0
  151. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +121 -0
  152. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +45 -0
  153. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +65 -0
  154. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +263 -0
  155. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +933 -0
  156. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +110 -0
  157. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +307 -0
  158. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +236 -0
  159. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +898 -0
  160. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +125 -0
  161. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +393 -0
  162. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +245 -0
  163. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +841 -0
  164. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +120 -0
  165. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +294 -0
  166. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +228 -0
  167. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +721 -0
  168. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +37 -0
  169. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +93 -0
  170. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +86 -0
  171. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +213 -0
  172. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +170 -0
  173. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +520 -0
  174. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +50 -0
  175. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +109 -0
  176. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +93 -0
  177. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +217 -0
  178. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +315 -0
  179. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1098 -0
  180. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +125 -0
  181. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +631 -0
  182. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +107 -0
  183. package/dist/swaps/trusted/ln/LnForGasSwap.js +343 -0
  184. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +21 -0
  185. package/dist/swaps/trusted/ln/LnForGasWrapper.js +62 -0
  186. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +164 -0
  187. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +520 -0
  188. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +48 -0
  189. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +74 -0
  190. package/dist/types/AmountData.d.ts +9 -0
  191. package/dist/types/AmountData.js +2 -0
  192. package/dist/types/CustomPriceFunction.d.ts +5 -0
  193. package/dist/types/CustomPriceFunction.js +2 -0
  194. package/dist/types/PriceInfoType.d.ts +28 -0
  195. package/dist/types/PriceInfoType.js +57 -0
  196. package/dist/types/SwapExecutionAction.d.ts +7 -0
  197. package/dist/types/SwapExecutionAction.js +2 -0
  198. package/dist/types/SwapWithSigner.d.ts +14 -0
  199. package/dist/types/SwapWithSigner.js +40 -0
  200. package/dist/types/Token.d.ts +53 -0
  201. package/dist/types/Token.js +58 -0
  202. package/dist/types/TokenAmount.d.ts +57 -0
  203. package/dist/types/TokenAmount.js +47 -0
  204. package/dist/types/fees/Fee.d.ts +49 -0
  205. package/dist/types/fees/Fee.js +2 -0
  206. package/dist/types/fees/FeeBreakdown.d.ts +10 -0
  207. package/dist/types/fees/FeeBreakdown.js +2 -0
  208. package/dist/types/fees/PercentagePPM.d.ts +15 -0
  209. package/dist/types/fees/PercentagePPM.js +17 -0
  210. package/dist/types/lnurl/LNURLPay.d.ts +54 -0
  211. package/dist/types/lnurl/LNURLPay.js +28 -0
  212. package/dist/types/lnurl/LNURLWithdraw.d.ts +42 -0
  213. package/dist/types/lnurl/LNURLWithdraw.js +24 -0
  214. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -0
  215. package/dist/utils/AutomaticClockDriftCorrection.js +70 -0
  216. package/dist/utils/BitcoinUtils.d.ts +13 -0
  217. package/dist/utils/BitcoinUtils.js +98 -0
  218. package/dist/utils/BitcoinWalletUtils.d.ts +7 -0
  219. package/dist/utils/BitcoinWalletUtils.js +14 -0
  220. package/dist/utils/Logger.d.ts +7 -0
  221. package/dist/utils/Logger.js +12 -0
  222. package/dist/utils/RetryUtils.d.ts +21 -0
  223. package/dist/utils/RetryUtils.js +66 -0
  224. package/dist/utils/SwapUtils.d.ts +31 -0
  225. package/dist/utils/SwapUtils.js +18 -0
  226. package/dist/{Utils.d.ts → utils/TimeoutUtils.d.ts} +9 -3
  227. package/dist/utils/TimeoutUtils.js +55 -0
  228. package/dist/utils/TokenUtils.d.ts +11 -0
  229. package/dist/utils/TokenUtils.js +29 -0
  230. package/dist/utils/TypeUtils.d.ts +7 -0
  231. package/dist/utils/TypeUtils.js +2 -0
  232. package/dist/utils/Utils.d.ts +57 -0
  233. package/dist/utils/Utils.js +178 -0
  234. package/package.json +14 -6
  235. package/src/SmartChainAssets.ts +11 -3
  236. package/src/bitcoin/BitcoinRpcWithAddressIndex.ts +87 -0
  237. package/src/bitcoin/LightningNetworkApi.ts +16 -0
  238. package/src/bitcoin/coinselect2/accumulative.ts +68 -0
  239. package/src/bitcoin/coinselect2/blackjack.ts +49 -0
  240. package/src/bitcoin/coinselect2/index.ts +92 -0
  241. package/src/bitcoin/coinselect2/utils.ts +189 -0
  242. package/src/bitcoin/mempool/MempoolApi.ts +554 -0
  243. package/src/bitcoin/mempool/MempoolBitcoinBlock.ts +88 -0
  244. package/src/bitcoin/mempool/MempoolBitcoinRpc.ts +437 -0
  245. package/src/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.ts +134 -0
  246. package/src/bitcoin/wallet/BitcoinWallet.ts +375 -0
  247. package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -0
  248. package/src/bitcoin/wallet/MinimalBitcoinWalletInterface.ts +19 -0
  249. package/src/bitcoin/wallet/MinimalLightningNetworkWalletInterface.ts +7 -0
  250. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +108 -0
  251. package/src/enums/FeeType.ts +9 -0
  252. package/src/enums/SwapAmountType.ts +9 -0
  253. package/src/enums/SwapDirection.ts +9 -0
  254. package/src/enums/SwapType.ts +15 -0
  255. package/src/errors/IntermediaryError.ts +24 -0
  256. package/src/errors/PaymentAuthError.ts +26 -0
  257. package/src/errors/RequestError.ts +51 -0
  258. package/src/errors/UserError.ts +14 -0
  259. package/src/events/UnifiedSwapEventListener.ts +171 -0
  260. package/src/http/HttpUtils.ts +92 -0
  261. package/src/http/paramcoders/IParamReader.ts +10 -0
  262. package/src/http/paramcoders/ParamDecoder.ts +142 -0
  263. package/src/http/paramcoders/ParamEncoder.ts +37 -0
  264. package/src/http/paramcoders/SchemaVerifier.ts +153 -0
  265. package/src/http/paramcoders/client/ResponseParamDecoder.ts +58 -0
  266. package/src/http/paramcoders/client/StreamParamEncoder.ts +29 -0
  267. package/src/http/paramcoders/client/StreamingFetchPromise.ts +193 -0
  268. package/src/index.ts +102 -4
  269. package/src/intermediaries/Intermediary.ts +204 -0
  270. package/src/intermediaries/IntermediaryDiscovery.ts +485 -0
  271. package/src/intermediaries/apis/IntermediaryAPI.ts +940 -0
  272. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -0
  273. package/src/lnurl/LNURL.ts +403 -0
  274. package/src/prices/RedundantSwapPrice.ts +245 -0
  275. package/src/prices/SingleSwapPrice.ts +47 -0
  276. package/src/prices/SwapPriceWithChain.ts +157 -0
  277. package/src/prices/abstract/ICachedSwapPrice.ts +86 -0
  278. package/src/prices/abstract/IPriceProvider.ts +128 -0
  279. package/src/prices/abstract/ISwapPrice.ts +328 -0
  280. package/src/prices/providers/BinancePriceProvider.ts +41 -0
  281. package/src/prices/providers/CoinGeckoPriceProvider.ts +40 -0
  282. package/src/prices/providers/CoinPaprikaPriceProvider.ts +44 -0
  283. package/src/prices/providers/CustomPriceProvider.ts +29 -0
  284. package/src/prices/providers/KrakenPriceProvider.ts +74 -0
  285. package/src/prices/providers/OKXPriceProvider.ts +53 -0
  286. package/src/prices/providers/abstract/ExchangePriceProvider.ts +29 -0
  287. package/src/prices/providers/abstract/HttpPriceProvider.ts +15 -0
  288. package/src/storage/IUnifiedStorage.ts +83 -0
  289. package/src/storage/UnifiedSwapStorage.ts +104 -0
  290. package/src/storage-browser/IndexedDBUnifiedStorage.ts +328 -0
  291. package/src/{storage → storage-browser}/LocalStorageManager.ts +2 -1
  292. package/src/swapper/Swapper.ts +2107 -0
  293. package/src/{SwapperFactory.ts → swapper/SwapperFactory.ts} +113 -72
  294. package/src/swapper/SwapperUtils.ts +510 -0
  295. package/src/swapper/SwapperWithChain.ts +464 -0
  296. package/src/swapper/SwapperWithSigner.ts +300 -0
  297. package/src/swaps/IAddressSwap.ts +20 -0
  298. package/src/swaps/IBTCWalletSwap.ts +77 -0
  299. package/src/swaps/IClaimableSwap.ts +30 -0
  300. package/src/swaps/IClaimableSwapWrapper.ts +9 -0
  301. package/src/swaps/IRefundableSwap.ts +29 -0
  302. package/src/swaps/ISwap.ts +490 -0
  303. package/src/swaps/ISwapWithGasDrop.ts +19 -0
  304. package/src/swaps/ISwapWrapper.ts +344 -0
  305. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +168 -0
  306. package/src/swaps/escrow_swaps/IEscrowSwap.ts +197 -0
  307. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +210 -0
  308. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +150 -0
  309. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +219 -0
  310. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +84 -0
  311. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1082 -0
  312. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +429 -0
  313. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1078 -0
  314. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +549 -0
  315. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +974 -0
  316. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +443 -0
  317. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +860 -0
  318. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +104 -0
  319. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +256 -0
  320. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +716 -0
  321. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +151 -0
  322. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +299 -0
  323. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1394 -0
  324. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +796 -0
  325. package/src/swaps/trusted/ln/LnForGasSwap.ts +402 -0
  326. package/src/swaps/trusted/ln/LnForGasWrapper.ts +70 -0
  327. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +633 -0
  328. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +110 -0
  329. package/src/types/AmountData.ts +9 -0
  330. package/src/types/CustomPriceFunction.ts +5 -0
  331. package/src/types/PriceInfoType.ts +67 -0
  332. package/src/types/SwapExecutionAction.ts +8 -0
  333. package/src/types/SwapWithSigner.ts +57 -0
  334. package/src/types/Token.ts +90 -0
  335. package/src/types/TokenAmount.ts +110 -0
  336. package/src/types/fees/Fee.ts +55 -0
  337. package/src/types/fees/FeeBreakdown.ts +11 -0
  338. package/src/types/fees/PercentagePPM.ts +24 -0
  339. package/src/types/lnurl/LNURLPay.ts +72 -0
  340. package/src/types/lnurl/LNURLWithdraw.ts +55 -0
  341. package/src/utils/AutomaticClockDriftCorrection.ts +71 -0
  342. package/src/utils/BitcoinUtils.ts +86 -0
  343. package/src/utils/BitcoinWalletUtils.ts +16 -0
  344. package/src/utils/Logger.ts +15 -0
  345. package/src/utils/RetryUtils.ts +71 -0
  346. package/src/utils/SwapUtils.ts +38 -0
  347. package/src/utils/TimeoutUtils.ts +50 -0
  348. package/src/utils/TokenUtils.ts +25 -0
  349. package/src/utils/TypeUtils.ts +9 -0
  350. package/src/utils/Utils.ts +182 -0
  351. package/dist/SwapperFactory.d.ts +0 -52
  352. package/dist/Utils.js +0 -37
  353. package/dist/fs-storage/FileSystemStorageManager.d.ts +0 -15
  354. package/dist/fs-storage/FileSystemStorageManager.js +0 -60
  355. package/dist/fs-storage/index.d.ts +0 -1
  356. package/dist/fs-storage/index.js +0 -17
  357. package/src/SmartChainAssets.js +0 -75
  358. package/src/SwapperFactory.js +0 -120
  359. package/src/Utils.js +0 -37
  360. package/src/Utils.ts +0 -31
  361. package/src/fs-storage/FileSystemStorageManager.ts +0 -71
  362. package/src/fs-storage/index.ts +0 -1
  363. package/src/index.js +0 -21
  364. package/src/storage/LocalStorageManager.js +0 -72
@@ -0,0 +1,796 @@
1
+ import {ISwapWrapper, ISwapWrapperOptions, SwapTypeDefinition, WrapperCtorTokens} from "../ISwapWrapper";
2
+ import {
3
+ BtcRelay,
4
+ ChainEvent,
5
+ ChainType,
6
+ RelaySynchronizer,
7
+ SpvVaultClaimEvent,
8
+ SpvVaultCloseEvent,
9
+ SpvVaultFrontEvent,
10
+ SpvVaultTokenBalance,
11
+ SpvWithdrawalStateType
12
+ } from "@atomiqlabs/base";
13
+ import {SpvFromBTCSwap, SpvFromBTCSwapInit, SpvFromBTCSwapState} from "./SpvFromBTCSwap";
14
+ import {BTC_NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils";
15
+ import {SwapType} from "../../enums/SwapType";
16
+ import {BitcoinRpcWithAddressIndex} from "../../bitcoin/BitcoinRpcWithAddressIndex";
17
+ import {UnifiedSwapStorage} from "../../storage/UnifiedSwapStorage";
18
+ import {UnifiedSwapEventListener} from "../../events/UnifiedSwapEventListener";
19
+ import {ISwapPrice} from "../../prices/abstract/ISwapPrice";
20
+ import {EventEmitter} from "events";
21
+ import {Intermediary} from "../../intermediaries/Intermediary";
22
+ import {extendAbortController, randomBytes, throwIfUndefined} from "../../utils/Utils";
23
+ import {toCoinselectAddressType, toOutputScript} from "../../utils/BitcoinUtils";
24
+ import {IntermediaryAPI, SpvFromBTCPrepareResponseType} from "../../intermediaries/apis/IntermediaryAPI";
25
+ import {RequestError} from "../../errors/RequestError";
26
+ import {IntermediaryError} from "../../errors/IntermediaryError";
27
+ import {CoinselectAddressTypes} from "../../bitcoin/coinselect2";
28
+ import {OutScript, Transaction} from "@scure/btc-signer";
29
+ import {ISwap} from "../ISwap";
30
+ import {IClaimableSwapWrapper} from "../IClaimableSwapWrapper";
31
+ import {AmountData} from "../../types/AmountData";
32
+ import {tryWithRetries} from "../../utils/RetryUtils";
33
+ import {AllOptional, AllRequired} from "../../utils/TypeUtils";
34
+
35
+ export type SpvFromBTCOptions = {
36
+ gasAmount?: bigint,
37
+ unsafeZeroWatchtowerFee?: boolean,
38
+ feeSafetyFactor?: number,
39
+ maxAllowedNetworkFeeRate?: number
40
+ };
41
+
42
+ export type SpvFromBTCWrapperOptions = ISwapWrapperOptions & {
43
+ maxConfirmations: number,
44
+ bitcoinNetwork: BTC_NETWORK,
45
+ bitcoinBlocktime: number,
46
+ maxTransactionsDelta: number, //Maximum accepted difference in state between SC state and bitcoin state, in terms of by how many transactions are they differing
47
+ maxRawAmountAdjustmentDifferencePPM: number,
48
+ maxBtcFeeMultiplier: number,
49
+ maxBtcFeeOffset: number
50
+ };
51
+
52
+ export type SpvFromBTCTypeDefinition<T extends ChainType> = SwapTypeDefinition<T, SpvFromBTCWrapper<T>, SpvFromBTCSwap<T>>;
53
+
54
+ export class SpvFromBTCWrapper<
55
+ T extends ChainType
56
+ > extends ISwapWrapper<T, SpvFromBTCTypeDefinition<T>, SpvFromBTCWrapperOptions> implements IClaimableSwapWrapper<SpvFromBTCSwap<T>> {
57
+
58
+ public readonly claimableSwapStates = [SpvFromBTCSwapState.BTC_TX_CONFIRMED];
59
+ public readonly TYPE = SwapType.SPV_VAULT_FROM_BTC;
60
+ public readonly swapDeserializer = SpvFromBTCSwap;
61
+
62
+ readonly synchronizer: RelaySynchronizer<any, T["TX"], any>;
63
+ readonly contract: T["SpvVaultContract"];
64
+ readonly btcRelay: T["BtcRelay"];
65
+ readonly btcRpc: BitcoinRpcWithAddressIndex<any>;
66
+
67
+ readonly spvWithdrawalDataDeserializer: new (data: any) => T["SpvVaultWithdrawalData"];
68
+
69
+ /**
70
+ * @param chainIdentifier
71
+ * @param unifiedStorage Storage interface for the current environment
72
+ * @param unifiedChainEvents On-chain event listener
73
+ * @param chain
74
+ * @param contract Underlying contract handling the swaps
75
+ * @param prices Pricing to use
76
+ * @param tokens
77
+ * @param spvWithdrawalDataDeserializer Deserializer for SpvVaultWithdrawalData
78
+ * @param btcRelay
79
+ * @param synchronizer Btc relay synchronizer
80
+ * @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
81
+ * @param options
82
+ * @param events Instance to use for emitting events
83
+ */
84
+ constructor(
85
+ chainIdentifier: string,
86
+ unifiedStorage: UnifiedSwapStorage<T>,
87
+ unifiedChainEvents: UnifiedSwapEventListener<T>,
88
+ chain: T["ChainInterface"],
89
+ contract: T["SpvVaultContract"],
90
+ prices: ISwapPrice,
91
+ tokens: WrapperCtorTokens,
92
+ spvWithdrawalDataDeserializer: new (data: any) => T["SpvVaultWithdrawalData"],
93
+ btcRelay: BtcRelay<any, T["TX"], any>,
94
+ synchronizer: RelaySynchronizer<any, T["TX"], any>,
95
+ btcRpc: BitcoinRpcWithAddressIndex<any>,
96
+ options?: AllOptional<SpvFromBTCWrapperOptions>,
97
+ events?: EventEmitter<{swapState: [ISwap]}>
98
+ ) {
99
+ if(options==null) options = {};
100
+ options.bitcoinNetwork ??= TEST_NETWORK;
101
+ options.maxConfirmations ??= 6;
102
+ options.bitcoinBlocktime ??= 10*60;
103
+ options.maxTransactionsDelta ??= 3;
104
+ options.maxRawAmountAdjustmentDifferencePPM ??= 100;
105
+ options.maxBtcFeeOffset ??= 5;
106
+ options.maxBtcFeeMultiplier ??= 1.5;
107
+ super(
108
+ chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens,
109
+ {
110
+ bitcoinNetwork: options.bitcoinNetwork ?? TEST_NETWORK,
111
+ maxConfirmations: options.maxConfirmations ?? 6,
112
+ bitcoinBlocktime: options.bitcoinBlocktime ?? 10*60,
113
+ maxTransactionsDelta: options.maxTransactionsDelta ?? 3,
114
+ maxRawAmountAdjustmentDifferencePPM: options.maxRawAmountAdjustmentDifferencePPM ?? 100,
115
+ maxBtcFeeOffset: options.maxBtcFeeOffset ?? 5,
116
+ maxBtcFeeMultiplier: options.maxBtcFeeMultiplier ?? 1.5
117
+ },
118
+ events
119
+ );
120
+ this.spvWithdrawalDataDeserializer = spvWithdrawalDataDeserializer;
121
+ this.contract = contract;
122
+ this.btcRelay = btcRelay;
123
+ this.synchronizer = synchronizer;
124
+ this.btcRpc = btcRpc;
125
+ }
126
+
127
+ readonly pendingSwapStates: Array<SpvFromBTCSwap<T>["state"]> = [
128
+ SpvFromBTCSwapState.CREATED,
129
+ SpvFromBTCSwapState.SIGNED,
130
+ SpvFromBTCSwapState.POSTED,
131
+ SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
132
+ SpvFromBTCSwapState.BROADCASTED,
133
+ SpvFromBTCSwapState.DECLINED,
134
+ SpvFromBTCSwapState.BTC_TX_CONFIRMED
135
+ ];
136
+ readonly tickSwapState: Array<SpvFromBTCSwap<T>["state"]> = [
137
+ SpvFromBTCSwapState.CREATED,
138
+ SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
139
+ SpvFromBTCSwapState.SIGNED,
140
+ SpvFromBTCSwapState.POSTED,
141
+ SpvFromBTCSwapState.BROADCASTED
142
+ ];
143
+
144
+ protected async processEventFront(event: SpvVaultFrontEvent, swap: SpvFromBTCSwap<T>): Promise<boolean> {
145
+ if(
146
+ swap.state===SpvFromBTCSwapState.SIGNED || swap.state===SpvFromBTCSwapState.POSTED ||
147
+ swap.state===SpvFromBTCSwapState.BROADCASTED || swap.state===SpvFromBTCSwapState.DECLINED ||
148
+ swap.state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap.state===SpvFromBTCSwapState.BTC_TX_CONFIRMED
149
+ ) {
150
+ await swap._setBitcoinTxId(event.btcTxId).catch(e => {
151
+ this.logger.warn("processEventFront(): Failed to set bitcoin txId: ", e);
152
+ });
153
+ swap.state = SpvFromBTCSwapState.FRONTED;
154
+ return true;
155
+ }
156
+ return false;
157
+ }
158
+
159
+ protected async processEventClaim(event: SpvVaultClaimEvent, swap: SpvFromBTCSwap<T>): Promise<boolean> {
160
+ if(
161
+ swap.state===SpvFromBTCSwapState.SIGNED || swap.state===SpvFromBTCSwapState.POSTED ||
162
+ swap.state===SpvFromBTCSwapState.BROADCASTED || swap.state===SpvFromBTCSwapState.DECLINED ||
163
+ swap.state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap.state===SpvFromBTCSwapState.BTC_TX_CONFIRMED
164
+ ) {
165
+ await swap._setBitcoinTxId(event.btcTxId).catch(e => {
166
+ this.logger.warn("processEventClaim(): Failed to set bitcoin txId: ", e);
167
+ });
168
+ swap.state = SpvFromBTCSwapState.CLAIMED;
169
+ return true;
170
+ }
171
+ return false;
172
+ }
173
+
174
+ protected processEventClose(event: SpvVaultCloseEvent, swap: SpvFromBTCSwap<T>): Promise<boolean> {
175
+ if(
176
+ swap.state===SpvFromBTCSwapState.SIGNED || swap.state===SpvFromBTCSwapState.POSTED ||
177
+ swap.state===SpvFromBTCSwapState.BROADCASTED || swap.state===SpvFromBTCSwapState.DECLINED ||
178
+ swap.state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap.state===SpvFromBTCSwapState.BTC_TX_CONFIRMED
179
+ ) {
180
+ swap.state = SpvFromBTCSwapState.CLOSED;
181
+ return Promise.resolve(true);
182
+ }
183
+ return Promise.resolve(false);
184
+ }
185
+
186
+ protected async processEvent(event: ChainEvent<T["Data"]>, swap: SpvFromBTCSwap<T>): Promise<void> {
187
+ if(swap==null) return;
188
+
189
+ let swapChanged: boolean = false;
190
+ if(event instanceof SpvVaultFrontEvent) {
191
+ swapChanged = await this.processEventFront(event, swap);
192
+ if(event.meta?.txId!=null && swap.frontTxId!==event.meta.txId) {
193
+ swap.frontTxId = event.meta.txId;
194
+ swapChanged ||= true;
195
+ }
196
+ }
197
+ if(event instanceof SpvVaultClaimEvent) {
198
+ swapChanged = await this.processEventClaim(event, swap);
199
+ if(event.meta?.txId!=null && swap.claimTxId!==event.meta.txId) {
200
+ swap.claimTxId = event.meta.txId;
201
+ swapChanged ||= true;
202
+ }
203
+ }
204
+ if(event instanceof SpvVaultCloseEvent) {
205
+ swapChanged = await this.processEventClose(event, swap);
206
+ }
207
+
208
+ this.logger.info("processEvents(): "+event.constructor.name+" processed for "+swap.getId()+" swap: ", swap);
209
+
210
+ if(swapChanged) {
211
+ await swap._saveAndEmit();
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Pre-fetches latest finalized block height of the smart chain
217
+ *
218
+ * @param abortController
219
+ * @private
220
+ */
221
+ private async preFetchFinalizedBlockHeight(abortController: AbortController): Promise<number | undefined> {
222
+ try {
223
+ const block = await this.chain.getFinalizedBlock();
224
+ return block.height;
225
+ } catch (e) {
226
+ abortController.abort(e);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Pre-fetches caller (watchtower) bounty data for the swap. Doesn't throw, instead returns null and aborts the
232
+ * provided abortController
233
+ *
234
+ * @param amountData
235
+ * @param options Options as passed to the swap creation function
236
+ * @param pricePrefetch
237
+ * @param nativeTokenPricePrefetch
238
+ * @param abortController
239
+ * @private
240
+ */
241
+ private async preFetchCallerFeeShare(
242
+ amountData: AmountData,
243
+ options: AllRequired<SpvFromBTCOptions>,
244
+ pricePrefetch: Promise<bigint | undefined>,
245
+ nativeTokenPricePrefetch: Promise<bigint | undefined> | undefined,
246
+ abortController: AbortController
247
+ ): Promise<bigint | undefined> {
248
+ if(options.unsafeZeroWatchtowerFee) return 0n;
249
+ if(amountData.amount===0n) return 0n;
250
+
251
+ try {
252
+ const [
253
+ feePerBlock,
254
+ btcRelayData,
255
+ currentBtcBlock,
256
+ claimFeeRate,
257
+ nativeTokenPrice
258
+ ] = await Promise.all([
259
+ this.btcRelay.getFeePerBlock(),
260
+ this.btcRelay.getTipData(),
261
+ this.btcRpc.getTipHeight(),
262
+ this.contract.getClaimFee(this.chain.randomAddress()),
263
+ nativeTokenPricePrefetch ?? (amountData.token===this.chain.getNativeCurrencyAddress() ?
264
+ pricePrefetch :
265
+ this.prices.preFetchPrice(this.chainIdentifier, this.chain.getNativeCurrencyAddress(), abortController.signal))
266
+ ]);
267
+
268
+ if(btcRelayData==null) throw new Error("Btc relay doesn't seem to be initialized!");
269
+
270
+ const currentBtcRelayBlock = btcRelayData.blockheight;
271
+ const blockDelta = Math.max(currentBtcBlock-currentBtcRelayBlock+this.options.maxConfirmations, 0);
272
+
273
+ const totalFeeInNativeToken = (
274
+ (BigInt(blockDelta) * feePerBlock) +
275
+ (claimFeeRate * BigInt(this.options.maxTransactionsDelta))
276
+ ) * BigInt(Math.floor(options.feeSafetyFactor*1000000)) / 1_000_000n;
277
+
278
+ let payoutAmount: bigint;
279
+ if(amountData.exactIn) {
280
+ //Convert input amount in BTC to
281
+ const amountInNativeToken = await this.prices.getFromBtcSwapAmount(this.chainIdentifier, amountData.amount, this.chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
282
+ payoutAmount = amountInNativeToken - totalFeeInNativeToken;
283
+ } else {
284
+ if(amountData.token===this.chain.getNativeCurrencyAddress()) {
285
+ //Both amounts in same currency
286
+ payoutAmount = amountData.amount;
287
+ } else {
288
+ //Need to convert both to native currency
289
+ const btcAmount = await this.prices.getToBtcSwapAmount(this.chainIdentifier, amountData.amount, amountData.token, abortController.signal, await pricePrefetch);
290
+ payoutAmount = await this.prices.getFromBtcSwapAmount(this.chainIdentifier, btcAmount, this.chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
291
+ }
292
+ }
293
+
294
+ this.logger.debug("preFetchCallerFeeShare(): Caller fee in native token: "+totalFeeInNativeToken.toString(10)+" total payout in native token: "+payoutAmount.toString(10));
295
+
296
+ const callerFeeShare = ((totalFeeInNativeToken * 100_000n) + payoutAmount - 1n) / payoutAmount; //Make sure to round up here
297
+ if(callerFeeShare < 0n) return 0n;
298
+ if(callerFeeShare >= 2n**20n) return 2n**20n - 1n;
299
+ return callerFeeShare;
300
+ } catch (e) {
301
+ abortController.abort(e);
302
+ }
303
+ }
304
+
305
+
306
+ /**
307
+ * Verifies response returned from intermediary
308
+ *
309
+ * @param resp Response as returned by the intermediary
310
+ * @param amountData
311
+ * @param lp Intermediary
312
+ * @param options Options as passed to the swap creation function
313
+ * @param callerFeeShare
314
+ * @param bitcoinFeeRatePromise Maximum accepted fee rate from the LPs
315
+ * @param abortSignal
316
+ * @private
317
+ * @throws {IntermediaryError} in case the response is invalid
318
+ */
319
+ private async verifyReturnedData(
320
+ resp: SpvFromBTCPrepareResponseType,
321
+ amountData: AmountData,
322
+ lp: Intermediary,
323
+ options: SpvFromBTCOptions,
324
+ callerFeeShare: bigint,
325
+ bitcoinFeeRatePromise: Promise<number | undefined>,
326
+ abortSignal: AbortSignal
327
+ ): Promise<{
328
+ vault: T["SpvVaultData"],
329
+ vaultUtxoValue: number
330
+ }> {
331
+ const btcFeeRate = await throwIfUndefined(bitcoinFeeRatePromise, "Bitcoin fee rate promise failed!");
332
+ abortSignal.throwIfAborted();
333
+ if(btcFeeRate!=null && resp.btcFeeRate > btcFeeRate) throw new IntermediaryError("Bitcoin fee rate returned too high!");
334
+
335
+ //Vault related
336
+ let vaultScript: Uint8Array;
337
+ let vaultAddressType: CoinselectAddressTypes;
338
+ let btcAddressScript: Uint8Array;
339
+ //Ensure valid btc addresses returned
340
+ try {
341
+ vaultScript = toOutputScript(this.options.bitcoinNetwork, resp.vaultBtcAddress);
342
+ vaultAddressType = toCoinselectAddressType(vaultScript);
343
+ btcAddressScript = toOutputScript(this.options.bitcoinNetwork, resp.btcAddress);
344
+ } catch (e) {
345
+ throw new IntermediaryError("Invalid btc address data returned", e);
346
+ }
347
+ const decodedUtxo = resp.btcUtxo.split(":");
348
+ if(
349
+ resp.address!==lp.getAddress(this.chainIdentifier) || //Ensure the LP is indeed the vault owner
350
+ resp.vaultId < 0n || //Ensure vaultId is not negative
351
+ vaultScript==null || //Make sure vault script is parsable and of known type
352
+ btcAddressScript==null || //Make sure btc address script is parsable and of known type
353
+ vaultAddressType==="p2pkh" || vaultAddressType==="p2sh-p2wpkh" || //Constrain the vault script type to witness types
354
+ decodedUtxo.length!==2 || decodedUtxo[0].length!==64 || isNaN(parseInt(decodedUtxo[1])) || //Check valid UTXO
355
+ resp.btcFeeRate < 1 || resp.btcFeeRate > 10000 //Sanity check on the returned BTC fee rate
356
+ ) throw new IntermediaryError("Invalid vault data returned!");
357
+
358
+ //Amounts sanity
359
+ if(resp.btcAmountSwap + resp.btcAmountGas !==resp.btcAmount) throw new Error("Btc amount mismatch");
360
+ if(resp.swapFeeBtc + resp.gasSwapFeeBtc !==resp.totalFeeBtc) throw new Error("Btc fee mismatch");
361
+
362
+ //TODO: For now ensure fees are at 0
363
+ if(
364
+ resp.callerFeeShare!==callerFeeShare ||
365
+ resp.frontingFeeShare!==0n ||
366
+ resp.executionFeeShare!==0n
367
+ ) throw new IntermediaryError("Invalid caller/fronting/execution fee returned");
368
+
369
+ //Check expiry
370
+ const timeNowSeconds = Math.floor(Date.now()/1000);
371
+ if(resp.expiry < timeNowSeconds) throw new IntermediaryError(`Quote already expired, expiry: ${resp.expiry}, systemTime: ${timeNowSeconds}, clockAdjusted: ${(Date as any)._now!=null}`);
372
+
373
+ let utxo = resp.btcUtxo.toLowerCase();
374
+ const [txId, voutStr] = utxo.split(":");
375
+
376
+ const abortController = extendAbortController(abortSignal);
377
+ let [vault, {vaultUtxoValue, btcTx}] = await Promise.all([
378
+ (async() => {
379
+ //Fetch vault data
380
+ let vault: T["SpvVaultData"] | null;
381
+ try {
382
+ vault = await this.contract.getVaultData(resp.address, resp.vaultId);
383
+ } catch (e) {
384
+ this.logger.error("Error getting spv vault (owner: "+resp.address+" vaultId: "+resp.vaultId.toString(10)+"): ", e);
385
+ throw new IntermediaryError("Spv swap vault not found", e);
386
+ }
387
+ abortController.signal.throwIfAborted();
388
+
389
+ //Make sure vault is opened
390
+ if(vault==null || !vault.isOpened()) throw new IntermediaryError("Returned spv swap vault is not opened!");
391
+ //Make sure the vault doesn't require insane amount of confirmations
392
+ if(vault.getConfirmations()>this.options.maxConfirmations) throw new IntermediaryError("SPV swap vault needs too many confirmations: "+vault.getConfirmations());
393
+ const tokenData = vault.getTokenData();
394
+
395
+ //Amounts - make sure the amounts match
396
+ if(amountData.exactIn) {
397
+ if(resp.btcAmount !== amountData.amount) throw new IntermediaryError("Invalid amount returned");
398
+ } else {
399
+ //Check the difference between amount adjusted due to scaling to raw amount
400
+ const adjustedAmount = amountData.amount / tokenData[0].multiplier * tokenData[0].multiplier;
401
+ const adjustmentPPM = (amountData.amount - adjustedAmount)*1_000_000n / amountData.amount;
402
+ if(adjustmentPPM > this.options.maxRawAmountAdjustmentDifferencePPM)
403
+ throw new IntermediaryError("Invalid amount0 multiplier used, rawAmount diff too high");
404
+ if(resp.total !== adjustedAmount) throw new IntermediaryError("Invalid total returned");
405
+ }
406
+ if(options.gasAmount==null || options.gasAmount===0n) {
407
+ if(resp.totalGas !== 0n) throw new IntermediaryError("Invalid gas total returned");
408
+ } else {
409
+ //Check the difference between amount adjusted due to scaling to raw amount
410
+ const adjustedGasAmount = options.gasAmount / tokenData[0].multiplier * tokenData[0].multiplier;
411
+ const adjustmentPPM = (options.gasAmount - adjustedGasAmount)*1_000_000n / options.gasAmount;
412
+ if(adjustmentPPM > this.options.maxRawAmountAdjustmentDifferencePPM)
413
+ throw new IntermediaryError("Invalid amount1 multiplier used, rawAmount diff too high");
414
+ if(resp.totalGas !== adjustedGasAmount) throw new IntermediaryError("Invalid gas total returned");
415
+ }
416
+
417
+ return vault;
418
+ })(),
419
+ (async() => {
420
+ //Require the vault UTXO to have at least 1 confirmation
421
+ let btcTx = await this.btcRpc.getTransaction(txId);
422
+ if(btcTx==null) throw new IntermediaryError("Invalid UTXO, doesn't exist (txId)");
423
+ abortController.signal.throwIfAborted();
424
+ if(btcTx.confirmations==null || btcTx.confirmations<1) throw new IntermediaryError("SPV vault UTXO not confirmed");
425
+ const vout = parseInt(voutStr);
426
+ if(btcTx.outs[vout]==null) throw new IntermediaryError("Invalid UTXO, doesn't exist");
427
+ const vaultUtxoValue = btcTx.outs[vout].value;
428
+ return {btcTx, vaultUtxoValue};
429
+ })(),
430
+ (async() => {
431
+ //Require vault UTXO is unspent
432
+ if(await this.btcRpc.isSpent(utxo)) throw new IntermediaryError("Returned spv vault UTXO is already spent", null, true);
433
+ abortController.signal.throwIfAborted();
434
+ })()
435
+ ]).catch(e => {
436
+ abortController.abort(e);
437
+ throw e;
438
+ });
439
+
440
+ this.logger.debug("verifyReturnedData(): Vault UTXO: "+vault.getUtxo()+" current utxo: "+utxo);
441
+
442
+ //Trace returned utxo back to what's saved on-chain
443
+ let pendingWithdrawals: T["SpvVaultWithdrawalData"][] = [];
444
+ while(vault.getUtxo()!==utxo) {
445
+ const [txId, voutStr] = utxo.split(":");
446
+ //Such that 1st tx isn't fetched twice
447
+ if(btcTx.txid!==txId) {
448
+ const _btcTx = await this.btcRpc.getTransaction(txId);
449
+ if(_btcTx==null) throw new IntermediaryError("Invalid ancestor transaction (not found)");
450
+ btcTx = _btcTx;
451
+ }
452
+ const withdrawalData = await this.contract.getWithdrawalData(btcTx);
453
+ abortSignal.throwIfAborted();
454
+ pendingWithdrawals.unshift(withdrawalData);
455
+ utxo = pendingWithdrawals[0].getSpentVaultUtxo();
456
+ this.logger.debug("verifyReturnedData(): Vault UTXO: "+vault.getUtxo()+" current utxo: "+utxo);
457
+ if(pendingWithdrawals.length>=this.options.maxTransactionsDelta)
458
+ throw new IntermediaryError("BTC <> SC state difference too deep, maximum: "+this.options.maxTransactionsDelta);
459
+ }
460
+
461
+ //Verify that the vault has enough balance after processing all pending withdrawals
462
+ let vaultBalances: {balances: SpvVaultTokenBalance[]};
463
+ try {
464
+ vaultBalances = vault.calculateStateAfter(pendingWithdrawals);
465
+ } catch (e) {
466
+ this.logger.error("Error calculating spv vault balance (owner: "+resp.address+" vaultId: "+resp.vaultId.toString(10)+"): ", e);
467
+ throw new IntermediaryError("Spv swap vault balance prediction failed", e);
468
+ }
469
+ if(vaultBalances.balances[0].scaledAmount < resp.total)
470
+ throw new IntermediaryError("SPV swap vault, insufficient balance, required: "+resp.total.toString(10)+
471
+ " has: "+vaultBalances.balances[0].scaledAmount.toString(10));
472
+ if(vaultBalances.balances[1].scaledAmount < resp.totalGas)
473
+ throw new IntermediaryError("SPV swap vault, insufficient balance, required: "+resp.totalGas.toString(10)+
474
+ " has: "+vaultBalances.balances[1].scaledAmount.toString(10));
475
+
476
+ //Also verify that all the withdrawal txns are valid, this is an extra sanity check
477
+ try {
478
+ for(let withdrawal of pendingWithdrawals) {
479
+ await this.contract.checkWithdrawalTx(withdrawal);
480
+ }
481
+ } catch (e) {
482
+ this.logger.error("Error calculating spv vault balance (owner: "+resp.address+" vaultId: "+resp.vaultId.toString(10)+"): ", e);
483
+ throw new IntermediaryError("Spv swap vault balance prediction failed", e);
484
+ }
485
+ abortSignal.throwIfAborted();
486
+
487
+ return {
488
+ vault,
489
+ vaultUtxoValue
490
+ };
491
+ }
492
+
493
+ /**
494
+ * Returns a newly created swap, receiving 'amount' on chain
495
+ *
496
+ * @param signer Smartchain signer's address intiating the swap
497
+ * @param amountData Amount of token & amount to swap
498
+ * @param lps LPs (liquidity providers) to get the quotes from
499
+ * @param options Quote options
500
+ * @param additionalParams Additional parameters sent to the LP when creating the swap
501
+ * @param abortSignal Abort signal for aborting the process
502
+ */
503
+ create(
504
+ signer: string,
505
+ amountData: AmountData,
506
+ lps: Intermediary[],
507
+ options?: SpvFromBTCOptions,
508
+ additionalParams?: Record<string, any>,
509
+ abortSignal?: AbortSignal
510
+ ): {
511
+ quote: Promise<SpvFromBTCSwap<T>>,
512
+ intermediary: Intermediary
513
+ }[] {
514
+ const _options: AllRequired<SpvFromBTCOptions> = {
515
+ gasAmount: options?.gasAmount ?? 0n,
516
+ unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
517
+ feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
518
+ maxAllowedNetworkFeeRate: options?.maxAllowedNetworkFeeRate ?? Infinity
519
+ };
520
+
521
+ const _abortController = extendAbortController(abortSignal);
522
+ const pricePrefetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
523
+ const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
524
+ const finalizedBlockHeightPrefetchPromise: Promise<number | undefined> = this.preFetchFinalizedBlockHeight(_abortController);
525
+ const nativeTokenAddress = this.chain.getNativeCurrencyAddress();
526
+ const gasTokenPricePrefetchPromise: Promise<bigint | undefined> | undefined = _options.gasAmount===0n ?
527
+ undefined :
528
+ this.preFetchPrice({token: nativeTokenAddress}, _abortController.signal);
529
+ const callerFeePrefetchPromise = this.preFetchCallerFeeShare(amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, _abortController);
530
+ const bitcoinFeeRatePromise: Promise<number | undefined> = _options.maxAllowedNetworkFeeRate!=Infinity ?
531
+ Promise.resolve(_options.maxAllowedNetworkFeeRate) :
532
+ this.btcRpc.getFeeRate().then(x => this.options.maxBtcFeeOffset + (x*this.options.maxBtcFeeMultiplier)).catch(e => {
533
+ _abortController.abort(e);
534
+ return undefined;
535
+ });
536
+
537
+ return lps.map(lp => {
538
+ return {
539
+ intermediary: lp,
540
+ quote: tryWithRetries(async () => {
541
+ if(lp.services[SwapType.SPV_VAULT_FROM_BTC]==null) throw new Error("LP service for processing spv vault swaps not found!");
542
+
543
+ const abortController = extendAbortController(_abortController.signal);
544
+
545
+ try {
546
+ const resp = await tryWithRetries(async(retryCount: number) => {
547
+ return await IntermediaryAPI.prepareSpvFromBTC(
548
+ this.chainIdentifier, lp.url,
549
+ {
550
+ address: signer,
551
+ amount: amountData.amount,
552
+ token: amountData.token.toString(),
553
+ exactOut: !amountData.exactIn,
554
+ gasToken: nativeTokenAddress,
555
+ gasAmount: _options.gasAmount,
556
+ callerFeeRate: throwIfUndefined(callerFeePrefetchPromise, "Caller fee prefetch failed!"),
557
+ frontingFeeRate: 0n,
558
+ additionalParams
559
+ },
560
+ this.options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
561
+ );
562
+ }, undefined, e => e instanceof RequestError, abortController.signal);
563
+
564
+ this.logger.debug("create("+lp.url+"): LP response: ", resp)
565
+
566
+ const callerFeeShare = (await callerFeePrefetchPromise)!;
567
+
568
+ const [
569
+ pricingInfo,
570
+ gasPricingInfo,
571
+ {vault, vaultUtxoValue}
572
+ ] = await Promise.all([
573
+ this.verifyReturnedPrice(
574
+ lp.services[SwapType.SPV_VAULT_FROM_BTC],
575
+ false, resp.btcAmountSwap,
576
+ resp.total * (100_000n + callerFeeShare) / 100_000n,
577
+ amountData.token, {}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
578
+ ),
579
+ _options.gasAmount===0n ? Promise.resolve(undefined) : this.verifyReturnedPrice(
580
+ {...lp.services[SwapType.SPV_VAULT_FROM_BTC], swapBaseFee: 0}, //Base fee should be charged only on the amount, not on gas
581
+ false, resp.btcAmountGas,
582
+ resp.totalGas * (100_000n + callerFeeShare) / 100_000n,
583
+ nativeTokenAddress, {}, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
584
+ ),
585
+ this.verifyReturnedData(resp, amountData, lp, _options, callerFeeShare, bitcoinFeeRatePromise, abortController.signal)
586
+ ]);
587
+
588
+ const swapInit: SpvFromBTCSwapInit = {
589
+ pricingInfo,
590
+ url: lp.url,
591
+ expiry: resp.expiry * 1000,
592
+ swapFee: resp.swapFee,
593
+ swapFeeBtc: resp.swapFeeBtc,
594
+ exactIn: amountData.exactIn ?? true,
595
+
596
+ quoteId: resp.quoteId,
597
+
598
+ recipient: signer,
599
+
600
+ vaultOwner: resp.address,
601
+ vaultId: resp.vaultId,
602
+ vaultRequiredConfirmations: vault.getConfirmations(),
603
+ vaultTokenMultipliers: vault.getTokenData().map(val => val.multiplier),
604
+ vaultBtcAddress: resp.vaultBtcAddress,
605
+ vaultUtxo: resp.btcUtxo,
606
+ vaultUtxoValue: BigInt(vaultUtxoValue),
607
+
608
+ btcDestinationAddress: resp.btcAddress,
609
+ btcAmount: resp.btcAmount,
610
+ btcAmountSwap: resp.btcAmountSwap,
611
+ btcAmountGas: resp.btcAmountGas,
612
+ minimumBtcFeeRate: resp.btcFeeRate,
613
+
614
+ outputTotalSwap: resp.total,
615
+ outputSwapToken: amountData.token,
616
+ outputTotalGas: resp.totalGas,
617
+ outputGasToken: nativeTokenAddress,
618
+ gasSwapFeeBtc: resp.gasSwapFeeBtc,
619
+ gasSwapFee: resp.gasSwapFee,
620
+ gasPricingInfo,
621
+
622
+ callerFeeShare: resp.callerFeeShare,
623
+ frontingFeeShare: resp.frontingFeeShare,
624
+ executionFeeShare: resp.executionFeeShare,
625
+
626
+ genesisSmartChainBlockHeight: await throwIfUndefined(
627
+ finalizedBlockHeightPrefetchPromise,
628
+ "Finalize block height promise failed!"
629
+ )
630
+ };
631
+ const quote = new SpvFromBTCSwap<T>(this, swapInit);
632
+ await quote._save();
633
+ return quote;
634
+ } catch (e) {
635
+ abortController.abort(e);
636
+ throw e;
637
+ }
638
+ }, undefined, err => !(err instanceof IntermediaryError && err.recoverable), _abortController.signal)
639
+ }
640
+ });
641
+ }
642
+
643
+ /**
644
+ * Returns a random dummy PSBT that can be used for fee estimation, the last output (the LP output) is omitted
645
+ * to allow for coinselection algorithm to determine maximum sendable amount there
646
+ *
647
+ * @param includeGasToken Whether to return the PSBT also with the gas token amount (increases the vSize by 8)
648
+ */
649
+ public getDummySwapPsbt(includeGasToken = false): Transaction {
650
+ //Construct dummy swap psbt
651
+ const psbt = new Transaction({
652
+ allowUnknownInputs: true,
653
+ allowLegacyWitnessUtxo: true,
654
+ allowUnknownOutputs: true
655
+ });
656
+
657
+ const randomVaultOutScript = OutScript.encode({type: "tr", pubkey: Buffer.from("0101010101010101010101010101010101010101010101010101010101010101", "hex")});
658
+
659
+ psbt.addInput({
660
+ txid: randomBytes(32),
661
+ index: 0,
662
+ witnessUtxo: {
663
+ script: randomVaultOutScript,
664
+ amount: 600n
665
+ }
666
+ });
667
+
668
+ psbt.addOutput({
669
+ script: randomVaultOutScript,
670
+ amount: 600n
671
+ });
672
+
673
+ const opReturnData = this.contract.toOpReturnData(
674
+ this.chain.randomAddress(),
675
+ includeGasToken ? [0xFFFFFFFFFFFFFFFFn, 0xFFFFFFFFFFFFFFFFn] : [0xFFFFFFFFFFFFFFFFn]
676
+ );
677
+
678
+ psbt.addOutput({
679
+ script: Buffer.concat([
680
+ opReturnData.length <= 75 ? Buffer.from([0x6a, opReturnData.length]) : Buffer.from([0x6a, 0x4c, opReturnData.length]),
681
+ opReturnData
682
+ ]),
683
+ amount: 0n
684
+ });
685
+
686
+ return psbt;
687
+ }
688
+
689
+ protected async _checkPastSwaps(pastSwaps: SpvFromBTCSwap<T>[]): Promise<{
690
+ changedSwaps: SpvFromBTCSwap<T>[];
691
+ removeSwaps: SpvFromBTCSwap<T>[]
692
+ }> {
693
+ const changedSwaps: Set<SpvFromBTCSwap<T>> = new Set();
694
+ const removeSwaps: SpvFromBTCSwap<T>[] = [];
695
+
696
+ const broadcastedOrConfirmedSwaps: (SpvFromBTCSwap<T> & {data: T["SpvVaultWithdrawalData"]})[] = [];
697
+
698
+ for(let pastSwap of pastSwaps) {
699
+ let changed: boolean = false;
700
+
701
+ if(
702
+ pastSwap.state===SpvFromBTCSwapState.SIGNED ||
703
+ pastSwap.state===SpvFromBTCSwapState.POSTED ||
704
+ pastSwap.state===SpvFromBTCSwapState.BROADCASTED ||
705
+ pastSwap.state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
706
+ pastSwap.state===SpvFromBTCSwapState.DECLINED ||
707
+ pastSwap.state===SpvFromBTCSwapState.BTC_TX_CONFIRMED
708
+ ) {
709
+ //Check BTC transaction
710
+ if(await pastSwap._syncStateFromBitcoin(false)) changed ||= true;
711
+ }
712
+
713
+ if(
714
+ pastSwap.state===SpvFromBTCSwapState.CREATED ||
715
+ pastSwap.state===SpvFromBTCSwapState.SIGNED ||
716
+ pastSwap.state===SpvFromBTCSwapState.POSTED
717
+ ) {
718
+ if(pastSwap.expiry<Date.now()) {
719
+ if(pastSwap.state===SpvFromBTCSwapState.CREATED) {
720
+ pastSwap.state = SpvFromBTCSwapState.QUOTE_EXPIRED;
721
+ } else {
722
+ pastSwap.state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
723
+ }
724
+ changed ||= true;
725
+ }
726
+ }
727
+
728
+ if(pastSwap.isQuoteExpired()) {
729
+ removeSwaps.push(pastSwap);
730
+ continue;
731
+ }
732
+ if(changed) changedSwaps.add(pastSwap);
733
+
734
+ if(pastSwap.state===SpvFromBTCSwapState.BROADCASTED || pastSwap.state===SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
735
+ if(pastSwap.data!=null) broadcastedOrConfirmedSwaps.push(pastSwap as (SpvFromBTCSwap<T> & {data: T["SpvVaultWithdrawalData"]}));
736
+ }
737
+ }
738
+
739
+ const checkWithdrawalStateSwaps: (SpvFromBTCSwap<T> & {data: T["SpvVaultWithdrawalData"]})[] = [];
740
+ const _fronts = await this.contract.getFronterAddresses(broadcastedOrConfirmedSwaps.map(val => ({
741
+ owner: val.vaultOwner,
742
+ vaultId: val.vaultId,
743
+ withdrawal: val.data!
744
+ })));
745
+ const _vaultUtxos = await this.contract.getVaultLatestUtxos(broadcastedOrConfirmedSwaps.map(val => ({
746
+ owner: val.vaultOwner,
747
+ vaultId: val.vaultId
748
+ })));
749
+ for(const pastSwap of broadcastedOrConfirmedSwaps) {
750
+ const fronterAddress = _fronts[pastSwap.data.getTxId()];
751
+ const latestVaultUtxo = _vaultUtxos[pastSwap.vaultOwner]?.[pastSwap.vaultId.toString(10)];
752
+ if(fronterAddress===undefined) this.logger.warn(`_checkPastSwaps(): No fronter address returned for ${pastSwap.data.getTxId()}`);
753
+ if(latestVaultUtxo===undefined) this.logger.warn(`_checkPastSwaps(): No last vault utxo returned for ${pastSwap.data.getTxId()}`);
754
+ if(await pastSwap._shouldCheckWithdrawalState(fronterAddress, latestVaultUtxo)) checkWithdrawalStateSwaps.push(pastSwap);
755
+ }
756
+
757
+ const withdrawalStates = await this.contract.getWithdrawalStates(
758
+ checkWithdrawalStateSwaps.map(val => ({
759
+ withdrawal: val.data,
760
+ scStartBlockheight: val.genesisSmartChainBlockHeight
761
+ }))
762
+ );
763
+ for(const pastSwap of checkWithdrawalStateSwaps) {
764
+ const status = withdrawalStates[pastSwap.data.getTxId()];
765
+ if(status==null) {
766
+ this.logger.warn(`_checkPastSwaps(): No withdrawal state returned for ${pastSwap.data.getTxId()}`);
767
+ continue;
768
+ }
769
+ this.logger.debug("syncStateFromChain(): status of "+pastSwap.data.btcTx.txid, status?.type);
770
+ let changed = false;
771
+ switch(status.type) {
772
+ case SpvWithdrawalStateType.FRONTED:
773
+ pastSwap.frontTxId = status.txId;
774
+ pastSwap.state = SpvFromBTCSwapState.FRONTED;
775
+ changed ||= true;
776
+ break;
777
+ case SpvWithdrawalStateType.CLAIMED:
778
+ pastSwap.claimTxId = status.txId;
779
+ pastSwap.state = SpvFromBTCSwapState.CLAIMED;
780
+ changed ||= true;
781
+ break;
782
+ case SpvWithdrawalStateType.CLOSED:
783
+ pastSwap.state = SpvFromBTCSwapState.CLOSED;
784
+ changed ||= true;
785
+ break;
786
+ }
787
+ if(changed) changedSwaps.add(pastSwap);
788
+ }
789
+
790
+ return {
791
+ changedSwaps: Array.from(changedSwaps),
792
+ removeSwaps
793
+ };
794
+ }
795
+
796
+ }