@atomiqlabs/lp-lib 17.5.3 → 17.6.0
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/assertions/FromBtcAmountAssertions.js +2 -2
- package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +16 -6
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +2 -2
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +34 -24
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +2 -2
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +30 -19
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +1 -1
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +12 -5
- package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.d.ts +2 -2
- package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.js +1 -1
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +38 -4
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +35 -15
- package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +2 -2
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +7 -2
- package/dist/utils/Utils.d.ts +33 -0
- package/dist/utils/Utils.js +51 -1
- package/package.json +1 -1
- package/src/swaps/assertions/FromBtcAmountAssertions.ts +2 -2
- package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +14 -5
- package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +46 -29
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +42 -24
- package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +13 -7
- package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +4 -4
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +37 -7
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +36 -18
- package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +25 -2
- package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +5 -1
- package/src/utils/Utils.ts +48 -0
|
@@ -124,8 +124,8 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
124
124
|
const tooLow = amountBD < (min * 95n / 100n);
|
|
125
125
|
const tooHigh = amountBD > (max * 105n / 100n);
|
|
126
126
|
if (tooLow || tooHigh) {
|
|
127
|
-
const adjustedMin = min * (1000000n - fees.feePPM) /
|
|
128
|
-
const adjustedMax = max * (1000000n - fees.feePPM) /
|
|
127
|
+
const adjustedMin = min * (1000000n - fees.feePPM) / 1000000n - fees.baseFee;
|
|
128
|
+
const adjustedMax = max * (1000000n - fees.feePPM) / 1000000n - fees.baseFee;
|
|
129
129
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(adjustedMin, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch);
|
|
130
130
|
const maxIn = await this.swapPricing.getFromBtcSwapAmount(adjustedMax, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch);
|
|
131
131
|
throw {
|
|
@@ -92,8 +92,13 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
92
92
|
]);
|
|
93
93
|
const refundSwaps = [];
|
|
94
94
|
for (let { obj: swap } of queriedData) {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
try {
|
|
96
|
+
if (await this.processPastSwap(swap))
|
|
97
|
+
refundSwaps.push(swap);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
|
|
101
|
+
}
|
|
97
102
|
}
|
|
98
103
|
await this.refundSwaps(refundSwaps);
|
|
99
104
|
}
|
|
@@ -110,10 +115,15 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
110
115
|
if (unlock == null)
|
|
111
116
|
continue;
|
|
112
117
|
this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
try {
|
|
119
|
+
await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
|
|
120
|
+
this.swapLogger.info(refundSwap, "refundSwaps(): swap refunded, address: " + refundSwap.address);
|
|
121
|
+
//The swap should be removed by the event handler
|
|
122
|
+
await refundSwap.setState(FromBtcSwapAbs_1.FromBtcSwapState.REFUNDED);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
|
|
126
|
+
}
|
|
117
127
|
unlock();
|
|
118
128
|
}
|
|
119
129
|
}
|
|
@@ -9,7 +9,7 @@ import { ILightningWallet } from "../../../wallets/ILightningWallet";
|
|
|
9
9
|
import { LightningAssertions } from "../../assertions/LightningAssertions";
|
|
10
10
|
export type FromBtcLnConfig = FromBtcBaseConfig & {
|
|
11
11
|
invoiceTimeoutSeconds?: number;
|
|
12
|
-
|
|
12
|
+
destinationHtlcTimeoutSeconds: bigint;
|
|
13
13
|
gracePeriod: bigint;
|
|
14
14
|
};
|
|
15
15
|
export type FromBtcLnRequestType = {
|
|
@@ -30,6 +30,7 @@ export declare class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAb
|
|
|
30
30
|
readonly config: FromBtcLnConfig;
|
|
31
31
|
readonly lightning: ILightningWallet;
|
|
32
32
|
readonly LightningAssertions: LightningAssertions;
|
|
33
|
+
readonly minCltv: bigint;
|
|
33
34
|
constructor(storageDirectory: IIntermediaryStorage<FromBtcLnSwapAbs>, path: string, chains: MultichainData, lightning: ILightningWallet, swapPricing: ISwapPrice, config: FromBtcLnConfig);
|
|
34
35
|
protected processPastSwap(swap: FromBtcLnSwapAbs): Promise<"REFUND" | "SETTLE" | "CANCEL" | null>;
|
|
35
36
|
protected refundSwaps(refundSwaps: FromBtcLnSwapAbs[]): Promise<void>;
|
|
@@ -90,7 +91,6 @@ export declare class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAb
|
|
|
90
91
|
* @param blockheightPrefetch
|
|
91
92
|
* @param signal
|
|
92
93
|
* @throws {DefinedRuntimeError} Will throw if HTLC expires too soon and therefore cannot be processed
|
|
93
|
-
* @returns expiry timeout in seconds
|
|
94
94
|
*/
|
|
95
95
|
private checkHtlcExpiry;
|
|
96
96
|
/**
|
|
@@ -25,6 +25,10 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
25
25
|
this.config.invoiceTimeoutSeconds = this.config.invoiceTimeoutSeconds || 90;
|
|
26
26
|
this.lightning = lightning;
|
|
27
27
|
this.LightningAssertions = new LightningAssertions_1.LightningAssertions(this.logger, lightning);
|
|
28
|
+
const numerator = (this.config.destinationHtlcTimeoutSeconds + this.config.gracePeriod) * this.config.safetyFactor;
|
|
29
|
+
this.minCltv = (0, Utils_1.bigIntMax)((numerator + this.config.bitcoinBlocktime - 1n)
|
|
30
|
+
/ this.config.bitcoinBlocktime, //Ceil division
|
|
31
|
+
(0, Utils_1.getMinSafeBlockWindowFast)(this.config.safetyFactor));
|
|
28
32
|
}
|
|
29
33
|
async processPastSwap(swap) {
|
|
30
34
|
const { swapContract, signer } = this.getChain(swap.chainIdentifier);
|
|
@@ -116,9 +120,14 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
116
120
|
if (unlock == null)
|
|
117
121
|
continue;
|
|
118
122
|
this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
try {
|
|
124
|
+
await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
|
|
125
|
+
this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: " + refundSwap.pr);
|
|
126
|
+
await refundSwap.setState(FromBtcLnSwapAbs_1.FromBtcLnSwapState.REFUNDED);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
|
|
130
|
+
}
|
|
122
131
|
unlock();
|
|
123
132
|
}
|
|
124
133
|
}
|
|
@@ -170,16 +179,21 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
170
179
|
}
|
|
171
180
|
]);
|
|
172
181
|
for (let { obj: swap } of queriedData) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
try {
|
|
183
|
+
switch (await this.processPastSwap(swap)) {
|
|
184
|
+
case "CANCEL":
|
|
185
|
+
cancelInvoices.push(swap);
|
|
186
|
+
break;
|
|
187
|
+
case "SETTLE":
|
|
188
|
+
settleInvoices.push(swap);
|
|
189
|
+
break;
|
|
190
|
+
case "REFUND":
|
|
191
|
+
refundSwaps.push(swap);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
|
|
183
197
|
}
|
|
184
198
|
}
|
|
185
199
|
await this.refundSwaps(refundSwaps);
|
|
@@ -261,14 +275,13 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
261
275
|
const balancePrefetch = this.getBalancePrefetch(invoiceData.chainIdentifier, useToken, abortController);
|
|
262
276
|
const blockheightPrefetch = this.getBlockheightPrefetch(abortController);
|
|
263
277
|
const signDataPrefetchPromise = this.getSignDataPrefetch(invoiceData.chainIdentifier, abortController);
|
|
264
|
-
let expiryTimeout;
|
|
265
278
|
try {
|
|
266
279
|
//Check if we have enough liquidity to proceed
|
|
267
280
|
await this.checkBalance(escrowAmount, balancePrefetch, abortController.signal);
|
|
268
281
|
if (invoiceData.metadata != null)
|
|
269
282
|
invoiceData.metadata.times.htlcBalanceChecked = Date.now();
|
|
270
283
|
//Check if HTLC expiry is long enough
|
|
271
|
-
|
|
284
|
+
await this.checkHtlcExpiry(invoice, blockheightPrefetch, abortController.signal);
|
|
272
285
|
if (invoiceData.metadata != null)
|
|
273
286
|
invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
274
287
|
}
|
|
@@ -283,7 +296,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
283
296
|
}
|
|
284
297
|
const { swapContract, signer } = this.getChain(invoiceData.chainIdentifier);
|
|
285
298
|
//Create real swap data
|
|
286
|
-
const payInvoiceObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, signer.getAddress(), invoiceData.claimer, useToken, escrowAmount, invoiceData.claimHash, 0n, BigInt(Math.floor(Date.now() / 1000)) +
|
|
299
|
+
const payInvoiceObject = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, signer.getAddress(), invoiceData.claimer, useToken, escrowAmount, invoiceData.claimHash, 0n, BigInt(Math.floor(Date.now() / 1000)) + this.config.destinationHtlcTimeoutSeconds, false, true, invoiceData.securityDeposit, 0n, invoiceData.depositToken);
|
|
287
300
|
abortController.signal.throwIfAborted();
|
|
288
301
|
if (invoiceData.metadata != null)
|
|
289
302
|
invoiceData.metadata.times.htlcSwapCreated = Date.now();
|
|
@@ -402,25 +415,23 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
402
415
|
* @param blockheightPrefetch
|
|
403
416
|
* @param signal
|
|
404
417
|
* @throws {DefinedRuntimeError} Will throw if HTLC expires too soon and therefore cannot be processed
|
|
405
|
-
* @returns expiry timeout in seconds
|
|
406
418
|
*/
|
|
407
419
|
async checkHtlcExpiry(invoice, blockheightPrefetch, signal) {
|
|
408
420
|
const timeout = this.getInvoicePaymentsTimeout(invoice);
|
|
409
421
|
const current_block_height = await blockheightPrefetch;
|
|
410
422
|
signal.throwIfAborted();
|
|
411
423
|
const blockDelta = BigInt(timeout - current_block_height);
|
|
412
|
-
const htlcExpiresTooSoon = blockDelta < this.
|
|
424
|
+
const htlcExpiresTooSoon = blockDelta < this.minCltv;
|
|
413
425
|
if (htlcExpiresTooSoon) {
|
|
414
426
|
throw {
|
|
415
427
|
code: 20002,
|
|
416
428
|
msg: "Not enough time to reliably process the swap",
|
|
417
429
|
data: {
|
|
418
|
-
requiredDelta: this.
|
|
430
|
+
requiredDelta: this.minCltv.toString(10),
|
|
419
431
|
actualDelta: blockDelta.toString(10)
|
|
420
432
|
}
|
|
421
433
|
};
|
|
422
434
|
}
|
|
423
|
-
return (this.config.minCltv * this.config.bitcoinBlocktime / this.config.safetyFactor) - this.config.gracePeriod;
|
|
424
435
|
}
|
|
425
436
|
/**
|
|
426
437
|
* Cancels the swap (CANCELED state) & also cancels the LN invoice (including all pending HTLCs)
|
|
@@ -568,7 +579,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
568
579
|
//Create swap
|
|
569
580
|
const hodlInvoiceObj = {
|
|
570
581
|
description: description ?? (chainIdentifier + "-" + parsedBody.address),
|
|
571
|
-
cltvDelta: Number(this.
|
|
582
|
+
cltvDelta: Number(this.minCltv) + 5,
|
|
572
583
|
expiresAt: Date.now() + (this.config.invoiceTimeoutSeconds * 1000),
|
|
573
584
|
id: parsedBody.paymentHash,
|
|
574
585
|
mtokens: amountBD * 1000n,
|
|
@@ -580,8 +591,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
580
591
|
metadata.times.invoiceCreated = Date.now();
|
|
581
592
|
metadata.invoiceResponse = { ...hodlInvoice };
|
|
582
593
|
//Pre-compute the security deposit
|
|
583
|
-
const
|
|
584
|
-
const totalSecurityDeposit = await this.getSecurityDeposit(chainIdentifier, amountBD, swapFee, expiryTimeout, baseSDPromise, depositToken, depositTokenPricePrefetchPromise, fees, abortController.signal, metadata);
|
|
594
|
+
const totalSecurityDeposit = await this.getSecurityDeposit(chainIdentifier, amountBD, swapFee, this.config.destinationHtlcTimeoutSeconds, baseSDPromise, depositToken, depositTokenPricePrefetchPromise, fees, abortController.signal, metadata);
|
|
585
595
|
metadata.times.securityDepositCalculated = Date.now();
|
|
586
596
|
const createdSwap = new FromBtcLnSwapAbs_1.FromBtcLnSwapAbs(chainIdentifier, hodlInvoice.request, parsedBody.paymentHash, hodlInvoice.mtokens, swapFee, swapFeeInToken, parsedBody.address, useToken, totalInToken, swapContract.getHashForHtlc(Buffer.from(parsedBody.paymentHash, "hex")).toString("hex"), totalSecurityDeposit, depositToken);
|
|
587
597
|
metadata.times.swapCreated = Date.now();
|
|
@@ -712,7 +722,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
712
722
|
}
|
|
713
723
|
getInfoData() {
|
|
714
724
|
return {
|
|
715
|
-
minCltv: Number(this.
|
|
725
|
+
minCltv: Number(this.minCltv)
|
|
716
726
|
};
|
|
717
727
|
}
|
|
718
728
|
}
|
|
@@ -9,7 +9,7 @@ import { ILightningWallet } from "../../../wallets/ILightningWallet";
|
|
|
9
9
|
import { LightningAssertions } from "../../assertions/LightningAssertions";
|
|
10
10
|
export type FromBtcLnAutoConfig = FromBtcBaseConfig & {
|
|
11
11
|
invoiceTimeoutSeconds?: number;
|
|
12
|
-
|
|
12
|
+
destinationHtlcTimeoutSeconds: bigint;
|
|
13
13
|
gracePeriod: bigint;
|
|
14
14
|
gasTokenMax: {
|
|
15
15
|
[chainId: string]: bigint;
|
|
@@ -37,6 +37,7 @@ export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoS
|
|
|
37
37
|
readonly config: FromBtcLnAutoConfig;
|
|
38
38
|
readonly lightning: ILightningWallet;
|
|
39
39
|
readonly LightningAssertions: LightningAssertions;
|
|
40
|
+
readonly minCltv: bigint;
|
|
40
41
|
constructor(storageDirectory: IIntermediaryStorage<FromBtcLnAutoSwap>, path: string, chains: MultichainData, lightning: ILightningWallet, swapPricing: ISwapPrice, config: FromBtcLnAutoConfig);
|
|
41
42
|
protected processPastSwap(swap: FromBtcLnAutoSwap): Promise<"REFUND" | "SETTLE" | null>;
|
|
42
43
|
protected refundSwaps(refundSwaps: FromBtcLnAutoSwap[]): Promise<void>;
|
|
@@ -95,7 +96,6 @@ export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoS
|
|
|
95
96
|
*
|
|
96
97
|
* @param invoice
|
|
97
98
|
* @throws {DefinedRuntimeError} Will throw if HTLC expires too soon and therefore cannot be processed
|
|
98
|
-
* @returns expiry timeout in seconds
|
|
99
99
|
*/
|
|
100
100
|
private checkHtlcExpiry;
|
|
101
101
|
/**
|
|
@@ -32,6 +32,10 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
32
32
|
if (!swapContract.supportsInitWithoutClaimer)
|
|
33
33
|
this.allowedTokens[chain].clear();
|
|
34
34
|
}
|
|
35
|
+
const numerator = (this.config.destinationHtlcTimeoutSeconds + this.config.gracePeriod) * this.config.safetyFactor;
|
|
36
|
+
this.minCltv = (0, Utils_1.bigIntMax)((numerator + this.config.bitcoinBlocktime - 1n)
|
|
37
|
+
/ this.config.bitcoinBlocktime, //Ceil division
|
|
38
|
+
(0, Utils_1.getMinSafeBlockWindowFast)(this.config.safetyFactor));
|
|
35
39
|
}
|
|
36
40
|
async processPastSwap(swap) {
|
|
37
41
|
const { swapContract, signer } = this.getChain(swap.chainIdentifier);
|
|
@@ -134,9 +138,14 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
134
138
|
if (unlock == null)
|
|
135
139
|
continue;
|
|
136
140
|
this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
try {
|
|
142
|
+
await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
|
|
143
|
+
this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: " + refundSwap.pr);
|
|
144
|
+
await refundSwap.setState(FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.REFUNDED);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
|
|
148
|
+
}
|
|
140
149
|
unlock();
|
|
141
150
|
}
|
|
142
151
|
}
|
|
@@ -174,13 +183,18 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
174
183
|
}
|
|
175
184
|
]);
|
|
176
185
|
for (let { obj: swap } of queriedData) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
try {
|
|
187
|
+
switch (await this.processPastSwap(swap)) {
|
|
188
|
+
case "SETTLE":
|
|
189
|
+
settleInvoices.push(swap);
|
|
190
|
+
break;
|
|
191
|
+
case "REFUND":
|
|
192
|
+
refundSwaps.push(swap);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
|
|
184
198
|
}
|
|
185
199
|
}
|
|
186
200
|
await this.refundSwaps(refundSwaps);
|
|
@@ -257,10 +271,9 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
257
271
|
if (invoiceData.metadata != null)
|
|
258
272
|
invoiceData.metadata.times.htlcReceived = Date.now();
|
|
259
273
|
const useToken = invoiceData.token;
|
|
260
|
-
let expiryTimeout;
|
|
261
274
|
try {
|
|
262
275
|
//Check if HTLC expiry is long enough
|
|
263
|
-
|
|
276
|
+
await this.checkHtlcExpiry(invoice);
|
|
264
277
|
if (invoiceData.metadata != null)
|
|
265
278
|
invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
266
279
|
}
|
|
@@ -273,7 +286,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
273
286
|
}
|
|
274
287
|
const { swapContract, signer } = this.getChain(invoiceData.chainIdentifier);
|
|
275
288
|
//Create real swap data
|
|
276
|
-
const swapData = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, signer.getAddress(), invoiceData.claimer, useToken, invoiceData.amountToken, invoiceData.claimHash, 0n, BigInt(Math.floor(Date.now() / 1000)) +
|
|
289
|
+
const swapData = await swapContract.createSwapData(base_1.ChainSwapType.HTLC, signer.getAddress(), invoiceData.claimer, useToken, invoiceData.amountToken, invoiceData.claimHash, 0n, BigInt(Math.floor(Date.now() / 1000)) + this.config.destinationHtlcTimeoutSeconds, false, true, invoiceData.amountGasToken + invoiceData.claimerBounty, invoiceData.claimerBounty, invoiceData.gasToken);
|
|
277
290
|
if (invoiceData.metadata != null)
|
|
278
291
|
invoiceData.metadata.times.htlcSwapCreated = Date.now();
|
|
279
292
|
//Important to prevent race condition and issuing 2 signed init messages at the same time
|
|
@@ -440,24 +453,22 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
440
453
|
*
|
|
441
454
|
* @param invoice
|
|
442
455
|
* @throws {DefinedRuntimeError} Will throw if HTLC expires too soon and therefore cannot be processed
|
|
443
|
-
* @returns expiry timeout in seconds
|
|
444
456
|
*/
|
|
445
457
|
async checkHtlcExpiry(invoice) {
|
|
446
458
|
const timeout = this.getInvoicePaymentsTimeout(invoice);
|
|
447
459
|
const current_block_height = await this.lightning.getBlockheight();
|
|
448
460
|
const blockDelta = BigInt(timeout - current_block_height);
|
|
449
|
-
const htlcExpiresTooSoon = blockDelta < this.
|
|
461
|
+
const htlcExpiresTooSoon = blockDelta < this.minCltv;
|
|
450
462
|
if (htlcExpiresTooSoon) {
|
|
451
463
|
throw {
|
|
452
464
|
code: 20002,
|
|
453
465
|
msg: "Not enough time to reliably process the swap",
|
|
454
466
|
data: {
|
|
455
|
-
requiredDelta: this.
|
|
467
|
+
requiredDelta: this.minCltv.toString(10),
|
|
456
468
|
actualDelta: blockDelta.toString(10)
|
|
457
469
|
}
|
|
458
470
|
};
|
|
459
471
|
}
|
|
460
|
-
return (this.config.minCltv * this.config.bitcoinBlocktime / this.config.safetyFactor) - this.config.gracePeriod;
|
|
461
472
|
}
|
|
462
473
|
/**
|
|
463
474
|
* Cancels the swap (CANCELED state) & also cancels the LN invoice (including all pending HTLCs)
|
|
@@ -630,7 +641,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
630
641
|
//Create swap
|
|
631
642
|
const hodlInvoiceObj = {
|
|
632
643
|
description: description ?? (chainIdentifier + "-" + parsedBody.address),
|
|
633
|
-
cltvDelta: Number(this.
|
|
644
|
+
cltvDelta: Number(this.minCltv) + 5,
|
|
634
645
|
expiresAt: Date.now() + (this.config.invoiceTimeoutSeconds * 1000),
|
|
635
646
|
id: parsedBody.paymentHash,
|
|
636
647
|
mtokens: totalBtcInput * 1000n,
|
|
@@ -721,7 +732,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
721
732
|
};
|
|
722
733
|
}
|
|
723
734
|
return {
|
|
724
|
-
minCltv: Number(this.
|
|
735
|
+
minCltv: Number(this.minCltv),
|
|
725
736
|
invoiceTimeoutSeconds: this.config.invoiceTimeoutSeconds,
|
|
726
737
|
gasTokens: mappedDict
|
|
727
738
|
};
|
|
@@ -91,7 +91,7 @@ export declare class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSw
|
|
|
91
91
|
* @private
|
|
92
92
|
* @throws DefinedRuntimeError will throw an error in case the actual fee is higher than quoted fee
|
|
93
93
|
*/
|
|
94
|
-
protected checkCalculatedTxFee(quotedSatsPerVbyte:
|
|
94
|
+
protected checkCalculatedTxFee(quotedSatsPerVbyte: number, actualSatsPerVbyte: number): void;
|
|
95
95
|
/**
|
|
96
96
|
* Sends a bitcoin transaction to payout BTC for a swap
|
|
97
97
|
*
|
|
@@ -149,7 +149,8 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
149
149
|
}
|
|
150
150
|
]);
|
|
151
151
|
for (let { obj: swap } of queriedData) {
|
|
152
|
-
await this.processPastSwap(swap)
|
|
152
|
+
await this.processPastSwap(swap)
|
|
153
|
+
.catch(e => this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e));
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
async processBtcTx(swap, tx) {
|
|
@@ -285,7 +286,7 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
285
286
|
if (swap.metadata != null)
|
|
286
287
|
swap.metadata.times.payCLTVChecked = Date.now();
|
|
287
288
|
const satsPerVbyte = await this.bitcoin.getFeeRate();
|
|
288
|
-
this.checkCalculatedTxFee(swap.satsPerVbyte,
|
|
289
|
+
this.checkCalculatedTxFee(swap.satsPerVbyte, satsPerVbyte);
|
|
289
290
|
if (swap.metadata != null)
|
|
290
291
|
swap.metadata.times.payChainFee = Date.now();
|
|
291
292
|
const signResult = await this.bitcoin.getSignedTransaction(swap.address, Number(swap.amount), satsPerVbyte, swap.nonce, Number(swap.satsPerVbyte));
|
|
@@ -332,8 +333,14 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
332
333
|
const isTxSent = tx != null;
|
|
333
334
|
if (!isTxSent) {
|
|
334
335
|
//Reset the state to COMMITED
|
|
335
|
-
this.
|
|
336
|
-
|
|
336
|
+
const walletTx = await this.bitcoin.getWalletTransaction(swap.txId);
|
|
337
|
+
if (walletTx == null) {
|
|
338
|
+
this.swapLogger.info(swap, "processInitialized(state=BTC_SENDING): btc transaction not found, resetting to COMMITED state, txId: " + swap.txId + " address: " + swap.address);
|
|
339
|
+
await swap.setState(ToBtcSwapAbs_1.ToBtcSwapState.COMMITED);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
this.swapLogger.error(swap, "processInitialized(state=BTC_SENDING): btc transaction not found in mempool, but known to the wallet! txId: " + swap.txId + " address: " + swap.address);
|
|
343
|
+
}
|
|
337
344
|
}
|
|
338
345
|
else {
|
|
339
346
|
this.swapLogger.info(swap, "processInitialized(state=BTC_SENDING): btc transaction found, advancing to BTC_SENT state, txId: " + swap.txId + " address: " + swap.address);
|
|
@@ -536,7 +543,7 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
536
543
|
};
|
|
537
544
|
return {
|
|
538
545
|
networkFee: BigInt(chainFeeResp.networkFee),
|
|
539
|
-
satsPerVbyte:
|
|
546
|
+
satsPerVbyte: chainFeeResp.satsPerVbyte
|
|
540
547
|
};
|
|
541
548
|
}
|
|
542
549
|
startRestServer(restServer) {
|
|
@@ -13,13 +13,13 @@ export declare enum ToBtcSwapState {
|
|
|
13
13
|
export declare class ToBtcSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap<T, ToBtcSwapState> {
|
|
14
14
|
sending: boolean;
|
|
15
15
|
readonly address: string;
|
|
16
|
-
readonly satsPerVbyte:
|
|
16
|
+
readonly satsPerVbyte: number;
|
|
17
17
|
readonly nonce: bigint;
|
|
18
18
|
readonly requiredConfirmations: number;
|
|
19
19
|
readonly preferedConfirmationTarget: number;
|
|
20
20
|
btcRawTx: string;
|
|
21
21
|
txId: string;
|
|
22
|
-
constructor(chainIdentifier: string, address: string, amount: bigint, swapFee: bigint, swapFeeInToken: bigint, networkFee: bigint, networkFeeInToken: bigint, satsPerVbyte:
|
|
22
|
+
constructor(chainIdentifier: string, address: string, amount: bigint, swapFee: bigint, swapFeeInToken: bigint, networkFee: bigint, networkFeeInToken: bigint, satsPerVbyte: number, nonce: bigint, requiredConfirmations: number, preferedConfirmationTarget: number);
|
|
23
23
|
constructor(obj: any);
|
|
24
24
|
serialize(): any;
|
|
25
25
|
isInitiated(): boolean;
|
|
@@ -29,7 +29,7 @@ class ToBtcSwapAbs extends ToBtcBaseSwap_1.ToBtcBaseSwap {
|
|
|
29
29
|
else {
|
|
30
30
|
super(chainIdOrObj);
|
|
31
31
|
this.address = chainIdOrObj.address;
|
|
32
|
-
this.satsPerVbyte =
|
|
32
|
+
this.satsPerVbyte = Number(chainIdOrObj.satsPerVbyte);
|
|
33
33
|
this.nonce = BigInt(chainIdOrObj.nonce);
|
|
34
34
|
this.requiredConfirmations = chainIdOrObj.requiredConfirmations;
|
|
35
35
|
this.preferedConfirmationTarget = chainIdOrObj.preferedConfirmationTarget;
|
|
@@ -100,7 +100,8 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
100
100
|
}
|
|
101
101
|
]);
|
|
102
102
|
for (let { obj: swap } of queriedData) {
|
|
103
|
-
await this.processPastSwap(swap)
|
|
103
|
+
await this.processPastSwap(swap)
|
|
104
|
+
.catch(e => this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e));
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
/**
|
|
@@ -198,6 +199,10 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
198
199
|
this.swapLogger.info(invoiceData, "subscribeToPayment(): result callback, outcome: " + result.status + " invoice: " + invoiceData.pr);
|
|
199
200
|
this.processPaymentResult(invoiceData, result).catch(e => this.swapLogger.error(invoiceData, "subscribeToPayment(): process payment result", e));
|
|
200
201
|
this.activeSubscriptions.delete(paymentHash);
|
|
202
|
+
}).catch(e => {
|
|
203
|
+
this.swapLogger.error(invoiceData, "subscribeToPayment(): error while waiting for invoice payment: ", e);
|
|
204
|
+
//Delete from active subscription, the watchdog will re-subscribe later
|
|
205
|
+
this.activeSubscriptions.delete(paymentHash);
|
|
201
206
|
});
|
|
202
207
|
this.swapLogger.info(invoiceData, "subscribeToPayment(): subscribe to payment outcome, invoice: " + invoiceData.pr);
|
|
203
208
|
this.activeSubscriptions.add(paymentHash);
|
|
@@ -311,6 +316,9 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
311
316
|
else
|
|
312
317
|
throw e;
|
|
313
318
|
}
|
|
319
|
+
const unlock = swap.lock(120);
|
|
320
|
+
if (unlock == null)
|
|
321
|
+
return;
|
|
314
322
|
await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED);
|
|
315
323
|
await this.saveSwapData(swap);
|
|
316
324
|
try {
|
|
@@ -322,13 +330,29 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
322
330
|
if ((0, Utils_1.isDefinedRuntimeError)(e)) {
|
|
323
331
|
if (swap.metadata != null)
|
|
324
332
|
swap.metadata.payError = e;
|
|
325
|
-
await
|
|
326
|
-
|
|
327
|
-
|
|
333
|
+
const payment = await this.lightning.getPayment(swap.lnPaymentHash);
|
|
334
|
+
if (!unlock())
|
|
335
|
+
return;
|
|
336
|
+
if (payment == null || payment.status === "failed") {
|
|
337
|
+
if (swap.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.COMMITED)
|
|
338
|
+
await swap.setState(ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE);
|
|
339
|
+
await this.saveSwapData(swap);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
else if (payment.status === "confirmed") {
|
|
343
|
+
await this.processPaymentResult(swap, payment);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
//Fall-through to subscribe
|
|
348
|
+
}
|
|
328
349
|
}
|
|
329
350
|
else
|
|
330
351
|
throw e;
|
|
331
352
|
}
|
|
353
|
+
finally {
|
|
354
|
+
unlock();
|
|
355
|
+
}
|
|
332
356
|
this.subscribeToPayment(swap);
|
|
333
357
|
return;
|
|
334
358
|
}
|
|
@@ -832,6 +856,16 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
832
856
|
msg: "Payment expired"
|
|
833
857
|
};
|
|
834
858
|
if (data.state === ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE) {
|
|
859
|
+
if (data.payInitiated) {
|
|
860
|
+
//Check that the ln payment is indeed non-existent or failed
|
|
861
|
+
const lnPayment = await this.lightning.getPayment(data.lnPaymentHash);
|
|
862
|
+
if (lnPayment != null && lnPayment.status !== "failed")
|
|
863
|
+
throw {
|
|
864
|
+
_httpStatus: 200,
|
|
865
|
+
code: 20008,
|
|
866
|
+
msg: "Payment in-flight"
|
|
867
|
+
};
|
|
868
|
+
}
|
|
835
869
|
const refundSigData = await swapContract.getRefundSignature(signer, data.data, this.config.refundAuthorizationTimeout);
|
|
836
870
|
//Double check the state after promise result
|
|
837
871
|
if (data.state !== ToBtcLnSwapAbs_1.ToBtcLnSwapState.NON_PAYABLE)
|
|
@@ -223,7 +223,8 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
223
223
|
}
|
|
224
224
|
]);
|
|
225
225
|
for (let { obj: swap } of swaps) {
|
|
226
|
-
await this.processPastSwap(swap)
|
|
226
|
+
await this.processPastSwap(swap)
|
|
227
|
+
.catch(e => this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e));
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
getPricePrefetches(chainIdentifier, token, gasToken, abortController) {
|
|
@@ -615,14 +616,12 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
615
616
|
};
|
|
616
617
|
const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreFromBtcExecute(SwapHandler_1.SwapHandlerType.FROM_BTC_SPV, swap);
|
|
617
618
|
if ((0, IPlugin_1.isQuoteThrow)(pluginCheckResult)) {
|
|
618
|
-
|
|
619
|
+
if (swap.state === SpvVaultSwap_1.SpvVaultSwapState.CREATED)
|
|
620
|
+
await this.removeSwapData(swap, SpvVaultSwap_1.SpvVaultSwapState.FAILED);
|
|
621
|
+
throw {
|
|
619
622
|
code: 29999,
|
|
620
623
|
msg: pluginCheckResult.message
|
|
621
624
|
};
|
|
622
|
-
if (swap.metadata != null)
|
|
623
|
-
swap.metadata.postQuoteError = error;
|
|
624
|
-
await this.removeSwapData(swap, SpvVaultSwap_1.SpvVaultSwapState.FAILED);
|
|
625
|
-
throw error;
|
|
626
625
|
}
|
|
627
626
|
await this.Vaults.checkVaultReplacedTransactions(vault, true);
|
|
628
627
|
if (swap.vaultUtxo !== vault.getLatestUtxo()) {
|
|
@@ -631,24 +630,33 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
631
630
|
msg: "Vault UTXO already spent, please get another quote and try again!"
|
|
632
631
|
};
|
|
633
632
|
}
|
|
633
|
+
const unlock = swap.lock(120);
|
|
634
|
+
if (!unlock)
|
|
635
|
+
throw {
|
|
636
|
+
code: 20517,
|
|
637
|
+
msg: "Bitcoin transaction submission already in progress, please retry later!"
|
|
638
|
+
};
|
|
639
|
+
let swapSendingSet = false;
|
|
640
|
+
let dataSendingSet = false;
|
|
634
641
|
try {
|
|
635
642
|
const btcRawTx = Buffer.from(signedTx.toBytes(true, true)).toString("hex");
|
|
636
643
|
//Double-check the state to prevent race condition
|
|
637
|
-
if (swap.state !== SpvVaultSwap_1.SpvVaultSwapState.CREATED)
|
|
644
|
+
if (swap.state !== SpvVaultSwap_1.SpvVaultSwapState.CREATED)
|
|
638
645
|
throw {
|
|
639
646
|
code: 20505,
|
|
640
647
|
msg: "Invalid quote ID, not found or expired!"
|
|
641
648
|
};
|
|
642
|
-
}
|
|
643
649
|
//Double check in-flight swap count
|
|
644
650
|
this.checkTooManyInflightSwaps();
|
|
645
651
|
swap.btcTxId = signedTx.id;
|
|
646
652
|
swap.state = SpvVaultSwap_1.SpvVaultSwapState.SIGNED;
|
|
647
653
|
swap.sending = true;
|
|
654
|
+
swapSendingSet = true;
|
|
648
655
|
await this.saveSwapData(swap);
|
|
649
656
|
data.btcTx.raw = btcRawTx;
|
|
650
657
|
data.sending = true;
|
|
651
658
|
vault.addWithdrawal(data);
|
|
659
|
+
dataSendingSet = true;
|
|
652
660
|
await this.Vaults.saveVault(vault);
|
|
653
661
|
this.swapLogger.info(swap, "REST: /postQuote: BTC transaction signed, txId: " + swap.btcTxId);
|
|
654
662
|
try {
|
|
@@ -666,15 +674,27 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
666
674
|
}
|
|
667
675
|
}
|
|
668
676
|
catch (e) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
677
|
+
if (swapSendingSet)
|
|
678
|
+
swap.sending = false;
|
|
679
|
+
if (dataSendingSet) {
|
|
680
|
+
data.sending = false;
|
|
681
|
+
vault.removeWithdrawal(data);
|
|
682
|
+
await this.Vaults.saveVault(vault);
|
|
683
|
+
}
|
|
684
|
+
//Check if the error is only because the state has already changed
|
|
685
|
+
if (!(0, Utils_1.isDefinedRuntimeError)(e) || e.code !== 20505) {
|
|
686
|
+
//We only make the swap failed if the error happened in CREATED or SIGNED states
|
|
687
|
+
if (swap.state === SpvVaultSwap_1.SpvVaultSwapState.CREATED || swap.state === SpvVaultSwap_1.SpvVaultSwapState.SIGNED) {
|
|
688
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && swap.metadata != null)
|
|
689
|
+
swap.metadata.postQuoteError = e;
|
|
690
|
+
await this.removeSwapData(swap, SpvVaultSwap_1.SpvVaultSwapState.FAILED);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
676
693
|
throw e;
|
|
677
694
|
}
|
|
695
|
+
finally {
|
|
696
|
+
unlock();
|
|
697
|
+
}
|
|
678
698
|
await responseStream.writeParamsAndEnd({
|
|
679
699
|
code: 20000,
|
|
680
700
|
msg: "Success",
|
|
@@ -246,7 +246,7 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
246
246
|
if (swap.state !== FromBtcTrustedSwap_1.FromBtcTrustedSwapState.BTC_CONFIRMED)
|
|
247
247
|
return;
|
|
248
248
|
const txns = await chainInterface.txsTransfer(signer.getAddress(), swap.token, swap.adjustedOutput, swap.dstAddress);
|
|
249
|
-
let unlock = swap.lock(30
|
|
249
|
+
let unlock = swap.lock(30);
|
|
250
250
|
if (unlock == null)
|
|
251
251
|
return;
|
|
252
252
|
const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreFromBtcExecute(SwapHandler_1.SwapHandlerType.FROM_BTC_TRUSTED, swap);
|
|
@@ -341,7 +341,7 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
341
341
|
await this.processPastSwap(swap, txs[0]?.tx, txs[0]?.vout);
|
|
342
342
|
}
|
|
343
343
|
catch (e) {
|
|
344
|
-
this.swapLogger.error(swap, "
|
|
344
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
}
|