@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.
Files changed (34) hide show
  1. package/dist/swaps/assertions/FromBtcAmountAssertions.js +2 -2
  2. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +45 -19
  3. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +2 -2
  4. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +35 -25
  5. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +2 -2
  6. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +33 -22
  7. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +1 -1
  8. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +29 -11
  9. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.d.ts +2 -2
  10. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.js +1 -1
  11. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.d.ts +1 -0
  12. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +54 -9
  13. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +83 -30
  14. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +2 -2
  15. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +11 -17
  16. package/dist/utils/Utils.d.ts +33 -0
  17. package/dist/utils/Utils.js +51 -1
  18. package/dist/utils/paramcoders/ParamDecoder.js +4 -86
  19. package/dist/utils/paramcoders/SchemaVerifier.d.ts +11 -2
  20. package/dist/utils/paramcoders/SchemaVerifier.js +93 -47
  21. package/package.json +1 -1
  22. package/src/swaps/assertions/FromBtcAmountAssertions.ts +2 -2
  23. package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +44 -19
  24. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +47 -30
  25. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +45 -27
  26. package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +28 -13
  27. package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +4 -4
  28. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +63 -13
  29. package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +79 -33
  30. package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +25 -2
  31. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +10 -16
  32. package/src/utils/Utils.ts +48 -0
  33. package/src/utils/paramcoders/ParamDecoder.ts +10 -66
  34. 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
- const val = req[fieldName];
36
- const type = schema[fieldName];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/lp-lib",
3
- "version": "17.5.3",
3
+ "version": "17.6.1",
4
4
  "description": "Main functionality implementation for atomiq LP node",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -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) / (1000000n - fees.baseFee);
190
- const adjustedMax = max * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
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, 0n);
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 isCommited = await swapContract.isCommited(swap.data);
95
- if(isCommited) {
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 isCommited = await swapContract.isCommited(swap.data);
113
- if(isCommited) {
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
- if(await this.processPastSwap(swap)) refundSwaps.push(swap);
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
- await swapContract.refund(signer, refundSwap.data, true, false, {waitForConfirmation: true});
161
- this.swapLogger.info(refundSwap, "refundSwaps(): swap refunded, address: "+refundSwap.address);
162
- //The swap should be removed by the event handler
163
- await refundSwap.setState(FromBtcSwapState.REFUNDED);
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.BigInt,
205
- safetyFactor: FieldTypeEnum.BigInt,
206
- startTimestamp: FieldTypeEnum.BigInt,
207
- addBlock: FieldTypeEnum.BigInt,
208
- addFee: FieldTypeEnum.BigInt,
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, null).toString("hex"),
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.BigInt,
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.BigInt,
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 {expressHandlerWrapper, getAbortController, HEX_REGEX, isDefinedRuntimeError} from "../../../utils/Utils";
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
- minCltv: bigint,
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
- await swapContract.refund(signer, refundSwap.data, true, false, {waitForConfirmation: true});
173
- this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: "+refundSwap.pr);
174
-
175
- await refundSwap.setState(FromBtcLnSwapState.REFUNDED);
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
- switch(await this.processPastSwap(swap)) {
232
- case "CANCEL":
233
- cancelInvoices.push(swap);
234
- break;
235
- case "SETTLE":
236
- settleInvoices.push(swap);
237
- break;
238
- case "REFUND":
239
- refundSwaps.push(swap);
240
- break;
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
- expiryTimeout = await this.checkHtlcExpiry(invoice, blockheightPrefetch, abortController.signal);
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)) + expiryTimeout,
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<bigint> {
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.config.minCltv;
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.config.minCltv.toString(10),
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.BigInt,
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.config.minCltv) + 5,
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, expiryTimeout,
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.config.minCltv)
912
+ minCltv: Number(this.minCltv)
896
913
  };
897
914
  }
898
915