@atomiqlabs/sdk 8.7.7 → 8.9.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/api/index.d.ts +1 -0
- package/api/index.js +3 -0
- package/dist/ApiList.d.ts +37 -0
- package/dist/ApiList.js +30 -0
- package/dist/api/ApiEndpoints.d.ts +393 -0
- package/dist/api/ApiEndpoints.js +2 -0
- package/dist/api/ApiParser.d.ts +10 -0
- package/dist/api/ApiParser.js +134 -0
- package/dist/api/ApiTypes.d.ts +157 -0
- package/dist/api/ApiTypes.js +75 -0
- package/dist/api/SerializedAction.d.ts +40 -0
- package/dist/api/SerializedAction.js +59 -0
- package/dist/api/SwapperApi.d.ts +50 -0
- package/dist/api/SwapperApi.js +431 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +24 -0
- package/dist/bitcoin/coinselect2/accumulative.d.ts +1 -0
- package/dist/bitcoin/coinselect2/accumulative.js +1 -1
- package/dist/bitcoin/coinselect2/blackjack.d.ts +1 -0
- package/dist/bitcoin/coinselect2/blackjack.js +1 -1
- package/dist/bitcoin/coinselect2/index.d.ts +3 -2
- package/dist/bitcoin/coinselect2/index.js +2 -2
- package/dist/bitcoin/coinselect2/utils.d.ts +7 -2
- package/dist/bitcoin/coinselect2/utils.js +45 -10
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +8 -25
- package/dist/bitcoin/wallet/BitcoinWallet.js +31 -18
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +40 -2
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +7 -2
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +10 -4
- package/dist/events/UnifiedSwapEventListener.d.ts +4 -3
- package/dist/events/UnifiedSwapEventListener.js +8 -2
- package/dist/http/HttpUtils.d.ts +4 -2
- package/dist/http/HttpUtils.js +10 -4
- package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
- package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
- package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +182 -15
- package/dist/intermediaries/apis/IntermediaryAPI.js +192 -31
- package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
- package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
- package/dist/storage/IUnifiedStorage.d.ts +45 -3
- package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
- package/dist/storage/UnifiedSwapStorage.js +46 -8
- package/dist/swapper/Swapper.d.ts +77 -4
- package/dist/swapper/Swapper.js +117 -25
- package/dist/swapper/SwapperUtils.d.ts +18 -2
- package/dist/swapper/SwapperUtils.js +39 -1
- package/dist/swaps/ISwap.d.ts +70 -9
- package/dist/swaps/ISwap.js +28 -6
- package/dist/swaps/ISwapWrapper.d.ts +11 -1
- package/dist/swaps/ISwapWrapper.js +23 -3
- package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
- package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +85 -22
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +299 -56
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +41 -7
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +183 -58
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
- package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
- package/dist/types/SwapExecutionAction.d.ts +141 -34
- package/dist/types/SwapExecutionAction.js +104 -0
- package/dist/types/SwapExecutionStep.d.ts +144 -0
- package/dist/types/SwapExecutionStep.js +87 -0
- package/dist/types/TokenAmount.d.ts +6 -0
- package/dist/types/TokenAmount.js +26 -1
- package/dist/utils/BitcoinUtils.d.ts +4 -0
- package/dist/utils/BitcoinUtils.js +73 -1
- package/dist/utils/BitcoinWalletUtils.d.ts +2 -2
- package/dist/utils/Utils.d.ts +3 -1
- package/dist/utils/Utils.js +7 -1
- package/package.json +7 -4
- package/src/api/ApiEndpoints.ts +427 -0
- package/src/api/ApiParser.ts +138 -0
- package/src/api/ApiTypes.ts +229 -0
- package/src/api/SerializedAction.ts +97 -0
- package/src/api/SwapperApi.ts +545 -0
- package/src/api/index.ts +5 -0
- package/src/bitcoin/coinselect2/accumulative.ts +2 -1
- package/src/bitcoin/coinselect2/blackjack.ts +2 -1
- package/src/bitcoin/coinselect2/index.ts +5 -4
- package/src/bitcoin/coinselect2/utils.ts +55 -14
- package/src/bitcoin/wallet/BitcoinWallet.ts +69 -57
- package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -3
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +12 -4
- package/src/events/UnifiedSwapEventListener.ts +11 -3
- package/src/http/HttpUtils.ts +10 -4
- package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
- package/src/index.ts +1 -0
- package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
- package/src/intermediaries/apis/IntermediaryAPI.ts +335 -35
- package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
- package/src/storage/IUnifiedStorage.ts +45 -4
- package/src/storage/UnifiedSwapStorage.ts +42 -8
- package/src/swapper/Swapper.ts +165 -24
- package/src/swapper/SwapperUtils.ts +42 -2
- package/src/swaps/ISwap.ts +88 -16
- package/src/swaps/ISwapWrapper.ts +28 -3
- package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
- package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +413 -64
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +239 -61
- package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
- package/src/types/SwapExecutionAction.ts +266 -43
- package/src/types/SwapExecutionStep.ts +224 -0
- package/src/types/TokenAmount.ts +36 -2
- package/src/utils/BitcoinUtils.ts +73 -0
- package/src/utils/BitcoinWalletUtils.ts +2 -2
- package/src/utils/Utils.ts +10 -1
- package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +0 -258
|
@@ -22,9 +22,14 @@ import {ISwapPrice} from "../../prices/abstract/ISwapPrice";
|
|
|
22
22
|
import {EventEmitter} from "events";
|
|
23
23
|
import {Intermediary} from "../../intermediaries/Intermediary";
|
|
24
24
|
import {extendAbortController, mapArrayToObject, randomBytes, throwIfUndefined} from "../../utils/Utils";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
fromOutputScript,
|
|
27
|
+
getDummyOutputScript,
|
|
28
|
+
toCoinselectAddressType,
|
|
29
|
+
toOutputScript
|
|
30
|
+
} from "../../utils/BitcoinUtils";
|
|
26
31
|
import {IntermediaryAPI, SpvFromBTCPrepareResponseType} from "../../intermediaries/apis/IntermediaryAPI";
|
|
27
|
-
import {RequestError} from "../../errors/RequestError";
|
|
32
|
+
import {OutOfBoundsError, RequestError} from "../../errors/RequestError";
|
|
28
33
|
import {IntermediaryError} from "../../errors/IntermediaryError";
|
|
29
34
|
import {CoinselectAddressTypes} from "../../bitcoin/coinselect2";
|
|
30
35
|
import {OutScript, Transaction} from "@scure/btc-signer";
|
|
@@ -33,8 +38,10 @@ import {IClaimableSwapWrapper} from "../IClaimableSwapWrapper";
|
|
|
33
38
|
import {AmountData} from "../../types/AmountData";
|
|
34
39
|
import {tryWithRetries} from "../../utils/RetryUtils";
|
|
35
40
|
import {AllOptional} from "../../utils/TypeUtils";
|
|
36
|
-
import {fromHumanReadableString} from "../../utils/TokenUtils";
|
|
37
41
|
import {UserError} from "../../errors/UserError";
|
|
42
|
+
import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "../../bitcoin/wallet/IBitcoinWallet";
|
|
43
|
+
import {utils} from "../../bitcoin/coinselect2/utils";
|
|
44
|
+
import {BitcoinWallet} from "../../bitcoin/wallet/BitcoinWallet";
|
|
38
45
|
|
|
39
46
|
export type SpvFromBTCOptions = {
|
|
40
47
|
/**
|
|
@@ -75,6 +82,16 @@ export type SpvFromBTCOptions = {
|
|
|
75
82
|
* whitelist-only wallets
|
|
76
83
|
*/
|
|
77
84
|
stickyAddress?: boolean,
|
|
85
|
+
/**
|
|
86
|
+
* A bitcoin wallet UTXOs to fully use as an input for this swap, use this option along with passing `amount` as
|
|
87
|
+
* `undefined` when you want to swap the full BTC balance of the wallet in a single swap
|
|
88
|
+
*/
|
|
89
|
+
sourceWalletUtxos?: BitcoinWalletUtxoBase[] | Promise<BitcoinWalletUtxoBase[]>,
|
|
90
|
+
/**
|
|
91
|
+
* Bitcoin fee rate to use when deriving `maxAllowedBitcoinFeeRate` and when calculating the input amount based
|
|
92
|
+
* on the `sourceWalletUtxos`
|
|
93
|
+
*/
|
|
94
|
+
bitcoinFeeRate?: Promise<number> | number,
|
|
78
95
|
|
|
79
96
|
/**
|
|
80
97
|
* @deprecated Use `maxAllowedBitcoinFeeRate` instead!
|
|
@@ -94,6 +111,9 @@ export type SpvFromBTCWrapperOptions = ISwapWrapperOptions & {
|
|
|
94
111
|
|
|
95
112
|
export type SpvFromBTCTypeDefinition<T extends ChainType> = SwapTypeDefinition<T, SpvFromBTCWrapper<T>, SpvFromBTCSwap<T>>;
|
|
96
113
|
|
|
114
|
+
export const REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE: CoinselectAddressTypes = "p2tr";
|
|
115
|
+
export const REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE: CoinselectAddressTypes = "p2wpkh";
|
|
116
|
+
|
|
97
117
|
/**
|
|
98
118
|
* New spv vault (UTXO-controlled vault) based swaps for Bitcoin -> Smart chain swaps not requiring
|
|
99
119
|
* any initiation on the destination chain, and with the added possibility for the user to receive
|
|
@@ -205,6 +225,7 @@ export class SpvFromBTCWrapper<
|
|
|
205
225
|
* @param versionedContracts
|
|
206
226
|
* @param versionedSynchronizer
|
|
207
227
|
* @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
|
|
228
|
+
* @param lpApi
|
|
208
229
|
* @param options
|
|
209
230
|
* @param events Instance to use for emitting events
|
|
210
231
|
*/
|
|
@@ -228,11 +249,12 @@ export class SpvFromBTCWrapper<
|
|
|
228
249
|
}
|
|
229
250
|
},
|
|
230
251
|
btcRpc: BitcoinRpcWithAddressIndex<any>,
|
|
252
|
+
lpApi: IntermediaryAPI,
|
|
231
253
|
options?: AllOptional<SpvFromBTCWrapperOptions>,
|
|
232
254
|
events?: EventEmitter<{swapState: [ISwap]}>
|
|
233
255
|
) {
|
|
234
256
|
super(
|
|
235
|
-
chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens,
|
|
257
|
+
chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, lpApi,
|
|
236
258
|
{
|
|
237
259
|
...options,
|
|
238
260
|
bitcoinNetwork: options?.bitcoinNetwork ?? TEST_NETWORK,
|
|
@@ -347,20 +369,16 @@ export class SpvFromBTCWrapper<
|
|
|
347
369
|
*
|
|
348
370
|
* @param amountData
|
|
349
371
|
* @param options Options as passed to the swap creation function
|
|
350
|
-
* @param pricePrefetch
|
|
351
|
-
* @param nativeTokenPricePrefetch
|
|
352
372
|
* @param abortController
|
|
353
373
|
* @param contractVersion
|
|
354
374
|
* @private
|
|
355
375
|
*/
|
|
356
|
-
private async
|
|
357
|
-
amountData:
|
|
376
|
+
private async preFetchCallerFeeInNativeToken(
|
|
377
|
+
amountData: {amount?: bigint},
|
|
358
378
|
options: {
|
|
359
379
|
unsafeZeroWatchtowerFee: boolean,
|
|
360
380
|
feeSafetyFactor: number
|
|
361
381
|
},
|
|
362
|
-
pricePrefetch: Promise<bigint | undefined>,
|
|
363
|
-
nativeTokenPricePrefetch: Promise<bigint | undefined> | undefined,
|
|
364
382
|
abortController: AbortController,
|
|
365
383
|
contractVersion: string
|
|
366
384
|
): Promise<bigint | undefined> {
|
|
@@ -372,16 +390,12 @@ export class SpvFromBTCWrapper<
|
|
|
372
390
|
feePerBlock,
|
|
373
391
|
btcRelayData,
|
|
374
392
|
currentBtcBlock,
|
|
375
|
-
claimFeeRate
|
|
376
|
-
nativeTokenPrice
|
|
393
|
+
claimFeeRate
|
|
377
394
|
] = await Promise.all([
|
|
378
395
|
this.btcRelay(contractVersion).getFeePerBlock(),
|
|
379
396
|
this.btcRelay(contractVersion).getTipData(),
|
|
380
397
|
this._btcRpc.getTipHeight(),
|
|
381
|
-
this._contract(contractVersion).getClaimFee(this._chain.randomAddress())
|
|
382
|
-
nativeTokenPricePrefetch ?? (amountData.token===this._chain.getNativeCurrencyAddress() ?
|
|
383
|
-
pricePrefetch :
|
|
384
|
-
this._prices.preFetchPrice(this.chainIdentifier, this._chain.getNativeCurrencyAddress(), abortController.signal))
|
|
398
|
+
this._contract(contractVersion).getClaimFee(this._chain.randomAddress())
|
|
385
399
|
]);
|
|
386
400
|
|
|
387
401
|
if(btcRelayData==null) throw new Error("Btc relay doesn't seem to be initialized!");
|
|
@@ -394,33 +408,65 @@ export class SpvFromBTCWrapper<
|
|
|
394
408
|
(claimFeeRate * BigInt(this._options.maxTransactionsDelta))
|
|
395
409
|
) * BigInt(Math.floor(options.feeSafetyFactor*1000000)) / 1_000_000n;
|
|
396
410
|
|
|
397
|
-
|
|
398
|
-
if(amountData.exactIn) {
|
|
399
|
-
//Convert input amount in BTC to
|
|
400
|
-
const amountInNativeToken = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, amountData.amount, this._chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
|
|
401
|
-
payoutAmount = amountInNativeToken - totalFeeInNativeToken;
|
|
402
|
-
} else {
|
|
403
|
-
if(amountData.token===this._chain.getNativeCurrencyAddress()) {
|
|
404
|
-
//Both amounts in same currency
|
|
405
|
-
payoutAmount = amountData.amount;
|
|
406
|
-
} else {
|
|
407
|
-
//Need to convert both to native currency
|
|
408
|
-
const btcAmount = await this._prices.getToBtcSwapAmount(this.chainIdentifier, amountData.amount, amountData.token, abortController.signal, await pricePrefetch);
|
|
409
|
-
payoutAmount = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, btcAmount, this._chain.getNativeCurrencyAddress(), abortController.signal, nativeTokenPrice);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
this.logger.debug("preFetchCallerFeeShare(): Caller fee in native token: "+totalFeeInNativeToken.toString(10)+" total payout in native token: "+payoutAmount.toString(10));
|
|
414
|
-
|
|
415
|
-
const callerFeeShare = ((totalFeeInNativeToken * 100_000n) + payoutAmount - 1n) / payoutAmount; //Make sure to round up here
|
|
416
|
-
if(callerFeeShare < 0n) return 0n;
|
|
417
|
-
if(callerFeeShare >= 2n**20n) return 2n**20n - 1n;
|
|
418
|
-
return callerFeeShare;
|
|
411
|
+
return totalFeeInNativeToken;
|
|
419
412
|
} catch (e) {
|
|
420
413
|
abortController.abort(e);
|
|
421
414
|
}
|
|
422
415
|
}
|
|
423
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Pre-fetches caller (watchtower) bounty data for the swap. Doesn't throw, instead returns null and aborts the
|
|
419
|
+
* provided abortController
|
|
420
|
+
*
|
|
421
|
+
* @param amountPrefetch
|
|
422
|
+
* @param totalFeeInNativeTokenPrefetch
|
|
423
|
+
* @param amountData
|
|
424
|
+
* @param options Options as passed to the swap creation function
|
|
425
|
+
* @param pricePrefetch
|
|
426
|
+
* @param nativeTokenPricePrefetch
|
|
427
|
+
* @param abortSignal
|
|
428
|
+
* @private
|
|
429
|
+
*/
|
|
430
|
+
private async computeCallerFeeShare(
|
|
431
|
+
amountPrefetch: Promise<bigint | undefined>,
|
|
432
|
+
totalFeeInNativeTokenPrefetch: Promise<bigint | undefined>,
|
|
433
|
+
amountData: {exactIn: boolean, token: string},
|
|
434
|
+
options: {unsafeZeroWatchtowerFee: boolean},
|
|
435
|
+
pricePrefetch: Promise<bigint | undefined>,
|
|
436
|
+
nativeTokenPricePrefetch: Promise<bigint | undefined> | undefined,
|
|
437
|
+
abortSignal?: AbortSignal
|
|
438
|
+
): Promise<bigint> {
|
|
439
|
+
if(options.unsafeZeroWatchtowerFee) return 0n;
|
|
440
|
+
|
|
441
|
+
const amount = await throwIfUndefined(amountPrefetch, "Cannot get swap amount!");
|
|
442
|
+
if(amount===0n) return 0n;
|
|
443
|
+
|
|
444
|
+
const totalFeeInNativeToken = await throwIfUndefined(totalFeeInNativeTokenPrefetch, "Cannot get total fee in native token!");
|
|
445
|
+
const nativeTokenPrice = await nativeTokenPricePrefetch;
|
|
446
|
+
|
|
447
|
+
let payoutAmount: bigint;
|
|
448
|
+
if(amountData.exactIn) {
|
|
449
|
+
//Convert input amount in BTC to
|
|
450
|
+
const amountInNativeToken = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, amount, this._chain.getNativeCurrencyAddress(), abortSignal, nativeTokenPrice);
|
|
451
|
+
payoutAmount = amountInNativeToken - totalFeeInNativeToken;
|
|
452
|
+
} else {
|
|
453
|
+
if(amountData.token===this._chain.getNativeCurrencyAddress()) {
|
|
454
|
+
//Both amounts in same currency
|
|
455
|
+
payoutAmount = amount;
|
|
456
|
+
} else {
|
|
457
|
+
//Need to convert both to native currency
|
|
458
|
+
const btcAmount = await this._prices.getToBtcSwapAmount(this.chainIdentifier, amount, amountData.token, abortSignal, await pricePrefetch);
|
|
459
|
+
payoutAmount = await this._prices.getFromBtcSwapAmount(this.chainIdentifier, btcAmount, this._chain.getNativeCurrencyAddress(), abortSignal, nativeTokenPrice);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
this.logger.debug("computeCallerFeeShare(): Caller fee in native token: "+totalFeeInNativeToken.toString(10)+" total payout in native token: "+payoutAmount.toString(10));
|
|
464
|
+
|
|
465
|
+
const callerFeeShare = ((totalFeeInNativeToken * 100_000n) + payoutAmount - 1n) / payoutAmount; //Make sure to round up here
|
|
466
|
+
if(callerFeeShare < 0n) return 0n;
|
|
467
|
+
if(callerFeeShare >= 2n**20n) return 2n**20n - 1n;
|
|
468
|
+
return callerFeeShare;
|
|
469
|
+
}
|
|
424
470
|
|
|
425
471
|
/**
|
|
426
472
|
* Verifies response returned from intermediary
|
|
@@ -430,7 +476,8 @@ export class SpvFromBTCWrapper<
|
|
|
430
476
|
* @param lp Intermediary
|
|
431
477
|
* @param options Options as passed to the swap creation function
|
|
432
478
|
* @param callerFeeShare
|
|
433
|
-
* @param
|
|
479
|
+
* @param maxBitcoinFeeRatePromise Maximum accepted fee rate from the LPs
|
|
480
|
+
* @param bitcoinFeeRatePromise
|
|
434
481
|
* @param abortSignal
|
|
435
482
|
* @private
|
|
436
483
|
* @throws {IntermediaryError} in case the response is invalid
|
|
@@ -440,16 +487,18 @@ export class SpvFromBTCWrapper<
|
|
|
440
487
|
amountData: AmountData,
|
|
441
488
|
lp: Intermediary,
|
|
442
489
|
options: {
|
|
443
|
-
gasAmount: bigint
|
|
490
|
+
gasAmount: bigint,
|
|
491
|
+
sourceWalletUtxos?: Promise<BitcoinWalletUtxoBase[]>
|
|
444
492
|
},
|
|
445
493
|
callerFeeShare: bigint,
|
|
446
|
-
|
|
494
|
+
maxBitcoinFeeRatePromise: Promise<number | undefined>,
|
|
495
|
+
bitcoinFeeRatePromise: Promise<number | undefined> | undefined,
|
|
447
496
|
abortSignal: AbortSignal
|
|
448
497
|
): Promise<{
|
|
449
498
|
vault: T["SpvVaultData"],
|
|
450
499
|
vaultUtxoValue: number
|
|
451
500
|
}> {
|
|
452
|
-
const btcFeeRate = await throwIfUndefined(
|
|
501
|
+
const btcFeeRate = await throwIfUndefined(maxBitcoinFeeRatePromise, "Bitcoin fee rate promise failed!");
|
|
453
502
|
abortSignal.throwIfAborted();
|
|
454
503
|
if(btcFeeRate!=null && resp.btcFeeRate > btcFeeRate) throw new IntermediaryError(`Required bitcoin fee rate returned from the LP is too high! Maximum accepted: ${btcFeeRate} sats/vB, required by LP: ${resp.btcFeeRate} sats/vB`);
|
|
455
504
|
|
|
@@ -459,11 +508,13 @@ export class SpvFromBTCWrapper<
|
|
|
459
508
|
let vaultScript: Uint8Array;
|
|
460
509
|
let vaultAddressType: CoinselectAddressTypes;
|
|
461
510
|
let btcAddressScript: Uint8Array;
|
|
511
|
+
let btcAddressType: CoinselectAddressTypes;
|
|
462
512
|
//Ensure valid btc addresses returned
|
|
463
513
|
try {
|
|
464
514
|
vaultScript = toOutputScript(this._options.bitcoinNetwork, resp.vaultBtcAddress);
|
|
465
515
|
vaultAddressType = toCoinselectAddressType(vaultScript);
|
|
466
516
|
btcAddressScript = toOutputScript(this._options.bitcoinNetwork, resp.btcAddress);
|
|
517
|
+
btcAddressType = toCoinselectAddressType(btcAddressScript);
|
|
467
518
|
} catch (e) {
|
|
468
519
|
throw new IntermediaryError("Invalid btc address data returned", e);
|
|
469
520
|
}
|
|
@@ -473,7 +524,8 @@ export class SpvFromBTCWrapper<
|
|
|
473
524
|
resp.vaultId < 0n || //Ensure vaultId is not negative
|
|
474
525
|
vaultScript==null || //Make sure vault script is parsable and of known type
|
|
475
526
|
btcAddressScript==null || //Make sure btc address script is parsable and of known type
|
|
476
|
-
|
|
527
|
+
btcAddressType!==REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE || //Constrain the btc address script type
|
|
528
|
+
vaultAddressType!==REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE || //Constrain the vault script type
|
|
477
529
|
decodedUtxo.length!==2 || decodedUtxo[0].length!==64 || isNaN(parseInt(decodedUtxo[1])) || //Check valid UTXO
|
|
478
530
|
resp.btcFeeRate < 1 || resp.btcFeeRate > 10000 //Sanity check on the returned BTC fee rate
|
|
479
531
|
) throw new IntermediaryError("Invalid vault data returned!");
|
|
@@ -517,7 +569,22 @@ export class SpvFromBTCWrapper<
|
|
|
517
569
|
|
|
518
570
|
//Amounts - make sure the amounts match
|
|
519
571
|
if(amountData.exactIn) {
|
|
520
|
-
if(resp.
|
|
572
|
+
if(!resp.usedUtxoInputCalculation) {
|
|
573
|
+
//Legacy calculation
|
|
574
|
+
if(resp.btcAmount !== amountData.amount) throw new IntermediaryError("Invalid amount returned");
|
|
575
|
+
} else {
|
|
576
|
+
//Implies the raw UTXOs were passed for amount derivation
|
|
577
|
+
//Verify the derivation was done correctly
|
|
578
|
+
if(options.sourceWalletUtxos==null) throw new IntermediaryError("Invalid usedUtxoInputCalcuation return value");
|
|
579
|
+
if(bitcoinFeeRatePromise==null) throw new Error("bitcoinFeeRatePromise must be passed for UTXO-based input amount calculation checks");
|
|
580
|
+
const walletUtxos = await options.sourceWalletUtxos;
|
|
581
|
+
const bitcoinFeeRate = await throwIfUndefined(bitcoinFeeRatePromise, "Failed to fetch bitcoin fee rate!");
|
|
582
|
+
const {balance} = BitcoinWallet.getSpendableBalance(
|
|
583
|
+
walletUtxos, Math.max(resp.btcFeeRate, bitcoinFeeRate),
|
|
584
|
+
this.getDummySwapPsbt(options.gasAmount!==0n), REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE
|
|
585
|
+
);
|
|
586
|
+
if(resp.btcAmount !== balance) throw new IntermediaryError(`Invalid amount returned, expected: ${balance.toString(10)}, got: ${resp.btcAmount.toString(10)}`);
|
|
587
|
+
}
|
|
521
588
|
} else {
|
|
522
589
|
//Check the difference between amount adjusted due to scaling to raw amount
|
|
523
590
|
const adjustedAmount = amountData.amount / tokenData[0].multiplier * tokenData[0].multiplier;
|
|
@@ -613,6 +680,72 @@ export class SpvFromBTCWrapper<
|
|
|
613
680
|
};
|
|
614
681
|
}
|
|
615
682
|
|
|
683
|
+
private async amountPrefetch(
|
|
684
|
+
amountData: {token: string, exactIn: boolean, amount?: bigint},
|
|
685
|
+
bitcoinFeeRatePromise: Promise<number | undefined>,
|
|
686
|
+
walletUtxosPromise: Promise<BitcoinWalletUtxoBase[]> | undefined,
|
|
687
|
+
includeGas: boolean,
|
|
688
|
+
abortController: AbortController
|
|
689
|
+
): Promise<bigint | undefined> {
|
|
690
|
+
if(amountData.amount!=null) return amountData.amount;
|
|
691
|
+
try {
|
|
692
|
+
const bitcoinFeeRate = await throwIfUndefined(bitcoinFeeRatePromise, "Cannot fetch Bitcoin fee rate!");
|
|
693
|
+
if(walletUtxosPromise==null) throw new UserError("Cannot use empty amount without passing UTXOs!");
|
|
694
|
+
const walletUtxos = await walletUtxosPromise;
|
|
695
|
+
if(walletUtxos.length===0)
|
|
696
|
+
throw new UserError("Wallet doesn't have any BTC balance");
|
|
697
|
+
const spendableBalance = await BitcoinWallet.getSpendableBalance(
|
|
698
|
+
walletUtxos, bitcoinFeeRate,
|
|
699
|
+
this.getDummySwapPsbt(includeGas), REQUIRED_SPV_SWAP_LP_ADDRESS_TYPE
|
|
700
|
+
);
|
|
701
|
+
return spendableBalance.balance;
|
|
702
|
+
} catch (e) {
|
|
703
|
+
abortController.abort(e);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
private bitcoinFeeRatePrefetch(
|
|
708
|
+
options: {
|
|
709
|
+
maxAllowedBitcoinFeeRate: number,
|
|
710
|
+
sourceWalletUtxos?: Promise<BitcoinWalletUtxoBase[]>,
|
|
711
|
+
bitcoinFeeRate?: Promise<number>
|
|
712
|
+
},
|
|
713
|
+
abortController: AbortController
|
|
714
|
+
) {
|
|
715
|
+
let bitcoinFeeRatePromise: Promise<number | undefined> | undefined;
|
|
716
|
+
if(options?.sourceWalletUtxos!=null) {
|
|
717
|
+
if(options.bitcoinFeeRate!=null) {
|
|
718
|
+
bitcoinFeeRatePromise = options.bitcoinFeeRate.then(value => {
|
|
719
|
+
if(options.maxAllowedBitcoinFeeRate!=Infinity && options.maxAllowedBitcoinFeeRate<value)
|
|
720
|
+
throw new Error("Passed `maxAllowedBitcoinFeeRate` cannot be lower than `bitcoinFeeRate`");
|
|
721
|
+
return value;
|
|
722
|
+
});
|
|
723
|
+
} else {
|
|
724
|
+
bitcoinFeeRatePromise = this._btcRpc.getFeeRate().then(value => {
|
|
725
|
+
if(options.maxAllowedBitcoinFeeRate!=Infinity && value > options.maxAllowedBitcoinFeeRate) return options.maxAllowedBitcoinFeeRate;
|
|
726
|
+
return value;
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
bitcoinFeeRatePromise = bitcoinFeeRatePromise.catch(e => {
|
|
730
|
+
abortController.abort(e);
|
|
731
|
+
return undefined;
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
const maxBitcoinFeeRatePromise: Promise<number | undefined> = options.maxAllowedBitcoinFeeRate!=Infinity
|
|
735
|
+
? Promise.resolve(options.maxAllowedBitcoinFeeRate)
|
|
736
|
+
: throwIfUndefined(bitcoinFeeRatePromise ?? options.bitcoinFeeRate ?? this._btcRpc.getFeeRate())
|
|
737
|
+
.then(x => this._options.maxBtcFeeOffset + (x*this._options.maxBtcFeeMultiplier))
|
|
738
|
+
.catch(e => {
|
|
739
|
+
abortController.abort(e);
|
|
740
|
+
return undefined;
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
return {
|
|
744
|
+
bitcoinFeeRatePromise,
|
|
745
|
+
maxBitcoinFeeRatePromise
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
616
749
|
/**
|
|
617
750
|
* Returns a newly created Bitcoin -> Smart chain swap using the SPV vault (UTXO-controlled vault) swap protocol,
|
|
618
751
|
* with the passed amount. Also allows specifying additional "gas drop" native token that the receipient receives
|
|
@@ -627,7 +760,7 @@ export class SpvFromBTCWrapper<
|
|
|
627
760
|
*/
|
|
628
761
|
public create(
|
|
629
762
|
recipient: string,
|
|
630
|
-
amountData:
|
|
763
|
+
amountData: { amount?: bigint, token: string, exactIn: boolean },
|
|
631
764
|
lps: Intermediary[],
|
|
632
765
|
options?: SpvFromBTCOptions,
|
|
633
766
|
additionalParams?: Record<string, any>,
|
|
@@ -640,7 +773,13 @@ export class SpvFromBTCWrapper<
|
|
|
640
773
|
gasAmount: this.parseGasAmount(options?.gasAmount),
|
|
641
774
|
unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
|
|
642
775
|
feeSafetyFactor: options?.feeSafetyFactor ?? 1.25,
|
|
643
|
-
maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity
|
|
776
|
+
maxAllowedBitcoinFeeRate: options?.maxAllowedBitcoinFeeRate ?? options?.maxAllowedNetworkFeeRate ?? Infinity,
|
|
777
|
+
sourceWalletUtxos: options?.sourceWalletUtxos==undefined
|
|
778
|
+
? undefined
|
|
779
|
+
: options?.sourceWalletUtxos instanceof Promise ? options.sourceWalletUtxos : Promise.resolve(options.sourceWalletUtxos),
|
|
780
|
+
bitcoinFeeRate: options?.bitcoinFeeRate==undefined
|
|
781
|
+
? undefined
|
|
782
|
+
: options?.bitcoinFeeRate instanceof Promise ? options.bitcoinFeeRate : Promise.resolve(options.bitcoinFeeRate),
|
|
644
783
|
};
|
|
645
784
|
|
|
646
785
|
if(
|
|
@@ -652,6 +791,13 @@ export class SpvFromBTCWrapper<
|
|
|
652
791
|
)
|
|
653
792
|
) throw new UserError("Cannot specify `gasAmount` for swaps to a native token!");
|
|
654
793
|
|
|
794
|
+
if(amountData.amount==null && options?.sourceWalletUtxos==null)
|
|
795
|
+
throw new UserError("Source wallet UTXOs need to be passed when amount is null!");
|
|
796
|
+
if(amountData.amount==null && !amountData.exactIn)
|
|
797
|
+
throw new UserError("Amount can be null only for exactIn swaps!");
|
|
798
|
+
if(amountData.amount!=null && options?.sourceWalletUtxos!=null)
|
|
799
|
+
throw new UserError("Source wallet UTXOs cannot be passed while specifying an input amount!");
|
|
800
|
+
|
|
655
801
|
const lpVersions = Intermediary.getContractVersionsForLps(this.chainIdentifier, lps);
|
|
656
802
|
|
|
657
803
|
const _abortController = extendAbortController(abortSignal);
|
|
@@ -663,14 +809,12 @@ export class SpvFromBTCWrapper<
|
|
|
663
809
|
undefined :
|
|
664
810
|
this.preFetchPrice({token: nativeTokenAddress}, _abortController.signal);
|
|
665
811
|
const callerFeePrefetchPromise = mapArrayToObject(lpVersions, (contractVersion: string) => {
|
|
666
|
-
return this.
|
|
812
|
+
return this.preFetchCallerFeeInNativeToken(amountData, _options, _abortController, contractVersion);
|
|
667
813
|
});
|
|
668
|
-
const bitcoinFeeRatePromise
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return undefined;
|
|
673
|
-
});
|
|
814
|
+
const {maxBitcoinFeeRatePromise, bitcoinFeeRatePromise} = this.bitcoinFeeRatePrefetch(_options, _abortController);
|
|
815
|
+
const amountPromise = this.amountPrefetch(
|
|
816
|
+
amountData, maxBitcoinFeeRatePromise, _options.sourceWalletUtxos, _options.gasAmount!==0n, _abortController
|
|
817
|
+
);
|
|
674
818
|
|
|
675
819
|
return lps.map(lp => {
|
|
676
820
|
return {
|
|
@@ -680,21 +824,41 @@ export class SpvFromBTCWrapper<
|
|
|
680
824
|
const version = lp.getContractVersion(this.chainIdentifier);
|
|
681
825
|
|
|
682
826
|
const abortController = extendAbortController(_abortController.signal);
|
|
827
|
+
const callerFeeRatePromise = this.computeCallerFeeShare(
|
|
828
|
+
amountPromise,
|
|
829
|
+
callerFeePrefetchPromise[version],
|
|
830
|
+
amountData,
|
|
831
|
+
_options,
|
|
832
|
+
pricePrefetchPromise,
|
|
833
|
+
gasTokenPricePrefetchPromise,
|
|
834
|
+
abortController.signal
|
|
835
|
+
);
|
|
683
836
|
|
|
684
837
|
try {
|
|
685
838
|
const resp = await tryWithRetries(async(retryCount: number) => {
|
|
686
|
-
return await
|
|
839
|
+
return await this._lpApi.prepareSpvFromBTC(
|
|
687
840
|
this.chainIdentifier, lp.url,
|
|
688
841
|
{
|
|
689
842
|
address: recipient,
|
|
690
|
-
amount:
|
|
843
|
+
amount: throwIfUndefined(amountPromise, "Failed to compute swap amount"),
|
|
691
844
|
token: amountData.token.toString(),
|
|
692
845
|
exactOut: !amountData.exactIn,
|
|
693
846
|
gasToken: nativeTokenAddress,
|
|
694
847
|
gasAmount: _options.gasAmount,
|
|
695
|
-
callerFeeRate: throwIfUndefined(
|
|
848
|
+
callerFeeRate: throwIfUndefined(callerFeeRatePromise, "Caller fee prefetch failed!"),
|
|
696
849
|
frontingFeeRate: 0n,
|
|
697
850
|
stickyAddress: options?.stickyAddress,
|
|
851
|
+
amountUtxos: _options.sourceWalletUtxos!=null
|
|
852
|
+
? _options.sourceWalletUtxos.then(utxos => {
|
|
853
|
+
if(utxos.length===0) return undefined;
|
|
854
|
+
return utxos.map(utxo => ({
|
|
855
|
+
value: utxo.value,
|
|
856
|
+
vSize: utils.inputBytes({type: utxo.type}),
|
|
857
|
+
cpfp: utxo.cpfp==null ? undefined : {effectiveVSize: utxo.cpfp?.txVsize, effectiveFeeRate: utxo.cpfp?.txEffectiveFeeRate}
|
|
858
|
+
}));
|
|
859
|
+
})
|
|
860
|
+
: undefined,
|
|
861
|
+
amountFeeRate: bitcoinFeeRatePromise,
|
|
698
862
|
additionalParams
|
|
699
863
|
},
|
|
700
864
|
this._options.postRequestTimeout, abortController.signal, retryCount>0 ? false : undefined
|
|
@@ -703,7 +867,8 @@ export class SpvFromBTCWrapper<
|
|
|
703
867
|
|
|
704
868
|
this.logger.debug("create("+lp.url+"): LP response: ", resp)
|
|
705
869
|
|
|
706
|
-
const callerFeeShare =
|
|
870
|
+
const callerFeeShare = await callerFeeRatePromise;
|
|
871
|
+
const amount = await throwIfUndefined(amountPromise);
|
|
707
872
|
|
|
708
873
|
const [
|
|
709
874
|
pricingInfo,
|
|
@@ -722,9 +887,16 @@ export class SpvFromBTCWrapper<
|
|
|
722
887
|
resp.totalGas * (100_000n + callerFeeShare) / 100_000n,
|
|
723
888
|
nativeTokenAddress, {swapFeeBtc: resp.gasSwapFeeBtc}, gasTokenPricePrefetchPromise, usdPricePrefetchPromise, abortController.signal
|
|
724
889
|
),
|
|
725
|
-
this.verifyReturnedData(
|
|
890
|
+
this.verifyReturnedData(
|
|
891
|
+
resp,
|
|
892
|
+
{...amountData, amount},
|
|
893
|
+
lp, _options, callerFeeShare, maxBitcoinFeeRatePromise, bitcoinFeeRatePromise, abortController.signal
|
|
894
|
+
)
|
|
726
895
|
]);
|
|
727
896
|
|
|
897
|
+
let minimumBtcFeeRate: number = resp.btcFeeRate;
|
|
898
|
+
if(bitcoinFeeRatePromise!=null) minimumBtcFeeRate = Math.max(minimumBtcFeeRate, await throwIfUndefined(bitcoinFeeRatePromise));
|
|
899
|
+
|
|
728
900
|
const swapInit: SpvFromBTCSwapInit = {
|
|
729
901
|
pricingInfo,
|
|
730
902
|
url: lp.url,
|
|
@@ -749,7 +921,7 @@ export class SpvFromBTCWrapper<
|
|
|
749
921
|
btcAmount: resp.btcAmount,
|
|
750
922
|
btcAmountSwap: resp.btcAmountSwap,
|
|
751
923
|
btcAmountGas: resp.btcAmountGas,
|
|
752
|
-
minimumBtcFeeRate
|
|
924
|
+
minimumBtcFeeRate,
|
|
753
925
|
|
|
754
926
|
outputTotalSwap: resp.total,
|
|
755
927
|
outputSwapToken: amountData.token,
|
|
@@ -765,13 +937,19 @@ export class SpvFromBTCWrapper<
|
|
|
765
937
|
|
|
766
938
|
genesisSmartChainBlockHeight: await throwIfUndefined(
|
|
767
939
|
finalizedBlockHeightPrefetchPromise,
|
|
768
|
-
"
|
|
940
|
+
"Network finalized blockheight pre-fetch failed!"
|
|
769
941
|
),
|
|
770
942
|
contractVersion: version
|
|
771
943
|
};
|
|
772
944
|
const quote = new SpvFromBTCSwap<T>(this, swapInit);
|
|
773
945
|
return quote;
|
|
774
946
|
} catch (e) {
|
|
947
|
+
if(e instanceof OutOfBoundsError) {
|
|
948
|
+
const amountResult = await amountPromise.catch(() => undefined);
|
|
949
|
+
if(_options.sourceWalletUtxos!=null && amountResult!=null && amountResult<=0n) {
|
|
950
|
+
e = new UserError("Wallet doesn't have enough BTC balance to cover transaction fees");
|
|
951
|
+
}
|
|
952
|
+
}
|
|
775
953
|
abortController.abort(e);
|
|
776
954
|
throw e;
|
|
777
955
|
}
|
|
@@ -902,7 +1080,7 @@ export class SpvFromBTCWrapper<
|
|
|
902
1080
|
allowUnknownOutputs: true
|
|
903
1081
|
});
|
|
904
1082
|
|
|
905
|
-
const randomVaultOutScript =
|
|
1083
|
+
const randomVaultOutScript = getDummyOutputScript(REQUIRED_SPV_SWAP_VAULT_ADDRESS_TYPE);
|
|
906
1084
|
|
|
907
1085
|
psbt.addInput({
|
|
908
1086
|
txid: randomBytes(32),
|