@atomiqlabs/sdk 8.6.3 → 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 (78) hide show
  1. package/dist/events/UnifiedSwapEventListener.js +4 -2
  2. package/dist/intermediaries/Intermediary.d.ts +21 -0
  3. package/dist/intermediaries/Intermediary.js +25 -1
  4. package/dist/intermediaries/IntermediaryDiscovery.d.ts +15 -3
  5. package/dist/intermediaries/IntermediaryDiscovery.js +21 -3
  6. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +1 -0
  7. package/dist/swapper/Swapper.d.ts +9 -4
  8. package/dist/swapper/Swapper.js +89 -41
  9. package/dist/swapper/SwapperUtils.js +2 -1
  10. package/dist/swaps/ISwap.d.ts +5 -0
  11. package/dist/swaps/ISwap.js +4 -1
  12. package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +5 -5
  13. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +4 -0
  14. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -3
  15. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +19 -6
  16. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +54 -21
  17. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +7 -3
  18. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -4
  19. package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +3 -3
  20. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +8 -2
  21. package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +12 -8
  22. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +18 -18
  23. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +12 -6
  24. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +38 -24
  25. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +9 -9
  26. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +14 -7
  27. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +54 -38
  28. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +5 -5
  29. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +18 -7
  30. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +61 -33
  31. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +12 -12
  32. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +8 -2
  33. package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +13 -8
  34. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +1 -1
  35. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +13 -4
  36. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +44 -28
  37. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +2 -2
  38. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +8 -4
  39. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +29 -21
  40. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +1 -0
  41. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +13 -12
  42. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +21 -10
  43. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +136 -73
  44. package/dist/swaps/trusted/ln/LnForGasWrapper.js +2 -1
  45. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +2 -1
  46. package/dist/utils/Utils.d.ts +9 -0
  47. package/dist/utils/Utils.js +15 -1
  48. package/package.json +2 -2
  49. package/src/events/UnifiedSwapEventListener.ts +4 -2
  50. package/src/intermediaries/Intermediary.ts +31 -1
  51. package/src/intermediaries/IntermediaryDiscovery.ts +27 -8
  52. package/src/intermediaries/apis/IntermediaryAPI.ts +2 -1
  53. package/src/swapper/Swapper.ts +133 -61
  54. package/src/swapper/SwapperUtils.ts +3 -1
  55. package/src/swaps/ISwap.ts +10 -2
  56. package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +5 -5
  57. package/src/swaps/escrow_swaps/IEscrowSwap.ts +10 -3
  58. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +64 -26
  59. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +8 -5
  60. package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +3 -3
  61. package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +22 -12
  62. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +18 -18
  63. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +52 -31
  64. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +9 -9
  65. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +76 -52
  66. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +5 -5
  67. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +82 -38
  68. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +12 -12
  69. package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +21 -9
  70. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +1 -1
  71. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +56 -33
  72. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +2 -2
  73. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +40 -22
  74. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +17 -13
  75. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +149 -83
  76. package/src/swaps/trusted/ln/LnForGasWrapper.ts +2 -1
  77. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +2 -1
  78. package/src/utils/Utils.ts +14 -0
@@ -9,7 +9,7 @@ import {ISwapPrice} from "../../../../prices/abstract/ISwapPrice";
9
9
  import {EventEmitter} from "events";
10
10
  import {IntermediaryError} from "../../../../errors/IntermediaryError";
11
11
  import {SwapType} from "../../../../enums/SwapType";
12
- import {extendAbortController, throwIfUndefined} from "../../../../utils/Utils";
12
+ import {extendAbortController, mapArrayToObject, throwIfUndefined} from "../../../../utils/Utils";
13
13
  import {IntermediaryAPI, ToBTCLNResponseType} from "../../../../intermediaries/apis/IntermediaryAPI";
14
14
  import {RequestError} from "../../../../errors/RequestError";
15
15
  import {LNURL, LNURLPaySuccessAction} from "../../../../lnurl/LNURL";
@@ -102,21 +102,26 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
102
102
  unifiedStorage: UnifiedSwapStorage<T>,
103
103
  unifiedChainEvents: UnifiedSwapEventListener<T>,
104
104
  chain: T["ChainInterface"],
105
- contract: T["Contract"],
106
105
  prices: ISwapPrice,
107
106
  tokens: WrapperCtorTokens,
108
- swapDataDeserializer: new (data: any) => T["Data"],
107
+ versionedContracts: {
108
+ [version: string]: {
109
+ swapContract: T["Contract"],
110
+ swapDataConstructor: new (data: any) => T["Data"]
111
+ }
112
+ },
109
113
  options?: AllOptional<ToBTCLNWrapperOptions>,
110
114
  events?: EventEmitter<{swapState: [ISwap]}>
111
115
  ) {
112
116
  super(
113
- chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
117
+ chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens,
114
118
  {
115
119
  ...options,
116
120
  paymentTimeoutSeconds: options?.paymentTimeoutSeconds ?? 5*24*60*60,
117
121
  lightningBaseFee: options?.lightningBaseFee ?? 10,
118
122
  lightningFeePPM: options?.lightningFeePPM ?? 2000
119
123
  },
124
+ versionedContracts,
120
125
  events
121
126
  );
122
127
  }
@@ -221,7 +226,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
221
226
  throw new IntermediaryError("Invalid data returned - total amount");
222
227
 
223
228
  if(parsedPr.tagsObject.payment_hash==null) throw new Error("Swap invoice doesn't contain payment hash field!");
224
- const claimHash = this._contract.getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash, "hex"));
229
+ const claimHash = this._contract(lp.getContractVersion(this.chainIdentifier)).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash, "hex"));
225
230
 
226
231
  if(
227
232
  data.getAmount() !== resp.total ||
@@ -265,18 +270,19 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
265
270
  expiryTimestamp: bigint
266
271
  },
267
272
  preFetches: {
268
- feeRatePromise: Promise<string | undefined>,
273
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
269
274
  pricePreFetchPromise: Promise<bigint | undefined>,
270
275
  usdPricePrefetchPromise: Promise<number | undefined>,
271
- signDataPrefetchPromise?: Promise<T["PreFetchVerification"] | undefined>
276
+ signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
272
277
  },
273
278
  abort: AbortSignal | AbortController,
274
279
  additionalParams?: Record<string, any>,
275
280
  ) {
276
281
  if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
282
+ const version = lp.getContractVersion(this.chainIdentifier);
277
283
 
278
284
  const abortController = abort instanceof AbortController ? abort : extendAbortController(abort);
279
- const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController);
285
+ const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
280
286
 
281
287
  try {
282
288
  const {signDataPromise, resp} = await tryWithRetries(async(retryCount: number) => {
@@ -286,13 +292,13 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
286
292
  maxFee: await calculatedOptions.maxFee,
287
293
  expiryTimestamp: calculatedOptions.expiryTimestamp,
288
294
  token: amountData.token,
289
- feeRate: throwIfUndefined(preFetches.feeRatePromise),
295
+ feeRate: throwIfUndefined(preFetches.feeRatePromise[version]),
290
296
  additionalParams
291
297
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
292
298
 
293
- let signDataPromise = preFetches.signDataPrefetchPromise;
299
+ let signDataPromise = preFetches.signDataPrefetchPromise?.[version];
294
300
  if(signDataPromise==null) {
295
- signDataPromise = this.preFetchSignData(signDataPrefetch);
301
+ signDataPromise = this.preFetchSignData(signDataPrefetch, version);
296
302
  } else signDataPrefetch.catch(() => {});
297
303
 
298
304
  return {
@@ -304,7 +310,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
304
310
  if(parsedPr.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
305
311
  const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
306
312
  const totalFee: bigint = resp.swapFee + resp.maxFee;
307
- const data: T["Data"] = new this._swapDataDeserializer(resp.data);
313
+ const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
308
314
  data.setOfferer(signer);
309
315
 
310
316
  await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
@@ -316,7 +322,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
316
322
  preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal
317
323
  ),
318
324
  this.verifyReturnedSignature(
319
- signer, data, resp, preFetches.feeRatePromise, signDataPromise, abortController.signal
325
+ signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
320
326
  ),
321
327
  reputationPromise
322
328
  ]);
@@ -332,14 +338,15 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
332
338
  expiry: signatureExpiry,
333
339
  swapFee: resp.swapFee,
334
340
  swapFeeBtc,
335
- feeRate: (await preFetches.feeRatePromise)!,
341
+ feeRate: (await preFetches.feeRatePromise[version])!,
336
342
  signatureData: resp,
337
343
  data,
338
344
  networkFee: resp.maxFee,
339
345
  networkFeeBtc: resp.routingFeeSats,
340
346
  confidence: resp.confidence,
341
347
  pr,
342
- exactIn: false
348
+ exactIn: false,
349
+ contractVersion: version
343
350
  } as IToBTCSwapInit<T["Data"]>);
344
351
  return quote;
345
352
  } catch (e) {
@@ -370,10 +377,10 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
370
377
  additionalParams?: Record<string, any>,
371
378
  abortSignal?: AbortSignal,
372
379
  preFetches?: {
373
- feeRatePromise: Promise<string | undefined>,
380
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
374
381
  pricePreFetchPromise: Promise<bigint | undefined>,
375
382
  usdPricePrefetchPromise: Promise<number | undefined>,
376
- signDataPrefetchPromise?: Promise<T["PreFetchVerification"] | undefined>
383
+ signDataPrefetchPromise?: {[contractVersion: string]: Promise<T["PreFetchVerification"] | undefined> | undefined}
377
384
  }
378
385
  ): Promise<{
379
386
  quote: Promise<ToBTCLNSwap<T>>,
@@ -383,19 +390,27 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
383
390
  if(parsedPr.millisatoshis==null) throw new UserError("Must be an invoice with amount");
384
391
  const amountOut: bigint = (BigInt(parsedPr.millisatoshis) + 999n) / 1000n;
385
392
 
393
+ const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
394
+
386
395
  const _options = this.toRequiredSwapOptions({...amountData, amount: amountOut}, options);
387
396
 
388
397
  if(parsedPr.tagsObject.payment_hash==null) throw new Error("Provided lightning invoice doesn't contain payment hash field!");
389
398
  await this.checkPaymentHashWasPaid(parsedPr.tagsObject.payment_hash);
390
399
 
391
- const claimHash = this._contract.getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash, "hex"));
400
+ const _hash = mapArrayToObject(lpVersions, (contractVersion: string) => {
401
+ return this._contract(contractVersion).getHashForHtlc(Buffer.from(parsedPr.tagsObject.payment_hash!, "hex")).toString("hex");
402
+ });
392
403
 
393
404
  const _abortController = extendAbortController(abortSignal);
394
405
  const _preFetches = preFetches ?? {
395
406
  pricePreFetchPromise: this.preFetchPrice(amountData, _abortController.signal),
396
- feeRatePromise: this.preFetchFeeRate(signer, amountData, claimHash.toString("hex"), _abortController),
407
+ feeRatePromise: this.preFetchFeeRate(signer, amountData, _hash, _abortController, lpVersions),
397
408
  usdPricePrefetchPromise: this.preFetchUsdPrice(_abortController.signal),
398
- signDataPrefetchPromise: this._contract.preFetchBlockDataForSignatures==null ? this.preFetchSignData(Promise.resolve(true)) : undefined
409
+ signDataPrefetchPromise: mapArrayToObject(lpVersions, (contractVersion: string) => {
410
+ return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
411
+ this.preFetchSignData(Promise.resolve(true), contractVersion) :
412
+ undefined;
413
+ })
399
414
  };
400
415
 
401
416
  return lps.map(lp => {
@@ -451,7 +466,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
451
466
  expiryTimestamp: bigint
452
467
  },
453
468
  preFetches: {
454
- feeRatePromise: Promise<string | undefined>,
469
+ feeRatePromise: {[contractVersion: string]: Promise<string | undefined>},
455
470
  pricePreFetchPromise: Promise<bigint | undefined>,
456
471
  usdPricePrefetchPromise: Promise<number | undefined>
457
472
  },
@@ -459,9 +474,10 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
459
474
  additionalParams?: Record<string, any>,
460
475
  ) {
461
476
  if(lp.services[SwapType.TO_BTCLN]==null) throw new Error("LP service for processing to btcln swaps not found!");
477
+ const version = lp.getContractVersion(this.chainIdentifier);
462
478
 
463
479
  const abortController = extendAbortController(abortSignal);
464
- const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController);
480
+ const reputationPromise = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
465
481
 
466
482
  try {
467
483
  const {signDataPromise, prepareResp} = await tryWithRetries(async(retryCount: number) => {
@@ -476,7 +492,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
476
492
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
477
493
 
478
494
  return {
479
- signDataPromise: this.preFetchSignData(signDataPrefetch),
495
+ signDataPromise: this.preFetchSignData(signDataPrefetch, version),
480
496
  prepareResp: await response
481
497
  };
482
498
  }, undefined, e => e instanceof RequestError, abortController.signal);
@@ -498,7 +514,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
498
514
  (retryCount: number) => IntermediaryAPI.initToBTCLNExactIn(lp.url, {
499
515
  pr: invoice,
500
516
  reqId: prepareResp.reqId,
501
- feeRate: throwIfUndefined(preFetches.feeRatePromise),
517
+ feeRate: throwIfUndefined(preFetches.feeRatePromise[version]),
502
518
  additionalParams
503
519
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined),
504
520
  undefined, RequestError, abortController.signal
@@ -507,7 +523,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
507
523
  if(parsedInvoice.millisatoshis==null) throw new Error("Swap invoice doesn't have msat amount field!");
508
524
  const amountOut: bigint = (BigInt(parsedInvoice.millisatoshis) + 999n) / 1000n;
509
525
  const totalFee: bigint = resp.swapFee + resp.maxFee;
510
- const data: T["Data"] = new this._swapDataDeserializer(resp.data);
526
+ const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
511
527
  data.setOfferer(signer);
512
528
 
513
529
  await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
@@ -519,7 +535,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
519
535
  preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal
520
536
  ),
521
537
  this.verifyReturnedSignature(
522
- signer, data, resp, preFetches.feeRatePromise, signDataPromise, abortController.signal
538
+ signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal
523
539
  ),
524
540
  reputationPromise
525
541
  ]);
@@ -535,14 +551,15 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
535
551
  expiry: signatureExpiry,
536
552
  swapFee: resp.swapFee,
537
553
  swapFeeBtc,
538
- feeRate: (await preFetches.feeRatePromise)!,
554
+ feeRate: (await preFetches.feeRatePromise[version])!,
539
555
  signatureData: resp,
540
556
  data,
541
557
  networkFee: resp.maxFee,
542
558
  networkFeeBtc: resp.routingFeeSats,
543
559
  confidence: resp.confidence,
544
560
  pr: invoice,
545
- exactIn: true
561
+ exactIn: true,
562
+ contractVersion: version
546
563
  } as IToBTCSwapInit<T["Data"]>);
547
564
  return quote;
548
565
  } catch (e) {
@@ -578,13 +595,17 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
578
595
  }[]> {
579
596
  if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
580
597
 
598
+ const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
599
+
581
600
  const _abortController = extendAbortController(abortSignal);
582
601
  const pricePreFetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
583
602
  const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
584
- const feeRatePromise: Promise<string | undefined> = this.preFetchFeeRate(signer, amountData, undefined, _abortController);
585
- const signDataPrefetchPromise: Promise<T["PreFetchVerification"] | undefined> | undefined = this._contract.preFetchBlockDataForSignatures==null ?
586
- this.preFetchSignData(Promise.resolve(true)) :
587
- undefined;
603
+ const feeRatePromise = this.preFetchFeeRate(signer, amountData, undefined, _abortController, lpVersions);
604
+ const signDataPrefetchPromise = mapArrayToObject(lpVersions, (contractVersion: string) => {
605
+ return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
606
+ this.preFetchSignData(Promise.resolve(true), contractVersion) :
607
+ undefined;
608
+ });
588
609
 
589
610
  const _options = this.toRequiredSwapOptions(amountData, options, pricePreFetchPromise, _abortController.signal);
590
611
 
@@ -716,6 +737,7 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
716
737
  async recoverFromSwapDataAndState(
717
738
  init: {data: T["Data"], getInitTxId: () => Promise<string>, getTxBlock: () => Promise<{blockTime: number, blockHeight: number}>},
718
739
  state: SwapCommitState,
740
+ contractVersion: string,
719
741
  lp?: Intermediary
720
742
  ): Promise<ToBTCLNSwap<T> | null> {
721
743
  const data = init.data;
@@ -747,7 +769,8 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
747
769
  networkFeeBtc: 0n,
748
770
  confidence: 0,
749
771
  pr: paymentHash ?? undefined,
750
- exactIn: false
772
+ exactIn: false,
773
+ contractVersion
751
774
  };
752
775
  const swap = new ToBTCLNSwap(this, swapInit);
753
776
  swap._commitTxId = await init.getInitTxId();
@@ -102,7 +102,7 @@ export class ToBTCSwap<T extends ChainType = ChainType> extends IToBTCSwap<T, To
102
102
 
103
103
  const foundVout = btcTx.outs.find(vout => {
104
104
  if(requiredConfirmations!=null) {
105
- return this._data.getClaimHash()===this.wrapper._contract.getHashForOnchain(
105
+ return this._data.getClaimHash()===this._contract.getHashForOnchain(
106
106
  Buffer.from(vout.scriptPubKey.hex, "hex"),
107
107
  BigInt(vout.value),
108
108
  requiredConfirmations,
@@ -111,7 +111,7 @@ export class ToBTCSwap<T extends ChainType = ChainType> extends IToBTCSwap<T, To
111
111
  } else {
112
112
  for(let i=1;i<=20;i++) {
113
113
  if(
114
- this._data.getClaimHash()===this.wrapper._contract.getHashForOnchain(
114
+ this._data.getClaimHash()===this._contract.getHashForOnchain(
115
115
  Buffer.from(vout.scriptPubKey.hex, "hex"),
116
116
  BigInt(vout.value),
117
117
  i,
@@ -15,7 +15,12 @@ import {Buffer} from "buffer";
15
15
  import {UserError} from "../../../../errors/UserError";
16
16
  import {IntermediaryError} from "../../../../errors/IntermediaryError";
17
17
  import {SwapType} from "../../../../enums/SwapType";
18
- import {extendAbortController, randomBytes, throwIfUndefined} from "../../../../utils/Utils";
18
+ import {
19
+ extendAbortController,
20
+ mapArrayToObject,
21
+ randomBytes,
22
+ throwIfUndefined
23
+ } from "../../../../utils/Utils";
19
24
  import {toOutputScript} from "../../../../utils/BitcoinUtils";
20
25
  import {IntermediaryAPI, ToBTCResponseType} from "../../../../intermediaries/apis/IntermediaryAPI";
21
26
  import {RequestError} from "../../../../errors/RequestError";
@@ -73,10 +78,9 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
73
78
  * @param unifiedStorage Storage interface for the current environment
74
79
  * @param unifiedChainEvents Smart chain on-chain event listener
75
80
  * @param chain
76
- * @param contract Chain specific swap contract
81
+ * @param versionedContracts Chain specific versioned contracts
77
82
  * @param prices Swap pricing handler
78
83
  * @param tokens
79
- * @param swapDataDeserializer Deserializer for chain specific SwapData
80
84
  * @param btcRpc Bitcoin RPC api
81
85
  * @param options
82
86
  * @param events Instance to use for emitting events
@@ -86,16 +90,20 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
86
90
  unifiedStorage: UnifiedSwapStorage<T>,
87
91
  unifiedChainEvents: UnifiedSwapEventListener<T>,
88
92
  chain: T["ChainInterface"],
89
- contract: T["Contract"],
90
93
  prices: ISwapPrice,
91
94
  tokens: WrapperCtorTokens,
92
- swapDataDeserializer: new (data: any) => T["Data"],
95
+ versionedContracts: {
96
+ [version: string]: {
97
+ swapContract: T["Contract"],
98
+ swapDataConstructor: new (data: any) => T["Data"]
99
+ }
100
+ },
93
101
  btcRpc: BitcoinRpc<any>,
94
102
  options?: AllOptional<ToBTCWrapperOptions>,
95
103
  events?: EventEmitter<{swapState: [ISwap]}>
96
104
  ) {
97
105
  super(
98
- chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer,
106
+ chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens,
99
107
  {
100
108
  ...options,
101
109
  bitcoinNetwork: options?.bitcoinNetwork ?? TEST_NETWORK,
@@ -105,6 +113,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
105
113
  maxExpectedOnchainSendSafetyFactor: options?.maxExpectedOnchainSendSafetyFactor ?? 4,
106
114
  maxExpectedOnchainSendGracePeriodBlocks: options?.maxExpectedOnchainSendGracePeriodBlocks ?? 12,
107
115
  },
116
+ versionedContracts,
108
117
  events
109
118
  );
110
119
  this._btcRpc = btcRpc;
@@ -234,29 +243,35 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
234
243
  confirmationTarget: options?.confirmationTarget ?? 3,
235
244
  confirmations: options?.confirmations ?? 2
236
245
  };
246
+ const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
237
247
 
238
248
  const nonce: bigint = this.getRandomNonce();
239
249
  const outputScript: Buffer = this.btcAddressToOutputScript(recipient);
240
- const _hash: string | undefined = !amountData.exactIn ?
241
- this._contract.getHashForOnchain(outputScript, amountData.amount, _options.confirmations, nonce).toString("hex") :
250
+ const _hash = !amountData.exactIn ?
251
+ mapArrayToObject(lpVersions, (contractVersion: string) => {
252
+ return this._contract(contractVersion).getHashForOnchain(outputScript, amountData.amount, _options.confirmations, nonce).toString("hex");
253
+ }) :
242
254
  undefined;
243
255
 
244
256
  const _abortController = extendAbortController(abortSignal);
245
257
  const pricePreFetchPromise: Promise<bigint | undefined> = this.preFetchPrice(amountData, _abortController.signal);
246
258
  const usdPricePrefetchPromise: Promise<number | undefined> = this.preFetchUsdPrice(_abortController.signal);
247
- const feeRatePromise: Promise<string | undefined> = this.preFetchFeeRate(signer, amountData, _hash, _abortController);
248
- const _signDataPromise: Promise<T["PreFetchVerification"] | undefined> | undefined = this._contract.preFetchBlockDataForSignatures==null ?
249
- this.preFetchSignData(Promise.resolve(true)) :
250
- undefined;
259
+ const feeRatePromise = this.preFetchFeeRate(signer, amountData, _hash, _abortController, lpVersions);
260
+ const _signDataPromise = mapArrayToObject(lpVersions, (contractVersion: string) => {
261
+ return this._contract(contractVersion).preFetchBlockDataForSignatures==null ?
262
+ this.preFetchSignData(Promise.resolve(true), contractVersion) :
263
+ undefined;
264
+ });
251
265
 
252
266
  return lps.map(lp => {
253
267
  return {
254
268
  intermediary: lp,
255
269
  quote: (async () => {
256
270
  if(lp.services[SwapType.TO_BTC]==null) throw new Error("LP service for processing to btc swaps not found!");
271
+ const version = lp.getContractVersion(this.chainIdentifier);
257
272
 
258
273
  const abortController = extendAbortController(_abortController.signal);
259
- const reputationPromise: Promise<SingleChainReputationType | undefined> = this.preFetchIntermediaryReputation(amountData, lp, abortController);
274
+ const reputationPromise: Promise<SingleChainReputationType | undefined> = this.preFetchIntermediaryReputation(amountData, lp, abortController, version);
260
275
 
261
276
  try {
262
277
  const {signDataPromise, resp} = await tryWithRetries(async(retryCount) => {
@@ -269,13 +284,13 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
269
284
  token: amountData.token,
270
285
  offerer: signer,
271
286
  exactIn: amountData.exactIn,
272
- feeRate: throwIfUndefined(feeRatePromise),
287
+ feeRate: throwIfUndefined(feeRatePromise[version]),
273
288
  additionalParams
274
289
  }, this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined);
275
290
 
276
- let signDataPromise = _signDataPromise;
291
+ let signDataPromise = _signDataPromise[version];
277
292
  if(signDataPromise==null) {
278
- signDataPromise = this.preFetchSignData(signDataPrefetch);
293
+ signDataPromise = this.preFetchSignData(signDataPrefetch, version);
279
294
  } else signDataPrefetch.catch(() => {});
280
295
 
281
296
  return {
@@ -284,9 +299,9 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
284
299
  };
285
300
  }, undefined, RequestError, abortController.signal);
286
301
 
287
- let hash: string = _hash ?? this._contract.getHashForOnchain(outputScript, resp.amount, _options.confirmations, nonce).toString("hex");
302
+ let hash: string = _hash?.[version] ?? this._contract(version).getHashForOnchain(outputScript, resp.amount, _options.confirmations, nonce).toString("hex");
288
303
 
289
- const data: T["Data"] = new this._swapDataDeserializer(resp.data);
304
+ const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
290
305
  data.setOfferer(signer);
291
306
 
292
307
  this.verifyReturnedData(signer, resp, amountData, lp, _options, data, hash);
@@ -295,7 +310,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
295
310
  lp.services[SwapType.TO_BTC], true, resp.amount, data.getAmount(),
296
311
  amountData.token, resp, pricePreFetchPromise, usdPricePrefetchPromise, abortController.signal
297
312
  ),
298
- this.verifyReturnedSignature(signer, data, resp, feeRatePromise, signDataPromise, abortController.signal),
313
+ this.verifyReturnedSignature(signer, data, resp, feeRatePromise[version], signDataPromise, version, abortController.signal),
299
314
  reputationPromise
300
315
  ]);
301
316
  abortController.signal.throwIfAborted();
@@ -312,7 +327,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
312
327
  expiry: signatureExpiry,
313
328
  swapFee: resp.swapFee,
314
329
  swapFeeBtc,
315
- feeRate: (await feeRatePromise)!,
330
+ feeRate: (await feeRatePromise[version])!,
316
331
  signatureData: resp,
317
332
  data,
318
333
  networkFee: resp.networkFee,
@@ -323,7 +338,8 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
323
338
  satsPerVByte: Number(resp.satsPervByte),
324
339
  exactIn: amountData.exactIn,
325
340
  requiredConfirmations: _options.confirmations,
326
- nonce
341
+ nonce,
342
+ contractVersion: version
327
343
  } as ToBTCSwapInit<T["Data"]>);
328
344
  return quote;
329
345
  } catch (e) {
@@ -341,6 +357,7 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
341
357
  async recoverFromSwapDataAndState(
342
358
  init: {data: T["Data"], getInitTxId: () => Promise<string>, getTxBlock: () => Promise<{blockTime: number, blockHeight: number}>},
343
359
  state: SwapCommitState,
360
+ contractVersion: string,
344
361
  lp?: Intermediary
345
362
  ): Promise<ToBTCSwap<T> | null> {
346
363
  const data = init.data;
@@ -367,7 +384,8 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
367
384
  data,
368
385
  networkFee: 0n,
369
386
  networkFeeBtc: 0n,
370
- exactIn: true
387
+ exactIn: true,
388
+ contractVersion
371
389
  };
372
390
  const swap = new ToBTCSwap(this, swapInit);
373
391
  swap._commitTxId = await init.getInitTxId();
@@ -276,6 +276,8 @@ export class SpvFromBTCSwap<T extends ChainType>
276
276
  */
277
277
  private btcTxConfirmedAt?: number;
278
278
 
279
+ private _contract: T["SpvVaultContract"];
280
+
279
281
  constructor(wrapper: SpvFromBTCWrapper<T>, init: SpvFromBTCSwapInit);
280
282
  constructor(wrapper: SpvFromBTCWrapper<T>, obj: any);
281
283
  constructor(wrapper: SpvFromBTCWrapper<T>, initOrObject: SpvFromBTCSwapInit | any) {
@@ -342,10 +344,12 @@ export class SpvFromBTCSwap<T extends ChainType>
342
344
  this.gasPricingInfo = deserializePriceInfoType(initOrObject.gasPricingInfo);
343
345
  this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
344
346
  this.posted = initOrObject.posted;
345
- if(initOrObject.data!=null) this._data = new this.wrapper._spvWithdrawalDataDeserializer(initOrObject.data);
347
+ if(initOrObject.data!=null) this._data = new (this.wrapper._spvWithdrawalDataDeserializer(this._contractVersion))(initOrObject.data);
346
348
  }
347
349
  this.tryCalculateSwapFee();
348
350
  this.logger = getLogger("SPVFromBTC("+this.getId()+"): ");
351
+
352
+ this._contract = wrapper._contract(this._contractVersion);
349
353
  }
350
354
 
351
355
  /**
@@ -779,7 +783,7 @@ export class SpvFromBTCSwap<T extends ChainType>
779
783
 
780
784
  const out2script = toOutputScript(this.wrapper._options.bitcoinNetwork, this.btcDestinationAddress);
781
785
 
782
- const opReturnData = this.wrapper._contract.toOpReturnData(
786
+ const opReturnData = this._contract.toOpReturnData(
783
787
  this.recipient,
784
788
  [
785
789
  this.outputTotalSwap / this.vaultTokenMultipliers[0],
@@ -934,7 +938,7 @@ export class SpvFromBTCSwap<T extends ChainType>
934
938
  psbt.finalizeIdx(i);
935
939
  }
936
940
  const btcTx = await this.wrapper._btcRpc.parseTransaction(Buffer.from(psbt.toBytes(true)).toString("hex"));
937
- const data = await this.wrapper._contract.getWithdrawalData(btcTx);
941
+ const data = await this._contract.getWithdrawalData(btcTx);
938
942
 
939
943
  this.logger.debug("submitPsbt(): parsed withdrawal data: ", data);
940
944
 
@@ -971,7 +975,7 @@ export class SpvFromBTCSwap<T extends ChainType>
971
975
 
972
976
  //Verify tx is parsable by the contract
973
977
  try {
974
- await this.wrapper._contract.checkWithdrawalTx(data);
978
+ await this._contract.checkWithdrawalTx(data);
975
979
  } catch (e: any) {
976
980
  throw new Error("Transaction not parsable by the contract: "+(e.message ?? e.toString()));
977
981
  }
@@ -1273,7 +1277,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1273
1277
  if(!this.isClaimable()) throw new Error("Must be in BTC_TX_CONFIRMED state!");
1274
1278
  if(this._data==null) throw new Error("Expected swap to have withdrawal data filled!");
1275
1279
 
1276
- const vaultData = await this.wrapper._contract.getVaultData(this.vaultOwner, this.vaultId);
1280
+ const vaultData = await this._contract.getVaultData(this.vaultOwner, this.vaultId);
1277
1281
  if(vaultData==null) throw new Error(`Vault data for ${this.vaultOwner}:${this.vaultId.toString(10)} not found (already closed???)!`);
1278
1282
 
1279
1283
  const btcTx = await this.wrapper._btcRpc.getTransaction(this._data.btcTx.txid);
@@ -1291,13 +1295,13 @@ export class SpvFromBTCSwap<T extends ChainType>
1291
1295
  //Parse transactions to withdrawal data
1292
1296
  const withdrawalData: T["SpvVaultWithdrawalData"][] = [];
1293
1297
  for(let tx of txs) {
1294
- withdrawalData.push(await this.wrapper._contract.getWithdrawalData(tx));
1298
+ withdrawalData.push(await this._contract.getWithdrawalData(tx));
1295
1299
  }
1296
1300
 
1297
- return await this.wrapper._contract.txsClaim(
1301
+ return await this._contract.txsClaim(
1298
1302
  address ?? this._getInitiator(), vaultData,
1299
1303
  withdrawalData.map(tx => {return {tx}}),
1300
- this.wrapper._synchronizer, true
1304
+ this.wrapper._synchronizer(this._contractVersion), true
1301
1305
  );
1302
1306
  }
1303
1307
 
@@ -1335,7 +1339,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1335
1339
  this.logger.info("claim(): Transaction state is CLAIMED, swap was successfully claimed by the watchtower");
1336
1340
  return this._claimTxId!;
1337
1341
  }
1338
- const withdrawalState = await this.wrapper._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1342
+ const withdrawalState = await this._contract.getWithdrawalState(this._data, this._genesisSmartChainBlockHeight);
1339
1343
  if(withdrawalState!=null && withdrawalState.type===SpvWithdrawalStateType.CLAIMED) {
1340
1344
  this.logger.info("claim(): Transaction status is CLAIMED, swap was successfully claimed by the watchtower");
1341
1345
  this._claimTxId = withdrawalState.txId;
@@ -1374,7 +1378,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1374
1378
  try {
1375
1379
  //Be smart about checking withdrawal state
1376
1380
  if(await this._shouldCheckWithdrawalState()) {
1377
- status = await this.wrapper._contract.getWithdrawalState(
1381
+ status = await this._contract.getWithdrawalState(
1378
1382
  this._data, this._genesisSmartChainBlockHeight
1379
1383
  ) ?? {type: SpvWithdrawalStateType.NOT_FOUND};
1380
1384
  }
@@ -1654,7 +1658,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1654
1658
 
1655
1659
  if(this._state===SpvFromBTCSwapState.BROADCASTED || this._state===SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1656
1660
  if(await this._shouldCheckWithdrawalState()) {
1657
- const status = await this.wrapper._contract.getWithdrawalState(this._data!, this._genesisSmartChainBlockHeight);
1661
+ const status = await this._contract.getWithdrawalState(this._data!, this._genesisSmartChainBlockHeight);
1658
1662
  this.logger.debug("syncStateFromChain(): status of "+this._data!.btcTx.txid, status);
1659
1663
  switch(status?.type) {
1660
1664
  case SpvWithdrawalStateType.FRONTED:
@@ -1750,8 +1754,8 @@ export class SpvFromBTCSwap<T extends ChainType>
1750
1754
  * @internal
1751
1755
  */
1752
1756
  async _shouldCheckWithdrawalState(frontingAddress?: string | null, vaultDataUtxo?: string | null) {
1753
- if(frontingAddress===undefined) frontingAddress = await this.wrapper._contract.getFronterAddress(this.vaultOwner, this.vaultId, this._data!);
1754
- if(vaultDataUtxo===undefined) vaultDataUtxo = await this.wrapper._contract.getVaultLatestUtxo(this.vaultOwner, this.vaultId);
1757
+ if(frontingAddress===undefined) frontingAddress = await this._contract.getFronterAddress(this.vaultOwner, this.vaultId, this._data!);
1758
+ if(vaultDataUtxo===undefined) vaultDataUtxo = await this._contract.getVaultLatestUtxo(this.vaultOwner, this.vaultId);
1755
1759
 
1756
1760
  if(frontingAddress != null) return true; //In case the swap is fronted there will for sure be a fronted event
1757
1761
  if(vaultDataUtxo == null) return true; //Vault UTXO is null (the vault closed)