@atomiqlabs/sdk 8.7.7 → 8.9.0

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 (152) hide show
  1. package/api/index.d.ts +1 -0
  2. package/api/index.js +3 -0
  3. package/dist/ApiList.d.ts +37 -0
  4. package/dist/ApiList.js +30 -0
  5. package/dist/api/ApiEndpoints.d.ts +393 -0
  6. package/dist/api/ApiEndpoints.js +2 -0
  7. package/dist/api/ApiParser.d.ts +10 -0
  8. package/dist/api/ApiParser.js +134 -0
  9. package/dist/api/ApiTypes.d.ts +157 -0
  10. package/dist/api/ApiTypes.js +75 -0
  11. package/dist/api/SerializedAction.d.ts +40 -0
  12. package/dist/api/SerializedAction.js +59 -0
  13. package/dist/api/SwapperApi.d.ts +50 -0
  14. package/dist/api/SwapperApi.js +431 -0
  15. package/dist/api/index.d.ts +5 -0
  16. package/dist/api/index.js +24 -0
  17. package/dist/bitcoin/coinselect2/accumulative.d.ts +1 -0
  18. package/dist/bitcoin/coinselect2/accumulative.js +1 -1
  19. package/dist/bitcoin/coinselect2/blackjack.d.ts +1 -0
  20. package/dist/bitcoin/coinselect2/blackjack.js +1 -1
  21. package/dist/bitcoin/coinselect2/index.d.ts +3 -2
  22. package/dist/bitcoin/coinselect2/index.js +2 -2
  23. package/dist/bitcoin/coinselect2/utils.d.ts +7 -2
  24. package/dist/bitcoin/coinselect2/utils.js +45 -10
  25. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +8 -25
  26. package/dist/bitcoin/wallet/BitcoinWallet.js +31 -18
  27. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +40 -2
  28. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +7 -2
  29. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +10 -4
  30. package/dist/events/UnifiedSwapEventListener.d.ts +4 -3
  31. package/dist/events/UnifiedSwapEventListener.js +8 -2
  32. package/dist/http/HttpUtils.d.ts +4 -2
  33. package/dist/http/HttpUtils.js +10 -4
  34. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
  35. package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
  39. package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
  40. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +182 -15
  41. package/dist/intermediaries/apis/IntermediaryAPI.js +192 -31
  42. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
  43. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
  44. package/dist/storage/IUnifiedStorage.d.ts +45 -3
  45. package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
  46. package/dist/storage/UnifiedSwapStorage.js +46 -8
  47. package/dist/swapper/Swapper.d.ts +77 -4
  48. package/dist/swapper/Swapper.js +117 -25
  49. package/dist/swapper/SwapperUtils.d.ts +18 -2
  50. package/dist/swapper/SwapperUtils.js +39 -1
  51. package/dist/swaps/ISwap.d.ts +70 -9
  52. package/dist/swaps/ISwap.js +28 -6
  53. package/dist/swaps/ISwapWrapper.d.ts +11 -1
  54. package/dist/swaps/ISwapWrapper.js +23 -3
  55. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
  56. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
  57. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
  58. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
  59. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
  60. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
  61. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
  62. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
  63. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
  64. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
  65. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
  66. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
  67. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
  68. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
  69. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
  70. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
  71. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
  72. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
  73. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
  74. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
  75. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
  76. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
  77. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
  78. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
  79. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +85 -22
  80. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +299 -56
  81. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +41 -7
  82. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +183 -58
  83. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
  84. package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
  85. package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
  86. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
  87. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
  88. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
  89. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
  90. package/dist/types/SwapExecutionAction.d.ts +141 -34
  91. package/dist/types/SwapExecutionAction.js +104 -0
  92. package/dist/types/SwapExecutionStep.d.ts +144 -0
  93. package/dist/types/SwapExecutionStep.js +87 -0
  94. package/dist/types/TokenAmount.d.ts +6 -0
  95. package/dist/types/TokenAmount.js +26 -1
  96. package/dist/utils/BitcoinUtils.d.ts +4 -0
  97. package/dist/utils/BitcoinUtils.js +73 -1
  98. package/dist/utils/BitcoinWalletUtils.d.ts +2 -2
  99. package/dist/utils/Utils.d.ts +3 -1
  100. package/dist/utils/Utils.js +7 -1
  101. package/package.json +7 -4
  102. package/src/api/ApiEndpoints.ts +427 -0
  103. package/src/api/ApiParser.ts +138 -0
  104. package/src/api/ApiTypes.ts +229 -0
  105. package/src/api/SerializedAction.ts +97 -0
  106. package/src/api/SwapperApi.ts +545 -0
  107. package/src/api/index.ts +5 -0
  108. package/src/bitcoin/coinselect2/accumulative.ts +2 -1
  109. package/src/bitcoin/coinselect2/blackjack.ts +2 -1
  110. package/src/bitcoin/coinselect2/index.ts +5 -4
  111. package/src/bitcoin/coinselect2/utils.ts +55 -14
  112. package/src/bitcoin/wallet/BitcoinWallet.ts +69 -57
  113. package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -3
  114. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +12 -4
  115. package/src/events/UnifiedSwapEventListener.ts +11 -3
  116. package/src/http/HttpUtils.ts +10 -4
  117. package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
  118. package/src/index.ts +1 -0
  119. package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
  120. package/src/intermediaries/apis/IntermediaryAPI.ts +335 -35
  121. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
  122. package/src/storage/IUnifiedStorage.ts +45 -4
  123. package/src/storage/UnifiedSwapStorage.ts +42 -8
  124. package/src/swapper/Swapper.ts +165 -24
  125. package/src/swapper/SwapperUtils.ts +42 -2
  126. package/src/swaps/ISwap.ts +88 -16
  127. package/src/swaps/ISwapWrapper.ts +28 -3
  128. package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
  129. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
  130. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
  131. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
  132. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
  133. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
  134. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
  135. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
  136. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
  137. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
  138. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
  139. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
  140. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +413 -64
  141. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +239 -61
  142. package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
  143. package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
  144. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
  145. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
  146. package/src/types/SwapExecutionAction.ts +266 -43
  147. package/src/types/SwapExecutionStep.ts +224 -0
  148. package/src/types/TokenAmount.ts +36 -2
  149. package/src/utils/BitcoinUtils.ts +73 -0
  150. package/src/utils/BitcoinWalletUtils.ts +2 -2
  151. package/src/utils/Utils.ts +10 -1
  152. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +0 -258
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SpvFromBTCWrapper = void 0;
3
+ exports.SpvFromBTCWrapper = exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE = exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE = void 0;
4
4
  const ISwapWrapper_1 = require("../ISwapWrapper");
5
5
  const base_1 = require("@atomiqlabs/base");
6
6
  const SpvFromBTCSwap_1 = require("./SpvFromBTCSwap");
@@ -9,12 +9,15 @@ const SwapType_1 = require("../../enums/SwapType");
9
9
  const Intermediary_1 = require("../../intermediaries/Intermediary");
10
10
  const Utils_1 = require("../../utils/Utils");
11
11
  const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
12
- const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
13
12
  const RequestError_1 = require("../../errors/RequestError");
14
13
  const IntermediaryError_1 = require("../../errors/IntermediaryError");
15
14
  const btc_signer_1 = require("@scure/btc-signer");
16
15
  const RetryUtils_1 = require("../../utils/RetryUtils");
17
16
  const UserError_1 = require("../../errors/UserError");
17
+ const utils_2 = require("../../bitcoin/coinselect2/utils");
18
+ const BitcoinWallet_1 = require("../../bitcoin/wallet/BitcoinWallet");
19
+ exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE = "p2tr";
20
+ exports.REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE = "p2wpkh";
18
21
  /**
19
22
  * New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
20
23
  * any initiation on the destination chain, and with the added possibility for the user to receive
@@ -33,11 +36,12 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
33
36
  * @param versionedContracts
34
37
  * @param versionedSynchronizer
35
38
  * @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
39
+ * @param lpApi
36
40
  * @param options
37
41
  * @param events Instance to use for emitting events
38
42
  */
39
- constructor(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, versionedContracts, versionedSynchronizer, btcRpc, options, events) {
40
- super(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, {
43
+ constructor(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, versionedContracts, versionedSynchronizer, btcRpc, lpApi, options, events) {
44
+ super(chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, lpApi, {
41
45
  ...options,
42
46
  bitcoinNetwork: options?.bitcoinNetwork ?? utils_1.TEST_NETWORK,
43
47
  maxConfirmations: options?.maxConfirmations ?? 6,
@@ -209,26 +213,21 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
209
213
  *
210
214
  * @param amountData
211
215
  * @param options Options as passed to the swap creation function
212
- * @param pricePrefetch
213
- * @param nativeTokenPricePrefetch
214
216
  * @param abortController
215
217
  * @param contractVersion
216
218
  * @private
217
219
  */
218
- async preFetchCallerFeeShare(amountData, options, pricePrefetch, nativeTokenPricePrefetch, abortController, contractVersion) {
220
+ async preFetchCallerFeeInNativeToken(amountData, options, abortController, contractVersion) {
219
221
  if (options.unsafeZeroWatchtowerFee)
220
222
  return 0n;
221
223
  if (amountData.amount === 0n)
222
224
  return 0n;
223
225
  try {
224
- const [feePerBlock, btcRelayData, currentBtcBlock, claimFeeRate, nativeTokenPrice] = await Promise.all([
226
+ const [feePerBlock, btcRelayData, currentBtcBlock, claimFeeRate] = await Promise.all([
225
227
  this.btcRelay(contractVersion).getFeePerBlock(),
226
228
  this.btcRelay(contractVersion).getTipData(),
227
229
  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))
230
+ this._contract(contractVersion).getClaimFee(this._chain.randomAddress())
232
231
  ]);
233
232
  if (btcRelayData == null)
234
233
  throw new Error("Btc relay doesn't seem to be initialized!");
@@ -236,35 +235,58 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
236
235
  const blockDelta = Math.max(currentBtcBlock - currentBtcRelayBlock + this._options.maxConfirmations, 0);
237
236
  const totalFeeInNativeToken = ((BigInt(blockDelta) * feePerBlock) +
238
237
  (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;
238
+ return totalFeeInNativeToken;
263
239
  }
264
240
  catch (e) {
265
241
  abortController.abort(e);
266
242
  }
267
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
+ }
268
290
  /**
269
291
  * Verifies response returned from intermediary
270
292
  *
@@ -273,13 +295,14 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
273
295
  * @param lp Intermediary
274
296
  * @param options Options as passed to the swap creation function
275
297
  * @param callerFeeShare
276
- * @param bitcoinFeeRatePromise Maximum accepted fee rate from the LPs
298
+ * @param maxBitcoinFeeRatePromise Maximum accepted fee rate from the LPs
299
+ * @param bitcoinFeeRatePromise
277
300
  * @param abortSignal
278
301
  * @private
279
302
  * @throws {IntermediaryError} in case the response is invalid
280
303
  */
281
- async verifyReturnedData(resp, amountData, lp, options, callerFeeShare, bitcoinFeeRatePromise, abortSignal) {
282
- const btcFeeRate = await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise, "Bitcoin fee rate promise failed!");
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!");
283
306
  abortSignal.throwIfAborted();
284
307
  if (btcFeeRate != null && resp.btcFeeRate > btcFeeRate)
285
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`);
@@ -288,11 +311,13 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
288
311
  let vaultScript;
289
312
  let vaultAddressType;
290
313
  let btcAddressScript;
314
+ let btcAddressType;
291
315
  //Ensure valid btc addresses returned
292
316
  try {
293
317
  vaultScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.vaultBtcAddress);
294
318
  vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)(vaultScript);
295
319
  btcAddressScript = (0, BitcoinUtils_1.toOutputScript)(this._options.bitcoinNetwork, resp.btcAddress);
320
+ btcAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)(btcAddressScript);
296
321
  }
297
322
  catch (e) {
298
323
  throw new IntermediaryError_1.IntermediaryError("Invalid btc address data returned", e);
@@ -302,7 +327,8 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
302
327
  resp.vaultId < 0n || //Ensure vaultId is not negative
303
328
  vaultScript == null || //Make sure vault script is parsable and of known type
304
329
  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
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
306
332
  decodedUtxo.length !== 2 || decodedUtxo[0].length !== 64 || isNaN(parseInt(decodedUtxo[1])) || //Check valid UTXO
307
333
  resp.btcFeeRate < 1 || resp.btcFeeRate > 10000 //Sanity check on the returned BTC fee rate
308
334
  )
@@ -345,8 +371,24 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
345
371
  const tokenData = vault.getTokenData();
346
372
  //Amounts - make sure the amounts match
347
373
  if (amountData.exactIn) {
348
- if (resp.btcAmount !== amountData.amount)
349
- throw new IntermediaryError_1.IntermediaryError("Invalid amount returned");
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
+ }
350
392
  }
351
393
  else {
352
394
  //Check the difference between amount adjusted due to scaling to raw amount
@@ -447,6 +489,58 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
447
489
  vaultUtxoValue
448
490
  };
449
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
+ }
450
544
  /**
451
545
  * Returns a newly created Bitcoin -> Smart chain swap using the SPV vault (UTXO-controlled vault) swap protocol,
452
546
  * with the passed amount. Also allows specifying additional "gas drop" native token that the receipient receives
@@ -464,13 +558,25 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
464
558
  gasAmount: this.parseGasAmount(options?.gasAmount),
465
559
  unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
466
560
  feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
467
- maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
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),
468
568
  };
469
569
  if (_options.gasAmount !== 0n &&
470
570
  (this._chain.shouldGetNativeTokenDrop != null
471
571
  ? !this._chain.shouldGetNativeTokenDrop(amountData.token)
472
572
  : amountData.token === this._chain.getNativeCurrencyAddress()))
473
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!");
474
580
  const lpVersions = Intermediary_1.Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
475
581
  const _abortController = (0, Utils_1.extendAbortController)(abortSignal);
476
582
  const pricePrefetchPromise = this.preFetchPrice(amountData, _abortController.signal);
@@ -481,14 +587,10 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
481
587
  undefined :
482
588
  this.preFetchPrice({ token: nativeTokenAddress }, _abortController.signal);
483
589
  const callerFeePrefetchPromise = (0, Utils_1.mapArrayToObject)(lpVersions, (contractVersion) => {
484
- return this.preFetchCallerFeeShare(amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, _abortController, contractVersion);
590
+ return this.preFetchCallerFeeInNativeToken(amountData, _options, _abortController, contractVersion);
485
591
  });
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
- });
592
+ const { maxBitcoinFeeRatePromise, bitcoinFeeRatePromise } = this.bitcoinFeeRatePrefetch(_options, _abortController);
593
+ const amountPromise = this.amountPrefetch(amountData, maxBitcoinFeeRatePromise, _options.sourceWalletUtxos, _options.gasAmount !== 0n, _abortController);
492
594
  return lps.map(lp => {
493
595
  return {
494
596
  intermediary: lp,
@@ -497,29 +599,46 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
497
599
  throw new Error("LP service for processing spv vault swaps not found!");
498
600
  const version = lp.getContractVersion(this.chainIdentifier);
499
601
  const abortController = (0, Utils_1.extendAbortController)(_abortController.signal);
602
+ const callerFeeRatePromise = this.computeCallerFeeShare(amountPromise, callerFeePrefetchPromise[version], amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, abortController.signal);
500
603
  try {
501
604
  const resp = await (0, RetryUtils_1.tryWithRetries)(async (retryCount) => {
502
- return await IntermediaryAPI_1.IntermediaryAPI.prepareSpvFromBTC(this.chainIdentifier, lp.url, {
605
+ return await this._lpApi.prepareSpvFromBTC(this.chainIdentifier, lp.url, {
503
606
  address: recipient,
504
- amount: amountData.amount,
607
+ amount: (0, Utils_1.throwIfUndefined)(amountPromise, "Failed to compute swap amount"),
505
608
  token: amountData.token.toString(),
506
609
  exactOut: !amountData.exactIn,
507
610
  gasToken: nativeTokenAddress,
508
611
  gasAmount: _options.gasAmount,
509
- callerFeeRate: (0, Utils_1.throwIfUndefined)(callerFeePrefetchPromise[version], "Caller fee prefetch failed!"),
612
+ callerFeeRate: (0, Utils_1.throwIfUndefined)(callerFeeRatePromise, "Caller fee prefetch failed!"),
510
613
  frontingFeeRate: 0n,
511
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,
512
627
  additionalParams
513
628
  }, this._options.postRequestTimeout, abortController.signal, retryCount > 0 ? false : undefined);
514
629
  }, undefined, e => e instanceof RequestError_1.RequestError, abortController.signal);
515
630
  this.logger.debug("create(" + lp.url + "): LP response: ", resp);
516
- const callerFeeShare = (await callerFeePrefetchPromise[version]);
631
+ const callerFeeShare = await callerFeeRatePromise;
632
+ const amount = await (0, Utils_1.throwIfUndefined)(amountPromise);
517
633
  const [pricingInfo, gasPricingInfo, { vault, vaultUtxoValue }] = await Promise.all([
518
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),
519
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
520
636
  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)
637
+ this.verifyReturnedData(resp, { ...amountData, amount }, lp, _options, callerFeeShare, maxBitcoinFeeRatePromise, bitcoinFeeRatePromise, abortController.signal)
522
638
  ]);
639
+ let minimumBtcFeeRate = resp.btcFeeRate;
640
+ if (bitcoinFeeRatePromise != null)
641
+ minimumBtcFeeRate = Math.max(minimumBtcFeeRate, await (0, Utils_1.throwIfUndefined)(bitcoinFeeRatePromise));
523
642
  const swapInit = {
524
643
  pricingInfo,
525
644
  url: lp.url,
@@ -540,7 +659,7 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
540
659
  btcAmount: resp.btcAmount,
541
660
  btcAmountSwap: resp.btcAmountSwap,
542
661
  btcAmountGas: resp.btcAmountGas,
543
- minimumBtcFeeRate: resp.btcFeeRate,
662
+ minimumBtcFeeRate,
544
663
  outputTotalSwap: resp.total,
545
664
  outputSwapToken: amountData.token,
546
665
  outputTotalGas: resp.totalGas,
@@ -551,13 +670,19 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
551
670
  callerFeeShare: resp.callerFeeShare,
552
671
  frontingFeeShare: resp.frontingFeeShare,
553
672
  executionFeeShare: resp.executionFeeShare,
554
- genesisSmartChainBlockHeight: await (0, Utils_1.throwIfUndefined)(finalizedBlockHeightPrefetchPromise, "Finalize block height promise failed!"),
673
+ genesisSmartChainBlockHeight: await (0, Utils_1.throwIfUndefined)(finalizedBlockHeightPrefetchPromise, "Network finalized blockheight pre-fetch failed!"),
555
674
  contractVersion: version
556
675
  };
557
676
  const quote = new SpvFromBTCSwap_1.SpvFromBTCSwap(this, swapInit);
558
677
  return quote;
559
678
  }
560
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
+ }
561
686
  abortController.abort(e);
562
687
  throw e;
563
688
  }
@@ -682,7 +807,7 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
682
807
  allowLegacyWitnessUtxo: true,
683
808
  allowUnknownOutputs: true
684
809
  });
685
- const randomVaultOutScript = btc_signer_1.OutScript.encode({ type: "tr", pubkey: Buffer.from("0101010101010101010101010101010101010101010101010101010101010101", "hex") });
810
+ const randomVaultOutScript = (0, BitcoinUtils_1.getDummyOutputScript)(exports.REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE);
686
811
  psbt.addInput({
687
812
  txid: (0, Utils_1.randomBytes)(32),
688
813
  index: 0,
@@ -8,7 +8,9 @@ import { FeeType } from "../../../enums/FeeType";
8
8
  import { TokenAmount } from "../../../types/TokenAmount";
9
9
  import { BtcToken, SCToken } from "../../../types/Token";
10
10
  import { LoggerType } from "../../../utils/Logger";
11
- import { SwapExecutionAction, SwapExecutionActionLightning } from "../../../types/SwapExecutionAction";
11
+ import { SwapExecutionActionSendToAddress, SwapExecutionActionWait } from "../../../types/SwapExecutionAction";
12
+ import { SwapExecutionStepPayment, SwapExecutionStepSettlement } from "../../../types/SwapExecutionStep";
13
+ import { SwapStateInfo } from "../../../types/SwapStateInfo";
12
14
  /**
13
15
  * State enum for trusted Lightning gas swaps
14
16
  *
@@ -16,23 +18,23 @@ import { SwapExecutionAction, SwapExecutionActionLightning } from "../../../type
16
18
  */
17
19
  export declare enum LnForGasSwapState {
18
20
  /**
19
- * The swap quote expired without user sending in the lightning network payment
21
+ * The swap quote expired before the user paid the Lightning invoice
20
22
  */
21
23
  EXPIRED = -2,
22
24
  /**
23
- * The swap has failed after the intermediary already received a lightning network payment on the source
25
+ * The swap has failed before the destination payout completed, and the held Lightning invoice was released
24
26
  */
25
27
  FAILED = -1,
26
28
  /**
27
- * Swap was created, pay the provided lightning network invoice
29
+ * Swap was created, pay the provided Lightning invoice which will remain held until destination payout succeeds
28
30
  */
29
31
  PR_CREATED = 0,
30
32
  /**
31
- * User paid the lightning network invoice on the source
33
+ * The Lightning invoice was paid and is currently held until the user receives the destination funds
32
34
  */
33
35
  PR_PAID = 1,
34
36
  /**
35
- * The swap is finished after the intermediary sent funds on the destination chain
37
+ * The swap is finished after the destination payout succeeded and the held Lightning invoice was settled
36
38
  */
37
39
  FINISHED = 2
38
40
  }
@@ -210,17 +212,54 @@ export declare class LnForGasSwap<T extends ChainType = ChainType> extends ISwap
210
212
  fee: Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>>;
211
213
  }];
212
214
  /**
215
+ * @remarks Not supported
216
+ */
217
+ execute(): Promise<boolean>;
218
+ /**
219
+ * @internal
220
+ */
221
+ protected _getExecutionStatus(): Promise<{
222
+ steps: [SwapExecutionStepPayment<"LIGHTNING">, SwapExecutionStepSettlement<T["ChainId"], never>];
223
+ buildCurrentAction: () => Promise<SwapExecutionActionSendToAddress<true> | SwapExecutionActionWait<"LP"> | undefined>;
224
+ state: LnForGasSwapState;
225
+ }>;
226
+ /**
227
+ * @internal
213
228
  * @inheritDoc
214
229
  */
215
- txsExecute(): Promise<[SwapExecutionActionLightning]>;
230
+ _submitExecutionTransactions(): Promise<string[]>;
216
231
  /**
217
- * @remark Not supported
232
+ * @internal
218
233
  */
219
- execute(): Promise<boolean>;
234
+ private _buildLightningPaymentAction;
235
+ /**
236
+ * @internal
237
+ */
238
+ private _buildWaitLpAction;
239
+ /**
240
+ * @inheritDoc
241
+ */
242
+ getExecutionAction(): Promise<SwapExecutionActionSendToAddress<true> | SwapExecutionActionWait<"LP"> | undefined>;
243
+ /**
244
+ * @inheritDoc
245
+ */
246
+ getExecutionStatus(options?: {
247
+ skipBuildingAction?: boolean;
248
+ }): Promise<{
249
+ steps: [
250
+ SwapExecutionStepPayment<"LIGHTNING">,
251
+ SwapExecutionStepSettlement<T["ChainId"], never>
252
+ ];
253
+ currentAction: SwapExecutionActionSendToAddress<true> | SwapExecutionActionWait<"LP"> | undefined;
254
+ stateInfo: SwapStateInfo<LnForGasSwapState>;
255
+ }>;
220
256
  /**
221
257
  * @inheritDoc
222
258
  */
223
- getCurrentActions(): Promise<SwapExecutionAction<T>[]>;
259
+ getExecutionSteps(): Promise<[
260
+ SwapExecutionStepPayment<"LIGHTNING">,
261
+ SwapExecutionStepSettlement<T["ChainId"], never>
262
+ ]>;
224
263
  /**
225
264
  * Queries the intermediary (LP) node for the state of the swap
226
265
  *
@@ -232,13 +271,15 @@ export declare class LnForGasSwap<T extends ChainType = ChainType> extends ISwap
232
271
  protected checkInvoicePaid(save?: boolean): Promise<boolean | null>;
233
272
  /**
234
273
  * A blocking promise resolving when payment was received by the intermediary and client can continue,
235
- * rejecting in case of failure. The swap must be in {@link LnForGasSwapState.PR_CREATED} state!
274
+ * rejecting in case of failure. The swap must be in {@link LnForGasSwapState.PR_CREATED} or
275
+ * {@link LnForGasSwapState.PR_PAID} state!
236
276
  *
237
277
  * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
238
278
  * @param abortSignal Abort signal
279
+ * @param onPaymentReceived Callback as for when the LP reports having received the ln payment
239
280
  * @throws {Error} When in invalid state (not PR_CREATED)
240
281
  */
241
- waitForPayment(checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean>;
282
+ waitForPayment(checkIntervalSeconds?: number, abortSignal?: AbortSignal, onPaymentReceived?: (txId: string) => void): Promise<boolean>;
242
283
  /**
243
284
  * @inheritDoc
244
285
  */