@atomiqlabs/lp-lib 17.4.0 → 17.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/swaps/SwapHandler.d.ts +8 -2
- package/dist/swaps/assertions/AmountAssertions.d.ts +10 -1
- package/dist/swaps/assertions/AmountAssertions.js +16 -7
- package/dist/swaps/assertions/FromBtcAmountAssertions.js +14 -10
- package/dist/swaps/assertions/ToBtcAmountAssertions.js +16 -12
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +68 -1
- package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +2 -2
- package/package.json +1 -1
- package/src/swaps/SwapHandler.ts +7 -2
- package/src/swaps/assertions/AmountAssertions.ts +28 -8
- package/src/swaps/assertions/FromBtcAmountAssertions.ts +16 -10
- package/src/swaps/assertions/ToBtcAmountAssertions.ts +18 -12
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +83 -2
- package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +2 -2
|
@@ -37,10 +37,16 @@ export type SwapBaseConfig = {
|
|
|
37
37
|
bitcoinBlocktime: bigint;
|
|
38
38
|
baseFee: bigint;
|
|
39
39
|
feePPM: bigint;
|
|
40
|
-
max: bigint;
|
|
41
|
-
min: bigint;
|
|
42
40
|
safetyFactor: bigint;
|
|
43
41
|
swapCheckInterval: number;
|
|
42
|
+
max: bigint;
|
|
43
|
+
min: bigint;
|
|
44
|
+
minMaxOverrides?: {
|
|
45
|
+
[chainIdentifier: string]: {
|
|
46
|
+
min: bigint;
|
|
47
|
+
max: bigint;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
44
50
|
};
|
|
45
51
|
export type MultichainData = {
|
|
46
52
|
chains: {
|
|
@@ -4,19 +4,28 @@ export type AmountAssertionsConfig = {
|
|
|
4
4
|
max: bigint;
|
|
5
5
|
baseFee: bigint;
|
|
6
6
|
feePPM: bigint;
|
|
7
|
+
minMaxOverrides?: {
|
|
8
|
+
[chainIdentifier: string]: {
|
|
9
|
+
min: bigint;
|
|
10
|
+
max: bigint;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
7
13
|
};
|
|
8
14
|
export declare abstract class AmountAssertions {
|
|
9
15
|
readonly config: AmountAssertionsConfig;
|
|
10
16
|
readonly swapPricing: ISwapPrice;
|
|
11
17
|
constructor(config: AmountAssertionsConfig, swapPricing: ISwapPrice);
|
|
18
|
+
getSwapMinimum(chainIdentifier: string): bigint;
|
|
19
|
+
getSwapMaximum(chainIdentifier: string): bigint;
|
|
12
20
|
/**
|
|
13
21
|
* Checks whether the bitcoin amount is within specified min/max bounds
|
|
14
22
|
*
|
|
15
23
|
* @param amount
|
|
24
|
+
* @param chainIdentifier
|
|
16
25
|
* @protected
|
|
17
26
|
* @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
|
|
18
27
|
*/
|
|
19
|
-
protected checkBtcAmountInBounds(amount: bigint): void;
|
|
28
|
+
protected checkBtcAmountInBounds(amount: bigint, chainIdentifier: string): void;
|
|
20
29
|
/**
|
|
21
30
|
* Handles and throws plugin errors
|
|
22
31
|
*
|
|
@@ -7,31 +7,40 @@ class AmountAssertions {
|
|
|
7
7
|
this.config = config;
|
|
8
8
|
this.swapPricing = swapPricing;
|
|
9
9
|
}
|
|
10
|
+
getSwapMinimum(chainIdentifier) {
|
|
11
|
+
return this.config.minMaxOverrides?.[chainIdentifier]?.min ?? this.config.min;
|
|
12
|
+
}
|
|
13
|
+
getSwapMaximum(chainIdentifier) {
|
|
14
|
+
return this.config.minMaxOverrides?.[chainIdentifier]?.max ?? this.config.max;
|
|
15
|
+
}
|
|
10
16
|
/**
|
|
11
17
|
* Checks whether the bitcoin amount is within specified min/max bounds
|
|
12
18
|
*
|
|
13
19
|
* @param amount
|
|
20
|
+
* @param chainIdentifier
|
|
14
21
|
* @protected
|
|
15
22
|
* @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
|
|
16
23
|
*/
|
|
17
|
-
checkBtcAmountInBounds(amount) {
|
|
18
|
-
|
|
24
|
+
checkBtcAmountInBounds(amount, chainIdentifier) {
|
|
25
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
26
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
27
|
+
if (amount < min) {
|
|
19
28
|
throw {
|
|
20
29
|
code: 20003,
|
|
21
30
|
msg: "Amount too low!",
|
|
22
31
|
data: {
|
|
23
|
-
min:
|
|
24
|
-
max:
|
|
32
|
+
min: min.toString(10),
|
|
33
|
+
max: max.toString(10)
|
|
25
34
|
}
|
|
26
35
|
};
|
|
27
36
|
}
|
|
28
|
-
if (amount >
|
|
37
|
+
if (amount > max) {
|
|
29
38
|
throw {
|
|
30
39
|
code: 20004,
|
|
31
40
|
msg: "Amount too high!",
|
|
32
41
|
data: {
|
|
33
|
-
min:
|
|
34
|
-
max:
|
|
42
|
+
min: min.toString(10),
|
|
43
|
+
max: max.toString(10)
|
|
35
44
|
}
|
|
36
45
|
};
|
|
37
46
|
}
|
|
@@ -19,7 +19,9 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
19
19
|
* @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
|
|
20
20
|
*/
|
|
21
21
|
async preCheckFromBtcAmounts(swapType, request, requestedAmount, gasAmount) {
|
|
22
|
-
const
|
|
22
|
+
const min = this.getSwapMinimum(request.chainIdentifier);
|
|
23
|
+
const max = this.getSwapMaximum(request.chainIdentifier);
|
|
24
|
+
const res = await PluginManager_1.PluginManager.onHandlePreFromBtcQuote(swapType, request, requestedAmount, request.chainIdentifier, { minInBtc: min, maxInBtc: max }, { baseFeeInBtc: this.config.baseFee, feePPM: this.config.feePPM }, gasAmount);
|
|
23
25
|
if (res != null) {
|
|
24
26
|
AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(res);
|
|
25
27
|
if ((0, IPlugin_1.isQuoteSetFees)(res)) {
|
|
@@ -32,7 +34,7 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
if (requestedAmount.input)
|
|
35
|
-
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
37
|
+
this.checkBtcAmountInBounds(requestedAmount.amount, request.chainIdentifier);
|
|
36
38
|
if (gasAmount != null && gasAmount.amount !== 0n) {
|
|
37
39
|
if (gasAmount.amount > (this.config.gasTokenMax?.[request.chainIdentifier] ?? 0n)) {
|
|
38
40
|
throw {
|
|
@@ -64,7 +66,9 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
64
66
|
const chainIdentifier = request.chainIdentifier;
|
|
65
67
|
let securityDepositApyPPM;
|
|
66
68
|
let securityDepositBaseMultiplierPPM;
|
|
67
|
-
const
|
|
69
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
70
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
71
|
+
const res = await PluginManager_1.PluginManager.onHandlePostFromBtcQuote(swapType, request, requestedAmount, chainIdentifier, { minInBtc: min, maxInBtc: max }, { baseFeeInBtc: fees.baseFee, feePPM: fees.feePPM }, gasTokenAmount);
|
|
68
72
|
signal.throwIfAborted();
|
|
69
73
|
if (res != null) {
|
|
70
74
|
AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(res);
|
|
@@ -117,11 +121,11 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
117
121
|
const _amountBD = ((amountBD + fees.baseFee) * 1000000n + denominator - 1n) / denominator;
|
|
118
122
|
swapFee = _amountBD - amountBD;
|
|
119
123
|
amountBD = _amountBD;
|
|
120
|
-
const tooLow = amountBD < (
|
|
121
|
-
const tooHigh = amountBD > (
|
|
124
|
+
const tooLow = amountBD < (min * 95n / 100n);
|
|
125
|
+
const tooHigh = amountBD > (max * 105n / 100n);
|
|
122
126
|
if (tooLow || tooHigh) {
|
|
123
|
-
const adjustedMin =
|
|
124
|
-
const adjustedMax =
|
|
127
|
+
const adjustedMin = min * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
|
|
128
|
+
const adjustedMax = max * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
|
|
125
129
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(adjustedMin, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch);
|
|
126
130
|
const maxIn = await this.swapPricing.getFromBtcSwapAmount(adjustedMax, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch);
|
|
127
131
|
throw {
|
|
@@ -135,7 +139,7 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
else {
|
|
138
|
-
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
142
|
+
this.checkBtcAmountInBounds(requestedAmount.amount, chainIdentifier);
|
|
139
143
|
amountBD = requestedAmount.amount - amountBDgas;
|
|
140
144
|
swapFee = fees.baseFee + ((amountBD * fees.feePPM + 999999n) / 1000000n);
|
|
141
145
|
if (amountBD - swapFee < 0n) {
|
|
@@ -143,8 +147,8 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
143
147
|
code: 20003,
|
|
144
148
|
msg: "Amount too low!",
|
|
145
149
|
data: {
|
|
146
|
-
min:
|
|
147
|
-
max:
|
|
150
|
+
min: min.toString(10),
|
|
151
|
+
max: max.toString(10)
|
|
148
152
|
}
|
|
149
153
|
};
|
|
150
154
|
}
|
|
@@ -14,7 +14,9 @@ class ToBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
14
14
|
* @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
|
|
15
15
|
*/
|
|
16
16
|
async preCheckToBtcAmounts(swapType, request, requestedAmount) {
|
|
17
|
-
const
|
|
17
|
+
const min = this.getSwapMinimum(request.chainIdentifier);
|
|
18
|
+
const max = this.getSwapMaximum(request.chainIdentifier);
|
|
19
|
+
const res = await PluginManager_1.PluginManager.onHandlePreToBtcQuote(swapType, request, requestedAmount, request.chainIdentifier, { minInBtc: min, maxInBtc: max }, { baseFeeInBtc: this.config.baseFee, feePPM: this.config.feePPM });
|
|
18
20
|
if (res != null) {
|
|
19
21
|
AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(res);
|
|
20
22
|
if ((0, IPlugin_1.isQuoteSetFees)(res)) {
|
|
@@ -25,7 +27,7 @@ class ToBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
if (!requestedAmount.input) {
|
|
28
|
-
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
30
|
+
this.checkBtcAmountInBounds(requestedAmount.amount, request.chainIdentifier);
|
|
29
31
|
}
|
|
30
32
|
return {
|
|
31
33
|
baseFee: this.config.baseFee,
|
|
@@ -46,7 +48,9 @@ class ToBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
46
48
|
*/
|
|
47
49
|
async checkToBtcAmount(swapType, request, requestedAmount, fees, getNetworkFee, signal) {
|
|
48
50
|
const chainIdentifier = request.chainIdentifier;
|
|
49
|
-
const
|
|
51
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
52
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
53
|
+
const res = await PluginManager_1.PluginManager.onHandlePostToBtcQuote(swapType, request, requestedAmount, request.chainIdentifier, { minInBtc: min, maxInBtc: max }, { baseFeeInBtc: fees.baseFee, feePPM: fees.feePPM, networkFeeGetter: getNetworkFee });
|
|
50
54
|
signal.throwIfAborted();
|
|
51
55
|
if (res != null) {
|
|
52
56
|
AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(res);
|
|
@@ -90,19 +94,19 @@ class ToBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
90
94
|
//Decrease by base fee
|
|
91
95
|
amountBD = amountBD - fees.baseFee;
|
|
92
96
|
//If it's already smaller than minimum, set it to minimum so we can calculate the network fee
|
|
93
|
-
if (amountBD < (
|
|
94
|
-
amountBD =
|
|
97
|
+
if (amountBD < (min * 95n / 100n)) {
|
|
98
|
+
amountBD = min;
|
|
95
99
|
tooLow = true;
|
|
96
100
|
}
|
|
97
101
|
//If it's already larger than maximum, set it to maximum so we can calculate the network fee
|
|
98
|
-
if (amountBD > (
|
|
99
|
-
amountBD =
|
|
102
|
+
if (amountBD > (max * 105n / 100n)) {
|
|
103
|
+
amountBD = max;
|
|
100
104
|
tooHigh = true;
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
else {
|
|
104
108
|
amountBD = requestedAmount.amount;
|
|
105
|
-
this.checkBtcAmountInBounds(amountBD);
|
|
109
|
+
this.checkBtcAmountInBounds(amountBD, chainIdentifier);
|
|
106
110
|
}
|
|
107
111
|
const resp = await getNetworkFee(amountBD);
|
|
108
112
|
signal.throwIfAborted();
|
|
@@ -111,12 +115,12 @@ class ToBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
111
115
|
amountBD = amountBD - resp.networkFee;
|
|
112
116
|
//Decrease by percentage fee
|
|
113
117
|
amountBD = amountBD * 1000000n / (fees.feePPM + 1000000n);
|
|
114
|
-
tooHigh || (tooHigh = amountBD > (
|
|
115
|
-
tooLow || (tooLow = amountBD < (
|
|
118
|
+
tooHigh || (tooHigh = amountBD > (max * 105n / 100n));
|
|
119
|
+
tooLow || (tooLow = amountBD < (min * 95n / 100n));
|
|
116
120
|
if (tooLow || tooHigh) {
|
|
117
121
|
//Compute min/max
|
|
118
|
-
let adjustedMin =
|
|
119
|
-
let adjustedMax =
|
|
122
|
+
let adjustedMin = min * (fees.feePPM + 1000000n) / 1000000n;
|
|
123
|
+
let adjustedMax = max * (fees.feePPM + 1000000n) / 1000000n;
|
|
120
124
|
adjustedMin = adjustedMin + fees.baseFee + resp.networkFee;
|
|
121
125
|
adjustedMax = adjustedMax + fees.baseFee + resp.networkFee;
|
|
122
126
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(adjustedMin, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch);
|
|
@@ -17,6 +17,17 @@ const AmountAssertions_1 = require("../assertions/AmountAssertions");
|
|
|
17
17
|
const IPlugin_1 = require("../../plugins/IPlugin");
|
|
18
18
|
const StickyAddress_1 = require("./StickyAddress");
|
|
19
19
|
const TX_MAX_VSIZE = 16 * 1024;
|
|
20
|
+
function parseAmountAdjustUtxos(amountAdjustUtxos) {
|
|
21
|
+
if (!Array.isArray(amountAdjustUtxos))
|
|
22
|
+
return null;
|
|
23
|
+
if (amountAdjustUtxos.length > 250)
|
|
24
|
+
return null;
|
|
25
|
+
const validArray = amountAdjustUtxos.every(value => value != null && typeof (value) === "object" && typeof (value.value) === "number" && typeof (value.vSize) === "number" &&
|
|
26
|
+
(value.cpfp == null || (typeof (value.cpfp) === "object" && typeof (value.cpfp.effectiveVSize) === "number" && typeof (value.cpfp.effectiveFeeRate) === "number")));
|
|
27
|
+
if (!validArray)
|
|
28
|
+
return null;
|
|
29
|
+
return amountAdjustUtxos;
|
|
30
|
+
}
|
|
20
31
|
class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
21
32
|
constructor(storageDirectory, vaultStorage, path, chainsData, swapPricing, bitcoin, bitcoinRpc, spvVaultSigner, config, stickyAddresses) {
|
|
22
33
|
super(storageDirectory, path, chainsData, swapPricing);
|
|
@@ -309,6 +320,23 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
309
320
|
code: 20100,
|
|
310
321
|
msg: "Invalid request body"
|
|
311
322
|
};
|
|
323
|
+
const inputAmountAdjustments = req.paramReader.getExistingParamsOrNull({
|
|
324
|
+
amountUtxos: SchemaVerifier_1.FieldTypeEnum.AnyOptional,
|
|
325
|
+
amountFeeRate: SchemaVerifier_1.FieldTypeEnum.NumberOptional
|
|
326
|
+
});
|
|
327
|
+
if (inputAmountAdjustments == null)
|
|
328
|
+
throw {
|
|
329
|
+
code: 20100,
|
|
330
|
+
msg: "Invalid request body"
|
|
331
|
+
};
|
|
332
|
+
const clientInputUtxos = inputAmountAdjustments?.amountUtxos != null
|
|
333
|
+
? parseAmountAdjustUtxos(inputAmountAdjustments.amountUtxos)
|
|
334
|
+
: null;
|
|
335
|
+
if (inputAmountAdjustments?.amountUtxos != null && clientInputUtxos == null)
|
|
336
|
+
throw {
|
|
337
|
+
code: 20100,
|
|
338
|
+
msg: "Invalid request body (amountUtxos)"
|
|
339
|
+
};
|
|
312
340
|
const parsedBody = { ...preFetchParsedBody, ...actualParsedBody };
|
|
313
341
|
metadata.request = parsedBody;
|
|
314
342
|
if (parsedBody.gasToken !== chainInterface.getNativeCurrencyAddress())
|
|
@@ -333,6 +361,44 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
333
361
|
parsedBody.amount,
|
|
334
362
|
token: parsedBody.token
|
|
335
363
|
};
|
|
364
|
+
if (clientInputUtxos != null) {
|
|
365
|
+
if (parsedBody.exactOut)
|
|
366
|
+
throw {
|
|
367
|
+
code: 20193,
|
|
368
|
+
msg: "amountAdjustUtxos cannot be specified for exactOut swaps!"
|
|
369
|
+
};
|
|
370
|
+
let btcFeeRate = await btcFeeRatePrefetch;
|
|
371
|
+
if (inputAmountAdjustments.amountFeeRate != null && inputAmountAdjustments.amountFeeRate > btcFeeRate)
|
|
372
|
+
btcFeeRate = inputAmountAdjustments.amountFeeRate;
|
|
373
|
+
let feeAccumulator = 0;
|
|
374
|
+
let valueAccumulator = 0;
|
|
375
|
+
for (let utxo of clientInputUtxos) {
|
|
376
|
+
const cpfpAdditionalFee = utxo.cpfp == null ? 0 : Math.ceil(utxo.cpfp.effectiveVSize * Math.max(0, btcFeeRate - utxo.cpfp.effectiveFeeRate));
|
|
377
|
+
const spendFee = utxo.vSize * btcFeeRate;
|
|
378
|
+
const totalFee = cpfpAdditionalFee + spendFee;
|
|
379
|
+
if (totalFee > utxo.value)
|
|
380
|
+
continue; //Skip detrimental UTXO
|
|
381
|
+
feeAccumulator += totalFee;
|
|
382
|
+
valueAccumulator += utxo.value;
|
|
383
|
+
}
|
|
384
|
+
let baseTxVSize = 10.5; // 4b version, 1b inputs, 1b outputs, 4b locktime, 0.5vB witness flag + witness elements count
|
|
385
|
+
//vault input and output
|
|
386
|
+
baseTxVSize += 32 + 4 + 1 + 4; //Input base
|
|
387
|
+
baseTxVSize += this.vaultSigner.getAddressType() === "p2tr" ? (1 + 1 + 65) / 4 : (1 + 1 + 72 + 1 + 33) / 4;
|
|
388
|
+
baseTxVSize += 8 + 1; //Output base
|
|
389
|
+
baseTxVSize += this.vaultSigner.getAddressType() === "p2tr" ? 34 : 22;
|
|
390
|
+
//opreturn output
|
|
391
|
+
baseTxVSize += 8 + 1; //Output base
|
|
392
|
+
const opReturnDataSize = spvVaultContract.toOpReturnData(parsedBody.address, parsedBody.gasAmount > 0 ? [0xffffffffffffffffn, 0xffffffffffffffffn] : [0xffffffffffffffffn]).length;
|
|
393
|
+
baseTxVSize += (opReturnDataSize <= 0x4b ? 2 : 3 /*Needs an OP_PUSHDATA1 opcode*/) + opReturnDataSize;
|
|
394
|
+
//LP output
|
|
395
|
+
baseTxVSize += 8 + 1; //Output base
|
|
396
|
+
baseTxVSize += this.bitcoin.getAddressType() === "p2tr" ? 34 : this.bitcoin.getAddressType() === "p2wpkh" ? 22 : 23;
|
|
397
|
+
const baseTxFee = Math.ceil(baseTxVSize) * btcFeeRate;
|
|
398
|
+
feeAccumulator += baseTxFee;
|
|
399
|
+
const amount = Math.floor(valueAccumulator - Math.ceil(feeAccumulator));
|
|
400
|
+
requestedAmount.amount = BigInt(amount);
|
|
401
|
+
}
|
|
336
402
|
const gasTokenAmount = {
|
|
337
403
|
input: false,
|
|
338
404
|
amount: parsedBody.gasAmount * (100000n + parsedBody.callerFeeRate + parsedBody.frontingFeeRate) / 100000n,
|
|
@@ -420,7 +486,8 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
420
486
|
gasSwapFee: gasSwapFeeInToken.toString(10),
|
|
421
487
|
callerFeeShare: callerFeeShare.toString(10),
|
|
422
488
|
frontingFeeShare: frontingFeeShare.toString(10),
|
|
423
|
-
executionFeeShare: executionFeeShare.toString(10)
|
|
489
|
+
executionFeeShare: executionFeeShare.toString(10),
|
|
490
|
+
usedUtxoInputCalculation: clientInputUtxos != null
|
|
424
491
|
}
|
|
425
492
|
});
|
|
426
493
|
}));
|
|
@@ -134,9 +134,9 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
|
|
|
134
134
|
}
|
|
135
135
|
else {
|
|
136
136
|
//If lower than minimum then ignore
|
|
137
|
-
if (sentSats < this.
|
|
137
|
+
if (sentSats < this.AmountAssertions.getSwapMinimum(swap.chainIdentifier))
|
|
138
138
|
return;
|
|
139
|
-
if (sentSats > this.
|
|
139
|
+
if (sentSats > this.AmountAssertions.getSwapMaximum(swap.chainIdentifier)) {
|
|
140
140
|
swap.adjustedInput = sentSats;
|
|
141
141
|
swap.btcTx = tx;
|
|
142
142
|
swap.txId = tx.txid;
|
package/package.json
CHANGED
package/src/swaps/SwapHandler.ts
CHANGED
|
@@ -41,10 +41,15 @@ export type SwapBaseConfig = {
|
|
|
41
41
|
bitcoinBlocktime: bigint,
|
|
42
42
|
baseFee: bigint,
|
|
43
43
|
feePPM: bigint,
|
|
44
|
+
safetyFactor: bigint,
|
|
45
|
+
swapCheckInterval: number,
|
|
46
|
+
|
|
44
47
|
max: bigint,
|
|
45
48
|
min: bigint,
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
|
|
50
|
+
minMaxOverrides?: {
|
|
51
|
+
[chainIdentifier: string]: {min: bigint, max: bigint}
|
|
52
|
+
}
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
export type MultichainData = {
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import {ISwapPrice} from "../../prices/ISwapPrice";
|
|
2
2
|
import {isQuoteAmountTooHigh, isQuoteAmountTooLow, isQuoteThrow} from "../../plugins/IPlugin";
|
|
3
3
|
|
|
4
|
-
export type AmountAssertionsConfig = {
|
|
4
|
+
export type AmountAssertionsConfig = {
|
|
5
|
+
min: bigint,
|
|
6
|
+
max: bigint,
|
|
7
|
+
baseFee: bigint,
|
|
8
|
+
feePPM: bigint,
|
|
9
|
+
|
|
10
|
+
minMaxOverrides?: {
|
|
11
|
+
[chainIdentifier: string]: {min: bigint, max: bigint}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
5
14
|
|
|
6
15
|
export abstract class AmountAssertions {
|
|
7
16
|
|
|
@@ -13,32 +22,43 @@ export abstract class AmountAssertions {
|
|
|
13
22
|
this.swapPricing = swapPricing;
|
|
14
23
|
}
|
|
15
24
|
|
|
25
|
+
getSwapMinimum(chainIdentifier: string) {
|
|
26
|
+
return this.config.minMaxOverrides?.[chainIdentifier]?.min ?? this.config.min;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getSwapMaximum(chainIdentifier: string) {
|
|
30
|
+
return this.config.minMaxOverrides?.[chainIdentifier]?.max ?? this.config.max;
|
|
31
|
+
}
|
|
32
|
+
|
|
16
33
|
/**
|
|
17
34
|
* Checks whether the bitcoin amount is within specified min/max bounds
|
|
18
35
|
*
|
|
19
36
|
* @param amount
|
|
37
|
+
* @param chainIdentifier
|
|
20
38
|
* @protected
|
|
21
39
|
* @throws {DefinedRuntimeError} will throw an error if the amount is outside minimum/maximum bounds
|
|
22
40
|
*/
|
|
23
|
-
protected checkBtcAmountInBounds(amount: bigint): void {
|
|
24
|
-
|
|
41
|
+
protected checkBtcAmountInBounds(amount: bigint, chainIdentifier: string): void {
|
|
42
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
43
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
44
|
+
if (amount < min) {
|
|
25
45
|
throw {
|
|
26
46
|
code: 20003,
|
|
27
47
|
msg: "Amount too low!",
|
|
28
48
|
data: {
|
|
29
|
-
min:
|
|
30
|
-
max:
|
|
49
|
+
min: min.toString(10),
|
|
50
|
+
max: max.toString(10)
|
|
31
51
|
}
|
|
32
52
|
};
|
|
33
53
|
}
|
|
34
54
|
|
|
35
|
-
if(amount >
|
|
55
|
+
if(amount > max) {
|
|
36
56
|
throw {
|
|
37
57
|
code: 20004,
|
|
38
58
|
msg: "Amount too high!",
|
|
39
59
|
data: {
|
|
40
|
-
min:
|
|
41
|
-
max:
|
|
60
|
+
min: min.toString(10),
|
|
61
|
+
max: max.toString(10)
|
|
42
62
|
}
|
|
43
63
|
};
|
|
44
64
|
}
|
|
@@ -42,12 +42,15 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
42
42
|
securityDepositApyPPM?: bigint,
|
|
43
43
|
securityDepositBaseMultiplierPPM?: bigint,
|
|
44
44
|
}> {
|
|
45
|
+
const min = this.getSwapMinimum(request.chainIdentifier);
|
|
46
|
+
const max = this.getSwapMaximum(request.chainIdentifier);
|
|
47
|
+
|
|
45
48
|
const res = await PluginManager.onHandlePreFromBtcQuote(
|
|
46
49
|
swapType,
|
|
47
50
|
request,
|
|
48
51
|
requestedAmount,
|
|
49
52
|
request.chainIdentifier,
|
|
50
|
-
{minInBtc:
|
|
53
|
+
{minInBtc: min, maxInBtc: max},
|
|
51
54
|
{baseFeeInBtc: this.config.baseFee, feePPM: this.config.feePPM},
|
|
52
55
|
gasAmount
|
|
53
56
|
);
|
|
@@ -62,7 +65,7 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
|
-
if(requestedAmount.input) this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
68
|
+
if(requestedAmount.input) this.checkBtcAmountInBounds(requestedAmount.amount, request.chainIdentifier);
|
|
66
69
|
|
|
67
70
|
if(gasAmount!=null && gasAmount.amount!==0n) {
|
|
68
71
|
if(gasAmount.amount > (this.config.gasTokenMax?.[request.chainIdentifier] ?? 0n)) {
|
|
@@ -117,12 +120,15 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
117
120
|
let securityDepositApyPPM: bigint;
|
|
118
121
|
let securityDepositBaseMultiplierPPM: bigint;
|
|
119
122
|
|
|
123
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
124
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
125
|
+
|
|
120
126
|
const res = await PluginManager.onHandlePostFromBtcQuote(
|
|
121
127
|
swapType,
|
|
122
128
|
request,
|
|
123
129
|
requestedAmount,
|
|
124
130
|
chainIdentifier,
|
|
125
|
-
{minInBtc:
|
|
131
|
+
{minInBtc: min, maxInBtc: max},
|
|
126
132
|
{baseFeeInBtc: fees.baseFee, feePPM: fees.feePPM},
|
|
127
133
|
gasTokenAmount
|
|
128
134
|
);
|
|
@@ -177,11 +183,11 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
177
183
|
swapFee = _amountBD - amountBD;
|
|
178
184
|
amountBD = _amountBD;
|
|
179
185
|
|
|
180
|
-
const tooLow = amountBD < (
|
|
181
|
-
const tooHigh = amountBD > (
|
|
186
|
+
const tooLow = amountBD < (min * 95n / 100n);
|
|
187
|
+
const tooHigh = amountBD > (max * 105n / 100n);
|
|
182
188
|
if(tooLow || tooHigh) {
|
|
183
|
-
const adjustedMin =
|
|
184
|
-
const adjustedMax =
|
|
189
|
+
const adjustedMin = min * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
|
|
190
|
+
const adjustedMax = max * (1000000n - fees.feePPM) / (1000000n - fees.baseFee);
|
|
185
191
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(
|
|
186
192
|
adjustedMin, requestedAmount.token, chainIdentifier, null, requestedAmount.pricePrefetch
|
|
187
193
|
);
|
|
@@ -198,7 +204,7 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
198
204
|
};
|
|
199
205
|
}
|
|
200
206
|
} else {
|
|
201
|
-
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
207
|
+
this.checkBtcAmountInBounds(requestedAmount.amount, chainIdentifier);
|
|
202
208
|
amountBD = requestedAmount.amount - amountBDgas;
|
|
203
209
|
swapFee = fees.baseFee + ((amountBD * fees.feePPM + 999_999n) / 1000000n);
|
|
204
210
|
if(amountBD - swapFee < 0n) {
|
|
@@ -206,8 +212,8 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
206
212
|
code: 20003,
|
|
207
213
|
msg: "Amount too low!",
|
|
208
214
|
data: {
|
|
209
|
-
min:
|
|
210
|
-
max:
|
|
215
|
+
min: min.toString(10),
|
|
216
|
+
max: max.toString(10)
|
|
211
217
|
}
|
|
212
218
|
};
|
|
213
219
|
}
|
|
@@ -21,12 +21,15 @@ export class ToBtcAmountAssertions extends AmountAssertions {
|
|
|
21
21
|
request: RequestData<ToBtcLnRequestType | ToBtcRequestType>,
|
|
22
22
|
requestedAmount: {input: boolean, amount: bigint, token: string}
|
|
23
23
|
): Promise<{baseFee: bigint, feePPM: bigint}> {
|
|
24
|
+
const min = this.getSwapMinimum(request.chainIdentifier);
|
|
25
|
+
const max = this.getSwapMaximum(request.chainIdentifier);
|
|
26
|
+
|
|
24
27
|
const res = await PluginManager.onHandlePreToBtcQuote(
|
|
25
28
|
swapType,
|
|
26
29
|
request,
|
|
27
30
|
requestedAmount,
|
|
28
31
|
request.chainIdentifier,
|
|
29
|
-
{minInBtc:
|
|
32
|
+
{minInBtc: min, maxInBtc: max},
|
|
30
33
|
{baseFeeInBtc: this.config.baseFee, feePPM: this.config.feePPM},
|
|
31
34
|
);
|
|
32
35
|
if(res!=null) {
|
|
@@ -39,7 +42,7 @@ export class ToBtcAmountAssertions extends AmountAssertions {
|
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
if(!requestedAmount.input) {
|
|
42
|
-
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
45
|
+
this.checkBtcAmountInBounds(requestedAmount.amount, request.chainIdentifier);
|
|
43
46
|
}
|
|
44
47
|
return {
|
|
45
48
|
baseFee: this.config.baseFee,
|
|
@@ -77,12 +80,15 @@ export class ToBtcAmountAssertions extends AmountAssertions {
|
|
|
77
80
|
}> {
|
|
78
81
|
const chainIdentifier = request.chainIdentifier;
|
|
79
82
|
|
|
83
|
+
const min = this.getSwapMinimum(chainIdentifier);
|
|
84
|
+
const max = this.getSwapMaximum(chainIdentifier);
|
|
85
|
+
|
|
80
86
|
const res = await PluginManager.onHandlePostToBtcQuote<T>(
|
|
81
87
|
swapType,
|
|
82
88
|
request,
|
|
83
89
|
requestedAmount,
|
|
84
90
|
request.chainIdentifier,
|
|
85
|
-
{minInBtc:
|
|
91
|
+
{minInBtc: min, maxInBtc: max},
|
|
86
92
|
{baseFeeInBtc: fees.baseFee, feePPM: fees.feePPM, networkFeeGetter: getNetworkFee}
|
|
87
93
|
);
|
|
88
94
|
signal.throwIfAborted();
|
|
@@ -128,18 +134,18 @@ export class ToBtcAmountAssertions extends AmountAssertions {
|
|
|
128
134
|
amountBD = amountBD - fees.baseFee;
|
|
129
135
|
|
|
130
136
|
//If it's already smaller than minimum, set it to minimum so we can calculate the network fee
|
|
131
|
-
if(amountBD < (
|
|
132
|
-
amountBD =
|
|
137
|
+
if(amountBD < (min * 95n / 100n)) {
|
|
138
|
+
amountBD = min;
|
|
133
139
|
tooLow = true;
|
|
134
140
|
}
|
|
135
141
|
//If it's already larger than maximum, set it to maximum so we can calculate the network fee
|
|
136
|
-
if(amountBD > (
|
|
137
|
-
amountBD =
|
|
142
|
+
if(amountBD > (max * 105n / 100n)) {
|
|
143
|
+
amountBD = max;
|
|
138
144
|
tooHigh = true;
|
|
139
145
|
}
|
|
140
146
|
} else {
|
|
141
147
|
amountBD = requestedAmount.amount;
|
|
142
|
-
this.checkBtcAmountInBounds(amountBD);
|
|
148
|
+
this.checkBtcAmountInBounds(amountBD, chainIdentifier);
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
const resp = await getNetworkFee(amountBD);
|
|
@@ -152,12 +158,12 @@ export class ToBtcAmountAssertions extends AmountAssertions {
|
|
|
152
158
|
//Decrease by percentage fee
|
|
153
159
|
amountBD = amountBD * 1000000n / (fees.feePPM + 1000000n);
|
|
154
160
|
|
|
155
|
-
tooHigh ||= amountBD > (
|
|
156
|
-
tooLow ||= amountBD < (
|
|
161
|
+
tooHigh ||= amountBD > (max * 105n / 100n);
|
|
162
|
+
tooLow ||= amountBD < (min * 95n / 100n);
|
|
157
163
|
if(tooLow || tooHigh) {
|
|
158
164
|
//Compute min/max
|
|
159
|
-
let adjustedMin =
|
|
160
|
-
let adjustedMax =
|
|
165
|
+
let adjustedMin = min * (fees.feePPM + 1000000n) / 1000000n;
|
|
166
|
+
let adjustedMax = max * (fees.feePPM + 1000000n) / 1000000n;
|
|
161
167
|
adjustedMin = adjustedMin + fees.baseFee + resp.networkFee;
|
|
162
168
|
adjustedMax = adjustedMax + fees.baseFee + resp.networkFee;
|
|
163
169
|
const minIn = await this.swapPricing.getFromBtcSwapAmount(
|
|
@@ -64,6 +64,26 @@ export type SpvVaultPostQuote = {
|
|
|
64
64
|
|
|
65
65
|
const TX_MAX_VSIZE = 16*1024;
|
|
66
66
|
|
|
67
|
+
type AmountAdjustUtxo = {
|
|
68
|
+
value: number,
|
|
69
|
+
vSize: number,
|
|
70
|
+
cpfp?: {
|
|
71
|
+
effectiveVSize: number,
|
|
72
|
+
effectiveFeeRate: number
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseAmountAdjustUtxos(amountAdjustUtxos: any): AmountAdjustUtxo[] {
|
|
77
|
+
if(!Array.isArray(amountAdjustUtxos)) return null;
|
|
78
|
+
if(amountAdjustUtxos.length > 250) return null;
|
|
79
|
+
const validArray = amountAdjustUtxos.every(value =>
|
|
80
|
+
value!=null && typeof(value)==="object" && typeof(value.value)==="number" && typeof(value.vSize)==="number" &&
|
|
81
|
+
(value.cpfp==null || (typeof(value.cpfp)==="object" && typeof(value.cpfp.effectiveVSize)==="number" && typeof(value.cpfp.effectiveFeeRate)==="number"))
|
|
82
|
+
);
|
|
83
|
+
if(!validArray) return null;
|
|
84
|
+
return amountAdjustUtxos;
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapState> {
|
|
68
88
|
readonly type = SwapHandlerType.FROM_BTC_SPV;
|
|
69
89
|
readonly inflightSwapStates = new Set([SpvVaultSwapState.SIGNED, SpvVaultSwapState.SENT, SpvVaultSwapState.BTC_CONFIRMED]);
|
|
@@ -354,7 +374,7 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
|
|
|
354
374
|
gasTokenPricePrefetchPromise
|
|
355
375
|
} = this.getPricePrefetches(chainIdentifier, preFetchParsedBody.token, preFetchParsedBody.gasToken, abortController);
|
|
356
376
|
const nativeBalancePrefetch = this.prefetchNativeBalanceIfNeeded(chainIdentifier, abortController);
|
|
357
|
-
const btcFeeRatePrefetch = this.bitcoin.getFeeRate().catch(e => {
|
|
377
|
+
const btcFeeRatePrefetch: Promise<number> = this.bitcoin.getFeeRate().catch(e => {
|
|
358
378
|
abortController.abort(e);
|
|
359
379
|
return null;
|
|
360
380
|
});
|
|
@@ -405,6 +425,23 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
|
|
|
405
425
|
msg: "Invalid request body"
|
|
406
426
|
};
|
|
407
427
|
|
|
428
|
+
const inputAmountAdjustments = req.paramReader.getExistingParamsOrNull({
|
|
429
|
+
amountUtxos: FieldTypeEnum.AnyOptional,
|
|
430
|
+
amountFeeRate: FieldTypeEnum.NumberOptional
|
|
431
|
+
});
|
|
432
|
+
if(inputAmountAdjustments==null) throw {
|
|
433
|
+
code: 20100,
|
|
434
|
+
msg: "Invalid request body"
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const clientInputUtxos: AmountAdjustUtxo[] | null = inputAmountAdjustments?.amountUtxos!=null
|
|
438
|
+
? parseAmountAdjustUtxos(inputAmountAdjustments.amountUtxos)
|
|
439
|
+
: null;
|
|
440
|
+
if(inputAmountAdjustments?.amountUtxos!=null && clientInputUtxos==null) throw {
|
|
441
|
+
code: 20100,
|
|
442
|
+
msg: "Invalid request body (amountUtxos)"
|
|
443
|
+
};
|
|
444
|
+
|
|
408
445
|
const parsedBody: SpvVaultSwapRequestType = {...preFetchParsedBody, ...actualParsedBody};
|
|
409
446
|
metadata.request = parsedBody;
|
|
410
447
|
|
|
@@ -429,6 +466,48 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
|
|
|
429
466
|
parsedBody.amount,
|
|
430
467
|
token: parsedBody.token
|
|
431
468
|
};
|
|
469
|
+
if(clientInputUtxos!=null) {
|
|
470
|
+
if(parsedBody.exactOut) throw {
|
|
471
|
+
code: 20193,
|
|
472
|
+
msg: "amountAdjustUtxos cannot be specified for exactOut swaps!"
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
let btcFeeRate = await btcFeeRatePrefetch;
|
|
476
|
+
if(inputAmountAdjustments.amountFeeRate!=null && inputAmountAdjustments.amountFeeRate>btcFeeRate)
|
|
477
|
+
btcFeeRate = inputAmountAdjustments.amountFeeRate;
|
|
478
|
+
|
|
479
|
+
let feeAccumulator: number = 0;
|
|
480
|
+
let valueAccumulator: number = 0;
|
|
481
|
+
for(let utxo of clientInputUtxos) {
|
|
482
|
+
const cpfpAdditionalFee: number = utxo.cpfp==null ? 0 : Math.ceil(utxo.cpfp.effectiveVSize * Math.max(0, btcFeeRate - utxo.cpfp.effectiveFeeRate));
|
|
483
|
+
const spendFee: number = utxo.vSize * btcFeeRate;
|
|
484
|
+
const totalFee: number = cpfpAdditionalFee + spendFee;
|
|
485
|
+
if(totalFee > utxo.value) continue; //Skip detrimental UTXO
|
|
486
|
+
feeAccumulator += totalFee;
|
|
487
|
+
valueAccumulator += utxo.value;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
let baseTxVSize: number = 10.5; // 4b version, 1b inputs, 1b outputs, 4b locktime, 0.5vB witness flag + witness elements count
|
|
491
|
+
//vault input and output
|
|
492
|
+
baseTxVSize += 32 + 4 + 1 + 4; //Input base
|
|
493
|
+
baseTxVSize += this.vaultSigner.getAddressType()==="p2tr" ? (1+1+65)/4 : (1+1+72+1+33)/4;
|
|
494
|
+
baseTxVSize += 8 + 1; //Output base
|
|
495
|
+
baseTxVSize += this.vaultSigner.getAddressType()==="p2tr" ? 34 : 22;
|
|
496
|
+
//opreturn output
|
|
497
|
+
baseTxVSize += 8 + 1; //Output base
|
|
498
|
+
const opReturnDataSize = spvVaultContract.toOpReturnData(parsedBody.address, parsedBody.gasAmount > 0 ? [0xffffffffffffffffn, 0xffffffffffffffffn] : [0xffffffffffffffffn]).length;
|
|
499
|
+
baseTxVSize += (opReturnDataSize <= 0x4b ? 2 : 3 /*Needs an OP_PUSHDATA1 opcode*/) + opReturnDataSize;
|
|
500
|
+
//LP output
|
|
501
|
+
baseTxVSize += 8 + 1; //Output base
|
|
502
|
+
baseTxVSize += this.bitcoin.getAddressType()==="p2tr" ? 34 : this.bitcoin.getAddressType()==="p2wpkh" ? 22 : 23;
|
|
503
|
+
|
|
504
|
+
const baseTxFee = Math.ceil(baseTxVSize) * btcFeeRate;
|
|
505
|
+
feeAccumulator += baseTxFee;
|
|
506
|
+
|
|
507
|
+
const amount = Math.floor(valueAccumulator - Math.ceil(feeAccumulator));
|
|
508
|
+
requestedAmount.amount = BigInt(amount);
|
|
509
|
+
}
|
|
510
|
+
|
|
432
511
|
const gasTokenAmount = {
|
|
433
512
|
input: false,
|
|
434
513
|
amount: parsedBody.gasAmount * (100_000n + parsedBody.callerFeeRate + parsedBody.frontingFeeRate) / 100_000n,
|
|
@@ -562,7 +641,9 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
|
|
|
562
641
|
|
|
563
642
|
callerFeeShare: callerFeeShare.toString(10),
|
|
564
643
|
frontingFeeShare: frontingFeeShare.toString(10),
|
|
565
|
-
executionFeeShare: executionFeeShare.toString(10)
|
|
644
|
+
executionFeeShare: executionFeeShare.toString(10),
|
|
645
|
+
|
|
646
|
+
usedUtxoInputCalculation: clientInputUtxos!=null
|
|
566
647
|
}
|
|
567
648
|
});
|
|
568
649
|
}));
|
|
@@ -185,8 +185,8 @@ export class FromBtcTrusted extends SwapHandler<FromBtcTrustedSwap, FromBtcTrust
|
|
|
185
185
|
swap.adjustedOutput = swap.outputTokens;
|
|
186
186
|
} else {
|
|
187
187
|
//If lower than minimum then ignore
|
|
188
|
-
if(sentSats < this.
|
|
189
|
-
if(sentSats > this.
|
|
188
|
+
if(sentSats < this.AmountAssertions.getSwapMinimum(swap.chainIdentifier)) return;
|
|
189
|
+
if(sentSats > this.AmountAssertions.getSwapMaximum(swap.chainIdentifier)) {
|
|
190
190
|
swap.adjustedInput = sentSats;
|
|
191
191
|
swap.btcTx = tx;
|
|
192
192
|
swap.txId = tx.txid;
|