@atomiqlabs/lp-lib 16.0.2 → 16.0.4
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 +28 -0
- package/dist/swaps/SwapHandler.js +66 -0
- package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.d.ts +1 -0
- package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +4 -0
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +1 -0
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +27 -5
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +1 -7
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +7 -41
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +1 -0
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +5 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.d.ts +2 -1
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +23 -2
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.d.ts +1 -0
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +9 -0
- package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.d.ts +1 -0
- package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +12 -0
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.d.ts +1 -0
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +12 -0
- package/package.json +1 -1
- package/src/swaps/SwapHandler.ts +72 -1
- package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +5 -0
- package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +24 -6
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +7 -46
- package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +6 -0
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +23 -4
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +16 -3
- package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +15 -0
- package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +15 -1
|
@@ -12,6 +12,7 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
12
12
|
var _a;
|
|
13
13
|
super(storageDirectory, path, chains, swapPricing);
|
|
14
14
|
this.type = SwapHandler_1.SwapHandlerType.FROM_BTC_TRUSTED;
|
|
15
|
+
this.inflightSwapStates = new Set([FromBtcTrustedSwap_1.FromBtcTrustedSwapState.RECEIVED, FromBtcTrustedSwap_1.FromBtcTrustedSwapState.BTC_CONFIRMED, FromBtcTrustedSwap_1.FromBtcTrustedSwapState.SENT, FromBtcTrustedSwap_1.FromBtcTrustedSwapState.CONFIRMED]);
|
|
15
16
|
this.subscriptions = new Map();
|
|
16
17
|
this.doubleSpendWatchdogSwaps = new Set();
|
|
17
18
|
this.refundedSwaps = new Map();
|
|
@@ -148,6 +149,13 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
148
149
|
swap.txId = tx.txid;
|
|
149
150
|
swap.vout = vout;
|
|
150
151
|
this.subscriptions.delete(outputScript);
|
|
152
|
+
try {
|
|
153
|
+
this.checkTooManyInflightSwaps();
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
await this.refundSwap(swap);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
151
159
|
await swap.setState(FromBtcTrustedSwap_1.FromBtcTrustedSwapState.RECEIVED);
|
|
152
160
|
await this.storageManager.saveData(swap.getIdentifierHash(), swap.getSequence(), swap);
|
|
153
161
|
}
|
|
@@ -373,6 +381,7 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
373
381
|
metadata
|
|
374
382
|
};
|
|
375
383
|
const useToken = parsedBody.token;
|
|
384
|
+
this.checkTooManyInflightSwaps();
|
|
376
385
|
//Check request params
|
|
377
386
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount);
|
|
378
387
|
metadata.times.requestChecked = Date.now();
|
|
@@ -390,6 +399,9 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
390
399
|
abortController.abort(e);
|
|
391
400
|
return null;
|
|
392
401
|
});
|
|
402
|
+
const nativeBalancePrefetch = useToken === chainInterface.getNativeCurrencyAddress() ?
|
|
403
|
+
balancePrefetch : this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
404
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
393
405
|
//Check valid amount specified (min/max)
|
|
394
406
|
const { amountBD, swapFee, swapFeeInToken, totalInToken } = await this.AmountAssertions.checkFromBtcAmount(this.type, request, { ...requestedAmount, pricePrefetch: pricePrefetchPromise }, fees, abortController.signal);
|
|
395
407
|
metadata.times.priceCalculated = Date.now();
|
|
@@ -21,6 +21,7 @@ export type FromBtcLnTrustedRequestType = {
|
|
|
21
21
|
*/
|
|
22
22
|
export declare class FromBtcLnTrusted extends SwapHandler<FromBtcLnTrustedSwap, FromBtcLnTrustedSwapState> {
|
|
23
23
|
readonly type = SwapHandlerType.FROM_BTCLN_TRUSTED;
|
|
24
|
+
readonly inflightSwapStates: Set<FromBtcLnTrustedSwapState>;
|
|
24
25
|
activeSubscriptions: Map<string, AbortController>;
|
|
25
26
|
processedTxIds: Map<string, string>;
|
|
26
27
|
readonly config: SwapForGasServerConfig;
|
|
@@ -16,6 +16,7 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
16
16
|
constructor(storageDirectory, path, chains, lightning, swapPricing, config) {
|
|
17
17
|
super(storageDirectory, path, chains, swapPricing);
|
|
18
18
|
this.type = SwapHandler_1.SwapHandlerType.FROM_BTCLN_TRUSTED;
|
|
19
|
+
this.inflightSwapStates = new Set([FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.RECEIVED, FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.SENT, FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.CONFIRMED]);
|
|
19
20
|
this.activeSubscriptions = new Map();
|
|
20
21
|
this.processedTxIds = new Map();
|
|
21
22
|
this.lightning = lightning;
|
|
@@ -153,6 +154,13 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
153
154
|
const { signer, chainInterface } = this.getChain(invoiceData.chainIdentifier);
|
|
154
155
|
//Important to prevent race condition and issuing 2 signed init messages at the same time
|
|
155
156
|
if (invoiceData.state === FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.CREATED) {
|
|
157
|
+
try {
|
|
158
|
+
this.checkTooManyInflightSwaps();
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
await this.cancelSwapAndInvoice(invoiceData);
|
|
162
|
+
throw e;
|
|
163
|
+
}
|
|
156
164
|
if (invoiceData.metadata != null)
|
|
157
165
|
invoiceData.metadata.times.htlcReceived = Date.now();
|
|
158
166
|
await invoiceData.setState(FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.RECEIVED);
|
|
@@ -344,6 +352,7 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
344
352
|
metadata
|
|
345
353
|
};
|
|
346
354
|
const useToken = parsedBody.token;
|
|
355
|
+
this.checkTooManyInflightSwaps();
|
|
347
356
|
//Check request params
|
|
348
357
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount);
|
|
349
358
|
metadata.times.requestChecked = Date.now();
|
|
@@ -361,7 +370,10 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
361
370
|
abortController.abort(e);
|
|
362
371
|
return null;
|
|
363
372
|
});
|
|
373
|
+
const nativeBalancePrefetch = useToken === chainInterface.getNativeCurrencyAddress() ?
|
|
374
|
+
balancePrefetch : this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
364
375
|
const channelsPrefetch = this.LightningAssertions.getChannelsPrefetch(abortController);
|
|
376
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
365
377
|
//Check valid amount specified (min/max)
|
|
366
378
|
const { amountBD, swapFee, swapFeeInToken, totalInToken } = await this.AmountAssertions.checkFromBtcAmount(this.type, request, { ...requestedAmount, pricePrefetch: pricePrefetchPromise }, fees, abortController.signal);
|
|
367
379
|
metadata.times.priceCalculated = Date.now();
|
package/package.json
CHANGED
package/src/swaps/SwapHandler.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {Express, Request} from "express";
|
|
2
2
|
import {ISwapPrice} from "../prices/ISwapPrice";
|
|
3
3
|
import {
|
|
4
|
-
ChainSwapType,
|
|
5
4
|
ChainType
|
|
6
5
|
} from "@atomiqlabs/base";
|
|
7
6
|
import {SwapHandlerSwap} from "./SwapHandlerSwap";
|
|
@@ -35,6 +34,10 @@ export type SwapBaseConfig = {
|
|
|
35
34
|
initAuthorizationTimeouts?: {
|
|
36
35
|
[chainId: string]: number
|
|
37
36
|
},
|
|
37
|
+
minNativeBalances?: {
|
|
38
|
+
[chainId: string]: bigint
|
|
39
|
+
},
|
|
40
|
+
maxInflightSwaps?: number,
|
|
38
41
|
bitcoinBlocktime: bigint,
|
|
39
42
|
baseFee: bigint,
|
|
40
43
|
feePPM: bigint,
|
|
@@ -83,8 +86,11 @@ export abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHandlerSwap
|
|
|
83
86
|
readonly allowedTokens: {[chainId: string]: Set<string>};
|
|
84
87
|
readonly swapPricing: ISwapPrice;
|
|
85
88
|
|
|
89
|
+
abstract readonly inflightSwapStates: Set<S>;
|
|
86
90
|
abstract config: SwapBaseConfig;
|
|
87
91
|
|
|
92
|
+
inflightSwaps: Set<string> = new Set();
|
|
93
|
+
|
|
88
94
|
logger: LoggerType = getLogger(() => "SwapHandler("+this.type+"): ");
|
|
89
95
|
|
|
90
96
|
protected swapLogger = {
|
|
@@ -150,6 +156,7 @@ export abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHandlerSwap
|
|
|
150
156
|
await this.storageManager.removeData(hash, sequence);
|
|
151
157
|
await this.storageManager.saveData(swap.getIdentifierHash(), swap.getSequence(), swap);
|
|
152
158
|
}
|
|
159
|
+
if(this.inflightSwapStates.has(swap.state)) this.inflightSwaps.add(swap.getIdentifier());
|
|
153
160
|
}
|
|
154
161
|
}
|
|
155
162
|
|
|
@@ -172,6 +179,8 @@ export abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHandlerSwap
|
|
|
172
179
|
* @param ultimateState set the ultimate state of the swap before removing
|
|
173
180
|
*/
|
|
174
181
|
protected async removeSwapData(swap: V, ultimateState?: S) {
|
|
182
|
+
this.inflightSwaps.delete(swap.getIdentifier());
|
|
183
|
+
this.logger.debug("removeSwapData(): Removing in-flight swap, current in-flight swaps: "+this.inflightSwaps.size);
|
|
175
184
|
if(ultimateState!=null) await swap.setState(ultimateState);
|
|
176
185
|
if(swap!=null) await PluginManager.swapRemove(swap);
|
|
177
186
|
this.swapLogger.debug(swap, "removeSwapData(): removing swap final state: "+swap.state);
|
|
@@ -179,9 +188,71 @@ export abstract class SwapHandler<V extends SwapHandlerSwap<S> = SwapHandlerSwap
|
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
protected async saveSwapData(swap: V) {
|
|
191
|
+
if(this.inflightSwapStates.has(swap.state)) {
|
|
192
|
+
this.inflightSwaps.add(swap.getIdentifier());
|
|
193
|
+
this.logger.debug("removeSwapData(): Adding in-flight swap, current in-flight swaps: "+this.inflightSwaps.size);
|
|
194
|
+
} else {
|
|
195
|
+
this.inflightSwaps.delete(swap.getIdentifier());
|
|
196
|
+
this.logger.debug("removeSwapData(): Removing in-flight swap, current in-flight swaps: "+this.inflightSwaps.size);
|
|
197
|
+
}
|
|
182
198
|
await this.storageManager.saveData(swap.getIdentifierHash(), swap.getSequence(), swap);
|
|
183
199
|
}
|
|
184
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Pre-fetches native balance to further check if we have enough reserve in a native token
|
|
203
|
+
*
|
|
204
|
+
* @param chainIdentifier
|
|
205
|
+
* @param abortController
|
|
206
|
+
* @protected
|
|
207
|
+
*/
|
|
208
|
+
protected prefetchNativeBalanceIfNeeded(chainIdentifier: string, abortController: AbortController): Promise<bigint> | null {
|
|
209
|
+
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[chainIdentifier] ?? 0n;
|
|
210
|
+
if(minNativeTokenReserve===0n) return null;
|
|
211
|
+
|
|
212
|
+
const { chainInterface, signer} = this.getChain(chainIdentifier);
|
|
213
|
+
|
|
214
|
+
return chainInterface.getBalance(signer.getAddress(), chainInterface.getNativeCurrencyAddress()).catch(e => {
|
|
215
|
+
this.logger.error("getBalancePrefetch(): balancePrefetch error: ", e);
|
|
216
|
+
abortController.abort(e);
|
|
217
|
+
return null;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Checks if we have enough native balance to facilitate swaps
|
|
223
|
+
*
|
|
224
|
+
* @param chainIdentifier
|
|
225
|
+
* @param balancePrefetch
|
|
226
|
+
* @param signal
|
|
227
|
+
* @throws {DefinedRuntimeError} will throw an error if there are not enough funds in the vault
|
|
228
|
+
*/
|
|
229
|
+
protected async checkNativeBalance(chainIdentifier: string, balancePrefetch: Promise<bigint>, signal: AbortSignal | null): Promise<void> {
|
|
230
|
+
if(signal!=null) signal.throwIfAborted();
|
|
231
|
+
|
|
232
|
+
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[chainIdentifier] ?? 0n;
|
|
233
|
+
if(minNativeTokenReserve===0n) return;
|
|
234
|
+
const balance = await balancePrefetch;
|
|
235
|
+
|
|
236
|
+
if(balance==null || balance < minNativeTokenReserve) {
|
|
237
|
+
throw {
|
|
238
|
+
code: 20012,
|
|
239
|
+
msg: "LP ran out of native token to cover gas fees"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Checks whether there are too many swaps in-flight currently
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
protected checkTooManyInflightSwaps() {
|
|
249
|
+
if(this.config.maxInflightSwaps==null) return;
|
|
250
|
+
if(this.inflightSwaps.size>=this.config.maxInflightSwaps) throw {
|
|
251
|
+
code: 20013,
|
|
252
|
+
msg: "LP has too many in-flight swaps, retry later!"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
185
256
|
/**
|
|
186
257
|
* Checks if we have enough balance of the token in the swap vault
|
|
187
258
|
*
|
|
@@ -40,6 +40,7 @@ export type FromBtcRequestType = {
|
|
|
40
40
|
export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSwapState> {
|
|
41
41
|
readonly type = SwapHandlerType.FROM_BTC;
|
|
42
42
|
readonly swapType = ChainSwapType.CHAIN;
|
|
43
|
+
readonly inflightSwapStates = new Set([FromBtcSwapState.COMMITED]);
|
|
43
44
|
|
|
44
45
|
readonly config: FromBtcConfig & {swapTsCsvDelta: bigint};
|
|
45
46
|
|
|
@@ -303,6 +304,7 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
303
304
|
const useToken = parsedBody.token;
|
|
304
305
|
|
|
305
306
|
//Check request params
|
|
307
|
+
this.checkTooManyInflightSwaps();
|
|
306
308
|
this.checkSequence(parsedBody.sequence);
|
|
307
309
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount);
|
|
308
310
|
metadata.times.requestChecked = Date.now();
|
|
@@ -319,6 +321,7 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
319
321
|
} = this.getFromBtcPricePrefetches(chainIdentifier, useToken, depositToken, abortController);
|
|
320
322
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(chainIdentifier, useToken, abortController);
|
|
321
323
|
const signDataPrefetchPromise: Promise<any> = this.getSignDataPrefetch(chainIdentifier, abortController, responseStream);
|
|
324
|
+
const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
322
325
|
|
|
323
326
|
const dummySwapData = await this.getDummySwapData(chainIdentifier, useToken, parsedBody.address);
|
|
324
327
|
abortController.signal.throwIfAborted();
|
|
@@ -328,6 +331,8 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
328
331
|
abortController
|
|
329
332
|
);
|
|
330
333
|
|
|
334
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
335
|
+
|
|
331
336
|
//Check valid amount specified (min/max)
|
|
332
337
|
const {
|
|
333
338
|
amountBD,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
LightningNetworkInvoice
|
|
28
28
|
} from "../../../wallets/ILightningWallet";
|
|
29
29
|
import {LightningAssertions} from "../../assertions/LightningAssertions";
|
|
30
|
+
import {FromBtcLnAutoSwapState} from "../frombtcln_autoinit/FromBtcLnAutoSwap";
|
|
30
31
|
|
|
31
32
|
export type FromBtcLnConfig = FromBtcBaseConfig & {
|
|
32
33
|
invoiceTimeoutSeconds?: number,
|
|
@@ -49,6 +50,7 @@ export type FromBtcLnRequestType = {
|
|
|
49
50
|
export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromBtcLnSwapState> {
|
|
50
51
|
readonly type = SwapHandlerType.FROM_BTCLN;
|
|
51
52
|
readonly swapType = ChainSwapType.HTLC;
|
|
53
|
+
readonly inflightSwapStates = new Set([FromBtcLnSwapState.RECEIVED, FromBtcLnSwapState.COMMITED, FromBtcLnSwapState.CLAIMED]);
|
|
52
54
|
|
|
53
55
|
readonly config: FromBtcLnConfig;
|
|
54
56
|
readonly lightning: ILightningWallet;
|
|
@@ -95,12 +97,6 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
95
97
|
this.swapLogger.error(swap, "processPastSwap(state=CREATED): htlcReceived error", e);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
// @ts-ignore Previous call (htlcReceived) mutates the state of the swap, so this is valid
|
|
99
|
-
if(swap.state===FromBtcLnSwapState.CANCELED) {
|
|
100
|
-
this.swapLogger.info(swap, "processPastSwap(state=CREATED): invoice CANCELED after htlcReceived(), cancelling, invoice: "+swap.pr);
|
|
101
|
-
return "CANCEL";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
100
|
return null;
|
|
105
101
|
}
|
|
106
102
|
|
|
@@ -314,6 +310,14 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
314
310
|
const useToken = invoiceData.token;
|
|
315
311
|
const escrowAmount: bigint = invoiceData.totalTokens;
|
|
316
312
|
|
|
313
|
+
try {
|
|
314
|
+
this.checkTooManyInflightSwaps();
|
|
315
|
+
} catch (e) {
|
|
316
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcReceiveError = e;
|
|
317
|
+
if(invoiceData.state===FromBtcLnSwapState.CREATED) await this.cancelSwapAndInvoice(invoiceData);
|
|
318
|
+
throw e;
|
|
319
|
+
}
|
|
320
|
+
|
|
317
321
|
//Create abort controller for parallel fetches
|
|
318
322
|
const abortController = new AbortController();
|
|
319
323
|
|
|
@@ -333,6 +337,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
333
337
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
334
338
|
} catch (e) {
|
|
335
339
|
if(!abortController.signal.aborted) {
|
|
340
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcReceiveError = e;
|
|
336
341
|
if(invoiceData.state===FromBtcLnSwapState.CREATED) await this.cancelSwapAndInvoice(invoiceData);
|
|
337
342
|
}
|
|
338
343
|
throw e;
|
|
@@ -372,6 +377,15 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
372
377
|
|
|
373
378
|
//Important to prevent race condition and issuing 2 signed init messages at the same time
|
|
374
379
|
if(invoiceData.state===FromBtcLnSwapState.CREATED) {
|
|
380
|
+
//Re-check right before signing
|
|
381
|
+
try {
|
|
382
|
+
this.checkTooManyInflightSwaps();
|
|
383
|
+
} catch (e) {
|
|
384
|
+
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcReceiveError = e;
|
|
385
|
+
if(invoiceData.state===FromBtcLnSwapState.CREATED) await this.cancelSwapAndInvoice(invoiceData);
|
|
386
|
+
throw e;
|
|
387
|
+
}
|
|
388
|
+
|
|
375
389
|
invoiceData.data = payInvoiceObject;
|
|
376
390
|
invoiceData.prefix = sigData.prefix;
|
|
377
391
|
invoiceData.timeout = sigData.timeout;
|
|
@@ -622,6 +636,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
622
636
|
const useToken = parsedBody.token;
|
|
623
637
|
|
|
624
638
|
//Check request params
|
|
639
|
+
this.checkTooManyInflightSwaps();
|
|
625
640
|
this.checkDescriptionHash(parsedBody.descriptionHash);
|
|
626
641
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount);
|
|
627
642
|
metadata.times.requestChecked = Date.now();
|
|
@@ -638,6 +653,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
638
653
|
} = this.getFromBtcPricePrefetches(chainIdentifier, useToken, depositToken, abortController);
|
|
639
654
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(chainIdentifier, useToken, abortController);
|
|
640
655
|
const channelsPrefetch: Promise<LightningNetworkChannel[]> = this.LightningAssertions.getChannelsPrefetch(abortController);
|
|
656
|
+
const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
641
657
|
|
|
642
658
|
const dummySwapData = await this.getDummySwapData(chainIdentifier, useToken, parsedBody.address, parsedBody.paymentHash);
|
|
643
659
|
abortController.signal.throwIfAborted();
|
|
@@ -650,6 +666,8 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
650
666
|
//Asynchronously send the node's public key to the client
|
|
651
667
|
this.sendPublicKeyAsync(responseStream);
|
|
652
668
|
|
|
669
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
670
|
+
|
|
653
671
|
//Check valid amount specified (min/max)
|
|
654
672
|
const {
|
|
655
673
|
amountBD,
|
|
@@ -21,10 +21,6 @@ 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
|
-
},
|
|
28
24
|
invoiceTimeoutSeconds?: number,
|
|
29
25
|
minCltv: bigint,
|
|
30
26
|
gracePeriod: bigint,
|
|
@@ -49,9 +45,9 @@ export type FromBtcLnAutoRequestType = {
|
|
|
49
45
|
export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, FromBtcLnAutoSwapState> {
|
|
50
46
|
readonly type = SwapHandlerType.FROM_BTCLN_AUTO;
|
|
51
47
|
readonly swapType = ChainSwapType.HTLC;
|
|
48
|
+
readonly inflightSwapStates = new Set([FromBtcLnAutoSwapState.TXS_SENT, FromBtcLnAutoSwapState.COMMITED, FromBtcLnAutoSwapState.CLAIMED]);
|
|
52
49
|
|
|
53
50
|
activeSubscriptions: Set<string> = new Set<string>();
|
|
54
|
-
swapsWithInflighHtlcs: Set<FromBtcLnAutoSwap> = new Set();
|
|
55
51
|
|
|
56
52
|
readonly config: FromBtcLnAutoConfig;
|
|
57
53
|
readonly lightning: ILightningWallet;
|
|
@@ -114,8 +110,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
114
110
|
}
|
|
115
111
|
|
|
116
112
|
if(swap.state===FromBtcLnAutoSwapState.TXS_SENT || swap.state===FromBtcLnAutoSwapState.COMMITED) {
|
|
117
|
-
this.swapsWithInflighHtlcs.add(swap);
|
|
118
|
-
|
|
119
113
|
const onchainStatus = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
120
114
|
const state: FromBtcLnAutoSwapState = swap.state as FromBtcLnAutoSwapState;
|
|
121
115
|
if(onchainStatus.type===SwapCommitStateType.PAID) {
|
|
@@ -371,7 +365,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
371
365
|
if(invoiceData.state!==FromBtcLnAutoSwapState.RECEIVED) return;
|
|
372
366
|
|
|
373
367
|
try {
|
|
374
|
-
this.
|
|
368
|
+
this.checkTooManyInflightSwaps();
|
|
375
369
|
} catch (e) {
|
|
376
370
|
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = e;
|
|
377
371
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
@@ -389,15 +383,10 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
389
383
|
//Create abort controller for parallel fetches
|
|
390
384
|
const abortController = new AbortController();
|
|
391
385
|
|
|
392
|
-
//Minimum reserve of native token to be kept in the wallet
|
|
393
|
-
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[invoiceData.chainIdentifier] ?? 0n;
|
|
394
|
-
|
|
395
386
|
//Pre-fetch data
|
|
396
387
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(invoiceData.chainIdentifier, useToken, abortController);
|
|
397
388
|
const gasTokenBalancePrefetch: Promise<bigint> = invoiceData.getTotalOutputGasAmount()===0n || useToken===gasToken ?
|
|
398
389
|
null : this.getBalancePrefetch(invoiceData.chainIdentifier, gasToken, abortController);
|
|
399
|
-
const nativeBalancePrefetch: Promise<bigint> = minNativeTokenReserve===0n ?
|
|
400
|
-
null : this.getBalancePrefetch(invoiceData.chainIdentifier, chainInterface.getNativeCurrencyAddress(), abortController, false);
|
|
401
390
|
|
|
402
391
|
if(await swapContract.getInitAuthorizationExpiry(invoiceData.data, invoiceData) < Date.now()) {
|
|
403
392
|
if(invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = "Init authorization expired, before being sent!";
|
|
@@ -407,16 +396,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
407
396
|
return false;
|
|
408
397
|
}
|
|
409
398
|
|
|
410
|
-
try {
|
|
411
|
-
await this.checkBalance(minNativeTokenReserve, nativeBalancePrefetch, abortController.signal);
|
|
412
|
-
} catch (e) {
|
|
413
|
-
if(!abortController.signal.aborted) {
|
|
414
|
-
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = "Not enough native balance!";
|
|
415
|
-
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
416
|
-
}
|
|
417
|
-
throw e;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
399
|
try {
|
|
421
400
|
//Check if we have enough liquidity to proceed
|
|
422
401
|
if(useToken===gasToken) {
|
|
@@ -444,14 +423,13 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
444
423
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) {
|
|
445
424
|
//Re-check the current HTLC count
|
|
446
425
|
try {
|
|
447
|
-
this.
|
|
426
|
+
this.checkTooManyInflightSwaps();
|
|
448
427
|
} catch (e) {
|
|
449
428
|
if(isDefinedRuntimeError(e) && invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = e;
|
|
450
429
|
if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
|
|
451
430
|
throw e;
|
|
452
431
|
}
|
|
453
|
-
this.swapLogger.debug(invoiceData, `offerHtlc(): Sending HTLC offer, current in flight
|
|
454
|
-
this.swapsWithInflighHtlcs.add(invoiceData);
|
|
432
|
+
this.swapLogger.debug(invoiceData, `offerHtlc(): Sending HTLC offer, current in flight swaps: ${this.inflightSwaps.size}, invoice: `, invoiceData.pr);
|
|
455
433
|
//Setting the state variable is done outside the promise, so is done synchronously
|
|
456
434
|
await invoiceData.setState(FromBtcLnAutoSwapState.TXS_SENT);
|
|
457
435
|
await this.saveSwapData(invoiceData);
|
|
@@ -601,14 +579,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
601
579
|
}
|
|
602
580
|
}
|
|
603
581
|
|
|
604
|
-
private checkTooManyInflightHtlcs() {
|
|
605
|
-
if(this.config.maxInflightHtlcs==null) return;
|
|
606
|
-
if(this.swapsWithInflighHtlcs.size>=this.config.maxInflightHtlcs) throw {
|
|
607
|
-
code: 20401,
|
|
608
|
-
msg: "Too many in-flight HTLCs, retry later!"
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
582
|
startRestServer(restServer: Express) {
|
|
613
583
|
|
|
614
584
|
restServer.use(this.path+"/createInvoice", serverParamDecoder(10*1000));
|
|
@@ -697,7 +667,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
697
667
|
|
|
698
668
|
//Check request params
|
|
699
669
|
this.checkDescriptionHash(parsedBody.descriptionHash);
|
|
700
|
-
this.
|
|
670
|
+
this.checkTooManyInflightSwaps();
|
|
701
671
|
const fees = await this.AmountAssertions.preCheckFromBtcAmounts(this.type, request, requestedAmount, gasTokenAmount);
|
|
702
672
|
metadata.times.requestChecked = Date.now();
|
|
703
673
|
|
|
@@ -705,9 +675,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
705
675
|
const responseStream = res.responseStream;
|
|
706
676
|
const abortController = getAbortController(responseStream);
|
|
707
677
|
|
|
708
|
-
//Minimum reserve of native token to be kept in the wallet
|
|
709
|
-
const minNativeTokenReserve: bigint = this.config.minNativeBalances?.[chainIdentifier] ?? 0n;
|
|
710
|
-
|
|
711
678
|
//Pre-fetch data
|
|
712
679
|
const {
|
|
713
680
|
pricePrefetchPromise,
|
|
@@ -716,8 +683,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
716
683
|
const balancePrefetch: Promise<bigint> = this.getBalancePrefetch(chainIdentifier, useToken, abortController);
|
|
717
684
|
const gasTokenBalancePrefetch: Promise<bigint> = gasTokenAmount.amount===0n || useToken===gasToken ?
|
|
718
685
|
null : this.getBalancePrefetch(chainIdentifier, gasToken, abortController);
|
|
719
|
-
const nativeTokenBalancePrefetch: Promise<bigint> =
|
|
720
|
-
null : this.getBalancePrefetch(chainIdentifier, chainInterface.getNativeCurrencyAddress(), abortController, false);
|
|
686
|
+
const nativeTokenBalancePrefetch: Promise<bigint> = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
721
687
|
const channelsPrefetch: Promise<LightningNetworkChannel[]> = this.LightningAssertions.getChannelsPrefetch(abortController);
|
|
722
688
|
|
|
723
689
|
//Asynchronously send the node's public key to the client
|
|
@@ -744,7 +710,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
744
710
|
const totalBtcInput = amountBD + amountBDgas;
|
|
745
711
|
|
|
746
712
|
//Check if we have at least the minimum needed native balance
|
|
747
|
-
await this.
|
|
713
|
+
await this.checkNativeBalance(chainIdentifier, nativeTokenBalancePrefetch, abortController.signal);
|
|
748
714
|
|
|
749
715
|
//Check if we have enough funds to honor the request
|
|
750
716
|
if(useToken===gasToken) {
|
|
@@ -896,10 +862,5 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
|
|
|
896
862
|
};
|
|
897
863
|
}
|
|
898
864
|
|
|
899
|
-
protected async removeSwapData(swap: FromBtcLnAutoSwap, ultimateState?: FromBtcLnAutoSwapState) {
|
|
900
|
-
this.swapsWithInflighHtlcs.delete(swap);
|
|
901
|
-
return super.removeSwapData(swap, ultimateState);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
865
|
}
|
|
905
866
|
|
|
@@ -59,6 +59,7 @@ const MAX_PARALLEL_TX_PROCESSED = 10;
|
|
|
59
59
|
export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState> {
|
|
60
60
|
readonly type = SwapHandlerType.TO_BTC;
|
|
61
61
|
readonly swapType = ChainSwapType.CHAIN_NONCED;
|
|
62
|
+
readonly inflightSwapStates = new Set([ToBtcSwapState.BTC_SENDING, ToBtcSwapState.BTC_SENT]);
|
|
62
63
|
|
|
63
64
|
activeSubscriptions: {[txId: string]: ToBtcSwapAbs} = {};
|
|
64
65
|
bitcoinRpc: BitcoinRpc<BtcBlock>;
|
|
@@ -358,6 +359,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
|
|
|
358
359
|
// e.g. that 2 payouts share the same input and would effectively double-spend each other
|
|
359
360
|
return this.bitcoin.execute(async () => {
|
|
360
361
|
//Run checks
|
|
362
|
+
this.checkTooManyInflightSwaps();
|
|
361
363
|
this.checkExpiresTooSoon(swap);
|
|
362
364
|
if(swap.metadata!=null) swap.metadata.times.payCLTVChecked = Date.now();
|
|
363
365
|
|
|
@@ -672,6 +674,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
|
|
|
672
674
|
|
|
673
675
|
const responseStream = res.responseStream;
|
|
674
676
|
|
|
677
|
+
this.checkTooManyInflightSwaps();
|
|
675
678
|
this.checkNonceValid(parsedBody.nonce);
|
|
676
679
|
this.checkConfirmationTarget(parsedBody.confirmationTarget);
|
|
677
680
|
this.checkRequiredConfirmations(parsedBody.confirmations);
|
|
@@ -685,6 +688,9 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
|
|
|
685
688
|
const abortController = getAbortController(responseStream);
|
|
686
689
|
|
|
687
690
|
const {pricePrefetchPromise, signDataPrefetchPromise} = this.getToBtcPrefetches(chainIdentifier, useToken, responseStream, abortController);
|
|
691
|
+
const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
692
|
+
|
|
693
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
688
694
|
|
|
689
695
|
const {
|
|
690
696
|
amountBD,
|
|
@@ -86,11 +86,11 @@ export type ToBtcLnRequestType = {
|
|
|
86
86
|
* Swap handler handling to BTCLN swaps using submarine swaps
|
|
87
87
|
*/
|
|
88
88
|
export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwapState> {
|
|
89
|
-
|
|
90
|
-
activeSubscriptions: Set<string> = new Set<string>();
|
|
91
|
-
|
|
92
89
|
readonly type = SwapHandlerType.TO_BTCLN;
|
|
93
90
|
readonly swapType = ChainSwapType.HTLC;
|
|
91
|
+
readonly inflightSwapStates = new Set([ToBtcLnSwapState.COMMITED, ToBtcLnSwapState.PAID]);
|
|
92
|
+
|
|
93
|
+
activeSubscriptions: Set<string> = new Set<string>();
|
|
94
94
|
|
|
95
95
|
readonly config: ToBtcLnConfig & {minTsSendCltv: bigint};
|
|
96
96
|
|
|
@@ -391,6 +391,18 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
if(swap.state===ToBtcLnSwapState.SAVED) {
|
|
394
|
+
try {
|
|
395
|
+
this.checkTooManyInflightSwaps();
|
|
396
|
+
} catch (e) {
|
|
397
|
+
this.swapLogger.error(swap, "processInitialized(): checking too many inflight swaps error: ", e);
|
|
398
|
+
if(isDefinedRuntimeError(e)) {
|
|
399
|
+
if(swap.metadata!=null) swap.metadata.payError = e;
|
|
400
|
+
await swap.setState(ToBtcLnSwapState.NON_PAYABLE);
|
|
401
|
+
await this.saveSwapData(swap);
|
|
402
|
+
return;
|
|
403
|
+
} else throw e;
|
|
404
|
+
}
|
|
405
|
+
|
|
394
406
|
await swap.setState(ToBtcLnSwapState.COMMITED);
|
|
395
407
|
await this.saveSwapData(swap);
|
|
396
408
|
try {
|
|
@@ -690,11 +702,13 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
690
702
|
};
|
|
691
703
|
}
|
|
692
704
|
|
|
705
|
+
this.checkTooManyInflightSwaps();
|
|
706
|
+
const parsedAuth = this.checkExactInAuthorization(parsedBody.reqId);
|
|
707
|
+
|
|
693
708
|
const responseStream = res.responseStream;
|
|
694
709
|
const abortSignal = responseStream.getAbortSignal();
|
|
695
710
|
|
|
696
711
|
//Check request params
|
|
697
|
-
const parsedAuth = this.checkExactInAuthorization(parsedBody.reqId);
|
|
698
712
|
const {parsedPR, halfConfidence} = await this.checkPaymentRequest(parsedAuth.chainIdentifier, parsedBody.pr);
|
|
699
713
|
await this.checkPaymentRequestMatchesInitial(parsedBody.pr, parsedAuth);
|
|
700
714
|
|
|
@@ -836,6 +850,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
836
850
|
const currentTimestamp: bigint = BigInt(Math.floor(Date.now()/1000));
|
|
837
851
|
|
|
838
852
|
//Check request params
|
|
853
|
+
this.checkTooManyInflightSwaps();
|
|
839
854
|
this.checkAmount(parsedBody.amount, parsedBody.exactIn);
|
|
840
855
|
this.checkMaxFee(parsedBody.maxFee);
|
|
841
856
|
this.checkExpiry(parsedBody.expiryTimestamp, currentTimestamp);
|
|
@@ -854,11 +869,15 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
854
869
|
|
|
855
870
|
//Pre-fetch
|
|
856
871
|
const {pricePrefetchPromise, signDataPrefetchPromise} = this.getToBtcPrefetches(chainIdentifier, useToken, responseStream, abortController);
|
|
872
|
+
const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
857
873
|
|
|
858
874
|
//Check if prior payment has been made
|
|
859
875
|
await this.LightningAssertions.checkPriorPayment(parsedPR.id, abortController.signal);
|
|
860
876
|
metadata.times.priorPaymentChecked = Date.now();
|
|
861
877
|
|
|
878
|
+
//Check if we still have enough native balance
|
|
879
|
+
await this.checkNativeBalance(chainIdentifier, nativeBalancePrefetch, abortController.signal);
|
|
880
|
+
|
|
862
881
|
//Check amounts
|
|
863
882
|
const {
|
|
864
883
|
amountBD,
|