@atomiqlabs/sdk 8.5.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 +10 -15
  3. package/dist/swapper/Swapper.js +26 -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 +10 -2
  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 +12 -9
  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 +3 -0
  27. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +14 -4
  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 +37 -30
  34. package/src/swapper/SwapperFactory.ts +12 -6
  35. package/src/swaps/ISwap.ts +11 -3
  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 +9 -8
  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 +19 -4
  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
@@ -152,9 +152,15 @@ export type SwapperOptions = {
152
152
  */
153
153
  dontFetchLPs?: boolean,
154
154
  /**
155
- * By setting this flag the SDK persists all created swaps. By default, the SDK only saves and persists swaps that
156
- * are considered initiated, i.e. when `commit()`, `execute()` or `waitTillPayment` is called (or their respective
157
- * txs... prefixed variations).
155
+ * Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
156
+ * `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
157
+ * want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
158
+ *
159
+ * Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
160
+ * `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
161
+ * might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
162
+ * frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
163
+ * necessitating the saving of the swap data to the persistent storage until it is actually initiated.
158
164
  */
159
165
  saveUninitializedSwaps?: boolean,
160
166
  /**
@@ -310,7 +316,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
310
316
  bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
311
317
  chainsData: CtorMultiChainData<T>,
312
318
  pricing: ISwapPrice<T>,
313
- tokens: SwapperCtorTokens<T>,
319
+ tokens: SCToken[],
314
320
  messenger: Messenger,
315
321
  options?: SwapperOptions
316
322
  ) {
@@ -318,6 +324,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
318
324
  const storagePrefix = options?.storagePrefix ?? "atomiq-";
319
325
 
320
326
  options ??= {};
327
+ options.saveUninitializedSwaps ??= true;
321
328
  options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
322
329
  const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
323
330
 
@@ -340,22 +347,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
340
347
  this._tokens = {};
341
348
  this._tokensByTicker = {};
342
349
  for(let tokenData of tokens) {
343
- for(let chainId in tokenData.chains) {
344
- const chainData = tokenData.chains[chainId]!;
345
- this._tokens[chainId] ??= {};
346
- this._tokensByTicker[chainId] ??= {};
347
- this._tokens[chainId][chainData.address] = this._tokensByTicker[chainId][tokenData.ticker] = {
348
- chain: "SC",
349
- chainId,
350
- ticker: tokenData.ticker,
351
- name: tokenData.name,
352
- decimals: chainData.decimals,
353
- displayDecimals: chainData.displayDecimals,
354
- address: chainData.address,
355
- equals: (other: Token) => other.chainId===chainId && other.ticker===tokenData.ticker && other.address===chainData.address,
356
- toString: () => `${chainId}-${tokenData.ticker}`
357
- }
358
- }
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;
359
354
  }
360
355
 
361
356
  this.swapStateListener = (swap: ISwap) => {
@@ -388,6 +383,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
388
383
  {
389
384
  getRequestTimeout: this.options.getRequestTimeout,
390
385
  postRequestTimeout: this.options.postRequestTimeout,
386
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
391
387
  }
392
388
  );
393
389
  wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
@@ -403,6 +399,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
403
399
  {
404
400
  getRequestTimeout: this.options.getRequestTimeout,
405
401
  postRequestTimeout: this.options.postRequestTimeout,
402
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
406
403
  bitcoinNetwork: this._btcNetwork
407
404
  }
408
405
  );
@@ -419,6 +416,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
419
416
  {
420
417
  getRequestTimeout: this.options.getRequestTimeout,
421
418
  postRequestTimeout: this.options.postRequestTimeout,
419
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
422
420
  unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
423
421
  }
424
422
  );
@@ -437,6 +435,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
437
435
  {
438
436
  getRequestTimeout: this.options.getRequestTimeout,
439
437
  postRequestTimeout: this.options.postRequestTimeout,
438
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
440
439
  bitcoinNetwork: this._btcNetwork
441
440
  }
442
441
  );
@@ -449,7 +448,8 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
449
448
  this._tokens[chainId],
450
449
  {
451
450
  getRequestTimeout: this.options.getRequestTimeout,
452
- postRequestTimeout: this.options.postRequestTimeout
451
+ postRequestTimeout: this.options.postRequestTimeout,
452
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
453
453
  }
454
454
  );
455
455
  wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
@@ -463,6 +463,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
463
463
  {
464
464
  getRequestTimeout: this.options.getRequestTimeout,
465
465
  postRequestTimeout: this.options.postRequestTimeout,
466
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
466
467
  bitcoinNetwork: this._btcNetwork
467
468
  }
468
469
  );
@@ -483,6 +484,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
483
484
  {
484
485
  getRequestTimeout: this.options.getRequestTimeout,
485
486
  postRequestTimeout: this.options.postRequestTimeout,
487
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
486
488
  bitcoinNetwork: this._btcNetwork
487
489
  }
488
490
  );
@@ -503,6 +505,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
503
505
  {
504
506
  getRequestTimeout: this.options.getRequestTimeout,
505
507
  postRequestTimeout: this.options.postRequestTimeout,
508
+ saveUninitializedSwaps: this.options.saveUninitializedSwaps,
506
509
  unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
507
510
  }
508
511
  );
@@ -652,7 +655,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
652
655
  */
653
656
  async init(): Promise<void> {
654
657
  if(this.initialized) return;
655
- if(this.initPromise!=null) await this.initPromise;
658
+ if(this.initPromise!=null) {
659
+ await this.initPromise;
660
+ return;
661
+ }
656
662
 
657
663
  try {
658
664
  const promise = this._init();
@@ -849,10 +855,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
849
855
  if(swapLimitsChanged) this.emit("swapLimitsChanged");
850
856
 
851
857
  const quote = quotes[0].quote;
852
- if(this.options.saveUninitializedSwaps) {
853
- quote._setInitiated();
854
- await quote._save();
855
- }
858
+ await quote._save();
856
859
  return quote;
857
860
  } catch (e) {
858
861
  if(swapLimitsChanged) this.emit("swapLimitsChanged");
@@ -1327,13 +1330,15 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1327
1330
  * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1328
1331
  * @throws {Error} If no trusted intermediary specified
1329
1332
  */
1330
- 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]>> {
1331
1334
  if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
1332
1335
  if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
1333
1336
  recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1334
1337
  const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1335
1338
  if(useUrl==null) throw new Error("No trusted intermediary specified!");
1336
- 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;
1337
1342
  }
1338
1343
 
1339
1344
  /**
@@ -1346,7 +1351,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1346
1351
  * @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
1347
1352
  * @throws {Error} If no trusted intermediary specified
1348
1353
  */
1349
- createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1354
+ async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
1350
1355
  chainIdentifier: C, recipient: string,
1351
1356
  amount: bigint, refundAddress?: string,
1352
1357
  trustedIntermediaryOrUrl?: Intermediary | string
@@ -1356,7 +1361,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1356
1361
  recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
1357
1362
  const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
1358
1363
  if(useUrl==null) throw new Error("No trusted intermediary specified!");
1359
- 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;
1360
1367
  }
1361
1368
 
1362
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
  /**
@@ -229,9 +237,9 @@ export abstract class ISwap<
229
237
  //TODO: This doesn't hold strong reference to the swap, hence if no other strong reference to the
230
238
  // swap exists, it will just never resolve!
231
239
  return new Promise((resolve, reject) => {
232
- let listener: (swap: D["Swap"]) => void;
233
- listener = (swap) => {
234
- if(type==="eq" ? swap._state===targetState : type==="gte" ? swap._state>=targetState : swap._state!=targetState) {
240
+ let listener: () => void;
241
+ listener = () => {
242
+ if(type==="eq" ? this._state===targetState : type==="gte" ? this._state>=targetState : this._state!=targetState) {
235
243
  resolve();
236
244
  this.events.removeListener("swapState", listener);
237
245
  }
@@ -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);