@atomiqlabs/sdk 8.6.0 → 8.6.3

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 (61) hide show
  1. package/dist/http/paramcoders/ParamDecoder.js +9 -4
  2. package/dist/http/paramcoders/ParamEncoder.js +6 -1
  3. package/dist/intermediaries/IntermediaryDiscovery.js +4 -3
  4. package/dist/storage/UnifiedSwapStorage.js +13 -8
  5. package/dist/swapper/Swapper.d.ts +1 -12
  6. package/dist/swapper/Swapper.js +31 -28
  7. package/dist/swapper/SwapperFactory.d.ts +1 -0
  8. package/dist/swapper/SwapperFactory.js +9 -4
  9. package/dist/swaps/ISwap.d.ts +8 -0
  10. package/dist/swaps/ISwap.js +8 -0
  11. package/dist/swaps/ISwapWrapper.d.ts +23 -1
  12. package/dist/swaps/ISwapWrapper.js +88 -28
  13. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +4 -1
  14. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +2 -2
  15. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +1 -1
  16. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +2 -2
  17. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +16 -6
  18. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +8 -2
  19. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +1 -1
  20. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +14 -4
  21. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +8 -6
  22. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +2 -1
  23. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +7 -5
  24. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +8 -2
  25. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +1 -1
  26. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +0 -6
  27. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +8 -3
  28. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +8 -2
  29. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +1 -0
  30. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +3 -2
  31. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +1 -3
  32. package/dist/swaps/trusted/ln/LnForGasWrapper.js +0 -1
  33. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +0 -1
  34. package/dist/utils/RetryUtils.d.ts +2 -1
  35. package/dist/utils/RetryUtils.js +3 -2
  36. package/package.json +1 -1
  37. package/src/http/paramcoders/ParamDecoder.ts +8 -4
  38. package/src/http/paramcoders/ParamEncoder.ts +5 -1
  39. package/src/intermediaries/IntermediaryDiscovery.ts +6 -4
  40. package/src/storage/UnifiedSwapStorage.ts +13 -8
  41. package/src/swapper/Swapper.ts +35 -28
  42. package/src/swapper/SwapperFactory.ts +12 -6
  43. package/src/swaps/ISwap.ts +8 -0
  44. package/src/swaps/ISwapWrapper.ts +104 -28
  45. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +5 -1
  46. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +3 -3
  47. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +17 -8
  48. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +8 -3
  49. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +13 -5
  50. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +8 -7
  51. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +9 -5
  52. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -2
  53. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1 -1
  54. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +0 -3
  55. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -3
  56. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +7 -2
  57. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +5 -2
  58. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1 -3
  59. package/src/swaps/trusted/ln/LnForGasWrapper.ts +0 -1
  60. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +0 -1
  61. package/src/utils/RetryUtils.ts +11 -4
@@ -13,6 +13,9 @@ import {PriceInfoType} from "../types/PriceInfoType";
13
13
  import {fromHumanReadableString} from "../utils/TokenUtils";
14
14
  import {UserError} from "../errors/UserError";
15
15
 
16
+ export const DEFAULT_MAX_PARALLEL_SWAP_TICKS = 50;
17
+ export const DEFAULT_MAX_PARALLEL_SWAP_SYNCS = 50;
18
+
16
19
  /**
17
20
  * Options for swap wrapper configuration
18
21
  *
@@ -20,7 +23,19 @@ import {UserError} from "../errors/UserError";
20
23
  */
21
24
  export type ISwapWrapperOptions = {
22
25
  getRequestTimeout?: number,
23
- postRequestTimeout?: number
26
+ postRequestTimeout?: number,
27
+ /**
28
+ * How many swaps to call `_tick()` for in parallel
29
+ */
30
+ maxParallelSwapTicks?: number,
31
+ /**
32
+ * How many swaps to call `_sync()` for in parallel
33
+ */
34
+ maxParallelSwapSyncs?: number,
35
+ /**
36
+ * Whether to save swaps that are not initialized into the persistent storage
37
+ */
38
+ saveUninitializedSwaps?: boolean
24
39
  };
25
40
 
26
41
  /**
@@ -101,6 +116,11 @@ export abstract class ISwapWrapper<
101
116
  * @internal
102
117
  */
103
118
  protected tickInterval?: NodeJS.Timeout;
119
+ /**
120
+ * An internal abort controller for the running tick handler
121
+ * @internal
122
+ */
123
+ protected tickAbortController?: AbortController;
104
124
 
105
125
 
106
126
  /**
@@ -152,6 +172,11 @@ export abstract class ISwapWrapper<
152
172
  options: O,
153
173
  events?: EventEmitter<{swapState: [ISwap]}>
154
174
  ) {
175
+ if(options?.maxParallelSwapTicks!=null && options.maxParallelSwapTicks < 1)
176
+ throw new Error("maxParallelSwapTicks must be at least 1!");
177
+ if(options?.maxParallelSwapSyncs!=null && options.maxParallelSwapSyncs < 1)
178
+ throw new Error("maxParallelSwapSyncs must be at least 1!");
179
+
155
180
  this.unifiedStorage = unifiedStorage;
156
181
  this.unifiedChainEvents = unifiedChainEvents;
157
182
 
@@ -274,9 +299,20 @@ export abstract class ISwapWrapper<
274
299
  */
275
300
  protected startTickInterval(): void {
276
301
  if(this.tickSwapState==null || this.tickSwapState.length===0) return;
277
- this.tickInterval = setInterval(() => {
278
- this.tick();
279
- }, 1000);
302
+ if(this.tickAbortController!=null) this.tickAbortController.abort("New tick interval has been started!");
303
+ const abortController = this.tickAbortController = new AbortController();
304
+ let run: () => Promise<void>;
305
+ run = async () => {
306
+ if(!this.isInitialized) return;
307
+ await this.tick(undefined, abortController.signal).catch(e => {
308
+ if(abortController.signal.aborted) return;
309
+ this.logger.warn("startTickInterval(): Tick on swaps failed, error: ", e);
310
+ });
311
+ if(abortController.signal.aborted) return;
312
+ if(!this.isInitialized) return;
313
+ this.tickInterval = setTimeout(run, 1000);
314
+ }
315
+ run();
280
316
  }
281
317
 
282
318
  /**
@@ -343,11 +379,11 @@ export abstract class ISwapWrapper<
343
379
 
344
380
  if(this.processEvent!=null) this.unifiedChainEvents.registerListener(this.TYPE, this.processEvent.bind(this), this._swapDeserializer.bind(null, this));
345
381
 
382
+ this.isInitialized = true;
383
+
346
384
  if(!noTimers) this.startTickInterval();
347
385
 
348
386
  // this.logger.info("init(): Swap wrapper initialized");
349
-
350
- this.isInitialized = true;
351
387
  }
352
388
 
353
389
  /**
@@ -357,12 +393,21 @@ export abstract class ISwapWrapper<
357
393
  this.isInitialized = false;
358
394
  this.unifiedChainEvents.unregisterListener(this.TYPE);
359
395
  this.logger.info("stop(): Swap wrapper stopped");
360
- if(this.tickInterval!=null) clearInterval(this.tickInterval);
396
+ if(this.tickInterval!=null) {
397
+ clearTimeout(this.tickInterval);
398
+ delete this.tickInterval;
399
+ }
400
+ if(this.tickAbortController!=null) {
401
+ this.tickAbortController.abort("Wrapper instance stopped!");
402
+ delete this.tickAbortController;
403
+ }
361
404
  }
362
405
 
363
406
  /**
364
407
  * Runs checks on all the known pending swaps, syncing their state from on-chain data
365
408
  *
409
+ * @remarks Doesn't work properly if you pass non-persisted swaps
410
+ *
366
411
  * @param pastSwaps Optional array of past swaps to check, otherwise all relevant swaps will be fetched
367
412
  * from the persistent storage
368
413
  * @param noSave Whether to skip saving the swap changes in the persistent storage
@@ -373,18 +418,25 @@ export abstract class ISwapWrapper<
373
418
  (val: any) => new this._swapDeserializer(this, val)
374
419
  );
375
420
 
376
- const {removeSwaps, changedSwaps} = await this._checkPastSwaps(pastSwaps);
377
-
378
- if (!noSave) {
379
- await this.unifiedStorage.removeAll(removeSwaps);
380
- await this.unifiedStorage.saveAll(changedSwaps);
381
- changedSwaps.forEach(swap => swap._emitEvent());
382
- removeSwaps.forEach(swap => swap._emitEvent());
421
+ const maxParallelSyncs = this._options.maxParallelSwapSyncs ?? DEFAULT_MAX_PARALLEL_SWAP_SYNCS;
422
+
423
+ const totalRemoveSwaps: D["Swap"][] = [];
424
+ const totalChangedSwaps: D["Swap"][] = [];
425
+ for(let i=0; i<pastSwaps.length; i+=maxParallelSyncs) {
426
+ const {removeSwaps, changedSwaps} = await this._checkPastSwaps(pastSwaps.slice(i, i+maxParallelSyncs));
427
+ if (!noSave) {
428
+ await this.unifiedStorage.removeAll(removeSwaps);
429
+ await this.unifiedStorage.saveAll(changedSwaps);
430
+ changedSwaps.forEach(swap => swap._emitEvent());
431
+ removeSwaps.forEach(swap => swap._emitEvent());
432
+ }
433
+ totalRemoveSwaps.push(...removeSwaps);
434
+ totalChangedSwaps.push(...changedSwaps);
383
435
  }
384
436
 
385
437
  return {
386
- removeSwaps,
387
- changedSwaps
438
+ removeSwaps: totalRemoveSwaps,
439
+ changedSwaps: totalChangedSwaps
388
440
  }
389
441
  }
390
442
 
@@ -393,21 +445,43 @@ export abstract class ISwapWrapper<
393
445
  *
394
446
  * @param swaps Optional array of swaps to invoke `_tick()` on, otherwise all relevant swaps will be fetched
395
447
  * from the persistent storage
448
+ * @param abortSignal Abort signal
396
449
  */
397
- public async tick(swaps?: D["Swap"][]): Promise<void> {
450
+ public async tick(swaps?: D["Swap"][], abortSignal?: AbortSignal): Promise<void> {
398
451
  if(swaps==null) swaps = await this.unifiedStorage.query<D["Swap"]>(
399
452
  [[{key: "type", value: this.TYPE}, {key: "state", value: this.tickSwapState}]],
400
453
  (val: any) => new this._swapDeserializer(this, val)
401
454
  );
455
+ abortSignal?.throwIfAborted();
402
456
 
457
+ const parallelTicks = this._options.maxParallelSwapTicks ?? DEFAULT_MAX_PARALLEL_SWAP_TICKS;
458
+
459
+ let promises: Promise<any>[] = [];
403
460
  for(let pendingSwap of this.pendingSwaps.values()) {
404
461
  const value = pendingSwap.deref();
405
- if(value != null) value._tick(true);
462
+ if(value != null) promises.push(value._tick(true).catch(e => {
463
+ this.logger.warn(`tick(): Error ticking swap ${value.getId()}: `, e);
464
+ }));
465
+ if(promises.length >= parallelTicks) {
466
+ await Promise.all(promises);
467
+ abortSignal?.throwIfAborted();
468
+ promises = [];
469
+ }
406
470
  }
407
471
 
408
- swaps.forEach(value => {
409
- value._tick(true)
410
- });
472
+ for(let value of swaps) {
473
+ promises.push(value._tick(true).catch(e => {
474
+ this.logger.warn(`tick(): Error ticking swap ${value.getId()}: `, e);
475
+ }));
476
+ if(promises.length >= parallelTicks) {
477
+ await Promise.all(promises);
478
+ abortSignal?.throwIfAborted();
479
+ promises = [];
480
+ }
481
+ }
482
+
483
+ if(promises.length>0) await Promise.all(promises);
484
+ abortSignal?.throwIfAborted();
411
485
  }
412
486
 
413
487
  /**
@@ -426,12 +500,14 @@ export abstract class ISwapWrapper<
426
500
  * @internal
427
501
  */
428
502
  _saveSwapData(swap: D["Swap"]): Promise<void> {
429
- if(!swap.isInitiated()) {
430
- this.logger.debug("saveSwapData(): Swap "+swap.getId()+" not initiated, saving to pending swaps");
431
- this.pendingSwaps.set(swap.getId(), new WeakRef<D["Swap"]>(swap));
432
- return Promise.resolve();
433
- } else {
434
- this.pendingSwaps.delete(swap.getId());
503
+ if(!this._options.saveUninitializedSwaps) {
504
+ if(!swap.isInitiated()) {
505
+ this.logger.debug("saveSwapData(): Swap "+swap.getId()+" not initiated, saving to pending swaps");
506
+ this.pendingSwaps.set(swap.getId(), new WeakRef<D["Swap"]>(swap));
507
+ return Promise.resolve();
508
+ } else {
509
+ this.pendingSwaps.delete(swap.getId());
510
+ }
435
511
  }
436
512
  return this.unifiedStorage.save(swap);
437
513
  }
@@ -445,7 +521,7 @@ export abstract class ISwapWrapper<
445
521
  */
446
522
  _removeSwapData(swap: D["Swap"]): Promise<void> {
447
523
  this.pendingSwaps.delete(swap.getId());
448
- if(!swap.isInitiated()) return Promise.resolve();
524
+ if(!swap._persisted) return Promise.resolve();
449
525
  return this.unifiedStorage.remove(swap);
450
526
  }
451
527
 
@@ -66,7 +66,11 @@ export abstract class IEscrowSwapWrapper<
66
66
  * @internal
67
67
  */
68
68
  protected preFetchSignData(signDataPrefetch: Promise<any | null>): Promise<T["PreFetchVerification"] | undefined> {
69
- if(this._contract.preFetchForInitSignatureVerification==null) return Promise.resolve(undefined);
69
+ if(this._contract.preFetchForInitSignatureVerification==null) {
70
+ // Catch promise rejections, should they happen
71
+ signDataPrefetch.catch(() => {});
72
+ return Promise.resolve(undefined);
73
+ }
70
74
  return signDataPrefetch.then(obj => {
71
75
  if(obj==null) return undefined;
72
76
  return this._contract.preFetchForInitSignatureVerification!(obj);
@@ -71,7 +71,7 @@ export abstract class IFromBTCSelfInitSwap<
71
71
  * Returns if the swap can be committed
72
72
  * @internal
73
73
  */
74
- protected abstract canCommit(): boolean;
74
+ protected abstract canCommit(skipQuoteExpiryChecks?: boolean): boolean;
75
75
 
76
76
  /**
77
77
  * @inheritDoc
@@ -239,7 +239,7 @@ export abstract class IFromBTCSelfInitSwap<
239
239
  * @throws {Error} When in invalid state to commit the swap
240
240
  */
241
241
  async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
242
- if(!this.canCommit()) throw new Error("Must be in CREATED state!");
242
+ if(!this.canCommit(skipChecks)) throw new Error("Must be in CREATED state!");
243
243
  if(this._data==null || this.signatureData==null) throw new Error("data or signature data is null, invalid state?");
244
244
 
245
245
  if(!this.initiated) {
@@ -285,7 +285,7 @@ export abstract class IFromBTCSelfInitSwap<
285
285
  /**
286
286
  * @inheritDoc
287
287
  */
288
- abstract txsClaim(signer?: T["Signer"]): Promise<T["TX"][]>;
288
+ abstract txsClaim(signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]>;
289
289
 
290
290
  /**
291
291
  * @inheritDoc
@@ -282,8 +282,8 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
282
282
  * @inheritDoc
283
283
  * @internal
284
284
  */
285
- protected canCommit(): boolean {
286
- return this._state===FromBTCLNSwapState.PR_PAID;
285
+ protected canCommit(skipQuoteExpiryChecks?: boolean): boolean {
286
+ return this._state===FromBTCLNSwapState.PR_PAID || (!!skipQuoteExpiryChecks && this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED);
287
287
  }
288
288
 
289
289
  /**
@@ -899,7 +899,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
899
899
  );
900
900
 
901
901
  this._commitTxId = result[result.length-1];
902
- if(this._state===FromBTCLNSwapState.PR_PAID || this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
902
+ if(this._state===FromBTCLNSwapState.PR_PAID || this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this._state===FromBTCLNSwapState.QUOTE_EXPIRED) {
903
903
  await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
904
904
  }
905
905
  return this._commitTxId;
@@ -953,7 +953,18 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
953
953
  *
954
954
  * @internal
955
955
  */
956
- private async _txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
956
+ private async _txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
957
+ let address: string | undefined = undefined;
958
+ if(_signer!=null) {
959
+ if (typeof (_signer) === "string") {
960
+ address = _signer;
961
+ } else if (isAbstractSigner(_signer)) {
962
+ address = _signer.getAddress();
963
+ } else {
964
+ address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
965
+ }
966
+ }
967
+
957
968
  if(this._data==null) throw new Error("Unknown data, wrong state?");
958
969
  const useSecret = secret ?? this.secret;
959
970
  if(useSecret==null)
@@ -962,9 +973,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
962
973
  throw new Error("Invalid swap secret pre-image provided!");
963
974
 
964
975
  return this.wrapper._contract.txsClaimWithSecret(
965
- _signer==null ?
966
- this._getInitiator() :
967
- (isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer)),
976
+ address ?? this._getInitiator(),
968
977
  this._data, useSecret, true, true
969
978
  );
970
979
  }
@@ -978,7 +987,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
978
987
  *
979
988
  * @throws {Error} If in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
980
989
  */
981
- async txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
990
+ async txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
982
991
  if(this._state!==FromBTCLNSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state!");
983
992
  return this._txsClaim(_signer, secret);
984
993
  }
@@ -4,7 +4,7 @@ import {
4
4
  ChainSwapType,
5
5
  ChainType,
6
6
  ClaimEvent,
7
- InitializeEvent, LightningNetworkApi,
7
+ InitializeEvent, LightningNetworkApi, LNNodeLiquidity,
8
8
  RefundEvent, SwapCommitState, SwapCommitStateType
9
9
  } from "@atomiqlabs/base";
10
10
  import {Intermediary} from "../../../../intermediaries/Intermediary";
@@ -137,6 +137,7 @@ export class FromBTCLNWrapper<
137
137
  super(
138
138
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer, lnApi,
139
139
  {
140
+ ...options,
140
141
  safetyFactor: options?.safetyFactor ?? 2,
141
142
  bitcoinBlocktime: options?.bitcoinBlocktime ?? 10*60,
142
143
  unsafeSkipLnNodeCheck: options?.unsafeSkipLnNodeCheck ?? false
@@ -317,8 +318,13 @@ export class FromBTCLNWrapper<
317
318
  this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
318
319
  );
319
320
 
321
+ let lnCapacityPromise: Promise<LNNodeLiquidity | null> | undefined;
322
+ if(!_options.unsafeSkipLnNodeCheck) {
323
+ lnCapacityPromise = this.preFetchLnCapacity(lnPublicKey);
324
+ } else lnPublicKey.catch(() => {});
325
+
320
326
  return {
321
- lnCapacityPromise: _options.unsafeSkipLnNodeCheck ? null : this.preFetchLnCapacity(lnPublicKey),
327
+ lnCapacityPromise,
322
328
  resp: await response
323
329
  };
324
330
  }, undefined, RequestError, abortController.signal);
@@ -356,7 +362,6 @@ export class FromBTCLNWrapper<
356
362
  secret: secret?.toString("hex"),
357
363
  exactIn: amountData.exactIn ?? true
358
364
  } as FromBTCLNSwapInit<T["Data"]>);
359
- await quote._save();
360
365
  return quote;
361
366
  } catch (e) {
362
367
  abortController.abort(e);
@@ -566,7 +566,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
566
566
  * @internal
567
567
  */
568
568
  protected getInputAmountWithoutFee(): bigint | null {
569
- if(this.btcAmountGas==null || this.btcAmountSwap) return null;
569
+ if(this.btcAmountGas==null || this.btcAmountSwap==null) return null;
570
570
  return this.getInputSwapAmountWithoutFee()! + this.getInputGasAmountWithoutFee()! - this.getWatchtowerFeeAmountBtc()!;
571
571
  }
572
572
 
@@ -1144,7 +1144,17 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1144
1144
  *
1145
1145
  * @throws {Error} If in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1146
1146
  */
1147
- async txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
1147
+ async txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
1148
+ let address: string | undefined = undefined;
1149
+ if(_signer!=null) {
1150
+ if (typeof (_signer) === "string") {
1151
+ address = _signer;
1152
+ } else if (isAbstractSigner(_signer)) {
1153
+ address = _signer.getAddress();
1154
+ } else {
1155
+ address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
1156
+ }
1157
+ }
1148
1158
  if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state!");
1149
1159
  if(this._data==null) throw new Error("Unknown data, wrong state?");
1150
1160
 
@@ -1155,9 +1165,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1155
1165
  throw new Error("Invalid swap secret pre-image provided!");
1156
1166
 
1157
1167
  return await this.wrapper._contract.txsClaimWithSecret(
1158
- _signer==null ?
1159
- this._getInitiator() :
1160
- (isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer)),
1168
+ address ?? this._getInitiator(),
1161
1169
  this._data, useSecret, true, true
1162
1170
  );
1163
1171
  }
@@ -3,7 +3,7 @@ import {
3
3
  ChainSwapType,
4
4
  ChainType,
5
5
  ClaimEvent,
6
- InitializeEvent, LightningNetworkApi, Messenger,
6
+ InitializeEvent, LightningNetworkApi, LNNodeLiquidity, Messenger,
7
7
  RefundEvent, SwapCommitState, SwapCommitStateType
8
8
  } from "@atomiqlabs/base";
9
9
  import {Intermediary} from "../../../../intermediaries/Intermediary";
@@ -174,6 +174,7 @@ export class FromBTCLNAutoWrapper<
174
174
  super(
175
175
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer, lnApi,
176
176
  {
177
+ ...options,
177
178
  safetyFactor: options?.safetyFactor ?? 2,
178
179
  bitcoinBlocktime: options?.bitcoinBlocktime ?? 10*60,
179
180
  unsafeSkipLnNodeCheck: options?.unsafeSkipLnNodeCheck ?? false
@@ -210,7 +211,6 @@ export class FromBTCLNAutoWrapper<
210
211
  return false;
211
212
  }
212
213
 
213
- swap._commitTxId = event.meta?.txId;
214
214
  swap._commitedAt ??= Date.now();
215
215
  swap._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
216
216
  if(swap.hasSecretPreimage()) swap._broadcastSecret().catch(e => {
@@ -227,7 +227,6 @@ export class FromBTCLNAutoWrapper<
227
227
  */
228
228
  protected processEventClaim(swap: FromBTCLNAutoSwap<T>, event: ClaimEvent<T["Data"]>): Promise<boolean> {
229
229
  if(swap._state!==FromBTCLNAutoSwapState.FAILED && swap._state!==FromBTCLNAutoSwapState.CLAIM_CLAIMED) {
230
- swap._claimTxId = event.meta?.txId;
231
230
  swap._state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
232
231
  swap._setSwapSecret(event.result);
233
232
  return Promise.resolve(true);
@@ -241,7 +240,6 @@ export class FromBTCLNAutoWrapper<
241
240
  */
242
241
  protected processEventRefund(swap: FromBTCLNAutoSwap<T>, event: RefundEvent<T["Data"]>): Promise<boolean> {
243
242
  if(swap._state!==FromBTCLNAutoSwapState.CLAIM_CLAIMED && swap._state!==FromBTCLNAutoSwapState.FAILED) {
244
- swap._refundTxId ??= event.meta?.txId;
245
243
  swap._state = FromBTCLNAutoSwapState.FAILED;
246
244
  return Promise.resolve(true);
247
245
  }
@@ -435,8 +433,13 @@ export class FromBTCLNAutoWrapper<
435
433
  this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
436
434
  );
437
435
 
436
+ let lnCapacityPromise: Promise<LNNodeLiquidity | null> | undefined;
437
+ if(!_options.unsafeSkipLnNodeCheck) {
438
+ lnCapacityPromise = this.preFetchLnCapacity(lnPublicKey);
439
+ } else lnPublicKey.catch(() => {});
440
+
438
441
  return {
439
- lnCapacityPromise: _options.unsafeSkipLnNodeCheck ? undefined : this.preFetchLnCapacity(lnPublicKey),
442
+ lnCapacityPromise,
440
443
  resp: await response
441
444
  };
442
445
  }, undefined, RequestError, abortController.signal);
@@ -493,8 +496,6 @@ export class FromBTCLNAutoWrapper<
493
496
  exactIn: amountData.exactIn ?? true
494
497
  };
495
498
  const quote = new FromBTCLNAutoSwap<T>(this, swapInit);
496
- await quote._save();
497
- this.logger.debug("create(): Created new FromBTCLNAutoSwap quote, claimHash (pseudo escrowHash): ", quote._getEscrowHash());
498
499
  return quote;
499
500
  } catch (e) {
500
501
  abortController.abort(e);
@@ -333,8 +333,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
333
333
  * @inheritDoc
334
334
  * @internal
335
335
  */
336
- protected canCommit(): boolean {
337
- if(this._state!==FromBTCSwapState.PR_CREATED) return false;
336
+ protected canCommit(skipQuoteExpiryChecks?: boolean): boolean {
337
+ if(this._state!==FromBTCSwapState.PR_CREATED && (!skipQuoteExpiryChecks || this._state!==FromBTCSwapState.QUOTE_SOFT_EXPIRED)) return false;
338
338
  if(this.requiredConfirmations==null) return false;
339
339
  const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
340
340
  const currentTimestamp = BigInt(Math.floor(Date.now()/1000));
@@ -900,7 +900,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
900
900
  );
901
901
 
902
902
  this._commitTxId = result[result.length - 1];
903
- if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
903
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===FromBTCSwapState.QUOTE_EXPIRED) {
904
904
  await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
905
905
  }
906
906
  return this._commitTxId;
@@ -1191,6 +1191,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1191
1191
  return changed;
1192
1192
  }
1193
1193
 
1194
+ private btcTxLastChecked?: number;
1195
+
1194
1196
  /**
1195
1197
  * @inheritDoc
1196
1198
  * @internal
@@ -1222,6 +1224,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1222
1224
  }
1223
1225
  if(this.address==null) return save;
1224
1226
 
1227
+ this.btcTxLastChecked = Date.now();
1225
1228
  const res = await this.getBitcoinPayment();
1226
1229
  if(res!=null) {
1227
1230
  if(this.txId!==res.txId) {
@@ -1261,9 +1264,10 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1261
1264
  return true;
1262
1265
  }
1263
1266
  case FromBTCSwapState.EXPIRED:
1264
- //Check if bitcoin payment was received every 2 minutes
1265
- if(Math.floor(Date.now()/1000)%120===0) {
1267
+ //Check if bitcoin payment was received at least every 2 minutes
1268
+ if(this.btcTxLastChecked==null || Date.now() - this.btcTxLastChecked > 120_000) {
1266
1269
  if(this.address!=null) try {
1270
+ this.btcTxLastChecked = Date.now();
1267
1271
  const res = await this.getBitcoinPayment();
1268
1272
  if(res!=null) {
1269
1273
  let shouldSave: boolean = false;
@@ -150,6 +150,7 @@ export class FromBTCWrapper<
150
150
  super(
151
151
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
152
152
  {
153
+ ...options,
153
154
  bitcoinNetwork: options?.bitcoinNetwork ?? TEST_NETWORK,
154
155
  safetyFactor: options?.safetyFactor ?? 2,
155
156
  blocksTillTxConfirms: options?.blocksTillTxConfirms ?? 12,
@@ -472,8 +473,13 @@ export class FromBTCWrapper<
472
473
  this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
473
474
  );
474
475
 
476
+ let signDataPromise = _signDataPromise;
477
+ if(signDataPromise==null) {
478
+ signDataPromise = this.preFetchSignData(signDataPrefetch);
479
+ } else signDataPrefetch.catch(() => {});
480
+
475
481
  return {
476
- signDataPromise: _signDataPromise ?? this.preFetchSignData(signDataPrefetch),
482
+ signDataPromise,
477
483
  resp: await response
478
484
  };
479
485
  }, undefined, e => e instanceof RequestError, abortController.signal);
@@ -506,7 +512,6 @@ export class FromBTCWrapper<
506
512
  exactIn: amountData.exactIn ?? true,
507
513
  requiredConfirmations: resp.confirmations
508
514
  } as FromBTCSwapInit<T["Data"]>);
509
- await quote._save();
510
515
  return quote;
511
516
  } catch (e) {
512
517
  abortController.abort(e);
@@ -593,7 +593,7 @@ export abstract class IToBTCSwap<
593
593
  * @throws {Error} When in invalid state (not {@link ToBTCSwapState.CREATED})
594
594
  */
595
595
  async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
596
- if(this._state!==ToBTCSwapState.CREATED) throw new Error("Must be in CREATED state!");
596
+ if(this._state!==ToBTCSwapState.CREATED && (!skipChecks || this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED)) throw new Error("Must be in CREATED state!");
597
597
  if(this.signatureData==null) throw new Error("Init signature data not known, cannot commit!");
598
598
 
599
599
  if(!this.initiated) {
@@ -90,7 +90,6 @@ export abstract class IToBTCWrapper<
90
90
  protected async processEventInitialize(swap: D["Swap"], event: InitializeEvent<T["Data"]>): Promise<boolean> {
91
91
  if(swap._state===ToBTCSwapState.CREATED || swap._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
92
92
  swap._state = ToBTCSwapState.COMMITED;
93
- if(swap._commitTxId==null) swap._commitTxId = event.meta?.txId;
94
93
  return true;
95
94
  }
96
95
  return false;
@@ -108,7 +107,6 @@ export abstract class IToBTCWrapper<
108
107
  this.logger.warn(`processEventClaim(): Failed to set payment result ${event.result}: `, e);
109
108
  });
110
109
  swap._state = ToBTCSwapState.CLAIMED;
111
- if(swap._claimTxId==null) swap._claimTxId = event.meta?.txId;
112
110
  return true;
113
111
  }
114
112
  return false;
@@ -120,7 +118,6 @@ export abstract class IToBTCWrapper<
120
118
  protected processEventRefund(swap: D["Swap"], event: RefundEvent<T["Data"]>): Promise<boolean> {
121
119
  if(swap._state!==ToBTCSwapState.CLAIMED && swap._state!==ToBTCSwapState.REFUNDED) {
122
120
  swap._state = ToBTCSwapState.REFUNDED;
123
- if(swap._refundTxId==null) swap._refundTxId = event.meta?.txId;
124
121
  return Promise.resolve(true);
125
122
  }
126
123
  return Promise.resolve(false);
@@ -112,6 +112,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
112
112
  super(
113
113
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
114
114
  {
115
+ ...options,
115
116
  paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 5*24*60*60,
116
117
  lightningBaseFee: options?.lightningBaseFee ?? 10,
117
118
  lightningFeePPM: options?.lightningFeePPM ?? 2000
@@ -289,8 +290,13 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
289
290
  additionalParams
290
291
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
291
292
 
293
+ let signDataPromise = preFetches.signDataPrefetchPromise;
294
+ if(signDataPromise==null) {
295
+ signDataPromise = this.preFetchSignData(signDataPrefetch);
296
+ } else signDataPrefetch.catch(() => {});
297
+
292
298
  return {
293
- signDataPromise: preFetches.signDataPrefetchPromise ?? this.preFetchSignData(signDataPrefetch),
299
+ signDataPromise,
294
300
  resp: await response
295
301
  };
296
302
  }, undefined, e => e instanceof RequestError, abortController.signal);
@@ -335,7 +341,6 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
335
341
  pr,
336
342
  exactIn: false
337
343
  } as IToBTCSwapInit<T["Data"]>);
338
- await quote._save();
339
344
  return quote;
340
345
  } catch (e) {
341
346
  abortController.abort(e);
@@ -539,7 +544,6 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
539
544
  pr: invoice,
540
545
  exactIn: true
541
546
  } as IToBTCSwapInit<T["Data"]>);
542
- await quote._save();
543
547
  return quote;
544
548
  } catch (e) {
545
549
  abortController.abort(e);
@@ -97,6 +97,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
97
97
  super(
98
98
  chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
99
99
  {
100
+ ...options,
100
101
  bitcoinNetwork: options?.bitcoinNetwork ?? TEST_NETWORK,
101
102
  safetyFactor: options?.safetyFactor ?? 2,
102
103
  maxConfirmations: options?.maxConfirmations ?? 6,
@@ -272,8 +273,13 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
272
273
  additionalParams
273
274
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
274
275
 
276
+ let signDataPromise = _signDataPromise;
277
+ if(signDataPromise==null) {
278
+ signDataPromise = this.preFetchSignData(signDataPrefetch);
279
+ } else signDataPrefetch.catch(() => {});
280
+
275
281
  return {
276
- signDataPromise: _signDataPromise ?? this.preFetchSignData(signDataPrefetch),
282
+ signDataPromise,
277
283
  resp: await response
278
284
  };
279
285
  }, undefined, RequestError, abortController.signal);
@@ -319,7 +325,6 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
319
325
  requiredConfirmations: _options.confirmations,
320
326
  nonce
321
327
  } as ToBTCSwapInit<T["Data"]>);
322
- await quote._save();
323
328
  return quote;
324
329
  } catch (e) {
325
330
  abortController.abort(e);