@atomiqlabs/sdk 8.7.2 → 8.7.4
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/bitcoin/wallet/SingleAddressBitcoinWallet.js +15 -1
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +1 -0
- package/dist/intermediaries/apis/IntermediaryAPI.js +2 -1
- package/dist/prices/abstract/ISwapPrice.d.ts +4 -2
- package/dist/prices/abstract/ISwapPrice.js +18 -6
- package/dist/swapper/SwapperUtils.d.ts +6 -0
- package/dist/swapper/SwapperUtils.js +13 -0
- package/dist/swaps/ISwap.js +10 -5
- package/dist/swaps/ISwapWrapper.d.ts +1 -0
- package/dist/swaps/ISwapWrapper.js +2 -2
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +3 -2
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1 -1
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -3
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +3 -2
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +4 -4
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +4 -4
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1 -1
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +7 -0
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +7 -3
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -1
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +1 -1
- package/package.json +2 -2
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +14 -2
- package/src/intermediaries/apis/IntermediaryAPI.ts +4 -2
- package/src/prices/abstract/ISwapPrice.ts +19 -6
- package/src/swapper/SwapperUtils.ts +12 -0
- package/src/swaps/ISwap.ts +17 -6
- package/src/swaps/ISwapWrapper.ts +4 -3
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +4 -2
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +4 -1
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +11 -4
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +4 -2
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +6 -6
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -5
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +4 -1
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +18 -4
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -1
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +1 -1
|
@@ -5,10 +5,12 @@ const utils_1 = require("@scure/btc-signer/utils");
|
|
|
5
5
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
6
|
const buffer_1 = require("buffer");
|
|
7
7
|
const BitcoinWallet_1 = require("./BitcoinWallet");
|
|
8
|
+
const base_1 = require("@atomiqlabs/base");
|
|
8
9
|
const bip32_1 = require("@scure/bip32");
|
|
9
10
|
const bip39_1 = require("@scure/bip39");
|
|
10
11
|
const english_js_1 = require("@scure/bip39/wordlists/english.js");
|
|
11
12
|
const sha2_1 = require("@noble/hashes/sha2");
|
|
13
|
+
const logger = (0, base_1.getLogger)("SingleAddressBitcoinWallet: ");
|
|
12
14
|
/**
|
|
13
15
|
* Bitcoin wallet implementation deriving a single address from a WIF encoded private key
|
|
14
16
|
*
|
|
@@ -32,12 +34,24 @@ class SingleAddressBitcoinWallet extends BitcoinWallet_1.BitcoinWallet {
|
|
|
32
34
|
if (address == null)
|
|
33
35
|
throw new Error("Failed to generate p2wpkh address from the provided private key!");
|
|
34
36
|
this.address = address;
|
|
37
|
+
this.addressType = (0, BitcoinWallet_1.identifyAddressType)(this.address, network);
|
|
35
38
|
}
|
|
36
39
|
else {
|
|
37
40
|
this.address = addressDataOrWIF.address;
|
|
41
|
+
this.addressType = (0, BitcoinWallet_1.identifyAddressType)(this.address, network);
|
|
38
42
|
this.pubkey = buffer_1.Buffer.from(addressDataOrWIF.publicKey, "hex");
|
|
43
|
+
// Some wallets seem to be returning a full 33-byte compressed pubkey instead of a taproot
|
|
44
|
+
// 32-byte long X-only key. Handle these cases here
|
|
45
|
+
if (this.addressType === "p2tr") {
|
|
46
|
+
if (this.pubkey.length !== 33)
|
|
47
|
+
return;
|
|
48
|
+
const leadingByte = this.pubkey[0];
|
|
49
|
+
if (leadingByte !== 0x03 && leadingByte !== 0x02)
|
|
50
|
+
throw new Error("Invalid public key passed for taproot bitcoin wallet, expected an X-only 32-byte public key, or a compressed 33-byte public key");
|
|
51
|
+
logger.debug(`constructor(): Converting compressed public key ${addressDataOrWIF.publicKey} to taproot X-only 32-byte public key`);
|
|
52
|
+
this.pubkey = this.pubkey.slice(1);
|
|
53
|
+
}
|
|
39
54
|
}
|
|
40
|
-
this.addressType = (0, BitcoinWallet_1.identifyAddressType)(this.address, network);
|
|
41
55
|
}
|
|
42
56
|
/**
|
|
43
57
|
* Returns all the wallet addresses controlled by the wallet
|
|
@@ -244,6 +244,7 @@ export type SpvFromBTCPrepare = SwapInit & {
|
|
|
244
244
|
exactOut: boolean;
|
|
245
245
|
callerFeeRate: Promise<bigint>;
|
|
246
246
|
frontingFeeRate: bigint;
|
|
247
|
+
stickyAddress?: boolean;
|
|
247
248
|
};
|
|
248
249
|
declare const SpvFromBTCInitResponseSchema: {
|
|
249
250
|
readonly txId: FieldTypeEnum.String;
|
|
@@ -543,7 +543,8 @@ class IntermediaryAPI {
|
|
|
543
543
|
gasAmount: init.gasAmount.toString(10),
|
|
544
544
|
gasToken: init.gasToken,
|
|
545
545
|
frontingFeeRate: init.frontingFeeRate.toString(10),
|
|
546
|
-
callerFeeRate: init.callerFeeRate.then(val => val.toString(10))
|
|
546
|
+
callerFeeRate: init.callerFeeRate.then(val => val.toString(10)),
|
|
547
|
+
stickyAddress: init.stickyAddress
|
|
547
548
|
}, {
|
|
548
549
|
code: SchemaVerifier_1.FieldTypeEnum.Number,
|
|
549
550
|
msg: SchemaVerifier_1.FieldTypeEnum.String,
|
|
@@ -64,8 +64,9 @@ export declare abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
64
64
|
* @param tokenAddress Token address to be paid
|
|
65
65
|
* @param abortSignal
|
|
66
66
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
67
|
+
* @param realSwapFeeSats
|
|
67
68
|
*/
|
|
68
|
-
isValidAmountSend<C extends ChainIds<T>>(chainIdentifier: C, amountSats: bigint, satsBaseFee: bigint, feePPM: bigint, paidToken: bigint, tokenAddress: string, abortSignal?: AbortSignal, preFetchedPrice?: bigint | null): Promise<PriceInfoType>;
|
|
69
|
+
isValidAmountSend<C extends ChainIds<T>>(chainIdentifier: C, amountSats: bigint, satsBaseFee: bigint, feePPM: bigint, paidToken: bigint, tokenAddress: string, abortSignal?: AbortSignal, preFetchedPrice?: bigint | null, realSwapFeeSats?: bigint): Promise<PriceInfoType>;
|
|
69
70
|
/**
|
|
70
71
|
* Recomputes pricing info without fetching the current price
|
|
71
72
|
*
|
|
@@ -88,8 +89,9 @@ export declare abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
88
89
|
* @param tokenAddress Token address to be received
|
|
89
90
|
* @param abortSignal
|
|
90
91
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
92
|
+
* @param realSwapFeeSats
|
|
91
93
|
*/
|
|
92
|
-
isValidAmountReceive<C extends ChainIds<T>>(chainIdentifier: C, amountSats: bigint, satsBaseFee: bigint, feePPM: bigint, receiveToken: bigint, tokenAddress: string, abortSignal?: AbortSignal, preFetchedPrice?: bigint | null): Promise<PriceInfoType>;
|
|
94
|
+
isValidAmountReceive<C extends ChainIds<T>>(chainIdentifier: C, amountSats: bigint, satsBaseFee: bigint, feePPM: bigint, receiveToken: bigint, tokenAddress: string, abortSignal?: AbortSignal, preFetchedPrice?: bigint | null, realSwapFeeSats?: bigint): Promise<PriceInfoType>;
|
|
93
95
|
/**
|
|
94
96
|
* Pre-fetches the pricing data for a given token, such that further calls to {@link isValidAmountReceive} or
|
|
95
97
|
* {@link isValidAmountSend} are quicker and don't need to wait for the price fetch
|
|
@@ -59,10 +59,14 @@ class ISwapPrice {
|
|
|
59
59
|
* @param tokenAddress Token address to be paid
|
|
60
60
|
* @param abortSignal
|
|
61
61
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
62
|
+
* @param realSwapFeeSats
|
|
62
63
|
*/
|
|
63
|
-
async isValidAmountSend(chainIdentifier, amountSats, satsBaseFee, feePPM, paidToken, tokenAddress, abortSignal, preFetchedPrice) {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
async isValidAmountSend(chainIdentifier, amountSats, satsBaseFee, feePPM, paidToken, tokenAddress, abortSignal, preFetchedPrice, realSwapFeeSats) {
|
|
65
|
+
if (realSwapFeeSats != undefined && realSwapFeeSats < 0)
|
|
66
|
+
throw new Error("Invalid swap fee! Swap fee cannot be negative!");
|
|
67
|
+
const totalSats = realSwapFeeSats != undefined
|
|
68
|
+
? amountSats + realSwapFeeSats
|
|
69
|
+
: (amountSats * (1000000n + feePPM) / 1000000n) + satsBaseFee;
|
|
66
70
|
const totalUSats = totalSats * 1000000n;
|
|
67
71
|
const swapPriceUSatPerToken = totalUSats * (10n ** BigInt(this.getDecimalsThrowing(chainIdentifier, tokenAddress))) / paidToken;
|
|
68
72
|
if (this.shouldIgnore(chainIdentifier, tokenAddress))
|
|
@@ -122,10 +126,18 @@ class ISwapPrice {
|
|
|
122
126
|
* @param tokenAddress Token address to be received
|
|
123
127
|
* @param abortSignal
|
|
124
128
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
129
|
+
* @param realSwapFeeSats
|
|
125
130
|
*/
|
|
126
|
-
async isValidAmountReceive(chainIdentifier, amountSats, satsBaseFee, feePPM, receiveToken, tokenAddress, abortSignal, preFetchedPrice) {
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
async isValidAmountReceive(chainIdentifier, amountSats, satsBaseFee, feePPM, receiveToken, tokenAddress, abortSignal, preFetchedPrice, realSwapFeeSats) {
|
|
132
|
+
if (realSwapFeeSats != undefined) {
|
|
133
|
+
if (realSwapFeeSats >= amountSats)
|
|
134
|
+
throw new Error("Invalid swap fee! Larger than or equal to total output amount!");
|
|
135
|
+
if (realSwapFeeSats < 0)
|
|
136
|
+
throw new Error("Invalid swap fee! Must be non-negative!");
|
|
137
|
+
}
|
|
138
|
+
const totalSats = realSwapFeeSats != undefined
|
|
139
|
+
? amountSats - realSwapFeeSats
|
|
140
|
+
: (amountSats * (1000000n - feePPM) / 1000000n) - satsBaseFee;
|
|
129
141
|
const totalUSats = totalSats * 1000000n;
|
|
130
142
|
const swapPriceUSatPerToken = totalUSats * (10n ** BigInt(this.getDecimalsThrowing(chainIdentifier, tokenAddress))) / receiveToken;
|
|
131
143
|
if (this.shouldIgnore(chainIdentifier, tokenAddress))
|
|
@@ -138,6 +138,12 @@ export declare class SwapperUtils<T extends MultiChain> {
|
|
|
138
138
|
* Returns the address of the native currency of the smart chain
|
|
139
139
|
*/
|
|
140
140
|
getNativeToken<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier): SCToken<ChainIdentifier>;
|
|
141
|
+
/**
|
|
142
|
+
* Returns whether when swapping to the provided token a gas drop can be requested
|
|
143
|
+
*
|
|
144
|
+
* @param token
|
|
145
|
+
*/
|
|
146
|
+
destinationTokenSupportsGasDrop<ChainIdentifier extends ChainIds<T>>(token: SCToken<ChainIdentifier>): boolean;
|
|
141
147
|
/**
|
|
142
148
|
* Returns a random signer for a given smart chain
|
|
143
149
|
*
|
|
@@ -373,6 +373,19 @@ class SwapperUtils {
|
|
|
373
373
|
throw new Error("Invalid chain identifier! Unknown chain: " + chainIdentifier);
|
|
374
374
|
return this.root._tokens[chainIdentifier][this.root._chains[chainIdentifier].chainInterface.getNativeCurrencyAddress()];
|
|
375
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Returns whether when swapping to the provided token a gas drop can be requested
|
|
378
|
+
*
|
|
379
|
+
* @param token
|
|
380
|
+
*/
|
|
381
|
+
destinationTokenSupportsGasDrop(token) {
|
|
382
|
+
if (this.root._chains[token.chainId] == null)
|
|
383
|
+
throw new Error("Invalid chain identifier! Unknown chain: " + token.chainId);
|
|
384
|
+
const { chainInterface } = this.root._chains[token.chainId];
|
|
385
|
+
if (chainInterface.shouldGetNativeTokenDrop != null)
|
|
386
|
+
return chainInterface.shouldGetNativeTokenDrop(token.address);
|
|
387
|
+
return chainInterface.getNativeCurrencyAddress() === token.address;
|
|
388
|
+
}
|
|
376
389
|
/**
|
|
377
390
|
* Returns a random signer for a given smart chain
|
|
378
391
|
*
|
package/dist/swaps/ISwap.js
CHANGED
|
@@ -166,16 +166,21 @@ class ISwap {
|
|
|
166
166
|
if (this.pricingInfo == null)
|
|
167
167
|
return;
|
|
168
168
|
const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
169
|
-
const input = this.getInput();
|
|
170
169
|
const output = this.getOutput();
|
|
171
|
-
if (
|
|
170
|
+
if (output.isUnknown)
|
|
172
171
|
return;
|
|
173
|
-
if ((0, Token_1.isSCToken)(
|
|
174
|
-
|
|
172
|
+
if ((0, Token_1.isSCToken)(this.getInputToken()) && this.getDirection() === SwapDirection_1.SwapDirection.TO_BTC) {
|
|
173
|
+
const input = this.getInputWithoutFee();
|
|
174
|
+
if (input.isUnknown)
|
|
175
|
+
return;
|
|
176
|
+
this.pricingInfo = await this.wrapper._prices.isValidAmountSend(this.chainIdentifier, output.rawAmount, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, input.rawAmount + this.swapFee, input.token.address, undefined, undefined, this.swapFeeBtc);
|
|
175
177
|
this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
|
|
176
178
|
}
|
|
177
179
|
else if ((0, Token_1.isSCToken)(output.token) && this.getDirection() === SwapDirection_1.SwapDirection.FROM_BTC) {
|
|
178
|
-
|
|
180
|
+
const input = this.getInput();
|
|
181
|
+
if (input.isUnknown)
|
|
182
|
+
return;
|
|
183
|
+
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, input.rawAmount, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, output.rawAmount, output.token.address, undefined, undefined, this.swapFeeBtc);
|
|
179
184
|
this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
|
|
180
185
|
}
|
|
181
186
|
}
|
|
@@ -194,6 +194,7 @@ export declare abstract class ISwapWrapper<T extends ChainType, D extends SwapTy
|
|
|
194
194
|
swapFeePPM: number;
|
|
195
195
|
}, send: boolean, amountSats: bigint, amountToken: bigint, token: string, feeData: {
|
|
196
196
|
networkFee?: bigint;
|
|
197
|
+
swapFeeBtc?: bigint;
|
|
197
198
|
}, pricePrefetchPromise?: Promise<bigint | undefined>, usdPricePrefetchPromise?: Promise<number | undefined>, abortSignal?: AbortSignal): Promise<PriceInfoType>;
|
|
198
199
|
/**
|
|
199
200
|
* Processes a single smart chain on-chain event
|
|
@@ -114,8 +114,8 @@ class ISwapWrapper {
|
|
|
114
114
|
amountToken = amountToken - feeData.networkFee;
|
|
115
115
|
const [isValidAmount, usdPrice] = await Promise.all([
|
|
116
116
|
send ?
|
|
117
|
-
this._prices.isValidAmountSend(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise) :
|
|
118
|
-
this._prices.isValidAmountReceive(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise),
|
|
117
|
+
this._prices.isValidAmountSend(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise, feeData.swapFeeBtc) :
|
|
118
|
+
this._prices.isValidAmountReceive(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise, feeData.swapFeeBtc),
|
|
119
119
|
usdPricePrefetchPromise.then(value => {
|
|
120
120
|
if (value != null)
|
|
121
121
|
return value;
|
|
@@ -221,10 +221,11 @@ class FromBTCLNWrapper extends IFromBTCLNWrapper_1.IFromBTCLNWrapper {
|
|
|
221
221
|
if (decodedPr.timeExpireDate == null)
|
|
222
222
|
throw new IntermediaryError_1.IntermediaryError("Invalid returned swap invoice, no expiry date field");
|
|
223
223
|
const amountIn = (BigInt(decodedPr.millisatoshis) + 999n) / 1000n;
|
|
224
|
+
const swapFeeBtc = resp.swapFee * amountIn / (resp.total - resp.swapFee);
|
|
224
225
|
try {
|
|
225
226
|
this.verifyReturnedData(resp, amountData, lp, _options, decodedPr, paymentHash);
|
|
226
227
|
const [pricingInfo] = await Promise.all([
|
|
227
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTCLN], false, amountIn, resp.total, amountData.token, {}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
228
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTCLN], false, amountIn, resp.total, amountData.token, { swapFeeBtc }, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
228
229
|
this.verifyIntermediaryLiquidity(resp.total, (0, Utils_1.throwIfUndefined)(liquidityPromise)),
|
|
229
230
|
lnCapacityPromise != null ? this.verifyLnNodeCapacity(lp, decodedPr, lnCapacityPromise, abortController.signal) : Promise.resolve()
|
|
230
231
|
]);
|
|
@@ -233,7 +234,7 @@ class FromBTCLNWrapper extends IFromBTCLNWrapper_1.IFromBTCLNWrapper {
|
|
|
233
234
|
url: lp.url,
|
|
234
235
|
expiry: decodedPr.timeExpireDate * 1000,
|
|
235
236
|
swapFee: resp.swapFee,
|
|
236
|
-
swapFeeBtc
|
|
237
|
+
swapFeeBtc,
|
|
237
238
|
feeRate: (await _preFetches.feeRatePromise[version]),
|
|
238
239
|
initialSwapData: await this._contract(version).createSwapData(base_1.ChainSwapType.HTLC, lp.getAddress(this.chainIdentifier), recipient, amountData.token, resp.total, _hash[version], this.getRandomSequence(), BigInt(Math.floor(Date.now() / 1000)), false, true, resp.securityDeposit, 0n, nativeTokenAddress),
|
|
239
240
|
pr: resp.pr,
|
|
@@ -206,7 +206,7 @@ class FromBTCLNAutoSwap extends IEscrowSwap_1.IEscrowSwap {
|
|
|
206
206
|
if (this.pricingInfo == null || this.btcAmountSwap == null)
|
|
207
207
|
return;
|
|
208
208
|
const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
209
|
-
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken());
|
|
209
|
+
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputAmountWithoutFee(), this.getSwapData().getToken(), undefined, undefined, this.swapFeeBtc);
|
|
210
210
|
this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
|
|
211
211
|
}
|
|
212
212
|
//////////////////////////////
|
|
@@ -231,7 +231,10 @@ class FromBTCLNAutoWrapper extends IFromBTCLNWrapper_1.IFromBTCLNWrapper {
|
|
|
231
231
|
description: options?.description,
|
|
232
232
|
descriptionHash: (0, Utils_1.parseHashValueExact32Bytes)(options?.descriptionHash, "description hash")
|
|
233
233
|
};
|
|
234
|
-
if (
|
|
234
|
+
if (_options.gasAmount !== 0n &&
|
|
235
|
+
(this._chain.shouldGetNativeTokenDrop != null
|
|
236
|
+
? !this._chain.shouldGetNativeTokenDrop(amountData.token)
|
|
237
|
+
: amountData.token === this._chain.getNativeCurrencyAddress()))
|
|
235
238
|
throw new UserError_1.UserError("Cannot specify `gasAmount` for swaps to a native token!");
|
|
236
239
|
if (_options.description != null && buffer_1.Buffer.byteLength(_options.description, "utf8") > 500)
|
|
237
240
|
throw new UserError_1.UserError("Invalid description length");
|
|
@@ -301,9 +304,9 @@ class FromBTCLNAutoWrapper extends IFromBTCLNWrapper_1.IFromBTCLNWrapper {
|
|
|
301
304
|
try {
|
|
302
305
|
this.verifyReturnedData(resp, amountData, lp, _options, decodedPr, paymentHash, claimerBounty);
|
|
303
306
|
const [pricingInfo, gasPricingInfo] = await Promise.all([
|
|
304
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTCLN_AUTO], false, resp.btcAmountSwap, resp.total, amountData.token, {}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
307
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTCLN_AUTO], false, resp.btcAmountSwap, resp.total, amountData.token, { swapFeeBtc: resp.swapFeeBtc }, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
305
308
|
_options.gasAmount === 0n ? Promise.resolve(undefined) : this.verifyReturnedPrice({ ...lp.services[SwapType_1.SwapType.FROM_BTCLN_AUTO], swapBaseFee: 0 }, //Base fee should be charged only on the amount, not on gas
|
|
306
|
-
false, resp.btcAmountGas, resp.totalGas + resp.claimerBounty, nativeTokenAddress, {}, _preFetches.gasTokenPricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
309
|
+
false, resp.btcAmountGas, resp.totalGas + resp.claimerBounty, nativeTokenAddress, { swapFeeBtc: resp.gasSwapFeeBtc }, _preFetches.gasTokenPricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
307
310
|
this.verifyIntermediaryLiquidity(resp.total, (0, Utils_1.throwIfUndefined)(liquidityPromise)),
|
|
308
311
|
_options.unsafeSkipLnNodeCheck ? Promise.resolve() : this.verifyLnNodeCapacity(lp, decodedPr, lnCapacityPromise, abortController.signal)
|
|
309
312
|
]);
|
|
@@ -335,10 +335,11 @@ class FromBTCWrapper extends IFromBTCWrapper_1.IFromBTCWrapper {
|
|
|
335
335
|
}, undefined, e => e instanceof RequestError_1.RequestError, abortController.signal);
|
|
336
336
|
const data = new (this._swapDataDeserializer(version))(resp.data);
|
|
337
337
|
data.setClaimer(recipient);
|
|
338
|
+
const swapFeeBtc = resp.swapFee * resp.amount / (data.getAmount() - resp.swapFee);
|
|
338
339
|
this.verifyReturnedData(recipient, resp, amountData, lp, _options, data, sequence, (await claimerBountyPrefetchPromise[version]), nativeTokenAddress);
|
|
339
340
|
const [pricingInfo, signatureExpiry] = await Promise.all([
|
|
340
341
|
//Get intermediary's liquidity
|
|
341
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTC], false, resp.amount, resp.total, amountData.token, {}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
342
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.FROM_BTC], false, resp.amount, resp.total, amountData.token, { swapFeeBtc }, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
342
343
|
this.verifyReturnedSignature(recipient, data, resp, feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
343
344
|
this.verifyIntermediaryLiquidity(data.getAmount(), (0, Utils_1.throwIfUndefined)(liquidityPromise)),
|
|
344
345
|
]);
|
|
@@ -347,7 +348,7 @@ class FromBTCWrapper extends IFromBTCWrapper_1.IFromBTCWrapper {
|
|
|
347
348
|
url: lp.url,
|
|
348
349
|
expiry: signatureExpiry,
|
|
349
350
|
swapFee: resp.swapFee,
|
|
350
|
-
swapFeeBtc
|
|
351
|
+
swapFeeBtc,
|
|
351
352
|
feeRate: (await feeRatePromise[version]),
|
|
352
353
|
signatureData: resp,
|
|
353
354
|
data,
|
|
@@ -181,15 +181,15 @@ class ToBTCLNWrapper extends IToBTCWrapper_1.IToBTCWrapper {
|
|
|
181
181
|
const data = new (this._swapDataDeserializer(version))(resp.data);
|
|
182
182
|
data.setOfferer(signer);
|
|
183
183
|
await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
|
|
184
|
+
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
184
185
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
185
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTCLN], true, amountOut, data.getAmount(), amountData.token, { networkFee: resp.maxFee }, preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
186
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTCLN], true, amountOut, data.getAmount(), amountData.token, { networkFee: resp.maxFee, swapFeeBtc }, preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal),
|
|
186
187
|
this.verifyReturnedSignature(signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
187
188
|
reputationPromise
|
|
188
189
|
]);
|
|
189
190
|
abortController.signal.throwIfAborted();
|
|
190
191
|
if (reputation != null)
|
|
191
192
|
lp.reputation[amountData.token.toString()] = reputation;
|
|
192
|
-
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
193
193
|
const quote = new ToBTCLNSwap_1.ToBTCLNSwap(this, {
|
|
194
194
|
pricingInfo,
|
|
195
195
|
url: lp.url,
|
|
@@ -339,15 +339,15 @@ class ToBTCLNWrapper extends IToBTCWrapper_1.IToBTCWrapper {
|
|
|
339
339
|
const data = new (this._swapDataDeserializer(version))(resp.data);
|
|
340
340
|
data.setOfferer(signer);
|
|
341
341
|
await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
|
|
342
|
+
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
342
343
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
343
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTCLN], true, prepareResp.amount, data.getAmount(), amountData.token, { networkFee: resp.maxFee }, preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal),
|
|
344
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTCLN], true, prepareResp.amount, data.getAmount(), amountData.token, { networkFee: resp.maxFee, swapFeeBtc }, preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal),
|
|
344
345
|
this.verifyReturnedSignature(signer, data, resp, preFetches.feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
345
346
|
reputationPromise
|
|
346
347
|
]);
|
|
347
348
|
abortController.signal.throwIfAborted();
|
|
348
349
|
if (reputation != null)
|
|
349
350
|
lp.reputation[amountData.token.toString()] = reputation;
|
|
350
|
-
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
351
351
|
const quote = new ToBTCLNSwap_1.ToBTCLNSwap(this, {
|
|
352
352
|
pricingInfo,
|
|
353
353
|
url: lp.url,
|
|
@@ -200,18 +200,18 @@ class ToBTCWrapper extends IToBTCWrapper_1.IToBTCWrapper {
|
|
|
200
200
|
let hash = _hash?.[version] ?? this._contract(version).getHashForOnchain(outputScript, resp.amount, _options.confirmations, nonce).toString("hex");
|
|
201
201
|
const data = new (this._swapDataDeserializer(version))(resp.data);
|
|
202
202
|
data.setOfferer(signer);
|
|
203
|
+
const inputWithoutFees = data.getAmount() - resp.swapFee - resp.networkFee;
|
|
204
|
+
const swapFeeBtc = resp.swapFee * resp.amount / inputWithoutFees;
|
|
205
|
+
const networkFeeBtc = resp.networkFee * resp.amount / inputWithoutFees;
|
|
203
206
|
this.verifyReturnedData(signer, resp, amountData, lp, _options, data, hash);
|
|
204
207
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
205
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTC], true, resp.amount, data.getAmount(), amountData.token, resp, pricePreFetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
208
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.TO_BTC], true, resp.amount, data.getAmount(), amountData.token, { networkFee: resp.networkFee, swapFeeBtc }, pricePreFetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
206
209
|
this.verifyReturnedSignature(signer, data, resp, feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
207
210
|
reputationPromise
|
|
208
211
|
]);
|
|
209
212
|
abortController.signal.throwIfAborted();
|
|
210
213
|
if (reputation != null)
|
|
211
214
|
lp.reputation[amountData.token.toString()] = reputation;
|
|
212
|
-
const inputWithoutFees = data.getAmount() - resp.swapFee - resp.networkFee;
|
|
213
|
-
const swapFeeBtc = resp.swapFee * resp.amount / inputWithoutFees;
|
|
214
|
-
const networkFeeBtc = resp.networkFee * resp.amount / inputWithoutFees;
|
|
215
215
|
const quote = new ToBTCSwap_1.ToBTCSwap(this, {
|
|
216
216
|
pricingInfo,
|
|
217
217
|
url: lp.url,
|
|
@@ -255,7 +255,7 @@ class SpvFromBTCSwap extends ISwap_1.ISwap {
|
|
|
255
255
|
if (this.pricingInfo == null)
|
|
256
256
|
return;
|
|
257
257
|
const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
258
|
-
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken);
|
|
258
|
+
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken, undefined, undefined, this.swapFeeBtc);
|
|
259
259
|
this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
|
|
260
260
|
}
|
|
261
261
|
//////////////////////////////
|
|
@@ -46,6 +46,13 @@ export type SpvFromBTCOptions = {
|
|
|
46
46
|
* the settlement gas fee cost)
|
|
47
47
|
*/
|
|
48
48
|
feeSafetyFactor?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Instruct the LP to create a "sticky address" for your destination wallet address. After the first successful
|
|
51
|
+
* swap with that LP, the used bitcoin address will be permanently linked to your destination wallet address. So
|
|
52
|
+
* all subsequent swaps to the same address will yield the same LP deposit bitcoin address. Useful for corporate
|
|
53
|
+
* whitelist-only wallets
|
|
54
|
+
*/
|
|
55
|
+
stickyAddress?: boolean;
|
|
49
56
|
/**
|
|
50
57
|
* @deprecated Use `maxAllowedBitcoinFeeRate` instead!
|
|
51
58
|
*/
|
|
@@ -466,7 +466,10 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
|
|
|
466
466
|
feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
|
|
467
467
|
maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
|
|
468
468
|
};
|
|
469
|
-
if (
|
|
469
|
+
if (_options.gasAmount !== 0n &&
|
|
470
|
+
(this._chain.shouldGetNativeTokenDrop != null
|
|
471
|
+
? !this._chain.shouldGetNativeTokenDrop(amountData.token)
|
|
472
|
+
: amountData.token === this._chain.getNativeCurrencyAddress()))
|
|
470
473
|
throw new UserError_1.UserError("Cannot specify `gasAmount` for swaps to a native token!");
|
|
471
474
|
const lpVersions = Intermediary_1.Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
|
|
472
475
|
const _abortController = (0, Utils_1.extendAbortController)(abortSignal);
|
|
@@ -505,15 +508,16 @@ class SpvFromBTCWrapper extends ISwapWrapper_1.ISwapWrapper {
|
|
|
505
508
|
gasAmount: _options.gasAmount,
|
|
506
509
|
callerFeeRate: (0, Utils_1.throwIfUndefined)(callerFeePrefetchPromise[version], "Caller fee prefetch failed!"),
|
|
507
510
|
frontingFeeRate: 0n,
|
|
511
|
+
stickyAddress: options?.stickyAddress,
|
|
508
512
|
additionalParams
|
|
509
513
|
}, this._options.postRequestTimeout, abortController.signal, retryCount > 0 ? false : undefined);
|
|
510
514
|
}, undefined, e => e instanceof RequestError_1.RequestError, abortController.signal);
|
|
511
515
|
this.logger.debug("create(" + lp.url + "): LP response: ", resp);
|
|
512
516
|
const callerFeeShare = (await callerFeePrefetchPromise[version]);
|
|
513
517
|
const [pricingInfo, gasPricingInfo, { vault, vaultUtxoValue }] = await Promise.all([
|
|
514
|
-
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], false, resp.btcAmountSwap, resp.total * (100000n + callerFeeShare) / 100000n, amountData.token, {}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
518
|
+
this.verifyReturnedPrice(lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], false, resp.btcAmountSwap, resp.total * (100000n + callerFeeShare) / 100000n, amountData.token, { swapFeeBtc: resp.swapFeeBtc }, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
515
519
|
_options.gasAmount === 0n ? Promise.resolve(undefined) : this.verifyReturnedPrice({ ...lp.services[SwapType_1.SwapType.SPV_VAULT_FROM_BTC], swapBaseFee: 0 }, //Base fee should be charged only on the amount, not on gas
|
|
516
|
-
false, resp.btcAmountGas, resp.totalGas * (100000n + callerFeeShare) / 100000n, nativeTokenAddress, {}, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
520
|
+
false, resp.btcAmountGas, resp.totalGas * (100000n + callerFeeShare) / 100000n, nativeTokenAddress, { swapFeeBtc: resp.gasSwapFeeBtc }, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal),
|
|
517
521
|
this.verifyReturnedData(resp, amountData, lp, _options, callerFeeShare, bitcoinFeeRatePromise, abortController.signal)
|
|
518
522
|
]);
|
|
519
523
|
const swapInit = {
|
|
@@ -62,7 +62,7 @@ class LnForGasWrapper extends ISwapWrapper_1.ISwapWrapper {
|
|
|
62
62
|
throw new IntermediaryError_1.IntermediaryError("Invalid total returned");
|
|
63
63
|
const pricingInfo = await this.verifyReturnedPrice(typeof (lpOrUrl) === "string" || lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTCLN] == null ?
|
|
64
64
|
{ swapFeePPM: 10000, swapBaseFee: 10 } :
|
|
65
|
-
lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTCLN], false, amountIn, amount, token, {});
|
|
65
|
+
lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTCLN], false, amountIn, amount, token, { swapFeeBtc: resp.swapFeeSats });
|
|
66
66
|
const quoteInit = {
|
|
67
67
|
pr: resp.pr,
|
|
68
68
|
outputAmount: resp.total,
|
|
@@ -69,7 +69,7 @@ class OnchainForGasWrapper extends ISwapWrapper_1.ISwapWrapper {
|
|
|
69
69
|
throw new IntermediaryError_1.IntermediaryError("Invalid total returned");
|
|
70
70
|
const pricingInfo = await this.verifyReturnedPrice(typeof (lpOrUrl) === "string" || lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTC] == null ?
|
|
71
71
|
{ swapFeePPM: 10000, swapBaseFee: 10 } :
|
|
72
|
-
lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTC], false, resp.amountSats, amount, this._chain.getNativeCurrencyAddress(), {});
|
|
72
|
+
lpOrUrl.services[SwapType_1.SwapType.TRUSTED_FROM_BTC], false, resp.amountSats, amount, this._chain.getNativeCurrencyAddress(), { swapFeeBtc: resp.swapFeeSats });
|
|
73
73
|
const quote = new OnchainForGasSwap_1.OnchainForGasSwap(this, {
|
|
74
74
|
paymentHash: resp.paymentHash,
|
|
75
75
|
sequence: resp.sequence,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/sdk",
|
|
3
|
-
"version": "8.7.
|
|
3
|
+
"version": "8.7.4",
|
|
4
4
|
"description": "atomiq labs SDK for cross-chain swaps between smart chains and bitcoin",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types:": "./dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "adambor",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@atomiqlabs/base": "^13.5.
|
|
26
|
+
"@atomiqlabs/base": "^13.5.2",
|
|
27
27
|
"@atomiqlabs/bolt11": "1.6.1",
|
|
28
28
|
"@atomiqlabs/btc-mempool": "^1.0.4",
|
|
29
29
|
"@atomiqlabs/messenger-nostr": "^2.0.0",
|
|
@@ -3,12 +3,14 @@ import {BTC_NETWORK, NETWORK, pubECDSA, randomPrivateKeyBytes, TEST_NETWORK} fro
|
|
|
3
3
|
import {getAddress, Transaction, WIF} from "@scure/btc-signer";
|
|
4
4
|
import {Buffer} from "buffer";
|
|
5
5
|
import {identifyAddressType, BitcoinWallet} from "./BitcoinWallet";
|
|
6
|
-
import {BitcoinNetwork, BitcoinRpcWithAddressIndex} from "@atomiqlabs/base";
|
|
6
|
+
import {BitcoinNetwork, BitcoinRpcWithAddressIndex, getLogger} from "@atomiqlabs/base";
|
|
7
7
|
import {HDKey} from "@scure/bip32";
|
|
8
8
|
import {entropyToMnemonic, generateMnemonic, mnemonicToSeed} from "@scure/bip39";
|
|
9
9
|
import {wordlist} from "@scure/bip39/wordlists/english.js";
|
|
10
10
|
import {sha256} from "@noble/hashes/sha2";
|
|
11
11
|
|
|
12
|
+
const logger = getLogger("SingleAddressBitcoinWallet: ");
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Bitcoin wallet implementation deriving a single address from a WIF encoded private key
|
|
14
16
|
*
|
|
@@ -42,11 +44,21 @@ export class SingleAddressBitcoinWallet extends BitcoinWallet {
|
|
|
42
44
|
const address = getAddress("wpkh", this.privKey, network);
|
|
43
45
|
if(address==null) throw new Error("Failed to generate p2wpkh address from the provided private key!");
|
|
44
46
|
this.address = address;
|
|
47
|
+
this.addressType = identifyAddressType(this.address, network);
|
|
45
48
|
} else {
|
|
46
49
|
this.address = addressDataOrWIF.address;
|
|
50
|
+
this.addressType = identifyAddressType(this.address, network);
|
|
47
51
|
this.pubkey = Buffer.from(addressDataOrWIF.publicKey, "hex");
|
|
52
|
+
// Some wallets seem to be returning a full 33-byte compressed pubkey instead of a taproot
|
|
53
|
+
// 32-byte long X-only key. Handle these cases here
|
|
54
|
+
if(this.addressType==="p2tr") {
|
|
55
|
+
if(this.pubkey.length!==33) return;
|
|
56
|
+
const leadingByte = this.pubkey[0];
|
|
57
|
+
if(leadingByte!==0x03 && leadingByte!==0x02) throw new Error("Invalid public key passed for taproot bitcoin wallet, expected an X-only 32-byte public key, or a compressed 33-byte public key");
|
|
58
|
+
logger.debug(`constructor(): Converting compressed public key ${addressDataOrWIF.publicKey} to taproot X-only 32-byte public key`);
|
|
59
|
+
this.pubkey = this.pubkey.slice(1);
|
|
60
|
+
}
|
|
48
61
|
}
|
|
49
|
-
this.addressType = identifyAddressType(this.address, network);
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
/**
|
|
@@ -310,7 +310,8 @@ export type SpvFromBTCPrepare = SwapInit & {
|
|
|
310
310
|
gasToken: string,
|
|
311
311
|
exactOut: boolean,
|
|
312
312
|
callerFeeRate: Promise<bigint>,
|
|
313
|
-
frontingFeeRate: bigint
|
|
313
|
+
frontingFeeRate: bigint,
|
|
314
|
+
stickyAddress?: boolean
|
|
314
315
|
}
|
|
315
316
|
|
|
316
317
|
const SpvFromBTCInitResponseSchema = {
|
|
@@ -878,7 +879,8 @@ export class IntermediaryAPI {
|
|
|
878
879
|
gasAmount: init.gasAmount.toString(10),
|
|
879
880
|
gasToken: init.gasToken,
|
|
880
881
|
frontingFeeRate: init.frontingFeeRate.toString(10),
|
|
881
|
-
callerFeeRate: init.callerFeeRate.then(val => val.toString(10))
|
|
882
|
+
callerFeeRate: init.callerFeeRate.then(val => val.toString(10)),
|
|
883
|
+
stickyAddress: init.stickyAddress
|
|
882
884
|
}, {
|
|
883
885
|
code: FieldTypeEnum.Number,
|
|
884
886
|
msg: FieldTypeEnum.String,
|
|
@@ -100,6 +100,7 @@ export abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
100
100
|
* @param tokenAddress Token address to be paid
|
|
101
101
|
* @param abortSignal
|
|
102
102
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
103
|
+
* @param realSwapFeeSats
|
|
103
104
|
*/
|
|
104
105
|
public async isValidAmountSend<C extends ChainIds<T>>(
|
|
105
106
|
chainIdentifier: C,
|
|
@@ -109,11 +110,15 @@ export abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
109
110
|
paidToken: bigint,
|
|
110
111
|
tokenAddress: string,
|
|
111
112
|
abortSignal?: AbortSignal,
|
|
112
|
-
preFetchedPrice?: bigint | null
|
|
113
|
+
preFetchedPrice?: bigint | null,
|
|
114
|
+
realSwapFeeSats?: bigint
|
|
113
115
|
): Promise<PriceInfoType> {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
if(realSwapFeeSats!=undefined && realSwapFeeSats<0) throw new Error("Invalid swap fee! Swap fee cannot be negative!");
|
|
117
|
+
const totalSats = realSwapFeeSats!=undefined
|
|
118
|
+
? amountSats + realSwapFeeSats
|
|
119
|
+
: (amountSats * (1000000n + feePPM) / 1000000n) + satsBaseFee;
|
|
116
120
|
const totalUSats = totalSats * 1000000n;
|
|
121
|
+
|
|
117
122
|
const swapPriceUSatPerToken = totalUSats * (10n ** BigInt(this.getDecimalsThrowing(chainIdentifier, tokenAddress))) / paidToken;
|
|
118
123
|
|
|
119
124
|
if(this.shouldIgnore(chainIdentifier, tokenAddress)) return {
|
|
@@ -185,6 +190,7 @@ export abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
185
190
|
* @param tokenAddress Token address to be received
|
|
186
191
|
* @param abortSignal
|
|
187
192
|
* @param preFetchedPrice An optional price pre-fetched with {@link preFetchPrice}
|
|
193
|
+
* @param realSwapFeeSats
|
|
188
194
|
*/
|
|
189
195
|
public async isValidAmountReceive<C extends ChainIds<T>>(
|
|
190
196
|
chainIdentifier: C,
|
|
@@ -194,11 +200,18 @@ export abstract class ISwapPrice<T extends MultiChain = MultiChain> {
|
|
|
194
200
|
receiveToken: bigint,
|
|
195
201
|
tokenAddress: string,
|
|
196
202
|
abortSignal?: AbortSignal,
|
|
197
|
-
preFetchedPrice?: bigint | null
|
|
203
|
+
preFetchedPrice?: bigint | null,
|
|
204
|
+
realSwapFeeSats?: bigint
|
|
198
205
|
): Promise<PriceInfoType> {
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
if(realSwapFeeSats!=undefined) {
|
|
207
|
+
if(realSwapFeeSats>=amountSats) throw new Error("Invalid swap fee! Larger than or equal to total output amount!");
|
|
208
|
+
if(realSwapFeeSats<0) throw new Error("Invalid swap fee! Must be non-negative!");
|
|
209
|
+
}
|
|
210
|
+
const totalSats = realSwapFeeSats!=undefined
|
|
211
|
+
? amountSats - realSwapFeeSats
|
|
212
|
+
: (amountSats * (1000000n - feePPM) / 1000000n) - satsBaseFee;
|
|
201
213
|
const totalUSats = totalSats * 1000000n;
|
|
214
|
+
|
|
202
215
|
const swapPriceUSatPerToken = totalUSats * (10n ** BigInt(this.getDecimalsThrowing(chainIdentifier, tokenAddress))) / receiveToken;
|
|
203
216
|
|
|
204
217
|
if(this.shouldIgnore(chainIdentifier, tokenAddress)) return {
|
|
@@ -454,6 +454,18 @@ export class SwapperUtils<T extends MultiChain> {
|
|
|
454
454
|
return this.root._tokens[chainIdentifier][this.root._chains[chainIdentifier].chainInterface.getNativeCurrencyAddress()] as SCToken<ChainIdentifier>;
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Returns whether when swapping to the provided token a gas drop can be requested
|
|
459
|
+
*
|
|
460
|
+
* @param token
|
|
461
|
+
*/
|
|
462
|
+
destinationTokenSupportsGasDrop<ChainIdentifier extends ChainIds<T>>(token: SCToken<ChainIdentifier>): boolean {
|
|
463
|
+
if(this.root._chains[token.chainId]==null) throw new Error("Invalid chain identifier! Unknown chain: "+token.chainId);
|
|
464
|
+
const {chainInterface} = this.root._chains[token.chainId];
|
|
465
|
+
if(chainInterface.shouldGetNativeTokenDrop!=null) return chainInterface.shouldGetNativeTokenDrop(token.address);
|
|
466
|
+
return chainInterface.getNativeCurrencyAddress() !== token.address;
|
|
467
|
+
}
|
|
468
|
+
|
|
457
469
|
/**
|
|
458
470
|
* Returns a random signer for a given smart chain
|
|
459
471
|
*
|
package/src/swaps/ISwap.ts
CHANGED
|
@@ -322,28 +322,39 @@ export abstract class ISwap<
|
|
|
322
322
|
public async refreshPriceData(): Promise<void> {
|
|
323
323
|
if(this.pricingInfo==null) return;
|
|
324
324
|
const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
325
|
-
const input = this.getInput();
|
|
326
325
|
const output = this.getOutput();
|
|
327
|
-
if(
|
|
326
|
+
if(output.isUnknown) return;
|
|
327
|
+
|
|
328
|
+
if(isSCToken(this.getInputToken()) && this.getDirection()===SwapDirection.TO_BTC) {
|
|
329
|
+
const input = this.getInputWithoutFee();
|
|
330
|
+
if(input.isUnknown) return;
|
|
328
331
|
|
|
329
|
-
if(isSCToken(input.token) && this.getDirection()===SwapDirection.TO_BTC) {
|
|
330
332
|
this.pricingInfo = await this.wrapper._prices.isValidAmountSend(
|
|
331
333
|
this.chainIdentifier,
|
|
332
334
|
output.rawAmount!,
|
|
333
335
|
this.pricingInfo.satsBaseFee,
|
|
334
336
|
this.pricingInfo.feePPM,
|
|
335
|
-
input.rawAmount
|
|
336
|
-
input.token.address
|
|
337
|
+
input.rawAmount! + this.swapFee,
|
|
338
|
+
input.token.address,
|
|
339
|
+
undefined,
|
|
340
|
+
undefined,
|
|
341
|
+
this.swapFeeBtc
|
|
337
342
|
);
|
|
338
343
|
this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
|
|
339
344
|
} else if(isSCToken(output.token) && this.getDirection()===SwapDirection.FROM_BTC) {
|
|
345
|
+
const input = this.getInput();
|
|
346
|
+
if(input.isUnknown) return;
|
|
347
|
+
|
|
340
348
|
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(
|
|
341
349
|
this.chainIdentifier,
|
|
342
350
|
input.rawAmount!,
|
|
343
351
|
this.pricingInfo.satsBaseFee,
|
|
344
352
|
this.pricingInfo.feePPM,
|
|
345
353
|
output.rawAmount!,
|
|
346
|
-
output.token.address
|
|
354
|
+
output.token.address,
|
|
355
|
+
undefined,
|
|
356
|
+
undefined,
|
|
357
|
+
this.swapFeeBtc
|
|
347
358
|
);
|
|
348
359
|
this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
|
|
349
360
|
}
|
|
@@ -259,7 +259,8 @@ export abstract class ISwapWrapper<
|
|
|
259
259
|
amountToken: bigint,
|
|
260
260
|
token: string,
|
|
261
261
|
feeData: {
|
|
262
|
-
networkFee?: bigint
|
|
262
|
+
networkFee?: bigint,
|
|
263
|
+
swapFeeBtc?: bigint
|
|
263
264
|
},
|
|
264
265
|
pricePrefetchPromise: Promise<bigint | undefined> = Promise.resolve(undefined),
|
|
265
266
|
usdPricePrefetchPromise: Promise<number | undefined> = Promise.resolve(undefined),
|
|
@@ -271,8 +272,8 @@ export abstract class ISwapWrapper<
|
|
|
271
272
|
|
|
272
273
|
const [isValidAmount, usdPrice] = await Promise.all([
|
|
273
274
|
send ?
|
|
274
|
-
this._prices.isValidAmountSend(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise) :
|
|
275
|
-
this._prices.isValidAmountReceive(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise),
|
|
275
|
+
this._prices.isValidAmountSend(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise, feeData.swapFeeBtc) :
|
|
276
|
+
this._prices.isValidAmountReceive(this.chainIdentifier, amountSats, swapBaseFee, swapFeePPM, amountToken, token, abortSignal, await pricePrefetchPromise, feeData.swapFeeBtc),
|
|
276
277
|
usdPricePrefetchPromise.then(value => {
|
|
277
278
|
if(value!=null) return value;
|
|
278
279
|
return this._prices.preFetchUsdPrice(abortSignal);
|
|
@@ -342,12 +342,14 @@ export class FromBTCLNWrapper<
|
|
|
342
342
|
if(decodedPr.timeExpireDate==null) throw new IntermediaryError("Invalid returned swap invoice, no expiry date field");
|
|
343
343
|
const amountIn = (BigInt(decodedPr.millisatoshis) + 999n) / 1000n;
|
|
344
344
|
|
|
345
|
+
const swapFeeBtc = resp.swapFee * amountIn / (resp.total - resp.swapFee);
|
|
346
|
+
|
|
345
347
|
try {
|
|
346
348
|
this.verifyReturnedData(resp, amountData, lp, _options, decodedPr, paymentHash);
|
|
347
349
|
const [pricingInfo] = await Promise.all([
|
|
348
350
|
this.verifyReturnedPrice(
|
|
349
351
|
lp.services[SwapType.FROM_BTCLN], false, amountIn, resp.total,
|
|
350
|
-
amountData.token, {}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
352
|
+
amountData.token, {swapFeeBtc}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
351
353
|
),
|
|
352
354
|
this.verifyIntermediaryLiquidity(resp.total, throwIfUndefined(liquidityPromise)),
|
|
353
355
|
lnCapacityPromise!=null ? this.verifyLnNodeCapacity(lp, decodedPr, lnCapacityPromise, abortController.signal) : Promise.resolve()
|
|
@@ -358,7 +360,7 @@ export class FromBTCLNWrapper<
|
|
|
358
360
|
url: lp.url,
|
|
359
361
|
expiry: decodedPr.timeExpireDate*1000,
|
|
360
362
|
swapFee: resp.swapFee,
|
|
361
|
-
swapFeeBtc
|
|
363
|
+
swapFeeBtc,
|
|
362
364
|
feeRate: (await _preFetches.feeRatePromise[version])!,
|
|
363
365
|
initialSwapData: await this._contract(version).createSwapData(
|
|
364
366
|
ChainSwapType.HTLC, lp.getAddress(this.chainIdentifier), recipient, amountData.token,
|
|
@@ -309,7 +309,10 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
309
309
|
this.pricingInfo.satsBaseFee,
|
|
310
310
|
this.pricingInfo.feePPM,
|
|
311
311
|
this.getOutputAmountWithoutFee(),
|
|
312
|
-
this.getSwapData().getToken()
|
|
312
|
+
this.getSwapData().getToken(),
|
|
313
|
+
undefined,
|
|
314
|
+
undefined,
|
|
315
|
+
this.swapFeeBtc
|
|
313
316
|
);
|
|
314
317
|
this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
|
|
315
318
|
}
|
|
@@ -382,8 +382,15 @@ export class FromBTCLNAutoWrapper<
|
|
|
382
382
|
descriptionHash: parseHashValueExact32Bytes(options?.descriptionHash, "description hash")
|
|
383
383
|
};
|
|
384
384
|
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
|
|
386
|
+
if(
|
|
387
|
+
_options.gasAmount!==0n &&
|
|
388
|
+
(
|
|
389
|
+
this._chain.shouldGetNativeTokenDrop!=null
|
|
390
|
+
? !this._chain.shouldGetNativeTokenDrop(amountData.token)
|
|
391
|
+
: amountData.token===this._chain.getNativeCurrencyAddress()
|
|
392
|
+
)
|
|
393
|
+
) throw new UserError("Cannot specify `gasAmount` for swaps to a native token!");
|
|
387
394
|
|
|
388
395
|
if(_options.description!=null && Buffer.byteLength(_options.description, "utf8") > 500)
|
|
389
396
|
throw new UserError("Invalid description length");
|
|
@@ -468,13 +475,13 @@ export class FromBTCLNAutoWrapper<
|
|
|
468
475
|
lp.services[SwapType.FROM_BTCLN_AUTO],
|
|
469
476
|
false, resp.btcAmountSwap,
|
|
470
477
|
resp.total,
|
|
471
|
-
amountData.token, {}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
478
|
+
amountData.token, {swapFeeBtc: resp.swapFeeBtc}, _preFetches.pricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
472
479
|
),
|
|
473
480
|
_options.gasAmount===0n ? Promise.resolve(undefined) : this.verifyReturnedPrice(
|
|
474
481
|
{...lp.services[SwapType.FROM_BTCLN_AUTO], swapBaseFee: 0}, //Base fee should be charged only on the amount, not on gas
|
|
475
482
|
false, resp.btcAmountGas,
|
|
476
483
|
resp.totalGas + resp.claimerBounty,
|
|
477
|
-
nativeTokenAddress, {}, _preFetches.gasTokenPricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
484
|
+
nativeTokenAddress, {swapFeeBtc: resp.gasSwapFeeBtc}, _preFetches.gasTokenPricePrefetchPromise, _preFetches.usdPricePrefetchPromise, abortController.signal
|
|
478
485
|
),
|
|
479
486
|
this.verifyIntermediaryLiquidity(resp.total, throwIfUndefined(liquidityPromise)),
|
|
480
487
|
_options.unsafeSkipLnNodeCheck ? Promise.resolve() : this.verifyLnNodeCapacity(lp, decodedPr, lnCapacityPromise, abortController.signal)
|
|
@@ -528,12 +528,14 @@ export class FromBTCWrapper<
|
|
|
528
528
|
const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
|
|
529
529
|
data.setClaimer(recipient);
|
|
530
530
|
|
|
531
|
+
const swapFeeBtc = resp.swapFee * resp.amount / (data.getAmount() - resp.swapFee);
|
|
532
|
+
|
|
531
533
|
this.verifyReturnedData(recipient, resp, amountData, lp, _options, data, sequence, (await claimerBountyPrefetchPromise[version])!, nativeTokenAddress);
|
|
532
534
|
const [pricingInfo, signatureExpiry] = await Promise.all([
|
|
533
535
|
//Get intermediary's liquidity
|
|
534
536
|
this.verifyReturnedPrice(
|
|
535
537
|
lp.services[SwapType.FROM_BTC], false, resp.amount, resp.total,
|
|
536
|
-
amountData.token, {}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
538
|
+
amountData.token, {swapFeeBtc}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
537
539
|
),
|
|
538
540
|
this.verifyReturnedSignature(recipient, data, resp, feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
539
541
|
this.verifyIntermediaryLiquidity(data.getAmount(), throwIfUndefined(liquidityPromise)),
|
|
@@ -544,7 +546,7 @@ export class FromBTCWrapper<
|
|
|
544
546
|
url: lp.url,
|
|
545
547
|
expiry: signatureExpiry,
|
|
546
548
|
swapFee: resp.swapFee,
|
|
547
|
-
swapFeeBtc
|
|
549
|
+
swapFeeBtc,
|
|
548
550
|
feeRate: (await feeRatePromise[version])!,
|
|
549
551
|
signatureData: resp,
|
|
550
552
|
data,
|
|
@@ -315,10 +315,12 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
|
|
|
315
315
|
|
|
316
316
|
await this.verifyReturnedData(signer, resp, parsedPr, amountData.token, lp, calculatedOptions, data);
|
|
317
317
|
|
|
318
|
+
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
319
|
+
|
|
318
320
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
319
321
|
this.verifyReturnedPrice(
|
|
320
322
|
lp.services[SwapType.TO_BTCLN], true, amountOut, data.getAmount(),
|
|
321
|
-
amountData.token, {networkFee: resp.maxFee},
|
|
323
|
+
amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
|
|
322
324
|
preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortController.signal
|
|
323
325
|
),
|
|
324
326
|
this.verifyReturnedSignature(
|
|
@@ -330,8 +332,6 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
|
|
|
330
332
|
|
|
331
333
|
if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
|
|
332
334
|
|
|
333
|
-
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
334
|
-
|
|
335
335
|
const quote = new ToBTCLNSwap<T>(this, {
|
|
336
336
|
pricingInfo,
|
|
337
337
|
url: lp.url,
|
|
@@ -528,10 +528,12 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
|
|
|
528
528
|
|
|
529
529
|
await this.verifyReturnedData(signer, resp, parsedInvoice, amountData.token, lp, calculatedOptions, data, amountData.amount);
|
|
530
530
|
|
|
531
|
+
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
532
|
+
|
|
531
533
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
532
534
|
this.verifyReturnedPrice(
|
|
533
535
|
lp.services[SwapType.TO_BTCLN], true, prepareResp.amount, data.getAmount(),
|
|
534
|
-
amountData.token, {networkFee: resp.maxFee},
|
|
536
|
+
amountData.token, {networkFee: resp.maxFee, swapFeeBtc},
|
|
535
537
|
preFetches.pricePreFetchPromise, preFetches.usdPricePrefetchPromise, abortSignal
|
|
536
538
|
),
|
|
537
539
|
this.verifyReturnedSignature(
|
|
@@ -543,8 +545,6 @@ export class ToBTCLNWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCL
|
|
|
543
545
|
|
|
544
546
|
if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
|
|
545
547
|
|
|
546
|
-
const swapFeeBtc = resp.swapFee * amountOut / (data.getAmount() - totalFee);
|
|
547
|
-
|
|
548
548
|
const quote = new ToBTCLNSwap<T>(this, {
|
|
549
549
|
pricingInfo,
|
|
550
550
|
url: lp.url,
|
|
@@ -304,11 +304,15 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
|
|
|
304
304
|
const data: T["Data"] = new (this._swapDataDeserializer(version))(resp.data);
|
|
305
305
|
data.setOfferer(signer);
|
|
306
306
|
|
|
307
|
+
const inputWithoutFees = data.getAmount() - resp.swapFee - resp.networkFee;
|
|
308
|
+
const swapFeeBtc = resp.swapFee * resp.amount / inputWithoutFees;
|
|
309
|
+
const networkFeeBtc = resp.networkFee * resp.amount / inputWithoutFees;
|
|
310
|
+
|
|
307
311
|
this.verifyReturnedData(signer, resp, amountData, lp, _options, data, hash);
|
|
308
312
|
const [pricingInfo, signatureExpiry, reputation] = await Promise.all([
|
|
309
313
|
this.verifyReturnedPrice(
|
|
310
314
|
lp.services[SwapType.TO_BTC], true, resp.amount, data.getAmount(),
|
|
311
|
-
amountData.token, resp, pricePreFetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
315
|
+
amountData.token, {networkFee: resp.networkFee, swapFeeBtc}, pricePreFetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
312
316
|
),
|
|
313
317
|
this.verifyReturnedSignature(signer, data, resp, feeRatePromise[version], signDataPromise, version, abortController.signal),
|
|
314
318
|
reputationPromise
|
|
@@ -317,10 +321,6 @@ export class ToBTCWrapper<T extends ChainType> extends IToBTCWrapper<T, ToBTCDef
|
|
|
317
321
|
|
|
318
322
|
if(reputation!=null) lp.reputation[amountData.token.toString()] = reputation;
|
|
319
323
|
|
|
320
|
-
const inputWithoutFees = data.getAmount() - resp.swapFee - resp.networkFee;
|
|
321
|
-
const swapFeeBtc = resp.swapFee * resp.amount / inputWithoutFees;
|
|
322
|
-
const networkFeeBtc = resp.networkFee * resp.amount / inputWithoutFees
|
|
323
|
-
|
|
324
324
|
const quote = new ToBTCSwap<T>(this, {
|
|
325
325
|
pricingInfo,
|
|
326
326
|
url: lp.url,
|
|
@@ -402,7 +402,10 @@ export class SpvFromBTCSwap<T extends ChainType>
|
|
|
402
402
|
this.pricingInfo.satsBaseFee,
|
|
403
403
|
this.pricingInfo.feePPM,
|
|
404
404
|
this.getOutputWithoutFee().rawAmount,
|
|
405
|
-
this.outputSwapToken
|
|
405
|
+
this.outputSwapToken,
|
|
406
|
+
undefined,
|
|
407
|
+
undefined,
|
|
408
|
+
this.swapFeeBtc
|
|
406
409
|
);
|
|
407
410
|
this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
|
|
408
411
|
}
|
|
@@ -68,6 +68,13 @@ export type SpvFromBTCOptions = {
|
|
|
68
68
|
* the settlement gas fee cost)
|
|
69
69
|
*/
|
|
70
70
|
feeSafetyFactor?: number,
|
|
71
|
+
/**
|
|
72
|
+
* Instruct the LP to create a "sticky address" for your destination wallet address. After the first successful
|
|
73
|
+
* swap with that LP, the used bitcoin address will be permanently linked to your destination wallet address. So
|
|
74
|
+
* all subsequent swaps to the same address will yield the same LP deposit bitcoin address. Useful for corporate
|
|
75
|
+
* whitelist-only wallets
|
|
76
|
+
*/
|
|
77
|
+
stickyAddress?: boolean,
|
|
71
78
|
|
|
72
79
|
/**
|
|
73
80
|
* @deprecated Use `maxAllowedBitcoinFeeRate` instead!
|
|
@@ -636,8 +643,14 @@ export class SpvFromBTCWrapper<
|
|
|
636
643
|
maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
|
|
637
644
|
};
|
|
638
645
|
|
|
639
|
-
if(
|
|
640
|
-
|
|
646
|
+
if(
|
|
647
|
+
_options.gasAmount!==0n &&
|
|
648
|
+
(
|
|
649
|
+
this._chain.shouldGetNativeTokenDrop!=null
|
|
650
|
+
? !this._chain.shouldGetNativeTokenDrop(amountData.token)
|
|
651
|
+
: amountData.token===this._chain.getNativeCurrencyAddress()
|
|
652
|
+
)
|
|
653
|
+
) throw new UserError("Cannot specify `gasAmount` for swaps to a native token!");
|
|
641
654
|
|
|
642
655
|
const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
|
|
643
656
|
|
|
@@ -681,6 +694,7 @@ export class SpvFromBTCWrapper<
|
|
|
681
694
|
gasAmount: _options.gasAmount,
|
|
682
695
|
callerFeeRate: throwIfUndefined(callerFeePrefetchPromise[version], "Caller fee prefetch failed!"),
|
|
683
696
|
frontingFeeRate: 0n,
|
|
697
|
+
stickyAddress: options?.stickyAddress,
|
|
684
698
|
additionalParams
|
|
685
699
|
},
|
|
686
700
|
this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
|
|
@@ -700,13 +714,13 @@ export class SpvFromBTCWrapper<
|
|
|
700
714
|
lp.services[SwapType.SPV_VAULT_FROM_BTC],
|
|
701
715
|
false, resp.btcAmountSwap,
|
|
702
716
|
resp.total * (100_000n + callerFeeShare) / 100_000n,
|
|
703
|
-
amountData.token, {}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
717
|
+
amountData.token, {swapFeeBtc: resp.swapFeeBtc}, pricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
704
718
|
),
|
|
705
719
|
_options.gasAmount===0n ? Promise.resolve(undefined) : this.verifyReturnedPrice(
|
|
706
720
|
{...lp.services[SwapType.SPV_VAULT_FROM_BTC], swapBaseFee: 0}, //Base fee should be charged only on the amount, not on gas
|
|
707
721
|
false, resp.btcAmountGas,
|
|
708
722
|
resp.totalGas * (100_000n + callerFeeShare) / 100_000n,
|
|
709
|
-
nativeTokenAddress, {}, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
723
|
+
nativeTokenAddress, {swapFeeBtc: resp.gasSwapFeeBtc}, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
710
724
|
),
|
|
711
725
|
this.verifyReturnedData(resp, amountData, lp, _options, callerFeeShare, bitcoinFeeRatePromise, abortController.signal)
|
|
712
726
|
]);
|
|
@@ -68,7 +68,7 @@ export class LnForGasWrapper<T extends ChainType> extends ISwapWrapper<T, LnForG
|
|
|
68
68
|
{swapFeePPM: 10000, swapBaseFee: 10} :
|
|
69
69
|
lpOrUrl.services[SwapType.TRUSTED_FROM_BTCLN],
|
|
70
70
|
false, amountIn,
|
|
71
|
-
amount, token, {}
|
|
71
|
+
amount, token, {swapFeeBtc: resp.swapFeeSats}
|
|
72
72
|
);
|
|
73
73
|
|
|
74
74
|
const quoteInit: LnForGasSwapInit = {
|
|
@@ -105,7 +105,7 @@ export class OnchainForGasWrapper<T extends ChainType> extends ISwapWrapper<T, O
|
|
|
105
105
|
{swapFeePPM: 10000, swapBaseFee: 10} :
|
|
106
106
|
lpOrUrl.services[SwapType.TRUSTED_FROM_BTC],
|
|
107
107
|
false, resp.amountSats,
|
|
108
|
-
amount, this._chain.getNativeCurrencyAddress(), {}
|
|
108
|
+
amount, this._chain.getNativeCurrencyAddress(), {swapFeeBtc: resp.swapFeeSats}
|
|
109
109
|
);
|
|
110
110
|
|
|
111
111
|
const quote = new OnchainForGasSwap(this, {
|