@atomiqlabs/lp-lib 14.0.0-dev.15 → 14.0.0-dev.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/swaps/assertions/FromBtcAmountAssertions.js +6 -2
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +2 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +1 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.js +2 -0
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +2 -1
- package/dist/utils/BitcoinUtils.d.ts +2 -0
- package/dist/utils/BitcoinUtils.js +45 -0
- package/package.json +1 -1
- package/src/swaps/assertions/FromBtcAmountAssertions.ts +6 -2
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +2 -0
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.ts +3 -0
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +2 -1
- package/src/utils/BitcoinUtils.ts +42 -0
|
@@ -108,11 +108,15 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
108
108
|
amountBDgas = _amountBDgas;
|
|
109
109
|
}
|
|
110
110
|
let amountBD;
|
|
111
|
+
let swapFee;
|
|
111
112
|
if (!requestedAmount.input) {
|
|
112
113
|
amountBD = await this.swapPricing.getToBtcSwapAmount(requestedAmount.amount, requestedAmount.token, chainIdentifier, true, requestedAmount.pricePrefetch);
|
|
113
114
|
signal.throwIfAborted();
|
|
114
115
|
// amt = (amt+base_fee)/(1-fee)
|
|
115
|
-
|
|
116
|
+
const denominator = (1000000n - fees.feePPM);
|
|
117
|
+
const _amountBD = ((amountBD + fees.baseFee) * 1000000n + denominator - 1n) / denominator;
|
|
118
|
+
swapFee = _amountBD - amountBD;
|
|
119
|
+
amountBD = _amountBD;
|
|
116
120
|
const tooLow = amountBD < (this.config.min * 95n / 100n);
|
|
117
121
|
const tooHigh = amountBD > (this.config.max * 105n / 100n);
|
|
118
122
|
if (tooLow || tooHigh) {
|
|
@@ -133,6 +137,7 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
133
137
|
else {
|
|
134
138
|
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
135
139
|
amountBD = requestedAmount.amount - amountBDgas;
|
|
140
|
+
swapFee = fees.baseFee + ((amountBD * fees.feePPM + 999999n) / 1000000n);
|
|
136
141
|
if (amountBD < 0n) {
|
|
137
142
|
throw {
|
|
138
143
|
code: 20003,
|
|
@@ -144,7 +149,6 @@ class FromBtcAmountAssertions extends AmountAssertions_1.AmountAssertions {
|
|
|
144
149
|
};
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
|
-
const swapFee = fees.baseFee + (amountBD * fees.feePPM / 1000000n);
|
|
148
152
|
const swapFeeInToken = await this.swapPricing.getFromBtcSwapAmount(swapFee, requestedAmount.token, chainIdentifier, true, requestedAmount.pricePrefetch);
|
|
149
153
|
signal.throwIfAborted();
|
|
150
154
|
const gasSwapFeeInToken = gasTokenAmount == null ?
|
|
@@ -232,6 +232,8 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
|
|
|
232
232
|
" maxFee: " + maxFee.toString(10) +
|
|
233
233
|
" invoice: " + swap.pr);
|
|
234
234
|
const blockHeight = await this.lightning.getBlockheight();
|
|
235
|
+
swap.payInitiated = true;
|
|
236
|
+
await this.saveSwapData(swap);
|
|
235
237
|
try {
|
|
236
238
|
await this.lightning.pay({
|
|
237
239
|
request: swap.pr,
|
|
@@ -12,6 +12,7 @@ export declare enum ToBtcLnSwapState {
|
|
|
12
12
|
export declare class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap<T, ToBtcLnSwapState> {
|
|
13
13
|
lnPaymentHash: string;
|
|
14
14
|
readonly pr: string;
|
|
15
|
+
payInitiated: boolean;
|
|
15
16
|
secret: string;
|
|
16
17
|
constructor(chainIdentifier: string, lnPaymentHash: string, pr: string, amountMtokens: bigint, swapFee: bigint, swapFeeInToken: bigint, quotedNetworkFee: bigint, quotedNetworkFeeInToken: bigint);
|
|
17
18
|
constructor(obj: any);
|
|
@@ -27,6 +27,7 @@ class ToBtcLnSwapAbs extends ToBtcBaseSwap_1.ToBtcBaseSwap {
|
|
|
27
27
|
this.pr = chainIdOrObj.pr;
|
|
28
28
|
this.secret = chainIdOrObj.secret;
|
|
29
29
|
this.lnPaymentHash = chainIdOrObj.lnPaymentHash;
|
|
30
|
+
this.payInitiated = chainIdOrObj.payInitiated;
|
|
30
31
|
//Compatibility with older versions
|
|
31
32
|
this.quotedNetworkFee ?? (this.quotedNetworkFee = (0, Utils_1.deserializeBN)(chainIdOrObj.maxFee));
|
|
32
33
|
this.realNetworkFee ?? (this.realNetworkFee = (0, Utils_1.deserializeBN)(chainIdOrObj.realRoutingFee));
|
|
@@ -41,6 +42,7 @@ class ToBtcLnSwapAbs extends ToBtcBaseSwap_1.ToBtcBaseSwap {
|
|
|
41
42
|
partialSerialized.pr = this.pr;
|
|
42
43
|
partialSerialized.lnPaymentHash = this.lnPaymentHash;
|
|
43
44
|
partialSerialized.secret = this.secret;
|
|
45
|
+
partialSerialized.payInitiated = this.payInitiated;
|
|
44
46
|
return partialSerialized;
|
|
45
47
|
}
|
|
46
48
|
isInitiated() {
|
|
@@ -12,6 +12,7 @@ const FromBtcAmountAssertions_1 = require("../assertions/FromBtcAmountAssertions
|
|
|
12
12
|
const crypto_1 = require("crypto");
|
|
13
13
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
14
14
|
const SpvVaults_1 = require("./SpvVaults");
|
|
15
|
+
const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
|
|
15
16
|
const TX_MAX_VSIZE = 16 * 1024;
|
|
16
17
|
class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
17
18
|
constructor(storageDirectory, vaultStorage, path, chainsData, swapPricing, bitcoin, bitcoinRpc, spvVaultSigner, config) {
|
|
@@ -342,7 +343,7 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
|
|
|
342
343
|
//Check correct psbt
|
|
343
344
|
for (let i = 1; i < transaction.inputsLength; i++) { //Skip first vault input
|
|
344
345
|
const txIn = transaction.getInput(i);
|
|
345
|
-
if ((0,
|
|
346
|
+
if ((0, BitcoinUtils_1.isLegacyInput)(txIn))
|
|
346
347
|
throw {
|
|
347
348
|
code: 20514,
|
|
348
349
|
msg: "Legacy (pre-segwit) inputs in tx are not allowed!"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isLegacyInput = void 0;
|
|
4
|
+
const utxo_1 = require("@scure/btc-signer/utxo");
|
|
5
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
|
+
function parsePushOpcode(script) {
|
|
7
|
+
if (script[0] === 0x00) {
|
|
8
|
+
return Uint8Array.from([]);
|
|
9
|
+
}
|
|
10
|
+
if (script[0] <= 0x4b) {
|
|
11
|
+
return script.slice(1, 1 + script[0]);
|
|
12
|
+
}
|
|
13
|
+
if (script[0] === 0x4c) {
|
|
14
|
+
return script.slice(2, 2 + script[1]);
|
|
15
|
+
}
|
|
16
|
+
if (script[0] === 0x4d) {
|
|
17
|
+
const length = Buffer.from(script.slice(1, 3)).readUInt16LE();
|
|
18
|
+
return script.slice(3, 3 + length);
|
|
19
|
+
}
|
|
20
|
+
if (script[0] === 0x4e) {
|
|
21
|
+
const length = Buffer.from(script.slice(1, 5)).readUInt32LE();
|
|
22
|
+
return script.slice(5, 5 + length);
|
|
23
|
+
}
|
|
24
|
+
if (script[0] === 0x4f) {
|
|
25
|
+
return Uint8Array.from([0x81]);
|
|
26
|
+
}
|
|
27
|
+
if (script[0] >= 0x51 && script[0] <= 0x60) {
|
|
28
|
+
return Uint8Array.from([script[0] - 0x50]);
|
|
29
|
+
}
|
|
30
|
+
throw new Error("No push opcode detected");
|
|
31
|
+
}
|
|
32
|
+
function isLegacyInput(input) {
|
|
33
|
+
const prevOut = (0, utxo_1.getPrevOut)(input);
|
|
34
|
+
const first = btc_signer_1.OutScript.decode(prevOut.script);
|
|
35
|
+
if (first.type === "tr" || first.type === "wsh" || first.type === "wpkh")
|
|
36
|
+
return false;
|
|
37
|
+
if (first.type === "sh") {
|
|
38
|
+
const redeemScript = input.redeemScript ?? parsePushOpcode(input.finalScriptSig);
|
|
39
|
+
const second = btc_signer_1.OutScript.decode(redeemScript);
|
|
40
|
+
if (second.type === "wsh" || second.type === "wpkh")
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
exports.isLegacyInput = isLegacyInput;
|
package/package.json
CHANGED
|
@@ -166,12 +166,16 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
let amountBD: bigint;
|
|
169
|
+
let swapFee: bigint;
|
|
169
170
|
if(!requestedAmount.input) {
|
|
170
171
|
amountBD = await this.swapPricing.getToBtcSwapAmount(requestedAmount.amount, requestedAmount.token, chainIdentifier, true, requestedAmount.pricePrefetch);
|
|
171
172
|
signal.throwIfAborted();
|
|
172
173
|
|
|
173
174
|
// amt = (amt+base_fee)/(1-fee)
|
|
174
|
-
|
|
175
|
+
const denominator = (1000000n - fees.feePPM);
|
|
176
|
+
const _amountBD = ((amountBD + fees.baseFee) * 1000000n + denominator - 1n) / denominator;
|
|
177
|
+
swapFee = _amountBD - amountBD;
|
|
178
|
+
amountBD = _amountBD;
|
|
175
179
|
|
|
176
180
|
const tooLow = amountBD < (this.config.min * 95n / 100n);
|
|
177
181
|
const tooHigh = amountBD > (this.config.max * 105n / 100n);
|
|
@@ -196,6 +200,7 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
196
200
|
} else {
|
|
197
201
|
this.checkBtcAmountInBounds(requestedAmount.amount);
|
|
198
202
|
amountBD = requestedAmount.amount - amountBDgas;
|
|
203
|
+
swapFee = fees.baseFee + ((amountBD * fees.feePPM + 999_999n) / 1000000n);
|
|
199
204
|
if(amountBD < 0n) {
|
|
200
205
|
throw {
|
|
201
206
|
code: 20003,
|
|
@@ -208,7 +213,6 @@ export class FromBtcAmountAssertions extends AmountAssertions {
|
|
|
208
213
|
}
|
|
209
214
|
}
|
|
210
215
|
|
|
211
|
-
const swapFee = fees.baseFee + (amountBD * fees.feePPM / 1000000n);
|
|
212
216
|
const swapFeeInToken = await this.swapPricing.getFromBtcSwapAmount(swapFee, requestedAmount.token, chainIdentifier, true, requestedAmount.pricePrefetch);
|
|
213
217
|
signal.throwIfAborted();
|
|
214
218
|
|
|
@@ -338,6 +338,8 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
338
338
|
|
|
339
339
|
const blockHeight = await this.lightning.getBlockheight();
|
|
340
340
|
|
|
341
|
+
swap.payInitiated = true;
|
|
342
|
+
await this.saveSwapData(swap);
|
|
341
343
|
try {
|
|
342
344
|
await this.lightning.pay({
|
|
343
345
|
request: swap.pr,
|
|
@@ -17,6 +17,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
|
|
|
17
17
|
|
|
18
18
|
lnPaymentHash: string;
|
|
19
19
|
readonly pr: string;
|
|
20
|
+
payInitiated: boolean;
|
|
20
21
|
|
|
21
22
|
secret: string;
|
|
22
23
|
|
|
@@ -43,6 +44,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
|
|
|
43
44
|
this.pr = chainIdOrObj.pr;
|
|
44
45
|
this.secret = chainIdOrObj.secret;
|
|
45
46
|
this.lnPaymentHash = chainIdOrObj.lnPaymentHash;
|
|
47
|
+
this.payInitiated = chainIdOrObj.payInitiated;
|
|
46
48
|
|
|
47
49
|
//Compatibility with older versions
|
|
48
50
|
this.quotedNetworkFee ??= deserializeBN(chainIdOrObj.maxFee);
|
|
@@ -60,6 +62,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
|
|
|
60
62
|
partialSerialized.pr = this.pr;
|
|
61
63
|
partialSerialized.lnPaymentHash = this.lnPaymentHash;
|
|
62
64
|
partialSerialized.secret = this.secret;
|
|
65
|
+
partialSerialized.payInitiated = this.payInitiated;
|
|
63
66
|
return partialSerialized;
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -29,6 +29,7 @@ import {FromBtcAmountAssertions} from "../assertions/FromBtcAmountAssertions";
|
|
|
29
29
|
import {randomBytes} from "crypto";
|
|
30
30
|
import {getInputType, OutScript, Transaction} from "@scure/btc-signer";
|
|
31
31
|
import {SpvVaults, VAULT_DUST_AMOUNT} from "./SpvVaults";
|
|
32
|
+
import {isLegacyInput} from "../../utils/BitcoinUtils";
|
|
32
33
|
|
|
33
34
|
export type SpvVaultSwapHandlerConfig = SwapBaseConfig & {
|
|
34
35
|
vaultsCheckInterval: number,
|
|
@@ -471,7 +472,7 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
|
|
|
471
472
|
//Check correct psbt
|
|
472
473
|
for(let i=1;i<transaction.inputsLength;i++) { //Skip first vault input
|
|
473
474
|
const txIn = transaction.getInput(i);
|
|
474
|
-
if(
|
|
475
|
+
if(isLegacyInput(txIn)) throw {
|
|
475
476
|
code: 20514,
|
|
476
477
|
msg: "Legacy (pre-segwit) inputs in tx are not allowed!"
|
|
477
478
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {TransactionInput} from "@scure/btc-signer/psbt";
|
|
2
|
+
import {getPrevOut} from "@scure/btc-signer/utxo";
|
|
3
|
+
import {OutScript} from "@scure/btc-signer";
|
|
4
|
+
|
|
5
|
+
function parsePushOpcode(script: Uint8Array): Uint8Array {
|
|
6
|
+
if(script[0]===0x00) {
|
|
7
|
+
return Uint8Array.from([]);
|
|
8
|
+
}
|
|
9
|
+
if(script[0]<=0x4b) {
|
|
10
|
+
return script.slice(1, 1+script[0]);
|
|
11
|
+
}
|
|
12
|
+
if(script[0]===0x4c) {
|
|
13
|
+
return script.slice(2, 2+script[1]);
|
|
14
|
+
}
|
|
15
|
+
if(script[0]===0x4d) {
|
|
16
|
+
const length = Buffer.from(script.slice(1, 3)).readUInt16LE();
|
|
17
|
+
return script.slice(3, 3+length);
|
|
18
|
+
}
|
|
19
|
+
if(script[0]===0x4e) {
|
|
20
|
+
const length = Buffer.from(script.slice(1, 5)).readUInt32LE();
|
|
21
|
+
return script.slice(5, 5+length);
|
|
22
|
+
}
|
|
23
|
+
if(script[0]===0x4f) {
|
|
24
|
+
return Uint8Array.from([0x81]);
|
|
25
|
+
}
|
|
26
|
+
if(script[0]>=0x51 && script[0]<=0x60) {
|
|
27
|
+
return Uint8Array.from([script[0] - 0x50]);
|
|
28
|
+
}
|
|
29
|
+
throw new Error("No push opcode detected");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isLegacyInput(input: TransactionInput): boolean {
|
|
33
|
+
const prevOut = getPrevOut(input);
|
|
34
|
+
const first = OutScript.decode(prevOut.script);
|
|
35
|
+
if(first.type==="tr" || first.type==="wsh" || first.type==="wpkh") return false;
|
|
36
|
+
if(first.type==="sh") {
|
|
37
|
+
const redeemScript = input.redeemScript ?? parsePushOpcode(input.finalScriptSig);
|
|
38
|
+
const second = OutScript.decode(redeemScript);
|
|
39
|
+
if(second.type==="wsh" || second.type==="wpkh") return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|