@atomiqlabs/lp-lib 17.6.0 → 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/escrow/frombtc_abstract/FromBtcAbs.js +29 -13
- package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +1 -1
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +3 -3
- package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +17 -6
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.d.ts +1 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +16 -5
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +60 -27
- package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +4 -15
- 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/escrow/frombtc_abstract/FromBtcAbs.ts +30 -14
- package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +1 -1
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +3 -3
- package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +15 -6
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +26 -6
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +58 -30
- package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +5 -15
- package/src/utils/paramcoders/ParamDecoder.ts +10 -66
- package/src/utils/paramcoders/SchemaVerifier.ts +80 -37
|
@@ -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
|
|
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
|
|
55
|
-
if (
|
|
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
|
|
71
|
-
if (
|
|
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;
|
|
@@ -157,11 +167,11 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
157
167
|
async getClaimerBounty(req, expiry, signal) {
|
|
158
168
|
const parsedClaimerBounty = await req.paramReader.getParams({
|
|
159
169
|
claimerBounty: {
|
|
160
|
-
feePerBlock: SchemaVerifier_1.FieldTypeEnum.
|
|
161
|
-
safetyFactor: SchemaVerifier_1.FieldTypeEnum.
|
|
162
|
-
startTimestamp: SchemaVerifier_1.FieldTypeEnum.
|
|
163
|
-
addBlock: SchemaVerifier_1.FieldTypeEnum.
|
|
164
|
-
addFee: SchemaVerifier_1.FieldTypeEnum.
|
|
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,
|
|
165
175
|
},
|
|
166
176
|
}).catch(e => null);
|
|
167
177
|
signal.throwIfAborted();
|
|
@@ -172,6 +182,12 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
172
182
|
};
|
|
173
183
|
}
|
|
174
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
|
+
}
|
|
175
191
|
const blocksDelta = tsDelta / this.config.bitcoinBlocktime * parsedClaimerBounty.claimerBounty.safetyFactor;
|
|
176
192
|
const totalBlock = blocksDelta + parsedClaimerBounty.claimerBounty.addBlock;
|
|
177
193
|
return parsedClaimerBounty.claimerBounty.addFee + (totalBlock * parsedClaimerBounty.claimerBounty.feePerBlock);
|
|
@@ -179,7 +195,7 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
179
195
|
getDummySwapData(chainIdentifier, useToken, address) {
|
|
180
196
|
const { swapContract, signer } = this.getChain(chainIdentifier);
|
|
181
197
|
const dummyAmount = BigInt(Math.floor(Math.random() * 0x1000000));
|
|
182
|
-
return swapContract.createSwapData(base_1.ChainSwapType.CHAIN, signer.getAddress(), address, useToken, dummyAmount, swapContract.getHashForOnchain((0, crypto_1.randomBytes)(32), dummyAmount, 3
|
|
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)));
|
|
183
199
|
}
|
|
184
200
|
/**
|
|
185
201
|
* Sets up required listeners for the REST server
|
|
@@ -215,11 +231,11 @@ class FromBtcAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
215
231
|
address: (val) => val != null &&
|
|
216
232
|
typeof (val) === "string" &&
|
|
217
233
|
chainInterface.isValidAddress(val, true) ? val : null,
|
|
218
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
234
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
219
235
|
token: (val) => val != null &&
|
|
220
236
|
typeof (val) === "string" &&
|
|
221
237
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
222
|
-
sequence: SchemaVerifier_1.FieldTypeEnum.
|
|
238
|
+
sequence: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
223
239
|
exactOut: SchemaVerifier_1.FieldTypeEnum.BooleanOptional
|
|
224
240
|
});
|
|
225
241
|
if (parsedBody == null)
|
|
@@ -520,7 +520,7 @@ class FromBtcLnAbs extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
520
520
|
typeof (val) === "string" &&
|
|
521
521
|
val.length === 64 &&
|
|
522
522
|
Utils_1.HEX_REGEX.test(val) ? val : null,
|
|
523
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
523
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
524
524
|
token: (val) => val != null &&
|
|
525
525
|
typeof (val) === "string" &&
|
|
526
526
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
@@ -553,7 +553,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
553
553
|
typeof (val) === "string" &&
|
|
554
554
|
val.length === 64 &&
|
|
555
555
|
Utils_1.HEX_REGEX.test(val) ? val : null,
|
|
556
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
556
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
557
557
|
token: (val) => val != null &&
|
|
558
558
|
typeof (val) === "string" &&
|
|
559
559
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
@@ -562,8 +562,8 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
|
|
|
562
562
|
gasToken: (val) => val != null &&
|
|
563
563
|
typeof (val) === "string" &&
|
|
564
564
|
chainInterface.isValidToken(val) ? val : null,
|
|
565
|
-
gasAmount: SchemaVerifier_1.FieldTypeEnum.
|
|
566
|
-
claimerBounty: SchemaVerifier_1.FieldTypeEnum.
|
|
565
|
+
gasAmount: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
566
|
+
claimerBounty: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative
|
|
567
567
|
});
|
|
568
568
|
if (parsedBody == null)
|
|
569
569
|
throw {
|
|
@@ -438,7 +438,7 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
438
438
|
* @throws {DefinedRuntimeError} will throw an error if the nonce is invalid
|
|
439
439
|
*/
|
|
440
440
|
checkNonceValid(nonce) {
|
|
441
|
-
if (nonce <
|
|
441
|
+
if (nonce < 1 || nonce >= (2n ** 64n))
|
|
442
442
|
throw {
|
|
443
443
|
code: 20021,
|
|
444
444
|
msg: "Invalid request body (nonce - cannot be parsed)"
|
|
@@ -458,6 +458,11 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
458
458
|
* @throws {DefinedRuntimeError} will throw an error if the confirmationTarget is out of bounds
|
|
459
459
|
*/
|
|
460
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
|
+
};
|
|
461
466
|
if (confirmationTarget > this.config.maxConfTarget)
|
|
462
467
|
throw {
|
|
463
468
|
code: 20023,
|
|
@@ -476,6 +481,11 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
476
481
|
* @throws {DefinedRuntimeError} will throw an error if the confirmations are out of bounds
|
|
477
482
|
*/
|
|
478
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
|
+
};
|
|
479
489
|
if (confirmations > this.config.maxConfirmations)
|
|
480
490
|
throw {
|
|
481
491
|
code: 20025,
|
|
@@ -569,10 +579,10 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
569
579
|
*/
|
|
570
580
|
const parsedBody = await req.paramReader.getParams({
|
|
571
581
|
address: SchemaVerifier_1.FieldTypeEnum.String,
|
|
572
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
573
|
-
confirmationTarget: SchemaVerifier_1.FieldTypeEnum.
|
|
574
|
-
confirmations: SchemaVerifier_1.FieldTypeEnum.
|
|
575
|
-
nonce: SchemaVerifier_1.FieldTypeEnum.
|
|
582
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
583
|
+
confirmationTarget: SchemaVerifier_1.FieldTypeEnum.NumberPositive,
|
|
584
|
+
confirmations: SchemaVerifier_1.FieldTypeEnum.NumberPositive,
|
|
585
|
+
nonce: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
576
586
|
token: (val) => val != null &&
|
|
577
587
|
typeof (val) === "string" &&
|
|
578
588
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
@@ -649,7 +659,8 @@ class ToBtcAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
649
659
|
data: {
|
|
650
660
|
amount: amountBD.toString(10),
|
|
651
661
|
address: signer.getAddress(),
|
|
652
|
-
satsPervByte: networkFeeData.satsPerVbyte.toString(10),
|
|
662
|
+
satsPervByte: Math.floor(networkFeeData.satsPerVbyte).toString(10),
|
|
663
|
+
satsPervByteNumber: networkFeeData.satsPerVbyte,
|
|
653
664
|
networkFee: networkFeeInToken.toString(10),
|
|
654
665
|
swapFee: swapFeeInToken.toString(10),
|
|
655
666
|
totalFee: (swapFeeInToken + networkFeeInToken).toString(10),
|
|
@@ -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
|
|
@@ -33,9 +33,10 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
33
33
|
this.config.minLnBaseFee = this.config.minLnBaseFee || 5n;
|
|
34
34
|
this.config.exactInExpiry = this.config.exactInExpiry || 10 * 1000;
|
|
35
35
|
this.config.lnSendBitcoinBlockTimeSafetyFactorPPM = this.config.lnSendBitcoinBlockTimeSafetyFactorPPM ?? (this.config.safetyFactor * 1000000n);
|
|
36
|
-
if (this.config.lnSendBitcoinBlockTimeSafetyFactorPPM <=
|
|
37
|
-
throw new Error("Lightning network send block safety factor set below 1.
|
|
36
|
+
if (this.config.lnSendBitcoinBlockTimeSafetyFactorPPM <= 1250000n) {
|
|
37
|
+
throw new Error("Lightning network send block safety factor set below 1.25, this is insecure!");
|
|
38
38
|
}
|
|
39
|
+
this.cltvDeltaLowerBound = (0, Utils_1.getMinSafeBlockWindowSlow)(Number(this.config.lnSendBitcoinBlockTimeSafetyFactorPPM) / 1000000);
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
42
|
* Cleans up exactIn authorization that are already past their expiry
|
|
@@ -229,6 +230,11 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
229
230
|
const maxFee = swap.quotedNetworkFee;
|
|
230
231
|
const maxUsableCLTVdelta = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
|
|
231
232
|
/ (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
|
|
233
|
+
if (maxUsableCLTVdelta < this.cltvDeltaLowerBound)
|
|
234
|
+
throw {
|
|
235
|
+
code: 90008,
|
|
236
|
+
msg: "Calculated CLTV delta is too low for current safety factor!"
|
|
237
|
+
};
|
|
232
238
|
//Initiate payment
|
|
233
239
|
this.swapLogger.info(swap, "sendLightningPayment(): paying lightning network invoice," +
|
|
234
240
|
" cltvDelta: " + maxUsableCLTVdelta.toString(10) +
|
|
@@ -476,6 +482,11 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
476
482
|
async checkAndGetNetworkFee(amountBD, maxFee, expiryTimestamp, currentTimestamp, pr, metadata, abortSignal) {
|
|
477
483
|
const maxUsableCLTV = (expiryTimestamp - currentTimestamp - this.config.gracePeriod)
|
|
478
484
|
/ (this.config.bitcoinBlocktime * this.config.lnSendBitcoinBlockTimeSafetyFactorPPM / 1000000n);
|
|
485
|
+
if (maxUsableCLTV < this.cltvDeltaLowerBound)
|
|
486
|
+
throw {
|
|
487
|
+
code: 20002,
|
|
488
|
+
msg: "Cannot route the payment (calculated CLTV delta too short - increase timeout)!"
|
|
489
|
+
};
|
|
479
490
|
const blockHeight = await this.lightning.getBlockheight();
|
|
480
491
|
abortSignal.throwIfAborted();
|
|
481
492
|
metadata.times.blockheightFetched = Date.now();
|
|
@@ -682,8 +693,8 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
682
693
|
*/
|
|
683
694
|
const parsedBody = await req.paramReader.getParams({
|
|
684
695
|
pr: SchemaVerifier_1.FieldTypeEnum.String,
|
|
685
|
-
maxFee: SchemaVerifier_1.FieldTypeEnum.
|
|
686
|
-
expiryTimestamp: SchemaVerifier_1.FieldTypeEnum.
|
|
696
|
+
maxFee: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
697
|
+
expiryTimestamp: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
687
698
|
token: (val) => val != null &&
|
|
688
699
|
typeof (val) === "string" &&
|
|
689
700
|
this.isTokenSupported(chainIdentifier, val) ? val : null,
|
|
@@ -691,7 +702,7 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
691
702
|
typeof (val) === "string" &&
|
|
692
703
|
chainInterface.isValidAddress(val, true) ? val : null,
|
|
693
704
|
exactIn: SchemaVerifier_1.FieldTypeEnum.BooleanOptional,
|
|
694
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
705
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositiveOptional
|
|
695
706
|
});
|
|
696
707
|
if (parsedBody == null) {
|
|
697
708
|
throw {
|
|
@@ -174,13 +174,21 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
174
174
|
const vault = await this.Vaults.getVault(swap.chainIdentifier, swap.vaultOwner, swap.vaultId);
|
|
175
175
|
const foundWithdrawal = vault.pendingWithdrawals.find(val => val.btcTx.txid === swap.btcTxId);
|
|
176
176
|
let tx = foundWithdrawal?.btcTx;
|
|
177
|
-
if (tx == null)
|
|
177
|
+
if (tx == null) {
|
|
178
178
|
tx = await this.bitcoinRpc.getTransaction(swap.btcTxId);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
tx = await (0, BitcoinUtils_1.checkTransactionReplaced)(tx.txid, tx.raw, this.bitcoinRpc);
|
|
182
|
+
}
|
|
179
183
|
if (tx == null) {
|
|
180
184
|
await this.removeSwapData(swap, SpvVaultSwap_1.SpvVaultSwapState.FAILED);
|
|
185
|
+
if (foundWithdrawal != null) {
|
|
186
|
+
vault.removeWithdrawal(foundWithdrawal);
|
|
187
|
+
await this.Vaults.saveVault(vault);
|
|
188
|
+
}
|
|
181
189
|
return;
|
|
182
190
|
}
|
|
183
|
-
else if (tx.confirmations === 0) {
|
|
191
|
+
else if (tx.confirmations == null || tx.confirmations === 0) {
|
|
184
192
|
await swap.setState(SpvVaultSwap_1.SpvVaultSwapState.SENT);
|
|
185
193
|
await this.saveSwapData(swap);
|
|
186
194
|
return;
|
|
@@ -315,11 +323,11 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
315
323
|
* frontingFeeRate: string Fronting fee (in output token) to assign to the swap
|
|
316
324
|
*/
|
|
317
325
|
const actualParsedBody = await req.paramReader.getParams({
|
|
318
|
-
amount: SchemaVerifier_1.FieldTypeEnum.
|
|
319
|
-
gasAmount: SchemaVerifier_1.FieldTypeEnum.
|
|
326
|
+
amount: SchemaVerifier_1.FieldTypeEnum.BigIntPositive,
|
|
327
|
+
gasAmount: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
320
328
|
exactOut: SchemaVerifier_1.FieldTypeEnum.BooleanOptional,
|
|
321
|
-
callerFeeRate: SchemaVerifier_1.FieldTypeEnum.
|
|
322
|
-
frontingFeeRate: SchemaVerifier_1.FieldTypeEnum.
|
|
329
|
+
callerFeeRate: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
330
|
+
frontingFeeRate: SchemaVerifier_1.FieldTypeEnum.BigIntNotNegative,
|
|
323
331
|
});
|
|
324
332
|
abortController.signal.throwIfAborted();
|
|
325
333
|
if (actualParsedBody == null)
|
|
@@ -329,7 +337,7 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
329
337
|
};
|
|
330
338
|
const inputAmountAdjustments = req.paramReader.getExistingParamsOrNull({
|
|
331
339
|
amountUtxos: SchemaVerifier_1.FieldTypeEnum.AnyOptional,
|
|
332
|
-
amountFeeRate: SchemaVerifier_1.FieldTypeEnum.
|
|
340
|
+
amountFeeRate: SchemaVerifier_1.FieldTypeEnum.NumberPositiveOptional
|
|
333
341
|
});
|
|
334
342
|
if (inputAmountAdjustments == null)
|
|
335
343
|
throw {
|
|
@@ -630,25 +638,26 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
630
638
|
msg: "Vault UTXO already spent, please get another quote and try again!"
|
|
631
639
|
};
|
|
632
640
|
}
|
|
641
|
+
//Double-check the state to prevent race condition
|
|
642
|
+
if (swap.state !== SpvVaultSwap_1.SpvVaultSwapState.CREATED)
|
|
643
|
+
throw {
|
|
644
|
+
code: 20505,
|
|
645
|
+
msg: "Invalid quote ID, not found or expired!"
|
|
646
|
+
}; //Continues here is only synchronous code until the state change to SIGNED
|
|
633
647
|
const unlock = swap.lock(120);
|
|
634
648
|
if (!unlock)
|
|
635
649
|
throw {
|
|
636
650
|
code: 20517,
|
|
637
651
|
msg: "Bitcoin transaction submission already in progress, please retry later!"
|
|
638
652
|
};
|
|
653
|
+
const txId = signedTx.id;
|
|
639
654
|
let swapSendingSet = false;
|
|
640
655
|
let dataSendingSet = false;
|
|
641
656
|
try {
|
|
642
657
|
const btcRawTx = Buffer.from(signedTx.toBytes(true, true)).toString("hex");
|
|
643
|
-
//Double-check the state to prevent race condition
|
|
644
|
-
if (swap.state !== SpvVaultSwap_1.SpvVaultSwapState.CREATED)
|
|
645
|
-
throw {
|
|
646
|
-
code: 20505,
|
|
647
|
-
msg: "Invalid quote ID, not found or expired!"
|
|
648
|
-
};
|
|
649
658
|
//Double check in-flight swap count
|
|
650
659
|
this.checkTooManyInflightSwaps();
|
|
651
|
-
swap.btcTxId =
|
|
660
|
+
swap.btcTxId = txId;
|
|
652
661
|
swap.state = SpvVaultSwap_1.SpvVaultSwapState.SIGNED;
|
|
653
662
|
swap.sending = true;
|
|
654
663
|
swapSendingSet = true;
|
|
@@ -658,7 +667,7 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
658
667
|
vault.addWithdrawal(data);
|
|
659
668
|
dataSendingSet = true;
|
|
660
669
|
await this.Vaults.saveVault(vault);
|
|
661
|
-
this.swapLogger.info(swap, "REST: /postQuote: BTC transaction signed, txId: " +
|
|
670
|
+
this.swapLogger.info(swap, "REST: /postQuote: BTC transaction signed, txId: " + txId);
|
|
662
671
|
try {
|
|
663
672
|
await this.bitcoin.sendRawTransaction(btcRawTx);
|
|
664
673
|
await swap.setState(SpvVaultSwap_1.SpvVaultSwapState.SENT);
|
|
@@ -676,21 +685,45 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
676
685
|
catch (e) {
|
|
677
686
|
if (swapSendingSet)
|
|
678
687
|
swap.sending = false;
|
|
679
|
-
if (dataSendingSet)
|
|
688
|
+
if (dataSendingSet)
|
|
680
689
|
data.sending = false;
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
690
|
+
//We only make the swap failed if the error happened in CREATED or SIGNED states
|
|
691
|
+
if (swap.state === SpvVaultSwap_1.SpvVaultSwapState.CREATED || swap.state === SpvVaultSwap_1.SpvVaultSwapState.SIGNED) {
|
|
692
|
+
try {
|
|
693
|
+
const fetchedBtcTx = await this.bitcoin.getWalletTransaction(txId);
|
|
694
|
+
if (fetchedBtcTx == null) {
|
|
695
|
+
if (dataSendingSet) {
|
|
696
|
+
vault.removeWithdrawal(data);
|
|
697
|
+
await this.Vaults.saveVault(vault);
|
|
698
|
+
}
|
|
699
|
+
if ((0, Utils_1.isDefinedRuntimeError)(e) && swap.metadata != null)
|
|
700
|
+
swap.metadata.postQuoteError = e;
|
|
701
|
+
await this.removeSwapData(swap, SpvVaultSwap_1.SpvVaultSwapState.FAILED);
|
|
702
|
+
throw e; //This will get caught locally and throw the post quote error
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
//Transaction not-null set state to sent and fall through without throwing
|
|
706
|
+
await swap.setState(SpvVaultSwap_1.SpvVaultSwapState.SENT);
|
|
707
|
+
}
|
|
691
708
|
}
|
|
709
|
+
catch (getTxErr) {
|
|
710
|
+
if (getTxErr === e)
|
|
711
|
+
throw e;
|
|
712
|
+
//Cannot determine whether the bitcoin transaction was broadcasted or not
|
|
713
|
+
// do nothing here and let the watchdog handle this!
|
|
714
|
+
throw {
|
|
715
|
+
_httpStatus: 200,
|
|
716
|
+
code: 20001,
|
|
717
|
+
msg: "Transaction status unknown",
|
|
718
|
+
data: {
|
|
719
|
+
txId: swap.btcTxId
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
throw e;
|
|
692
726
|
}
|
|
693
|
-
throw e;
|
|
694
727
|
}
|
|
695
728
|
finally {
|
|
696
729
|
unlock();
|
|
@@ -211,13 +211,9 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
211
211
|
}
|
|
212
212
|
}).catch(e => this.swapLogger.error(invoiceData, "htlcReceived(): Error sending transfer txns", e));
|
|
213
213
|
if (result == null) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
await this.lightning.cancelHodlInvoice(invoice.id);
|
|
218
|
-
this.unsubscribeInvoice(invoice.id);
|
|
219
|
-
await this.removeSwapData(invoiceData);
|
|
220
|
-
this.swapLogger.info(invoiceData, "htlcReceived(): transaction sending failed, refunding lightning: ", invoiceData.pr);
|
|
214
|
+
this.swapLogger.info(invoiceData, "htlcReceived(): transaction sending failed: ", invoiceData.pr);
|
|
215
|
+
//Let the swap watchdog handle this case
|
|
216
|
+
unlock();
|
|
221
217
|
throw {
|
|
222
218
|
code: 20002,
|
|
223
219
|
msg: "Transaction sending failed"
|
|
@@ -235,14 +231,7 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
|
|
|
235
231
|
if (invoiceData.isLocked())
|
|
236
232
|
return;
|
|
237
233
|
const txStatus = await chainInterface.getTxStatus(invoiceData.scRawTx);
|
|
238
|
-
if (txStatus === "not_found") {
|
|
239
|
-
//Retry
|
|
240
|
-
invoiceData.txIds = { init: null };
|
|
241
|
-
invoiceData.scRawTx = null;
|
|
242
|
-
await invoiceData.setState(FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.RECEIVED);
|
|
243
|
-
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
244
|
-
}
|
|
245
|
-
if (txStatus === "reverted") {
|
|
234
|
+
if (txStatus === "reverted" || txStatus === "not_found") {
|
|
246
235
|
//Cancel invoice
|
|
247
236
|
await invoiceData.setState(FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.REFUNDED);
|
|
248
237
|
await this.storageManager.saveData(invoice.id, null, invoiceData);
|
|
@@ -118,51 +118,8 @@ class ParamDecoder {
|
|
|
118
118
|
const resultSchema = {};
|
|
119
119
|
for (let fieldName in schema) {
|
|
120
120
|
const val = await this.getParam(fieldName);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const result = type(val);
|
|
124
|
-
if (result == null)
|
|
125
|
-
return null;
|
|
126
|
-
resultSchema[fieldName] = result;
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (val == null && type >= 100) {
|
|
130
|
-
resultSchema[fieldName] = null;
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (type === SchemaVerifier_1.FieldTypeEnum.Any || type === SchemaVerifier_1.FieldTypeEnum.AnyOptional) {
|
|
134
|
-
resultSchema[fieldName] = val;
|
|
135
|
-
}
|
|
136
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.Boolean || type === SchemaVerifier_1.FieldTypeEnum.BooleanOptional) {
|
|
137
|
-
if (typeof (val) !== "boolean")
|
|
138
|
-
return null;
|
|
139
|
-
resultSchema[fieldName] = val;
|
|
140
|
-
}
|
|
141
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.Number || type === SchemaVerifier_1.FieldTypeEnum.NumberOptional) {
|
|
142
|
-
if (typeof (val) !== "number")
|
|
143
|
-
return null;
|
|
144
|
-
if (isNaN(val))
|
|
145
|
-
return null;
|
|
146
|
-
resultSchema[fieldName] = val;
|
|
147
|
-
}
|
|
148
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.BigInt || type === SchemaVerifier_1.FieldTypeEnum.BigIntOptional) {
|
|
149
|
-
const result = (0, SchemaVerifier_1.parseBigInt)(val);
|
|
150
|
-
if (result == null)
|
|
151
|
-
return null;
|
|
152
|
-
resultSchema[fieldName] = result;
|
|
153
|
-
}
|
|
154
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.String || type === SchemaVerifier_1.FieldTypeEnum.StringOptional) {
|
|
155
|
-
if (typeof (val) !== "string")
|
|
156
|
-
return null;
|
|
157
|
-
resultSchema[fieldName] = val;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
//Probably another request schema
|
|
161
|
-
const result = (0, SchemaVerifier_1.verifySchema)(val, type);
|
|
162
|
-
if (result == null)
|
|
163
|
-
return null;
|
|
164
|
-
resultSchema[fieldName] = result;
|
|
165
|
-
}
|
|
121
|
+
if (!(0, SchemaVerifier_1.verifySchemaField)(val, schema[fieldName], fieldName, resultSchema))
|
|
122
|
+
return null;
|
|
166
123
|
}
|
|
167
124
|
return resultSchema;
|
|
168
125
|
}
|
|
@@ -174,47 +131,8 @@ class ParamDecoder {
|
|
|
174
131
|
resultSchema[fieldName] = null;
|
|
175
132
|
continue;
|
|
176
133
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const result = type(val);
|
|
180
|
-
if (result == null)
|
|
181
|
-
return null;
|
|
182
|
-
resultSchema[fieldName] = result;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (type === SchemaVerifier_1.FieldTypeEnum.Any || type === SchemaVerifier_1.FieldTypeEnum.AnyOptional) {
|
|
186
|
-
resultSchema[fieldName] = val;
|
|
187
|
-
}
|
|
188
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.Boolean || type === SchemaVerifier_1.FieldTypeEnum.BooleanOptional) {
|
|
189
|
-
if (typeof (val) !== "boolean")
|
|
190
|
-
return null;
|
|
191
|
-
resultSchema[fieldName] = val;
|
|
192
|
-
}
|
|
193
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.Number || type === SchemaVerifier_1.FieldTypeEnum.NumberOptional) {
|
|
194
|
-
if (typeof (val) !== "number")
|
|
195
|
-
return null;
|
|
196
|
-
if (isNaN(val))
|
|
197
|
-
return null;
|
|
198
|
-
resultSchema[fieldName] = val;
|
|
199
|
-
}
|
|
200
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.BigInt || type === SchemaVerifier_1.FieldTypeEnum.BigIntOptional) {
|
|
201
|
-
const result = (0, SchemaVerifier_1.parseBigInt)(val);
|
|
202
|
-
if (result == null)
|
|
203
|
-
return null;
|
|
204
|
-
resultSchema[fieldName] = result;
|
|
205
|
-
}
|
|
206
|
-
else if (type === SchemaVerifier_1.FieldTypeEnum.String || type === SchemaVerifier_1.FieldTypeEnum.StringOptional) {
|
|
207
|
-
if (typeof (val) !== "string")
|
|
208
|
-
return null;
|
|
209
|
-
resultSchema[fieldName] = val;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
//Probably another request schema
|
|
213
|
-
const result = (0, SchemaVerifier_1.verifySchema)(val, type);
|
|
214
|
-
if (result == null)
|
|
215
|
-
return null;
|
|
216
|
-
resultSchema[fieldName] = result;
|
|
217
|
-
}
|
|
134
|
+
if (!(0, SchemaVerifier_1.verifySchemaField)(val, schema[fieldName], fieldName, resultSchema))
|
|
135
|
+
return null;
|
|
218
136
|
}
|
|
219
137
|
return resultSchema;
|
|
220
138
|
}
|
|
@@ -5,17 +5,26 @@ export declare enum FieldTypeEnum {
|
|
|
5
5
|
Number = 2,
|
|
6
6
|
BigInt = 3,
|
|
7
7
|
Any = 4,
|
|
8
|
+
BigIntPositive = 5,
|
|
9
|
+
BigIntNotNegative = 6,
|
|
10
|
+
NumberPositive = 7,
|
|
11
|
+
NumberNotNegative = 8,
|
|
8
12
|
StringOptional = 100,
|
|
9
13
|
BooleanOptional = 101,
|
|
10
14
|
NumberOptional = 102,
|
|
11
15
|
BigIntOptional = 103,
|
|
12
|
-
AnyOptional = 104
|
|
16
|
+
AnyOptional = 104,
|
|
17
|
+
BigIntPositiveOptional = 105,
|
|
18
|
+
BigIntNotNegativeOptional = 106,
|
|
19
|
+
NumberPositiveOptional = 107,
|
|
20
|
+
NumberNotNegativeOptional = 108
|
|
13
21
|
}
|
|
14
|
-
export type FieldType<T extends FieldTypeEnum | RequestSchema | ((val: any) => (string | boolean | number | bigint | any))> = T extends FieldTypeEnum.String ? string : T extends FieldTypeEnum.Boolean ? boolean : T extends FieldTypeEnum.Number ? number : T extends FieldTypeEnum.BigInt ? bigint : T extends FieldTypeEnum.Any ? any : T extends FieldTypeEnum.StringOptional ? string : T extends FieldTypeEnum.BooleanOptional ? boolean : T extends FieldTypeEnum.NumberOptional ? number : T extends FieldTypeEnum.BigIntOptional ? bigint : T extends FieldTypeEnum.AnyOptional ? any : T extends RequestSchema ? RequestSchemaResult<T> : T extends ((val: any) => string) ? string : T extends ((val: any) => boolean) ? boolean : T extends ((val: any) => number) ? number : T extends ((val: any) => bigint) ? bigint : T extends ((val: any) => any) ? any : never;
|
|
22
|
+
export type FieldType<T extends FieldTypeEnum | RequestSchema | ((val: any) => (string | boolean | number | bigint | any))> = T extends FieldTypeEnum.String ? string : T extends FieldTypeEnum.Boolean ? boolean : T extends FieldTypeEnum.Number ? number : T extends FieldTypeEnum.NumberPositive ? number : T extends FieldTypeEnum.NumberNotNegative ? number : T extends FieldTypeEnum.BigInt ? bigint : T extends FieldTypeEnum.Any ? any : T extends FieldTypeEnum.BigIntPositive ? bigint : T extends FieldTypeEnum.BigIntNotNegative ? bigint : T extends FieldTypeEnum.StringOptional ? string : T extends FieldTypeEnum.BooleanOptional ? boolean : T extends FieldTypeEnum.NumberOptional ? number : T extends FieldTypeEnum.NumberPositiveOptional ? number : T extends FieldTypeEnum.NumberNotNegativeOptional ? number : T extends FieldTypeEnum.BigIntOptional ? bigint : T extends FieldTypeEnum.AnyOptional ? any : T extends FieldTypeEnum.BigIntPositiveOptional ? bigint : T extends FieldTypeEnum.BigIntNotNegativeOptional ? bigint : T extends RequestSchema ? RequestSchemaResult<T> : T extends ((val: any) => string) ? string : T extends ((val: any) => boolean) ? boolean : T extends ((val: any) => number) ? number : T extends ((val: any) => bigint) ? bigint : T extends ((val: any) => any) ? any : never;
|
|
15
23
|
export type RequestSchemaResult<T extends RequestSchema> = {
|
|
16
24
|
[key in keyof T]: FieldType<T[key]>;
|
|
17
25
|
};
|
|
18
26
|
export type RequestSchema = {
|
|
19
27
|
[fieldName: string]: FieldTypeEnum | RequestSchema | ((val: any) => any);
|
|
20
28
|
};
|
|
29
|
+
export declare function verifySchemaField(val: any, type: FieldTypeEnum | RequestSchema | ((val: any) => any), fieldName: string, resultSchema: any): boolean;
|
|
21
30
|
export declare function verifySchema<T extends RequestSchema>(req: any, schema: T): RequestSchemaResult<T>;
|