@atomiqlabs/sdk 8.6.5 → 8.6.6

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