@atomiqlabs/sdk 8.4.3 → 8.5.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 (93) hide show
  1. package/README.md +8 -8
  2. package/dist/enums/SwapSide.d.ts +15 -0
  3. package/dist/enums/SwapSide.js +19 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +2 -0
  6. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +2 -2
  7. package/dist/intermediaries/apis/IntermediaryAPI.js +2 -2
  8. package/dist/storage/UnifiedSwapStorage.d.ts +2 -0
  9. package/dist/swapper/Swapper.d.ts +27 -21
  10. package/dist/swapper/Swapper.js +19 -26
  11. package/dist/swapper/SwapperWithChain.d.ts +14 -18
  12. package/dist/swapper/SwapperWithChain.js +2 -2
  13. package/dist/swapper/SwapperWithSigner.d.ts +9 -13
  14. package/dist/swaps/IBTCWalletSwap.d.ts +1 -1
  15. package/dist/swaps/ISwap.d.ts +14 -0
  16. package/dist/swaps/ISwap.js +2 -0
  17. package/dist/swaps/ISwapWithGasDrop.d.ts +1 -1
  18. package/dist/swaps/ISwapWrapper.d.ts +12 -12
  19. package/dist/swaps/ISwapWrapper.js +22 -14
  20. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +3 -3
  21. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +8 -8
  22. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +10 -6
  23. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +10 -0
  24. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +23 -2
  25. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +20 -17
  26. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +8 -4
  27. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +9 -0
  28. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +44 -3
  29. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +9 -14
  30. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +7 -3
  31. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +7 -0
  32. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +20 -2
  33. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +15 -8
  34. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +16 -7
  35. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +13 -0
  36. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +1 -1
  37. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +52 -6
  38. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +20 -30
  39. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +1 -1
  40. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +6 -0
  41. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +10 -6
  42. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +8 -0
  43. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +32 -1
  44. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +8 -5
  45. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +11 -3
  46. package/dist/swaps/trusted/ln/LnForGasSwap.js +12 -0
  47. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +12 -4
  48. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +13 -0
  49. package/dist/types/Token.d.ts +65 -25
  50. package/dist/types/Token.js +28 -13
  51. package/dist/types/TokenAmount.d.ts +2 -2
  52. package/dist/types/fees/Fee.d.ts +3 -3
  53. package/dist/types/lnurl/LNURLPay.d.ts +0 -1
  54. package/dist/types/lnurl/LNURLPay.js +0 -1
  55. package/dist/types/lnurl/LNURLWithdraw.d.ts +0 -1
  56. package/dist/types/lnurl/LNURLWithdraw.js +0 -1
  57. package/dist/utils/Utils.d.ts +1 -0
  58. package/dist/utils/Utils.js +17 -1
  59. package/package.json +1 -1
  60. package/src/enums/SwapSide.ts +16 -0
  61. package/src/index.ts +2 -0
  62. package/src/intermediaries/apis/IntermediaryAPI.ts +4 -4
  63. package/src/storage/UnifiedSwapStorage.ts +2 -0
  64. package/src/swapper/Swapper.ts +46 -37
  65. package/src/swapper/SwapperWithChain.ts +15 -10
  66. package/src/swapper/SwapperWithSigner.ts +8 -3
  67. package/src/swaps/IBTCWalletSwap.ts +1 -1
  68. package/src/swaps/ISwap.ts +18 -0
  69. package/src/swaps/ISwapWithGasDrop.ts +1 -1
  70. package/src/swaps/ISwapWrapper.ts +24 -23
  71. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +3 -3
  72. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +8 -8
  73. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +18 -6
  74. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +48 -22
  75. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +14 -4
  76. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +56 -20
  77. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +11 -3
  78. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +57 -22
  79. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +22 -7
  80. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +1 -1
  81. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +80 -37
  82. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +1 -1
  83. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +12 -3
  84. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +15 -6
  85. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +53 -11
  86. package/src/swaps/trusted/ln/LnForGasSwap.ts +18 -4
  87. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +19 -4
  88. package/src/types/Token.ts +98 -32
  89. package/src/types/TokenAmount.ts +5 -7
  90. package/src/types/fees/Fee.ts +3 -3
  91. package/src/types/lnurl/LNURLPay.ts +0 -1
  92. package/src/types/lnurl/LNURLWithdraw.ts +0 -1
  93. package/src/utils/Utils.ts +18 -2
@@ -21,15 +21,60 @@ import {sha256} from "@noble/hashes/sha2";
21
21
  import {AmountData} from "../../../../types/AmountData";
22
22
  import {LNURLPayParamsWithUrl} from "../../../../types/lnurl/LNURLPay";
23
23
  import {tryWithRetries} from "../../../../utils/RetryUtils";
24
- import {AllOptional, AllRequired} from "../../../../utils/TypeUtils";
24
+ import {AllOptional} from "../../../../utils/TypeUtils";
25
25
  import {LightningInvoiceCreateService} from "../../../../types/wallets/LightningInvoiceCreateService";
26
26
 
27
27
  export type ToBTCLNOptions = {
28
+ /**
29
+ * HTLC expiration timeout in seconds to use when offering the HTLC to the LP. Larger expirations mean that more
30
+ * lightning network payment paths can be considered (every hop in the lightning network payment adds additional
31
+ * timeout requirement). On the other side, larger expiration also means that user's funds are locked for longer
32
+ * in case of a non-cooperative LP.
33
+ *
34
+ * Uses 5 days as default.
35
+ */
28
36
  expirySeconds?: number,
29
- maxFee?: bigint | Promise<bigint>,
30
- expiryTimestamp?: bigint,
37
+ /**
38
+ * Maximum fee for routing the swap output payment through the lightning network. Higher fee percentages means that
39
+ * more payment routes can be considered (every hop in the lightning network payment adds additional fee
40
+ * requirements).
41
+ *
42
+ * The fee is express as percentage of the swap value, uses `0.2` by default which means the maximum
43
+ * routing fee is capped at 0.2% of the swap value.
44
+ *
45
+ * The full fee also contains the base component (set by `maxRoutingBaseFee` option), the resulting maximum routing
46
+ * fee rate is:
47
+ *
48
+ * `maxRoutingFee` = `maxRoutingBaseFee` sats + `value` * `maxRoutingFeePercentage`%
49
+ */
50
+ maxRoutingFeePercentage?: number,
51
+ /**
52
+ *
53
+ * Maximum base fee (in sats) for routing the swap output payment through the lightning network. Higher fee
54
+ * percentages means that more payment routes can be considered (every hop in the lightning network payment adds additional fee
55
+ * requirements).
56
+ *
57
+ * Uses 10 sats as a default.
58
+ *
59
+ * The full fee also contains the value percentage component (set by `maxRoutingFeePercentage` option), the
60
+ * resulting maximum routing fee rate is:
61
+ *
62
+ * `maxRoutingFee` = `maxRoutingBaseFee` sats + (`value` * `maxRoutingFeePercentage`%)
63
+ */
64
+ maxRoutingBaseFee?: bigint,
65
+
66
+ /**
67
+ * @deprecated Use `maxRoutingFeePercentage` and express the routing fee in percentage instead!
68
+ */
31
69
  maxRoutingPPM?: bigint,
32
- maxRoutingBaseFee?: bigint
70
+ /**
71
+ * @deprecated Adjust fee with `maxRoutingFeePercentage` & `maxRoutingBaseFee` params!
72
+ */
73
+ maxFee?: bigint | Promise<bigint>,
74
+ /**
75
+ * @deprecated Pass desired HTLC expiration timeout as `expirySeconds`
76
+ */
77
+ expiryTimestamp?: bigint
33
78
  }
34
79
 
35
80
  export type ToBTCLNWrapperOptions = ISwapWrapperOptions & {
@@ -67,7 +112,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
67
112
  super(
68
113
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
69
114
  {
70
- paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 4*24*60*60,
115
+ paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 5*24*60*60,
71
116
  lightningBaseFee: options?.lightningBaseFee ?? 10,
72
117
  lightningFeePPM: options?.lightningFeePPM ?? 2000
73
118
  },
@@ -75,10 +120,12 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
75
120
  );
76
121
  }
77
122
 
78
- private toRequiredSwapOptions(amountData: AmountData, options?: ToBTCLNOptions, pricePreFetchPromise?: Promise<bigint | undefined>, abortSignal?: AbortSignal): AllRequired<ToBTCLNOptions> {
123
+ private toRequiredSwapOptions(amountData: AmountData, options?: ToBTCLNOptions, pricePreFetchPromise?: Promise<bigint | undefined>, abortSignal?: AbortSignal) {
79
124
  const expirySeconds = options?.expirySeconds ?? this._options.paymentTimeoutSeconds;
80
125
  const maxRoutingBaseFee = options?.maxRoutingBaseFee ?? BigInt(this._options.lightningBaseFee);
81
- const maxRoutingPPM = options?.maxRoutingPPM ?? BigInt(this._options.lightningFeePPM);
126
+ const maxRoutingPPM = options?.maxRoutingFeePercentage!=null
127
+ ? BigInt(Math.floor(options.maxRoutingFeePercentage * 10_000))
128
+ : options?.maxRoutingPPM ?? BigInt(this._options.lightningFeePPM);
82
129
 
83
130
  let maxFee: bigint | Promise<bigint>;
84
131
  if(options?.maxFee!=null) {
@@ -97,10 +144,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
97
144
  }
98
145
 
99
146
  return {
100
- expirySeconds,
101
147
  expiryTimestamp: options?.expiryTimestamp ?? BigInt(Math.floor(Date.now()/1000)+expirySeconds),
102
- maxRoutingBaseFee,
103
- maxRoutingPPM,
104
148
  maxFee
105
149
  }
106
150
  }
@@ -149,7 +193,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
149
193
  * @param parsedPr Parsed bolt11 lightning invoice
150
194
  * @param token Smart chain token to be used in the swap
151
195
  * @param lp
152
- * @param options Swap options as passed to the swap create function
196
+ * @param calculatedOptions Swap options computed from the swap create options
153
197
  * @param data Parsed swap data returned by the LP
154
198
  * @param requiredTotal Required total to be paid on the input (for exactIn swaps)
155
199
  *
@@ -163,11 +207,14 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
163
207
  parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
164
208
  token: string,
165
209
  lp: Intermediary,
166
- options: AllRequired<ToBTCLNOptions>,
210
+ calculatedOptions: {
211
+ maxFee: bigint | Promise<bigint>,
212
+ expiryTimestamp: bigint
213
+ },
167
214
  data: T["Data"],
168
215
  requiredTotal?: bigint
169
216
  ): Promise<void> {
170
- if(resp.routingFeeSats > await options.maxFee) throw new IntermediaryError("Invalid max fee sats returned");
217
+ if(resp.routingFeeSats > await calculatedOptions.maxFee) throw new IntermediaryError("Invalid max fee sats returned");
171
218
 
172
219
  if(requiredTotal!=null && resp.total !== requiredTotal)
173
220
  throw new IntermediaryError("Invalid data returned - total amount");
@@ -178,7 +225,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
178
225
  if(
179
226
  data.getAmount() !== resp.total ||
180
227
  !Buffer.from(data.getClaimHash(), "hex").equals(claimHash) ||
181
- data.getExpiry() !== options.expiryTimestamp ||
228
+ data.getExpiry() !== calculatedOptions.expiryTimestamp ||
182
229
  data.getType()!==ChainSwapType.HTLC ||
183
230
  !data.isPayIn() ||
184
231
  !data.isToken(token) ||
@@ -198,7 +245,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
198
245
  * @param lp Intermediary
199
246
  * @param pr bolt11 lightning network invoice
200
247
  * @param parsedPr Parsed bolt11 lightning network invoice
201
- * @param options Options as passed to the swap create function
248
+ * @param calculatedOptions Swap options computed from the swap create options
202
249
  * @param preFetches
203
250
  * @param abort Abort signal or controller, if AbortController is passed it is used as-is, when AbortSignal is passed
204
251
  * it is extended with extendAbortController and then used
@@ -212,7 +259,10 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
212
259
  lp: Intermediary,
213
260
  pr: string,
214
261
  parsedPr: PaymentRequestObject & {tagsObject: TagsObject},
215
- options: AllRequired<ToBTCLNOptions>,
262
+ calculatedOptions: {
263
+ maxFee: bigint | Promise<bigint>,
264
+ expiryTimestamp: bigint
265
+ },
216
266
  preFetches: {
217
267
  feeRatePromise: Promise<string | undefined>,
218
268
  pricePreFetchPromise: Promise<bigint | undefined>,
@@ -232,8 +282,8 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
232
282
  const {signDataPrefetch, response} = IntermediaryAPI.initToBTCLN(this.chainIdentifier, lp.url, {
233
283
  offerer: signer,
234
284
  pr,
235
- maxFee: await options.maxFee,
236
- expiryTimestamp: options.expiryTimestamp,
285
+ maxFee: await calculatedOptions.maxFee,
286
+ expiryTimestamp: calculatedOptions.expiryTimestamp,
237
287
  token: amountData.token,
238
288
  feeRate: throwIfUndefined(preFetches.feeRatePromise),
239
289
  additionalParams
@@ -251,7 +301,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
251
301
  const data: T["Data"] = new this._swapDataDeserializer(resp.data);
252
302
  data.setOfferer(signer);
253
303
 
254
- await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, options, data);
304
+ await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
255
305
 
256
306
  const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
257
307
  this.verifyReturnedPrice(
@@ -309,7 +359,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
309
359
  async create(
310
360
  signer: string,
311
361
  recipient: string,
312
- amountData: Omit<AmountData, "amount">,
362
+ amountData: Omit<AmountData, "amount"> & {exactIn: false},
313
363
  lps: Intermediary[],
314
364
  options?: ToBTCLNOptions,
315
365
  additionalParams?: Record<string, any>,
@@ -328,17 +378,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
328
378
  if(parsedPr.millisatoshis==null) throw new UserError("Must be an invoice with amount");
329
379
  const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
330
380
 
331
- const expirySeconds = options?.expirySeconds ?? this._options.paymentTimeoutSeconds;
332
- const maxRoutingBaseFee = options?.maxRoutingBaseFee ?? BigInt(this._options.lightningBaseFee);
333
- const maxRoutingPPM = options?.maxRoutingPPM ?? BigInt(this._options.lightningFeePPM);
334
-
335
- const _options: AllRequired<ToBTCLNOptions> = {
336
- expirySeconds,
337
- expiryTimestamp: options?.expiryTimestamp ?? BigInt(Math.floor(Date.now()/1000)+expirySeconds),
338
- maxRoutingBaseFee,
339
- maxRoutingPPM,
340
- maxFee: options?.maxFee ?? this.calculateFeeForAmount(amountOut, maxRoutingBaseFee, maxRoutingPPM)
341
- }
381
+ const _options = this.toRequiredSwapOptions({...amountData, amount: amountOut}, options);
342
382
 
343
383
  if(parsedPr.tagsObject.payment_hash==null) throw new Error("Provided lightning invoice doesn't contain payment hash field!");
344
384
  await this.checkPaymentHashWasPaid(parsedPr.tagsObject.payment_hash);
@@ -388,7 +428,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
388
428
  * @param lp Intermediary (LPs) to get the quote from
389
429
  * @param dummyPr Dummy minimum value bolt11 lightning invoice returned from the LNURL-pay, used to estimate
390
430
  * network fees for an actual invoice
391
- * @param options Optional additional quote options
431
+ * @param calculatedOptions Swap options computed from the swap create options
392
432
  * @param preFetches Optional existing pre-fetch promises for the swap (only used internally for LNURL swaps)
393
433
  * @param abortSignal Abort signal
394
434
  * @param additionalParams Additional params to be sent to the intermediary
@@ -401,7 +441,10 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
401
441
  invoiceCreateService: LightningInvoiceCreateService,
402
442
  lp: Intermediary,
403
443
  dummyPr: string,
404
- options: AllRequired<ToBTCLNOptions> & {comment?: string},
444
+ calculatedOptions: {
445
+ maxFee: bigint | Promise<bigint>,
446
+ expiryTimestamp: bigint
447
+ },
405
448
  preFetches: {
406
449
  feeRatePromise: Promise<string | undefined>,
407
450
  pricePreFetchPromise: Promise<bigint | undefined>,
@@ -422,8 +465,8 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
422
465
  offerer: signer,
423
466
  pr: dummyPr,
424
467
  amount: amountData.amount,
425
- maxFee: await options.maxFee,
426
- expiryTimestamp: options.expiryTimestamp,
468
+ maxFee: await calculatedOptions.maxFee,
469
+ expiryTimestamp: calculatedOptions.expiryTimestamp,
427
470
  additionalParams
428
471
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
429
472
 
@@ -462,7 +505,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
462
505
  const data: T["Data"] = new this._swapDataDeserializer(resp.data);
463
506
  data.setOfferer(signer);
464
507
 
465
- await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, options, data, amountData.amount);
508
+ await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
466
509
 
467
510
  const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
468
511
  this.verifyReturnedPrice(
@@ -570,7 +613,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
570
613
 
571
614
  const invoice = await invoiceCreateService.getInvoice(Number(amountData.amount), _abortController.signal);
572
615
 
573
- return (await this.create(signer, invoice, amountData, lps, options, additionalParams, _abortController.signal, {
616
+ return (await this.create(signer, invoice, {...amountData, exactIn: false}, lps, options, additionalParams, _abortController.signal, {
574
617
  feeRatePromise,
575
618
  pricePreFetchPromise,
576
619
  usdPricePrefetchPromise,
@@ -154,7 +154,7 @@ export class ToBTCSwap<T extends ChainType = ChainType> extends IToBTCSwap<T, To
154
154
  /**
155
155
  * @inheritDoc
156
156
  */
157
- getOutput(): TokenAmount<T["ChainId"], BtcToken<false>> {
157
+ getOutput(): TokenAmount<BtcToken<false>> {
158
158
  return toTokenAmount(this.amount ?? null, this.outputToken, this.wrapper._prices, this.pricingInfo);
159
159
  }
160
160
 
@@ -25,11 +25,17 @@ import {UnifiedSwapStorage} from "../../../../storage/UnifiedSwapStorage";
25
25
  import {ISwap} from "../../../ISwap";
26
26
  import {AmountData} from "../../../../types/AmountData";
27
27
  import {tryWithRetries} from "../../../../utils/RetryUtils";
28
- import {AllOptional, AllRequired} from "../../../../utils/TypeUtils";
28
+ import {AllOptional} from "../../../../utils/TypeUtils";
29
29
  import {ToBTCSwapState} from "../IToBTCSwap";
30
30
 
31
31
  export type ToBTCOptions = {
32
+ /**
33
+ * @deprecated Ignored by the LP anyway
34
+ */
32
35
  confirmationTarget?: number,
36
+ /**
37
+ * @deprecated Default 2 confirmations should be enough for any currently supported amount by atomiq
38
+ */
33
39
  confirmations?: number
34
40
  }
35
41
 
@@ -154,7 +160,10 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
154
160
  resp: ToBTCResponseType,
155
161
  amountData: AmountData,
156
162
  lp: Intermediary,
157
- options: AllRequired<ToBTCOptions>,
163
+ options: {
164
+ confirmations: number,
165
+ confirmationTarget: number
166
+ },
158
167
  data: T["Data"],
159
168
  hash: string
160
169
  ): void {
@@ -220,7 +229,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
220
229
  intermediary: Intermediary
221
230
  }[] {
222
231
  if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
223
- const _options: AllRequired<ToBTCOptions> = {
232
+ const _options = {
224
233
  confirmationTarget: options?.confirmationTarget ?? 3,
225
234
  confirmations: options?.confirmations ?? 2
226
235
  };
@@ -506,6 +506,15 @@ export class SpvFromBTCSwap<T extends ChainType>
506
506
  return this._state===SpvFromBTCSwapState.FAILED || this._state===SpvFromBTCSwapState.DECLINED || this._state===SpvFromBTCSwapState.CLOSED;
507
507
  }
508
508
 
509
+ /**
510
+ * @inheritDoc
511
+ */
512
+ isInProgress(): boolean {
513
+ return this._state===SpvFromBTCSwapState.POSTED ||
514
+ this._state===SpvFromBTCSwapState.BROADCASTED ||
515
+ this._state===SpvFromBTCSwapState.BTC_TX_CONFIRMED;
516
+ }
517
+
509
518
  /**
510
519
  * @inheritDoc
511
520
  */
@@ -573,7 +582,7 @@ export class SpvFromBTCSwap<T extends ChainType>
573
582
  *
574
583
  * @internal
575
584
  */
576
- protected getOutputWithoutFee(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
585
+ protected getOutputWithoutFee(): TokenAmount<SCToken<T["ChainId"]>, true> {
577
586
  return toTokenAmount(
578
587
  (this.outputTotalSwap * (100_000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare) / 100_000n) + (this.swapFee ?? 0n),
579
588
  this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo
@@ -695,21 +704,21 @@ export class SpvFromBTCSwap<T extends ChainType>
695
704
  /**
696
705
  * @inheritDoc
697
706
  */
698
- getOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
707
+ getOutput(): TokenAmount<SCToken<T["ChainId"]>, true> {
699
708
  return toTokenAmount(this.outputTotalSwap, this.wrapper._tokens[this.outputSwapToken], this.wrapper._prices, this.pricingInfo);
700
709
  }
701
710
 
702
711
  /**
703
712
  * @inheritDoc
704
713
  */
705
- getGasDropOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
714
+ getGasDropOutput(): TokenAmount<SCToken<T["ChainId"]>, true> {
706
715
  return toTokenAmount(this.outputTotalGas, this.wrapper._tokens[this.outputGasToken], this.wrapper._prices, this.gasPricingInfo);
707
716
  }
708
717
 
709
718
  /**
710
719
  * @inheritDoc
711
720
  */
712
- getInputWithoutFee(): TokenAmount<T["ChainId"], BtcToken<false>, true> {
721
+ getInputWithoutFee(): TokenAmount<BtcToken<false>, true> {
713
722
  return toTokenAmount(this.getInputAmountWithoutFee(), BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
714
723
  }
715
724
 
@@ -723,7 +732,7 @@ export class SpvFromBTCSwap<T extends ChainType>
723
732
  /**
724
733
  * @inheritDoc
725
734
  */
726
- getInput(): TokenAmount<T["ChainId"], BtcToken<false>, true> {
735
+ getInput(): TokenAmount<BtcToken<false>, true> {
727
736
  return toTokenAmount(this.btcAmount, BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
728
737
  }
729
738
 
@@ -987,7 +996,7 @@ export class SpvFromBTCSwap<T extends ChainType>
987
996
  /**
988
997
  * @inheritDoc
989
998
  */
990
- async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<any, BtcToken<false>, true> | null> {
999
+ async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<BtcToken<false>, true> | null> {
991
1000
  const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
992
1001
  const txFee = await bitcoinWallet.getFundedPsbtFee((await this.getPsbt()).psbt, feeRate);
993
1002
  if(txFee==null) return null;
@@ -32,13 +32,47 @@ import {ISwap} from "../ISwap";
32
32
  import {IClaimableSwapWrapper} from "../IClaimableSwapWrapper";
33
33
  import {AmountData} from "../../types/AmountData";
34
34
  import {tryWithRetries} from "../../utils/RetryUtils";
35
- import {AllOptional, AllRequired} from "../../utils/TypeUtils";
35
+ import {AllOptional} from "../../utils/TypeUtils";
36
+ import {fromHumanReadableString} from "../../utils/TokenUtils";
37
+ import {UserError} from "../../errors/UserError";
36
38
 
37
39
  export type SpvFromBTCOptions = {
38
- gasAmount?: bigint,
40
+ /**
41
+ * Optional additional native token to receive as an output of the swap (e.g. STRK on Starknet or cBTC on Citrea).
42
+ *
43
+ * When passed as a `bigint` it is specified in base units of the token and in `string` it is the human readable
44
+ * decimal format.
45
+ */
46
+ gasAmount?: bigint | string,
47
+ /**
48
+ * The LP enforces a minimum bitcoin fee rate in sats/vB for the swap transaction. With this config you can optionally
49
+ * limit how high of a minimum fee rate would you accept.
50
+ *
51
+ * By default the maximum allowed fee rate is calculated dynamically based on current bitcoin fee rate as:
52
+ *
53
+ * `maxAllowedBitcoinFeeRate` = 10 + `currentBitcoinFeeRate` * 1.5
54
+ */
55
+ maxAllowedBitcoinFeeRate?: number,
56
+ /**
57
+ * A flag to attach 0 watchtower fee to the swap, this would make the settlement unattractive for the watchtowers
58
+ * and therefore automatic settlement for such swaps will not be possible, you will have to settle manually
59
+ * with {@link FromBTCLNSwap.claim} or {@link FromBTCLNSwap.txsClaim} functions.
60
+ */
39
61
  unsafeZeroWatchtowerFee?: boolean,
62
+ /**
63
+ * A safety factor to use when estimating the watchtower fee to attach to the swap (this has to cover the gas fee
64
+ * of watchtowers settling the swap). A higher multiple here would mean that a swap is more attractive for
65
+ * watchtowers to settle automatically.
66
+ *
67
+ * Uses a `1.25` multiple by default (i.e. the current network fee is multiplied by 1.25 and then used to estimate
68
+ * the settlement gas fee cost)
69
+ */
40
70
  feeSafetyFactor?: number,
41
- maxAllowedNetworkFeeRate?: number
71
+
72
+ /**
73
+ * @deprecated Use `maxAllowedBitcoinFeeRate` instead!
74
+ */
75
+ maxAllowedNetworkFeeRate?: number,
42
76
  };
43
77
 
44
78
  export type SpvFromBTCWrapperOptions = ISwapWrapperOptions & {
@@ -275,7 +309,10 @@ export class SpvFromBTCWrapper<
275
309
  */
276
310
  private async preFetchCallerFeeShare(
277
311
  amountData: AmountData,
278
- options: AllRequired<SpvFromBTCOptions>,
312
+ options: {
313
+ unsafeZeroWatchtowerFee: boolean,
314
+ feeSafetyFactor: number
315
+ },
279
316
  pricePrefetch: Promise<bigint | undefined>,
280
317
  nativeTokenPricePrefetch: Promise<bigint | undefined> | undefined,
281
318
  abortController: AbortController
@@ -355,7 +392,9 @@ export class SpvFromBTCWrapper<
355
392
  resp: SpvFromBTCPrepareResponseType,
356
393
  amountData: AmountData,
357
394
  lp: Intermediary,
358
- options: SpvFromBTCOptions,
395
+ options: {
396
+ gasAmount: bigint
397
+ },
359
398
  callerFeeShare: bigint,
360
399
  bitcoinFeeRatePromise: Promise<number | undefined>,
361
400
  abortSignal: AbortSignal
@@ -438,7 +477,7 @@ export class SpvFromBTCWrapper<
438
477
  throw new IntermediaryError("Invalid amount0 multiplier used, rawAmount diff too high");
439
478
  if(resp.total !== adjustedAmount) throw new IntermediaryError("Invalid total returned");
440
479
  }
441
- if(options.gasAmount==null || options.gasAmount===0n) {
480
+ if(options.gasAmount===0n) {
442
481
  if(resp.totalGas !== 0n) throw new IntermediaryError("Invalid gas total returned");
443
482
  } else {
444
483
  //Check the difference between amount adjusted due to scaling to raw amount
@@ -548,13 +587,16 @@ export class SpvFromBTCWrapper<
548
587
  quote: Promise<SpvFromBTCSwap<T>>,
549
588
  intermediary: Intermediary
550
589
  }[] {
551
- const _options: AllRequired<SpvFromBTCOptions> = {
552
- gasAmount: options?.gasAmount ?? 0n,
590
+ const _options = {
591
+ gasAmount: this.parseGasAmount(options?.gasAmount),
553
592
  unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
554
593
  feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
555
- maxAllowedNetworkFeeRate: options?.maxAllowedNetworkFeeRate ?? Infinity
594
+ maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
556
595
  };
557
596
 
597
+ if(amountData.token===this._chain.getNativeCurrencyAddress() && _options.gasAmount!==0n)
598
+ throw new UserError("Cannot specify `gasAmount` for swaps to a native token!");
599
+
558
600
  const _abortController = extendAbortController(abortSignal);
559
601
  const pricePrefetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
560
602
  const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
@@ -564,8 +606,8 @@ export class SpvFromBTCWrapper<
564
606
  undefined :
565
607
  this.preFetchPrice({token: nativeTokenAddress}, _abortController.signal);
566
608
  const callerFeePrefetchPromise = this.preFetchCallerFeeShare(amountData, _options, pricePrefetchPromise, gasTokenPricePrefetchPromise, _abortController);
567
- const bitcoinFeeRatePromise: Promise<number | undefined> = _options.maxAllowedNetworkFeeRate!=Infinity ?
568
- Promise.resolve(_options.maxAllowedNetworkFeeRate) :
609
+ const bitcoinFeeRatePromise: Promise<number | undefined> = _options.maxAllowedBitcoinFeeRate!=Infinity ?
610
+ Promise.resolve(_options.maxAllowedBitcoinFeeRate) :
569
611
  this._btcRpc.getFeeRate().then(x => this._options.maxBtcFeeOffset + (x*this._options.maxBtcFeeMultiplier)).catch(e => {
570
612
  _abortController.abort(e);
571
613
  return undefined;
@@ -272,6 +272,13 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
272
272
  return this._state===LnForGasSwapState.FINISHED;
273
273
  }
274
274
 
275
+ /**
276
+ * @inheritDoc
277
+ */
278
+ isInProgress(): boolean {
279
+ return (this._state===LnForGasSwapState.PR_CREATED && this.initiated) || this._state===LnForGasSwapState.PR_PAID;
280
+ }
281
+
275
282
  /**
276
283
  * @inheritDoc
277
284
  * @internal
@@ -311,7 +318,7 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
311
318
  /**
312
319
  * @inheritDoc
313
320
  */
314
- getOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
321
+ getOutput(): TokenAmount<SCToken<T["ChainId"]>, true> {
315
322
  return toTokenAmount(
316
323
  this.outputAmount, this.wrapper._tokens[this.wrapper._chain.getNativeCurrencyAddress()],
317
324
  this.wrapper._prices, this.pricingInfo
@@ -328,7 +335,7 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
328
335
  /**
329
336
  * @inheritDoc
330
337
  */
331
- getInput(): TokenAmount<T["ChainId"], BtcToken<true>, true> {
338
+ getInput(): TokenAmount<BtcToken<true>, true> {
332
339
  const parsed = bolt11Decode(this.pr);
333
340
  const msats = parsed.millisatoshis;
334
341
  if(msats==null) throw new Error("Swap lightning invoice has no msat amount field!");
@@ -339,7 +346,7 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
339
346
  /**
340
347
  * @inheritDoc
341
348
  */
342
- getInputWithoutFee(): TokenAmount<T["ChainId"], BtcToken<true>, true> {
349
+ getInputWithoutFee(): TokenAmount<BtcToken<true>, true> {
343
350
  const parsed = bolt11Decode(this.pr);
344
351
  const msats = parsed.millisatoshis;
345
352
  if(msats==null) throw new Error("Swap lightning invoice has no msat amount field!");
@@ -420,6 +427,13 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
420
427
  throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED");
421
428
  }
422
429
 
430
+ /**
431
+ * @remark Not supported
432
+ */
433
+ async execute(): Promise<boolean> {
434
+ throw new Error("Not supported");
435
+ }
436
+
423
437
  /**
424
438
  * @inheritDoc
425
439
  */
@@ -572,4 +586,4 @@ export class LnForGasSwap<T extends ChainType = ChainType> extends ISwap<T, LnFo
572
586
  return Promise.resolve(false);
573
587
  }
574
588
 
575
- }
589
+ }
@@ -314,6 +314,14 @@ export class OnchainForGasSwap<T extends ChainType = ChainType> extends ISwap<T,
314
314
  return this._state===OnchainForGasSwapState.FINISHED;
315
315
  }
316
316
 
317
+ /**
318
+ * @inheritDoc
319
+ */
320
+ isInProgress(): boolean {
321
+ return (this._state===OnchainForGasSwapState.PR_CREATED && this.txId!=null) ||
322
+ (this._state===OnchainForGasSwapState.REFUNDABLE && this.refundAddress!=null);
323
+ }
324
+
317
325
  /**
318
326
  * @inheritDoc
319
327
  * @internal
@@ -354,7 +362,7 @@ export class OnchainForGasSwap<T extends ChainType = ChainType> extends ISwap<T,
354
362
  /**
355
363
  * @inheritDoc
356
364
  */
357
- getOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
365
+ getOutput(): TokenAmount<SCToken<T["ChainId"]>, true> {
358
366
  return toTokenAmount(
359
367
  this.outputAmount, this.wrapper._tokens[this.wrapper._chain.getNativeCurrencyAddress()],
360
368
  this.wrapper._prices, this.pricingInfo
@@ -371,14 +379,14 @@ export class OnchainForGasSwap<T extends ChainType = ChainType> extends ISwap<T,
371
379
  /**
372
380
  * @inheritDoc
373
381
  */
374
- getInput(): TokenAmount<T["ChainId"], BtcToken<false>, true> {
382
+ getInput(): TokenAmount<BtcToken<false>, true> {
375
383
  return toTokenAmount(this.inputAmount, BitcoinTokens.BTC, this.wrapper._prices, this.pricingInfo);
376
384
  }
377
385
 
378
386
  /**
379
387
  * @inheritDoc
380
388
  */
381
- getInputWithoutFee(): TokenAmount<T["ChainId"], BtcToken<false>, true> {
389
+ getInputWithoutFee(): TokenAmount<BtcToken<false>, true> {
382
390
  return toTokenAmount(
383
391
  this.inputAmount - (this.swapFeeBtc ?? 0n), BitcoinTokens.BTC,
384
392
  this.wrapper._prices, this.pricingInfo
@@ -518,7 +526,7 @@ export class OnchainForGasSwap<T extends ChainType = ChainType> extends ISwap<T,
518
526
  /**
519
527
  * @inheritDoc
520
528
  */
521
- async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<any, BtcToken<false>, true> | null> {
529
+ async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<BtcToken<false>, true> | null> {
522
530
  const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
523
531
  const txFee = await bitcoinWallet.getTransactionFee(this.address, this.inputAmount, feeRate);
524
532
  if(txFee==null) return null;
@@ -584,6 +592,13 @@ export class OnchainForGasSwap<T extends ChainType = ChainType> extends ISwap<T,
584
592
  throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
585
593
  }
586
594
 
595
+ /**
596
+ * @remark Not supported
597
+ */
598
+ async execute(): Promise<boolean> {
599
+ throw new Error("Not supported");
600
+ }
601
+
587
602
  /**
588
603
  * @inheritDoc
589
604
  *