@atomiqlabs/lp-lib 17.5.3 → 17.6.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/assertions/FromBtcAmountAssertions.js +2 -2
- package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +45 -19
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +2 -2
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +35 -25
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +2 -2
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +33 -22
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +1 -1
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +29 -11
- 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.d.ts +1 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +54 -9
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +83 -30
- package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +2 -2
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +11 -17
- package/dist/utils/Utils.d.ts +33 -0
- package/dist/utils/Utils.js +51 -1
- package/dist/utils/paramcoders/ParamDecoder.js +4 -86
- package/dist/utils/paramcoders/SchemaVerifier.d.ts +11 -2
- package/dist/utils/paramcoders/SchemaVerifier.js +93 -47
- package/package.json +1 -1
- package/src/swaps/assertions/FromBtcAmountAssertions.ts +2 -2
- package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +44 -19
- package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +47 -30
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +45 -27
- package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +28 -13
- package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +4 -4
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +63 -13
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +79 -33
- package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +25 -2
- package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +10 -16
- package/src/utils/Utils.ts +48 -0
- package/src/utils/paramcoders/ParamDecoder.ts +10 -66
- package/src/utils/paramcoders/SchemaVerifier.ts +80 -37
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.verifySchema = exports.FieldTypeEnum = exports.parseBigInt = void 0;
|
|
3
|
+
exports.verifySchema = exports.verifySchemaField = exports.FieldTypeEnum = exports.parseBigInt = void 0;
|
|
4
4
|
function parseBigInt(str) {
|
|
5
5
|
if (str == null)
|
|
6
6
|
return null;
|
|
@@ -21,63 +21,109 @@ var FieldTypeEnum;
|
|
|
21
21
|
FieldTypeEnum[FieldTypeEnum["Number"] = 2] = "Number";
|
|
22
22
|
FieldTypeEnum[FieldTypeEnum["BigInt"] = 3] = "BigInt";
|
|
23
23
|
FieldTypeEnum[FieldTypeEnum["Any"] = 4] = "Any";
|
|
24
|
+
FieldTypeEnum[FieldTypeEnum["BigIntPositive"] = 5] = "BigIntPositive";
|
|
25
|
+
FieldTypeEnum[FieldTypeEnum["BigIntNotNegative"] = 6] = "BigIntNotNegative";
|
|
26
|
+
FieldTypeEnum[FieldTypeEnum["NumberPositive"] = 7] = "NumberPositive";
|
|
27
|
+
FieldTypeEnum[FieldTypeEnum["NumberNotNegative"] = 8] = "NumberNotNegative";
|
|
24
28
|
FieldTypeEnum[FieldTypeEnum["StringOptional"] = 100] = "StringOptional";
|
|
25
29
|
FieldTypeEnum[FieldTypeEnum["BooleanOptional"] = 101] = "BooleanOptional";
|
|
26
30
|
FieldTypeEnum[FieldTypeEnum["NumberOptional"] = 102] = "NumberOptional";
|
|
27
31
|
FieldTypeEnum[FieldTypeEnum["BigIntOptional"] = 103] = "BigIntOptional";
|
|
28
32
|
FieldTypeEnum[FieldTypeEnum["AnyOptional"] = 104] = "AnyOptional";
|
|
33
|
+
FieldTypeEnum[FieldTypeEnum["BigIntPositiveOptional"] = 105] = "BigIntPositiveOptional";
|
|
34
|
+
FieldTypeEnum[FieldTypeEnum["BigIntNotNegativeOptional"] = 106] = "BigIntNotNegativeOptional";
|
|
35
|
+
FieldTypeEnum[FieldTypeEnum["NumberPositiveOptional"] = 107] = "NumberPositiveOptional";
|
|
36
|
+
FieldTypeEnum[FieldTypeEnum["NumberNotNegativeOptional"] = 108] = "NumberNotNegativeOptional";
|
|
29
37
|
})(FieldTypeEnum = exports.FieldTypeEnum || (exports.FieldTypeEnum = {}));
|
|
38
|
+
function verifySchemaField(val, type, fieldName, resultSchema) {
|
|
39
|
+
if (typeof (type) === "function") {
|
|
40
|
+
const result = type(val);
|
|
41
|
+
if (result == null)
|
|
42
|
+
return false;
|
|
43
|
+
resultSchema[fieldName] = result;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (val == null && type >= 100) {
|
|
47
|
+
resultSchema[fieldName] = null;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (type === FieldTypeEnum.Any || type === FieldTypeEnum.AnyOptional) {
|
|
51
|
+
resultSchema[fieldName] = val;
|
|
52
|
+
}
|
|
53
|
+
else if (type === FieldTypeEnum.Boolean || type === FieldTypeEnum.BooleanOptional) {
|
|
54
|
+
if (typeof (val) !== "boolean")
|
|
55
|
+
return false;
|
|
56
|
+
resultSchema[fieldName] = val;
|
|
57
|
+
}
|
|
58
|
+
else if (type === FieldTypeEnum.Number || type === FieldTypeEnum.NumberOptional) {
|
|
59
|
+
if (typeof (val) !== "number")
|
|
60
|
+
return false;
|
|
61
|
+
if (isNaN(val))
|
|
62
|
+
return false;
|
|
63
|
+
resultSchema[fieldName] = val;
|
|
64
|
+
}
|
|
65
|
+
else if (type === FieldTypeEnum.NumberPositive || type === FieldTypeEnum.NumberPositiveOptional) {
|
|
66
|
+
if (typeof (val) !== "number")
|
|
67
|
+
return false;
|
|
68
|
+
if (isNaN(val))
|
|
69
|
+
return false;
|
|
70
|
+
if (val <= 0)
|
|
71
|
+
return false;
|
|
72
|
+
resultSchema[fieldName] = val;
|
|
73
|
+
}
|
|
74
|
+
else if (type === FieldTypeEnum.NumberNotNegative || type === FieldTypeEnum.NumberNotNegativeOptional) {
|
|
75
|
+
if (typeof (val) !== "number")
|
|
76
|
+
return false;
|
|
77
|
+
if (isNaN(val))
|
|
78
|
+
return false;
|
|
79
|
+
if (val < 0)
|
|
80
|
+
return false;
|
|
81
|
+
resultSchema[fieldName] = val;
|
|
82
|
+
}
|
|
83
|
+
else if (type === FieldTypeEnum.BigInt || type === FieldTypeEnum.BigIntOptional) {
|
|
84
|
+
const result = parseBigInt(val);
|
|
85
|
+
if (result == null)
|
|
86
|
+
return false;
|
|
87
|
+
resultSchema[fieldName] = result;
|
|
88
|
+
}
|
|
89
|
+
else if (type === FieldTypeEnum.BigIntPositive || type === FieldTypeEnum.BigIntPositiveOptional) {
|
|
90
|
+
const result = parseBigInt(val);
|
|
91
|
+
if (result == null)
|
|
92
|
+
return false;
|
|
93
|
+
if (result <= 0n)
|
|
94
|
+
return false;
|
|
95
|
+
resultSchema[fieldName] = result;
|
|
96
|
+
}
|
|
97
|
+
else if (type === FieldTypeEnum.BigIntNotNegative || type === FieldTypeEnum.BigIntNotNegativeOptional) {
|
|
98
|
+
const result = parseBigInt(val);
|
|
99
|
+
if (result == null)
|
|
100
|
+
return false;
|
|
101
|
+
if (result < 0n)
|
|
102
|
+
return false;
|
|
103
|
+
resultSchema[fieldName] = result;
|
|
104
|
+
}
|
|
105
|
+
else if (type === FieldTypeEnum.String || type === FieldTypeEnum.StringOptional) {
|
|
106
|
+
if (typeof (val) !== "string")
|
|
107
|
+
return false;
|
|
108
|
+
resultSchema[fieldName] = val;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
//Probably another request schema
|
|
112
|
+
const result = verifySchema(val, type);
|
|
113
|
+
if (result == null)
|
|
114
|
+
return false;
|
|
115
|
+
resultSchema[fieldName] = result;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
exports.verifySchemaField = verifySchemaField;
|
|
30
120
|
function verifySchema(req, schema) {
|
|
31
121
|
if (req == null)
|
|
32
122
|
return null;
|
|
33
123
|
const resultSchema = {};
|
|
34
124
|
for (let fieldName in schema) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (typeof (type) === "function") {
|
|
38
|
-
const result = type(val);
|
|
39
|
-
if (result == null)
|
|
40
|
-
return null;
|
|
41
|
-
resultSchema[fieldName] = result;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (val == null && type >= 100) {
|
|
45
|
-
resultSchema[fieldName] = null;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (type === FieldTypeEnum.Any || type === FieldTypeEnum.AnyOptional) {
|
|
49
|
-
resultSchema[fieldName] = val;
|
|
50
|
-
}
|
|
51
|
-
else if (type === FieldTypeEnum.Boolean || type === FieldTypeEnum.BooleanOptional) {
|
|
52
|
-
if (typeof (val) !== "boolean")
|
|
53
|
-
return null;
|
|
54
|
-
resultSchema[fieldName] = val;
|
|
55
|
-
}
|
|
56
|
-
else if (type === FieldTypeEnum.Number || type === FieldTypeEnum.NumberOptional) {
|
|
57
|
-
if (typeof (val) !== "number")
|
|
58
|
-
return null;
|
|
59
|
-
if (isNaN(val))
|
|
60
|
-
return null;
|
|
61
|
-
resultSchema[fieldName] = val;
|
|
62
|
-
}
|
|
63
|
-
else if (type === FieldTypeEnum.BigInt || type === FieldTypeEnum.BigIntOptional) {
|
|
64
|
-
const result = parseBigInt(val);
|
|
65
|
-
if (result == null)
|
|
66
|
-
return null;
|
|
67
|
-
resultSchema[fieldName] = result;
|
|
68
|
-
}
|
|
69
|
-
else if (type === FieldTypeEnum.String || type === FieldTypeEnum.StringOptional) {
|
|
70
|
-
if (typeof (val) !== "string")
|
|
71
|
-
return null;
|
|
72
|
-
resultSchema[fieldName] = val;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
//Probably another request schema
|
|
76
|
-
const result = verifySchema(val, type);
|
|
77
|
-
if (result == null)
|
|
78
|
-
return null;
|
|
79
|
-
resultSchema[fieldName] = result;
|
|
80
|
-
}
|
|
125
|
+
if (!verifySchemaField(req[fieldName], schema[fieldName], fieldName, resultSchema))
|
|
126
|
+
return null;
|
|
81
127
|
}
|
|
82
128
|
return resultSchema;
|
|
83
129
|
}
|
package/package.json
CHANGED
|
@@ -186,8 +186,8 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
186
186
|
const tooLow = amountBD < (min * 95n / 100n);
|
|
187
187
|
const tooHigh = amountBD > (max * 105n / 100n);
|
|
188
188
|
if(tooLow || tooHigh) {
|
|
189
|
-
const adjustedMin = min * (1000000n - fees.feePPM) /
|
|
190
|
-
const adjustedMax = max * (1000000n - fees.feePPM) /
|
|
189
|
+
const adjustedMin = min * (1000000n - fees.feePPM) / 1_000_000n - fees.baseFee;
|
|
190
|
+
const adjustedMax = max * (1000000n - fees.feePPM) / 1_000_000n - fees.baseFee;
|
|
191
191
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(
|
|
192
192
|
adjustedMin, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch
|
|
193
193
|
);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ClaimEvent,
|
|
9
9
|
InitializeEvent,
|
|
10
10
|
RefundEvent,
|
|
11
|
+
SwapCommitStateType,
|
|
11
12
|
SwapData
|
|
12
13
|
} from "@atomiqlabs/base";
|
|
13
14
|
import {randomBytes} from "crypto";
|
|
@@ -21,7 +22,6 @@ import {ServerParamEncoder} from "../../../utils/paramcoders/server/ServerParamE
|
|
|
21
22
|
import {FromBtcBaseConfig, FromBtcBaseSwapHandler} from "../FromBtcBaseSwapHandler";
|
|
22
23
|
import {IBitcoinWallet} from "../../../wallets/IBitcoinWallet";
|
|
23
24
|
import {isQuoteThrow} from "../../../plugins/IPlugin";
|
|
24
|
-
import {FromBtcLnSwapState} from "../frombtcln_abstract/FromBtcLnSwapAbs";
|
|
25
25
|
|
|
26
26
|
export type FromBtcConfig = FromBtcBaseConfig & {
|
|
27
27
|
confirmations: number,
|
|
@@ -74,7 +74,7 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
74
74
|
private getHash(chainIdentifier: string, address: string, amount: bigint): Buffer {
|
|
75
75
|
const parsedOutputScript = this.bitcoin.toOutputScript(address);
|
|
76
76
|
const {swapContract} = this.getChain(chainIdentifier);
|
|
77
|
-
return swapContract.getHashForOnchain(parsedOutputScript, amount, this.config.confirmations
|
|
77
|
+
return swapContract.getHashForOnchain(parsedOutputScript, amount, this.config.confirmations);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -91,13 +91,18 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
91
91
|
if(swap.state===FromBtcSwapState.CREATED) {
|
|
92
92
|
if(!await swapContract.isInitAuthorizationExpired(swap.data, swap)) return false;
|
|
93
93
|
|
|
94
|
-
const
|
|
95
|
-
if(
|
|
94
|
+
const commitState = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
95
|
+
if(commitState.type===SwapCommitStateType.COMMITED || commitState.type===SwapCommitStateType.REFUNDABLE) {
|
|
96
96
|
this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap was commited, but processed from watchdog, address: "+swap.address);
|
|
97
97
|
await swap.setState(FromBtcSwapState.COMMITED);
|
|
98
98
|
await this.saveSwapData(swap);
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
|
+
if(commitState.type===SwapCommitStateType.PAID) {
|
|
102
|
+
this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap was claimed, but processed from watchdog, address: "+swap.address);
|
|
103
|
+
await this.removeSwapData(swap, FromBtcSwapState.CLAIMED);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
101
106
|
|
|
102
107
|
this.swapLogger.info(swap, "processPastSwap(state=CREATED): removing past swap due to authorization expiry, address: "+swap.address);
|
|
103
108
|
await this.removeSwapData(swap, FromBtcSwapState.CANCELED);
|
|
@@ -109,11 +114,16 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
109
114
|
if(swap.state===FromBtcSwapState.COMMITED) {
|
|
110
115
|
if(!await swapContract.isExpired(signer.getAddress(), swap.data)) return false;
|
|
111
116
|
|
|
112
|
-
const
|
|
113
|
-
if(
|
|
117
|
+
const commitState = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
118
|
+
if(commitState.type===SwapCommitStateType.COMMITED || commitState.type===SwapCommitStateType.REFUNDABLE) {
|
|
114
119
|
this.swapLogger.info(swap, "processPastSwap(state=COMMITED): swap expired, will refund, address: "+swap.address);
|
|
115
120
|
return true;
|
|
116
121
|
}
|
|
122
|
+
if(commitState.type===SwapCommitStateType.PAID) {
|
|
123
|
+
this.swapLogger.info(swap, "processPastSwap(state=COMMITED): swap was claimed, but processed from watchdog, address: "+swap.address);
|
|
124
|
+
await this.removeSwapData(swap, FromBtcSwapState.CLAIMED);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
117
127
|
|
|
118
128
|
this.swapLogger.warn(swap, "processPastSwap(state=COMMITED): commited swap expired and not committed anymore (already refunded?), address: "+swap.address);
|
|
119
129
|
await this.removeSwapData(swap, FromBtcSwapState.CANCELED);
|
|
@@ -139,7 +149,11 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
139
149
|
const refundSwaps: FromBtcSwapAbs[] = [];
|
|
140
150
|
|
|
141
151
|
for(let {obj: swap} of queriedData) {
|
|
142
|
-
|
|
152
|
+
try {
|
|
153
|
+
if (await this.processPastSwap(swap)) refundSwaps.push(swap);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
|
|
156
|
+
}
|
|
143
157
|
}
|
|
144
158
|
|
|
145
159
|
await this.refundSwaps(refundSwaps);
|
|
@@ -156,11 +170,16 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
156
170
|
const {swapContract, signer} = this.getChain(refundSwap.chainIdentifier);
|
|
157
171
|
const unlock = refundSwap.lock(swapContract.refundTimeout);
|
|
158
172
|
if(unlock==null) continue;
|
|
173
|
+
|
|
159
174
|
this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
try {
|
|
176
|
+
await swapContract.refund(signer, refundSwap.data, true, false, {waitForConfirmation: true});
|
|
177
|
+
this.swapLogger.info(refundSwap, "refundSwaps(): swap refunded, address: "+refundSwap.address);
|
|
178
|
+
//The swap should be removed by the event handler
|
|
179
|
+
await refundSwap.setState(FromBtcSwapState.REFUNDED);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
|
|
182
|
+
}
|
|
164
183
|
unlock();
|
|
165
184
|
}
|
|
166
185
|
}
|
|
@@ -201,11 +220,11 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
201
220
|
private async getClaimerBounty(req: Request & {paramReader: IParamReader}, expiry: bigint, signal: AbortSignal): Promise<bigint> {
|
|
202
221
|
const parsedClaimerBounty = await req.paramReader.getParams({
|
|
203
222
|
claimerBounty: {
|
|
204
|
-
feePerBlock: FieldTypeEnum.
|
|
205
|
-
safetyFactor: FieldTypeEnum.
|
|
206
|
-
startTimestamp: FieldTypeEnum.
|
|
207
|
-
addBlock: FieldTypeEnum.
|
|
208
|
-
addFee: FieldTypeEnum.
|
|
223
|
+
feePerBlock: FieldTypeEnum.BigIntNotNegative,
|
|
224
|
+
safetyFactor: FieldTypeEnum.BigIntNotNegative,
|
|
225
|
+
startTimestamp: FieldTypeEnum.BigIntNotNegative,
|
|
226
|
+
addBlock: FieldTypeEnum.BigIntNotNegative,
|
|
227
|
+
addFee: FieldTypeEnum.BigIntNotNegative,
|
|
209
228
|
},
|
|
210
229
|
}).catch(e => null);
|
|
211
230
|
|
|
@@ -219,6 +238,12 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
219
238
|
}
|
|
220
239
|
|
|
221
240
|
const tsDelta: bigint = expiry - parsedClaimerBounty.claimerBounty.startTimestamp;
|
|
241
|
+
if(tsDelta < 0n) {
|
|
242
|
+
throw {
|
|
243
|
+
code: 20043,
|
|
244
|
+
msg: "Invalid claimerBounty (ts delta < 0)"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
222
247
|
const blocksDelta: bigint = tsDelta / this.config.bitcoinBlocktime * parsedClaimerBounty.claimerBounty.safetyFactor;
|
|
223
248
|
const totalBlock: bigint = blocksDelta + parsedClaimerBounty.claimerBounty.addBlock;
|
|
224
249
|
return parsedClaimerBounty.claimerBounty.addFee + (totalBlock * parsedClaimerBounty.claimerBounty.feePerBlock);
|
|
@@ -233,7 +258,7 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
233
258
|
address,
|
|
234
259
|
useToken,
|
|
235
260
|
dummyAmount,
|
|
236
|
-
swapContract.getHashForOnchain(randomBytes(32), dummyAmount, 3
|
|
261
|
+
swapContract.getHashForOnchain(randomBytes(32), dummyAmount, 3).toString("hex"),
|
|
237
262
|
BigIntBufferUtils.fromBuffer(randomBytes(8)),
|
|
238
263
|
BigInt(Math.floor(Date.now()/1000)) + this.config.swapTsCsvDelta,
|
|
239
264
|
false,
|
|
@@ -283,11 +308,11 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
|
|
|
283
308
|
address: (val: string) => val!=null &&
|
|
284
309
|
typeof(val)==="string" &&
|
|
285
310
|
chainInterface.isValidAddress(val, true) ? val : null,
|
|
286
|
-
amount: FieldTypeEnum.
|
|
311
|
+
amount: FieldTypeEnum.BigIntPositive,
|
|
287
312
|
token: (val: string) => val!=null &&
|
|
288
313
|
typeof(val)==="string" &&
|
|
289
314
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
290
|
-
sequence: FieldTypeEnum.
|
|
315
|
+
sequence: FieldTypeEnum.BigIntNotNegative,
|
|
291
316
|
exactOut: FieldTypeEnum.BooleanOptional
|
|
292
317
|
});
|
|
293
318
|
if(parsedBody==null) throw {
|
|
@@ -12,7 +12,13 @@ import {
|
|
|
12
12
|
SwapCommitStateType,
|
|
13
13
|
SwapData
|
|
14
14
|
} from "@atomiqlabs/base";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
bigIntMax,
|
|
17
|
+
expressHandlerWrapper,
|
|
18
|
+
getAbortController, getMinSafeBlockWindowFast,
|
|
19
|
+
HEX_REGEX,
|
|
20
|
+
isDefinedRuntimeError
|
|
21
|
+
} from "../../../utils/Utils";
|
|
16
22
|
import {PluginManager} from "../../../plugins/PluginManager";
|
|
17
23
|
import {IIntermediaryStorage} from "../../../storage/IIntermediaryStorage";
|
|
18
24
|
import {FieldTypeEnum, verifySchema} from "../../../utils/paramcoders/SchemaVerifier";
|
|
@@ -32,7 +38,7 @@ import {FromBtcLnAutoSwapState} from "../frombtcln_autoinit/FromBtcLnAutoSwap";
|
|
|
32
38
|
|
|
33
39
|
export type FromBtcLnConfig = FromBtcBaseConfig & {
|
|
34
40
|
invoiceTimeoutSeconds?: number,
|
|
35
|
-
|
|
41
|
+
destinationHtlcTimeoutSeconds: bigint,
|
|
36
42
|
gracePeriod: bigint
|
|
37
43
|
}
|
|
38
44
|
|
|
@@ -57,6 +63,8 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
57
63
|
readonly lightning: ILightningWallet;
|
|
58
64
|
readonly LightningAssertions: LightningAssertions;
|
|
59
65
|
|
|
66
|
+
readonly minCltv: bigint;
|
|
67
|
+
|
|
60
68
|
constructor(
|
|
61
69
|
storageDirectory: IIntermediaryStorage<FromBtcLnSwapAbs>,
|
|
62
70
|
path: string,
|
|
@@ -70,6 +78,13 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
70
78
|
this.config.invoiceTimeoutSeconds = this.config.invoiceTimeoutSeconds || 90;
|
|
71
79
|
this.lightning = lightning;
|
|
72
80
|
this.LightningAssertions = new LightningAssertions(this.logger, lightning);
|
|
81
|
+
|
|
82
|
+
const numerator = (this.config.destinationHtlcTimeoutSeconds + this.config.gracePeriod) * this.config.safetyFactor;
|
|
83
|
+
this.minCltv = bigIntMax(
|
|
84
|
+
(numerator + this.config.bitcoinBlocktime - 1n)
|
|
85
|
+
/ this.config.bitcoinBlocktime, //Ceil division
|
|
86
|
+
getMinSafeBlockWindowFast(this.config.safetyFactor)
|
|
87
|
+
);
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
protected async processPastSwap(swap: FromBtcLnSwapAbs): Promise<"REFUND" | "SETTLE" | "CANCEL" | null> {
|
|
@@ -169,10 +184,13 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
169
184
|
if(unlock==null) continue;
|
|
170
185
|
|
|
171
186
|
this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
187
|
+
try {
|
|
188
|
+
await swapContract.refund(signer, refundSwap.data, true, false, {waitForConfirmation: true});
|
|
189
|
+
this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: "+refundSwap.pr);
|
|
190
|
+
await refundSwap.setState(FromBtcLnSwapState.REFUNDED);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
|
|
193
|
+
}
|
|
176
194
|
unlock();
|
|
177
195
|
}
|
|
178
196
|
}
|
|
@@ -228,16 +246,20 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
228
246
|
]);
|
|
229
247
|
|
|
230
248
|
for(let {obj: swap} of queriedData) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
try {
|
|
250
|
+
switch (await this.processPastSwap(swap)) {
|
|
251
|
+
case "CANCEL":
|
|
252
|
+
cancelInvoices.push(swap);
|
|
253
|
+
break;
|
|
254
|
+
case "SETTLE":
|
|
255
|
+
settleInvoices.push(swap);
|
|
256
|
+
break;
|
|
257
|
+
case "REFUND":
|
|
258
|
+
refundSwaps.push(swap);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e)
|
|
241
263
|
}
|
|
242
264
|
}
|
|
243
265
|
|
|
@@ -327,14 +349,13 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
327
349
|
const blockheightPrefetch = this.getBlockheightPrefetch(abortController);
|
|
328
350
|
const signDataPrefetchPromise: Promise<any> = this.getSignDataPrefetch(invoiceData.chainIdentifier, abortController);
|
|
329
351
|
|
|
330
|
-
let expiryTimeout: bigint;
|
|
331
352
|
try {
|
|
332
353
|
//Check if we have enough liquidity to proceed
|
|
333
354
|
await this.checkBalance(escrowAmount, balancePrefetch, abortController.signal);
|
|
334
355
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcBalanceChecked = Date.now();
|
|
335
356
|
|
|
336
357
|
//Check if HTLC expiry is long enough
|
|
337
|
-
|
|
358
|
+
await this.checkHtlcExpiry(invoice, blockheightPrefetch, abortController.signal);
|
|
338
359
|
if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcTimeoutCalculated = Date.now();
|
|
339
360
|
} catch (e) {
|
|
340
361
|
if(!abortController.signal.aborted) {
|
|
@@ -355,7 +376,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
355
376
|
escrowAmount,
|
|
356
377
|
invoiceData.claimHash,
|
|
357
378
|
0n,
|
|
358
|
-
BigInt(Math.floor(Date.now() / 1000)) +
|
|
379
|
+
BigInt(Math.floor(Date.now() / 1000)) + this.config.destinationHtlcTimeoutSeconds,
|
|
359
380
|
false,
|
|
360
381
|
true,
|
|
361
382
|
invoiceData.securityDeposit,
|
|
@@ -493,28 +514,25 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
493
514
|
* @param blockheightPrefetch
|
|
494
515
|
* @param signal
|
|
495
516
|
* @throws {DefinedRuntimeError} Will throw if HTLC expires too soon and therefore cannot be processed
|
|
496
|
-
* @returns expiry timeout in seconds
|
|
497
517
|
*/
|
|
498
|
-
private async checkHtlcExpiry(invoice: LightningNetworkInvoice, blockheightPrefetch: Promise<number>, signal: AbortSignal): Promise<
|
|
518
|
+
private async checkHtlcExpiry(invoice: LightningNetworkInvoice, blockheightPrefetch: Promise<number>, signal: AbortSignal): Promise<void> {
|
|
499
519
|
const timeout: number = this.getInvoicePaymentsTimeout(invoice);
|
|
500
520
|
const current_block_height = await blockheightPrefetch;
|
|
501
521
|
signal.throwIfAborted();
|
|
502
522
|
|
|
503
523
|
const blockDelta = BigInt(timeout - current_block_height);
|
|
504
524
|
|
|
505
|
-
const htlcExpiresTooSoon = blockDelta < this.
|
|
525
|
+
const htlcExpiresTooSoon = blockDelta < this.minCltv;
|
|
506
526
|
if(htlcExpiresTooSoon) {
|
|
507
527
|
throw {
|
|
508
528
|
code: 20002,
|
|
509
529
|
msg: "Not enough time to reliably process the swap",
|
|
510
530
|
data: {
|
|
511
|
-
requiredDelta: this.
|
|
531
|
+
requiredDelta: this.minCltv.toString(10),
|
|
512
532
|
actualDelta: blockDelta.toString(10)
|
|
513
533
|
}
|
|
514
534
|
};
|
|
515
535
|
}
|
|
516
|
-
|
|
517
|
-
return (this.config.minCltv * this.config.bitcoinBlocktime / this.config.safetyFactor) - this.config.gracePeriod;
|
|
518
536
|
}
|
|
519
537
|
|
|
520
538
|
/**
|
|
@@ -628,7 +646,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
628
646
|
typeof(val)==="string" &&
|
|
629
647
|
val.length===64 &&
|
|
630
648
|
HEX_REGEX.test(val) ? val: null,
|
|
631
|
-
amount: FieldTypeEnum.
|
|
649
|
+
amount: FieldTypeEnum.BigIntPositive,
|
|
632
650
|
token: (val: string) => val!=null &&
|
|
633
651
|
typeof(val)==="string" &&
|
|
634
652
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
@@ -711,7 +729,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
711
729
|
//Create swap
|
|
712
730
|
const hodlInvoiceObj: HodlInvoiceInit = {
|
|
713
731
|
description: description ?? (chainIdentifier+"-"+parsedBody.address),
|
|
714
|
-
cltvDelta: Number(this.
|
|
732
|
+
cltvDelta: Number(this.minCltv) + 5,
|
|
715
733
|
expiresAt: Date.now()+(this.config.invoiceTimeoutSeconds*1000),
|
|
716
734
|
id: parsedBody.paymentHash,
|
|
717
735
|
mtokens: amountBD * 1000n,
|
|
@@ -725,9 +743,8 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
725
743
|
metadata.invoiceResponse = {...hodlInvoice};
|
|
726
744
|
|
|
727
745
|
//Pre-compute the security deposit
|
|
728
|
-
const expiryTimeout = (this.config.minCltv * this.config.bitcoinBlocktime / this.config.safetyFactor) - this.config.gracePeriod;
|
|
729
746
|
const totalSecurityDeposit = await this.getSecurityDeposit(
|
|
730
|
-
chainIdentifier, amountBD, swapFee,
|
|
747
|
+
chainIdentifier, amountBD, swapFee, this.config.destinationHtlcTimeoutSeconds,
|
|
731
748
|
baseSDPromise, depositToken, depositTokenPricePrefetchPromise, fees,
|
|
732
749
|
abortController.signal, metadata
|
|
733
750
|
);
|
|
@@ -892,7 +909,7 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
|
|
|
892
909
|
|
|
893
910
|
getInfoData(): any {
|
|
894
911
|
return {
|
|
895
|
-
minCltv: Number(this.
|
|
912
|
+
minCltv: Number(this.minCltv)
|
|
896
913
|
};
|
|
897
914
|
}
|
|
898
915
|
|