@atomiqlabs/sdk 8.6.0 → 8.6.2

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 (52) hide show
  1. package/dist/storage/UnifiedSwapStorage.js +13 -8
  2. package/dist/swapper/Swapper.d.ts +1 -12
  3. package/dist/swapper/Swapper.js +25 -26
  4. package/dist/swapper/SwapperFactory.d.ts +1 -0
  5. package/dist/swapper/SwapperFactory.js +9 -4
  6. package/dist/swaps/ISwap.d.ts +8 -0
  7. package/dist/swaps/ISwap.js +8 -0
  8. package/dist/swaps/ISwapWrapper.d.ts +23 -1
  9. package/dist/swaps/ISwapWrapper.js +88 -28
  10. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +4 -1
  11. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +2 -2
  12. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +1 -1
  13. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +2 -2
  14. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +16 -6
  15. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +8 -2
  16. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +1 -1
  17. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +14 -4
  18. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +8 -6
  19. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +2 -1
  20. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +7 -5
  21. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +8 -2
  22. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +1 -1
  23. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +0 -6
  24. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +8 -3
  25. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +8 -2
  26. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +1 -0
  27. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +3 -2
  28. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +1 -3
  29. package/dist/swaps/trusted/ln/LnForGasWrapper.js +0 -1
  30. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +0 -1
  31. package/package.json +1 -1
  32. package/src/storage/UnifiedSwapStorage.ts +13 -8
  33. package/src/swapper/Swapper.ts +27 -27
  34. package/src/swapper/SwapperFactory.ts +12 -6
  35. package/src/swaps/ISwap.ts +8 -0
  36. package/src/swaps/ISwapWrapper.ts +104 -28
  37. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +5 -1
  38. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +3 -3
  39. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +17 -8
  40. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +8 -3
  41. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +13 -5
  42. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +8 -7
  43. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +9 -5
  44. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -2
  45. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1 -1
  46. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +0 -3
  47. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -3
  48. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +7 -2
  49. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +5 -2
  50. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1 -3
  51. package/src/swaps/trusted/ln/LnForGasWrapper.ts +0 -1
  52. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +0 -1
@@ -316,7 +316,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
316
316
  bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
317
317
  chainsData: CtorMultiChainData<T>,
318
318
  pricing: ISwapPrice<T>,
319
- tokens: SwapperCtorTokens<T>,
319
+ tokens: SCToken[],
320
320
  messenger: Messenger,
321
321
  options?: SwapperOptions
322
322
  ) {
@@ -347,22 +347,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
347
347
  this._tokens = {};
348
348
  this._tokensByTicker = {};
349
349
  for(let tokenData of tokens) {
350
- for(let chainId in tokenData.chains) {
351
- const chainData = tokenData.chains[chainId]!;
352
- this._tokens[chainId] ??= {};
353
- this._tokensByTicker[chainId] ??= {};
354
- this._tokens[chainId][chainData.address] = this._tokensByTicker[chainId][tokenData.ticker] = {
355
- chain: "SC",
356
- chainId,
357
- ticker: tokenData.ticker,
358
- name: tokenData.name,
359
- decimals: chainData.decimals,
360
- displayDecimals: chainData.displayDecimals,
361
- address: chainData.address,
362
- equals: (other: Token) => other.chainId===chainId && other.ticker===tokenData.ticker && other.address===chainData.address,
363
- toString: () => `${chainId}-${tokenData.ticker}`
364
- }
365
- }
350
+ const chainId = tokenData.chainId;
351
+ this._tokens[chainId] ??= {};
352
+ this._tokensByTicker[chainId] ??= {};
353
+ this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
366
354
  }
367
355
 
368
356
  this.swapStateListener = (swap: ISwap) => {
@@ -395,6 +383,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
395
383
  {
396
384
  getRequestTimeout: this.options.getRequestTimeout,
397
385
  postRequestTimeout: this.options.postRequestTimeout,
386
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
398
387
  }
399
388
  );
400
389
  wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
@@ -410,6 +399,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
410
399
  {
411
400
  getRequestTimeout: this.options.getRequestTimeout,
412
401
  postRequestTimeout: this.options.postRequestTimeout,
402
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
413
403
  bitcoinNetwork: this._btcNetwork
414
404
  }
415
405
  );
@@ -426,6 +416,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
426
416
  {
427
417
  getRequestTimeout: this.options.getRequestTimeout,
428
418
  postRequestTimeout: this.options.postRequestTimeout,
419
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
429
420
  unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
430
421
  }
431
422
  );
@@ -444,6 +435,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
444
435
  {
445
436
  getRequestTimeout: this.options.getRequestTimeout,
446
437
  postRequestTimeout: this.options.postRequestTimeout,
438
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
447
439
  bitcoinNetwork: this._btcNetwork
448
440
  }
449
441
  );
@@ -456,7 +448,8 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
456
448
  this._tokens[chainId],
457
449
  {
458
450
  getRequestTimeout: this.options.getRequestTimeout,
459
- postRequestTimeout: this.options.postRequestTimeout
451
+ postRequestTimeout: this.options.postRequestTimeout,
452
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
460
453
  }
461
454
  );
462
455
  wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
@@ -470,6 +463,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
470
463
  {
471
464
  getRequestTimeout: this.options.getRequestTimeout,
472
465
  postRequestTimeout: this.options.postRequestTimeout,
466
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
473
467
  bitcoinNetwork: this._btcNetwork
474
468
  }
475
469
  );
@@ -490,6 +484,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
490
484
  {
491
485
  getRequestTimeout: this.options.getRequestTimeout,
492
486
  postRequestTimeout: this.options.postRequestTimeout,
487
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
493
488
  bitcoinNetwork: this._btcNetwork
494
489
  }
495
490
  );
@@ -510,6 +505,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
510
505
  {
511
506
  getRequestTimeout: this.options.getRequestTimeout,
512
507
  postRequestTimeout: this.options.postRequestTimeout,
508
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
513
509
  unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
514
510
  }
515
511
  );
@@ -659,7 +655,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
659
655
  */
660
656
  async init(): Promise<void> {
661
657
  if(this.initialized) return;
662
- if(this.initPromise!=null) await this.initPromise;
658
+ if(this.initPromise!=null) {
659
+ await this.initPromise;
660
+ return;
661
+ }
663
662
 
664
663
  try {
665
664
  const promise = this._init();
@@ -856,10 +855,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
856
855
  if(swapLimitsChanged) this.emit("swapLimitsChanged");
857
856
 
858
857
  const quote = quotes[0].quote;
859
- if(this.options.saveUninitializedSwaps) {
860
- quote._setInitiated();
861
- await quote._save();
862
- }
858
+ await quote._save();
863
859
  return quote;
864
860
  } catch (e) {
865
861
  if(swapLimitsChanged) this.emit("swapLimitsChanged");
@@ -1334,13 +1330,15 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1334
1330
  * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1335
1331
  * @throws {Error} If no trusted intermediary specified
1336
1332
  */
1337
- createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
1333
+ async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
1338
1334
  if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1339
1335
  if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1340
1336
  recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1341
1337
  const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1342
1338
  if(useUrl==null) throw new Error("No trusted intermediary specified!");
1343
- return this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
1339
+ const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
1340
+ await swap._save();
1341
+ return swap;
1344
1342
  }
1345
1343
 
1346
1344
  /**
@@ -1353,7 +1351,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1353
1351
  * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1354
1352
  * @throws {Error} If no trusted intermediary specified
1355
1353
  */
1356
- createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1354
+ async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1357
1355
  chainIdentifier: C, recipient: string,
1358
1356
  amount: bigint, refundAddress?: string,
1359
1357
  trustedIntermediaryOrUrl?: Intermediary | string
@@ -1363,7 +1361,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1363
1361
  recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1364
1362
  const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1365
1363
  if(useUrl==null) throw new Error("No trusted intermediary specified!");
1366
- return this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
1364
+ const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
1365
+ await swap._save();
1366
+ return swap;
1367
1367
  }
1368
1368
 
1369
1369
  /**
@@ -9,7 +9,7 @@ import {SmartChainAssets, SmartChainAssetTickers} from "../SmartChainAssets";
9
9
  import {NostrMessenger} from "@atomiqlabs/messenger-nostr";
10
10
  import {Swapper, SwapperOptions} from "./Swapper";
11
11
  import {CustomPriceProvider} from "../prices/providers/CustomPriceProvider";
12
- import {BitcoinTokens, BtcToken, SCToken} from "../types/Token";
12
+ import {BitcoinTokens, BtcToken, SCToken, Token} from "../types/Token";
13
13
  import {SwapType} from "../enums/SwapType";
14
14
  import {SwapTypeMapping} from "../utils/SwapUtils";
15
15
  import {RedundantSwapPrice, RedundantSwapPriceAssets} from "../prices/RedundantSwapPrice";
@@ -191,6 +191,8 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
191
191
  */
192
192
  TokenResolver: TypedTokenResolvers<T> = {} as any;
193
193
 
194
+ private smartChainTokens: SCToken[] = [];
195
+
194
196
  constructor(readonly initializers: T) {
195
197
  this.initializers = initializers;
196
198
  initializers.forEach(initializer => {
@@ -200,15 +202,19 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
200
202
 
201
203
  for(let ticker in initializer.tokens) {
202
204
  const assetData = initializer.tokens[ticker] as any;
203
- tokens[ticker] = addressMap[assetData.address] = {
205
+ const token: SCToken = {
204
206
  chain: "SC",
205
207
  chainId: initializer.chainId,
206
- address: assetData.address,
208
+ ticker,
207
209
  name: SmartChainAssets[ticker as SmartChainAssetTickers]?.name ?? ticker,
208
210
  decimals: assetData.decimals,
209
211
  displayDecimals: assetData.displayDecimals,
210
- ticker
211
- } as any;
212
+ address: assetData.address,
213
+ equals: (other: Token) => other.chainId===initializer.chainId && other.ticker===ticker && other.address===assetData.address,
214
+ toString: () => `${initializer.chainId}-${ticker}`
215
+ };
216
+ this.smartChainTokens.push(token);
217
+ tokens[ticker] = addressMap[assetData.address] = token;
212
218
  }
213
219
 
214
220
  this.TokenResolver[initializer.chainId as keyof TypedTokenResolvers<T>] = {
@@ -280,7 +286,7 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
280
286
  (btcRelay: BtcRelay<any, any, any>) => new MempoolBtcRelaySynchronizer(btcRelay, bitcoinRpc),
281
287
  chains as any,
282
288
  swapPricing,
283
- pricingAssets,
289
+ this.smartChainTokens,
284
290
  options.messenger,
285
291
  options
286
292
  );
@@ -132,6 +132,14 @@ export abstract class ISwap<
132
132
  * @internal
133
133
  */
134
134
  _randomNonce: string;
135
+ /**
136
+ * Whether the swap is saved in the persistent storage or not.
137
+ *
138
+ * @remarks This field itself is not persisted but is instead derived during runtime
139
+ *
140
+ * @internal
141
+ */
142
+ _persisted: boolean = false;
135
143
 
136
144
 
137
145
  /**
@@ -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
  }