@atomiqlabs/sdk 8.5.0 → 8.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/storage/UnifiedSwapStorage.js +13 -8
- package/dist/swapper/Swapper.d.ts +10 -15
- package/dist/swapper/Swapper.js +26 -26
- package/dist/swapper/SwapperFactory.d.ts +1 -0
- package/dist/swapper/SwapperFactory.js +9 -4
- package/dist/swaps/ISwap.d.ts +8 -0
- package/dist/swaps/ISwap.js +10 -2
- package/dist/swaps/ISwapWrapper.d.ts +23 -1
- package/dist/swaps/ISwapWrapper.js +88 -28
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +4 -1
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +2 -2
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +1 -1
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +2 -2
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +16 -6
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +8 -2
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +1 -1
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +14 -4
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +12 -9
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +2 -1
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +7 -5
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +8 -2
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +1 -1
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +0 -6
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +8 -3
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +8 -2
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +3 -0
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +14 -4
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +1 -3
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +0 -1
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +0 -1
- package/package.json +1 -1
- package/src/storage/UnifiedSwapStorage.ts +13 -8
- package/src/swapper/Swapper.ts +37 -30
- package/src/swapper/SwapperFactory.ts +12 -6
- package/src/swaps/ISwap.ts +11 -3
- package/src/swaps/ISwapWrapper.ts +104 -28
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +5 -1
- package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +3 -3
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +17 -8
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +8 -3
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +13 -5
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +9 -8
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +9 -5
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -2
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1 -1
- package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +0 -3
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -3
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +7 -2
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +19 -4
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1 -3
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +0 -1
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +0 -1
package/src/swapper/Swapper.ts
CHANGED
|
@@ -152,9 +152,15 @@ export type SwapperOptions = {
|
|
|
152
152
|
*/
|
|
153
153
|
dontFetchLPs?: boolean,
|
|
154
154
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
155
|
+
* Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
|
|
156
|
+
* `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
|
|
157
|
+
* want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
|
|
158
|
+
*
|
|
159
|
+
* Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
|
|
160
|
+
* `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
|
|
161
|
+
* might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
|
|
162
|
+
* frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
|
|
163
|
+
* necessitating the saving of the swap data to the persistent storage until it is actually initiated.
|
|
158
164
|
*/
|
|
159
165
|
saveUninitializedSwaps?: boolean,
|
|
160
166
|
/**
|
|
@@ -310,7 +316,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
310
316
|
bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
|
|
311
317
|
chainsData: CtorMultiChainData<T>,
|
|
312
318
|
pricing: ISwapPrice<T>,
|
|
313
|
-
tokens:
|
|
319
|
+
tokens: SCToken[],
|
|
314
320
|
messenger: Messenger,
|
|
315
321
|
options?: SwapperOptions
|
|
316
322
|
) {
|
|
@@ -318,6 +324,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
318
324
|
const storagePrefix = options?.storagePrefix ?? "atomiq-";
|
|
319
325
|
|
|
320
326
|
options ??= {};
|
|
327
|
+
options.saveUninitializedSwaps ??= true;
|
|
321
328
|
options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
|
|
322
329
|
const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
|
|
323
330
|
|
|
@@ -340,22 +347,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
340
347
|
this._tokens = {};
|
|
341
348
|
this._tokensByTicker = {};
|
|
342
349
|
for(let tokenData of tokens) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
this._tokens[chainId][chainData.address] = this._tokensByTicker[chainId][tokenData.ticker] = {
|
|
348
|
-
chain: "SC",
|
|
349
|
-
chainId,
|
|
350
|
-
ticker: tokenData.ticker,
|
|
351
|
-
name: tokenData.name,
|
|
352
|
-
decimals: chainData.decimals,
|
|
353
|
-
displayDecimals: chainData.displayDecimals,
|
|
354
|
-
address: chainData.address,
|
|
355
|
-
equals: (other: Token) => other.chainId===chainId && other.ticker===tokenData.ticker && other.address===chainData.address,
|
|
356
|
-
toString: () => `${chainId}-${tokenData.ticker}`
|
|
357
|
-
}
|
|
358
|
-
}
|
|
350
|
+
const chainId = tokenData.chainId;
|
|
351
|
+
this._tokens[chainId] ??= {};
|
|
352
|
+
this._tokensByTicker[chainId] ??= {};
|
|
353
|
+
this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
|
|
359
354
|
}
|
|
360
355
|
|
|
361
356
|
this.swapStateListener = (swap: ISwap) => {
|
|
@@ -388,6 +383,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
388
383
|
{
|
|
389
384
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
390
385
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
386
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
391
387
|
}
|
|
392
388
|
);
|
|
393
389
|
wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
|
|
@@ -403,6 +399,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
403
399
|
{
|
|
404
400
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
405
401
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
402
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
406
403
|
bitcoinNetwork: this._btcNetwork
|
|
407
404
|
}
|
|
408
405
|
);
|
|
@@ -419,6 +416,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
419
416
|
{
|
|
420
417
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
421
418
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
419
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
422
420
|
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
423
421
|
}
|
|
424
422
|
);
|
|
@@ -437,6 +435,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
437
435
|
{
|
|
438
436
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
439
437
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
438
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
440
439
|
bitcoinNetwork: this._btcNetwork
|
|
441
440
|
}
|
|
442
441
|
);
|
|
@@ -449,7 +448,8 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
449
448
|
this._tokens[chainId],
|
|
450
449
|
{
|
|
451
450
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
452
|
-
postRequestTimeout: this.options.postRequestTimeout
|
|
451
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
452
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
453
453
|
}
|
|
454
454
|
);
|
|
455
455
|
wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
|
|
@@ -463,6 +463,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
463
463
|
{
|
|
464
464
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
465
465
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
466
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
466
467
|
bitcoinNetwork: this._btcNetwork
|
|
467
468
|
}
|
|
468
469
|
);
|
|
@@ -483,6 +484,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
483
484
|
{
|
|
484
485
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
485
486
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
487
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
486
488
|
bitcoinNetwork: this._btcNetwork
|
|
487
489
|
}
|
|
488
490
|
);
|
|
@@ -503,6 +505,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
503
505
|
{
|
|
504
506
|
getRequestTimeout: this.options.getRequestTimeout,
|
|
505
507
|
postRequestTimeout: this.options.postRequestTimeout,
|
|
508
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
506
509
|
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
507
510
|
}
|
|
508
511
|
);
|
|
@@ -652,7 +655,10 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
652
655
|
*/
|
|
653
656
|
async init(): Promise<void> {
|
|
654
657
|
if(this.initialized) return;
|
|
655
|
-
if(this.initPromise!=null)
|
|
658
|
+
if(this.initPromise!=null) {
|
|
659
|
+
await this.initPromise;
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
656
662
|
|
|
657
663
|
try {
|
|
658
664
|
const promise = this._init();
|
|
@@ -849,10 +855,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
849
855
|
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
850
856
|
|
|
851
857
|
const quote = quotes[0].quote;
|
|
852
|
-
|
|
853
|
-
quote._setInitiated();
|
|
854
|
-
await quote._save();
|
|
855
|
-
}
|
|
858
|
+
await quote._save();
|
|
856
859
|
return quote;
|
|
857
860
|
} catch (e) {
|
|
858
861
|
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
@@ -1327,13 +1330,15 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
1327
1330
|
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1328
1331
|
* @throws {Error} If no trusted intermediary specified
|
|
1329
1332
|
*/
|
|
1330
|
-
createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
|
|
1333
|
+
async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
|
|
1331
1334
|
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1332
1335
|
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1333
1336
|
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1334
1337
|
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1335
1338
|
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1336
|
-
|
|
1339
|
+
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
|
|
1340
|
+
await swap._save();
|
|
1341
|
+
return swap;
|
|
1337
1342
|
}
|
|
1338
1343
|
|
|
1339
1344
|
/**
|
|
@@ -1346,7 +1351,7 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
1346
1351
|
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1347
1352
|
* @throws {Error} If no trusted intermediary specified
|
|
1348
1353
|
*/
|
|
1349
|
-
createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
|
|
1354
|
+
async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
|
|
1350
1355
|
chainIdentifier: C, recipient: string,
|
|
1351
1356
|
amount: bigint, refundAddress?: string,
|
|
1352
1357
|
trustedIntermediaryOrUrl?: Intermediary | string
|
|
@@ -1356,7 +1361,9 @@ export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
|
1356
1361
|
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1357
1362
|
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1358
1363
|
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1359
|
-
|
|
1364
|
+
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
|
|
1365
|
+
await swap._save();
|
|
1366
|
+
return swap;
|
|
1360
1367
|
}
|
|
1361
1368
|
|
|
1362
1369
|
/**
|
|
@@ -9,7 +9,7 @@ import {SmartChainAssets, SmartChainAssetTickers} from "../SmartChainAssets";
|
|
|
9
9
|
import {NostrMessenger} from "@atomiqlabs/messenger-nostr";
|
|
10
10
|
import {Swapper, SwapperOptions} from "./Swapper";
|
|
11
11
|
import {CustomPriceProvider} from "../prices/providers/CustomPriceProvider";
|
|
12
|
-
import {BitcoinTokens, BtcToken, SCToken} from "../types/Token";
|
|
12
|
+
import {BitcoinTokens, BtcToken, SCToken, Token} from "../types/Token";
|
|
13
13
|
import {SwapType} from "../enums/SwapType";
|
|
14
14
|
import {SwapTypeMapping} from "../utils/SwapUtils";
|
|
15
15
|
import {RedundantSwapPrice, RedundantSwapPriceAssets} from "../prices/RedundantSwapPrice";
|
|
@@ -191,6 +191,8 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
|
|
|
191
191
|
*/
|
|
192
192
|
TokenResolver: TypedTokenResolvers<T> = {} as any;
|
|
193
193
|
|
|
194
|
+
private smartChainTokens: SCToken[] = [];
|
|
195
|
+
|
|
194
196
|
constructor(readonly initializers: T) {
|
|
195
197
|
this.initializers = initializers;
|
|
196
198
|
initializers.forEach(initializer => {
|
|
@@ -200,15 +202,19 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
|
|
|
200
202
|
|
|
201
203
|
for(let ticker in initializer.tokens) {
|
|
202
204
|
const assetData = initializer.tokens[ticker] as any;
|
|
203
|
-
|
|
205
|
+
const token: SCToken = {
|
|
204
206
|
chain: "SC",
|
|
205
207
|
chainId: initializer.chainId,
|
|
206
|
-
|
|
208
|
+
ticker,
|
|
207
209
|
name: SmartChainAssets[ticker as SmartChainAssetTickers]?.name ?? ticker,
|
|
208
210
|
decimals: assetData.decimals,
|
|
209
211
|
displayDecimals: assetData.displayDecimals,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
address: assetData.address,
|
|
213
|
+
equals: (other: Token) => other.chainId===initializer.chainId && other.ticker===ticker && other.address===assetData.address,
|
|
214
|
+
toString: () => `${initializer.chainId}-${ticker}`
|
|
215
|
+
};
|
|
216
|
+
this.smartChainTokens.push(token);
|
|
217
|
+
tokens[ticker] = addressMap[assetData.address] = token;
|
|
212
218
|
}
|
|
213
219
|
|
|
214
220
|
this.TokenResolver[initializer.chainId as keyof TypedTokenResolvers<T>] = {
|
|
@@ -280,7 +286,7 @@ export class SwapperFactory<T extends readonly ChainInitializer<any, ChainType,
|
|
|
280
286
|
(btcRelay: BtcRelay<any, any, any>) => new MempoolBtcRelaySynchronizer(btcRelay, bitcoinRpc),
|
|
281
287
|
chains as any,
|
|
282
288
|
swapPricing,
|
|
283
|
-
|
|
289
|
+
this.smartChainTokens,
|
|
284
290
|
options.messenger,
|
|
285
291
|
options
|
|
286
292
|
);
|
package/src/swaps/ISwap.ts
CHANGED
|
@@ -132,6 +132,14 @@ export abstract class ISwap<
|
|
|
132
132
|
* @internal
|
|
133
133
|
*/
|
|
134
134
|
_randomNonce: string;
|
|
135
|
+
/**
|
|
136
|
+
* Whether the swap is saved in the persistent storage or not.
|
|
137
|
+
*
|
|
138
|
+
* @remarks This field itself is not persisted but is instead derived during runtime
|
|
139
|
+
*
|
|
140
|
+
* @internal
|
|
141
|
+
*/
|
|
142
|
+
_persisted: boolean = false;
|
|
135
143
|
|
|
136
144
|
|
|
137
145
|
/**
|
|
@@ -229,9 +237,9 @@ export abstract class ISwap<
|
|
|
229
237
|
//TODO: This doesn't hold strong reference to the swap, hence if no other strong reference to the
|
|
230
238
|
// swap exists, it will just never resolve!
|
|
231
239
|
return new Promise((resolve, reject) => {
|
|
232
|
-
let listener: (
|
|
233
|
-
listener = (
|
|
234
|
-
if(type==="eq" ?
|
|
240
|
+
let listener: () => void;
|
|
241
|
+
listener = () => {
|
|
242
|
+
if(type==="eq" ? this._state===targetState : type==="gte" ? this._state>=targetState : this._state!=targetState) {
|
|
235
243
|
resolve();
|
|
236
244
|
this.events.removeListener("swapState", listener);
|
|
237
245
|
}
|
|
@@ -13,6 +13,9 @@ import {PriceInfoType} from "../types/PriceInfoType";
|
|
|
13
13
|
import {fromHumanReadableString} from "../utils/TokenUtils";
|
|
14
14
|
import {UserError} from "../errors/UserError";
|
|
15
15
|
|
|
16
|
+
export const DEFAULT_MAX_PARALLEL_SWAP_TICKS = 50;
|
|
17
|
+
export const DEFAULT_MAX_PARALLEL_SWAP_SYNCS = 50;
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
20
|
* Options for swap wrapper configuration
|
|
18
21
|
*
|
|
@@ -20,7 +23,19 @@ import {UserError} from "../errors/UserError";
|
|
|
20
23
|
*/
|
|
21
24
|
export type ISwapWrapperOptions = {
|
|
22
25
|
getRequestTimeout?: number,
|
|
23
|
-
postRequestTimeout?: number
|
|
26
|
+
postRequestTimeout?: number,
|
|
27
|
+
/**
|
|
28
|
+
* How many swaps to call `_tick()` for in parallel
|
|
29
|
+
*/
|
|
30
|
+
maxParallelSwapTicks?: number,
|
|
31
|
+
/**
|
|
32
|
+
* How many swaps to call `_sync()` for in parallel
|
|
33
|
+
*/
|
|
34
|
+
maxParallelSwapSyncs?: number,
|
|
35
|
+
/**
|
|
36
|
+
* Whether to save swaps that are not initialized into the persistent storage
|
|
37
|
+
*/
|
|
38
|
+
saveUninitializedSwaps?: boolean
|
|
24
39
|
};
|
|
25
40
|
|
|
26
41
|
/**
|
|
@@ -101,6 +116,11 @@ export abstract class ISwapWrapper<
|
|
|
101
116
|
* @internal
|
|
102
117
|
*/
|
|
103
118
|
protected tickInterval?: NodeJS.Timeout;
|
|
119
|
+
/**
|
|
120
|
+
* An internal abort controller for the running tick handler
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
protected tickAbortController?: AbortController;
|
|
104
124
|
|
|
105
125
|
|
|
106
126
|
/**
|
|
@@ -152,6 +172,11 @@ export abstract class ISwapWrapper<
|
|
|
152
172
|
options: O,
|
|
153
173
|
events?: EventEmitter<{swapState: [ISwap]}>
|
|
154
174
|
) {
|
|
175
|
+
if(options?.maxParallelSwapTicks!=null && options.maxParallelSwapTicks < 1)
|
|
176
|
+
throw new Error("maxParallelSwapTicks must be at least 1!");
|
|
177
|
+
if(options?.maxParallelSwapSyncs!=null && options.maxParallelSwapSyncs < 1)
|
|
178
|
+
throw new Error("maxParallelSwapSyncs must be at least 1!");
|
|
179
|
+
|
|
155
180
|
this.unifiedStorage = unifiedStorage;
|
|
156
181
|
this.unifiedChainEvents = unifiedChainEvents;
|
|
157
182
|
|
|
@@ -274,9 +299,20 @@ export abstract class ISwapWrapper<
|
|
|
274
299
|
*/
|
|
275
300
|
protected startTickInterval(): void {
|
|
276
301
|
if(this.tickSwapState==null || this.tickSwapState.length===0) return;
|
|
277
|
-
this.
|
|
278
|
-
|
|
279
|
-
|
|
302
|
+
if(this.tickAbortController!=null) this.tickAbortController.abort("New tick interval has been started!");
|
|
303
|
+
const abortController = this.tickAbortController = new AbortController();
|
|
304
|
+
let run: () => Promise<void>;
|
|
305
|
+
run = async () => {
|
|
306
|
+
if(!this.isInitialized) return;
|
|
307
|
+
await this.tick(undefined, abortController.signal).catch(e => {
|
|
308
|
+
if(abortController.signal.aborted) return;
|
|
309
|
+
this.logger.warn("startTickInterval(): Tick on swaps failed, error: ", e);
|
|
310
|
+
});
|
|
311
|
+
if(abortController.signal.aborted) return;
|
|
312
|
+
if(!this.isInitialized) return;
|
|
313
|
+
this.tickInterval = setTimeout(run, 1000);
|
|
314
|
+
}
|
|
315
|
+
run();
|
|
280
316
|
}
|
|
281
317
|
|
|
282
318
|
/**
|
|
@@ -343,11 +379,11 @@ export abstract class ISwapWrapper<
|
|
|
343
379
|
|
|
344
380
|
if(this.processEvent!=null) this.unifiedChainEvents.registerListener(this.TYPE, this.processEvent.bind(this), this._swapDeserializer.bind(null, this));
|
|
345
381
|
|
|
382
|
+
this.isInitialized = true;
|
|
383
|
+
|
|
346
384
|
if(!noTimers) this.startTickInterval();
|
|
347
385
|
|
|
348
386
|
// this.logger.info("init(): Swap wrapper initialized");
|
|
349
|
-
|
|
350
|
-
this.isInitialized = true;
|
|
351
387
|
}
|
|
352
388
|
|
|
353
389
|
/**
|
|
@@ -357,12 +393,21 @@ export abstract class ISwapWrapper<
|
|
|
357
393
|
this.isInitialized = false;
|
|
358
394
|
this.unifiedChainEvents.unregisterListener(this.TYPE);
|
|
359
395
|
this.logger.info("stop(): Swap wrapper stopped");
|
|
360
|
-
if(this.tickInterval!=null)
|
|
396
|
+
if(this.tickInterval!=null) {
|
|
397
|
+
clearTimeout(this.tickInterval);
|
|
398
|
+
delete this.tickInterval;
|
|
399
|
+
}
|
|
400
|
+
if(this.tickAbortController!=null) {
|
|
401
|
+
this.tickAbortController.abort("Wrapper instance stopped!");
|
|
402
|
+
delete this.tickAbortController;
|
|
403
|
+
}
|
|
361
404
|
}
|
|
362
405
|
|
|
363
406
|
/**
|
|
364
407
|
* Runs checks on all the known pending swaps, syncing their state from on-chain data
|
|
365
408
|
*
|
|
409
|
+
* @remarks Doesn't work properly if you pass non-persisted swaps
|
|
410
|
+
*
|
|
366
411
|
* @param pastSwaps Optional array of past swaps to check, otherwise all relevant swaps will be fetched
|
|
367
412
|
* from the persistent storage
|
|
368
413
|
* @param noSave Whether to skip saving the swap changes in the persistent storage
|
|
@@ -373,18 +418,25 @@ export abstract class ISwapWrapper<
|
|
|
373
418
|
(val: any) => new this._swapDeserializer(this, val)
|
|
374
419
|
);
|
|
375
420
|
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
changedSwaps
|
|
382
|
-
|
|
421
|
+
const maxParallelSyncs = this._options.maxParallelSwapSyncs ?? DEFAULT_MAX_PARALLEL_SWAP_SYNCS;
|
|
422
|
+
|
|
423
|
+
const totalRemoveSwaps: D["Swap"][] = [];
|
|
424
|
+
const totalChangedSwaps: D["Swap"][] = [];
|
|
425
|
+
for(let i=0; i<pastSwaps.length; i+=maxParallelSyncs) {
|
|
426
|
+
const {removeSwaps, changedSwaps} = await this._checkPastSwaps(pastSwaps.slice(i, i+maxParallelSyncs));
|
|
427
|
+
if (!noSave) {
|
|
428
|
+
await this.unifiedStorage.removeAll(removeSwaps);
|
|
429
|
+
await this.unifiedStorage.saveAll(changedSwaps);
|
|
430
|
+
changedSwaps.forEach(swap => swap._emitEvent());
|
|
431
|
+
removeSwaps.forEach(swap => swap._emitEvent());
|
|
432
|
+
}
|
|
433
|
+
totalRemoveSwaps.push(...removeSwaps);
|
|
434
|
+
totalChangedSwaps.push(...changedSwaps);
|
|
383
435
|
}
|
|
384
436
|
|
|
385
437
|
return {
|
|
386
|
-
removeSwaps,
|
|
387
|
-
changedSwaps
|
|
438
|
+
removeSwaps: totalRemoveSwaps,
|
|
439
|
+
changedSwaps: totalChangedSwaps
|
|
388
440
|
}
|
|
389
441
|
}
|
|
390
442
|
|
|
@@ -393,21 +445,43 @@ export abstract class ISwapWrapper<
|
|
|
393
445
|
*
|
|
394
446
|
* @param swaps Optional array of swaps to invoke `_tick()` on, otherwise all relevant swaps will be fetched
|
|
395
447
|
* from the persistent storage
|
|
448
|
+
* @param abortSignal Abort signal
|
|
396
449
|
*/
|
|
397
|
-
public async tick(swaps?: D["Swap"][]): Promise<void> {
|
|
450
|
+
public async tick(swaps?: D["Swap"][], abortSignal?: AbortSignal): Promise<void> {
|
|
398
451
|
if(swaps==null) swaps = await this.unifiedStorage.query<D["Swap"]>(
|
|
399
452
|
[[{key: "type", value: this.TYPE}, {key: "state", value: this.tickSwapState}]],
|
|
400
453
|
(val: any) => new this._swapDeserializer(this, val)
|
|
401
454
|
);
|
|
455
|
+
abortSignal?.throwIfAborted();
|
|
402
456
|
|
|
457
|
+
const parallelTicks = this._options.maxParallelSwapTicks ?? DEFAULT_MAX_PARALLEL_SWAP_TICKS;
|
|
458
|
+
|
|
459
|
+
let promises: Promise<any>[] = [];
|
|
403
460
|
for(let pendingSwap of this.pendingSwaps.values()) {
|
|
404
461
|
const value = pendingSwap.deref();
|
|
405
|
-
if(value != null) value._tick(true)
|
|
462
|
+
if(value != null) promises.push(value._tick(true).catch(e => {
|
|
463
|
+
this.logger.warn(`tick(): Error ticking swap ${value.getId()}: `, e);
|
|
464
|
+
}));
|
|
465
|
+
if(promises.length >= parallelTicks) {
|
|
466
|
+
await Promise.all(promises);
|
|
467
|
+
abortSignal?.throwIfAborted();
|
|
468
|
+
promises = [];
|
|
469
|
+
}
|
|
406
470
|
}
|
|
407
471
|
|
|
408
|
-
|
|
409
|
-
value._tick(true)
|
|
410
|
-
|
|
472
|
+
for(let value of swaps) {
|
|
473
|
+
promises.push(value._tick(true).catch(e => {
|
|
474
|
+
this.logger.warn(`tick(): Error ticking swap ${value.getId()}: `, e);
|
|
475
|
+
}));
|
|
476
|
+
if(promises.length >= parallelTicks) {
|
|
477
|
+
await Promise.all(promises);
|
|
478
|
+
abortSignal?.throwIfAborted();
|
|
479
|
+
promises = [];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if(promises.length>0) await Promise.all(promises);
|
|
484
|
+
abortSignal?.throwIfAborted();
|
|
411
485
|
}
|
|
412
486
|
|
|
413
487
|
/**
|
|
@@ -426,12 +500,14 @@ export abstract class ISwapWrapper<
|
|
|
426
500
|
* @internal
|
|
427
501
|
*/
|
|
428
502
|
_saveSwapData(swap: D["Swap"]): Promise<void> {
|
|
429
|
-
if(!
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
503
|
+
if(!this._options.saveUninitializedSwaps) {
|
|
504
|
+
if(!swap.isInitiated()) {
|
|
505
|
+
this.logger.debug("saveSwapData(): Swap "+swap.getId()+" not initiated, saving to pending swaps");
|
|
506
|
+
this.pendingSwaps.set(swap.getId(), new WeakRef<D["Swap"]>(swap));
|
|
507
|
+
return Promise.resolve();
|
|
508
|
+
} else {
|
|
509
|
+
this.pendingSwaps.delete(swap.getId());
|
|
510
|
+
}
|
|
435
511
|
}
|
|
436
512
|
return this.unifiedStorage.save(swap);
|
|
437
513
|
}
|
|
@@ -445,7 +521,7 @@ export abstract class ISwapWrapper<
|
|
|
445
521
|
*/
|
|
446
522
|
_removeSwapData(swap: D["Swap"]): Promise<void> {
|
|
447
523
|
this.pendingSwaps.delete(swap.getId());
|
|
448
|
-
if(!swap.
|
|
524
|
+
if(!swap._persisted) return Promise.resolve();
|
|
449
525
|
return this.unifiedStorage.remove(swap);
|
|
450
526
|
}
|
|
451
527
|
|
|
@@ -66,7 +66,11 @@ export abstract class IEscrowSwapWrapper<
|
|
|
66
66
|
* @internal
|
|
67
67
|
*/
|
|
68
68
|
protected preFetchSignData(signDataPrefetch: Promise<any | null>): Promise<T["PreFetchVerification"] | undefined> {
|
|
69
|
-
if(this._contract.preFetchForInitSignatureVerification==null)
|
|
69
|
+
if(this._contract.preFetchForInitSignatureVerification==null) {
|
|
70
|
+
// Catch promise rejections, should they happen
|
|
71
|
+
signDataPrefetch.catch(() => {});
|
|
72
|
+
return Promise.resolve(undefined);
|
|
73
|
+
}
|
|
70
74
|
return signDataPrefetch.then(obj => {
|
|
71
75
|
if(obj==null) return undefined;
|
|
72
76
|
return this._contract.preFetchForInitSignatureVerification!(obj);
|
|
@@ -71,7 +71,7 @@ export abstract class IFromBTCSelfInitSwap<
|
|
|
71
71
|
* Returns if the swap can be committed
|
|
72
72
|
* @internal
|
|
73
73
|
*/
|
|
74
|
-
protected abstract canCommit(): boolean;
|
|
74
|
+
protected abstract canCommit(skipQuoteExpiryChecks?: boolean): boolean;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* @inheritDoc
|
|
@@ -239,7 +239,7 @@ export abstract class IFromBTCSelfInitSwap<
|
|
|
239
239
|
* @throws {Error} When in invalid state to commit the swap
|
|
240
240
|
*/
|
|
241
241
|
async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
|
|
242
|
-
if(!this.canCommit()) throw new Error("Must be in CREATED state!");
|
|
242
|
+
if(!this.canCommit(skipChecks)) throw new Error("Must be in CREATED state!");
|
|
243
243
|
if(this._data==null || this.signatureData==null) throw new Error("data or signature data is null, invalid state?");
|
|
244
244
|
|
|
245
245
|
if(!this.initiated) {
|
|
@@ -285,7 +285,7 @@ export abstract class IFromBTCSelfInitSwap<
|
|
|
285
285
|
/**
|
|
286
286
|
* @inheritDoc
|
|
287
287
|
*/
|
|
288
|
-
abstract txsClaim(signer?: T["Signer"]): Promise<T["TX"][]>;
|
|
288
|
+
abstract txsClaim(signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]>;
|
|
289
289
|
|
|
290
290
|
/**
|
|
291
291
|
* @inheritDoc
|
|
@@ -282,8 +282,8 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
|
282
282
|
* @inheritDoc
|
|
283
283
|
* @internal
|
|
284
284
|
*/
|
|
285
|
-
protected canCommit(): boolean {
|
|
286
|
-
return this._state===FromBTCLNSwapState.PR_PAID;
|
|
285
|
+
protected canCommit(skipQuoteExpiryChecks?: boolean): boolean {
|
|
286
|
+
return this._state===FromBTCLNSwapState.PR_PAID || (!!skipQuoteExpiryChecks && this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
/**
|
|
@@ -899,7 +899,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
|
899
899
|
);
|
|
900
900
|
|
|
901
901
|
this._commitTxId = result[result.length-1];
|
|
902
|
-
if(this._state===FromBTCLNSwapState.PR_PAID || this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
902
|
+
if(this._state===FromBTCLNSwapState.PR_PAID || this._state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this._state===FromBTCLNSwapState.QUOTE_EXPIRED) {
|
|
903
903
|
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
904
904
|
}
|
|
905
905
|
return this._commitTxId;
|
|
@@ -953,7 +953,18 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
|
953
953
|
*
|
|
954
954
|
* @internal
|
|
955
955
|
*/
|
|
956
|
-
private async _txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
|
|
956
|
+
private async _txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
|
|
957
|
+
let address: string | undefined = undefined;
|
|
958
|
+
if(_signer!=null) {
|
|
959
|
+
if (typeof (_signer) === "string") {
|
|
960
|
+
address = _signer;
|
|
961
|
+
} else if (isAbstractSigner(_signer)) {
|
|
962
|
+
address = _signer.getAddress();
|
|
963
|
+
} else {
|
|
964
|
+
address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
957
968
|
if(this._data==null) throw new Error("Unknown data, wrong state?");
|
|
958
969
|
const useSecret = secret ?? this.secret;
|
|
959
970
|
if(useSecret==null)
|
|
@@ -962,9 +973,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
|
962
973
|
throw new Error("Invalid swap secret pre-image provided!");
|
|
963
974
|
|
|
964
975
|
return this.wrapper._contract.txsClaimWithSecret(
|
|
965
|
-
|
|
966
|
-
this._getInitiator() :
|
|
967
|
-
(isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer)),
|
|
976
|
+
address ?? this._getInitiator(),
|
|
968
977
|
this._data, useSecret, true, true
|
|
969
978
|
);
|
|
970
979
|
}
|
|
@@ -978,7 +987,7 @@ export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
|
978
987
|
*
|
|
979
988
|
* @throws {Error} If in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
|
|
980
989
|
*/
|
|
981
|
-
async txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
|
|
990
|
+
async txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
|
|
982
991
|
if(this._state!==FromBTCLNSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state!");
|
|
983
992
|
return this._txsClaim(_signer, secret);
|
|
984
993
|
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
ChainSwapType,
|
|
5
5
|
ChainType,
|
|
6
6
|
ClaimEvent,
|
|
7
|
-
InitializeEvent, LightningNetworkApi,
|
|
7
|
+
InitializeEvent, LightningNetworkApi, LNNodeLiquidity,
|
|
8
8
|
RefundEvent, SwapCommitState, SwapCommitStateType
|
|
9
9
|
} from "@atomiqlabs/base";
|
|
10
10
|
import {Intermediary} from "../../../../intermediaries/Intermediary";
|
|
@@ -137,6 +137,7 @@ export class FromBTCLNWrapper<
|
|
|
137
137
|
super(
|
|
138
138
|
chainIdentifier, unifiedStorage, unifiedChainEvents, chain, contract, prices, tokens, swapDataDeserializer, lnApi,
|
|
139
139
|
{
|
|
140
|
+
...options,
|
|
140
141
|
safetyFactor: options?.safetyFactor ?? 2,
|
|
141
142
|
bitcoinBlocktime: options?.bitcoinBlocktime ?? 10*60,
|
|
142
143
|
unsafeSkipLnNodeCheck: options?.unsafeSkipLnNodeCheck ?? false
|
|
@@ -317,8 +318,13 @@ export class FromBTCLNWrapper<
|
|
|
317
318
|
this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
|
|
318
319
|
);
|
|
319
320
|
|
|
321
|
+
let lnCapacityPromise: Promise<LNNodeLiquidity | null> | undefined;
|
|
322
|
+
if(!_options.unsafeSkipLnNodeCheck) {
|
|
323
|
+
lnCapacityPromise = this.preFetchLnCapacity(lnPublicKey);
|
|
324
|
+
} else lnPublicKey.catch(() => {});
|
|
325
|
+
|
|
320
326
|
return {
|
|
321
|
-
lnCapacityPromise
|
|
327
|
+
lnCapacityPromise,
|
|
322
328
|
resp: await response
|
|
323
329
|
};
|
|
324
330
|
}, undefined, RequestError, abortController.signal);
|
|
@@ -356,7 +362,6 @@ export class FromBTCLNWrapper<
|
|
|
356
362
|
secret: secret?.toString("hex"),
|
|
357
363
|
exactIn: amountData.exactIn ?? true
|
|
358
364
|
} as FromBTCLNSwapInit<T["Data"]>);
|
|
359
|
-
await quote._save();
|
|
360
365
|
return quote;
|
|
361
366
|
} catch (e) {
|
|
362
367
|
abortController.abort(e);
|