@atomiqlabs/lp-lib 16.0.0 → 16.0.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.
- package/dist/swaps/SwapHandler.d.ts +0 -7
- package/dist/swaps/SwapHandler.js +9 -12
- package/dist/swaps/escrow/EscrowHandler.d.ts +0 -1
- package/dist/swaps/escrow/EscrowHandler.js +3 -12
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +7 -0
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +56 -14
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +2 -2
- package/package.json +1 -1
- package/src/swaps/SwapHandler.ts +2 -19
- package/src/swaps/escrow/EscrowHandler.ts +2 -10
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +56 -15
- package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +2 -2
|
@@ -106,13 +106,6 @@ export declare abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHan
|
|
|
106
106
|
* Returns data to be returned in swap handler info
|
|
107
107
|
*/
|
|
108
108
|
abstract getInfoData(): any;
|
|
109
|
-
/**
|
|
110
|
-
* Remove swap data
|
|
111
|
-
*
|
|
112
|
-
* @param hash
|
|
113
|
-
* @param sequence
|
|
114
|
-
*/
|
|
115
|
-
protected removeSwapData(hash: string, sequence: bigint): Promise<void>;
|
|
116
109
|
/**
|
|
117
110
|
* Remove swap data
|
|
118
111
|
*
|
|
@@ -67,18 +67,15 @@ class SwapHandler {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
swap
|
|
79
|
-
if (sequenceOrUltimateState != null && typeof (sequenceOrUltimateState) !== "bigint")
|
|
80
|
-
await swap.setState(sequenceOrUltimateState);
|
|
81
|
-
}
|
|
70
|
+
/**
|
|
71
|
+
* Remove swap data
|
|
72
|
+
*
|
|
73
|
+
* @param swap
|
|
74
|
+
* @param ultimateState set the ultimate state of the swap before removing
|
|
75
|
+
*/
|
|
76
|
+
async removeSwapData(swap, ultimateState) {
|
|
77
|
+
if (ultimateState != null)
|
|
78
|
+
await swap.setState(ultimateState);
|
|
82
79
|
if (swap != null)
|
|
83
80
|
await PluginManager_1.PluginManager.swapRemove(swap);
|
|
84
81
|
this.swapLogger.debug(swap, "removeSwapData(): removing swap final state: " + swap.state);
|
|
@@ -31,7 +31,6 @@ export declare abstract class EscrowHandler<V extends EscrowHandlerSwap<SwapData
|
|
|
31
31
|
*/
|
|
32
32
|
protected subscribeToEvents(): void;
|
|
33
33
|
protected loadData(ctor: new (data: any) => V): Promise<void>;
|
|
34
|
-
protected removeSwapData(hash: string, sequence: bigint): Promise<void>;
|
|
35
34
|
protected removeSwapData(swap: V, ultimateState?: S): Promise<void>;
|
|
36
35
|
protected saveSwapData(swap: V): Promise<void>;
|
|
37
36
|
protected saveSwapToEscrowHashMap(swap: V): void;
|
|
@@ -73,18 +73,9 @@ class EscrowHandler extends SwapHandler_1.SwapHandler {
|
|
|
73
73
|
this.saveSwapToEscrowHashMap(swap);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
async removeSwapData(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (typeof (sequenceOrUltimateState) !== "bigint")
|
|
80
|
-
throw new Error("Sequence must be a BN instance!");
|
|
81
|
-
swap = await this.storageManager.getData(hashOrSwap, sequenceOrUltimateState);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
swap = hashOrSwap;
|
|
85
|
-
if (sequenceOrUltimateState != null && typeof (sequenceOrUltimateState) !== "bigint")
|
|
86
|
-
await swap.setState(sequenceOrUltimateState);
|
|
87
|
-
}
|
|
76
|
+
async removeSwapData(swap, ultimateState) {
|
|
77
|
+
if (ultimateState != null)
|
|
78
|
+
await swap.setState(ultimateState);
|
|
88
79
|
if (swap != null)
|
|
89
80
|
await PluginManager_1.PluginManager.swapRemove(swap);
|
|
90
81
|
this.swapLogger.debug(swap, "removeSwapData(): removing swap final state: " + swap.state);
|
|
@@ -8,6 +8,10 @@ import { FromBtcBaseConfig, FromBtcBaseSwapHandler } from "../FromBtcBaseSwapHan
|
|
|
8
8
|
import { ILightningWallet } from "../../../wallets/ILightningWallet";
|
|
9
9
|
import { LightningAssertions } from "../../assertions/LightningAssertions";
|
|
10
10
|
export type FromBtcLnAutoConfig = FromBtcBaseConfig & {
|
|
11
|
+
maxInflightHtlcs?: number;
|
|
12
|
+
minNativeBalances?: {
|
|
13
|
+
[chainId: string]: bigint;
|
|
14
|
+
};
|
|
11
15
|
invoiceTimeoutSeconds?: number;
|
|
12
16
|
minCltv: bigint;
|
|
13
17
|
gracePeriod: bigint;
|
|
@@ -33,6 +37,7 @@ export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoS
|
|
|
33
37
|
readonly type = SwapHandlerType.FROM_BTCLN_AUTO;
|
|
34
38
|
readonly swapType = ChainSwapType.HTLC;
|
|
35
39
|
activeSubscriptions: Set<string>;
|
|
40
|
+
swapsWithInflighHtlcs: Set<FromBtcLnAutoSwap>;
|
|
36
41
|
readonly config: FromBtcLnAutoConfig;
|
|
37
42
|
readonly lightning: ILightningWallet;
|
|
38
43
|
readonly LightningAssertions: LightningAssertions;
|
|
@@ -105,7 +110,9 @@ export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoS
|
|
|
105
110
|
* @returns the fetched lightning invoice
|
|
106
111
|
*/
|
|
107
112
|
private checkInvoiceStatus;
|
|
113
|
+
private checkTooManyInflightHtlcs;
|
|
108
114
|
startRestServer(restServer: Express): void;
|
|
109
115
|
init(): Promise<void>;
|
|
110
116
|
getInfoData(): any;
|
|
117
|
+
protected removeSwapData(swap: FromBtcLnAutoSwap, ultimateState?: FromBtcLnAutoSwapState): Promise<void>;
|
|
111
118
|
}
|
|
@@ -20,6 +20,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
20
20
|
this.type = SwapHandler_1.SwapHandlerType.FROM_BTCLN_AUTO;
|
|
21
21
|
this.swapType = base_1.ChainSwapType.HTLC;
|
|
22
22
|
this.activeSubscriptions = new Set();
|
|
23
|
+
this.swapsWithInflighHtlcs = new Set();
|
|
23
24
|
this.config = config;
|
|
24
25
|
this.config.invoiceTimeoutSeconds = this.config.invoiceTimeoutSeconds || 90;
|
|
25
26
|
this.lightning = lightning;
|
|
@@ -55,13 +56,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
55
56
|
}
|
|
56
57
|
if (swap.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED) {
|
|
57
58
|
try {
|
|
58
|
-
|
|
59
|
-
//Expired
|
|
60
|
-
if (swap.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED) {
|
|
61
|
-
this.swapLogger.info(swap, "processPastSwap(state=RECEIVED): offer HTLC expired, cancelling invoice: " + swap.pr);
|
|
62
|
-
await this.cancelSwapAndInvoice(swap);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
59
|
+
await this.offerHtlc(swap);
|
|
65
60
|
}
|
|
66
61
|
catch (e) {
|
|
67
62
|
this.swapLogger.error(swap, "processPastSwap(state=RECEIVED): offerHtlc error", e);
|
|
@@ -69,6 +64,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
69
64
|
return null;
|
|
70
65
|
}
|
|
71
66
|
if (swap.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.TXS_SENT || swap.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.COMMITED) {
|
|
67
|
+
this.swapsWithInflighHtlcs.add(swap);
|
|
72
68
|
const onchainStatus = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
73
69
|
const state = swap.state;
|
|
74
70
|
if (onchainStatus.type === base_1.SwapCommitStateType.PAID) {
|
|
@@ -255,7 +251,6 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
255
251
|
if (invoiceData.metadata != null)
|
|
256
252
|
invoiceData.metadata.times.htlcReceived = Date.now();
|
|
257
253
|
const useToken = invoiceData.token;
|
|
258
|
-
const gasToken = invoiceData.gasToken;
|
|
259
254
|
let expiryTimeout;
|
|
260
255
|
try {
|
|
261
256
|
//Check if HTLC expiry is long enough
|
|
@@ -264,6 +259,8 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
264
259
|
invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
265
260
|
}
|
|
266
261
|
catch (e) {
|
|
262
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && invoiceData.metadata != null)
|
|
263
|
+
invoiceData.metadata.htlcReceiveError = e;
|
|
267
264
|
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.CREATED)
|
|
268
265
|
await this.cancelSwapAndInvoice(invoiceData);
|
|
269
266
|
throw e;
|
|
@@ -287,6 +284,16 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
287
284
|
async offerHtlc(invoiceData) {
|
|
288
285
|
if (invoiceData.state !== FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED)
|
|
289
286
|
return;
|
|
287
|
+
try {
|
|
288
|
+
this.checkTooManyInflightHtlcs();
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && invoiceData.metadata != null)
|
|
292
|
+
invoiceData.metadata.htlcOfferError = e;
|
|
293
|
+
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED)
|
|
294
|
+
await this.cancelSwapAndInvoice(invoiceData);
|
|
295
|
+
throw e;
|
|
296
|
+
}
|
|
290
297
|
this.swapLogger.debug(invoiceData, "offerHtlc(): invoice: ", invoiceData.pr);
|
|
291
298
|
if (invoiceData.metadata != null)
|
|
292
299
|
invoiceData.metadata.times.offerHtlc = Date.now();
|
|
@@ -295,11 +302,15 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
295
302
|
const { swapContract, signer, chainInterface } = this.getChain(invoiceData.chainIdentifier);
|
|
296
303
|
//Create abort controller for parallel fetches
|
|
297
304
|
const abortController = new AbortController();
|
|
305
|
+
//Minimum reserve of native token to be kept in the wallet
|
|
306
|
+
const minNativeTokenReserve = this.config.minNativeBalances?.[invoiceData.chainIdentifier] ?? 0n;
|
|
298
307
|
//Pre-fetch data
|
|
299
308
|
const balancePrefetch = this.getBalancePrefetch(invoiceData.chainIdentifier, useToken, abortController);
|
|
300
|
-
const gasTokenBalancePrefetch = invoiceData.getTotalOutputGasAmount() === 0n || useToken === gasToken ?
|
|
309
|
+
const gasTokenBalancePrefetch = (invoiceData.getTotalOutputGasAmount() === 0n && minNativeTokenReserve === 0n) || useToken === gasToken ?
|
|
301
310
|
null : this.getBalancePrefetch(invoiceData.chainIdentifier, gasToken, abortController);
|
|
302
311
|
if (await swapContract.getInitAuthorizationExpiry(invoiceData.data, invoiceData) < Date.now()) {
|
|
312
|
+
if (invoiceData.metadata != null)
|
|
313
|
+
invoiceData.metadata.htlcOfferError = "Init authorization expired, before being sent!";
|
|
303
314
|
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED) {
|
|
304
315
|
await this.cancelSwapAndInvoice(invoiceData);
|
|
305
316
|
}
|
|
@@ -308,17 +319,19 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
308
319
|
try {
|
|
309
320
|
//Check if we have enough liquidity to proceed
|
|
310
321
|
if (useToken === gasToken) {
|
|
311
|
-
await this.checkBalance(invoiceData.getTotalOutputAmount() + invoiceData.getTotalOutputGasAmount(), balancePrefetch, abortController.signal);
|
|
322
|
+
await this.checkBalance(invoiceData.getTotalOutputAmount() + invoiceData.getTotalOutputGasAmount() + minNativeTokenReserve, balancePrefetch, abortController.signal);
|
|
312
323
|
}
|
|
313
324
|
else {
|
|
314
325
|
await this.checkBalance(invoiceData.getTotalOutputAmount(), balancePrefetch, abortController.signal);
|
|
315
|
-
await this.checkBalance(invoiceData.getTotalOutputGasAmount(), gasTokenBalancePrefetch, abortController.signal);
|
|
326
|
+
await this.checkBalance(invoiceData.getTotalOutputGasAmount() + minNativeTokenReserve, gasTokenBalancePrefetch, abortController.signal);
|
|
316
327
|
}
|
|
317
328
|
if (invoiceData.metadata != null)
|
|
318
329
|
invoiceData.metadata.times.offerHtlcChecked = Date.now();
|
|
319
330
|
}
|
|
320
331
|
catch (e) {
|
|
321
332
|
if (!abortController.signal.aborted) {
|
|
333
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && invoiceData.metadata != null)
|
|
334
|
+
invoiceData.metadata.htlcOfferError = e;
|
|
322
335
|
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED)
|
|
323
336
|
await this.cancelSwapAndInvoice(invoiceData);
|
|
324
337
|
}
|
|
@@ -331,6 +344,19 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
331
344
|
signature: invoiceData.signature
|
|
332
345
|
}, true);
|
|
333
346
|
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED) {
|
|
347
|
+
//Re-check the current HTLC count
|
|
348
|
+
try {
|
|
349
|
+
this.checkTooManyInflightHtlcs();
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && invoiceData.metadata != null)
|
|
353
|
+
invoiceData.metadata.htlcOfferError = e;
|
|
354
|
+
if (invoiceData.state === FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED)
|
|
355
|
+
await this.cancelSwapAndInvoice(invoiceData);
|
|
356
|
+
throw e;
|
|
357
|
+
}
|
|
358
|
+
this.swapLogger.debug(invoiceData, `offerHtlc(): Sending HTLC offer, current in flight HTLCs: ${this.swapsWithInflighHtlcs.size}, invoice: `, invoiceData.pr);
|
|
359
|
+
this.swapsWithInflighHtlcs.add(invoiceData);
|
|
334
360
|
//Setting the state variable is done outside the promise, so is done synchronously
|
|
335
361
|
await invoiceData.setState(FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.TXS_SENT);
|
|
336
362
|
await this.saveSwapData(invoiceData);
|
|
@@ -471,6 +497,15 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
471
497
|
return invoice;
|
|
472
498
|
}
|
|
473
499
|
}
|
|
500
|
+
checkTooManyInflightHtlcs() {
|
|
501
|
+
if (this.config.maxInflightHtlcs == null)
|
|
502
|
+
return;
|
|
503
|
+
if (this.swapsWithInflighHtlcs.size >= this.config.maxInflightHtlcs)
|
|
504
|
+
throw {
|
|
505
|
+
code: 20401,
|
|
506
|
+
msg: "Too many in-flight HTLCs, retry later!"
|
|
507
|
+
};
|
|
508
|
+
}
|
|
474
509
|
startRestServer(restServer) {
|
|
475
510
|
restServer.use(this.path + "/createInvoice", (0, ServerParamDecoder_1.serverParamDecoder)(10 * 1000));
|
|
476
511
|
restServer.post(this.path + "/createInvoice", (0, Utils_1.expressHandlerWrapper)(async (req, res) => {
|
|
@@ -551,15 +586,18 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
551
586
|
const gasToken = parsedBody.gasToken;
|
|
552
587
|
//Check request params
|
|
553
588
|
this.checkDescriptionHash(parsedBody.descriptionHash);
|
|
589
|
+
this.checkTooManyInflightHtlcs();
|
|
554
590
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount, gasTokenAmount);
|
|
555
591
|
metadata.times.requestChecked = Date.now();
|
|
556
592
|
//Create abortController for parallel prefetches
|
|
557
593
|
const responseStream = res.responseStream;
|
|
558
594
|
const abortController = (0, Utils_1.getAbortController)(responseStream);
|
|
595
|
+
//Minimum reserve of native token to be kept in the wallet
|
|
596
|
+
const minNativeTokenReserve = this.config.minNativeBalances?.[chainIdentifier] ?? 0n;
|
|
559
597
|
//Pre-fetch data
|
|
560
598
|
const { pricePrefetchPromise, gasTokenPricePrefetchPromise } = this.getFromBtcPricePrefetches(chainIdentifier, useToken, gasToken, abortController);
|
|
561
599
|
const balancePrefetch = this.getBalancePrefetch(chainIdentifier, useToken, abortController);
|
|
562
|
-
const gasTokenBalancePrefetch = gasTokenAmount.amount === 0n || useToken === gasToken ?
|
|
600
|
+
const gasTokenBalancePrefetch = (gasTokenAmount.amount === 0n && minNativeTokenReserve == 0n) || useToken === gasToken ?
|
|
563
601
|
null : this.getBalancePrefetch(chainIdentifier, gasToken, abortController);
|
|
564
602
|
const channelsPrefetch = this.LightningAssertions.getChannelsPrefetch(abortController);
|
|
565
603
|
//Asynchronously send the node's public key to the client
|
|
@@ -570,11 +608,11 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
570
608
|
const totalBtcInput = amountBD + amountBDgas;
|
|
571
609
|
//Check if we have enough funds to honor the request
|
|
572
610
|
if (useToken === gasToken) {
|
|
573
|
-
await this.checkBalance(totalInToken + totalInGasToken, balancePrefetch, abortController.signal);
|
|
611
|
+
await this.checkBalance(totalInToken + totalInGasToken + minNativeTokenReserve, balancePrefetch, abortController.signal);
|
|
574
612
|
}
|
|
575
613
|
else {
|
|
576
614
|
await this.checkBalance(totalInToken, balancePrefetch, abortController.signal);
|
|
577
|
-
await this.checkBalance(totalInGasToken, gasTokenBalancePrefetch, abortController.signal);
|
|
615
|
+
await this.checkBalance(totalInGasToken + minNativeTokenReserve, gasTokenBalancePrefetch, abortController.signal);
|
|
578
616
|
}
|
|
579
617
|
await this.LightningAssertions.checkInboundLiquidity(totalBtcInput, channelsPrefetch, abortController.signal);
|
|
580
618
|
metadata.times.balanceChecked = Date.now();
|
|
@@ -678,5 +716,9 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
678
716
|
gasTokens: mappedDict
|
|
679
717
|
};
|
|
680
718
|
}
|
|
719
|
+
async removeSwapData(swap, ultimateState) {
|
|
720
|
+
this.swapsWithInflighHtlcs.delete(swap);
|
|
721
|
+
return super.removeSwapData(swap, ultimateState);
|
|
722
|
+
}
|
|
681
723
|
}
|
|
682
724
|
exports.FromBtcLnAuto = FromBtcLnAuto;
|
|
@@ -189,7 +189,7 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
189
189
|
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
190
190
|
await this.lightning.cancelHodlInvoice(invoice.id);
|
|
191
191
|
this.unsubscribeInvoice(invoice.id);
|
|
192
|
-
await this.removeSwapData(
|
|
192
|
+
await this.removeSwapData(invoiceData);
|
|
193
193
|
this.swapLogger.info(invoiceData, "htlcReceived(): transaction sending failed, refunding lightning: ", invoiceData.pr);
|
|
194
194
|
throw {
|
|
195
195
|
code: 20002,
|
|
@@ -221,7 +221,7 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
221
221
|
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
222
222
|
await this.lightning.cancelHodlInvoice(invoice.id);
|
|
223
223
|
this.unsubscribeInvoice(invoice.id);
|
|
224
|
-
await this.removeSwapData(
|
|
224
|
+
await this.removeSwapData(invoiceData);
|
|
225
225
|
this.swapLogger.info(invoiceData, "htlcReceived(): transaction reverted, refunding lightning: ", invoiceData.pr);
|
|
226
226
|
throw {
|
|
227
227
|
code: 20002,
|
package/package.json
CHANGED
package/src/swaps/SwapHandler.ts
CHANGED
|
@@ -165,31 +165,14 @@ export abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHandlerSwap
|
|
|
165
165
|
*/
|
|
166
166
|
abstract getInfoData(): any;
|
|
167
167
|
|
|
168
|
-
/**
|
|
169
|
-
* Remove swap data
|
|
170
|
-
*
|
|
171
|
-
* @param hash
|
|
172
|
-
* @param sequence
|
|
173
|
-
*/
|
|
174
|
-
protected removeSwapData(hash: string, sequence: bigint): Promise<void>;
|
|
175
|
-
|
|
176
168
|
/**
|
|
177
169
|
* Remove swap data
|
|
178
170
|
*
|
|
179
171
|
* @param swap
|
|
180
172
|
* @param ultimateState set the ultimate state of the swap before removing
|
|
181
173
|
*/
|
|
182
|
-
protected removeSwapData(swap: V, ultimateState?: S)
|
|
183
|
-
|
|
184
|
-
protected async removeSwapData(hashOrSwap: string | V, sequenceOrUltimateState?: bigint | S) {
|
|
185
|
-
let swap: V;
|
|
186
|
-
if(typeof(hashOrSwap)==="string") {
|
|
187
|
-
if(typeof(sequenceOrUltimateState)!=="bigint") throw new Error("Sequence must be a BN instance!");
|
|
188
|
-
swap = await this.storageManager.getData(hashOrSwap, sequenceOrUltimateState);
|
|
189
|
-
} else {
|
|
190
|
-
swap = hashOrSwap;
|
|
191
|
-
if(sequenceOrUltimateState!=null && typeof(sequenceOrUltimateState)!=="bigint") await swap.setState(sequenceOrUltimateState);
|
|
192
|
-
}
|
|
174
|
+
protected async removeSwapData(swap: V, ultimateState?: S) {
|
|
175
|
+
if(ultimateState!=null) await swap.setState(ultimateState);
|
|
193
176
|
if(swap!=null) await PluginManager.swapRemove(swap);
|
|
194
177
|
this.swapLogger.debug(swap, "removeSwapData(): removing swap final state: "+swap.state);
|
|
195
178
|
await this.storageManager.removeData(swap.getIdentifierHash(), swap.getSequence());
|
|
@@ -89,17 +89,9 @@ export abstract class EscrowHandler<V extends EscrowHandlerSwap<SwapData, S>, S>
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
protected removeSwapData(hash: string, sequence: bigint): Promise<void>;
|
|
93
92
|
protected removeSwapData(swap: V, ultimateState?: S): Promise<void>;
|
|
94
|
-
protected async removeSwapData(
|
|
95
|
-
|
|
96
|
-
if(typeof(hashOrSwap)==="string") {
|
|
97
|
-
if(typeof(sequenceOrUltimateState)!=="bigint") throw new Error("Sequence must be a BN instance!");
|
|
98
|
-
swap = await this.storageManager.getData(hashOrSwap, sequenceOrUltimateState);
|
|
99
|
-
} else {
|
|
100
|
-
swap = hashOrSwap;
|
|
101
|
-
if(sequenceOrUltimateState!=null && typeof(sequenceOrUltimateState)!=="bigint") await swap.setState(sequenceOrUltimateState);
|
|
102
|
-
}
|
|
93
|
+
protected async removeSwapData(swap: V, ultimateState?: S) {
|
|
94
|
+
if(ultimateState!=null) await swap.setState(ultimateState);
|
|
103
95
|
if(swap!=null) await PluginManager.swapRemove(swap);
|
|
104
96
|
this.swapLogger.debug(swap, "removeSwapData(): removing swap final state: "+swap.state);
|
|
105
97
|
this.removeSwapFromEscrowHashMap(swap);
|
|
@@ -4,7 +4,7 @@ import {FromBtcLnAutoSwap, FromBtcLnAutoSwapState} from "./FromBtcLnAutoSwap";
|
|
|
4
4
|
import {MultichainData, SwapHandlerType} from "../../SwapHandler";
|
|
5
5
|
import {ISwapPrice} from "../../../prices/ISwapPrice";
|
|
6
6
|
import {ChainSwapType, ClaimEvent, InitializeEvent, RefundEvent, SwapCommitStateType, SwapData} from "@atomiqlabs/base";
|
|
7
|
-
import {expressHandlerWrapper, getAbortController, HEX_REGEX} from "../../../utils/Utils";
|
|
7
|
+
import {expressHandlerWrapper, getAbortController, HEX_REGEX, isDefinedRuntimeError} from "../../../utils/Utils";
|
|
8
8
|
import {PluginManager} from "../../../plugins/PluginManager";
|
|
9
9
|
import {IIntermediaryStorage} from "../../../storage/IIntermediaryStorage";
|
|
10
10
|
import {FieldTypeEnum, verifySchema} from "../../../utils/paramcoders/SchemaVerifier";
|
|
@@ -21,6 +21,10 @@ import {
|
|
|
21
21
|
import {LightningAssertions} from "../../assertions/LightningAssertions";
|
|
22
22
|
|
|
23
23
|
export type FromBtcLnAutoConfig = FromBtcBaseConfig & {
|
|
24
|
+
maxInflightHtlcs?: number,
|
|
25
|
+
minNativeBalances?: {
|
|
26
|
+
[chainId: string]: bigint
|
|
27
|
+
},
|
|
24
28
|
invoiceTimeoutSeconds?: number,
|
|
25
29
|
minCltv: bigint,
|
|
26
30
|
gracePeriod: bigint,
|
|
@@ -47,6 +51,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
47
51
|
readonly swapType = ChainSwapType.HTLC;
|
|
48
52
|
|
|
49
53
|
activeSubscriptions: Set<string> = new Set<string>();
|
|
54
|
+
swapsWithInflighHtlcs: Set<FromBtcLnAutoSwap> = new Set();
|
|
50
55
|
|
|
51
56
|
readonly config: FromBtcLnAutoConfig;
|
|
52
57
|
readonly lightning: ILightningWallet;
|
|
@@ -100,13 +105,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
100
105
|
|
|
101
106
|
if(swap.state===FromBtcLnAutoSwapState.RECEIVED) {
|
|
102
107
|
try {
|
|
103
|
-
|
|
104
|
-
//Expired
|
|
105
|
-
if(swap.state===FromBtcLnAutoSwapState.RECEIVED) {
|
|
106
|
-
this.swapLogger.info(swap, "processPastSwap(state=RECEIVED): offer HTLC expired, cancelling invoice: "+swap.pr);
|
|
107
|
-
await this.cancelSwapAndInvoice(swap);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
108
|
+
await this.offerHtlc(swap);
|
|
110
109
|
} catch (e) {
|
|
111
110
|
this.swapLogger.error(swap, "processPastSwap(state=RECEIVED): offerHtlc error", e);
|
|
112
111
|
}
|
|
@@ -115,6 +114,8 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
if(swap.state===FromBtcLnAutoSwapState.TXS_SENT || swap.state===FromBtcLnAutoSwapState.COMMITED) {
|
|
117
|
+
this.swapsWithInflighHtlcs.add(swap);
|
|
118
|
+
|
|
118
119
|
const onchainStatus = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
119
120
|
const state: FromBtcLnAutoSwapState = swap.state as FromBtcLnAutoSwapState;
|
|
120
121
|
if(onchainStatus.type===SwapCommitStateType.PAID) {
|
|
@@ -319,7 +320,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
319
320
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcReceived = Date.now();
|
|
320
321
|
|
|
321
322
|
const useToken = invoiceData.token;
|
|
322
|
-
const gasToken = invoiceData.gasToken;
|
|
323
323
|
|
|
324
324
|
let expiryTimeout: bigint;
|
|
325
325
|
try {
|
|
@@ -327,6 +327,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
327
327
|
expiryTimeout = await this.checkHtlcExpiry(invoice);
|
|
328
328
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
329
329
|
} catch (e) {
|
|
330
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcReceiveError = e;
|
|
330
331
|
if(invoiceData.state===FromBtcLnAutoSwapState.CREATED) await this.cancelSwapAndInvoice(invoiceData);
|
|
331
332
|
throw e;
|
|
332
333
|
}
|
|
@@ -369,6 +370,14 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
369
370
|
private async offerHtlc(invoiceData: FromBtcLnAutoSwap) {
|
|
370
371
|
if(invoiceData.state!==FromBtcLnAutoSwapState.RECEIVED) return;
|
|
371
372
|
|
|
373
|
+
try {
|
|
374
|
+
this.checkTooManyInflightHtlcs();
|
|
375
|
+
} catch (e) {
|
|
376
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = e;
|
|
377
|
+
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
378
|
+
throw e;
|
|
379
|
+
}
|
|
380
|
+
|
|
372
381
|
this.swapLogger.debug(invoiceData, "offerHtlc(): invoice: ", invoiceData.pr);
|
|
373
382
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.offerHtlc = Date.now();
|
|
374
383
|
|
|
@@ -380,12 +389,16 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
380
389
|
//Create abort controller for parallel fetches
|
|
381
390
|
const abortController = new AbortController();
|
|
382
391
|
|
|
392
|
+
//Minimum reserve of native token to be kept in the wallet
|
|
393
|
+
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[invoiceData.chainIdentifier] ?? 0n;
|
|
394
|
+
|
|
383
395
|
//Pre-fetch data
|
|
384
396
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(invoiceData.chainIdentifier, useToken, abortController);
|
|
385
|
-
const gasTokenBalancePrefetch: Promise<bigint> = invoiceData.getTotalOutputGasAmount()===0n || useToken===gasToken ?
|
|
397
|
+
const gasTokenBalancePrefetch: Promise<bigint> = (invoiceData.getTotalOutputGasAmount()===0n && minNativeTokenReserve===0n) || useToken===gasToken ?
|
|
386
398
|
null : this.getBalancePrefetch(invoiceData.chainIdentifier, gasToken, abortController);
|
|
387
399
|
|
|
388
400
|
if(await swapContract.getInitAuthorizationExpiry(invoiceData.data, invoiceData) < Date.now()) {
|
|
401
|
+
if(invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = "Init authorization expired, before being sent!";
|
|
389
402
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) {
|
|
390
403
|
await this.cancelSwapAndInvoice(invoiceData);
|
|
391
404
|
}
|
|
@@ -395,14 +408,15 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
395
408
|
try {
|
|
396
409
|
//Check if we have enough liquidity to proceed
|
|
397
410
|
if(useToken===gasToken) {
|
|
398
|
-
await this.checkBalance(invoiceData.getTotalOutputAmount() + invoiceData.getTotalOutputGasAmount(), balancePrefetch, abortController.signal);
|
|
411
|
+
await this.checkBalance(invoiceData.getTotalOutputAmount() + invoiceData.getTotalOutputGasAmount() + minNativeTokenReserve, balancePrefetch, abortController.signal);
|
|
399
412
|
} else {
|
|
400
413
|
await this.checkBalance(invoiceData.getTotalOutputAmount(), balancePrefetch, abortController.signal);
|
|
401
|
-
await this.checkBalance(invoiceData.getTotalOutputGasAmount(), gasTokenBalancePrefetch, abortController.signal);
|
|
414
|
+
await this.checkBalance(invoiceData.getTotalOutputGasAmount() + minNativeTokenReserve, gasTokenBalancePrefetch, abortController.signal);
|
|
402
415
|
}
|
|
403
416
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.offerHtlcChecked = Date.now();
|
|
404
417
|
} catch (e) {
|
|
405
418
|
if(!abortController.signal.aborted) {
|
|
419
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = e;
|
|
406
420
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
407
421
|
}
|
|
408
422
|
throw e;
|
|
@@ -416,6 +430,16 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
416
430
|
}, true);
|
|
417
431
|
|
|
418
432
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) {
|
|
433
|
+
//Re-check the current HTLC count
|
|
434
|
+
try {
|
|
435
|
+
this.checkTooManyInflightHtlcs();
|
|
436
|
+
} catch (e) {
|
|
437
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = e;
|
|
438
|
+
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
439
|
+
throw e;
|
|
440
|
+
}
|
|
441
|
+
this.swapLogger.debug(invoiceData, `offerHtlc(): Sending HTLC offer, current in flight HTLCs: ${this.swapsWithInflighHtlcs.size}, invoice: `, invoiceData.pr);
|
|
442
|
+
this.swapsWithInflighHtlcs.add(invoiceData);
|
|
419
443
|
//Setting the state variable is done outside the promise, so is done synchronously
|
|
420
444
|
await invoiceData.setState(FromBtcLnAutoSwapState.TXS_SENT);
|
|
421
445
|
await this.saveSwapData(invoiceData);
|
|
@@ -565,6 +589,14 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
565
589
|
}
|
|
566
590
|
}
|
|
567
591
|
|
|
592
|
+
private checkTooManyInflightHtlcs() {
|
|
593
|
+
if(this.config.maxInflightHtlcs==null) return;
|
|
594
|
+
if(this.swapsWithInflighHtlcs.size>=this.config.maxInflightHtlcs) throw {
|
|
595
|
+
code: 20401,
|
|
596
|
+
msg: "Too many in-flight HTLCs, retry later!"
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
568
600
|
startRestServer(restServer: Express) {
|
|
569
601
|
|
|
570
602
|
restServer.use(this.path+"/createInvoice", serverParamDecoder(10*1000));
|
|
@@ -653,6 +685,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
653
685
|
|
|
654
686
|
//Check request params
|
|
655
687
|
this.checkDescriptionHash(parsedBody.descriptionHash);
|
|
688
|
+
this.checkTooManyInflightHtlcs();
|
|
656
689
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount, gasTokenAmount);
|
|
657
690
|
metadata.times.requestChecked = Date.now();
|
|
658
691
|
|
|
@@ -660,13 +693,16 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
660
693
|
const responseStream = res.responseStream;
|
|
661
694
|
const abortController = getAbortController(responseStream);
|
|
662
695
|
|
|
696
|
+
//Minimum reserve of native token to be kept in the wallet
|
|
697
|
+
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[chainIdentifier] ?? 0n;
|
|
698
|
+
|
|
663
699
|
//Pre-fetch data
|
|
664
700
|
const {
|
|
665
701
|
pricePrefetchPromise,
|
|
666
702
|
gasTokenPricePrefetchPromise
|
|
667
703
|
} = this.getFromBtcPricePrefetches(chainIdentifier, useToken, gasToken, abortController);
|
|
668
704
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(chainIdentifier, useToken, abortController);
|
|
669
|
-
const gasTokenBalancePrefetch: Promise<bigint> = gasTokenAmount.amount===0n || useToken===gasToken ?
|
|
705
|
+
const gasTokenBalancePrefetch: Promise<bigint> = (gasTokenAmount.amount===0n && minNativeTokenReserve==0n) || useToken===gasToken ?
|
|
670
706
|
null : this.getBalancePrefetch(chainIdentifier, gasToken, abortController);
|
|
671
707
|
const channelsPrefetch: Promise<LightningNetworkChannel[]> = this.LightningAssertions.getChannelsPrefetch(abortController);
|
|
672
708
|
|
|
@@ -695,10 +731,10 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
695
731
|
|
|
696
732
|
//Check if we have enough funds to honor the request
|
|
697
733
|
if(useToken===gasToken) {
|
|
698
|
-
await this.checkBalance(totalInToken + totalInGasToken, balancePrefetch, abortController.signal);
|
|
734
|
+
await this.checkBalance(totalInToken + totalInGasToken + minNativeTokenReserve, balancePrefetch, abortController.signal);
|
|
699
735
|
} else {
|
|
700
736
|
await this.checkBalance(totalInToken, balancePrefetch, abortController.signal);
|
|
701
|
-
await this.checkBalance(totalInGasToken, gasTokenBalancePrefetch, abortController.signal);
|
|
737
|
+
await this.checkBalance(totalInGasToken + minNativeTokenReserve, gasTokenBalancePrefetch, abortController.signal);
|
|
702
738
|
}
|
|
703
739
|
await this.LightningAssertions.checkInboundLiquidity(totalBtcInput, channelsPrefetch, abortController.signal);
|
|
704
740
|
metadata.times.balanceChecked = Date.now();
|
|
@@ -843,5 +879,10 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
843
879
|
};
|
|
844
880
|
}
|
|
845
881
|
|
|
882
|
+
protected async removeSwapData(swap: FromBtcLnAutoSwap, ultimateState?: FromBtcLnAutoSwapState) {
|
|
883
|
+
this.swapsWithInflighHtlcs.delete(swap);
|
|
884
|
+
return super.removeSwapData(swap, ultimateState);
|
|
885
|
+
}
|
|
886
|
+
|
|
846
887
|
}
|
|
847
888
|
|
|
@@ -234,7 +234,7 @@ export class FromBtcLnTrusted extends SwapHandler<FromBtcLnTrustedSwap, FromBtcL
|
|
|
234
234
|
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
235
235
|
await this.lightning.cancelHodlInvoice(invoice.id);
|
|
236
236
|
this.unsubscribeInvoice(invoice.id);
|
|
237
|
-
await this.removeSwapData(
|
|
237
|
+
await this.removeSwapData(invoiceData);
|
|
238
238
|
this.swapLogger.info(invoiceData, "htlcReceived(): transaction sending failed, refunding lightning: ", invoiceData.pr);
|
|
239
239
|
throw {
|
|
240
240
|
code: 20002,
|
|
@@ -267,7 +267,7 @@ export class FromBtcLnTrusted extends SwapHandler<FromBtcLnTrustedSwap, FromBtcL
|
|
|
267
267
|
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
268
268
|
await this.lightning.cancelHodlInvoice(invoice.id);
|
|
269
269
|
this.unsubscribeInvoice(invoice.id);
|
|
270
|
-
await this.removeSwapData(
|
|
270
|
+
await this.removeSwapData(invoiceData);
|
|
271
271
|
this.swapLogger.info(invoiceData, "htlcReceived(): transaction reverted, refunding lightning: ", invoiceData.pr);
|
|
272
272
|
throw {
|
|
273
273
|
code: 20002,
|