@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
@@ -47,9 +47,10 @@ class UnifiedSwapEventListener {
47
47
  const htlcCheckInitializeEvents = {};
48
48
  for (let event of events) {
49
49
  const escrowHash = chainEventToEscrowHash(event);
50
+ const eventVersion = event.contractVersion ?? "v1";
50
51
  if (escrowHash != null) {
51
52
  const swap = swapsByEscrowHash[escrowHash];
52
- if (swap != null) {
53
+ if (swap != null && (swap._contractVersion ?? "v1") === eventVersion) {
53
54
  const obj = this.listeners[swap.getType()];
54
55
  if (obj == null)
55
56
  continue;
@@ -85,8 +86,9 @@ class UnifiedSwapEventListener {
85
86
  logger.debug("processEvents(): Additional HTLC swaps founds: ", swapsByClaimDataHash);
86
87
  for (let claimData in htlcCheckInitializeEvents) {
87
88
  const event = htlcCheckInitializeEvents[claimData];
89
+ const eventVersion = event.contractVersion ?? "v1";
88
90
  const swap = swapsByClaimDataHash[claimData];
89
- if (swap != null) {
91
+ if (swap != null && (swap._contractVersion ?? "v1") === eventVersion) {
90
92
  const obj = this.listeners[swap.getType()];
91
93
  if (obj == null)
92
94
  continue;
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ParamDecoder = void 0;
4
4
  const buffer_1 = require("buffer");
5
+ function ensureBuffer(input) {
6
+ if (input instanceof buffer_1.Buffer)
7
+ return input;
8
+ return buffer_1.Buffer.from(input);
9
+ }
5
10
  class ParamDecoder {
6
11
  constructor() {
7
12
  this.frameData = [];
@@ -48,8 +53,8 @@ class ParamDecoder {
48
53
  leavesBuffer = null;
49
54
  }
50
55
  else {
51
- this.frameHeader = leavesBuffer.subarray(0, 4);
52
- leavesBuffer = leavesBuffer.subarray(4);
56
+ this.frameHeader = ensureBuffer(leavesBuffer.subarray(0, 4));
57
+ leavesBuffer = ensureBuffer(leavesBuffer.subarray(4));
53
58
  }
54
59
  }
55
60
  else if (this.frameHeader.length < 4) {
@@ -60,14 +65,14 @@ class ParamDecoder {
60
65
  }
61
66
  else {
62
67
  this.frameHeader = buffer_1.Buffer.concat([this.frameHeader, leavesBuffer.subarray(0, requiredLen)]);
63
- leavesBuffer = leavesBuffer.subarray(requiredLen);
68
+ leavesBuffer = ensureBuffer(leavesBuffer.subarray(requiredLen));
64
69
  }
65
70
  }
66
71
  if (leavesBuffer == null)
67
72
  continue;
68
73
  if (this.frameHeader == null || this.frameHeader.length < 4)
69
74
  continue;
70
- const frameLength = this.frameHeader.readUint32LE();
75
+ const frameLength = this.frameHeader.readUint32LE != null ? this.frameHeader.readUint32LE() : this.frameHeader.readUInt32LE();
71
76
  const requiredLen = frameLength - this.frameDataLength;
72
77
  if (leavesBuffer.length <= requiredLen) {
73
78
  this.frameData.push(leavesBuffer);
@@ -15,7 +15,12 @@ class ParamEncoder {
15
15
  writeParams(data) {
16
16
  const serialized = buffer_1.Buffer.from(JSON.stringify(data));
17
17
  const frameLengthBuffer = buffer_1.Buffer.alloc(4);
18
- frameLengthBuffer.writeUint32LE(serialized.length);
18
+ if (frameLengthBuffer.writeUint32LE != null) {
19
+ frameLengthBuffer.writeUint32LE(serialized.length);
20
+ }
21
+ else {
22
+ frameLengthBuffer.writeUInt32LE(serialized.length);
23
+ }
19
24
  return this.writeFN(buffer_1.Buffer.concat([
20
25
  frameLengthBuffer,
21
26
  serialized
@@ -50,6 +50,12 @@ export declare class Intermediary {
50
50
  readonly addresses: {
51
51
  [chainIdentifier: string]: string;
52
52
  };
53
+ /**
54
+ * Contract versions of the intermediary on smart chains
55
+ */
56
+ readonly contractVersions: {
57
+ [chainIdentifier: string]: string;
58
+ };
53
59
  /**
54
60
  * Swap protocol services offered by the intermediary
55
61
  */
@@ -97,6 +103,8 @@ export declare class Intermediary {
97
103
  [chainIdentifier: string]: string;
98
104
  }, services: ServicesType, reputation?: {
99
105
  [chainIdentifier: string]: SingleChainReputationType;
106
+ }, contractVersions?: {
107
+ [chainIdentifier: string]: string;
100
108
  });
101
109
  /**
102
110
  * Returns the input/output swap limit for given swap type, chain and token
@@ -154,4 +162,17 @@ export declare class Intermediary {
154
162
  * @param chainIdentifier Chain identifier of the smart chain
155
163
  */
156
164
  getAddress(chainIdentifier: string): string;
165
+ /**
166
+ * Returns the contract version used by the intermediary for a given chain
167
+ *
168
+ * @param chainIdentifier
169
+ */
170
+ getContractVersion(chainIdentifier: string): string;
171
+ /**
172
+ * Returns the range of contract versions used by the LPs
173
+ *
174
+ * @param chainIdentifier
175
+ * @param lps
176
+ */
177
+ static getContractVersionsForLps(chainIdentifier: string, lps: Intermediary[]): string[];
157
178
  }
@@ -9,7 +9,7 @@ const RetryUtils_1 = require("../utils/RetryUtils");
9
9
  * @category LPs
10
10
  */
11
11
  class Intermediary {
12
- constructor(url, addresses, services, reputation = {}) {
12
+ constructor(url, addresses, services, reputation = {}, contractVersions = {}) {
13
13
  /**
14
14
  * Reputation of the intermediary on different smart chains, this is only fetched
15
15
  * on-demand when creating a swap where reputation is checked
@@ -24,6 +24,7 @@ class Intermediary {
24
24
  this.addresses = addresses;
25
25
  this.services = services;
26
26
  this.reputation = reputation;
27
+ this.contractVersions = contractVersions;
27
28
  this.swapBounds = {};
28
29
  for (let _swapType in this.services) {
29
30
  const swapType = parseInt(_swapType);
@@ -138,5 +139,28 @@ class Intermediary {
138
139
  getAddress(chainIdentifier) {
139
140
  return this.addresses[chainIdentifier];
140
141
  }
142
+ /**
143
+ * Returns the contract version used by the intermediary for a given chain
144
+ *
145
+ * @param chainIdentifier
146
+ */
147
+ getContractVersion(chainIdentifier) {
148
+ return this.contractVersions[chainIdentifier] ?? "v1";
149
+ }
150
+ /**
151
+ * Returns the range of contract versions used by the LPs
152
+ *
153
+ * @param chainIdentifier
154
+ * @param lps
155
+ */
156
+ static getContractVersionsForLps(chainIdentifier, lps) {
157
+ const versions = [];
158
+ lps.forEach((lp) => {
159
+ const lpVersion = lp.getContractVersion(chainIdentifier);
160
+ if (!versions.includes(lpVersion))
161
+ versions.push(lpVersion);
162
+ });
163
+ return versions;
164
+ }
141
165
  }
142
166
  exports.Intermediary = Intermediary;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Intermediary } from "./Intermediary";
3
3
  import { SwapType } from "../enums/SwapType";
4
- import { SwapContract } from "@atomiqlabs/base";
4
+ import { SpvVaultContract, SwapContract } from "@atomiqlabs/base";
5
5
  import { EventEmitter } from "events";
6
6
  /**
7
7
  * Swap handler type mapping for intermediary communication
@@ -83,7 +83,12 @@ export declare class IntermediaryDiscovery extends EventEmitter {
83
83
  * Swap contracts for checking intermediary signatures
84
84
  */
85
85
  swapContracts: {
86
- [key: string]: SwapContract;
86
+ [chainIdentifier: string]: {
87
+ [contractVersion: string]: {
88
+ swapContract: SwapContract;
89
+ spvVaultContract: SpvVaultContract;
90
+ };
91
+ };
87
92
  };
88
93
  /**
89
94
  * Registry URL used as a source for the list of intermediaries, this should be a link to a
@@ -105,7 +110,12 @@ export declare class IntermediaryDiscovery extends EventEmitter {
105
110
  */
106
111
  private overrideNodeUrls?;
107
112
  constructor(swapContracts: {
108
- [key: string]: SwapContract;
113
+ [chainIdentifier: string]: {
114
+ [contractVersion: string]: {
115
+ swapContract: SwapContract;
116
+ spvVaultContract: SpvVaultContract;
117
+ };
118
+ };
109
119
  }, registryUrl?: string, nodeUrls?: string[], httpRequestTimeout?: number, maxWaitForOthersTimeout?: number);
110
120
  /**
111
121
  * Fetches the URLs of swap intermediaries from registry or from a pre-defined array of node urls
@@ -183,6 +193,8 @@ export declare class IntermediaryDiscovery extends EventEmitter {
183
193
  /**
184
194
  * Returns swap candidates for a specific swap type & token address
185
195
  *
196
+ * @remark Also filters the LPs based on supported swap versions
197
+ *
186
198
  * @param chainIdentifier Chain identifier of the smart chain
187
199
  * @param swapType Swap protocol type
188
200
  * @param tokenAddress Token address
@@ -129,20 +129,28 @@ class IntermediaryDiscovery extends events_1.EventEmitter {
129
129
  * @param abortSignal
130
130
  */
131
131
  async getNodeInfo(url, abortSignal) {
132
- const response = await (0, RetryUtils_1.tryWithRetries)(() => IntermediaryAPI_1.IntermediaryAPI.getIntermediaryInfo(url, this.httpRequestTimeout, abortSignal), { maxRetries: 3, delay: 100, exponential: true }, undefined, abortSignal);
132
+ const response = await (0, RetryUtils_1.tryWithRetries)(() => IntermediaryAPI_1.IntermediaryAPI.getIntermediaryInfo(url, this.httpRequestTimeout, abortSignal), { maxRetries: 3, delay: 100, exponential: true }, undefined, abortSignal, "debug");
133
133
  abortSignal?.throwIfAborted();
134
134
  const promises = [];
135
135
  const addresses = {};
136
+ const contractVersions = {};
136
137
  for (let chain in response.chains) {
137
138
  if (this.swapContracts[chain] != null) {
139
+ const { signature, address, contractVersion } = response.chains[chain];
140
+ const _contractVersion = contractVersion ?? "v1";
141
+ const contract = this.swapContracts[chain][_contractVersion];
142
+ if (contract == null) {
143
+ logger.warn("getNodeInfo(): Unknown chain contract version " + _contractVersion + " for " + chain + " reported by intermediary: " + url);
144
+ continue;
145
+ }
138
146
  promises.push((async () => {
139
- const { signature, address } = response.chains[chain];
140
147
  try {
141
- await this.swapContracts[chain].isValidDataSignature(buffer_1.Buffer.from(response.envelope), signature, address);
148
+ await contract.swapContract.isValidDataSignature(buffer_1.Buffer.from(response.envelope), signature, address);
142
149
  addresses[chain] = address;
150
+ contractVersions[chain] = _contractVersion;
143
151
  }
144
152
  catch (e) {
145
- logger.warn("Failed to verify " + chain + " signature for intermediary: " + url);
153
+ logger.warn("getNodeInfo(): Failed to verify " + chain + " signature for intermediary: " + url);
146
154
  }
147
155
  })());
148
156
  }
@@ -171,6 +179,7 @@ class IntermediaryDiscovery extends events_1.EventEmitter {
171
179
  }
172
180
  return {
173
181
  addresses,
182
+ contractVersions,
174
183
  info
175
184
  };
176
185
  }
@@ -188,10 +197,11 @@ class IntermediaryDiscovery extends events_1.EventEmitter {
188
197
  for (let key in nodeInfo.info.services) {
189
198
  services[swapHandlerTypeToSwapType(key)] = nodeInfo.info.services[key];
190
199
  }
191
- return new Intermediary_1.Intermediary(url, nodeInfo.addresses, services);
200
+ return new Intermediary_1.Intermediary(url, nodeInfo.addresses, services, undefined, nodeInfo.contractVersions);
192
201
  }
193
202
  catch (e) {
194
- logger.warn("fetchIntermediaries(): Error contacting intermediary " + url + ": ", e);
203
+ logger.warn("fetchIntermediaries(): Intermediary " + url + ` is unreachable due to ${e.name ?? e.message} error, skipping...`);
204
+ logger.debug("fetchIntermediaries(): Error contacting intermediary " + url + ": ", e);
195
205
  return null;
196
206
  }
197
207
  }
@@ -355,6 +365,8 @@ class IntermediaryDiscovery extends events_1.EventEmitter {
355
365
  /**
356
366
  * Returns swap candidates for a specific swap type & token address
357
367
  *
368
+ * @remark Also filters the LPs based on supported swap versions
369
+ *
358
370
  * @param chainIdentifier Chain identifier of the smart chain
359
371
  * @param swapType Swap protocol type
360
372
  * @param tokenAddress Token address
@@ -376,6 +388,13 @@ class IntermediaryDiscovery extends events_1.EventEmitter {
376
388
  return false;
377
389
  if (!swapService.chainTokens[chainIdentifier].includes(tokenAddress.toString()))
378
390
  return false;
391
+ const contracts = this.swapContracts[chainIdentifier][e.getContractVersion(chainIdentifier) ?? "v1"];
392
+ if (contracts == null)
393
+ return false;
394
+ if (swapType === SwapType_1.SwapType.FROM_BTCLN_AUTO && !contracts.swapContract?.supportsInitWithoutClaimer)
395
+ return false;
396
+ if (swapType === SwapType_1.SwapType.SPV_VAULT_FROM_BTC && contracts.spvVaultContract == null)
397
+ return false;
379
398
  return true;
380
399
  });
381
400
  candidates.sort(getIntermediaryComparator(swapType, tokenAddress, amount));
@@ -7,6 +7,7 @@ export type InfoHandlerResponse = {
7
7
  [chainIdentifier: string]: {
8
8
  address: string;
9
9
  signature: string;
10
+ contractVersion?: string;
10
11
  };
11
12
  };
12
13
  };
@@ -159,14 +159,19 @@ type ChainSpecificData<T extends ChainType> = {
159
159
  [SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>;
160
160
  };
161
161
  chainEvents: T["Events"];
162
- swapContract: T["Contract"];
163
- spvVaultContract: T["SpvVaultContract"];
164
162
  chainInterface: T["ChainInterface"];
165
- btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>;
166
- synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>;
167
163
  unifiedChainEvents: UnifiedSwapEventListener<T>;
168
164
  unifiedSwapStorage: UnifiedSwapStorage<T>;
169
165
  reviver: (val: any) => ISwap<T>;
166
+ defaultVersion: string;
167
+ versionedContracts: {
168
+ [contractVersion: string]: {
169
+ swapContract: T["Contract"];
170
+ spvVaultContract: T["SpvVaultContract"];
171
+ btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>;
172
+ synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>;
173
+ };
174
+ };
170
175
  };
171
176
  type MultiChainData<T extends MultiChain> = {
172
177
  [chainIdentifier in keyof T]: ChainSpecificData<T[chainIdentifier]>;
@@ -91,30 +91,50 @@ class Swapper extends events_1.EventEmitter {
91
91
  this.emit("swapState", swap);
92
92
  };
93
93
  this._chains = (0, Utils_1.objectMap)(chainsData, (chainData, key) => {
94
- const { swapContract, chainEvents, btcRelay, chainInterface, spvVaultContract, spvVaultWithdrawalDataConstructor, chainId } = chainData;
95
- const synchronizer = bitcoinSynchronizer(btcRelay);
94
+ let { chainInterface, chainEvents, chainId, btcRelay, swapContract, swapDataConstructor, spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor, defaultVersion, versions } = chainData;
95
+ defaultVersion ??= "v1";
96
+ if (versions == null) {
97
+ versions = {
98
+ [defaultVersion]: {
99
+ btcRelay,
100
+ swapContract,
101
+ swapDataConstructor,
102
+ spvVaultContract,
103
+ spvVaultDataConstructor,
104
+ spvVaultWithdrawalDataConstructor
105
+ }
106
+ };
107
+ }
108
+ const versionedContracts = (0, Utils_1.objectMap)(versions, (value, key) => {
109
+ return {
110
+ swapContract: value.swapContract,
111
+ spvVaultContract: value.spvVaultContract,
112
+ btcRelay: value.btcRelay,
113
+ synchronizer: bitcoinSynchronizer(value.btcRelay)
114
+ };
115
+ });
96
116
  const storageHandler = swapStorage(storagePrefix + chainId);
97
117
  const unifiedSwapStorage = new UnifiedSwapStorage_1.UnifiedSwapStorage(storageHandler, this.options.noSwapCache);
98
118
  const unifiedChainEvents = new UnifiedSwapEventListener_1.UnifiedSwapEventListener(unifiedSwapStorage, chainEvents);
99
119
  const wrappers = {};
100
- wrappers[SwapType_1.SwapType.TO_BTCLN] = new ToBTCLNWrapper_1.ToBTCLNWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, swapContract, pricing, this._tokens[chainId], chainData.swapDataConstructor, {
120
+ wrappers[SwapType_1.SwapType.TO_BTCLN] = new ToBTCLNWrapper_1.ToBTCLNWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, {
101
121
  getRequestTimeout: this.options.getRequestTimeout,
102
122
  postRequestTimeout: this.options.postRequestTimeout,
103
123
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
104
124
  });
105
- wrappers[SwapType_1.SwapType.TO_BTC] = new ToBTCWrapper_1.ToBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, swapContract, pricing, this._tokens[chainId], chainData.swapDataConstructor, this._bitcoinRpc, {
125
+ wrappers[SwapType_1.SwapType.TO_BTC] = new ToBTCWrapper_1.ToBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, this._bitcoinRpc, {
106
126
  getRequestTimeout: this.options.getRequestTimeout,
107
127
  postRequestTimeout: this.options.postRequestTimeout,
108
128
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
109
129
  bitcoinNetwork: this._btcNetwork
110
130
  });
111
- wrappers[SwapType_1.SwapType.FROM_BTCLN] = new FromBTCLNWrapper_1.FromBTCLNWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, swapContract, pricing, this._tokens[chainId], chainData.swapDataConstructor, lightningApi, {
131
+ wrappers[SwapType_1.SwapType.FROM_BTCLN] = new FromBTCLNWrapper_1.FromBTCLNWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, lightningApi, {
112
132
  getRequestTimeout: this.options.getRequestTimeout,
113
133
  postRequestTimeout: this.options.postRequestTimeout,
114
134
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
115
135
  unsafeSkipLnNodeCheck: this.bitcoinNetwork === base_1.BitcoinNetwork.TESTNET4 || this.bitcoinNetwork === base_1.BitcoinNetwork.REGTEST
116
136
  });
117
- wrappers[SwapType_1.SwapType.FROM_BTC] = new FromBTCWrapper_1.FromBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, swapContract, pricing, this._tokens[chainId], chainData.swapDataConstructor, btcRelay, synchronizer, this._bitcoinRpc, {
137
+ wrappers[SwapType_1.SwapType.FROM_BTC] = new FromBTCWrapper_1.FromBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, versionedContracts, this._bitcoinRpc, {
118
138
  getRequestTimeout: this.options.getRequestTimeout,
119
139
  postRequestTimeout: this.options.postRequestTimeout,
120
140
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
@@ -131,16 +151,18 @@ class Swapper extends events_1.EventEmitter {
131
151
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
132
152
  bitcoinNetwork: this._btcNetwork
133
153
  });
154
+ // This is gated on the default version of the contracts
134
155
  if (spvVaultContract != null) {
135
- wrappers[SwapType_1.SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper_1.SpvFromBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, spvVaultContract, pricing, this._tokens[chainId], spvVaultWithdrawalDataConstructor, btcRelay, synchronizer, bitcoinRpc, {
156
+ wrappers[SwapType_1.SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper_1.SpvFromBTCWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, versionedContracts, bitcoinRpc, {
136
157
  getRequestTimeout: this.options.getRequestTimeout,
137
158
  postRequestTimeout: this.options.postRequestTimeout,
138
159
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
139
160
  bitcoinNetwork: this._btcNetwork
140
161
  });
141
162
  }
163
+ // This is gated on the default version of the contracts
142
164
  if (swapContract.supportsInitWithoutClaimer) {
143
- wrappers[SwapType_1.SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper_1.FromBTCLNAutoWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, swapContract, pricing, this._tokens[chainId], chainData.swapDataConstructor, lightningApi, this.messenger, {
165
+ wrappers[SwapType_1.SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper_1.FromBTCLNAutoWrapper(key, unifiedSwapStorage, unifiedChainEvents, chainInterface, pricing, this._tokens[chainId], versions, lightningApi, this.messenger, {
144
166
  getRequestTimeout: this.options.getRequestTimeout,
145
167
  postRequestTimeout: this.options.postRequestTimeout,
146
168
  saveUninitializedSwaps: this.options.saveUninitializedSwaps,
@@ -156,18 +178,16 @@ class Swapper extends events_1.EventEmitter {
156
178
  };
157
179
  return {
158
180
  chainEvents,
159
- spvVaultContract,
160
- swapContract,
161
181
  chainInterface,
162
- btcRelay,
163
- synchronizer,
164
182
  wrappers,
165
183
  unifiedChainEvents,
166
184
  unifiedSwapStorage,
167
- reviver
185
+ defaultVersion,
186
+ reviver,
187
+ versionedContracts
168
188
  };
169
189
  });
170
- const contracts = (0, Utils_1.objectMap)(chainsData, (data) => data.swapContract);
190
+ const contracts = (0, Utils_1.objectMap)(chainsData, (data) => data.versions ?? { [data.defaultVersion ?? "v1"]: { swapContract: data.swapContract, spvVaultContract: data.spvVaultContract } });
171
191
  if (options.intermediaryUrl != null) {
172
192
  this.intermediaryDiscovery = new IntermediaryDiscovery_1.IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
173
193
  }
@@ -182,7 +202,7 @@ class Swapper extends events_1.EventEmitter {
182
202
  });
183
203
  }
184
204
  async _init() {
185
- this.logger.debug("init(): Initializing swapper, sdk-lib version 16.1.3");
205
+ this.logger.debug("init(): Initializing swapper...");
186
206
  const abortController = new AbortController();
187
207
  const promises = [];
188
208
  let automaticClockDriftCorrectionPromise = undefined;
@@ -218,9 +238,15 @@ class Swapper extends events_1.EventEmitter {
218
238
  const chainPromises = [];
219
239
  for (let chainIdentifier in this._chains) {
220
240
  chainPromises.push((async () => {
221
- const { swapContract, unifiedChainEvents, unifiedSwapStorage, wrappers, reviver } = this._chains[chainIdentifier];
222
- await swapContract.start();
223
- this.logger.debug("init(): Intialized swap contract: " + chainIdentifier);
241
+ const { chainInterface, versionedContracts, unifiedChainEvents, unifiedSwapStorage, wrappers, reviver } = this._chains[chainIdentifier];
242
+ const _chainInterface = chainInterface;
243
+ if (_chainInterface.verifyNetwork != null) {
244
+ await _chainInterface.verifyNetwork(this.bitcoinNetwork);
245
+ }
246
+ for (let contractVersion in versionedContracts) {
247
+ await versionedContracts[contractVersion].swapContract.start();
248
+ this.logger.debug("init(): Intialized swap contract: " + chainIdentifier + ` version: ${contractVersion}`);
249
+ }
224
250
  await unifiedSwapStorage.init();
225
251
  if (unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage_1.IndexedDBUnifiedStorage) {
226
252
  //Try to migrate the data here
@@ -1169,15 +1195,35 @@ class Swapper extends events_1.EventEmitter {
1169
1195
  * initiated after this blockheight
1170
1196
  */
1171
1197
  async recoverSwaps(chainId, signer, startBlockheight) {
1172
- const { spvVaultContract, swapContract, unifiedSwapStorage, reviver, wrappers } = this._chains[chainId];
1173
- if (swapContract.getHistoricalSwaps == null ||
1174
- (spvVaultContract != null && spvVaultContract.getHistoricalWithdrawalStates == null))
1198
+ //TODO: Recover swaps from all the known contract versions
1199
+ const { versionedContracts, unifiedSwapStorage, reviver, wrappers } = this._chains[chainId];
1200
+ const recoveredSwaps = [];
1201
+ let someVersionSupportsRecovery = false;
1202
+ const recoveredEscrowStates = {};
1203
+ const recoveredSpvStates = {};
1204
+ for (let contractVersion in versionedContracts) {
1205
+ const { swapContract, spvVaultContract } = versionedContracts[contractVersion];
1206
+ if (swapContract.getHistoricalSwaps == null ||
1207
+ (spvVaultContract != null && spvVaultContract.getHistoricalWithdrawalStates == null)) {
1208
+ this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
1209
+ continue;
1210
+ }
1211
+ someVersionSupportsRecovery = true;
1212
+ const { swaps } = await swapContract.getHistoricalSwaps(signer, startBlockheight);
1213
+ const spvVaultData = wrappers[SwapType_1.SwapType.SPV_VAULT_FROM_BTC] == null
1214
+ ? undefined
1215
+ : await spvVaultContract?.getHistoricalWithdrawalStates(signer, startBlockheight);
1216
+ for (let key in swaps)
1217
+ recoveredEscrowStates[key] = { ...swaps[key], contractVersion };
1218
+ if (spvVaultData != null)
1219
+ for (let key in spvVaultData.withdrawals)
1220
+ (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
1221
+ }
1222
+ if (!someVersionSupportsRecovery)
1175
1223
  throw new Error(`Historical swap recovery is not supported for ${chainId}`);
1176
- const { swaps } = await swapContract.getHistoricalSwaps(signer, startBlockheight);
1177
- const spvVaultData = await spvVaultContract?.getHistoricalWithdrawalStates(signer, startBlockheight);
1178
- const escrowHashes = Object.keys(swaps);
1179
- if (spvVaultData != null)
1180
- Object.keys(spvVaultData.withdrawals).forEach(btcTxId => escrowHashes.push(btcTxId));
1224
+ const escrowHashes = Object.keys(recoveredEscrowStates);
1225
+ for (let contractVersion in recoveredSpvStates)
1226
+ Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
1181
1227
  this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
1182
1228
  this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
1183
1229
  const knownSwapsArray = await unifiedSwapStorage.query([[{ key: "escrowHash", value: escrowHashes }]], reviver);
@@ -1188,10 +1234,10 @@ class Swapper extends events_1.EventEmitter {
1188
1234
  knownSwaps[escrowHash] = val;
1189
1235
  });
1190
1236
  this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
1191
- const recoveredSwaps = [];
1192
- for (let escrowHash in swaps) {
1193
- const { init, state } = swaps[escrowHash];
1237
+ for (let escrowHash in recoveredEscrowStates) {
1238
+ const { init, state, contractVersion } = recoveredEscrowStates[escrowHash];
1194
1239
  const knownSwap = knownSwaps[escrowHash];
1240
+ const { swapContract } = versionedContracts[contractVersion];
1195
1241
  if (knownSwap == null) {
1196
1242
  if (init == null) {
1197
1243
  this.logger.warn(`recoverSwaps(escrow): Fetched ${escrowHash} swap state, but swap not found locally!`);
@@ -1200,6 +1246,10 @@ class Swapper extends events_1.EventEmitter {
1200
1246
  }
1201
1247
  else if (knownSwap instanceof IEscrowSwap_1.IEscrowSwap) {
1202
1248
  this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
1249
+ if ((knownSwap._contractVersion ?? "v1") !== contractVersion) {
1250
+ this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
1251
+ continue;
1252
+ }
1203
1253
  if (await knownSwap._forciblySetOnchainState(state)) {
1204
1254
  await knownSwap._save();
1205
1255
  }
@@ -1218,17 +1268,17 @@ class Swapper extends events_1.EventEmitter {
1218
1268
  //To BTCLN
1219
1269
  typeIdentified = true;
1220
1270
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
1221
- swap = await wrappers[SwapType_1.SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, lp);
1271
+ swap = await wrappers[SwapType_1.SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1222
1272
  }
1223
1273
  else if (data.isClaimer(signer)) {
1224
1274
  //From BTCLN
1225
1275
  typeIdentified = true;
1226
1276
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
1227
- if (this.supportsSwapType(chainId, SwapType_1.SwapType.FROM_BTCLN_AUTO)) {
1228
- swap = await wrappers[SwapType_1.SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, lp);
1277
+ if (swapContract.supportsInitWithoutClaimer && wrappers[SwapType_1.SwapType.FROM_BTCLN_AUTO] != null) {
1278
+ swap = await wrappers[SwapType_1.SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1229
1279
  }
1230
1280
  else {
1231
- swap = await wrappers[SwapType_1.SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, lp);
1281
+ swap = await wrappers[SwapType_1.SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1232
1282
  }
1233
1283
  }
1234
1284
  }
@@ -1236,13 +1286,13 @@ class Swapper extends events_1.EventEmitter {
1236
1286
  //To BTC
1237
1287
  typeIdentified = true;
1238
1288
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
1239
- swap = await wrappers[SwapType_1.SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, lp);
1289
+ swap = await wrappers[SwapType_1.SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1240
1290
  }
1241
1291
  else if (data.getType() === base_1.ChainSwapType.CHAIN) {
1242
1292
  //From BTC
1243
1293
  typeIdentified = true;
1244
1294
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
1245
- swap = await wrappers[SwapType_1.SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, lp);
1295
+ swap = await wrappers[SwapType_1.SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
1246
1296
  }
1247
1297
  if (swap != null) {
1248
1298
  recoveredSwaps.push(swap);
@@ -1252,14 +1302,16 @@ class Swapper extends events_1.EventEmitter {
1252
1302
  this.logger.debug(`recoverSwaps(escrow): Swap data type correctly identified but swap returned is null for swap ${escrowHash}`);
1253
1303
  }
1254
1304
  }
1255
- if (spvVaultContract != null && spvVaultData != null) {
1256
- const vaultsData = await spvVaultContract.getMultipleVaultData(Object.keys(spvVaultData.withdrawals)
1305
+ for (let contractVersion in recoveredSpvStates) {
1306
+ const { spvVaultContract } = versionedContracts[contractVersion];
1307
+ const spvVaultData = recoveredSpvStates[contractVersion];
1308
+ const vaultsData = await spvVaultContract.getMultipleVaultData(Object.keys(spvVaultData)
1257
1309
  .map(btcTxId => ({
1258
- owner: spvVaultData.withdrawals[btcTxId].owner,
1259
- vaultId: spvVaultData.withdrawals[btcTxId].vaultId
1310
+ owner: spvVaultData[btcTxId].owner,
1311
+ vaultId: spvVaultData[btcTxId].vaultId
1260
1312
  })));
1261
- for (let btcTxId in spvVaultData.withdrawals) {
1262
- const state = spvVaultData.withdrawals[btcTxId];
1313
+ for (let btcTxId in spvVaultData) {
1314
+ const state = spvVaultData[btcTxId];
1263
1315
  const knownSwap = knownSwaps[btcTxId];
1264
1316
  if (knownSwap != null) {
1265
1317
  if (knownSwap instanceof SpvFromBTCSwap_1.SpvFromBTCSwap) {
@@ -1276,7 +1328,7 @@ class Swapper extends events_1.EventEmitter {
1276
1328
  }
1277
1329
  }
1278
1330
  const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && state.owner.toLowerCase() === val.getAddress(chainId).toLowerCase());
1279
- const swap = await wrappers[SwapType_1.SwapType.SPV_VAULT_FROM_BTC].recoverFromState(state, vaultsData[state.owner]?.[state.vaultId.toString(10)], lp);
1331
+ const swap = await wrappers[SwapType_1.SwapType.SPV_VAULT_FROM_BTC].recoverFromState(state, contractVersion, vaultsData[state.owner]?.[state.vaultId.toString(10)], lp);
1280
1332
  if (swap != null) {
1281
1333
  recoveredSwaps.push(swap);
1282
1334
  }
@@ -337,7 +337,8 @@ class SwapperUtils {
337
337
  async getSpendableBalance(wallet, token, options) {
338
338
  if (this.root._chains[token.chainId] == null)
339
339
  throw new Error("Invalid chain identifier! Unknown chain: " + token.chainId);
340
- const { swapContract, chainInterface } = this.root._chains[token.chainId];
340
+ const { defaultVersion, versionedContracts, chainInterface } = this.root._chains[token.chainId];
341
+ const { swapContract } = versionedContracts[defaultVersion];
341
342
  let signer;
342
343
  if (typeof (wallet) === "string") {
343
344
  signer = wallet;
@@ -25,6 +25,7 @@ export type ISwapInit = {
25
25
  swapFee: bigint;
26
26
  swapFeeBtc: bigint;
27
27
  exactIn: boolean;
28
+ contractVersion: string;
28
29
  };
29
30
  /**
30
31
  * Type guard to check if an object is an ISwapInit
@@ -119,6 +120,10 @@ export declare abstract class ISwap<T extends ChainType = ChainType, D extends S
119
120
  * @internal
120
121
  */
121
122
  _persisted: boolean;
123
+ /**
124
+ * @internal
125
+ */
126
+ _contractVersion?: string;
122
127
  /**
123
128
  * Event emitter emitting `"swapState"` event when swap's state changes
124
129
  */
@@ -73,6 +73,7 @@ class ISwap {
73
73
  this.version = this.currentVersion;
74
74
  this.createdAt = Date.now();
75
75
  this._randomNonce = (0, Utils_1.randomBytes)(16).toString("hex");
76
+ this._contractVersion = swapInitOrObj.contractVersion;
76
77
  }
77
78
  else {
78
79
  this.expiry = swapInitOrObj.expiry;
@@ -97,6 +98,7 @@ class ISwap {
97
98
  this.exactIn = swapInitOrObj.exactIn;
98
99
  this.createdAt = swapInitOrObj.createdAt ?? swapInitOrObj.expiry;
99
100
  this._randomNonce = swapInitOrObj.randomNonce;
101
+ this._contractVersion = swapInitOrObj.contractVersion;
100
102
  }
101
103
  if (this.version !== this.currentVersion) {
102
104
  this.upgradeVersion();
@@ -323,7 +325,8 @@ class ISwap {
323
325
  initiated: this.initiated,
324
326
  exactIn: this.exactIn,
325
327
  createdAt: this.createdAt,
326
- randomNonce: this._randomNonce
328
+ randomNonce: this._randomNonce,
329
+ contractVersion: this._contractVersion
327
330
  };
328
331
  }
329
332
  //////////////////////////////