@atomiqlabs/sdk 8.7.7 → 8.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +1760 -1760
  3. package/dist/SmartChainAssets.d.ts +181 -181
  4. package/dist/SmartChainAssets.js +181 -181
  5. package/dist/bitcoin/coinselect2/accumulative.d.ts +7 -6
  6. package/dist/bitcoin/coinselect2/accumulative.js +52 -52
  7. package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -6
  8. package/dist/bitcoin/coinselect2/blackjack.js +38 -38
  9. package/dist/bitcoin/coinselect2/index.d.ts +20 -19
  10. package/dist/bitcoin/coinselect2/index.js +69 -69
  11. package/dist/bitcoin/coinselect2/utils.d.ts +82 -77
  12. package/dist/bitcoin/coinselect2/utils.js +158 -123
  13. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -130
  14. package/dist/bitcoin/wallet/BitcoinWallet.js +335 -322
  15. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -78
  16. package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
  17. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -101
  18. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -190
  19. package/dist/enums/FeeType.d.ts +15 -15
  20. package/dist/enums/FeeType.js +19 -19
  21. package/dist/enums/SwapAmountType.d.ts +15 -15
  22. package/dist/enums/SwapAmountType.js +19 -19
  23. package/dist/enums/SwapDirection.d.ts +15 -15
  24. package/dist/enums/SwapDirection.js +19 -19
  25. package/dist/enums/SwapSide.d.ts +15 -15
  26. package/dist/enums/SwapSide.js +19 -19
  27. package/dist/enums/SwapType.d.ts +75 -75
  28. package/dist/enums/SwapType.js +79 -79
  29. package/dist/errors/IntermediaryError.d.ts +13 -13
  30. package/dist/errors/IntermediaryError.js +27 -27
  31. package/dist/errors/RequestError.d.ts +32 -32
  32. package/dist/errors/RequestError.js +54 -54
  33. package/dist/errors/UserError.d.ts +8 -8
  34. package/dist/errors/UserError.js +16 -16
  35. package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
  36. package/dist/events/UnifiedSwapEventListener.js +132 -132
  37. package/dist/http/HttpUtils.d.ts +27 -27
  38. package/dist/http/HttpUtils.js +91 -91
  39. package/dist/http/paramcoders/IParamReader.d.ts +8 -8
  40. package/dist/http/paramcoders/IParamReader.js +2 -2
  41. package/dist/http/paramcoders/ParamDecoder.d.ts +44 -44
  42. package/dist/http/paramcoders/ParamDecoder.js +137 -137
  43. package/dist/http/paramcoders/ParamEncoder.d.ts +20 -20
  44. package/dist/http/paramcoders/ParamEncoder.js +36 -36
  45. package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
  46. package/dist/http/paramcoders/SchemaVerifier.js +145 -145
  47. package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
  48. package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
  49. package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -13
  50. package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
  51. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
  52. package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -174
  53. package/dist/index.d.ts +85 -85
  54. package/dist/index.js +158 -158
  55. package/dist/intermediaries/Intermediary.d.ts +178 -178
  56. package/dist/intermediaries/Intermediary.js +166 -166
  57. package/dist/intermediaries/IntermediaryDiscovery.d.ts +211 -211
  58. package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
  59. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +450 -440
  60. package/dist/intermediaries/apis/IntermediaryAPI.js +618 -603
  61. package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
  62. package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
  63. package/dist/lnurl/LNURL.d.ts +102 -102
  64. package/dist/lnurl/LNURL.js +321 -321
  65. package/dist/prices/RedundantSwapPrice.d.ts +110 -110
  66. package/dist/prices/RedundantSwapPrice.js +222 -222
  67. package/dist/prices/SingleSwapPrice.d.ts +34 -34
  68. package/dist/prices/SingleSwapPrice.js +44 -44
  69. package/dist/prices/SwapPriceWithChain.d.ts +107 -107
  70. package/dist/prices/SwapPriceWithChain.js +128 -128
  71. package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
  72. package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
  73. package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
  74. package/dist/prices/abstract/IPriceProvider.js +74 -74
  75. package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
  76. package/dist/prices/abstract/ISwapPrice.js +279 -279
  77. package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
  78. package/dist/prices/providers/BinancePriceProvider.js +30 -30
  79. package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
  80. package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
  81. package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
  82. package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
  83. package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
  84. package/dist/prices/providers/CustomPriceProvider.js +35 -35
  85. package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
  86. package/dist/prices/providers/KrakenPriceProvider.js +45 -45
  87. package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
  88. package/dist/prices/providers/OKXPriceProvider.js +29 -29
  89. package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
  90. package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
  91. package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
  92. package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
  93. package/dist/storage/IUnifiedStorage.d.ts +85 -85
  94. package/dist/storage/IUnifiedStorage.js +2 -2
  95. package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
  96. package/dist/storage/UnifiedSwapStorage.js +116 -116
  97. package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
  98. package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
  99. package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
  100. package/dist/storage-browser/LocalStorageManager.js +93 -93
  101. package/dist/swapper/Swapper.d.ts +732 -692
  102. package/dist/swapper/Swapper.js +1713 -1657
  103. package/dist/swapper/SwapperFactory.d.ts +135 -135
  104. package/dist/swapper/SwapperFactory.js +162 -162
  105. package/dist/swapper/SwapperUtils.d.ts +206 -206
  106. package/dist/swapper/SwapperUtils.js +481 -481
  107. package/dist/swapper/SwapperWithChain.d.ts +404 -404
  108. package/dist/swapper/SwapperWithChain.js +469 -469
  109. package/dist/swapper/SwapperWithSigner.d.ts +322 -322
  110. package/dist/swapper/SwapperWithSigner.js +318 -318
  111. package/dist/swaps/IAddressSwap.d.ts +22 -22
  112. package/dist/swaps/IAddressSwap.js +14 -14
  113. package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
  114. package/dist/swaps/IBTCWalletSwap.js +18 -18
  115. package/dist/swaps/IClaimableSwap.d.ts +49 -49
  116. package/dist/swaps/IClaimableSwap.js +15 -15
  117. package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
  118. package/dist/swaps/IClaimableSwapWrapper.js +2 -2
  119. package/dist/swaps/IRefundableSwap.d.ts +43 -43
  120. package/dist/swaps/IRefundableSwap.js +14 -14
  121. package/dist/swaps/ISwap.d.ts +392 -392
  122. package/dist/swaps/ISwap.js +349 -349
  123. package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
  124. package/dist/swaps/ISwapWithGasDrop.js +12 -12
  125. package/dist/swaps/ISwapWrapper.d.ts +285 -285
  126. package/dist/swaps/ISwapWrapper.js +353 -353
  127. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
  128. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
  129. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +139 -139
  130. package/dist/swaps/escrow_swaps/IEscrowSwap.js +170 -170
  131. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +128 -128
  132. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
  133. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +105 -105
  134. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +129 -129
  135. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
  136. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
  137. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +64 -64
  138. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +82 -82
  139. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +531 -531
  140. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
  141. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +190 -190
  142. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
  143. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +583 -583
  144. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
  145. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +235 -235
  146. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
  147. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
  148. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
  149. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +202 -202
  150. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
  151. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
  152. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
  153. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +68 -68
  154. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +117 -117
  155. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -127
  156. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
  157. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +251 -251
  158. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +536 -536
  159. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
  160. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
  161. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +132 -132
  162. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
  163. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +637 -631
  164. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1448 -1444
  165. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +257 -225
  166. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -822
  167. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
  168. package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
  169. package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
  170. package/dist/swaps/trusted/ln/LnForGasWrapper.js +83 -83
  171. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
  172. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
  173. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +69 -69
  174. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +93 -93
  175. package/dist/types/AmountData.d.ts +10 -10
  176. package/dist/types/AmountData.js +2 -2
  177. package/dist/types/CustomPriceFunction.d.ts +11 -11
  178. package/dist/types/CustomPriceFunction.js +2 -2
  179. package/dist/types/PriceInfoType.d.ts +28 -28
  180. package/dist/types/PriceInfoType.js +57 -57
  181. package/dist/types/SwapExecutionAction.d.ts +88 -88
  182. package/dist/types/SwapExecutionAction.js +2 -2
  183. package/dist/types/SwapStateInfo.d.ts +5 -5
  184. package/dist/types/SwapStateInfo.js +2 -2
  185. package/dist/types/SwapWithSigner.d.ts +17 -17
  186. package/dist/types/SwapWithSigner.js +43 -43
  187. package/dist/types/Token.d.ts +99 -99
  188. package/dist/types/Token.js +76 -76
  189. package/dist/types/TokenAmount.d.ts +69 -69
  190. package/dist/types/TokenAmount.js +60 -60
  191. package/dist/types/fees/Fee.d.ts +50 -50
  192. package/dist/types/fees/Fee.js +2 -2
  193. package/dist/types/fees/FeeBreakdown.d.ts +11 -11
  194. package/dist/types/fees/FeeBreakdown.js +2 -2
  195. package/dist/types/fees/PercentagePPM.d.ts +17 -17
  196. package/dist/types/fees/PercentagePPM.js +18 -18
  197. package/dist/types/lnurl/LNURLPay.d.ts +61 -61
  198. package/dist/types/lnurl/LNURLPay.js +31 -31
  199. package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
  200. package/dist/types/lnurl/LNURLWithdraw.js +27 -27
  201. package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
  202. package/dist/types/wallets/LightningInvoiceCreateService.js +15 -15
  203. package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
  204. package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
  205. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
  206. package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
  207. package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
  208. package/dist/utils/AutomaticClockDriftCorrection.js +70 -70
  209. package/dist/utils/BitcoinUtils.d.ts +16 -14
  210. package/dist/utils/BitcoinUtils.js +141 -102
  211. package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
  212. package/dist/utils/BitcoinWalletUtils.js +14 -14
  213. package/dist/utils/Logger.d.ts +7 -7
  214. package/dist/utils/Logger.js +12 -12
  215. package/dist/utils/RetryUtils.d.ts +22 -22
  216. package/dist/utils/RetryUtils.js +67 -67
  217. package/dist/utils/SwapUtils.d.ts +88 -88
  218. package/dist/utils/SwapUtils.js +72 -72
  219. package/dist/utils/TimeoutUtils.d.ts +17 -17
  220. package/dist/utils/TimeoutUtils.js +55 -55
  221. package/dist/utils/TokenUtils.d.ts +19 -19
  222. package/dist/utils/TokenUtils.js +37 -37
  223. package/dist/utils/TypeUtils.d.ts +7 -7
  224. package/dist/utils/TypeUtils.js +2 -2
  225. package/dist/utils/Utils.d.ts +67 -67
  226. package/dist/utils/Utils.js +208 -208
  227. package/package.json +43 -43
  228. package/src/SmartChainAssets.ts +186 -186
  229. package/src/bitcoin/coinselect2/accumulative.ts +69 -68
  230. package/src/bitcoin/coinselect2/blackjack.ts +50 -49
  231. package/src/bitcoin/coinselect2/index.ts +93 -92
  232. package/src/bitcoin/coinselect2/utils.ts +236 -195
  233. package/src/bitcoin/wallet/BitcoinWallet.ts +439 -427
  234. package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -99
  235. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -217
  236. package/src/enums/FeeType.ts +15 -15
  237. package/src/enums/SwapAmountType.ts +16 -16
  238. package/src/enums/SwapDirection.ts +15 -15
  239. package/src/enums/SwapSide.ts +16 -16
  240. package/src/enums/SwapType.ts +75 -75
  241. package/src/errors/IntermediaryError.ts +28 -28
  242. package/src/errors/RequestError.ts +64 -64
  243. package/src/errors/UserError.ts +15 -15
  244. package/src/events/UnifiedSwapEventListener.ts +173 -173
  245. package/src/http/HttpUtils.ts +91 -91
  246. package/src/http/paramcoders/IParamReader.ts +9 -9
  247. package/src/http/paramcoders/ParamDecoder.ts +145 -145
  248. package/src/http/paramcoders/ParamEncoder.ts +40 -40
  249. package/src/http/paramcoders/SchemaVerifier.ts +153 -153
  250. package/src/http/paramcoders/client/ResponseParamDecoder.ts +57 -57
  251. package/src/http/paramcoders/client/StreamParamEncoder.ts +28 -28
  252. package/src/http/paramcoders/client/StreamingFetchPromise.ts +192 -192
  253. package/src/index.ts +140 -140
  254. package/src/intermediaries/Intermediary.ts +280 -280
  255. package/src/intermediaries/IntermediaryDiscovery.ts +541 -541
  256. package/src/intermediaries/apis/IntermediaryAPI.ts +963 -947
  257. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -257
  258. package/src/lnurl/LNURL.ts +402 -402
  259. package/src/prices/RedundantSwapPrice.ts +264 -264
  260. package/src/prices/SingleSwapPrice.ts +50 -50
  261. package/src/prices/SwapPriceWithChain.ts +194 -194
  262. package/src/prices/abstract/ICachedSwapPrice.ts +85 -85
  263. package/src/prices/abstract/IPriceProvider.ts +127 -127
  264. package/src/prices/abstract/ISwapPrice.ts +390 -390
  265. package/src/prices/providers/BinancePriceProvider.ts +48 -48
  266. package/src/prices/providers/CoinGeckoPriceProvider.ts +46 -46
  267. package/src/prices/providers/CoinPaprikaPriceProvider.ts +49 -49
  268. package/src/prices/providers/CustomPriceProvider.ts +40 -40
  269. package/src/prices/providers/KrakenPriceProvider.ts +83 -83
  270. package/src/prices/providers/OKXPriceProvider.ts +59 -59
  271. package/src/prices/providers/abstract/ExchangePriceProvider.ts +31 -31
  272. package/src/prices/providers/abstract/HttpPriceProvider.ts +14 -14
  273. package/src/storage/IUnifiedStorage.ts +95 -95
  274. package/src/storage/UnifiedSwapStorage.ts +141 -141
  275. package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
  276. package/src/storage-browser/LocalStorageManager.ts +106 -106
  277. package/src/swapper/Swapper.ts +2488 -2416
  278. package/src/swapper/SwapperFactory.ts +307 -307
  279. package/src/swapper/SwapperUtils.ts +570 -570
  280. package/src/swapper/SwapperWithChain.ts +707 -707
  281. package/src/swapper/SwapperWithSigner.ts +511 -511
  282. package/src/swaps/IAddressSwap.ts +30 -30
  283. package/src/swaps/IBTCWalletSwap.ts +92 -92
  284. package/src/swaps/IClaimableSwap.ts +65 -65
  285. package/src/swaps/IClaimableSwapWrapper.ts +17 -17
  286. package/src/swaps/IRefundableSwap.ts +58 -58
  287. package/src/swaps/ISwap.ts +703 -703
  288. package/src/swaps/ISwapWithGasDrop.ts +25 -25
  289. package/src/swaps/ISwapWrapper.ts +539 -539
  290. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
  291. package/src/swaps/escrow_swaps/IEscrowSwap.ts +269 -269
  292. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +282 -282
  293. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +169 -169
  294. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +300 -300
  295. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +107 -107
  296. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1473 -1474
  297. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +601 -601
  298. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1582 -1582
  299. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +750 -750
  300. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1299 -1299
  301. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +610 -610
  302. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1096 -1096
  303. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +138 -138
  304. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +304 -304
  305. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +786 -786
  306. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
  307. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +401 -401
  308. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1812 -1799
  309. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1236 -1060
  310. package/src/swaps/trusted/ln/LnForGasSwap.ts +589 -589
  311. package/src/swaps/trusted/ln/LnForGasWrapper.ts +91 -91
  312. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +862 -862
  313. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +131 -131
  314. package/src/types/AmountData.ts +9 -9
  315. package/src/types/CustomPriceFunction.ts +11 -11
  316. package/src/types/PriceInfoType.ts +66 -66
  317. package/src/types/SwapExecutionAction.ts +99 -99
  318. package/src/types/SwapStateInfo.ts +6 -6
  319. package/src/types/SwapWithSigner.ts +61 -61
  320. package/src/types/Token.ts +163 -163
  321. package/src/types/TokenAmount.ts +132 -132
  322. package/src/types/fees/Fee.ts +56 -56
  323. package/src/types/fees/FeeBreakdown.ts +11 -11
  324. package/src/types/fees/PercentagePPM.ts +26 -26
  325. package/src/types/lnurl/LNURLPay.ts +79 -79
  326. package/src/types/lnurl/LNURLWithdraw.ts +61 -61
  327. package/src/types/wallets/LightningInvoiceCreateService.ts +30 -30
  328. package/src/types/wallets/MinimalBitcoinWalletInterface.ts +21 -21
  329. package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +9 -9
  330. package/src/utils/AutomaticClockDriftCorrection.ts +71 -71
  331. package/src/utils/BitcoinUtils.ts +132 -91
  332. package/src/utils/BitcoinWalletUtils.ts +15 -15
  333. package/src/utils/Logger.ts +14 -14
  334. package/src/utils/RetryUtils.ts +78 -78
  335. package/src/utils/SwapUtils.ts +99 -99
  336. package/src/utils/TimeoutUtils.ts +49 -49
  337. package/src/utils/TokenUtils.ts +33 -33
  338. package/src/utils/TypeUtils.ts +8 -8
  339. package/src/utils/Utils.ts +212 -212
@@ -1,822 +1,947 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SpvFromBTCWrapper = void 0;
4
- const ISwapWrapper_1 = require("../ISwapWrapper");
5
- const base_1 = require("@atomiqlabs/base");
6
- const SpvFromBTCSwap_1 = require("./SpvFromBTCSwap");
7
- const utils_1 = require("@scure/btc-signer/utils");
8
- const SwapType_1 = require("../../enums/SwapType");
9
- const Intermediary_1 = require("../../intermediaries/Intermediary");
10
- const Utils_1 = require("../../utils/Utils");
11
- const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
12
- const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
13
- const RequestError_1 = require("../../errors/RequestError");
14
- const IntermediaryError_1 = require("../../errors/IntermediaryError");
15
- const btc_signer_1 = require("@scure/btc-signer");
16
- const RetryUtils_1 = require("../../utils/RetryUtils");
17
- const UserError_1 = require("../../errors/UserError");
18
- /**
19
- * New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
20
- * any initiation on the destination chain, and with the added possibility for the user to receive
21
- * a native token on the destination chain as part of the swap (a "gas drop" feature).
22
- *
23
- * @category Swaps/Bitcoin Smart chain
24
- */
25
- class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
26
- /**
27
- * @param chainIdentifier
28
- * @param unifiedStorage Storage interface for the current environment
29
- * @param unifiedChainEvents On-chain event listener
30
- * @param chain
31
- * @param prices Pricing to use
32
- * @param tokens
33
- * @param versionedContracts
34
- * @param versionedSynchronizer
35
- * @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
36
- * @param options
37
- * @param events Instance to use for emitting events
38
- */
39
- constructor(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, versionedContracts, versionedSynchronizer, btcRpc, options, events) {
40
- super(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, {
41
- ...options,
42
- bitcoinNetwork: options?.bitcoinNetwork ?? utils_1.TEST_NETWORK,
43
- maxConfirmations: options?.maxConfirmations ?? 6,
44
- bitcoinBlocktime: options?.bitcoinBlocktime ?? 10 * 60,
45
- maxTransactionsDelta: options?.maxTransactionsDelta ?? 3,
46
- maxRawAmountAdjustmentDifferencePPM: options?.maxRawAmountAdjustmentDifferencePPM ?? 100,
47
- maxBtcFeeOffset: options?.maxBtcFeeOffset ?? 10,
48
- maxBtcFeeMultiplier: options?.maxBtcFeeMultiplier ?? 1.5
49
- }, events);
50
- this.TYPE = SwapType_1.SwapType.SPV_VAULT_FROM_BTC;
51
- /**
52
- * @internal
53
- */
54
- this._claimableSwapStates = [SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED];
55
- /**
56
- * @internal
57
- */
58
- this._swapDeserializer = SpvFromBTCSwap_1.SpvFromBTCSwap;
59
- /**
60
- * @internal
61
- */
62
- this.btcRelay = (version) => {
63
- const _version = version ?? "v1";
64
- const data = this.versionedContracts[_version];
65
- if (data == null)
66
- throw new Error(`Invalid contract version ${_version} requested`);
67
- return data.btcRelay;
68
- };
69
- /**
70
- * @internal
71
- */
72
- this.tickSwapState = [
73
- SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED,
74
- SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
75
- SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED,
76
- SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED,
77
- SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED
78
- ];
79
- /**
80
- * @internal
81
- */
82
- this._synchronizer = (version) => {
83
- const _version = version ?? "v1";
84
- const data = this.versionedSynchronizer[_version];
85
- if (data == null)
86
- throw new Error(`Invalid contract version ${_version} requested`);
87
- return data.synchronizer;
88
- };
89
- /**
90
- * @internal
91
- */
92
- this._contract = (version) => {
93
- const _version = version ?? "v1";
94
- const data = this.versionedContracts[_version];
95
- if (data == null)
96
- throw new Error(`Invalid contract version ${_version} requested`);
97
- return data.spvVaultContract;
98
- };
99
- /**
100
- * @internal
101
- */
102
- this._spvWithdrawalDataDeserializer = (version) => {
103
- const _version = version ?? "v1";
104
- const data = this.versionedContracts[_version];
105
- if (data == null)
106
- throw new Error(`Invalid contract version ${_version} requested`);
107
- return data.spvVaultWithdrawalDataConstructor;
108
- };
109
- /**
110
- * @internal
111
- */
112
- this._pendingSwapStates = [
113
- SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED,
114
- SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED,
115
- SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED,
116
- SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
117
- SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED,
118
- SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED,
119
- SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED
120
- ];
121
- this.versionedContracts = {};
122
- this.versionedSynchronizer = {};
123
- this.versionedContracts = versionedContracts;
124
- this.versionedSynchronizer = versionedSynchronizer;
125
- this._btcRpc = btcRpc;
126
- }
127
- async processEventFront(event, swap) {
128
- if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
129
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
130
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
131
- swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
132
- await swap._setBitcoinTxId(event.btcTxId).catch(e => {
133
- this.logger.warn("processEventFront(): Failed to set bitcoin txId: ", e);
134
- });
135
- return true;
136
- }
137
- return false;
138
- }
139
- async processEventClaim(event, swap) {
140
- if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
141
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
142
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED ||
143
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
144
- swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
145
- await swap._setBitcoinTxId(event.btcTxId).catch(e => {
146
- this.logger.warn("processEventClaim(): Failed to set bitcoin txId: ", e);
147
- });
148
- return true;
149
- }
150
- return false;
151
- }
152
- processEventClose(event, swap) {
153
- if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
154
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
155
- swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
156
- swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLOSED;
157
- return Promise.resolve(true);
158
- }
159
- return Promise.resolve(false);
160
- }
161
- /**
162
- * @inheritDoc
163
- * @internal
164
- */
165
- async processEvent(event, swap) {
166
- if (swap == null)
167
- return;
168
- let swapChanged = false;
169
- if (event instanceof base_1.SpvVaultFrontEvent) {
170
- swapChanged = await this.processEventFront(event, swap);
171
- if (event.meta?.txId != null && swap._frontTxId !== event.meta.txId) {
172
- swap._frontTxId = event.meta.txId;
173
- swapChanged ||= true;
174
- }
175
- }
176
- if (event instanceof base_1.SpvVaultClaimEvent) {
177
- swapChanged = await this.processEventClaim(event, swap);
178
- if (event.meta?.txId != null && swap._claimTxId !== event.meta.txId) {
179
- swap._claimTxId = event.meta.txId;
180
- swapChanged ||= true;
181
- }
182
- }
183
- if (event instanceof base_1.SpvVaultCloseEvent) {
184
- swapChanged = await this.processEventClose(event, swap);
185
- }
186
- this.logger.info("processEvents(): " + event.constructor.name + " processed for " + swap.getId() + " swap: ", swap);
187
- if (swapChanged) {
188
- await swap._saveAndEmit();
189
- }
190
- }
191
- /**
192
- * Pre-fetches latest finalized block height of the smart chain
193
- *
194
- * @param abortController
195
- * @private
196
- */
197
- async preFetchFinalizedBlockHeight(abortController) {
198
- try {
199
- const block = await this._chain.getFinalizedBlock();
200
- return block.height;
201
- }
202
- catch (e) {
203
- abortController.abort(e);
204
- }
205
- }
206
- /**
207
- * Pre-fetches caller (watchtower) bounty data for the swap. Doesn't throw, instead returns null and aborts the
208
- * provided abortController
209
- *
210
- * @param amountData
211
- * @param options Options as passed to the swap creation function
212
- * @param pricePrefetch
213
- * @param nativeTokenPricePrefetch
214
- * @param abortController
215
- * @param contractVersion
216
- * @private
217
- */
218
- async preFetchCallerFeeShare(amountData, options, pricePrefetch, nativeTokenPricePrefetch, abortController, contractVersion) {
219
- if (options.unsafeZeroWatchtowerFee)
220
- return 0n;
221
- if (amountData.amount === 0n)
222
- return 0n;
223
- try {
224
- const [feePerBlock, btcRelayData, currentBtcBlock, claimFeeRate, nativeTokenPrice] = await Promise.all([
225
- this.btcRelay(contractVersion).getFeePerBlock(),
226
- this.btcRelay(contractVersion).getTipData(),
227
- this._btcRpc.getTipHeight(),
228
- this._contract(contractVersion).getClaimFee(this._chain.randomAddress()),
229
- nativeTokenPricePrefetch ?? (amountData.token === this._chain.getNativeCurrencyAddress() ?
230
- pricePrefetch :
231
- this._prices.preFetchPrice(this.chainIdentifier, this._chain.getNativeCurrencyAddress(), abortController.signal))
232
- ]);
233
- if (btcRelayData == null)
234
- throw new Error("Btc relay doesn't seem to be initialized!");
235
- const currentBtcRelayBlock = btcRelayData.blockheight;
236
- const blockDelta = Math.max(currentBtcBlock - currentBtcRelayBlock + this._options.maxConfirmations, 0);
237
- const totalFeeInNativeToken = ((BigInt(blockDelta) * feePerBlock) +
238
- (claimFeeRate * BigInt(this._options.maxTransactionsDelta))) * BigInt(Math.floor(options.feeSafetyFactor * 1000000)) / 1000000n;
239
- let payoutAmount;
240
- if (amountData.exactIn) {
241
- //Convert input amount in BTC to
242
- const amountInNativeToken = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, amountData.amount, this._chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
243
- payoutAmount = amountInNativeToken - totalFeeInNativeToken;
244
- }
245
- else {
246
- if (amountData.token === this._chain.getNativeCurrencyAddress()) {
247
- //Both amounts in same currency
248
- payoutAmount = amountData.amount;
249
- }
250
- else {
251
- //Need to convert both to native currency
252
- const btcAmount = await this._prices.getToBtcSwapAmount(this.chainIdentifier, amountData.amount, amountData.token, abortController.signal, await pricePrefetch);
253
- payoutAmount = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, btcAmount, this._chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
254
- }
255
- }
256
- this.logger.debug("preFetchCallerFeeShare(): Caller fee in native token: " + totalFeeInNativeToken.toString(10) + " total payout in native token: " + payoutAmount.toString(10));
257
- const callerFeeShare = ((totalFeeInNativeToken * 100000n) + payoutAmount - 1n) / payoutAmount; //Make sure to round up here
258
- if (callerFeeShare < 0n)
259
- return 0n;
260
- if (callerFeeShare >= 2n ** 20n)
261
- return 2n ** 20n - 1n;
262
- return callerFeeShare;
263
- }
264
- catch (e) {
265
- abortController.abort(e);
266
- }
267
- }
268
- /**
269
- * Verifies response returned from intermediary
270
- *
271
- * @param resp Response as returned by the intermediary
272
- * @param amountData
273
- * @param lp Intermediary
274
- * @param options Options as passed to the swap creation function
275
- * @param callerFeeShare
276
- * @param bitcoinFeeRatePromise Maximum accepted fee rate from the LPs
277
- * @param abortSignal
278
- * @private
279
- * @throws {IntermediaryError} in case the response is invalid
280
- */
281
- async verifyReturnedData(resp, amountData, lp, options, callerFeeShare, bitcoinFeeRatePromise, abortSignal) {
282
- const btcFeeRate = await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise, "Bitcoin fee rate promise failed!");
283
- abortSignal.throwIfAborted();
284
- if (btcFeeRate != null && resp.btcFeeRate > btcFeeRate)
285
- throw new IntermediaryError_1.IntermediaryError(`Required bitcoin fee rate returned from the LP is too high! Maximum accepted: ${btcFeeRate} sats/vB, required by LP: ${resp.btcFeeRate} sats/vB`);
286
- const lpVersion = lp.getContractVersion(this.chainIdentifier);
287
- //Vault related
288
- let vaultScript;
289
- let vaultAddressType;
290
- let btcAddressScript;
291
- //Ensure valid btc addresses returned
292
- try {
293
- vaultScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.vaultBtcAddress);
294
- vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)(vaultScript);
295
- btcAddressScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.btcAddress);
296
- }
297
- catch (e) {
298
- throw new IntermediaryError_1.IntermediaryError("Invalid btc address data returned", e);
299
- }
300
- const decodedUtxo = resp.btcUtxo.split(":");
301
- if (resp.address !== lp.getAddress(this.chainIdentifier) || //Ensure the LP is indeed the vault owner
302
- resp.vaultId < 0n || //Ensure vaultId is not negative
303
- vaultScript == null || //Make sure vault script is parsable and of known type
304
- btcAddressScript == null || //Make sure btc address script is parsable and of known type
305
- vaultAddressType === "p2pkh" || vaultAddressType === "p2sh-p2wpkh" || //Constrain the vault script type to witness types
306
- decodedUtxo.length !== 2 || decodedUtxo[0].length !== 64 || isNaN(parseInt(decodedUtxo[1])) || //Check valid UTXO
307
- resp.btcFeeRate < 1 || resp.btcFeeRate > 10000 //Sanity check on the returned BTC fee rate
308
- )
309
- throw new IntermediaryError_1.IntermediaryError("Invalid vault data returned!");
310
- //Amounts sanity
311
- if (resp.btcAmountSwap + resp.btcAmountGas !== resp.btcAmount)
312
- throw new Error("Btc amount mismatch");
313
- if (resp.swapFeeBtc + resp.gasSwapFeeBtc !== resp.totalFeeBtc)
314
- throw new Error("Btc fee mismatch");
315
- //TODO: For now ensure fees are at 0
316
- if (resp.callerFeeShare !== callerFeeShare ||
317
- resp.frontingFeeShare !== 0n ||
318
- resp.executionFeeShare !== 0n)
319
- throw new IntermediaryError_1.IntermediaryError("Invalid caller/fronting/execution fee returned");
320
- //Check expiry
321
- const timeNowSeconds = Math.floor(Date.now() / 1000);
322
- if (resp.expiry < timeNowSeconds)
323
- throw new IntermediaryError_1.IntermediaryError(`Quote already expired, expiry: ${resp.expiry}, systemTime: ${timeNowSeconds}, clockAdjusted: ${Date._now != null}`);
324
- let utxo = resp.btcUtxo.toLowerCase();
325
- const [txId, voutStr] = utxo.split(":");
326
- const abortController = (0, Utils_1.extendAbortController)(abortSignal);
327
- let [vault, { vaultUtxoValue, btcTx }] = await Promise.all([
328
- (async () => {
329
- //Fetch vault data
330
- let vault;
331
- try {
332
- vault = await this._contract(lpVersion).getVaultData(resp.address, resp.vaultId);
333
- }
334
- catch (e) {
335
- this.logger.error("Error getting spv vault (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
336
- throw new IntermediaryError_1.IntermediaryError("Spv swap vault not found", e);
337
- }
338
- abortController.signal.throwIfAborted();
339
- //Make sure vault is opened
340
- if (vault == null || !vault.isOpened())
341
- throw new IntermediaryError_1.IntermediaryError("Returned spv swap vault is not opened!");
342
- //Make sure the vault doesn't require insane amount of confirmations
343
- if (vault.getConfirmations() > this._options.maxConfirmations)
344
- throw new IntermediaryError_1.IntermediaryError("SPV swap vault needs too many confirmations: " + vault.getConfirmations());
345
- const tokenData = vault.getTokenData();
346
- //Amounts - make sure the amounts match
347
- if (amountData.exactIn) {
348
- if (resp.btcAmount !== amountData.amount)
349
- throw new IntermediaryError_1.IntermediaryError("Invalid amount returned");
350
- }
351
- else {
352
- //Check the difference between amount adjusted due to scaling to raw amount
353
- const adjustedAmount = amountData.amount / tokenData[0].multiplier * tokenData[0].multiplier;
354
- const adjustmentPPM = (amountData.amount - adjustedAmount) * 1000000n / amountData.amount;
355
- if (adjustmentPPM > this._options.maxRawAmountAdjustmentDifferencePPM)
356
- throw new IntermediaryError_1.IntermediaryError("Invalid amount0 multiplier used, rawAmount diff too high");
357
- if (resp.total !== adjustedAmount)
358
- throw new IntermediaryError_1.IntermediaryError("Invalid total returned");
359
- }
360
- if (options.gasAmount === 0n) {
361
- if (resp.totalGas !== 0n)
362
- throw new IntermediaryError_1.IntermediaryError("Invalid gas total returned");
363
- }
364
- else {
365
- //Check the difference between amount adjusted due to scaling to raw amount
366
- const adjustedGasAmount = options.gasAmount / tokenData[0].multiplier * tokenData[0].multiplier;
367
- const adjustmentPPM = (options.gasAmount - adjustedGasAmount) * 1000000n / options.gasAmount;
368
- if (adjustmentPPM > this._options.maxRawAmountAdjustmentDifferencePPM)
369
- throw new IntermediaryError_1.IntermediaryError("Invalid amount1 multiplier used, rawAmount diff too high");
370
- if (resp.totalGas !== adjustedGasAmount)
371
- throw new IntermediaryError_1.IntermediaryError("Invalid gas total returned");
372
- }
373
- return vault;
374
- })(),
375
- (async () => {
376
- //Require the vault UTXO to have at least 1 confirmation
377
- let btcTx = await this._btcRpc.getTransaction(txId);
378
- if (btcTx == null)
379
- throw new IntermediaryError_1.IntermediaryError("Invalid UTXO, doesn't exist (txId)");
380
- abortController.signal.throwIfAborted();
381
- if (btcTx.confirmations == null || btcTx.confirmations < 1)
382
- throw new IntermediaryError_1.IntermediaryError("SPV vault UTXO not confirmed");
383
- const vout = parseInt(voutStr);
384
- if (btcTx.outs[vout] == null)
385
- throw new IntermediaryError_1.IntermediaryError("Invalid UTXO, doesn't exist");
386
- const vaultUtxoValue = btcTx.outs[vout].value;
387
- return { btcTx, vaultUtxoValue };
388
- })(),
389
- (async () => {
390
- //Require vault UTXO is unspent
391
- if (await this._btcRpc.isSpent(utxo))
392
- throw new IntermediaryError_1.IntermediaryError("Returned spv vault UTXO is already spent", null, true);
393
- abortController.signal.throwIfAborted();
394
- })()
395
- ]).catch(e => {
396
- abortController.abort(e);
397
- throw e;
398
- });
399
- this.logger.debug("verifyReturnedData(): Vault UTXO: " + vault.getUtxo() + " current utxo: " + utxo);
400
- //Trace returned utxo back to what's saved on-chain
401
- let pendingWithdrawals = [];
402
- while (vault.getUtxo() !== utxo) {
403
- const [txId, voutStr] = utxo.split(":");
404
- //Such that 1st tx isn't fetched twice
405
- if (btcTx.txid !== txId) {
406
- const _btcTx = await this._btcRpc.getTransaction(txId);
407
- if (_btcTx == null)
408
- throw new IntermediaryError_1.IntermediaryError("Invalid ancestor transaction (not found)");
409
- btcTx = _btcTx;
410
- }
411
- const withdrawalData = await this._contract(lpVersion).getWithdrawalData(btcTx);
412
- abortSignal.throwIfAborted();
413
- pendingWithdrawals.unshift(withdrawalData);
414
- utxo = pendingWithdrawals[0].getSpentVaultUtxo();
415
- this.logger.debug("verifyReturnedData(): Vault UTXO: " + vault.getUtxo() + " current utxo: " + utxo);
416
- if (pendingWithdrawals.length >= this._options.maxTransactionsDelta)
417
- throw new IntermediaryError_1.IntermediaryError("BTC <> SC state difference too deep, maximum: " + this._options.maxTransactionsDelta);
418
- }
419
- //Verify that the vault has enough balance after processing all pending withdrawals
420
- let vaultBalances;
421
- try {
422
- vaultBalances = vault.calculateStateAfter(pendingWithdrawals);
423
- }
424
- catch (e) {
425
- this.logger.error("Error calculating spv vault balance (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
426
- throw new IntermediaryError_1.IntermediaryError("Spv swap vault balance prediction failed", e);
427
- }
428
- if (vaultBalances.balances[0].scaledAmount < resp.total)
429
- throw new IntermediaryError_1.IntermediaryError("SPV swap vault, insufficient balance, required: " + resp.total.toString(10) +
430
- " has: " + vaultBalances.balances[0].scaledAmount.toString(10));
431
- if (vaultBalances.balances[1].scaledAmount < resp.totalGas)
432
- throw new IntermediaryError_1.IntermediaryError("SPV swap vault, insufficient balance, required: " + resp.totalGas.toString(10) +
433
- " has: " + vaultBalances.balances[1].scaledAmount.toString(10));
434
- //Also verify that all the withdrawal txns are valid, this is an extra sanity check
435
- try {
436
- for (let withdrawal of pendingWithdrawals) {
437
- await this._contract(lpVersion).checkWithdrawalTx(withdrawal);
438
- }
439
- }
440
- catch (e) {
441
- this.logger.error("Error calculating spv vault balance (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
442
- throw new IntermediaryError_1.IntermediaryError("Spv swap vault balance prediction failed", e);
443
- }
444
- abortSignal.throwIfAborted();
445
- return {
446
- vault,
447
- vaultUtxoValue
448
- };
449
- }
450
- /**
451
- * Returns a newly created Bitcoin -> Smart chain swap using the SPV vault (UTXO-controlled vault) swap protocol,
452
- * with the passed amount. Also allows specifying additional "gas drop" native token that the receipient receives
453
- * on the destination chain in the `options` argument.
454
- *
455
- * @param recipient Recipient address on the destination smart chain
456
- * @param amountData Amount, token and exact input/output data for to swap
457
- * @param lps An array of intermediaries (LPs) to get the quotes from
458
- * @param options Optional additional quote options
459
- * @param additionalParams Optional additional parameters sent to the LP when creating the swap
460
- * @param abortSignal Abort signal
461
- */
462
- create(recipient, amountData, lps, options, additionalParams, abortSignal) {
463
- const _options = {
464
- gasAmount: this.parseGasAmount(options?.gasAmount),
465
- unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
466
- feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
467
- maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
468
- };
469
- if (_options.gasAmount !== 0n &&
470
- (this._chain.shouldGetNativeTokenDrop != null
471
- ? !this._chain.shouldGetNativeTokenDrop(amountData.token)
472
- : amountData.token === this._chain.getNativeCurrencyAddress()))
473
- throw new UserError_1.UserError("Cannot specify `gasAmount` for swaps to a native token!");
474
- const lpVersions = Intermediary_1.Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
475
- const _abortController = (0, Utils_1.extendAbortController)(abortSignal);
476
- const pricePrefetchPromise = this.preFetchPrice(amountData, _abortController.signal);
477
- const usdPricePrefetchPromise = this.preFetchUsdPrice(_abortController.signal);
478
- const finalizedBlockHeightPrefetchPromise = this.preFetchFinalizedBlockHeight(_abortController);
479
- const nativeTokenAddress = this._chain.getNativeCurrencyAddress();
480
- const gasTokenPricePrefetchPromise = _options.gasAmount === 0n ?
481
- undefined :
482
- this.preFetchPrice({ token: nativeTokenAddress }, _abortController.signal);
483
- const callerFeePrefetchPromise = (0, Utils_1.mapArrayToObject)(lpVersions, (contractVersion) => {
484
- return this.preFetchCallerFeeShare(amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, _abortController, contractVersion);
485
- });
486
- const bitcoinFeeRatePromise = _options.maxAllowedBitcoinFeeRate != Infinity ?
487
- Promise.resolve(_options.maxAllowedBitcoinFeeRate) :
488
- this._btcRpc.getFeeRate().then(x => this._options.maxBtcFeeOffset + (x * this._options.maxBtcFeeMultiplier)).catch(e => {
489
- _abortController.abort(e);
490
- return undefined;
491
- });
492
- return lps.map(lp => {
493
- return {
494
- intermediary: lp,
495
- quote: (0, RetryUtils_1.tryWithRetries)(async () => {
496
- if (lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC] == null)
497
- throw new Error("LP service for processing spv vault swaps not found!");
498
- const version = lp.getContractVersion(this.chainIdentifier);
499
- const abortController = (0, Utils_1.extendAbortController)(_abortController.signal);
500
- try {
501
- const resp = await (0, RetryUtils_1.tryWithRetries)(async (retryCount) => {
502
- return await IntermediaryAPI_1.IntermediaryAPI.prepareSpvFromBTC(this.chainIdentifier, lp.url, {
503
- address: recipient,
504
- amount: amountData.amount,
505
- token: amountData.token.toString(),
506
- exactOut: !amountData.exactIn,
507
- gasToken: nativeTokenAddress,
508
- gasAmount: _options.gasAmount,
509
- callerFeeRate: (0, Utils_1.throwIfUndefined)(callerFeePrefetchPromise[version], "Caller fee prefetch failed!"),
510
- frontingFeeRate: 0n,
511
- stickyAddress: options?.stickyAddress,
512
- additionalParams
513
- }, this._options.postRequestTimeout, abortController.signal, retryCount > 0 ? false : undefined);
514
- }, undefined, e => e instanceof RequestError_1.RequestError, abortController.signal);
515
- this.logger.debug("create(" + lp.url + "): LP response: ", resp);
516
- const callerFeeShare = (await callerFeePrefetchPromise[version]);
517
- const [pricingInfo, gasPricingInfo, { vault, vaultUtxoValue }] = await Promise.all([
518
- this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], false, resp.btcAmountSwap, resp.total * (100000n + callerFeeShare) / 100000n, amountData.token, { swapFeeBtc: resp.swapFeeBtc }, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
519
- _options.gasAmount === 0n ? Promise.resolve(undefined) : this.verifyReturnedPrice({ ...lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], swapBaseFee: 0 }, //Base fee should be charged only on the amount, not on gas
520
- false, resp.btcAmountGas, resp.totalGas * (100000n + callerFeeShare) / 100000n, nativeTokenAddress, { swapFeeBtc: resp.gasSwapFeeBtc }, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
521
- this.verifyReturnedData(resp, amountData, lp, _options, callerFeeShare, bitcoinFeeRatePromise, abortController.signal)
522
- ]);
523
- const swapInit = {
524
- pricingInfo,
525
- url: lp.url,
526
- expiry: resp.expiry * 1000,
527
- swapFee: resp.swapFee,
528
- swapFeeBtc: resp.swapFeeBtc,
529
- exactIn: amountData.exactIn ?? true,
530
- quoteId: resp.quoteId,
531
- recipient,
532
- vaultOwner: resp.address,
533
- vaultId: resp.vaultId,
534
- vaultRequiredConfirmations: vault.getConfirmations(),
535
- vaultTokenMultipliers: vault.getTokenData().map(val => val.multiplier),
536
- vaultBtcAddress: resp.vaultBtcAddress,
537
- vaultUtxo: resp.btcUtxo,
538
- vaultUtxoValue: BigInt(vaultUtxoValue),
539
- btcDestinationAddress: resp.btcAddress,
540
- btcAmount: resp.btcAmount,
541
- btcAmountSwap: resp.btcAmountSwap,
542
- btcAmountGas: resp.btcAmountGas,
543
- minimumBtcFeeRate: resp.btcFeeRate,
544
- outputTotalSwap: resp.total,
545
- outputSwapToken: amountData.token,
546
- outputTotalGas: resp.totalGas,
547
- outputGasToken: nativeTokenAddress,
548
- gasSwapFeeBtc: resp.gasSwapFeeBtc,
549
- gasSwapFee: resp.gasSwapFee,
550
- gasPricingInfo,
551
- callerFeeShare: resp.callerFeeShare,
552
- frontingFeeShare: resp.frontingFeeShare,
553
- executionFeeShare: resp.executionFeeShare,
554
- genesisSmartChainBlockHeight: await (0, Utils_1.throwIfUndefined)(finalizedBlockHeightPrefetchPromise, "Finalize block height promise failed!"),
555
- contractVersion: version
556
- };
557
- const quote = new SpvFromBTCSwap_1.SpvFromBTCSwap(this, swapInit);
558
- return quote;
559
- }
560
- catch (e) {
561
- abortController.abort(e);
562
- throw e;
563
- }
564
- }, undefined, err => !(err instanceof IntermediaryError_1.IntermediaryError && err.recoverable), _abortController.signal)
565
- };
566
- });
567
- }
568
- /**
569
- * Recovers an SPV vault (UTXO-controlled vault) based swap from smart chain on-chain data
570
- *
571
- * @param state State of the spv vault withdrawal recovered from on-chain data
572
- * @param vault SPV vault processing the swap
573
- * @param lp Intermediary (LP) used as a counterparty for the swap
574
- */
575
- async recoverFromState(state, contractVersion, vault, lp) {
576
- //Get the vault
577
- vault ??= await this._contract(contractVersion).getVaultData(state.owner, state.vaultId);
578
- if (vault == null)
579
- return null;
580
- if (state.btcTxId == null)
581
- return null;
582
- const btcTx = await this._btcRpc.getTransaction(state.btcTxId);
583
- if (btcTx == null)
584
- return null;
585
- const withdrawalData = await this._contract(contractVersion).getWithdrawalData(btcTx)
586
- .catch(e => {
587
- this.logger.warn(`Error parsing withdrawal data for tx ${btcTx.txid}: `, e);
588
- return null;
589
- });
590
- if (withdrawalData == null)
591
- return null;
592
- const vaultTokens = vault.getTokenData();
593
- const withdrawalDataOutputs = withdrawalData.getTotalOutput();
594
- const txBlock = await state.getTxBlock?.();
595
- const swapInit = {
596
- pricingInfo: {
597
- isValid: true,
598
- satsBaseFee: 0n,
599
- swapPriceUSatPerToken: 100000000000000n,
600
- realPriceUSatPerToken: 100000000000000n,
601
- differencePPM: 0n,
602
- feePPM: 0n,
603
- },
604
- url: lp?.url,
605
- expiry: 0,
606
- swapFee: 0n,
607
- swapFeeBtc: 0n,
608
- exactIn: true,
609
- //Use bitcoin tx id as quote id, even though this is not strictly correct as this
610
- // is an off-chain identifier presented by the LP that cannot be recovered from on-chain
611
- // data
612
- quoteId: btcTx.txid,
613
- recipient: state.recipient,
614
- vaultOwner: state.owner,
615
- vaultId: state.vaultId,
616
- vaultRequiredConfirmations: vault.getConfirmations(),
617
- vaultTokenMultipliers: vault.getTokenData().map(val => val.multiplier),
618
- vaultBtcAddress: (0, BitcoinUtils_1.fromOutputScript)(this._options.bitcoinNetwork, withdrawalData.getNewVaultScript().toString("hex")),
619
- vaultUtxo: withdrawalData.getSpentVaultUtxo(),
620
- vaultUtxoValue: BigInt(withdrawalData.getNewVaultBtcAmount()),
621
- btcDestinationAddress: (0, BitcoinUtils_1.fromOutputScript)(this._options.bitcoinNetwork, btcTx.outs[2].scriptPubKey.hex),
622
- btcAmount: BigInt(btcTx.outs[2].value),
623
- btcAmountSwap: BigInt(btcTx.outs[2].value),
624
- btcAmountGas: 0n,
625
- minimumBtcFeeRate: 0,
626
- outputTotalSwap: withdrawalDataOutputs[0] * vaultTokens[0].multiplier,
627
- outputSwapToken: vaultTokens[0].token,
628
- outputTotalGas: withdrawalDataOutputs[1] * vaultTokens[1].multiplier,
629
- outputGasToken: vaultTokens[1].token,
630
- gasSwapFeeBtc: 0n,
631
- gasSwapFee: 0n,
632
- gasPricingInfo: {
633
- isValid: true,
634
- satsBaseFee: 0n,
635
- swapPriceUSatPerToken: 100000000000000n,
636
- realPriceUSatPerToken: 100000000000000n,
637
- differencePPM: 0n,
638
- feePPM: 0n,
639
- },
640
- callerFeeShare: withdrawalData.callerFeeRate,
641
- frontingFeeShare: withdrawalData.frontingFeeRate,
642
- executionFeeShare: withdrawalData.executionFeeRate,
643
- genesisSmartChainBlockHeight: txBlock?.blockHeight ?? 0,
644
- contractVersion
645
- };
646
- const quote = new SpvFromBTCSwap_1.SpvFromBTCSwap(this, swapInit);
647
- quote._data = withdrawalData;
648
- if (txBlock != null) {
649
- quote.createdAt = txBlock.blockTime * 1000;
650
- }
651
- else if (btcTx.blockhash == null) {
652
- quote.createdAt = Date.now();
653
- }
654
- else {
655
- const blockHeader = await this._btcRpc.getBlockHeader(btcTx.blockhash);
656
- quote.createdAt = blockHeader == null ? Date.now() : blockHeader.getTimestamp() * 1000;
657
- }
658
- quote._setInitiated();
659
- if (btcTx.inputAddresses != null)
660
- quote._senderAddress = btcTx.inputAddresses[1];
661
- if (state.type === base_1.SpvWithdrawalStateType.FRONTED) {
662
- quote._frontTxId = state.txId;
663
- quote._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
664
- }
665
- else {
666
- quote._claimTxId = state.txId;
667
- quote._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
668
- }
669
- await quote._save();
670
- return quote;
671
- }
672
- /**
673
- * Returns a random dummy PSBT that can be used for fee estimation, the last output (the LP output) is omitted
674
- * to allow for coinselection algorithm to determine maximum sendable amount there
675
- *
676
- * @param includeGasToken Whether to return the PSBT also with the gas token amount (increases the vSize by 8)
677
- */
678
- getDummySwapPsbt(includeGasToken = false) {
679
- //Construct dummy swap psbt
680
- const psbt = new btc_signer_1.Transaction({
681
- allowUnknownInputs: true,
682
- allowLegacyWitnessUtxo: true,
683
- allowUnknownOutputs: true
684
- });
685
- const randomVaultOutScript = btc_signer_1.OutScript.encode({ type: "tr", pubkey: Buffer.from("0101010101010101010101010101010101010101010101010101010101010101", "hex") });
686
- psbt.addInput({
687
- txid: (0, Utils_1.randomBytes)(32),
688
- index: 0,
689
- witnessUtxo: {
690
- script: randomVaultOutScript,
691
- amount: 600n
692
- }
693
- });
694
- psbt.addOutput({
695
- script: randomVaultOutScript,
696
- amount: 600n
697
- });
698
- let longestOpReturnData = undefined;
699
- for (let contractVersion in this.versionedContracts) {
700
- if (this.versionedContracts[contractVersion].spvVaultContract == null)
701
- continue;
702
- const opReturnData = this._contract(contractVersion).toOpReturnData(this._chain.randomAddress(), includeGasToken ? [0xffffffffffffffffn, 0xffffffffffffffffn] : [0xffffffffffffffffn]);
703
- if (longestOpReturnData == null || longestOpReturnData.length < opReturnData.length)
704
- longestOpReturnData = opReturnData;
705
- }
706
- if (longestOpReturnData == null)
707
- throw new Error(`No contract version supporting the Spv Vault BTC -> ${this.chainIdentifier} swaps found!`);
708
- psbt.addOutput({
709
- script: Buffer.concat([
710
- longestOpReturnData.length <= 75 ? Buffer.from([0x6a, longestOpReturnData.length]) : Buffer.from([0x6a, 0x4c, longestOpReturnData.length]),
711
- longestOpReturnData
712
- ]),
713
- amount: 0n
714
- });
715
- return psbt;
716
- }
717
- /**
718
- * @inheritDoc
719
- * @internal
720
- */
721
- async _checkPastSwaps(pastSwaps) {
722
- const changedSwaps = new Set();
723
- const removeSwaps = [];
724
- const broadcastedOrConfirmedSwaps = {};
725
- for (let pastSwap of pastSwaps) {
726
- let changed = false;
727
- if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED ||
728
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
729
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED ||
730
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
731
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
732
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
733
- //Check BTC transaction
734
- if (await pastSwap._syncStateFromBitcoin(false))
735
- changed ||= true;
736
- }
737
- if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED ||
738
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED ||
739
- pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED) {
740
- if (await pastSwap._verifyQuoteDefinitelyExpired()) {
741
- if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED) {
742
- pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_EXPIRED;
743
- }
744
- else {
745
- pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
746
- }
747
- changed ||= true;
748
- }
749
- }
750
- if (pastSwap.isQuoteExpired()) {
751
- removeSwaps.push(pastSwap);
752
- continue;
753
- }
754
- if (changed)
755
- changedSwaps.add(pastSwap);
756
- if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
757
- if (pastSwap._data != null)
758
- (broadcastedOrConfirmedSwaps[pastSwap._contractVersion ?? "v1"] ??= []).push(pastSwap);
759
- }
760
- }
761
- for (let contractVersion in broadcastedOrConfirmedSwaps) {
762
- if (this.versionedContracts[contractVersion] == null) {
763
- this.logger.warn(`_checkPastSwaps(): No contract was found for ${this.chainIdentifier} version ${contractVersion}! Skipping these swaps!`);
764
- continue;
765
- }
766
- const _broadcastedOrConfirmedSwaps = broadcastedOrConfirmedSwaps[contractVersion];
767
- const checkWithdrawalStateSwaps = [];
768
- const _fronts = await this._contract(contractVersion).getFronterAddresses(_broadcastedOrConfirmedSwaps.map(val => ({
769
- ...val.getSpvVaultData(),
770
- withdrawal: val._data
771
- })));
772
- const _vaultUtxos = await this._contract(contractVersion).getVaultLatestUtxos(_broadcastedOrConfirmedSwaps.map(val => val.getSpvVaultData()));
773
- for (const pastSwap of _broadcastedOrConfirmedSwaps) {
774
- const fronterAddress = _fronts[pastSwap._data.getTxId()];
775
- const vault = pastSwap.getSpvVaultData();
776
- const latestVaultUtxo = _vaultUtxos[vault.owner]?.[vault.vaultId.toString(10)];
777
- if (fronterAddress === undefined)
778
- this.logger.warn(`_checkPastSwaps(): No fronter address returned for ${pastSwap._data.getTxId()}`);
779
- if (latestVaultUtxo === undefined)
780
- this.logger.warn(`_checkPastSwaps(): No last vault utxo returned for ${pastSwap._data.getTxId()}`);
781
- if (await pastSwap._shouldCheckWithdrawalState(fronterAddress, latestVaultUtxo))
782
- checkWithdrawalStateSwaps.push(pastSwap);
783
- }
784
- const withdrawalStates = await this._contract(contractVersion).getWithdrawalStates(checkWithdrawalStateSwaps.map(val => ({
785
- withdrawal: val._data,
786
- scStartBlockheight: val._genesisSmartChainBlockHeight
787
- })));
788
- for (const pastSwap of checkWithdrawalStateSwaps) {
789
- const status = withdrawalStates[pastSwap._data.getTxId()];
790
- if (status == null) {
791
- this.logger.warn(`_checkPastSwaps(): No withdrawal state returned for ${pastSwap._data.getTxId()}`);
792
- continue;
793
- }
794
- this.logger.debug("syncStateFromChain(): status of " + pastSwap._data.btcTx.txid, status?.type);
795
- let changed = false;
796
- switch (status.type) {
797
- case base_1.SpvWithdrawalStateType.FRONTED:
798
- pastSwap._frontTxId = status.txId;
799
- pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
800
- changed ||= true;
801
- break;
802
- case base_1.SpvWithdrawalStateType.CLAIMED:
803
- pastSwap._claimTxId = status.txId;
804
- pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
805
- changed ||= true;
806
- break;
807
- case base_1.SpvWithdrawalStateType.CLOSED:
808
- pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLOSED;
809
- changed ||= true;
810
- break;
811
- }
812
- if (changed)
813
- changedSwaps.add(pastSwap);
814
- }
815
- }
816
- return {
817
- changedSwaps: Array.from(changedSwaps),
818
- removeSwaps
819
- };
820
- }
821
- }
822
- exports.SpvFromBTCWrapper = SpvFromBTCWrapper;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpvFromBTCWrapper = exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE = exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE = void 0;
4
+ const ISwapWrapper_1 = require("../ISwapWrapper");
5
+ const base_1 = require("@atomiqlabs/base");
6
+ const SpvFromBTCSwap_1 = require("./SpvFromBTCSwap");
7
+ const utils_1 = require("@scure/btc-signer/utils");
8
+ const SwapType_1 = require("../../enums/SwapType");
9
+ const Intermediary_1 = require("../../intermediaries/Intermediary");
10
+ const Utils_1 = require("../../utils/Utils");
11
+ const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
12
+ const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
13
+ const RequestError_1 = require("../../errors/RequestError");
14
+ const IntermediaryError_1 = require("../../errors/IntermediaryError");
15
+ const btc_signer_1 = require("@scure/btc-signer");
16
+ const RetryUtils_1 = require("../../utils/RetryUtils");
17
+ const UserError_1 = require("../../errors/UserError");
18
+ const utils_2 = require("../../bitcoin/coinselect2/utils");
19
+ const BitcoinWallet_1 = require("../../bitcoin/wallet/BitcoinWallet");
20
+ exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE = "p2tr";
21
+ exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE = "p2wpkh";
22
+ /**
23
+ * New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
24
+ * any initiation on the destination chain, and with the added possibility for the user to receive
25
+ * a native token on the destination chain as part of the swap (a "gas drop" feature).
26
+ *
27
+ * @category Swaps/Bitcoin → Smart chain
28
+ */
29
+ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
30
+ /**
31
+ * @param chainIdentifier
32
+ * @param unifiedStorage Storage interface for the current environment
33
+ * @param unifiedChainEvents On-chain event listener
34
+ * @param chain
35
+ * @param prices Pricing to use
36
+ * @param tokens
37
+ * @param versionedContracts
38
+ * @param versionedSynchronizer
39
+ * @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
40
+ * @param options
41
+ * @param events Instance to use for emitting events
42
+ */
43
+ constructor(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, versionedContracts, versionedSynchronizer, btcRpc, options, events) {
44
+ super(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, {
45
+ ...options,
46
+ bitcoinNetwork: options?.bitcoinNetwork ?? utils_1.TEST_NETWORK,
47
+ maxConfirmations: options?.maxConfirmations ?? 6,
48
+ bitcoinBlocktime: options?.bitcoinBlocktime ?? 10 * 60,
49
+ maxTransactionsDelta: options?.maxTransactionsDelta ?? 3,
50
+ maxRawAmountAdjustmentDifferencePPM: options?.maxRawAmountAdjustmentDifferencePPM ?? 100,
51
+ maxBtcFeeOffset: options?.maxBtcFeeOffset ?? 10,
52
+ maxBtcFeeMultiplier: options?.maxBtcFeeMultiplier ?? 1.5
53
+ }, events);
54
+ this.TYPE = SwapType_1.SwapType.SPV_VAULT_FROM_BTC;
55
+ /**
56
+ * @internal
57
+ */
58
+ this._claimableSwapStates = [SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED];
59
+ /**
60
+ * @internal
61
+ */
62
+ this._swapDeserializer = SpvFromBTCSwap_1.SpvFromBTCSwap;
63
+ /**
64
+ * @internal
65
+ */
66
+ this.btcRelay = (version) => {
67
+ const _version = version ?? "v1";
68
+ const data = this.versionedContracts[_version];
69
+ if (data == null)
70
+ throw new Error(`Invalid contract version ${_version} requested`);
71
+ return data.btcRelay;
72
+ };
73
+ /**
74
+ * @internal
75
+ */
76
+ this.tickSwapState = [
77
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED,
78
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
79
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED,
80
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED,
81
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED
82
+ ];
83
+ /**
84
+ * @internal
85
+ */
86
+ this._synchronizer = (version) => {
87
+ const _version = version ?? "v1";
88
+ const data = this.versionedSynchronizer[_version];
89
+ if (data == null)
90
+ throw new Error(`Invalid contract version ${_version} requested`);
91
+ return data.synchronizer;
92
+ };
93
+ /**
94
+ * @internal
95
+ */
96
+ this._contract = (version) => {
97
+ const _version = version ?? "v1";
98
+ const data = this.versionedContracts[_version];
99
+ if (data == null)
100
+ throw new Error(`Invalid contract version ${_version} requested`);
101
+ return data.spvVaultContract;
102
+ };
103
+ /**
104
+ * @internal
105
+ */
106
+ this._spvWithdrawalDataDeserializer = (version) => {
107
+ const _version = version ?? "v1";
108
+ const data = this.versionedContracts[_version];
109
+ if (data == null)
110
+ throw new Error(`Invalid contract version ${_version} requested`);
111
+ return data.spvVaultWithdrawalDataConstructor;
112
+ };
113
+ /**
114
+ * @internal
115
+ */
116
+ this._pendingSwapStates = [
117
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED,
118
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED,
119
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED,
120
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED,
121
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED,
122
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED,
123
+ SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED
124
+ ];
125
+ this.versionedContracts = {};
126
+ this.versionedSynchronizer = {};
127
+ this.versionedContracts = versionedContracts;
128
+ this.versionedSynchronizer = versionedSynchronizer;
129
+ this._btcRpc = btcRpc;
130
+ }
131
+ async processEventFront(event, swap) {
132
+ if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
133
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
134
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
135
+ swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
136
+ await swap._setBitcoinTxId(event.btcTxId).catch(e => {
137
+ this.logger.warn("processEventFront(): Failed to set bitcoin txId: ", e);
138
+ });
139
+ return true;
140
+ }
141
+ return false;
142
+ }
143
+ async processEventClaim(event, swap) {
144
+ if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
145
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
146
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED ||
147
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
148
+ swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
149
+ await swap._setBitcoinTxId(event.btcTxId).catch(e => {
150
+ this.logger.warn("processEventClaim(): Failed to set bitcoin txId: ", e);
151
+ });
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+ processEventClose(event, swap) {
157
+ if (swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
158
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
159
+ swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED || swap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
160
+ swap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLOSED;
161
+ return Promise.resolve(true);
162
+ }
163
+ return Promise.resolve(false);
164
+ }
165
+ /**
166
+ * @inheritDoc
167
+ * @internal
168
+ */
169
+ async processEvent(event, swap) {
170
+ if (swap == null)
171
+ return;
172
+ let swapChanged = false;
173
+ if (event instanceof base_1.SpvVaultFrontEvent) {
174
+ swapChanged = await this.processEventFront(event, swap);
175
+ if (event.meta?.txId != null && swap._frontTxId !== event.meta.txId) {
176
+ swap._frontTxId = event.meta.txId;
177
+ swapChanged ||= true;
178
+ }
179
+ }
180
+ if (event instanceof base_1.SpvVaultClaimEvent) {
181
+ swapChanged = await this.processEventClaim(event, swap);
182
+ if (event.meta?.txId != null && swap._claimTxId !== event.meta.txId) {
183
+ swap._claimTxId = event.meta.txId;
184
+ swapChanged ||= true;
185
+ }
186
+ }
187
+ if (event instanceof base_1.SpvVaultCloseEvent) {
188
+ swapChanged = await this.processEventClose(event, swap);
189
+ }
190
+ this.logger.info("processEvents(): " + event.constructor.name + " processed for " + swap.getId() + " swap: ", swap);
191
+ if (swapChanged) {
192
+ await swap._saveAndEmit();
193
+ }
194
+ }
195
+ /**
196
+ * Pre-fetches latest finalized block height of the smart chain
197
+ *
198
+ * @param abortController
199
+ * @private
200
+ */
201
+ async preFetchFinalizedBlockHeight(abortController) {
202
+ try {
203
+ const block = await this._chain.getFinalizedBlock();
204
+ return block.height;
205
+ }
206
+ catch (e) {
207
+ abortController.abort(e);
208
+ }
209
+ }
210
+ /**
211
+ * Pre-fetches caller (watchtower) bounty data for the swap. Doesn't throw, instead returns null and aborts the
212
+ * provided abortController
213
+ *
214
+ * @param amountData
215
+ * @param options Options as passed to the swap creation function
216
+ * @param abortController
217
+ * @param contractVersion
218
+ * @private
219
+ */
220
+ async preFetchCallerFeeInNativeToken(amountData, options, abortController, contractVersion) {
221
+ if (options.unsafeZeroWatchtowerFee)
222
+ return 0n;
223
+ if (amountData.amount === 0n)
224
+ return 0n;
225
+ try {
226
+ const [feePerBlock, btcRelayData, currentBtcBlock, claimFeeRate] = await Promise.all([
227
+ this.btcRelay(contractVersion).getFeePerBlock(),
228
+ this.btcRelay(contractVersion).getTipData(),
229
+ this._btcRpc.getTipHeight(),
230
+ this._contract(contractVersion).getClaimFee(this._chain.randomAddress())
231
+ ]);
232
+ if (btcRelayData == null)
233
+ throw new Error("Btc relay doesn't seem to be initialized!");
234
+ const currentBtcRelayBlock = btcRelayData.blockheight;
235
+ const blockDelta = Math.max(currentBtcBlock - currentBtcRelayBlock + this._options.maxConfirmations, 0);
236
+ const totalFeeInNativeToken = ((BigInt(blockDelta) * feePerBlock) +
237
+ (claimFeeRate * BigInt(this._options.maxTransactionsDelta))) * BigInt(Math.floor(options.feeSafetyFactor * 1000000)) / 1000000n;
238
+ return totalFeeInNativeToken;
239
+ }
240
+ catch (e) {
241
+ abortController.abort(e);
242
+ }
243
+ }
244
+ /**
245
+ * Pre-fetches caller (watchtower) bounty data for the swap. Doesn't throw, instead returns null and aborts the
246
+ * provided abortController
247
+ *
248
+ * @param amountPrefetch
249
+ * @param totalFeeInNativeTokenPrefetch
250
+ * @param amountData
251
+ * @param options Options as passed to the swap creation function
252
+ * @param pricePrefetch
253
+ * @param nativeTokenPricePrefetch
254
+ * @param abortSignal
255
+ * @private
256
+ */
257
+ async computeCallerFeeShare(amountPrefetch, totalFeeInNativeTokenPrefetch, amountData, options, pricePrefetch, nativeTokenPricePrefetch, abortSignal) {
258
+ if (options.unsafeZeroWatchtowerFee)
259
+ return 0n;
260
+ const amount = await (0, Utils_1.throwIfUndefined)(amountPrefetch, "Cannot get swap amount!");
261
+ if (amount === 0n)
262
+ return 0n;
263
+ const totalFeeInNativeToken = await (0, Utils_1.throwIfUndefined)(totalFeeInNativeTokenPrefetch, "Cannot get total fee in native token!");
264
+ const nativeTokenPrice = await nativeTokenPricePrefetch;
265
+ let payoutAmount;
266
+ if (amountData.exactIn) {
267
+ //Convert input amount in BTC to
268
+ const amountInNativeToken = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, amount, this._chain.getNativeCurrencyAddress(), abortSignal, nativeTokenPrice);
269
+ payoutAmount = amountInNativeToken - totalFeeInNativeToken;
270
+ }
271
+ else {
272
+ if (amountData.token === this._chain.getNativeCurrencyAddress()) {
273
+ //Both amounts in same currency
274
+ payoutAmount = amount;
275
+ }
276
+ else {
277
+ //Need to convert both to native currency
278
+ const btcAmount = await this._prices.getToBtcSwapAmount(this.chainIdentifier, amount, amountData.token, abortSignal, await pricePrefetch);
279
+ payoutAmount = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, btcAmount, this._chain.getNativeCurrencyAddress(), abortSignal, nativeTokenPrice);
280
+ }
281
+ }
282
+ this.logger.debug("computeCallerFeeShare(): Caller fee in native token: " + totalFeeInNativeToken.toString(10) + " total payout in native token: " + payoutAmount.toString(10));
283
+ const callerFeeShare = ((totalFeeInNativeToken * 100000n) + payoutAmount - 1n) / payoutAmount; //Make sure to round up here
284
+ if (callerFeeShare < 0n)
285
+ return 0n;
286
+ if (callerFeeShare >= 2n ** 20n)
287
+ return 2n ** 20n - 1n;
288
+ return callerFeeShare;
289
+ }
290
+ /**
291
+ * Verifies response returned from intermediary
292
+ *
293
+ * @param resp Response as returned by the intermediary
294
+ * @param amountData
295
+ * @param lp Intermediary
296
+ * @param options Options as passed to the swap creation function
297
+ * @param callerFeeShare
298
+ * @param maxBitcoinFeeRatePromise Maximum accepted fee rate from the LPs
299
+ * @param bitcoinFeeRatePromise
300
+ * @param abortSignal
301
+ * @private
302
+ * @throws {IntermediaryError} in case the response is invalid
303
+ */
304
+ async verifyReturnedData(resp, amountData, lp, options, callerFeeShare, maxBitcoinFeeRatePromise, bitcoinFeeRatePromise, abortSignal) {
305
+ const btcFeeRate = await (0, Utils_1.throwIfUndefined)(maxBitcoinFeeRatePromise, "Bitcoin fee rate promise failed!");
306
+ abortSignal.throwIfAborted();
307
+ if (btcFeeRate != null && resp.btcFeeRate > btcFeeRate)
308
+ throw new IntermediaryError_1.IntermediaryError(`Required bitcoin fee rate returned from the LP is too high! Maximum accepted: ${btcFeeRate} sats/vB, required by LP: ${resp.btcFeeRate} sats/vB`);
309
+ const lpVersion = lp.getContractVersion(this.chainIdentifier);
310
+ //Vault related
311
+ let vaultScript;
312
+ let vaultAddressType;
313
+ let btcAddressScript;
314
+ let btcAddressType;
315
+ //Ensure valid btc addresses returned
316
+ try {
317
+ vaultScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.vaultBtcAddress);
318
+ vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)(vaultScript);
319
+ btcAddressScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.btcAddress);
320
+ btcAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)(btcAddressScript);
321
+ }
322
+ catch (e) {
323
+ throw new IntermediaryError_1.IntermediaryError("Invalid btc address data returned", e);
324
+ }
325
+ const decodedUtxo = resp.btcUtxo.split(":");
326
+ if (resp.address !== lp.getAddress(this.chainIdentifier) || //Ensure the LP is indeed the vault owner
327
+ resp.vaultId < 0n || //Ensure vaultId is not negative
328
+ vaultScript == null || //Make sure vault script is parsable and of known type
329
+ btcAddressScript == null || //Make sure btc address script is parsable and of known type
330
+ btcAddressType !== exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE || //Constrain the btc address script type
331
+ vaultAddressType !== exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE || //Constrain the vault script type
332
+ decodedUtxo.length !== 2 || decodedUtxo[0].length !== 64 || isNaN(parseInt(decodedUtxo[1])) || //Check valid UTXO
333
+ resp.btcFeeRate < 1 || resp.btcFeeRate > 10000 //Sanity check on the returned BTC fee rate
334
+ )
335
+ throw new IntermediaryError_1.IntermediaryError("Invalid vault data returned!");
336
+ //Amounts sanity
337
+ if (resp.btcAmountSwap + resp.btcAmountGas !== resp.btcAmount)
338
+ throw new Error("Btc amount mismatch");
339
+ if (resp.swapFeeBtc + resp.gasSwapFeeBtc !== resp.totalFeeBtc)
340
+ throw new Error("Btc fee mismatch");
341
+ //TODO: For now ensure fees are at 0
342
+ if (resp.callerFeeShare !== callerFeeShare ||
343
+ resp.frontingFeeShare !== 0n ||
344
+ resp.executionFeeShare !== 0n)
345
+ throw new IntermediaryError_1.IntermediaryError("Invalid caller/fronting/execution fee returned");
346
+ //Check expiry
347
+ const timeNowSeconds = Math.floor(Date.now() / 1000);
348
+ if (resp.expiry < timeNowSeconds)
349
+ throw new IntermediaryError_1.IntermediaryError(`Quote already expired, expiry: ${resp.expiry}, systemTime: ${timeNowSeconds}, clockAdjusted: ${Date._now != null}`);
350
+ let utxo = resp.btcUtxo.toLowerCase();
351
+ const [txId, voutStr] = utxo.split(":");
352
+ const abortController = (0, Utils_1.extendAbortController)(abortSignal);
353
+ let [vault, { vaultUtxoValue, btcTx }] = await Promise.all([
354
+ (async () => {
355
+ //Fetch vault data
356
+ let vault;
357
+ try {
358
+ vault = await this._contract(lpVersion).getVaultData(resp.address, resp.vaultId);
359
+ }
360
+ catch (e) {
361
+ this.logger.error("Error getting spv vault (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
362
+ throw new IntermediaryError_1.IntermediaryError("Spv swap vault not found", e);
363
+ }
364
+ abortController.signal.throwIfAborted();
365
+ //Make sure vault is opened
366
+ if (vault == null || !vault.isOpened())
367
+ throw new IntermediaryError_1.IntermediaryError("Returned spv swap vault is not opened!");
368
+ //Make sure the vault doesn't require insane amount of confirmations
369
+ if (vault.getConfirmations() > this._options.maxConfirmations)
370
+ throw new IntermediaryError_1.IntermediaryError("SPV swap vault needs too many confirmations: " + vault.getConfirmations());
371
+ const tokenData = vault.getTokenData();
372
+ //Amounts - make sure the amounts match
373
+ if (amountData.exactIn) {
374
+ if (!resp.usedUtxoInputCalculation) {
375
+ //Legacy calculation
376
+ if (resp.btcAmount !== amountData.amount)
377
+ throw new IntermediaryError_1.IntermediaryError("Invalid amount returned");
378
+ }
379
+ else {
380
+ //Implies the raw UTXOs were passed for amount derivation
381
+ //Verify the derivation was done correctly
382
+ if (options.sourceWalletUtxos == null)
383
+ throw new IntermediaryError_1.IntermediaryError("Invalid usedUtxoInputCalcuation return value");
384
+ if (bitcoinFeeRatePromise == null)
385
+ throw new Error("bitcoinFeeRatePromise must be passed for UTXO-based input amount calculation checks");
386
+ const walletUtxos = await options.sourceWalletUtxos;
387
+ const bitcoinFeeRate = await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise, "Failed to fetch bitcoin fee rate!");
388
+ const { balance } = BitcoinWallet_1.BitcoinWallet.getSpendableBalance(walletUtxos, Math.max(resp.btcFeeRate, bitcoinFeeRate), this.getDummySwapPsbt(options.gasAmount !== 0n), exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE);
389
+ if (resp.btcAmount !== balance)
390
+ throw new IntermediaryError_1.IntermediaryError(`Invalid amount returned, expected: ${balance.toString(10)}, got: ${resp.btcAmount.toString(10)}`);
391
+ }
392
+ }
393
+ else {
394
+ //Check the difference between amount adjusted due to scaling to raw amount
395
+ const adjustedAmount = amountData.amount / tokenData[0].multiplier * tokenData[0].multiplier;
396
+ const adjustmentPPM = (amountData.amount - adjustedAmount) * 1000000n / amountData.amount;
397
+ if (adjustmentPPM > this._options.maxRawAmountAdjustmentDifferencePPM)
398
+ throw new IntermediaryError_1.IntermediaryError("Invalid amount0 multiplier used, rawAmount diff too high");
399
+ if (resp.total !== adjustedAmount)
400
+ throw new IntermediaryError_1.IntermediaryError("Invalid total returned");
401
+ }
402
+ if (options.gasAmount === 0n) {
403
+ if (resp.totalGas !== 0n)
404
+ throw new IntermediaryError_1.IntermediaryError("Invalid gas total returned");
405
+ }
406
+ else {
407
+ //Check the difference between amount adjusted due to scaling to raw amount
408
+ const adjustedGasAmount = options.gasAmount / tokenData[0].multiplier * tokenData[0].multiplier;
409
+ const adjustmentPPM = (options.gasAmount - adjustedGasAmount) * 1000000n / options.gasAmount;
410
+ if (adjustmentPPM > this._options.maxRawAmountAdjustmentDifferencePPM)
411
+ throw new IntermediaryError_1.IntermediaryError("Invalid amount1 multiplier used, rawAmount diff too high");
412
+ if (resp.totalGas !== adjustedGasAmount)
413
+ throw new IntermediaryError_1.IntermediaryError("Invalid gas total returned");
414
+ }
415
+ return vault;
416
+ })(),
417
+ (async () => {
418
+ //Require the vault UTXO to have at least 1 confirmation
419
+ let btcTx = await this._btcRpc.getTransaction(txId);
420
+ if (btcTx == null)
421
+ throw new IntermediaryError_1.IntermediaryError("Invalid UTXO, doesn't exist (txId)");
422
+ abortController.signal.throwIfAborted();
423
+ if (btcTx.confirmations == null || btcTx.confirmations < 1)
424
+ throw new IntermediaryError_1.IntermediaryError("SPV vault UTXO not confirmed");
425
+ const vout = parseInt(voutStr);
426
+ if (btcTx.outs[vout] == null)
427
+ throw new IntermediaryError_1.IntermediaryError("Invalid UTXO, doesn't exist");
428
+ const vaultUtxoValue = btcTx.outs[vout].value;
429
+ return { btcTx, vaultUtxoValue };
430
+ })(),
431
+ (async () => {
432
+ //Require vault UTXO is unspent
433
+ if (await this._btcRpc.isSpent(utxo))
434
+ throw new IntermediaryError_1.IntermediaryError("Returned spv vault UTXO is already spent", null, true);
435
+ abortController.signal.throwIfAborted();
436
+ })()
437
+ ]).catch(e => {
438
+ abortController.abort(e);
439
+ throw e;
440
+ });
441
+ this.logger.debug("verifyReturnedData(): Vault UTXO: " + vault.getUtxo() + " current utxo: " + utxo);
442
+ //Trace returned utxo back to what's saved on-chain
443
+ let pendingWithdrawals = [];
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)
450
+ throw new IntermediaryError_1.IntermediaryError("Invalid ancestor transaction (not found)");
451
+ btcTx = _btcTx;
452
+ }
453
+ const withdrawalData = await this._contract(lpVersion).getWithdrawalData(btcTx);
454
+ abortSignal.throwIfAborted();
455
+ pendingWithdrawals.unshift(withdrawalData);
456
+ utxo = pendingWithdrawals[0].getSpentVaultUtxo();
457
+ this.logger.debug("verifyReturnedData(): Vault UTXO: " + vault.getUtxo() + " current utxo: " + utxo);
458
+ if (pendingWithdrawals.length >= this._options.maxTransactionsDelta)
459
+ throw new IntermediaryError_1.IntermediaryError("BTC <> SC state difference too deep, maximum: " + this._options.maxTransactionsDelta);
460
+ }
461
+ //Verify that the vault has enough balance after processing all pending withdrawals
462
+ let vaultBalances;
463
+ try {
464
+ vaultBalances = vault.calculateStateAfter(pendingWithdrawals);
465
+ }
466
+ catch (e) {
467
+ this.logger.error("Error calculating spv vault balance (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
468
+ throw new IntermediaryError_1.IntermediaryError("Spv swap vault balance prediction failed", e);
469
+ }
470
+ if (vaultBalances.balances[0].scaledAmount < resp.total)
471
+ throw new IntermediaryError_1.IntermediaryError("SPV swap vault, insufficient balance, required: " + resp.total.toString(10) +
472
+ " has: " + vaultBalances.balances[0].scaledAmount.toString(10));
473
+ if (vaultBalances.balances[1].scaledAmount < resp.totalGas)
474
+ throw new IntermediaryError_1.IntermediaryError("SPV swap vault, insufficient balance, required: " + resp.totalGas.toString(10) +
475
+ " has: " + vaultBalances.balances[1].scaledAmount.toString(10));
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(lpVersion).checkWithdrawalTx(withdrawal);
480
+ }
481
+ }
482
+ catch (e) {
483
+ this.logger.error("Error calculating spv vault balance (owner: " + resp.address + " vaultId: " + resp.vaultId.toString(10) + "): ", e);
484
+ throw new IntermediaryError_1.IntermediaryError("Spv swap vault balance prediction failed", e);
485
+ }
486
+ abortSignal.throwIfAborted();
487
+ return {
488
+ vault,
489
+ vaultUtxoValue
490
+ };
491
+ }
492
+ async amountPrefetch(amountData, bitcoinFeeRatePromise, walletUtxosPromise, includeGas, abortController) {
493
+ if (amountData.amount != null)
494
+ return amountData.amount;
495
+ try {
496
+ const bitcoinFeeRate = await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise, "Cannot fetch Bitcoin fee rate!");
497
+ if (walletUtxosPromise == null)
498
+ throw new UserError_1.UserError("Cannot use empty amount without passing UTXOs!");
499
+ const walletUtxos = await walletUtxosPromise;
500
+ if (walletUtxos.length === 0)
501
+ throw new UserError_1.UserError("Wallet doesn't have any BTC balance");
502
+ const spendableBalance = await BitcoinWallet_1.BitcoinWallet.getSpendableBalance(walletUtxos, bitcoinFeeRate, this.getDummySwapPsbt(includeGas), exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE);
503
+ return spendableBalance.balance;
504
+ }
505
+ catch (e) {
506
+ abortController.abort(e);
507
+ }
508
+ }
509
+ bitcoinFeeRatePrefetch(options, abortController) {
510
+ let bitcoinFeeRatePromise;
511
+ if (options?.sourceWalletUtxos != null) {
512
+ if (options.bitcoinFeeRate != null) {
513
+ bitcoinFeeRatePromise = options.bitcoinFeeRate.then(value => {
514
+ if (options.maxAllowedBitcoinFeeRate != Infinity && options.maxAllowedBitcoinFeeRate < value)
515
+ throw new Error("Passed `maxAllowedBitcoinFeeRate` cannot be lower than `bitcoinFeeRate`");
516
+ return value;
517
+ });
518
+ }
519
+ else {
520
+ bitcoinFeeRatePromise = this._btcRpc.getFeeRate().then(value => {
521
+ if (options.maxAllowedBitcoinFeeRate != Infinity && value > options.maxAllowedBitcoinFeeRate)
522
+ return options.maxAllowedBitcoinFeeRate;
523
+ return value;
524
+ });
525
+ }
526
+ bitcoinFeeRatePromise = bitcoinFeeRatePromise.catch(e => {
527
+ abortController.abort(e);
528
+ return undefined;
529
+ });
530
+ }
531
+ const maxBitcoinFeeRatePromise = options.maxAllowedBitcoinFeeRate != Infinity
532
+ ? Promise.resolve(options.maxAllowedBitcoinFeeRate)
533
+ : (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise ?? options.bitcoinFeeRate ?? this._btcRpc.getFeeRate())
534
+ .then(x => this._options.maxBtcFeeOffset + (x * this._options.maxBtcFeeMultiplier))
535
+ .catch(e => {
536
+ abortController.abort(e);
537
+ return undefined;
538
+ });
539
+ return {
540
+ bitcoinFeeRatePromise,
541
+ maxBitcoinFeeRatePromise
542
+ };
543
+ }
544
+ /**
545
+ * Returns a newly created Bitcoin -> Smart chain swap using the SPV vault (UTXO-controlled vault) swap protocol,
546
+ * with the passed amount. Also allows specifying additional "gas drop" native token that the receipient receives
547
+ * on the destination chain in the `options` argument.
548
+ *
549
+ * @param recipient Recipient address on the destination smart chain
550
+ * @param amountData Amount, token and exact input/output data for to swap
551
+ * @param lps An array of intermediaries (LPs) to get the quotes from
552
+ * @param options Optional additional quote options
553
+ * @param additionalParams Optional additional parameters sent to the LP when creating the swap
554
+ * @param abortSignal Abort signal
555
+ */
556
+ create(recipient, amountData, lps, options, additionalParams, abortSignal) {
557
+ const _options = {
558
+ gasAmount: this.parseGasAmount(options?.gasAmount),
559
+ unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
560
+ feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
561
+ maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity,
562
+ sourceWalletUtxos: options?.sourceWalletUtxos == undefined
563
+ ? undefined
564
+ : options?.sourceWalletUtxos instanceof Promise ? options.sourceWalletUtxos : Promise.resolve(options.sourceWalletUtxos),
565
+ bitcoinFeeRate: options?.bitcoinFeeRate == undefined
566
+ ? undefined
567
+ : options?.bitcoinFeeRate instanceof Promise ? options.bitcoinFeeRate : Promise.resolve(options.bitcoinFeeRate),
568
+ };
569
+ if (_options.gasAmount !== 0n &&
570
+ (this._chain.shouldGetNativeTokenDrop != null
571
+ ? !this._chain.shouldGetNativeTokenDrop(amountData.token)
572
+ : amountData.token === this._chain.getNativeCurrencyAddress()))
573
+ throw new UserError_1.UserError("Cannot specify `gasAmount` for swaps to a native token!");
574
+ if (amountData.amount == null && options?.sourceWalletUtxos == null)
575
+ throw new UserError_1.UserError("Source wallet UTXOs need to be passed when amount is null!");
576
+ if (amountData.amount == null && !amountData.exactIn)
577
+ throw new UserError_1.UserError("Amount can be null only for exactIn swaps!");
578
+ if (amountData.amount != null && options?.sourceWalletUtxos != null)
579
+ throw new UserError_1.UserError("Source wallet UTXOs cannot be passed while specifying an input amount!");
580
+ const lpVersions = Intermediary_1.Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
581
+ const _abortController = (0, Utils_1.extendAbortController)(abortSignal);
582
+ const pricePrefetchPromise = this.preFetchPrice(amountData, _abortController.signal);
583
+ const usdPricePrefetchPromise = this.preFetchUsdPrice(_abortController.signal);
584
+ const finalizedBlockHeightPrefetchPromise = this.preFetchFinalizedBlockHeight(_abortController);
585
+ const nativeTokenAddress = this._chain.getNativeCurrencyAddress();
586
+ const gasTokenPricePrefetchPromise = _options.gasAmount === 0n ?
587
+ undefined :
588
+ this.preFetchPrice({ token: nativeTokenAddress }, _abortController.signal);
589
+ const callerFeePrefetchPromise = (0, Utils_1.mapArrayToObject)(lpVersions, (contractVersion) => {
590
+ return this.preFetchCallerFeeInNativeToken(amountData, _options, _abortController, contractVersion);
591
+ });
592
+ const { maxBitcoinFeeRatePromise, bitcoinFeeRatePromise } = this.bitcoinFeeRatePrefetch(_options, _abortController);
593
+ const amountPromise = this.amountPrefetch(amountData, maxBitcoinFeeRatePromise, _options.sourceWalletUtxos, _options.gasAmount !== 0n, _abortController);
594
+ return lps.map(lp => {
595
+ return {
596
+ intermediary: lp,
597
+ quote: (0, RetryUtils_1.tryWithRetries)(async () => {
598
+ if (lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC] == null)
599
+ throw new Error("LP service for processing spv vault swaps not found!");
600
+ const version = lp.getContractVersion(this.chainIdentifier);
601
+ const abortController = (0, Utils_1.extendAbortController)(_abortController.signal);
602
+ const callerFeeRatePromise = this.computeCallerFeeShare(amountPromise, callerFeePrefetchPromise[version], amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, abortController.signal);
603
+ try {
604
+ const resp = await (0, RetryUtils_1.tryWithRetries)(async (retryCount) => {
605
+ return await IntermediaryAPI_1.IntermediaryAPI.prepareSpvFromBTC(this.chainIdentifier, lp.url, {
606
+ address: recipient,
607
+ amount: (0, Utils_1.throwIfUndefined)(amountPromise, "Failed to compute swap amount"),
608
+ token: amountData.token.toString(),
609
+ exactOut: !amountData.exactIn,
610
+ gasToken: nativeTokenAddress,
611
+ gasAmount: _options.gasAmount,
612
+ callerFeeRate: (0, Utils_1.throwIfUndefined)(callerFeeRatePromise, "Caller fee prefetch failed!"),
613
+ frontingFeeRate: 0n,
614
+ stickyAddress: options?.stickyAddress,
615
+ amountUtxos: _options.sourceWalletUtxos != null
616
+ ? _options.sourceWalletUtxos.then(utxos => {
617
+ if (utxos.length === 0)
618
+ return undefined;
619
+ return utxos.map(utxo => ({
620
+ value: utxo.value,
621
+ vSize: utils_2.utils.inputBytes({ type: utxo.type }),
622
+ cpfp: utxo.cpfp == null ? undefined : { effectiveVSize: utxo.cpfp?.txVsize, effectiveFeeRate: utxo.cpfp?.txEffectiveFeeRate }
623
+ }));
624
+ })
625
+ : undefined,
626
+ amountFeeRate: bitcoinFeeRatePromise,
627
+ additionalParams
628
+ }, this._options.postRequestTimeout, abortController.signal, retryCount > 0 ? false : undefined);
629
+ }, undefined, e => e instanceof RequestError_1.RequestError, abortController.signal);
630
+ this.logger.debug("create(" + lp.url + "): LP response: ", resp);
631
+ const callerFeeShare = await callerFeeRatePromise;
632
+ const amount = await (0, Utils_1.throwIfUndefined)(amountPromise);
633
+ const [pricingInfo, gasPricingInfo, { vault, vaultUtxoValue }] = await Promise.all([
634
+ this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], false, resp.btcAmountSwap, resp.total * (100000n + callerFeeShare) / 100000n, amountData.token, { swapFeeBtc: resp.swapFeeBtc }, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
635
+ _options.gasAmount === 0n ? Promise.resolve(undefined) : this.verifyReturnedPrice({ ...lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], swapBaseFee: 0 }, //Base fee should be charged only on the amount, not on gas
636
+ false, resp.btcAmountGas, resp.totalGas * (100000n + callerFeeShare) / 100000n, nativeTokenAddress, { swapFeeBtc: resp.gasSwapFeeBtc }, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
637
+ this.verifyReturnedData(resp, { ...amountData, amount }, lp, _options, callerFeeShare, maxBitcoinFeeRatePromise, bitcoinFeeRatePromise, abortController.signal)
638
+ ]);
639
+ let minimumBtcFeeRate = resp.btcFeeRate;
640
+ if (bitcoinFeeRatePromise != null)
641
+ minimumBtcFeeRate = Math.max(minimumBtcFeeRate, await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise));
642
+ const swapInit = {
643
+ pricingInfo,
644
+ url: lp.url,
645
+ expiry: resp.expiry * 1000,
646
+ swapFee: resp.swapFee,
647
+ swapFeeBtc: resp.swapFeeBtc,
648
+ exactIn: amountData.exactIn ?? true,
649
+ quoteId: resp.quoteId,
650
+ recipient,
651
+ vaultOwner: resp.address,
652
+ vaultId: resp.vaultId,
653
+ vaultRequiredConfirmations: vault.getConfirmations(),
654
+ vaultTokenMultipliers: vault.getTokenData().map(val => val.multiplier),
655
+ vaultBtcAddress: resp.vaultBtcAddress,
656
+ vaultUtxo: resp.btcUtxo,
657
+ vaultUtxoValue: BigInt(vaultUtxoValue),
658
+ btcDestinationAddress: resp.btcAddress,
659
+ btcAmount: resp.btcAmount,
660
+ btcAmountSwap: resp.btcAmountSwap,
661
+ btcAmountGas: resp.btcAmountGas,
662
+ minimumBtcFeeRate,
663
+ outputTotalSwap: resp.total,
664
+ outputSwapToken: amountData.token,
665
+ outputTotalGas: resp.totalGas,
666
+ outputGasToken: nativeTokenAddress,
667
+ gasSwapFeeBtc: resp.gasSwapFeeBtc,
668
+ gasSwapFee: resp.gasSwapFee,
669
+ gasPricingInfo,
670
+ callerFeeShare: resp.callerFeeShare,
671
+ frontingFeeShare: resp.frontingFeeShare,
672
+ executionFeeShare: resp.executionFeeShare,
673
+ genesisSmartChainBlockHeight: await (0, Utils_1.throwIfUndefined)(finalizedBlockHeightPrefetchPromise, "Finalize block height promise failed!"),
674
+ contractVersion: version
675
+ };
676
+ const quote = new SpvFromBTCSwap_1.SpvFromBTCSwap(this, swapInit);
677
+ return quote;
678
+ }
679
+ catch (e) {
680
+ if (e instanceof RequestError_1.OutOfBoundsError) {
681
+ const amountResult = await amountPromise.catch(() => undefined);
682
+ if (_options.sourceWalletUtxos != null && amountResult != null && amountResult <= 0n) {
683
+ e = new UserError_1.UserError("Wallet doesn't have enough BTC balance to cover transaction fees");
684
+ }
685
+ }
686
+ abortController.abort(e);
687
+ throw e;
688
+ }
689
+ }, undefined, err => !(err instanceof IntermediaryError_1.IntermediaryError && err.recoverable), _abortController.signal)
690
+ };
691
+ });
692
+ }
693
+ /**
694
+ * Recovers an SPV vault (UTXO-controlled vault) based swap from smart chain on-chain data
695
+ *
696
+ * @param state State of the spv vault withdrawal recovered from on-chain data
697
+ * @param vault SPV vault processing the swap
698
+ * @param lp Intermediary (LP) used as a counterparty for the swap
699
+ */
700
+ async recoverFromState(state, contractVersion, vault, lp) {
701
+ //Get the vault
702
+ vault ??= await this._contract(contractVersion).getVaultData(state.owner, state.vaultId);
703
+ if (vault == null)
704
+ return null;
705
+ if (state.btcTxId == null)
706
+ return null;
707
+ const btcTx = await this._btcRpc.getTransaction(state.btcTxId);
708
+ if (btcTx == null)
709
+ return null;
710
+ const withdrawalData = await this._contract(contractVersion).getWithdrawalData(btcTx)
711
+ .catch(e => {
712
+ this.logger.warn(`Error parsing withdrawal data for tx ${btcTx.txid}: `, e);
713
+ return null;
714
+ });
715
+ if (withdrawalData == null)
716
+ return null;
717
+ const vaultTokens = vault.getTokenData();
718
+ const withdrawalDataOutputs = withdrawalData.getTotalOutput();
719
+ const txBlock = await state.getTxBlock?.();
720
+ const swapInit = {
721
+ pricingInfo: {
722
+ isValid: true,
723
+ satsBaseFee: 0n,
724
+ swapPriceUSatPerToken: 100000000000000n,
725
+ realPriceUSatPerToken: 100000000000000n,
726
+ differencePPM: 0n,
727
+ feePPM: 0n,
728
+ },
729
+ url: lp?.url,
730
+ expiry: 0,
731
+ swapFee: 0n,
732
+ swapFeeBtc: 0n,
733
+ exactIn: true,
734
+ //Use bitcoin tx id as quote id, even though this is not strictly correct as this
735
+ // is an off-chain identifier presented by the LP that cannot be recovered from on-chain
736
+ // data
737
+ quoteId: btcTx.txid,
738
+ recipient: state.recipient,
739
+ vaultOwner: state.owner,
740
+ vaultId: state.vaultId,
741
+ vaultRequiredConfirmations: vault.getConfirmations(),
742
+ vaultTokenMultipliers: vault.getTokenData().map(val => val.multiplier),
743
+ vaultBtcAddress: (0, BitcoinUtils_1.fromOutputScript)(this._options.bitcoinNetwork, withdrawalData.getNewVaultScript().toString("hex")),
744
+ vaultUtxo: withdrawalData.getSpentVaultUtxo(),
745
+ vaultUtxoValue: BigInt(withdrawalData.getNewVaultBtcAmount()),
746
+ btcDestinationAddress: (0, BitcoinUtils_1.fromOutputScript)(this._options.bitcoinNetwork, btcTx.outs[2].scriptPubKey.hex),
747
+ btcAmount: BigInt(btcTx.outs[2].value),
748
+ btcAmountSwap: BigInt(btcTx.outs[2].value),
749
+ btcAmountGas: 0n,
750
+ minimumBtcFeeRate: 0,
751
+ outputTotalSwap: withdrawalDataOutputs[0] * vaultTokens[0].multiplier,
752
+ outputSwapToken: vaultTokens[0].token,
753
+ outputTotalGas: withdrawalDataOutputs[1] * vaultTokens[1].multiplier,
754
+ outputGasToken: vaultTokens[1].token,
755
+ gasSwapFeeBtc: 0n,
756
+ gasSwapFee: 0n,
757
+ gasPricingInfo: {
758
+ isValid: true,
759
+ satsBaseFee: 0n,
760
+ swapPriceUSatPerToken: 100000000000000n,
761
+ realPriceUSatPerToken: 100000000000000n,
762
+ differencePPM: 0n,
763
+ feePPM: 0n,
764
+ },
765
+ callerFeeShare: withdrawalData.callerFeeRate,
766
+ frontingFeeShare: withdrawalData.frontingFeeRate,
767
+ executionFeeShare: withdrawalData.executionFeeRate,
768
+ genesisSmartChainBlockHeight: txBlock?.blockHeight ?? 0,
769
+ contractVersion
770
+ };
771
+ const quote = new SpvFromBTCSwap_1.SpvFromBTCSwap(this, swapInit);
772
+ quote._data = withdrawalData;
773
+ if (txBlock != null) {
774
+ quote.createdAt = txBlock.blockTime * 1000;
775
+ }
776
+ else if (btcTx.blockhash == null) {
777
+ quote.createdAt = Date.now();
778
+ }
779
+ else {
780
+ const blockHeader = await this._btcRpc.getBlockHeader(btcTx.blockhash);
781
+ quote.createdAt = blockHeader == null ? Date.now() : blockHeader.getTimestamp() * 1000;
782
+ }
783
+ quote._setInitiated();
784
+ if (btcTx.inputAddresses != null)
785
+ quote._senderAddress = btcTx.inputAddresses[1];
786
+ if (state.type === base_1.SpvWithdrawalStateType.FRONTED) {
787
+ quote._frontTxId = state.txId;
788
+ quote._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
789
+ }
790
+ else {
791
+ quote._claimTxId = state.txId;
792
+ quote._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
793
+ }
794
+ await quote._save();
795
+ return quote;
796
+ }
797
+ /**
798
+ * Returns a random dummy PSBT that can be used for fee estimation, the last output (the LP output) is omitted
799
+ * to allow for coinselection algorithm to determine maximum sendable amount there
800
+ *
801
+ * @param includeGasToken Whether to return the PSBT also with the gas token amount (increases the vSize by 8)
802
+ */
803
+ getDummySwapPsbt(includeGasToken = false) {
804
+ //Construct dummy swap psbt
805
+ const psbt = new btc_signer_1.Transaction({
806
+ allowUnknownInputs: true,
807
+ allowLegacyWitnessUtxo: true,
808
+ allowUnknownOutputs: true
809
+ });
810
+ const randomVaultOutScript = (0, BitcoinUtils_1.getDummyOutputScript)(exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE);
811
+ psbt.addInput({
812
+ txid: (0, Utils_1.randomBytes)(32),
813
+ index: 0,
814
+ witnessUtxo: {
815
+ script: randomVaultOutScript,
816
+ amount: 600n
817
+ }
818
+ });
819
+ psbt.addOutput({
820
+ script: randomVaultOutScript,
821
+ amount: 600n
822
+ });
823
+ let longestOpReturnData = undefined;
824
+ for (let contractVersion in this.versionedContracts) {
825
+ if (this.versionedContracts[contractVersion].spvVaultContract == null)
826
+ continue;
827
+ const opReturnData = this._contract(contractVersion).toOpReturnData(this._chain.randomAddress(), includeGasToken ? [0xffffffffffffffffn, 0xffffffffffffffffn] : [0xffffffffffffffffn]);
828
+ if (longestOpReturnData == null || longestOpReturnData.length < opReturnData.length)
829
+ longestOpReturnData = opReturnData;
830
+ }
831
+ if (longestOpReturnData == null)
832
+ throw new Error(`No contract version supporting the Spv Vault BTC -> ${this.chainIdentifier} swaps found!`);
833
+ psbt.addOutput({
834
+ script: Buffer.concat([
835
+ longestOpReturnData.length <= 75 ? Buffer.from([0x6a, longestOpReturnData.length]) : Buffer.from([0x6a, 0x4c, longestOpReturnData.length]),
836
+ longestOpReturnData
837
+ ]),
838
+ amount: 0n
839
+ });
840
+ return psbt;
841
+ }
842
+ /**
843
+ * @inheritDoc
844
+ * @internal
845
+ */
846
+ async _checkPastSwaps(pastSwaps) {
847
+ const changedSwaps = new Set();
848
+ const removeSwaps = [];
849
+ const broadcastedOrConfirmedSwaps = {};
850
+ for (let pastSwap of pastSwaps) {
851
+ let changed = false;
852
+ if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED ||
853
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED ||
854
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED ||
855
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
856
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.DECLINED ||
857
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
858
+ //Check BTC transaction
859
+ if (await pastSwap._syncStateFromBitcoin(false))
860
+ changed ||= true;
861
+ }
862
+ if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED ||
863
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.SIGNED ||
864
+ pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.POSTED) {
865
+ if (await pastSwap._verifyQuoteDefinitelyExpired()) {
866
+ if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.CREATED) {
867
+ pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_EXPIRED;
868
+ }
869
+ else {
870
+ pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
871
+ }
872
+ changed ||= true;
873
+ }
874
+ }
875
+ if (pastSwap.isQuoteExpired()) {
876
+ removeSwaps.push(pastSwap);
877
+ continue;
878
+ }
879
+ if (changed)
880
+ changedSwaps.add(pastSwap);
881
+ if (pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BROADCASTED || pastSwap._state === SpvFromBTCSwap_1.SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
882
+ if (pastSwap._data != null)
883
+ (broadcastedOrConfirmedSwaps[pastSwap._contractVersion ?? "v1"] ??= []).push(pastSwap);
884
+ }
885
+ }
886
+ for (let contractVersion in broadcastedOrConfirmedSwaps) {
887
+ if (this.versionedContracts[contractVersion] == null) {
888
+ this.logger.warn(`_checkPastSwaps(): No contract was found for ${this.chainIdentifier} version ${contractVersion}! Skipping these swaps!`);
889
+ continue;
890
+ }
891
+ const _broadcastedOrConfirmedSwaps = broadcastedOrConfirmedSwaps[contractVersion];
892
+ const checkWithdrawalStateSwaps = [];
893
+ const _fronts = await this._contract(contractVersion).getFronterAddresses(_broadcastedOrConfirmedSwaps.map(val => ({
894
+ ...val.getSpvVaultData(),
895
+ withdrawal: val._data
896
+ })));
897
+ const _vaultUtxos = await this._contract(contractVersion).getVaultLatestUtxos(_broadcastedOrConfirmedSwaps.map(val => val.getSpvVaultData()));
898
+ for (const pastSwap of _broadcastedOrConfirmedSwaps) {
899
+ const fronterAddress = _fronts[pastSwap._data.getTxId()];
900
+ const vault = pastSwap.getSpvVaultData();
901
+ const latestVaultUtxo = _vaultUtxos[vault.owner]?.[vault.vaultId.toString(10)];
902
+ if (fronterAddress === undefined)
903
+ this.logger.warn(`_checkPastSwaps(): No fronter address returned for ${pastSwap._data.getTxId()}`);
904
+ if (latestVaultUtxo === undefined)
905
+ this.logger.warn(`_checkPastSwaps(): No last vault utxo returned for ${pastSwap._data.getTxId()}`);
906
+ if (await pastSwap._shouldCheckWithdrawalState(fronterAddress, latestVaultUtxo))
907
+ checkWithdrawalStateSwaps.push(pastSwap);
908
+ }
909
+ const withdrawalStates = await this._contract(contractVersion).getWithdrawalStates(checkWithdrawalStateSwaps.map(val => ({
910
+ withdrawal: val._data,
911
+ scStartBlockheight: val._genesisSmartChainBlockHeight
912
+ })));
913
+ for (const pastSwap of checkWithdrawalStateSwaps) {
914
+ const status = withdrawalStates[pastSwap._data.getTxId()];
915
+ if (status == null) {
916
+ this.logger.warn(`_checkPastSwaps(): No withdrawal state returned for ${pastSwap._data.getTxId()}`);
917
+ continue;
918
+ }
919
+ this.logger.debug("syncStateFromChain(): status of " + pastSwap._data.btcTx.txid, status?.type);
920
+ let changed = false;
921
+ switch (status.type) {
922
+ case base_1.SpvWithdrawalStateType.FRONTED:
923
+ pastSwap._frontTxId = status.txId;
924
+ pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.FRONTED;
925
+ changed ||= true;
926
+ break;
927
+ case base_1.SpvWithdrawalStateType.CLAIMED:
928
+ pastSwap._claimTxId = status.txId;
929
+ pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLAIMED;
930
+ changed ||= true;
931
+ break;
932
+ case base_1.SpvWithdrawalStateType.CLOSED:
933
+ pastSwap._state = SpvFromBTCSwap_1.SpvFromBTCSwapState.CLOSED;
934
+ changed ||= true;
935
+ break;
936
+ }
937
+ if (changed)
938
+ changedSwaps.add(pastSwap);
939
+ }
940
+ }
941
+ return {
942
+ changedSwaps: Array.from(changedSwaps),
943
+ removeSwaps
944
+ };
945
+ }
946
+ }
947
+ exports.SpvFromBTCWrapper = SpvFromBTCWrapper;