@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
@@ -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) / (1000000n - fees.baseFee);
128
- const adjustedMax = max * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
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 {
@@ -36,7 +36,7 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
36
36
  getHash(chainIdentifier, address, amount) {
37
37
  const parsedOutputScript = this.bitcoin.toOutputScript(address);
38
38
  const { swapContract } = this.getChain(chainIdentifier);
39
- return swapContract.getHashForOnchain(parsedOutputScript, amount, this.config.confirmations, 0n);
39
+ return swapContract.getHashForOnchain(parsedOutputScript, amount, this.config.confirmations);
40
40
  }
41
41
  /**
42
42
  * Processes past swap
@@ -51,13 +51,18 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
51
51
  if (swap.state === FromBtcSwapAbs_1.FromBtcSwapState.CREATED) {
52
52
  if (!await swapContract.isInitAuthorizationExpired(swap.data, swap))
53
53
  return false;
54
- const isCommited = await swapContract.isCommited(swap.data);
55
- if (isCommited) {
54
+ const commitState = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
55
+ if (commitState.type === base_1.SwapCommitStateType.COMMITED || commitState.type === base_1.SwapCommitStateType.REFUNDABLE) {
56
56
  this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap was commited, but processed from watchdog, address: " + swap.address);
57
57
  await swap.setState(FromBtcSwapAbs_1.FromBtcSwapState.COMMITED);
58
58
  await this.saveSwapData(swap);
59
59
  return false;
60
60
  }
61
+ if (commitState.type === base_1.SwapCommitStateType.PAID) {
62
+ this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap was claimed, but processed from watchdog, address: " + swap.address);
63
+ await this.removeSwapData(swap, FromBtcSwapAbs_1.FromBtcSwapState.CLAIMED);
64
+ return false;
65
+ }
61
66
  this.swapLogger.info(swap, "processPastSwap(state=CREATED): removing past swap due to authorization expiry, address: " + swap.address);
62
67
  await this.removeSwapData(swap, FromBtcSwapAbs_1.FromBtcSwapState.CANCELED);
63
68
  await this.bitcoin.addUnusedAddress(swap.address);
@@ -67,11 +72,16 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
67
72
  if (swap.state === FromBtcSwapAbs_1.FromBtcSwapState.COMMITED) {
68
73
  if (!await swapContract.isExpired(signer.getAddress(), swap.data))
69
74
  return false;
70
- const isCommited = await swapContract.isCommited(swap.data);
71
- if (isCommited) {
75
+ const commitState = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
76
+ if (commitState.type === base_1.SwapCommitStateType.COMMITED || commitState.type === base_1.SwapCommitStateType.REFUNDABLE) {
72
77
  this.swapLogger.info(swap, "processPastSwap(state=COMMITED): swap expired, will refund, address: " + swap.address);
73
78
  return true;
74
79
  }
80
+ if (commitState.type === base_1.SwapCommitStateType.PAID) {
81
+ this.swapLogger.info(swap, "processPastSwap(state=COMMITED): swap was claimed, but processed from watchdog, address: " + swap.address);
82
+ await this.removeSwapData(swap, FromBtcSwapAbs_1.FromBtcSwapState.CLAIMED);
83
+ return false;
84
+ }
75
85
  this.swapLogger.warn(swap, "processPastSwap(state=COMMITED): commited swap expired and not committed anymore (already refunded?), address: " + swap.address);
76
86
  await this.removeSwapData(swap, FromBtcSwapAbs_1.FromBtcSwapState.CANCELED);
77
87
  return false;
@@ -92,8 +102,13 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
92
102
  ]);
93
103
  const refundSwaps = [];
94
104
  for (let { obj: swap } of queriedData) {
95
- if (await this.processPastSwap(swap))
96
- refundSwaps.push(swap);
105
+ try {
106
+ if (await this.processPastSwap(swap))
107
+ refundSwaps.push(swap);
108
+ }
109
+ catch (e) {
110
+ this.swapLogger.error(swap, "processPastSwap(): Error executing watchdog function: ", e);
111
+ }
97
112
  }
98
113
  await this.refundSwaps(refundSwaps);
99
114
  }
@@ -110,10 +125,15 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
110
125
  if (unlock == null)
111
126
  continue;
112
127
  this.swapLogger.debug(refundSwap, "refundSwaps(): initiate refund of swap");
113
- await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
114
- this.swapLogger.info(refundSwap, "refundSwaps(): swap refunded, address: " + refundSwap.address);
115
- //The swap should be removed by the event handler
116
- await refundSwap.setState(FromBtcSwapAbs_1.FromBtcSwapState.REFUNDED);
128
+ try {
129
+ await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
130
+ this.swapLogger.info(refundSwap, "refundSwaps(): swap refunded, address: " + refundSwap.address);
131
+ //The swap should be removed by the event handler
132
+ await refundSwap.setState(FromBtcSwapAbs_1.FromBtcSwapState.REFUNDED);
133
+ }
134
+ catch (e) {
135
+ this.swapLogger.error(refundSwap, "refundSwaps(): error refunding swap: ", e);
136
+ }
117
137
  unlock();
118
138
  }
119
139
  }
@@ -147,11 +167,11 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
147
167
  async getClaimerBounty(req, expiry, signal) {
148
168
  const parsedClaimerBounty = await req.paramReader.getParams({
149
169
  claimerBounty: {
150
- feePerBlock: SchemaVerifier_1.FieldTypeEnum.BigInt,
151
- safetyFactor: SchemaVerifier_1.FieldTypeEnum.BigInt,
152
- startTimestamp: SchemaVerifier_1.FieldTypeEnum.BigInt,
153
- addBlock: SchemaVerifier_1.FieldTypeEnum.BigInt,
154
- addFee: SchemaVerifier_1.FieldTypeEnum.BigInt,
170
+ feePerBlock: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
171
+ safetyFactor: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
172
+ startTimestamp: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
173
+ addBlock: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
174
+ addFee: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
155
175
  },
156
176
  }).catch(e => null);
157
177
  signal.throwIfAborted();
@@ -162,6 +182,12 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
162
182
  };
163
183
  }
164
184
  const tsDelta = expiry - parsedClaimerBounty.claimerBounty.startTimestamp;
185
+ if (tsDelta < 0n) {
186
+ throw {
187
+ code: 20043,
188
+ msg: "Invalid claimerBounty (ts delta < 0)"
189
+ };
190
+ }
165
191
  const blocksDelta = tsDelta / this.config.bitcoinBlocktime * parsedClaimerBounty.claimerBounty.safetyFactor;
166
192
  const totalBlock = blocksDelta + parsedClaimerBounty.claimerBounty.addBlock;
167
193
  return parsedClaimerBounty.claimerBounty.addFee + (totalBlock * parsedClaimerBounty.claimerBounty.feePerBlock);
@@ -169,7 +195,7 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
169
195
  getDummySwapData(chainIdentifier, useToken, address) {
170
196
  const { swapContract, signer } = this.getChain(chainIdentifier);
171
197
  const dummyAmount = BigInt(Math.floor(Math.random() * 0x1000000));
172
- return swapContract.createSwapData(base_1.ChainSwapType.CHAIN, signer.getAddress(), address, useToken, dummyAmount, swapContract.getHashForOnchain((0, crypto_1.randomBytes)(32), dummyAmount, 3, null).toString("hex"), base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8)), BigInt(Math.floor(Date.now() / 1000)) + this.config.swapTsCsvDelta, false, true, BigInt(Math.floor(Math.random() * 0x10000)), BigInt(Math.floor(Math.random() * 0x10000)));
198
+ return swapContract.createSwapData(base_1.ChainSwapType.CHAIN, signer.getAddress(), address, useToken, dummyAmount, swapContract.getHashForOnchain((0, crypto_1.randomBytes)(32), dummyAmount, 3).toString("hex"), base_1.BigIntBufferUtils.fromBuffer((0, crypto_1.randomBytes)(8)), BigInt(Math.floor(Date.now() / 1000)) + this.config.swapTsCsvDelta, false, true, BigInt(Math.floor(Math.random() * 0x10000)), BigInt(Math.floor(Math.random() * 0x10000)));
173
199
  }
174
200
  /**
175
201
  * Sets up required listeners for the REST server
@@ -205,11 +231,11 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
205
231
  address: (val) => val != null &&
206
232
  typeof (val) === "string" &&
207
233
  chainInterface.isValidAddress(val, true) ? val : null,
208
- amount: SchemaVerifier_1.FieldTypeEnum.BigInt,
234
+ amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
209
235
  token: (val) => val != null &&
210
236
  typeof (val) === "string" &&
211
237
  this.isTokenSupported(chainIdentifier, val) ? val : null,
212
- sequence: SchemaVerifier_1.FieldTypeEnum.BigInt,
238
+ sequence: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
213
239
  exactOut: SchemaVerifier_1.FieldTypeEnum.BooleanOptional
214
240
  });
215
241
  if (parsedBody == null)
@@ -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
- minCltv: bigint;
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
- await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
120
- this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: " + refundSwap.pr);
121
- await refundSwap.setState(FromBtcLnSwapAbs_1.FromBtcLnSwapState.REFUNDED);
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
- switch (await this.processPastSwap(swap)) {
174
- case "CANCEL":
175
- cancelInvoices.push(swap);
176
- break;
177
- case "SETTLE":
178
- settleInvoices.push(swap);
179
- break;
180
- case "REFUND":
181
- refundSwaps.push(swap);
182
- break;
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
- expiryTimeout = await this.checkHtlcExpiry(invoice, blockheightPrefetch, abortController.signal);
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)) + expiryTimeout, false, true, invoiceData.securityDeposit, 0n, invoiceData.depositToken);
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.config.minCltv;
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.config.minCltv.toString(10),
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)
@@ -509,7 +520,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
509
520
  typeof (val) === "string" &&
510
521
  val.length === 64 &&
511
522
  Utils_1.HEX_REGEX.test(val) ? val : null,
512
- amount: SchemaVerifier_1.FieldTypeEnum.BigInt,
523
+ amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
513
524
  token: (val) => val != null &&
514
525
  typeof (val) === "string" &&
515
526
  this.isTokenSupported(chainIdentifier, val) ? val : null,
@@ -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.config.minCltv) + 5,
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 expiryTimeout = (this.config.minCltv * this.config.bitcoinBlocktime / this.config.safetyFactor) - this.config.gracePeriod;
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.config.minCltv)
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
- minCltv: bigint;
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
- await swapContract.refund(signer, refundSwap.data, true, false, { waitForConfirmation: true });
138
- this.swapLogger.info(refundSwap, "refundsSwaps(): swap refunded, invoice: " + refundSwap.pr);
139
- await refundSwap.setState(FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.REFUNDED);
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
- switch (await this.processPastSwap(swap)) {
178
- case "SETTLE":
179
- settleInvoices.push(swap);
180
- break;
181
- case "REFUND":
182
- refundSwaps.push(swap);
183
- break;
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
- expiryTimeout = await this.checkHtlcExpiry(invoice);
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)) + expiryTimeout, false, true, invoiceData.amountGasToken + invoiceData.claimerBounty, invoiceData.claimerBounty, invoiceData.gasToken);
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.config.minCltv;
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.config.minCltv.toString(10),
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)
@@ -542,7 +553,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
542
553
  typeof (val) === "string" &&
543
554
  val.length === 64 &&
544
555
  Utils_1.HEX_REGEX.test(val) ? val : null,
545
- amount: SchemaVerifier_1.FieldTypeEnum.BigInt,
556
+ amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
546
557
  token: (val) => val != null &&
547
558
  typeof (val) === "string" &&
548
559
  this.isTokenSupported(chainIdentifier, val) ? val : null,
@@ -551,8 +562,8 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
551
562
  gasToken: (val) => val != null &&
552
563
  typeof (val) === "string" &&
553
564
  chainInterface.isValidToken(val) ? val : null,
554
- gasAmount: SchemaVerifier_1.FieldTypeEnum.BigInt,
555
- claimerBounty: SchemaVerifier_1.FieldTypeEnum.BigInt
565
+ gasAmount: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
566
+ claimerBounty: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative
556
567
  });
557
568
  if (parsedBody == null)
558
569
  throw {
@@ -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.config.minCltv) + 5,
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.config.minCltv),
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: bigint, actualSatsPerVbyte: bigint): void;
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, BigInt(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.swapLogger.info(swap, "processInitialized(state=BTC_SENDING): btc transaction not found, resetting to COMMITED state, txId: " + swap.txId + " address: " + swap.address);
336
- await swap.setState(ToBtcSwapAbs_1.ToBtcSwapState.COMMITED);
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);
@@ -431,7 +438,7 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
431
438
  * @throws {DefinedRuntimeError} will throw an error if the nonce is invalid
432
439
  */
433
440
  checkNonceValid(nonce) {
434
- if (nonce < 0 || nonce >= (2n ** 64n))
441
+ if (nonce < 1 || nonce >= (2n ** 64n))
435
442
  throw {
436
443
  code: 20021,
437
444
  msg: "Invalid request body (nonce - cannot be parsed)"
@@ -451,6 +458,11 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
451
458
  * @throws {DefinedRuntimeError} will throw an error if the confirmationTarget is out of bounds
452
459
  */
453
460
  checkConfirmationTarget(confirmationTarget) {
461
+ if (!Number.isFinite(confirmationTarget) || !Number.isSafeInteger(confirmationTarget))
462
+ throw {
463
+ code: 20028,
464
+ msg: "Invalid request body (confirmationTarget - not whole number)"
465
+ };
454
466
  if (confirmationTarget > this.config.maxConfTarget)
455
467
  throw {
456
468
  code: 20023,
@@ -469,6 +481,11 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
469
481
  * @throws {DefinedRuntimeError} will throw an error if the confirmations are out of bounds
470
482
  */
471
483
  checkRequiredConfirmations(confirmations) {
484
+ if (!Number.isFinite(confirmations) || !Number.isSafeInteger(confirmations))
485
+ throw {
486
+ code: 20027,
487
+ msg: "Invalid request body (confirmations - not whole number)"
488
+ };
472
489
  if (confirmations > this.config.maxConfirmations)
473
490
  throw {
474
491
  code: 20025,
@@ -536,7 +553,7 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
536
553
  };
537
554
  return {
538
555
  networkFee: BigInt(chainFeeResp.networkFee),
539
- satsPerVbyte: BigInt(chainFeeResp.satsPerVbyte)
556
+ satsPerVbyte: chainFeeResp.satsPerVbyte
540
557
  };
541
558
  }
542
559
  startRestServer(restServer) {
@@ -562,10 +579,10 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
562
579
  */
563
580
  const parsedBody = await req.paramReader.getParams({
564
581
  address: SchemaVerifier_1.FieldTypeEnum.String,
565
- amount: SchemaVerifier_1.FieldTypeEnum.BigInt,
566
- confirmationTarget: SchemaVerifier_1.FieldTypeEnum.Number,
567
- confirmations: SchemaVerifier_1.FieldTypeEnum.Number,
568
- nonce: SchemaVerifier_1.FieldTypeEnum.BigInt,
582
+ amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
583
+ confirmationTarget: SchemaVerifier_1.FieldTypeEnum.NumberPositive,
584
+ confirmations: SchemaVerifier_1.FieldTypeEnum.NumberPositive,
585
+ nonce: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
569
586
  token: (val) => val != null &&
570
587
  typeof (val) === "string" &&
571
588
  this.isTokenSupported(chainIdentifier, val) ? val : null,
@@ -642,7 +659,8 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
642
659
  data: {
643
660
  amount: amountBD.toString(10),
644
661
  address: signer.getAddress(),
645
- satsPervByte: networkFeeData.satsPerVbyte.toString(10),
662
+ satsPervByte: Math.floor(networkFeeData.satsPerVbyte).toString(10),
663
+ satsPervByteNumber: networkFeeData.satsPerVbyte,
646
664
  networkFee: networkFeeInToken.toString(10),
647
665
  swapFee: swapFeeInToken.toString(10),
648
666
  totalFee: (swapFeeInToken + networkFeeInToken).toString(10),
@@ -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: bigint;
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: bigint, nonce: bigint, requiredConfirmations: number, preferedConfirmationTarget: number);
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 = BigInt(chainIdOrObj.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;
@@ -68,6 +68,7 @@ export declare class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToB
68
68
  };
69
69
  readonly lightning: ILightningWallet;
70
70
  readonly LightningAssertions: LightningAssertions;
71
+ readonly cltvDeltaLowerBound: bigint;
71
72
  constructor(storageDirectory: IIntermediaryStorage<ToBtcLnSwapAbs>, path: string, chainData: MultichainData, lightning: ILightningWallet, swapPricing: ISwapPrice, config: ToBtcLnConfig);
72
73
  /**
73
74
  * Cleans up exactIn authorization that are already past their expiry