@atomiqlabs/sdk 8.6.2 → 8.7.1

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 (85) hide show
  1. package/dist/events/UnifiedSwapEventListener.js +4 -2
  2. package/dist/http/paramcoders/ParamDecoder.js +9 -4
  3. package/dist/http/paramcoders/ParamEncoder.js +6 -1
  4. package/dist/intermediaries/Intermediary.d.ts +21 -0
  5. package/dist/intermediaries/Intermediary.js +25 -1
  6. package/dist/intermediaries/IntermediaryDiscovery.d.ts +15 -3
  7. package/dist/intermediaries/IntermediaryDiscovery.js +25 -6
  8. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +1 -0
  9. package/dist/swapper/Swapper.d.ts +9 -4
  10. package/dist/swapper/Swapper.js +94 -42
  11. package/dist/swapper/SwapperUtils.js +2 -1
  12. package/dist/swaps/ISwap.d.ts +5 -0
  13. package/dist/swaps/ISwap.js +4 -1
  14. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +5 -5
  15. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +4 -0
  16. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -3
  17. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +19 -6
  18. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +54 -21
  19. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +7 -3
  20. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -4
  21. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +3 -3
  22. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +8 -2
  23. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +12 -8
  24. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +18 -18
  25. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +12 -6
  26. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +38 -24
  27. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +9 -9
  28. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +14 -7
  29. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +54 -38
  30. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +5 -5
  31. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +18 -7
  32. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +61 -33
  33. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +12 -12
  34. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +8 -2
  35. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +13 -8
  36. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +1 -1
  37. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +13 -4
  38. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +44 -28
  39. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +2 -2
  40. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +8 -4
  41. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +29 -21
  42. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +1 -0
  43. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +13 -12
  44. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +21 -10
  45. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +136 -73
  46. package/dist/swaps/trusted/ln/LnForGasWrapper.js +2 -1
  47. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +2 -1
  48. package/dist/utils/RetryUtils.d.ts +2 -1
  49. package/dist/utils/RetryUtils.js +3 -2
  50. package/dist/utils/Utils.d.ts +9 -0
  51. package/dist/utils/Utils.js +15 -1
  52. package/package.json +2 -2
  53. package/src/events/UnifiedSwapEventListener.ts +4 -2
  54. package/src/http/paramcoders/ParamDecoder.ts +8 -4
  55. package/src/http/paramcoders/ParamEncoder.ts +5 -1
  56. package/src/intermediaries/Intermediary.ts +31 -1
  57. package/src/intermediaries/IntermediaryDiscovery.ts +33 -12
  58. package/src/intermediaries/apis/IntermediaryAPI.ts +2 -1
  59. package/src/swapper/Swapper.ts +141 -62
  60. package/src/swapper/SwapperUtils.ts +3 -1
  61. package/src/swaps/ISwap.ts +10 -2
  62. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +5 -5
  63. package/src/swaps/escrow_swaps/IEscrowSwap.ts +10 -3
  64. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +64 -26
  65. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +8 -5
  66. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +3 -3
  67. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +22 -12
  68. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +18 -18
  69. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +52 -31
  70. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +9 -9
  71. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +76 -52
  72. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +5 -5
  73. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +82 -38
  74. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +12 -12
  75. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +21 -9
  76. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +1 -1
  77. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +56 -33
  78. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +2 -2
  79. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +40 -22
  80. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +17 -13
  81. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +149 -83
  82. package/src/swaps/trusted/ln/LnForGasWrapper.ts +2 -1
  83. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +2 -1
  84. package/src/utils/RetryUtils.ts +11 -4
  85. package/src/utils/Utils.ts +14 -0
@@ -1,6 +1,6 @@
1
1
  import {Intermediary, ServicesType} from "./Intermediary";
2
2
  import {SwapType} from "../enums/SwapType";
3
- import {SwapContract} from "@atomiqlabs/base";
3
+ import {SpvVaultContract, SwapContract} from "@atomiqlabs/base";
4
4
  import {EventEmitter} from "events";
5
5
  import {Buffer} from "buffer";
6
6
  import {bigIntMax, bigIntMin, extendAbortController} from "../utils/Utils";
@@ -169,7 +169,7 @@ export class IntermediaryDiscovery extends EventEmitter {
169
169
  /**
170
170
  * Swap contracts for checking intermediary signatures
171
171
  */
172
- swapContracts: {[key: string]: SwapContract};
172
+ swapContracts: {[chainIdentifier: string]: {[contractVersion: string]: {swapContract: SwapContract, spvVaultContract: SpvVaultContract}}};
173
173
  /**
174
174
  * Registry URL used as a source for the list of intermediaries, this should be a link to a
175
175
  * github-hosted JSON file
@@ -193,7 +193,7 @@ export class IntermediaryDiscovery extends EventEmitter {
193
193
  private overrideNodeUrls?: string[];
194
194
 
195
195
  constructor(
196
- swapContracts: {[key: string]: SwapContract},
196
+ swapContracts: {[chainIdentifier: string]: {[contractVersion: string]: {swapContract: SwapContract, spvVaultContract: SpvVaultContract}}},
197
197
  registryUrl: string = REGISTRY_URL,
198
198
  nodeUrls?: string[],
199
199
  httpRequestTimeout?: number,
@@ -236,26 +236,39 @@ export class IntermediaryDiscovery extends EventEmitter {
236
236
  * @param url
237
237
  * @param abortSignal
238
238
  */
239
- private async getNodeInfo(url: string, abortSignal?: AbortSignal) : Promise<{addresses: {[key: string]: string}, info: InfoHandlerResponseEnvelope}> {
239
+ private async getNodeInfo(url: string, abortSignal?: AbortSignal) : Promise<{
240
+ addresses: {[chainIdentifier: string]: string},
241
+ contractVersions: {[chainIdentifier: string]: string},
242
+ info: InfoHandlerResponseEnvelope
243
+ }> {
240
244
  const response = await tryWithRetries(
241
245
  () => IntermediaryAPI.getIntermediaryInfo(url, this.httpRequestTimeout, abortSignal),
242
246
  {maxRetries: 3, delay: 100, exponential: true},
243
247
  undefined,
244
- abortSignal
248
+ abortSignal,
249
+ "debug"
245
250
  );
246
251
  abortSignal?.throwIfAborted();
247
252
 
248
253
  const promises: Promise<void>[] = [];
249
- const addresses: {[key: string]: string} = {};
254
+ const addresses: {[chainIdentifier: string]: string} = {};
255
+ const contractVersions: {[chainIdentifier: string]: string} = {};
250
256
  for(let chain in response.chains) {
251
257
  if(this.swapContracts[chain]!=null) {
258
+ const {signature, address, contractVersion} = response.chains[chain];
259
+ const _contractVersion = contractVersion ?? "v1";
260
+ const contract = this.swapContracts[chain][_contractVersion];
261
+ if(contract==null) {
262
+ logger.warn("getNodeInfo(): Unknown chain contract version "+_contractVersion+" for "+chain+" reported by intermediary: "+url);
263
+ continue;
264
+ }
252
265
  promises.push((async () => {
253
- const {signature, address} = response.chains[chain];
254
266
  try {
255
- await this.swapContracts[chain].isValidDataSignature(Buffer.from(response.envelope), signature, address);
267
+ await contract.swapContract.isValidDataSignature(Buffer.from(response.envelope), signature, address);
256
268
  addresses[chain] = address;
269
+ contractVersions[chain] = _contractVersion;
257
270
  } catch (e) {
258
- logger.warn("Failed to verify "+chain+" signature for intermediary: "+url);
271
+ logger.warn("getNodeInfo(): Failed to verify "+chain+" signature for intermediary: "+url);
259
272
  }
260
273
  })());
261
274
  }
@@ -284,6 +297,7 @@ export class IntermediaryDiscovery extends EventEmitter {
284
297
 
285
298
  return {
286
299
  addresses,
300
+ contractVersions,
287
301
  info
288
302
  };
289
303
  }
@@ -302,9 +316,10 @@ export class IntermediaryDiscovery extends EventEmitter {
302
316
  for(let key in nodeInfo.info.services) {
303
317
  services[swapHandlerTypeToSwapType(key as SwapHandlerType)] = nodeInfo.info.services[key as SwapHandlerType];
304
318
  }
305
- return new Intermediary(url, nodeInfo.addresses, services);
306
- } catch (e) {
307
- logger.warn("fetchIntermediaries(): Error contacting intermediary "+url+": ", e);
319
+ return new Intermediary(url, nodeInfo.addresses, services, undefined, nodeInfo.contractVersions);
320
+ } catch (e: any) {
321
+ logger.warn("fetchIntermediaries(): Intermediary "+url+` is unreachable due to ${e.name ?? e.message} error, skipping...`);
322
+ logger.debug("fetchIntermediaries(): Error contacting intermediary "+url+": ", e);
308
323
  return null;
309
324
  }
310
325
  }
@@ -474,6 +489,8 @@ export class IntermediaryDiscovery extends EventEmitter {
474
489
  /**
475
490
  * Returns swap candidates for a specific swap type & token address
476
491
  *
492
+ * @remark Also filters the LPs based on supported swap versions
493
+ *
477
494
  * @param chainIdentifier Chain identifier of the smart chain
478
495
  * @param swapType Swap protocol type
479
496
  * @param tokenAddress Token address
@@ -489,6 +506,10 @@ export class IntermediaryDiscovery extends EventEmitter {
489
506
  if(swapService.chainTokens==null) return false;
490
507
  if(swapService.chainTokens[chainIdentifier]==null) return false;
491
508
  if(!swapService.chainTokens[chainIdentifier].includes(tokenAddress.toString())) return false;
509
+ const contracts = this.swapContracts[chainIdentifier][e.getContractVersion(chainIdentifier) ?? "v1"];
510
+ if(contracts==null) return false;
511
+ if(swapType===SwapType.FROM_BTCLN_AUTO && !contracts.swapContract?.supportsInitWithoutClaimer) return false;
512
+ if(swapType===SwapType.SPV_VAULT_FROM_BTC && contracts.spvVaultContract==null) return false;
492
513
  return true;
493
514
  });
494
515
 
@@ -14,7 +14,8 @@ export type InfoHandlerResponse = {
14
14
  chains: {
15
15
  [chainIdentifier: string]: {
16
16
  address: string,
17
- signature: string
17
+ signature: string,
18
+ contractVersion?: string,
18
19
  }
19
20
  }
20
21
  };
@@ -6,7 +6,7 @@ import {
6
6
  ChainSwapType,
7
7
  ChainType, LightningNetworkApi,
8
8
  Messenger,
9
- RelaySynchronizer
9
+ RelaySynchronizer, SpvWithdrawalClaimedState, SpvWithdrawalFrontedState, SwapCommitState, SwapContract, SwapData
10
10
  } from "@atomiqlabs/base";
11
11
  import {
12
12
  ToBTCLNOptions,
@@ -190,14 +190,20 @@ type ChainSpecificData<T extends ChainType> = {
190
190
  [SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>
191
191
  }
192
192
  chainEvents: T["Events"],
193
- swapContract: T["Contract"],
194
- spvVaultContract: T["SpvVaultContract"],
195
193
  chainInterface: T["ChainInterface"],
196
- btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
197
- synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
198
194
  unifiedChainEvents: UnifiedSwapEventListener<T>,
199
195
  unifiedSwapStorage: UnifiedSwapStorage<T>,
200
- reviver: (val: any) => ISwap<T>
196
+ reviver: (val: any) => ISwap<T>,
197
+ defaultVersion: string,
198
+
199
+ versionedContracts: {
200
+ [contractVersion: string]: {
201
+ swapContract: T["Contract"],
202
+ spvVaultContract: T["SpvVaultContract"],
203
+ btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
204
+ synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
205
+ }
206
+ }
201
207
  };
202
208
 
203
209
  type MultiChainData<T extends MultiChain> = {
@@ -358,12 +364,37 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
358
364
  };
359
365
 
360
366
  this._chains = objectMap<CtorMultiChainData<T>, MultiChainData<T>>(chainsData, <InputKey extends keyof CtorMultiChainData<T>>(chainData: CtorMultiChainData<T>[InputKey], key: string) => {
361
- const {
362
- swapContract, chainEvents, btcRelay,
363
- chainInterface, spvVaultContract, spvVaultWithdrawalDataConstructor,
364
- chainId
367
+ let {
368
+ chainInterface, chainEvents, chainId,
369
+ btcRelay,
370
+ swapContract, swapDataConstructor,
371
+ spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor,
372
+ defaultVersion, versions
365
373
  } = chainData;
366
- const synchronizer = bitcoinSynchronizer(btcRelay);
374
+
375
+ defaultVersion ??= "v1";
376
+
377
+ if(versions==null) {
378
+ versions = {
379
+ [defaultVersion]: {
380
+ btcRelay,
381
+ swapContract,
382
+ swapDataConstructor,
383
+ spvVaultContract,
384
+ spvVaultDataConstructor,
385
+ spvVaultWithdrawalDataConstructor
386
+ }
387
+ }
388
+ }
389
+
390
+ const versionedContracts = objectMap(versions, (value, key) => {
391
+ return {
392
+ swapContract: value.swapContract,
393
+ spvVaultContract: value.spvVaultContract,
394
+ btcRelay: value.btcRelay,
395
+ synchronizer: bitcoinSynchronizer(value.btcRelay)
396
+ };
397
+ });
367
398
 
368
399
  const storageHandler = swapStorage(storagePrefix + chainId);
369
400
  const unifiedSwapStorage = new UnifiedSwapStorage<T[InputKey]>(storageHandler, this.options.noSwapCache);
@@ -376,10 +407,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
376
407
  unifiedSwapStorage,
377
408
  unifiedChainEvents,
378
409
  chainInterface,
379
- swapContract,
380
410
  pricing,
381
411
  this._tokens[chainId],
382
- chainData.swapDataConstructor,
412
+ versions,
383
413
  {
384
414
  getRequestTimeout: this.options.getRequestTimeout,
385
415
  postRequestTimeout: this.options.postRequestTimeout,
@@ -391,10 +421,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
391
421
  unifiedSwapStorage,
392
422
  unifiedChainEvents,
393
423
  chainInterface,
394
- swapContract,
395
424
  pricing,
396
425
  this._tokens[chainId],
397
- chainData.swapDataConstructor,
426
+ versions,
398
427
  this._bitcoinRpc,
399
428
  {
400
429
  getRequestTimeout: this.options.getRequestTimeout,
@@ -408,10 +437,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
408
437
  unifiedSwapStorage,
409
438
  unifiedChainEvents,
410
439
  chainInterface,
411
- swapContract,
412
440
  pricing,
413
441
  this._tokens[chainId],
414
- chainData.swapDataConstructor,
442
+ versions,
415
443
  lightningApi,
416
444
  {
417
445
  getRequestTimeout: this.options.getRequestTimeout,
@@ -425,12 +453,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
425
453
  unifiedSwapStorage,
426
454
  unifiedChainEvents,
427
455
  chainInterface,
428
- swapContract,
429
456
  pricing,
430
457
  this._tokens[chainId],
431
- chainData.swapDataConstructor,
432
- btcRelay,
433
- synchronizer,
458
+ versions,
459
+ versionedContracts,
434
460
  this._bitcoinRpc,
435
461
  {
436
462
  getRequestTimeout: this.options.getRequestTimeout,
@@ -468,18 +494,17 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
468
494
  }
469
495
  );
470
496
 
497
+ // This is gated on the default version of the contracts
471
498
  if(spvVaultContract!=null) {
472
499
  wrappers[SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper<T[InputKey]>(
473
500
  key,
474
501
  unifiedSwapStorage,
475
502
  unifiedChainEvents,
476
503
  chainInterface,
477
- spvVaultContract,
478
504
  pricing,
479
505
  this._tokens[chainId],
480
- spvVaultWithdrawalDataConstructor,
481
- btcRelay,
482
- synchronizer,
506
+ versions,
507
+ versionedContracts,
483
508
  bitcoinRpc,
484
509
  {
485
510
  getRequestTimeout: this.options.getRequestTimeout,
@@ -490,16 +515,16 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
490
515
  );
491
516
  }
492
517
 
518
+ // This is gated on the default version of the contracts
493
519
  if(swapContract.supportsInitWithoutClaimer) {
494
520
  wrappers[SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper<T[InputKey]>(
495
521
  key,
496
522
  unifiedSwapStorage,
497
523
  unifiedChainEvents,
498
524
  chainInterface,
499
- swapContract,
500
525
  pricing,
501
526
  this._tokens[chainId],
502
- chainData.swapDataConstructor,
527
+ versions,
503
528
  lightningApi,
504
529
  this.messenger,
505
530
  {
@@ -521,22 +546,22 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
521
546
 
522
547
  return {
523
548
  chainEvents,
524
- spvVaultContract,
525
- swapContract,
526
549
  chainInterface,
527
- btcRelay,
528
- synchronizer,
529
550
 
530
551
  wrappers,
531
552
 
532
553
  unifiedChainEvents,
533
554
  unifiedSwapStorage,
534
555
 
535
- reviver
556
+ defaultVersion,
557
+
558
+ reviver,
559
+
560
+ versionedContracts
536
561
  }
537
562
  });
538
563
 
539
- const contracts = objectMap(chainsData, (data) => data.swapContract);
564
+ const contracts = objectMap(chainsData, (data) => data.versions ?? {[data.defaultVersion ?? "v1"]: {swapContract: data.swapContract, spvVaultContract: data.spvVaultContract}});
540
565
  if(options.intermediaryUrl!=null) {
541
566
  this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
542
567
  } else {
@@ -553,7 +578,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
553
578
  }
554
579
 
555
580
  private async _init(): Promise<void> {
556
- this.logger.debug("init(): Initializing swapper, sdk-lib version 16.1.3");
581
+ this.logger.debug("init(): Initializing swapper...");
557
582
 
558
583
  const abortController = new AbortController();
559
584
 
@@ -594,14 +619,23 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
594
619
  for(let chainIdentifier in this._chains) {
595
620
  chainPromises.push((async() => {
596
621
  const {
597
- swapContract,
622
+ chainInterface,
623
+ versionedContracts,
598
624
  unifiedChainEvents,
599
625
  unifiedSwapStorage,
600
626
  wrappers,
601
627
  reviver
602
628
  } = this._chains[chainIdentifier];
603
- await swapContract.start();
604
- this.logger.debug("init(): Intialized swap contract: "+chainIdentifier);
629
+
630
+ const _chainInterface: any = chainInterface;
631
+ if(_chainInterface.verifyNetwork!=null) {
632
+ await _chainInterface.verifyNetwork(this.bitcoinNetwork);
633
+ }
634
+
635
+ for(let contractVersion in versionedContracts) {
636
+ await versionedContracts[contractVersion].swapContract.start();
637
+ this.logger.debug("init(): Intialized swap contract: "+chainIdentifier+` version: ${contractVersion}`);
638
+ }
605
639
 
606
640
  await unifiedSwapStorage.init();
607
641
  if(unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage) {
@@ -1865,18 +1899,56 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1865
1899
  * initiated after this blockheight
1866
1900
  */
1867
1901
  async recoverSwaps<C extends ChainIds<T>>(chainId: C, signer: string, startBlockheight?: number): Promise<ISwap<T[C]>[]> {
1868
- const {spvVaultContract, swapContract, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1902
+ //TODO: Recover swaps from all the known contract versions
1903
+ const {versionedContracts, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
1869
1904
 
1870
- if(
1871
- swapContract.getHistoricalSwaps==null ||
1872
- (spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
1873
- ) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
1905
+ const recoveredSwaps: ISwap<T[C]>[] = [];
1906
+ let someVersionSupportsRecovery = false;
1907
+ const recoveredEscrowStates: {
1908
+ [p: string]: {
1909
+ init?: {
1910
+ data: SwapData
1911
+ getInitTxId: () => Promise<string>
1912
+ getTxBlock: () => Promise<{
1913
+ blockTime: number
1914
+ blockHeight: number
1915
+ }>
1916
+ },
1917
+ state: SwapCommitState
1918
+ contractVersion: string
1919
+ }
1920
+ } = {};
1921
+ const recoveredSpvStates: {
1922
+ [contractVersion: string]: {
1923
+ [escrowHash: string]: SpvWithdrawalClaimedState | SpvWithdrawalFrontedState
1924
+ }
1925
+ } = {};
1926
+
1927
+ for(let contractVersion in versionedContracts) {
1928
+ const {swapContract, spvVaultContract} = versionedContracts[contractVersion];
1929
+
1930
+ if(
1931
+ swapContract.getHistoricalSwaps==null ||
1932
+ (spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
1933
+ ) {
1934
+ this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
1935
+ continue;
1936
+ }
1874
1937
 
1875
- const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
1876
- const spvVaultData = await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
1938
+ someVersionSupportsRecovery = true;
1877
1939
 
1878
- const escrowHashes = Object.keys(swaps);
1879
- if(spvVaultData!=null) Object.keys(spvVaultData.withdrawals).forEach(btcTxId => escrowHashes.push(btcTxId));
1940
+ const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
1941
+ const spvVaultData = wrappers[SwapType.SPV_VAULT_FROM_BTC]==null
1942
+ ? undefined
1943
+ : await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
1944
+
1945
+ for(let key in swaps) recoveredEscrowStates[key] = {...swaps[key], contractVersion};
1946
+ if(spvVaultData!=null) for(let key in spvVaultData.withdrawals) (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
1947
+ }
1948
+ if(!someVersionSupportsRecovery) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
1949
+
1950
+ const escrowHashes = Object.keys(recoveredEscrowStates);
1951
+ for(let contractVersion in recoveredSpvStates) Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
1880
1952
  this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
1881
1953
  this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
1882
1954
  const knownSwapsArray = await unifiedSwapStorage.query([[{key: "escrowHash", value: escrowHashes}]], reviver);
@@ -1887,11 +1959,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1887
1959
  });
1888
1960
  this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
1889
1961
 
1890
- const recoveredSwaps: ISwap<T[C]>[] = [];
1891
-
1892
- for(let escrowHash in swaps) {
1893
- const {init, state} = swaps[escrowHash];
1962
+ for(let escrowHash in recoveredEscrowStates) {
1963
+ const {init, state, contractVersion} = recoveredEscrowStates[escrowHash];
1894
1964
  const knownSwap = knownSwaps[escrowHash];
1965
+ const { swapContract } = versionedContracts[contractVersion];
1895
1966
 
1896
1967
  if(knownSwap==null) {
1897
1968
  if(init==null) {
@@ -1900,6 +1971,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1900
1971
  }
1901
1972
  } else if(knownSwap instanceof IEscrowSwap) {
1902
1973
  this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
1974
+ if((knownSwap._contractVersion ?? "v1")!==contractVersion) {
1975
+ this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
1976
+ continue;
1977
+ }
1903
1978
  if(await knownSwap._forciblySetOnchainState(state)) {
1904
1979
  await knownSwap._save();
1905
1980
  }
@@ -1919,27 +1994,27 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1919
1994
  //To BTCLN
1920
1995
  typeIdentified = true;
1921
1996
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
1922
- swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, lp);
1997
+ swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1923
1998
  } else if(data.isClaimer(signer)) {
1924
1999
  //From BTCLN
1925
2000
  typeIdentified = true;
1926
2001
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
1927
- if(this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO)) {
1928
- swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, lp);
2002
+ if(swapContract.supportsInitWithoutClaimer && wrappers[SwapType.FROM_BTCLN_AUTO]!=null) {
2003
+ swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1929
2004
  } else {
1930
- swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, lp);
2005
+ swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1931
2006
  }
1932
2007
  }
1933
2008
  } else if(data.getType()===ChainSwapType.CHAIN_NONCED) {
1934
2009
  //To BTC
1935
2010
  typeIdentified = true;
1936
2011
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
1937
- swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, lp);
2012
+ swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1938
2013
  } else if(data.getType()===ChainSwapType.CHAIN) {
1939
2014
  //From BTC
1940
2015
  typeIdentified = true;
1941
2016
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
1942
- swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, lp);
2017
+ swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1943
2018
  }
1944
2019
 
1945
2020
  if(swap!=null) {
@@ -1949,17 +2024,20 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1949
2024
  }
1950
2025
  }
1951
2026
 
1952
- if(spvVaultContract!=null && spvVaultData!=null) {
2027
+ for(let contractVersion in recoveredSpvStates) {
2028
+ const { spvVaultContract } = versionedContracts[contractVersion];
2029
+ const spvVaultData = recoveredSpvStates[contractVersion];
2030
+
1953
2031
  const vaultsData = await spvVaultContract.getMultipleVaultData(
1954
- Object.keys(spvVaultData.withdrawals)
2032
+ Object.keys(spvVaultData)
1955
2033
  .map(btcTxId => ({
1956
- owner: spvVaultData.withdrawals[btcTxId].owner,
1957
- vaultId: spvVaultData.withdrawals[btcTxId].vaultId
2034
+ owner: spvVaultData[btcTxId].owner,
2035
+ vaultId: spvVaultData[btcTxId].vaultId
1958
2036
  }))
1959
2037
  );
1960
2038
 
1961
- for(let btcTxId in spvVaultData.withdrawals) {
1962
- const state = spvVaultData.withdrawals[btcTxId];
2039
+ for(let btcTxId in spvVaultData) {
2040
+ const state = spvVaultData[btcTxId];
1963
2041
  const knownSwap = knownSwaps[btcTxId];
1964
2042
 
1965
2043
  if(knownSwap!=null) {
@@ -1981,6 +2059,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
1981
2059
  );
1982
2060
  const swap = await wrappers[SwapType.SPV_VAULT_FROM_BTC].recoverFromState(
1983
2061
  state,
2062
+ contractVersion,
1984
2063
  vaultsData[state.owner]?.[state.vaultId.toString(10)],
1985
2064
  lp
1986
2065
  );
@@ -404,7 +404,9 @@ export class SwapperUtils<T extends MultiChain> {
404
404
  feeRate?: any
405
405
  }): Promise<TokenAmount> {
406
406
  if(this.root._chains[token.chainId]==null) throw new Error("Invalid chain identifier! Unknown chain: "+token.chainId);
407
- const {swapContract, chainInterface} = this.root._chains[token.chainId];
407
+ const {defaultVersion, versionedContracts, chainInterface} = this.root._chains[token.chainId];
408
+
409
+ const {swapContract} = versionedContracts[defaultVersion];
408
410
 
409
411
  let signer: string;
410
412
  if(typeof(wallet)==="string") {
@@ -25,7 +25,8 @@ export type ISwapInit = {
25
25
  expiry: number,
26
26
  swapFee: bigint,
27
27
  swapFeeBtc: bigint,
28
- exactIn: boolean
28
+ exactIn: boolean,
29
+ contractVersion: string
29
30
  };
30
31
 
31
32
  /**
@@ -140,6 +141,10 @@ export abstract class ISwap<
140
141
  * @internal
141
142
  */
142
143
  _persisted: boolean = false;
144
+ /**
145
+ * @internal
146
+ */
147
+ _contractVersion?: string;
143
148
 
144
149
 
145
150
  /**
@@ -181,6 +186,7 @@ export abstract class ISwap<
181
186
  this.version = this.currentVersion;
182
187
  this.createdAt = Date.now();
183
188
  this._randomNonce = randomBytes(16).toString("hex");
189
+ this._contractVersion = swapInitOrObj.contractVersion;
184
190
  } else {
185
191
  this.expiry = swapInitOrObj.expiry;
186
192
  this.url = swapInitOrObj.url;
@@ -211,6 +217,7 @@ export abstract class ISwap<
211
217
  this.createdAt = swapInitOrObj.createdAt ?? swapInitOrObj.expiry;
212
218
 
213
219
  this._randomNonce = swapInitOrObj.randomNonce;
220
+ this._contractVersion = swapInitOrObj.contractVersion;
214
221
  }
215
222
  if(this.version!==this.currentVersion) {
216
223
  this.upgradeVersion();
@@ -637,7 +644,8 @@ export abstract class ISwap<
637
644
  initiated: this.initiated,
638
645
  exactIn: this.exactIn,
639
646
  createdAt: this.createdAt,
640
- randomNonce: this._randomNonce
647
+ randomNonce: this._randomNonce,
648
+ contractVersion: this._contractVersion
641
649
  }
642
650
  }
643
651
 
@@ -89,7 +89,7 @@ export abstract class IEscrowSelfInitSwap<
89
89
  while(!expired) {
90
90
  await timeoutPromise(intervalSeconds*1000, abortSignal);
91
91
  try {
92
- expired = await this.wrapper._contract.isInitAuthorizationExpired(this._data, this.signatureData);
92
+ expired = await this._contract.isInitAuthorizationExpired(this._data, this.signatureData);
93
93
  } catch (e) {
94
94
  this.logger.error("watchdogWaitTillSignatureExpiry(): Error when checking signature expiry: ", e);
95
95
  }
@@ -106,14 +106,14 @@ export abstract class IEscrowSelfInitSwap<
106
106
  * @internal
107
107
  */
108
108
  protected getCommitFee(): Promise<bigint> {
109
- return this.wrapper._contract.getCommitFee(this._getInitiator(), this.getSwapData(), this.feeRate);
109
+ return this._contract.getCommitFee(this._getInitiator(), this.getSwapData(), this.feeRate);
110
110
  }
111
111
 
112
112
  /**
113
113
  * Returns the transaction fee paid on the smart chain side to initiate the escrow
114
114
  */
115
115
  async getSmartChainNetworkFee(): Promise<TokenAmount<SCToken<T["ChainId"]>, true>> {
116
- const swapContract: T["Contract"] = this.wrapper._contract;
116
+ const swapContract: T["Contract"] = this._contract;
117
117
  return toTokenAmount(
118
118
  await (
119
119
  swapContract.getRawCommitFee!=null ?
@@ -178,7 +178,7 @@ export abstract class IEscrowSelfInitSwap<
178
178
  async _verifyQuoteDefinitelyExpired(): Promise<boolean> {
179
179
  if(this._data==null || this.signatureData==null) throw new Error("data or signature data are null!");
180
180
 
181
- return this.wrapper._contract.isInitAuthorizationExpired(
181
+ return this._contract.isInitAuthorizationExpired(
182
182
  this._data!, this.signatureData!
183
183
  );
184
184
  }
@@ -190,7 +190,7 @@ export abstract class IEscrowSelfInitSwap<
190
190
  if(this._data==null || this.signatureData==null) throw new Error("data or signature data are null!");
191
191
 
192
192
  try {
193
- await this.wrapper._contract.isValidInitAuthorization(
193
+ await this._contract.isValidInitAuthorization(
194
194
  this._getInitiator(), this._data!, this.signatureData!, this.feeRate
195
195
  );
196
196
  return true;
@@ -50,6 +50,11 @@ export abstract class IEscrowSwap<
50
50
  */
51
51
  _claimTxId?: string;
52
52
 
53
+ /**
54
+ * @internal
55
+ */
56
+ protected _contract: T["Contract"];
57
+
53
58
  protected constructor(wrapper: D["Wrapper"], obj: any);
54
59
  protected constructor(wrapper: D["Wrapper"], swapInit: IEscrowSwapInit<T["Data"]>);
55
60
  protected constructor(
@@ -61,12 +66,14 @@ export abstract class IEscrowSwap<
61
66
  if(isIEscrowSwapInit(swapInitOrObj)) {
62
67
  this._data = swapInitOrObj.data;
63
68
  } else {
64
- if(swapInitOrObj.data!=null) this._data = new wrapper._swapDataDeserializer(swapInitOrObj.data);
69
+ if(swapInitOrObj.data!=null) this._data = new (wrapper._swapDataDeserializer(this._contractVersion))(swapInitOrObj.data);
65
70
 
66
71
  this._commitTxId = swapInitOrObj.commitTxId;
67
72
  this._claimTxId = swapInitOrObj.claimTxId;
68
73
  this._refundTxId = swapInitOrObj.refundTxId;
69
74
  }
75
+
76
+ this._contract = wrapper._contract(this._contractVersion);
70
77
  }
71
78
 
72
79
  /**
@@ -170,7 +177,7 @@ export abstract class IEscrowSwap<
170
177
  while(status?.type===SwapCommitStateType.NOT_COMMITED) {
171
178
  await timeoutPromise(intervalSeconds*1000, abortSignal);
172
179
  try {
173
- status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
180
+ status = await this._contract.getCommitStatus(this._getInitiator(), this._data);
174
181
  if(
175
182
  status?.type===SwapCommitStateType.NOT_COMMITED &&
176
183
  await this._verifyQuoteDefinitelyExpired()
@@ -200,7 +207,7 @@ export abstract class IEscrowSwap<
200
207
  while(status?.type===SwapCommitStateType.COMMITED || status?.type===SwapCommitStateType.REFUNDABLE) {
201
208
  await timeoutPromise(intervalSeconds*1000, abortSignal);
202
209
  try {
203
- status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
210
+ status = await this._contract.getCommitStatus(this._getInitiator(), this._data);
204
211
  } catch (e) {
205
212
  this.logger.error("watchdogWaitTillResult(): Error when fetching commit status: ", e);
206
213
  }