@atomiqlabs/sdk 8.1.8 → 8.3.5
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/coinselect2/utils.d.ts +6 -0
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +41 -5
- package/dist/bitcoin/wallet/BitcoinWallet.js +36 -1
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +52 -2
- package/dist/bitcoin/wallet/IBitcoinWallet.js +2 -1
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +42 -7
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +36 -1
- package/dist/enums/FeeType.d.ts +8 -1
- package/dist/enums/FeeType.js +8 -1
- package/dist/enums/SwapAmountType.d.ts +7 -0
- package/dist/enums/SwapAmountType.js +7 -0
- package/dist/enums/SwapDirection.d.ts +7 -0
- package/dist/enums/SwapDirection.js +7 -0
- package/dist/enums/SwapType.d.ts +62 -1
- package/dist/enums/SwapType.js +62 -1
- package/dist/errors/IntermediaryError.d.ts +4 -0
- package/dist/errors/IntermediaryError.js +1 -0
- package/dist/errors/RequestError.d.ts +15 -1
- package/dist/errors/RequestError.js +8 -0
- package/dist/errors/UserError.d.ts +1 -0
- package/dist/errors/UserError.js +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -6
- package/dist/intermediaries/Intermediary.d.ts +61 -14
- package/dist/intermediaries/Intermediary.js +38 -11
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +62 -29
- package/dist/intermediaries/IntermediaryDiscovery.js +39 -24
- package/dist/prices/RedundantSwapPrice.d.ts +26 -5
- package/dist/prices/RedundantSwapPrice.js +22 -2
- package/dist/prices/SingleSwapPrice.d.ts +10 -7
- package/dist/prices/SingleSwapPrice.js +11 -8
- package/dist/prices/SwapPriceWithChain.d.ts +56 -19
- package/dist/prices/SwapPriceWithChain.js +62 -25
- package/dist/prices/abstract/IPriceProvider.d.ts +4 -4
- package/dist/prices/abstract/IPriceProvider.js +1 -1
- package/dist/prices/abstract/ISwapPrice.d.ts +95 -46
- package/dist/prices/abstract/ISwapPrice.js +104 -56
- package/dist/prices/providers/BinancePriceProvider.d.ts +8 -1
- package/dist/prices/providers/BinancePriceProvider.js +8 -1
- package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +7 -1
- package/dist/prices/providers/CoinGeckoPriceProvider.js +7 -1
- package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +7 -1
- package/dist/prices/providers/CoinPaprikaPriceProvider.js +7 -1
- package/dist/prices/providers/CustomPriceProvider.d.ts +12 -1
- package/dist/prices/providers/CustomPriceProvider.js +12 -1
- package/dist/prices/providers/KrakenPriceProvider.d.ts +10 -1
- package/dist/prices/providers/KrakenPriceProvider.js +10 -1
- package/dist/prices/providers/OKXPriceProvider.d.ts +7 -1
- package/dist/prices/providers/OKXPriceProvider.js +7 -1
- package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +3 -0
- package/dist/prices/providers/abstract/ExchangePriceProvider.js +3 -0
- package/dist/storage/IUnifiedStorage.d.ts +19 -7
- package/dist/storage/UnifiedSwapStorage.d.ts +33 -3
- package/dist/storage/UnifiedSwapStorage.js +29 -1
- package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +31 -7
- package/dist/storage-browser/IndexedDBUnifiedStorage.js +29 -6
- package/dist/storage-browser/LocalStorageManager.d.ts +25 -1
- package/dist/storage-browser/LocalStorageManager.js +25 -1
- package/dist/swapper/Swapper.d.ts +380 -226
- package/dist/swapper/Swapper.js +383 -349
- package/dist/swapper/SwapperFactory.d.ts +66 -18
- package/dist/swapper/SwapperFactory.js +24 -3
- package/dist/swapper/SwapperUtils.d.ts +75 -28
- package/dist/swapper/SwapperUtils.js +107 -60
- package/dist/swapper/SwapperWithChain.d.ts +286 -91
- package/dist/swapper/SwapperWithChain.js +218 -64
- package/dist/swapper/SwapperWithSigner.d.ts +229 -80
- package/dist/swapper/SwapperWithSigner.js +190 -44
- package/dist/swaps/IAddressSwap.d.ts +12 -3
- package/dist/swaps/IAddressSwap.js +3 -2
- package/dist/swaps/IBTCWalletSwap.d.ts +26 -8
- package/dist/swaps/IBTCWalletSwap.js +3 -2
- package/dist/swaps/IClaimableSwap.d.ts +38 -6
- package/dist/swaps/IClaimableSwap.js +3 -2
- package/dist/swaps/IClaimableSwapWrapper.d.ts +11 -1
- package/dist/swaps/IRefundableSwap.d.ts +31 -5
- package/dist/swaps/IRefundableSwap.js +3 -2
- package/dist/swaps/ISwap.d.ts +162 -24
- package/dist/swaps/ISwap.js +92 -35
- package/dist/swaps/ISwapWithGasDrop.d.ts +8 -2
- package/dist/swaps/ISwapWithGasDrop.js +2 -1
- package/dist/swaps/ISwapWrapper.d.ts +161 -52
- package/dist/swaps/ISwapWrapper.js +131 -73
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +51 -6
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +22 -12
- package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +65 -12
- package/dist/swaps/escrow_swaps/IEscrowSwap.js +38 -19
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +39 -9
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +30 -21
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +31 -15
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +33 -18
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +97 -28
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +91 -27
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +22 -9
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +24 -11
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +278 -60
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +519 -241
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +77 -26
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +132 -50
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +313 -52
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +544 -194
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +87 -26
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +147 -58
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +222 -55
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +462 -244
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +77 -23
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +116 -46
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +195 -58
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +324 -191
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +30 -5
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +44 -19
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +61 -20
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +75 -32
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +76 -50
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +106 -101
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +37 -14
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +66 -20
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +46 -17
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +82 -27
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +350 -88
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +482 -215
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +76 -24
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +247 -124
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +148 -20
- package/dist/swaps/trusted/ln/LnForGasSwap.js +175 -45
- package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +29 -10
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +30 -11
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +202 -49
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +232 -80
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +34 -12
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +33 -14
- package/dist/types/AmountData.d.ts +2 -1
- package/dist/types/CustomPriceFunction.d.ts +8 -2
- package/dist/types/PriceInfoType.d.ts +4 -4
- package/dist/types/PriceInfoType.js +3 -3
- package/dist/types/SwapExecutionAction.d.ts +85 -4
- package/dist/types/SwapWithSigner.d.ts +5 -2
- package/dist/types/SwapWithSigner.js +5 -2
- package/dist/types/Token.d.ts +11 -5
- package/dist/types/Token.js +6 -3
- package/dist/types/TokenAmount.d.ts +3 -0
- package/dist/types/TokenAmount.js +2 -0
- package/dist/types/fees/Fee.d.ts +3 -2
- package/dist/types/fees/FeeBreakdown.d.ts +3 -2
- package/dist/types/fees/PercentagePPM.d.ts +4 -2
- package/dist/types/fees/PercentagePPM.js +2 -1
- package/dist/types/lnurl/LNURLPay.d.ts +20 -12
- package/dist/types/lnurl/LNURLPay.js +8 -4
- package/dist/types/lnurl/LNURLWithdraw.d.ts +17 -10
- package/dist/types/lnurl/LNURLWithdraw.js +8 -4
- package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -0
- package/dist/types/wallets/LightningInvoiceCreateService.js +15 -0
- package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +3 -1
- package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +4 -2
- package/dist/utils/BitcoinUtils.d.ts +1 -0
- package/dist/utils/BitcoinUtils.js +5 -1
- package/dist/utils/SwapUtils.d.ts +58 -1
- package/dist/utils/SwapUtils.js +55 -1
- package/dist/utils/TokenUtils.d.ts +10 -2
- package/dist/utils/TokenUtils.js +12 -4
- package/package.json +3 -3
- package/src/bitcoin/coinselect2/utils.ts +6 -0
- package/src/bitcoin/wallet/BitcoinWallet.ts +41 -5
- package/src/bitcoin/wallet/IBitcoinWallet.ts +57 -2
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +42 -6
- package/src/enums/FeeType.ts +8 -1
- package/src/enums/SwapAmountType.ts +7 -0
- package/src/enums/SwapDirection.ts +7 -0
- package/src/enums/SwapType.ts +62 -2
- package/src/errors/IntermediaryError.ts +4 -0
- package/src/errors/RequestError.ts +15 -1
- package/src/errors/UserError.ts +1 -0
- package/src/index.ts +12 -5
- package/src/intermediaries/Intermediary.ts +61 -14
- package/src/intermediaries/IntermediaryDiscovery.ts +69 -34
- package/src/prices/RedundantSwapPrice.ts +26 -6
- package/src/prices/SingleSwapPrice.ts +11 -8
- package/src/prices/SwapPriceWithChain.ts +63 -26
- package/src/prices/abstract/IPriceProvider.ts +4 -4
- package/src/prices/abstract/ISwapPrice.ts +115 -66
- package/src/prices/providers/BinancePriceProvider.ts +8 -1
- package/src/prices/providers/CoinGeckoPriceProvider.ts +7 -1
- package/src/prices/providers/CoinPaprikaPriceProvider.ts +7 -1
- package/src/prices/providers/CustomPriceProvider.ts +12 -1
- package/src/prices/providers/KrakenPriceProvider.ts +10 -1
- package/src/prices/providers/OKXPriceProvider.ts +7 -1
- package/src/prices/providers/abstract/ExchangePriceProvider.ts +3 -0
- package/src/storage/IUnifiedStorage.ts +19 -7
- package/src/storage/UnifiedSwapStorage.ts +33 -3
- package/src/storage-browser/IndexedDBUnifiedStorage.ts +31 -8
- package/src/storage-browser/LocalStorageManager.ts +25 -1
- package/src/swapper/Swapper.ts +599 -390
- package/src/swapper/SwapperFactory.ts +73 -24
- package/src/swapper/SwapperUtils.ts +107 -60
- package/src/swapper/SwapperWithChain.ts +320 -81
- package/src/swapper/SwapperWithSigner.ts +263 -56
- package/src/swaps/IAddressSwap.ts +13 -3
- package/src/swaps/IBTCWalletSwap.ts +26 -10
- package/src/swaps/IClaimableSwap.ts +41 -6
- package/src/swaps/IClaimableSwapWrapper.ts +11 -2
- package/src/swaps/IRefundableSwap.ts +34 -5
- package/src/swaps/ISwap.ts +224 -85
- package/src/swaps/ISwapWithGasDrop.ts +8 -2
- package/src/swaps/ISwapWrapper.ts +216 -98
- package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +64 -18
- package/src/swaps/escrow_swaps/IEscrowSwap.ts +83 -37
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +61 -30
- package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +37 -19
- package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +123 -50
- package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +24 -11
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +562 -258
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +156 -62
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +592 -227
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +177 -74
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +483 -245
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +141 -59
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +350 -195
- package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +48 -23
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +87 -40
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +110 -110
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +89 -34
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +101 -31
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +556 -259
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +292 -148
- package/src/swaps/trusted/ln/LnForGasSwap.ts +186 -47
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +34 -15
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +262 -88
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +41 -19
- package/src/types/AmountData.ts +2 -1
- package/src/types/CustomPriceFunction.ts +8 -2
- package/src/types/PriceInfoType.ts +4 -4
- package/src/types/SwapExecutionAction.ts +97 -5
- package/src/types/SwapWithSigner.ts +8 -4
- package/src/types/Token.ts +12 -5
- package/src/types/TokenAmount.ts +3 -0
- package/src/types/fees/Fee.ts +3 -2
- package/src/types/fees/FeeBreakdown.ts +3 -2
- package/src/types/fees/PercentagePPM.ts +4 -2
- package/src/types/lnurl/LNURLPay.ts +20 -12
- package/src/types/lnurl/LNURLWithdraw.ts +17 -10
- package/src/types/wallets/LightningInvoiceCreateService.ts +30 -0
- package/src/types/wallets/MinimalBitcoinWalletInterface.ts +3 -1
- package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +4 -2
- package/src/utils/BitcoinUtils.ts +5 -0
- package/src/utils/SwapUtils.ts +63 -1
- package/src/utils/TokenUtils.ts +12 -4
- package/dist/bitcoin/BitcoinRpcWithAddressIndex.d.ts +0 -68
- package/dist/bitcoin/BitcoinRpcWithAddressIndex.js +0 -2
- package/dist/bitcoin/LightningNetworkApi.d.ts +0 -12
- package/dist/bitcoin/LightningNetworkApi.js +0 -2
- package/dist/bitcoin/mempool/MempoolApi.d.ts +0 -350
- package/dist/bitcoin/mempool/MempoolApi.js +0 -311
- package/dist/bitcoin/mempool/MempoolBitcoinBlock.d.ts +0 -44
- package/dist/bitcoin/mempool/MempoolBitcoinBlock.js +0 -48
- package/dist/bitcoin/mempool/MempoolBitcoinRpc.d.ts +0 -119
- package/dist/bitcoin/mempool/MempoolBitcoinRpc.js +0 -361
- package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.d.ts +0 -22
- package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.js +0 -105
- package/dist/errors/PaymentAuthError.d.ts +0 -11
- package/dist/errors/PaymentAuthError.js +0 -23
- package/src/errors/PaymentAuthError.ts +0 -26
|
@@ -2,7 +2,8 @@ import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
|
|
|
2
2
|
import {SwapType} from "../../../../enums/SwapType";
|
|
3
3
|
import {
|
|
4
4
|
ChainSwapType,
|
|
5
|
-
ChainType,
|
|
5
|
+
ChainType,
|
|
6
|
+
isAbstractSigner,
|
|
6
7
|
SwapClaimWitnessMessage,
|
|
7
8
|
SwapCommitState,
|
|
8
9
|
SwapCommitStateType,
|
|
@@ -17,10 +18,7 @@ import {
|
|
|
17
18
|
InvoiceStatusResponseCodes
|
|
18
19
|
} from "../../../../intermediaries/apis/IntermediaryAPI";
|
|
19
20
|
import {IntermediaryError} from "../../../../errors/IntermediaryError";
|
|
20
|
-
import {
|
|
21
|
-
extendAbortController,
|
|
22
|
-
toBigInt
|
|
23
|
-
} from "../../../../utils/Utils";
|
|
21
|
+
import {extendAbortController, toBigInt} from "../../../../utils/Utils";
|
|
24
22
|
import {Fee} from "../../../../types/fees/Fee";
|
|
25
23
|
import {IAddressSwap} from "../../../IAddressSwap";
|
|
26
24
|
import {FromBTCLNAutoDefinition, FromBTCLNAutoWrapper} from "./FromBTCLNAutoWrapper";
|
|
@@ -31,7 +29,7 @@ import {IEscrowSwap, IEscrowSwapInit, isIEscrowSwapInit} from "../../IEscrowSwap
|
|
|
31
29
|
import {FeeType} from "../../../../enums/FeeType";
|
|
32
30
|
import {ppmToPercentage} from "../../../../types/fees/PercentagePPM";
|
|
33
31
|
import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
|
|
34
|
-
import {BitcoinTokens, BtcToken, SCToken
|
|
32
|
+
import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
|
|
35
33
|
import {getLogger, LoggerType} from "../../../../utils/Logger";
|
|
36
34
|
import {timeoutPromise} from "../../../../utils/TimeoutUtils";
|
|
37
35
|
import {isLNURLWithdraw, LNURLWithdraw, LNURLWithdrawParamsWithUrl} from "../../../../types/lnurl/LNURLWithdraw";
|
|
@@ -41,10 +39,11 @@ import {
|
|
|
41
39
|
PriceInfoType,
|
|
42
40
|
serializePriceInfoType
|
|
43
41
|
} from "../../../../types/PriceInfoType";
|
|
42
|
+
import {sha256} from "@noble/hashes/sha2";
|
|
44
43
|
|
|
45
44
|
/**
|
|
46
45
|
* State enum for FromBTCLNAuto swaps
|
|
47
|
-
* @category Swaps
|
|
46
|
+
* @category Swaps/Lightning → Smart chain
|
|
48
47
|
*/
|
|
49
48
|
export enum FromBTCLNAutoSwapState {
|
|
50
49
|
FAILED = -4,
|
|
@@ -58,12 +57,12 @@ export enum FromBTCLNAutoSwapState {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
export type FromBTCLNAutoSwapInit<T extends SwapData> = IEscrowSwapInit<T> & {
|
|
61
|
-
pr
|
|
62
|
-
secret
|
|
60
|
+
pr?: string,
|
|
61
|
+
secret?: string,
|
|
63
62
|
initialSwapData: T,
|
|
64
63
|
|
|
65
|
-
btcAmountSwap
|
|
66
|
-
btcAmountGas
|
|
64
|
+
btcAmountSwap?: bigint,
|
|
65
|
+
btcAmountGas?: bigint,
|
|
67
66
|
|
|
68
67
|
gasSwapFeeBtc: bigint,
|
|
69
68
|
gasSwapFee: bigint,
|
|
@@ -75,10 +74,10 @@ export type FromBTCLNAutoSwapInit<T extends SwapData> = IEscrowSwapInit<T> & {
|
|
|
75
74
|
};
|
|
76
75
|
|
|
77
76
|
export function isFromBTCLNAutoSwapInit<T extends SwapData>(obj: any): obj is FromBTCLNAutoSwapInit<T> {
|
|
78
|
-
return typeof obj.pr==="string" &&
|
|
79
|
-
typeof obj.secret==="string" &&
|
|
80
|
-
typeof obj.btcAmountSwap==="bigint" &&
|
|
81
|
-
typeof obj.btcAmountGas==="bigint" &&
|
|
77
|
+
return (obj.pr==null || typeof obj.pr==="string") &&
|
|
78
|
+
(obj.secret==null || typeof obj.secret==="string") &&
|
|
79
|
+
(obj.btcAmountSwap==null || typeof obj.btcAmountSwap==="bigint") &&
|
|
80
|
+
(obj.btcAmountGas==null || typeof obj.btcAmountGas==="bigint") &&
|
|
82
81
|
typeof obj.gasSwapFeeBtc==="bigint" &&
|
|
83
82
|
typeof obj.gasSwapFee==="bigint" &&
|
|
84
83
|
(obj.gasPricingInfo==null || isPriceInfoType(obj.gasPricingInfo)) &&
|
|
@@ -88,34 +87,65 @@ export function isFromBTCLNAutoSwapInit<T extends SwapData>(obj: any): obj is Fr
|
|
|
88
87
|
isIEscrowSwapInit(obj);
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
/**
|
|
91
|
+
* New escrow based (HTLC) swaps for Bitcoin Lightning -> Smart chain swaps not requiring manual settlement on
|
|
92
|
+
* the destination by the user, and instead letting the LP initiate the escrow. Permissionless watchtower network
|
|
93
|
+
* handles the claiming of HTLC, with the swap secret broadcasted over Nostr. Also adds a possibility for the user
|
|
94
|
+
* to receive a native token on the destination chain as part of the swap (a "gas drop" feature).
|
|
95
|
+
*
|
|
96
|
+
* @category Swaps/Lightning → Smart chain
|
|
97
|
+
*/
|
|
91
98
|
export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
92
99
|
extends IEscrowSwap<T, FromBTCLNAutoDefinition<T>>
|
|
93
100
|
implements IAddressSwap, ISwapWithGasDrop<T>, IClaimableSwap<T, FromBTCLNAutoDefinition<T>, FromBTCLNAutoSwapState> {
|
|
94
101
|
|
|
102
|
+
protected readonly TYPE: SwapType.FROM_BTCLN_AUTO = SwapType.FROM_BTCLN_AUTO;
|
|
103
|
+
/**
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
95
106
|
protected readonly logger: LoggerType;
|
|
107
|
+
/**
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
96
110
|
protected readonly inputToken: BtcToken<true> = BitcoinTokens.BTCLN;
|
|
97
|
-
protected readonly TYPE = SwapType.FROM_BTCLN_AUTO;
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
private readonly lnurlFailSignal: AbortController = new AbortController();
|
|
113
|
+
private readonly usesClaimHashAsId: boolean;
|
|
114
|
+
private readonly initialSwapData: T["Data"];
|
|
100
115
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
protected initialSwapData: T["Data"];
|
|
116
|
+
private readonly btcAmountSwap?: bigint;
|
|
117
|
+
private readonly btcAmountGas?: bigint;
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
private readonly gasSwapFeeBtc: bigint;
|
|
120
|
+
private readonly gasSwapFee: bigint;
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
protected readonly gasSwapFee: bigint;
|
|
110
|
-
gasPricingInfo?: PriceInfoType;
|
|
122
|
+
private readonly gasPricingInfo?: PriceInfoType;
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
/**
|
|
125
|
+
* In case the swap is recovered from on-chain data, the pr saved here is just a payment hash,
|
|
126
|
+
* as it is impossible to retrieve the actual lightning network invoice paid purely from on-chain
|
|
127
|
+
* data
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
private pr?: string;
|
|
131
|
+
private secret?: string;
|
|
116
132
|
|
|
117
|
-
|
|
118
|
-
|
|
133
|
+
private lnurl?: string;
|
|
134
|
+
private lnurlK1?: string;
|
|
135
|
+
private lnurlCallback?: string;
|
|
136
|
+
private prPosted?: boolean = false;
|
|
137
|
+
|
|
138
|
+
private broadcastTickCounter: number = 0;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Sets the LNURL data for the swap
|
|
142
|
+
*
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
_setLNURLData(lnurl: string, lnurlK1: string, lnurlCallback: string) {
|
|
146
|
+
this.lnurl = lnurl;
|
|
147
|
+
this.lnurlK1 = lnurlK1;
|
|
148
|
+
this.lnurlCallback = lnurlCallback;
|
|
119
149
|
}
|
|
120
150
|
|
|
121
151
|
constructor(wrapper: FromBTCLNAutoWrapper<T>, init: FromBTCLNAutoSwapInit<T["Data"]>);
|
|
@@ -127,7 +157,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
127
157
|
if(isFromBTCLNAutoSwapInit(initOrObject) && initOrObject.url!=null) initOrObject.url += "/frombtcln_auto";
|
|
128
158
|
super(wrapper, initOrObject);
|
|
129
159
|
if(isFromBTCLNAutoSwapInit(initOrObject)) {
|
|
130
|
-
this.
|
|
160
|
+
this._state = FromBTCLNAutoSwapState.PR_CREATED;
|
|
131
161
|
this.pr = initOrObject.pr;
|
|
132
162
|
this.secret = initOrObject.secret;
|
|
133
163
|
this.initialSwapData = initOrObject.initialSwapData;
|
|
@@ -139,12 +169,13 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
139
169
|
this.lnurl = initOrObject.lnurl;
|
|
140
170
|
this.lnurlK1 = initOrObject.lnurlK1;
|
|
141
171
|
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
172
|
+
this.usesClaimHashAsId = true;
|
|
142
173
|
} else {
|
|
143
174
|
this.pr = initOrObject.pr;
|
|
144
175
|
this.secret = initOrObject.secret;
|
|
145
176
|
|
|
146
177
|
if(initOrObject.initialSwapData==null) {
|
|
147
|
-
this.initialSwapData = this.
|
|
178
|
+
this.initialSwapData = this._data!;
|
|
148
179
|
} else {
|
|
149
180
|
this.initialSwapData = SwapData.deserialize<T["Data"]>(initOrObject.initialSwapData);
|
|
150
181
|
}
|
|
@@ -155,29 +186,42 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
155
186
|
this.gasSwapFee = toBigInt(initOrObject.gasSwapFee);
|
|
156
187
|
this.gasPricingInfo = deserializePriceInfoType(initOrObject.gasPricingInfo);
|
|
157
188
|
|
|
158
|
-
this.
|
|
159
|
-
this.
|
|
189
|
+
this._commitTxId = initOrObject.commitTxId;
|
|
190
|
+
this._claimTxId = initOrObject.claimTxId;
|
|
160
191
|
|
|
161
192
|
this.lnurl = initOrObject.lnurl;
|
|
162
193
|
this.lnurlK1 = initOrObject.lnurlK1;
|
|
163
194
|
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
164
195
|
this.prPosted = initOrObject.prPosted;
|
|
196
|
+
this.usesClaimHashAsId = initOrObject.usesClaimHashAsId ?? false;
|
|
165
197
|
}
|
|
166
198
|
this.tryRecomputeSwapPrice();
|
|
167
199
|
this.logger = getLogger("FromBTCLNAuto("+this.getIdentifierHashString()+"): ");
|
|
168
200
|
}
|
|
169
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @inheritDoc
|
|
204
|
+
* @internal
|
|
205
|
+
*/
|
|
206
|
+
protected getSwapData(): T["Data"] {
|
|
207
|
+
return this._data ?? this.initialSwapData;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @inheritDoc
|
|
212
|
+
* @internal
|
|
213
|
+
*/
|
|
170
214
|
protected upgradeVersion() { /*NOOP*/ }
|
|
171
215
|
|
|
172
216
|
/**
|
|
173
|
-
*
|
|
174
|
-
* @
|
|
217
|
+
* @inheritDoc
|
|
218
|
+
* @internal
|
|
175
219
|
*/
|
|
176
220
|
protected tryRecomputeSwapPrice() {
|
|
177
|
-
if(this.pricingInfo==null) return;
|
|
221
|
+
if(this.pricingInfo==null || this.btcAmountSwap==null) return;
|
|
178
222
|
if(this.pricingInfo.swapPriceUSatPerToken==null) {
|
|
179
223
|
const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
180
|
-
this.pricingInfo = this.wrapper.
|
|
224
|
+
this.pricingInfo = this.wrapper._prices.recomputePriceInfoReceive(
|
|
181
225
|
this.chainIdentifier,
|
|
182
226
|
this.btcAmountSwap,
|
|
183
227
|
this.pricingInfo.satsBaseFee,
|
|
@@ -193,10 +237,13 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
193
237
|
//////////////////////////////
|
|
194
238
|
//// Pricing
|
|
195
239
|
|
|
240
|
+
/**
|
|
241
|
+
* @inheritDoc
|
|
242
|
+
*/
|
|
196
243
|
async refreshPriceData(): Promise<void> {
|
|
197
|
-
if(this.pricingInfo==null) return;
|
|
244
|
+
if(this.pricingInfo==null || this.btcAmountSwap==null) return;
|
|
198
245
|
const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
199
|
-
this.pricingInfo = await this.wrapper.
|
|
246
|
+
this.pricingInfo = await this.wrapper._prices.isValidAmountReceive(
|
|
200
247
|
this.chainIdentifier,
|
|
201
248
|
this.btcAmountSwap,
|
|
202
249
|
this.pricingInfo.satsBaseFee,
|
|
@@ -211,60 +258,107 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
211
258
|
//////////////////////////////
|
|
212
259
|
//// Getters & utils
|
|
213
260
|
|
|
261
|
+
/**
|
|
262
|
+
* @inheritDoc
|
|
263
|
+
* @internal
|
|
264
|
+
*/
|
|
214
265
|
_getEscrowHash(): string | null {
|
|
215
266
|
//Use claim hash in case the data is not yet known
|
|
216
|
-
return this.
|
|
267
|
+
return this._data == null ? this.initialSwapData?.getClaimHash() : this._data?.getEscrowHash();
|
|
217
268
|
}
|
|
218
269
|
|
|
270
|
+
/**
|
|
271
|
+
* @inheritDoc
|
|
272
|
+
* @internal
|
|
273
|
+
*/
|
|
219
274
|
_getInitiator(): string {
|
|
220
275
|
return this.getSwapData().getClaimer();
|
|
221
276
|
}
|
|
222
277
|
|
|
278
|
+
/**
|
|
279
|
+
* @inheritDoc
|
|
280
|
+
*/
|
|
223
281
|
getId(): string {
|
|
224
282
|
return this.getIdentifierHashString();
|
|
225
283
|
}
|
|
226
284
|
|
|
285
|
+
/**
|
|
286
|
+
* @inheritDoc
|
|
287
|
+
*/
|
|
227
288
|
getOutputAddress(): string | null {
|
|
228
289
|
return this._getInitiator();
|
|
229
290
|
}
|
|
230
291
|
|
|
292
|
+
/**
|
|
293
|
+
* @inheritDoc
|
|
294
|
+
*/
|
|
231
295
|
getOutputTxId(): string | null {
|
|
232
|
-
return this.
|
|
296
|
+
return this._claimTxId ?? null;
|
|
233
297
|
}
|
|
234
298
|
|
|
299
|
+
/**
|
|
300
|
+
* @inheritDoc
|
|
301
|
+
*/
|
|
235
302
|
requiresAction(): boolean {
|
|
236
|
-
return this.
|
|
303
|
+
return this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED;
|
|
237
304
|
}
|
|
238
305
|
|
|
306
|
+
/**
|
|
307
|
+
* @inheritDoc
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
239
310
|
protected getIdentifierHashString(): string {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
311
|
+
const id: string = this.usesClaimHashAsId
|
|
312
|
+
? this.getClaimHash()
|
|
313
|
+
: this.getPaymentHash()!.toString("hex");
|
|
314
|
+
if(this._randomNonce==null) return id;
|
|
315
|
+
return id + this._randomNonce;
|
|
243
316
|
}
|
|
244
317
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Returns the payment hash of the swap and lightning network invoice, or `null` if not known (i.e. if
|
|
320
|
+
* the swap was recovered from on-chain data, the payment hash might not be known)
|
|
321
|
+
*
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
324
|
+
protected getPaymentHash(): Buffer | null {
|
|
325
|
+
if(this.pr==null) return null;
|
|
326
|
+
if(this.pr.toLowerCase().startsWith("ln")) {
|
|
327
|
+
const parsed = bolt11Decode(this.pr);
|
|
328
|
+
if(parsed.tagsObject.payment_hash==null) throw new Error("Swap invoice has no payment hash field!");
|
|
329
|
+
return Buffer.from(parsed.tagsObject.payment_hash, "hex");
|
|
330
|
+
}
|
|
331
|
+
return Buffer.from(this.pr, "hex");
|
|
249
332
|
}
|
|
250
333
|
|
|
334
|
+
/**
|
|
335
|
+
* @inheritDoc
|
|
336
|
+
*/
|
|
251
337
|
getInputAddress(): string | null {
|
|
252
|
-
return this.lnurl ?? this.pr;
|
|
338
|
+
return this.lnurl ?? this.pr ?? null;
|
|
253
339
|
}
|
|
254
340
|
|
|
255
|
-
|
|
256
|
-
|
|
341
|
+
/**
|
|
342
|
+
* @inheritDoc
|
|
343
|
+
*/
|
|
344
|
+
getInputTxId(): string | null {
|
|
345
|
+
const paymentHash = this.getPaymentHash();
|
|
346
|
+
if(paymentHash==null) return null;
|
|
347
|
+
return paymentHash.toString("hex");
|
|
257
348
|
}
|
|
258
349
|
|
|
259
350
|
/**
|
|
260
351
|
* Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap
|
|
261
352
|
*/
|
|
262
353
|
getAddress(): string {
|
|
263
|
-
return this.pr;
|
|
354
|
+
return this.pr ?? "";
|
|
264
355
|
}
|
|
265
356
|
|
|
357
|
+
/**
|
|
358
|
+
* @inheritDoc
|
|
359
|
+
*/
|
|
266
360
|
getHyperlink(): string {
|
|
267
|
-
return "lightning:"+this.pr.toUpperCase();
|
|
361
|
+
return this.pr==null ? "" : "lightning:"+this.pr.toUpperCase();
|
|
268
362
|
}
|
|
269
363
|
|
|
270
364
|
/**
|
|
@@ -272,11 +366,12 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
272
366
|
* if the LP doesn't make it expired sooner
|
|
273
367
|
*/
|
|
274
368
|
getDefinitiveExpiryTime(): number {
|
|
369
|
+
if(this.pr==null || !this.pr.toLowerCase().startsWith("ln")) return 0;
|
|
275
370
|
const decoded = bolt11Decode(this.pr);
|
|
276
371
|
if(decoded.tagsObject.min_final_cltv_expiry==null) throw new Error("Swap invoice doesn't contain final ctlv delta field!");
|
|
277
372
|
if(decoded.timeExpireDate==null) throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
278
373
|
const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
|
|
279
|
-
const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper.
|
|
374
|
+
const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper._options.bitcoinBlocktime * this.wrapper._options.safetyFactor;
|
|
280
375
|
return (decoded.timeExpireDate + finalCltvExpiryDelay)*1000;
|
|
281
376
|
}
|
|
282
377
|
|
|
@@ -284,38 +379,62 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
284
379
|
* Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
|
|
285
380
|
*/
|
|
286
381
|
getHtlcTimeoutTime(): number | null {
|
|
287
|
-
return this.
|
|
382
|
+
return this._data==null ? null : Number(this.wrapper._getHtlcTimeout(this._data))*1000;
|
|
288
383
|
}
|
|
289
384
|
|
|
385
|
+
/**
|
|
386
|
+
* @inheritDoc
|
|
387
|
+
*/
|
|
290
388
|
isFinished(): boolean {
|
|
291
|
-
return this.
|
|
389
|
+
return this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED || this._state===FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state===FromBTCLNAutoSwapState.FAILED;
|
|
292
390
|
}
|
|
293
391
|
|
|
392
|
+
/**
|
|
393
|
+
* @inheritDoc
|
|
394
|
+
*/
|
|
294
395
|
isClaimable(): boolean {
|
|
295
|
-
return this.
|
|
396
|
+
return this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED;
|
|
296
397
|
}
|
|
297
398
|
|
|
399
|
+
/**
|
|
400
|
+
* @inheritDoc
|
|
401
|
+
*/
|
|
298
402
|
isSuccessful(): boolean {
|
|
299
|
-
return this.
|
|
403
|
+
return this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED;
|
|
300
404
|
}
|
|
301
405
|
|
|
406
|
+
/**
|
|
407
|
+
* @inheritDoc
|
|
408
|
+
*/
|
|
302
409
|
isFailed(): boolean {
|
|
303
|
-
return this.
|
|
410
|
+
return this._state===FromBTCLNAutoSwapState.FAILED || this._state===FromBTCLNAutoSwapState.EXPIRED;
|
|
304
411
|
}
|
|
305
412
|
|
|
413
|
+
/**
|
|
414
|
+
* @inheritDoc
|
|
415
|
+
*/
|
|
306
416
|
isQuoteExpired(): boolean {
|
|
307
|
-
return this.
|
|
417
|
+
return this._state===FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
308
418
|
}
|
|
309
419
|
|
|
420
|
+
/**
|
|
421
|
+
* @inheritDoc
|
|
422
|
+
*/
|
|
310
423
|
isQuoteSoftExpired(): boolean {
|
|
311
|
-
return this.
|
|
424
|
+
return this._state===FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
312
425
|
}
|
|
313
426
|
|
|
427
|
+
/**
|
|
428
|
+
* @inheritDoc
|
|
429
|
+
*/
|
|
314
430
|
_verifyQuoteDefinitelyExpired(): Promise<boolean> {
|
|
315
431
|
return Promise.resolve(this.getDefinitiveExpiryTime()<Date.now());
|
|
316
432
|
}
|
|
317
433
|
|
|
318
|
-
|
|
434
|
+
/**
|
|
435
|
+
* @inheritDoc
|
|
436
|
+
*/
|
|
437
|
+
_verifyQuoteValid(): Promise<boolean> {
|
|
319
438
|
return Promise.resolve(this.getQuoteExpiry()>Date.now());
|
|
320
439
|
}
|
|
321
440
|
|
|
@@ -323,119 +442,194 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
323
442
|
//////////////////////////////
|
|
324
443
|
//// Amounts & fees
|
|
325
444
|
|
|
326
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Returns the satoshi amount of the lightning network invoice, or `null` if the lightning network
|
|
447
|
+
* invoice is not known (i.e. when the swap was recovered from on-chain data, the paid invoice
|
|
448
|
+
* cannot be recovered because it is purely off-chain)
|
|
449
|
+
*
|
|
450
|
+
* @internal
|
|
451
|
+
*/
|
|
452
|
+
protected getLightningInvoiceSats(): bigint | null {
|
|
453
|
+
if(this.pr==null || !this.pr.toLowerCase().startsWith("ln")) return null;
|
|
454
|
+
|
|
327
455
|
const parsed = bolt11Decode(this.pr);
|
|
328
456
|
if(parsed.millisatoshis==null) throw new Error("Swap invoice doesn't contain msat amount field!");
|
|
329
457
|
return (BigInt(parsed.millisatoshis) + 999n) / 1000n;
|
|
330
458
|
}
|
|
331
459
|
|
|
332
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Returns the watchtower fee paid in BTC satoshis, or null if known (i.e. if the swap was recovered from
|
|
462
|
+
* on-chain data)
|
|
463
|
+
*
|
|
464
|
+
* @protected
|
|
465
|
+
*/
|
|
466
|
+
protected getWatchtowerFeeAmountBtc(): bigint | null {
|
|
467
|
+
if(this.btcAmountGas==null) return null;
|
|
333
468
|
return (this.btcAmountGas - this.gasSwapFeeBtc) * this.getSwapData().getClaimerBounty() / this.getSwapData().getTotalDeposit();
|
|
334
469
|
}
|
|
335
470
|
|
|
336
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Returns the input amount for the actual swap (excluding the input amount used to cover the "gas drop"
|
|
473
|
+
* part of the swap), excluding fees
|
|
474
|
+
*
|
|
475
|
+
* @internal
|
|
476
|
+
*/
|
|
477
|
+
protected getInputSwapAmountWithoutFee(): bigint | null {
|
|
478
|
+
if(this.btcAmountSwap==null) return null;
|
|
337
479
|
return this.btcAmountSwap - this.swapFeeBtc;
|
|
338
480
|
}
|
|
339
481
|
|
|
340
|
-
|
|
482
|
+
/**
|
|
483
|
+
* Returns the input amount purely for the "gas drop" part of the swap (this much BTC in sats will be
|
|
484
|
+
* swapped into the native gas token on the destination chain), excluding fees
|
|
485
|
+
*
|
|
486
|
+
* @internal
|
|
487
|
+
*/
|
|
488
|
+
protected getInputGasAmountWithoutFee(): bigint | null {
|
|
489
|
+
if(this.btcAmountGas==null) return null;
|
|
341
490
|
return this.btcAmountGas - this.gasSwapFeeBtc;
|
|
342
491
|
}
|
|
343
492
|
|
|
344
|
-
|
|
345
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Get total btc amount in sats on the input, excluding the swap fee and watchtower fee
|
|
495
|
+
*
|
|
496
|
+
* @internal
|
|
497
|
+
*/
|
|
498
|
+
protected getInputAmountWithoutFee(): bigint | null {
|
|
499
|
+
if(this.btcAmountGas==null || this.btcAmountSwap) return null;
|
|
500
|
+
return this.getInputSwapAmountWithoutFee()! + this.getInputGasAmountWithoutFee()! - this.getWatchtowerFeeAmountBtc()!;
|
|
346
501
|
}
|
|
347
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Returns the "would be" output amount if the swap charged no swap fee
|
|
505
|
+
*
|
|
506
|
+
* @internal
|
|
507
|
+
*/
|
|
348
508
|
protected getOutputAmountWithoutFee(): bigint {
|
|
349
509
|
return this.getSwapData().getAmount() + this.swapFee;
|
|
350
510
|
}
|
|
351
511
|
|
|
512
|
+
/**
|
|
513
|
+
* @inheritDoc
|
|
514
|
+
*/
|
|
352
515
|
getInputToken(): BtcToken<true> {
|
|
353
516
|
return BitcoinTokens.BTCLN;
|
|
354
517
|
}
|
|
355
518
|
|
|
519
|
+
/**
|
|
520
|
+
* @inheritDoc
|
|
521
|
+
*/
|
|
356
522
|
getInput(): TokenAmount<T["ChainId"], BtcToken<true>> {
|
|
357
|
-
return toTokenAmount(this.getLightningInvoiceSats(), this.inputToken, this.wrapper.
|
|
523
|
+
return toTokenAmount(this.getLightningInvoiceSats(), this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
358
524
|
}
|
|
359
525
|
|
|
526
|
+
/**
|
|
527
|
+
* @inheritDoc
|
|
528
|
+
*/
|
|
360
529
|
getInputWithoutFee(): TokenAmount<T["ChainId"], BtcToken<true>> {
|
|
361
|
-
return toTokenAmount(this.getInputAmountWithoutFee(), this.inputToken, this.wrapper.
|
|
530
|
+
return toTokenAmount(this.getInputAmountWithoutFee(), this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
362
531
|
}
|
|
363
532
|
|
|
533
|
+
/**
|
|
534
|
+
* @inheritDoc
|
|
535
|
+
*/
|
|
364
536
|
getOutputToken(): SCToken<T["ChainId"]> {
|
|
365
|
-
return this.wrapper.
|
|
537
|
+
return this.wrapper._tokens[this.getSwapData().getToken()];
|
|
366
538
|
}
|
|
367
539
|
|
|
540
|
+
/**
|
|
541
|
+
* @inheritDoc
|
|
542
|
+
*/
|
|
368
543
|
getOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
|
|
369
|
-
return toTokenAmount(this.getSwapData().getAmount(), this.wrapper.
|
|
544
|
+
return toTokenAmount(this.getSwapData().getAmount(), this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo);
|
|
370
545
|
}
|
|
371
546
|
|
|
547
|
+
/**
|
|
548
|
+
* @inheritDoc
|
|
549
|
+
*/
|
|
372
550
|
getGasDropOutput(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
|
|
373
551
|
return toTokenAmount(
|
|
374
552
|
this.getSwapData().getSecurityDeposit() - this.getSwapData().getClaimerBounty(),
|
|
375
|
-
this.wrapper.
|
|
553
|
+
this.wrapper._tokens[this.getSwapData().getDepositToken()], this.wrapper._prices, this.gasPricingInfo
|
|
376
554
|
);
|
|
377
555
|
}
|
|
378
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Returns the swap fee charged by the intermediary (LP) on this swap
|
|
559
|
+
*
|
|
560
|
+
* @internal
|
|
561
|
+
*/
|
|
379
562
|
protected getSwapFee(): Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>> {
|
|
380
563
|
if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
|
|
381
564
|
|
|
382
|
-
const outputToken = this.wrapper.
|
|
565
|
+
const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
|
|
383
566
|
const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
|
|
384
567
|
* (10n ** BigInt(outputToken.decimals))
|
|
385
568
|
* 1_000_000n
|
|
386
569
|
/ this.pricingInfo.swapPriceUSatPerToken;
|
|
387
570
|
|
|
388
571
|
const feeWithoutBaseFee = this.gasSwapFeeBtc + this.swapFeeBtc - this.pricingInfo.satsBaseFee;
|
|
389
|
-
const
|
|
572
|
+
const inputSats = this.getLightningInvoiceSats();
|
|
573
|
+
const swapFeePPM = inputSats!=null
|
|
574
|
+
? feeWithoutBaseFee * 1000000n / (inputSats - this.swapFeeBtc - this.gasSwapFeeBtc)
|
|
575
|
+
: 0n;
|
|
390
576
|
|
|
391
|
-
const amountInSrcToken = toTokenAmount(this.swapFeeBtc + this.gasSwapFeeBtc, BitcoinTokens.BTCLN, this.wrapper.
|
|
577
|
+
const amountInSrcToken = toTokenAmount(this.swapFeeBtc + this.gasSwapFeeBtc, BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
|
|
392
578
|
return {
|
|
393
579
|
amountInSrcToken,
|
|
394
|
-
amountInDstToken: toTokenAmount(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper.
|
|
580
|
+
amountInDstToken: toTokenAmount(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
|
|
395
581
|
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
396
582
|
pastUsdValue: amountInSrcToken.pastUsdValue,
|
|
397
583
|
usdValue: amountInSrcToken.usdValue,
|
|
398
584
|
composition: {
|
|
399
|
-
base: toTokenAmount(this.pricingInfo.satsBaseFee, BitcoinTokens.BTCLN, this.wrapper.
|
|
585
|
+
base: toTokenAmount(this.pricingInfo.satsBaseFee, BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo),
|
|
400
586
|
percentage: ppmToPercentage(swapFeePPM)
|
|
401
587
|
}
|
|
402
588
|
};
|
|
403
589
|
}
|
|
404
590
|
|
|
591
|
+
/**
|
|
592
|
+
* Returns the fee to be paid to watchtowers on the destination chain to automatically
|
|
593
|
+
* process and settle this swap without requiring any user interaction
|
|
594
|
+
*
|
|
595
|
+
* @internal
|
|
596
|
+
*/
|
|
405
597
|
protected getWatchtowerFee(): Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>> {
|
|
406
598
|
if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
|
|
407
599
|
|
|
408
600
|
const btcWatchtowerFee = this.getWatchtowerFeeAmountBtc();
|
|
409
|
-
const outputToken = this.wrapper.
|
|
410
|
-
const watchtowerFeeInOutputToken = btcWatchtowerFee
|
|
601
|
+
const outputToken = this.wrapper._tokens[this.getSwapData().getToken()];
|
|
602
|
+
const watchtowerFeeInOutputToken = btcWatchtowerFee==null ? 0n : btcWatchtowerFee
|
|
411
603
|
* (10n ** BigInt(outputToken.decimals))
|
|
412
604
|
* 1_000_000n
|
|
413
605
|
/ this.pricingInfo.swapPriceUSatPerToken;
|
|
414
606
|
|
|
415
|
-
const amountInSrcToken = toTokenAmount(btcWatchtowerFee, BitcoinTokens.BTCLN, this.wrapper.
|
|
607
|
+
const amountInSrcToken = toTokenAmount(btcWatchtowerFee, BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo);
|
|
416
608
|
return {
|
|
417
609
|
amountInSrcToken,
|
|
418
|
-
amountInDstToken: toTokenAmount(watchtowerFeeInOutputToken, outputToken, this.wrapper.
|
|
610
|
+
amountInDstToken: toTokenAmount(watchtowerFeeInOutputToken, outputToken, this.wrapper._prices, this.pricingInfo),
|
|
419
611
|
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
420
612
|
usdValue: amountInSrcToken.usdValue,
|
|
421
613
|
pastUsdValue: amountInSrcToken.pastUsdValue
|
|
422
614
|
};
|
|
423
615
|
}
|
|
424
616
|
|
|
425
|
-
|
|
617
|
+
/**
|
|
618
|
+
* @inheritDoc
|
|
619
|
+
*/
|
|
426
620
|
getFee(): Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>> {
|
|
427
621
|
const swapFee = this.getSwapFee();
|
|
428
622
|
const watchtowerFee = this.getWatchtowerFee();
|
|
429
623
|
|
|
430
624
|
const amountInSrcToken = toTokenAmount(
|
|
431
625
|
swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount,
|
|
432
|
-
BitcoinTokens.BTCLN, this.wrapper.
|
|
626
|
+
BitcoinTokens.BTCLN, this.wrapper._prices, this.pricingInfo
|
|
433
627
|
);
|
|
434
628
|
return {
|
|
435
629
|
amountInSrcToken,
|
|
436
630
|
amountInDstToken: toTokenAmount(
|
|
437
631
|
swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount,
|
|
438
|
-
this.wrapper.
|
|
632
|
+
this.wrapper._tokens[this.getSwapData().getToken()], this.wrapper._prices, this.pricingInfo
|
|
439
633
|
),
|
|
440
634
|
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
441
635
|
usdValue: amountInSrcToken.usdValue,
|
|
@@ -443,6 +637,9 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
443
637
|
};
|
|
444
638
|
}
|
|
445
639
|
|
|
640
|
+
/**
|
|
641
|
+
* @inheritDoc
|
|
642
|
+
*/
|
|
446
643
|
getFeeBreakdown(): [
|
|
447
644
|
{type: FeeType.SWAP, fee: Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>>},
|
|
448
645
|
{type: FeeType.NETWORK_OUTPUT, fee: Fee<T["ChainId"], BtcToken<true>, SCToken<T["ChainId"]>>}
|
|
@@ -459,6 +656,12 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
459
656
|
];
|
|
460
657
|
}
|
|
461
658
|
|
|
659
|
+
private isValidSecretPreimage(secret: string) {
|
|
660
|
+
const paymentHash = Buffer.from(sha256(Buffer.from(secret, "hex")));
|
|
661
|
+
const claimHash = this.wrapper._contract.getHashForHtlc(paymentHash).toString("hex");
|
|
662
|
+
return this.getSwapData().getClaimHash()===claimHash;
|
|
663
|
+
}
|
|
664
|
+
|
|
462
665
|
|
|
463
666
|
//////////////////////////////
|
|
464
667
|
//// Execution
|
|
@@ -470,6 +673,8 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
470
673
|
* link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
|
|
471
674
|
* @param callbacks Callbacks to track the progress of the swap
|
|
472
675
|
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
676
|
+
* @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
|
|
677
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
473
678
|
*
|
|
474
679
|
* @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
|
|
475
680
|
* user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
|
|
@@ -484,17 +689,21 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
484
689
|
abortSignal?: AbortSignal,
|
|
485
690
|
lightningTxCheckIntervalSeconds?: number,
|
|
486
691
|
maxWaitTillAutomaticSettlementSeconds?: number
|
|
487
|
-
}
|
|
692
|
+
},
|
|
693
|
+
secret?: string
|
|
488
694
|
): Promise<boolean> {
|
|
489
|
-
if(this.
|
|
490
|
-
if(this.
|
|
491
|
-
if(this.
|
|
492
|
-
if(this.
|
|
695
|
+
if(this._state===FromBTCLNAutoSwapState.FAILED) throw new Error("Swap failed!");
|
|
696
|
+
if(this._state===FromBTCLNAutoSwapState.EXPIRED) throw new Error("Swap HTLC expired!");
|
|
697
|
+
if(this._state===FromBTCLNAutoSwapState.QUOTE_EXPIRED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Swap quote expired!");
|
|
698
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED) throw new Error("Swap already settled!");
|
|
493
699
|
|
|
494
700
|
let abortSignal = options?.abortSignal;
|
|
495
701
|
|
|
496
|
-
if(this.
|
|
702
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED) {
|
|
497
703
|
if(walletOrLnurlWithdraw!=null && this.lnurl==null) {
|
|
704
|
+
if(this.pr==null || !this.pr.toLowerCase().startsWith("ln"))
|
|
705
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
706
|
+
|
|
498
707
|
if(typeof(walletOrLnurlWithdraw)==="string" || isLNURLWithdraw(walletOrLnurlWithdraw)) {
|
|
499
708
|
await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
|
|
500
709
|
} else {
|
|
@@ -508,15 +717,17 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
508
717
|
}
|
|
509
718
|
}
|
|
510
719
|
|
|
511
|
-
if(this.
|
|
720
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.PR_PAID) {
|
|
512
721
|
const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
|
|
513
722
|
if (!paymentSuccess) throw new Error("Failed to receive lightning network payment");
|
|
514
723
|
}
|
|
515
724
|
|
|
516
|
-
if((this.
|
|
725
|
+
if((this._state as FromBTCLNAutoSwapState)===FromBTCLNAutoSwapState.CLAIM_CLAIMED) return true;
|
|
517
726
|
|
|
518
|
-
if(this.
|
|
519
|
-
|
|
727
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED) {
|
|
728
|
+
if(this.secret==null && secret==null)
|
|
729
|
+
throw new Error("Tried to wait till settlement, but no secret pre-image is known, please pass the secret pre-image as an argument!");
|
|
730
|
+
const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal, secret);
|
|
520
731
|
if (success && callbacks?.onSwapSettled != null) callbacks.onSwapSettled(this.getOutputTxId()!);
|
|
521
732
|
return success;
|
|
522
733
|
}
|
|
@@ -524,17 +735,21 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
524
735
|
throw new Error("Invalid state reached!");
|
|
525
736
|
}
|
|
526
737
|
|
|
738
|
+
/**
|
|
739
|
+
* @inheritDoc
|
|
740
|
+
*/
|
|
527
741
|
async txsExecute() {
|
|
528
|
-
if (this.
|
|
529
|
-
if (!await this.
|
|
742
|
+
if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
|
|
743
|
+
if (!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
|
|
530
744
|
return [
|
|
531
745
|
{
|
|
532
746
|
name: "Payment" as const,
|
|
533
747
|
description: "Initiates the swap by paying up the lightning network invoice",
|
|
534
|
-
chain: "LIGHTNING",
|
|
748
|
+
chain: "LIGHTNING" as const,
|
|
535
749
|
txs: [
|
|
536
750
|
{
|
|
537
|
-
|
|
751
|
+
type: "BOLT11_PAYMENT_REQUEST" as const,
|
|
752
|
+
address: this.getAddress(),
|
|
538
753
|
hyperlink: this.getHyperlink()
|
|
539
754
|
}
|
|
540
755
|
]
|
|
@@ -550,30 +765,38 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
550
765
|
//// Payment
|
|
551
766
|
|
|
552
767
|
/**
|
|
553
|
-
* Checks whether the LP received the LN payment
|
|
768
|
+
* Checks whether the LP received the LN payment
|
|
554
769
|
*
|
|
555
770
|
* @param save If the new swap state should be saved
|
|
771
|
+
*
|
|
772
|
+
* @internal
|
|
556
773
|
*/
|
|
557
774
|
async _checkIntermediaryPaymentReceived(save: boolean = true): Promise<boolean | null> {
|
|
558
775
|
if(
|
|
559
|
-
this.
|
|
560
|
-
this.
|
|
561
|
-
this.
|
|
562
|
-
this.
|
|
776
|
+
this._state===FromBTCLNAutoSwapState.PR_PAID ||
|
|
777
|
+
this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED ||
|
|
778
|
+
this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED ||
|
|
779
|
+
this._state===FromBTCLNAutoSwapState.FAILED ||
|
|
780
|
+
this._state===FromBTCLNAutoSwapState.EXPIRED
|
|
563
781
|
) return true;
|
|
564
|
-
if(this.
|
|
782
|
+
if(this._state===FromBTCLNAutoSwapState.QUOTE_EXPIRED) return false;
|
|
565
783
|
if(this.url==null) return false;
|
|
566
|
-
|
|
784
|
+
|
|
785
|
+
const paymentHash = this.getPaymentHash();
|
|
786
|
+
if(paymentHash==null)
|
|
787
|
+
throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
|
|
788
|
+
|
|
789
|
+
const resp = await IntermediaryAPI.getInvoiceStatus(this.url, paymentHash.toString("hex"));
|
|
567
790
|
switch(resp.code) {
|
|
568
791
|
case InvoiceStatusResponseCodes.PAID:
|
|
569
|
-
const data = new this.wrapper.
|
|
570
|
-
if(this.
|
|
792
|
+
const data = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
793
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) try {
|
|
571
794
|
await this._saveRealSwapData(data, save);
|
|
572
795
|
return true;
|
|
573
796
|
} catch (e) {}
|
|
574
797
|
return null;
|
|
575
798
|
case InvoiceStatusResponseCodes.EXPIRED:
|
|
576
|
-
this.
|
|
799
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
577
800
|
this.initiated = true;
|
|
578
801
|
if(save) await this._saveAndEmit();
|
|
579
802
|
return false;
|
|
@@ -582,11 +805,20 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
582
805
|
}
|
|
583
806
|
}
|
|
584
807
|
|
|
808
|
+
/**
|
|
809
|
+
* Checks and overrides the swap data for this swap. This is used to set the swap data from
|
|
810
|
+
* on-chain events.
|
|
811
|
+
*
|
|
812
|
+
* @param data Swap data of the escrow swap
|
|
813
|
+
* @param save If the new data should be saved
|
|
814
|
+
*
|
|
815
|
+
* @internal
|
|
816
|
+
*/
|
|
585
817
|
async _saveRealSwapData(data: T["Data"], save?: boolean): Promise<boolean> {
|
|
586
818
|
await this.checkIntermediaryReturnedData(data);
|
|
587
|
-
if(this.
|
|
588
|
-
this.
|
|
589
|
-
this.
|
|
819
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
|
|
820
|
+
this._state = FromBTCLNAutoSwapState.PR_PAID;
|
|
821
|
+
this._data = data;
|
|
590
822
|
this.initiated = true;
|
|
591
823
|
if(save) await this._saveAndEmit();
|
|
592
824
|
return true;
|
|
@@ -598,11 +830,13 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
598
830
|
* Checks the data returned by the intermediary in the payment auth request
|
|
599
831
|
*
|
|
600
832
|
* @param data Parsed swap data as returned by the intermediary
|
|
601
|
-
*
|
|
833
|
+
*
|
|
602
834
|
* @throws {IntermediaryError} If the returned are not valid
|
|
603
835
|
* @throws {Error} If the swap is already committed on-chain
|
|
836
|
+
*
|
|
837
|
+
* @private
|
|
604
838
|
*/
|
|
605
|
-
|
|
839
|
+
private async checkIntermediaryReturnedData(data: T["Data"]): Promise<void> {
|
|
606
840
|
if (!data.isPayOut()) throw new IntermediaryError("Invalid not pay out");
|
|
607
841
|
if (data.getType() !== ChainSwapType.HTLC) throw new IntermediaryError("Invalid swap type");
|
|
608
842
|
if (!data.isOfferer(this.getSwapData().getOfferer())) throw new IntermediaryError("Invalid offerer used");
|
|
@@ -615,25 +849,31 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
615
849
|
if (!data.isDepositToken(this.getSwapData().getDepositToken())) throw new IntermediaryError("Invalid deposit token used!");
|
|
616
850
|
if (data.hasSuccessAction()) throw new IntermediaryError("Invalid has success action");
|
|
617
851
|
|
|
618
|
-
if (await this.wrapper.
|
|
619
|
-
if (this.wrapper.
|
|
852
|
+
if (await this.wrapper._contract.isExpired(this._getInitiator(), data)) throw new IntermediaryError("Not enough time to claim!");
|
|
853
|
+
if (this.wrapper._getHtlcTimeout(data) <= (Date.now()/1000)) throw new IntermediaryError("HTLC expires too soon!");
|
|
620
854
|
}
|
|
621
855
|
|
|
622
856
|
/**
|
|
623
|
-
* Waits till
|
|
857
|
+
* Waits till a lightning network payment is received by the intermediary, and the intermediary
|
|
858
|
+
* initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
|
|
859
|
+
* for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
|
|
860
|
+
* or settle manually using the {@link claim} or {@link txsClaim} functions.
|
|
861
|
+
*
|
|
862
|
+
* If this swap is using an LNURL-withdraw link as input, it automatically posts the
|
|
863
|
+
* generated invoice to the LNURL service to pay it.
|
|
624
864
|
*
|
|
625
865
|
* @param onPaymentReceived Callback as for when the LP reports having received the ln payment
|
|
626
|
-
* @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
|
|
627
866
|
* @param abortSignal Abort signal to stop waiting for payment
|
|
867
|
+
* @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
|
|
628
868
|
*/
|
|
629
869
|
async waitForPayment(onPaymentReceived?: (txId: string) => void, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
630
870
|
checkIntervalSeconds ??= 5;
|
|
631
|
-
if(this.
|
|
871
|
+
if(this._state===FromBTCLNAutoSwapState.PR_PAID) {
|
|
632
872
|
await this.waitTillCommited(checkIntervalSeconds, abortSignal);
|
|
633
873
|
}
|
|
634
|
-
if(this.
|
|
874
|
+
if(this._state>=FromBTCLNAutoSwapState.CLAIM_COMMITED) return true;
|
|
635
875
|
if(
|
|
636
|
-
this.
|
|
876
|
+
this._state!==FromBTCLNAutoSwapState.PR_CREATED
|
|
637
877
|
) throw new Error("Must be in PR_CREATED state!");
|
|
638
878
|
|
|
639
879
|
const abortController = new AbortController();
|
|
@@ -642,6 +882,9 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
642
882
|
let save = false;
|
|
643
883
|
|
|
644
884
|
if(this.lnurl!=null && this.lnurlK1!=null && this.lnurlCallback!=null && !this.prPosted) {
|
|
885
|
+
if(this.pr==null || !this.pr.toLowerCase().startsWith("ln"))
|
|
886
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
887
|
+
|
|
645
888
|
LNURL.postInvoiceToLNURLWithdraw({k1: this.lnurlK1, callback: this.lnurlCallback}, this.pr).catch(e => {
|
|
646
889
|
this.lnurlFailSignal.abort(e);
|
|
647
890
|
});
|
|
@@ -660,18 +903,22 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
660
903
|
this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
|
|
661
904
|
this.lnurlFailSignal.signal.throwIfAborted();
|
|
662
905
|
|
|
663
|
-
|
|
906
|
+
const paymentHash = this.getPaymentHash();
|
|
907
|
+
if(paymentHash==null)
|
|
908
|
+
throw new Error("Swap payment hash not available, the swap was probably recovered!");
|
|
909
|
+
|
|
910
|
+
if(this.wrapper._messenger.warmup!=null) await this.wrapper._messenger.warmup().catch(e => {
|
|
664
911
|
this.logger.warn("waitForPayment(): Failed to warmup messenger: ", e);
|
|
665
912
|
});
|
|
666
913
|
|
|
667
|
-
if(this.
|
|
914
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED) {
|
|
668
915
|
const promises: Promise<boolean | undefined>[] = [
|
|
669
916
|
this.waitTillState(FromBTCLNAutoSwapState.PR_PAID, "gte", abortController.signal).then(() => true)
|
|
670
917
|
];
|
|
671
918
|
if(this.url!=null) promises.push((async () => {
|
|
672
919
|
let resp: InvoiceStatusResponse = {code: InvoiceStatusResponseCodes.PENDING, msg: ""};
|
|
673
920
|
while(!abortController.signal.aborted && resp.code===InvoiceStatusResponseCodes.PENDING) {
|
|
674
|
-
resp = await IntermediaryAPI.getInvoiceStatus(this.url!,
|
|
921
|
+
resp = await IntermediaryAPI.getInvoiceStatus(this.url!, paymentHash.toString("hex"));
|
|
675
922
|
if(resp.code===InvoiceStatusResponseCodes.PENDING)
|
|
676
923
|
await timeoutPromise(checkIntervalSeconds*1000, abortController.signal);
|
|
677
924
|
}
|
|
@@ -679,11 +926,11 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
679
926
|
abortController.signal.throwIfAborted();
|
|
680
927
|
|
|
681
928
|
if(resp.code===InvoiceStatusResponseCodes.PAID) {
|
|
682
|
-
const swapData = new this.wrapper.
|
|
929
|
+
const swapData = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
683
930
|
return await this._saveRealSwapData(swapData, true);
|
|
684
931
|
}
|
|
685
932
|
|
|
686
|
-
if(this.
|
|
933
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
|
|
687
934
|
if(resp.code===InvoiceStatusResponseCodes.EXPIRED) {
|
|
688
935
|
await this._saveAndEmit(FromBTCLNAutoSwapState.QUOTE_EXPIRED);
|
|
689
936
|
}
|
|
@@ -694,23 +941,31 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
694
941
|
abortController.abort();
|
|
695
942
|
|
|
696
943
|
if(!paymentResult) return false;
|
|
697
|
-
if(onPaymentReceived!=null) onPaymentReceived(this.getInputTxId());
|
|
944
|
+
if(onPaymentReceived!=null) onPaymentReceived(this.getInputTxId()!);
|
|
698
945
|
}
|
|
699
946
|
|
|
700
|
-
if((this.
|
|
947
|
+
if((this._state as FromBTCLNAutoSwapState)===FromBTCLNAutoSwapState.PR_PAID) {
|
|
701
948
|
await this.waitTillCommited(checkIntervalSeconds, abortSignal);
|
|
702
949
|
}
|
|
703
950
|
|
|
704
|
-
return this.
|
|
951
|
+
return this._state>=FromBTCLNAutoSwapState.CLAIM_COMMITED;
|
|
705
952
|
}
|
|
706
953
|
|
|
707
954
|
|
|
708
955
|
//////////////////////////////
|
|
709
956
|
//// Commit
|
|
710
957
|
|
|
958
|
+
/**
|
|
959
|
+
* Waits till the intermediary (LP) initiates the swap HTLC escrow on the destination smart chain side
|
|
960
|
+
*
|
|
961
|
+
* @param checkIntervalSeconds How often to check via a polling watchdog
|
|
962
|
+
* @param abortSignal Abort signal
|
|
963
|
+
*
|
|
964
|
+
* @internal
|
|
965
|
+
*/
|
|
711
966
|
protected async waitTillCommited(checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<void> {
|
|
712
|
-
if(this.
|
|
713
|
-
if(this.
|
|
967
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED) return Promise.resolve();
|
|
968
|
+
if(this._state!==FromBTCLNAutoSwapState.PR_PAID) throw new Error("Invalid state");
|
|
714
969
|
|
|
715
970
|
const abortController = extendAbortController(abortSignal);
|
|
716
971
|
let result: number | boolean;
|
|
@@ -728,7 +983,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
728
983
|
if(result===false) {
|
|
729
984
|
this.logger.debug("waitTillCommited(): Resolved from watchdog - HTLC expired");
|
|
730
985
|
if(
|
|
731
|
-
this.
|
|
986
|
+
this._state===FromBTCLNAutoSwapState.PR_PAID
|
|
732
987
|
) {
|
|
733
988
|
await this._saveAndEmit(FromBTCLNAutoSwapState.EXPIRED);
|
|
734
989
|
}
|
|
@@ -736,7 +991,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
736
991
|
}
|
|
737
992
|
|
|
738
993
|
if(
|
|
739
|
-
this.
|
|
994
|
+
this._state===FromBTCLNAutoSwapState.PR_PAID
|
|
740
995
|
) {
|
|
741
996
|
await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_COMMITED);
|
|
742
997
|
}
|
|
@@ -744,7 +999,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
744
999
|
if(result===0) this.logger.debug("waitTillCommited(): Resolved from state changed");
|
|
745
1000
|
if(result===true) {
|
|
746
1001
|
this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
747
|
-
await this._broadcastSecret().catch(e => {
|
|
1002
|
+
if(this.secret!=null) await this._broadcastSecret().catch(e => {
|
|
748
1003
|
this.logger.error("waitTillCommited(): Error broadcasting swap secret: ", e);
|
|
749
1004
|
});
|
|
750
1005
|
}
|
|
@@ -755,55 +1010,82 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
755
1010
|
//// Claim
|
|
756
1011
|
|
|
757
1012
|
/**
|
|
758
|
-
*
|
|
759
|
-
* (hash preimage)
|
|
1013
|
+
* @inheritDoc
|
|
760
1014
|
*
|
|
761
1015
|
* @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
|
|
762
|
-
* @
|
|
1016
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
1017
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
1018
|
+
*
|
|
1019
|
+
* @throws {Error} If in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
|
|
763
1020
|
*/
|
|
764
|
-
async txsClaim(_signer?: T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
765
|
-
if(this.
|
|
766
|
-
if(this.
|
|
1021
|
+
async txsClaim(_signer?: T["Signer"] | T["NativeSigner"], secret?: string): Promise<T["TX"][]> {
|
|
1022
|
+
if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state!");
|
|
1023
|
+
if(this._data==null) throw new Error("Unknown data, wrong state?");
|
|
1024
|
+
|
|
1025
|
+
const useSecret = secret ?? this.secret;
|
|
1026
|
+
if(useSecret==null)
|
|
1027
|
+
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
|
|
1028
|
+
if(!this.isValidSecretPreimage(useSecret))
|
|
1029
|
+
throw new Error("Invalid swap secret pre-image provided!");
|
|
767
1030
|
|
|
768
|
-
return await this.wrapper.
|
|
1031
|
+
return await this.wrapper._contract.txsClaimWithSecret(
|
|
769
1032
|
_signer==null ?
|
|
770
1033
|
this._getInitiator() :
|
|
771
|
-
(isAbstractSigner(_signer) ? _signer : await this.wrapper.
|
|
772
|
-
this.
|
|
1034
|
+
(isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer)),
|
|
1035
|
+
this._data, useSecret, true, true
|
|
773
1036
|
);
|
|
774
1037
|
}
|
|
775
1038
|
|
|
776
1039
|
/**
|
|
777
|
-
*
|
|
1040
|
+
* @inheritDoc
|
|
778
1041
|
*
|
|
779
1042
|
* @param _signer Signer to sign the transactions with, can also be different to the initializer
|
|
780
1043
|
* @param abortSignal Abort signal to stop waiting for transaction confirmation
|
|
1044
|
+
* @param onBeforeTxSent
|
|
1045
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
1046
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
781
1047
|
*/
|
|
782
|
-
async claim(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal): Promise<string> {
|
|
783
|
-
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.
|
|
784
|
-
|
|
785
|
-
|
|
1048
|
+
async claim(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, onBeforeTxSent?: (txId: string) => void, secret?: string): Promise<string> {
|
|
1049
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
1050
|
+
let txCount = 0;
|
|
1051
|
+
const txs = await this.txsClaim(_signer, secret);
|
|
1052
|
+
const result = await this.wrapper._chain.sendAndConfirm(
|
|
1053
|
+
signer, txs, true, abortSignal, undefined, (txId: string) => {
|
|
1054
|
+
txCount++;
|
|
1055
|
+
if(onBeforeTxSent!=null && txCount===1) onBeforeTxSent(txId);
|
|
1056
|
+
return Promise.resolve();
|
|
1057
|
+
}
|
|
786
1058
|
);
|
|
787
1059
|
|
|
788
|
-
this.
|
|
789
|
-
if(this.
|
|
1060
|
+
this._claimTxId = result[0];
|
|
1061
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state===FromBTCLNAutoSwapState.EXPIRED || this._state===FromBTCLNAutoSwapState.FAILED) {
|
|
790
1062
|
await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
|
|
791
1063
|
}
|
|
792
1064
|
return result[0];
|
|
793
1065
|
}
|
|
794
1066
|
|
|
795
1067
|
/**
|
|
796
|
-
* Waits till the swap is successfully claimed
|
|
1068
|
+
* Waits till the swap is successfully settled (claimed), should be called after sending the claim (settlement)
|
|
1069
|
+
* transactions manually to wait till the SDK processes the settlement and updates the swap state accordingly.
|
|
797
1070
|
*
|
|
798
1071
|
* @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
|
|
799
1072
|
* @param abortSignal AbortSignal
|
|
800
|
-
* @
|
|
1073
|
+
* @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
|
|
1074
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
1075
|
+
*
|
|
1076
|
+
* @throws {Error} If swap is in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
|
|
801
1077
|
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
802
1078
|
* @returns {boolean} whether the swap was claimed in time or not
|
|
803
1079
|
*/
|
|
804
|
-
async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
805
|
-
if(this.
|
|
806
|
-
if(this.
|
|
1080
|
+
async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, secret?: string): Promise<boolean> {
|
|
1081
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
|
|
1082
|
+
if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED) throw new Error("Invalid state (not CLAIM_COMMITED)");
|
|
1083
|
+
|
|
1084
|
+
if(secret!=null) {
|
|
1085
|
+
if(!this.isValidSecretPreimage(secret))
|
|
1086
|
+
throw new Error("Invalid swap secret pre-image provided!");
|
|
1087
|
+
this.secret = secret;
|
|
1088
|
+
}
|
|
807
1089
|
|
|
808
1090
|
const abortController = new AbortController();
|
|
809
1091
|
if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
@@ -841,15 +1123,15 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
841
1123
|
this.logger.debug("waitTillClaimed(): Resolved from watchdog");
|
|
842
1124
|
|
|
843
1125
|
if(res?.type===SwapCommitStateType.PAID) {
|
|
844
|
-
if((this.
|
|
845
|
-
this.
|
|
1126
|
+
if((this._state as FromBTCLNAutoSwapState)!==FromBTCLNAutoSwapState.CLAIM_CLAIMED) {
|
|
1127
|
+
this._claimTxId = await res.getClaimTxId();
|
|
846
1128
|
await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_CLAIMED);
|
|
847
1129
|
}
|
|
848
1130
|
}
|
|
849
1131
|
if(res?.type===SwapCommitStateType.NOT_COMMITED || res?.type===SwapCommitStateType.EXPIRED) {
|
|
850
1132
|
if(
|
|
851
|
-
(this.
|
|
852
|
-
(this.
|
|
1133
|
+
(this._state as FromBTCLNAutoSwapState)!==FromBTCLNAutoSwapState.CLAIM_CLAIMED &&
|
|
1134
|
+
(this._state as FromBTCLNAutoSwapState)!==FromBTCLNAutoSwapState.FAILED
|
|
853
1135
|
) {
|
|
854
1136
|
await this._saveAndEmit(FromBTCLNAutoSwapState.FAILED);
|
|
855
1137
|
}
|
|
@@ -863,23 +1145,33 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
863
1145
|
//// LNURL
|
|
864
1146
|
|
|
865
1147
|
/**
|
|
866
|
-
*
|
|
1148
|
+
* Whether this swap uses an LNURL-withdraw link
|
|
867
1149
|
*/
|
|
868
1150
|
isLNURL(): boolean {
|
|
869
1151
|
return this.lnurl!=null;
|
|
870
1152
|
}
|
|
871
1153
|
|
|
872
1154
|
/**
|
|
873
|
-
* Gets the used LNURL or null if this is not an LNURL-withdraw swap
|
|
1155
|
+
* Gets the used LNURL or `null` if this is not an LNURL-withdraw swap
|
|
874
1156
|
*/
|
|
875
1157
|
getLNURL(): string | null {
|
|
876
1158
|
return this.lnurl ?? null;
|
|
877
1159
|
}
|
|
878
1160
|
|
|
879
1161
|
/**
|
|
880
|
-
* Pay the generated lightning network invoice with LNURL-withdraw
|
|
1162
|
+
* Pay the generated lightning network invoice with an LNURL-withdraw link, this
|
|
1163
|
+
* is useful when you want to display a lightning payment QR code and also want to
|
|
1164
|
+
* allow payments using LNURL-withdraw NFC cards.
|
|
1165
|
+
*
|
|
1166
|
+
* Note that the swap needs to be created **without** an LNURL to begin with for this function
|
|
1167
|
+
* to work. If this swap is already using an LNURL-withdraw link, this function throws.
|
|
881
1168
|
*/
|
|
882
1169
|
async settleWithLNURLWithdraw(lnurl: string | LNURLWithdraw): Promise<void> {
|
|
1170
|
+
if(
|
|
1171
|
+
this._state!==FromBTCLNAutoSwapState.PR_CREATED &&
|
|
1172
|
+
this._state!==FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED
|
|
1173
|
+
) throw new Error("Must be in PR_CREATED state!");
|
|
1174
|
+
|
|
883
1175
|
if(this.lnurl!=null) throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
|
|
884
1176
|
let lnurlParams: LNURLWithdrawParamsWithUrl;
|
|
885
1177
|
if(typeof(lnurl)==="string") {
|
|
@@ -890,6 +1182,10 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
890
1182
|
} else {
|
|
891
1183
|
lnurlParams = lnurl.params;
|
|
892
1184
|
}
|
|
1185
|
+
|
|
1186
|
+
if(this.pr==null || !this.pr.toLowerCase().startsWith("ln"))
|
|
1187
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
1188
|
+
|
|
893
1189
|
LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
|
|
894
1190
|
this.lnurl = lnurlParams.url;
|
|
895
1191
|
this.lnurlCallback = lnurlParams.callback;
|
|
@@ -902,12 +1198,15 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
902
1198
|
//////////////////////////////
|
|
903
1199
|
//// Storage
|
|
904
1200
|
|
|
1201
|
+
/**
|
|
1202
|
+
* @inheritDoc
|
|
1203
|
+
*/
|
|
905
1204
|
serialize(): any {
|
|
906
1205
|
return {
|
|
907
1206
|
...super.serialize(),
|
|
908
|
-
data: this.
|
|
909
|
-
commitTxId: this.
|
|
910
|
-
claimTxId: this.
|
|
1207
|
+
data: this._data==null ? null : this._data.serialize(),
|
|
1208
|
+
commitTxId: this._commitTxId,
|
|
1209
|
+
claimTxId: this._claimTxId,
|
|
911
1210
|
btcAmountSwap: this.btcAmountSwap==null ? null : this.btcAmountSwap.toString(10),
|
|
912
1211
|
btcAmountGas: this.btcAmountGas==null ? null : this.btcAmountGas.toString(10),
|
|
913
1212
|
gasSwapFeeBtc: this.gasSwapFeeBtc==null ? null : this.gasSwapFeeBtc.toString(10),
|
|
@@ -919,7 +1218,8 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
919
1218
|
lnurlK1: this.lnurlK1,
|
|
920
1219
|
lnurlCallback: this.lnurlCallback,
|
|
921
1220
|
prPosted: this.prPosted,
|
|
922
|
-
initialSwapData: this.initialSwapData.serialize()
|
|
1221
|
+
initialSwapData: this.initialSwapData.serialize(),
|
|
1222
|
+
usesClaimHashAsId: this.usesClaimHashAsId
|
|
923
1223
|
};
|
|
924
1224
|
}
|
|
925
1225
|
|
|
@@ -934,69 +1234,66 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
934
1234
|
* @private
|
|
935
1235
|
*/
|
|
936
1236
|
private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if(commitStatus?.type===SwapCommitStateType.PAID) {
|
|
947
|
-
if(this.claimTxId==null) this.claimTxId = await commitStatus.getClaimTxId();
|
|
948
|
-
this.state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
|
|
949
|
-
return true;
|
|
1237
|
+
if(
|
|
1238
|
+
this._state===FromBTCLNAutoSwapState.PR_PAID ||
|
|
1239
|
+
this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED ||
|
|
1240
|
+
this._state===FromBTCLNAutoSwapState.EXPIRED
|
|
1241
|
+
) {
|
|
1242
|
+
//Check for expiry before the getCommitStatus to prevent race conditions
|
|
1243
|
+
let quoteExpired: boolean = false;
|
|
1244
|
+
if(this._state===FromBTCLNAutoSwapState.PR_PAID) {
|
|
1245
|
+
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
950
1246
|
}
|
|
951
1247
|
|
|
952
|
-
if
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
}
|
|
1248
|
+
//Check if it's already successfully paid
|
|
1249
|
+
commitStatus ??= await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data!);
|
|
1250
|
+
if(commitStatus!=null && await this._forciblySetOnchainState(commitStatus)) return true;
|
|
957
1251
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
switch(commitStatus?.type) {
|
|
962
|
-
case SwapCommitStateType.COMMITED:
|
|
963
|
-
this.state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
|
|
964
|
-
return true;
|
|
965
|
-
case SwapCommitStateType.EXPIRED:
|
|
966
|
-
this.state = FromBTCLNAutoSwapState.EXPIRED;
|
|
967
|
-
return true;
|
|
968
|
-
case SwapCommitStateType.PAID:
|
|
969
|
-
if(this.claimTxId==null) this.claimTxId = await commitStatus.getClaimTxId();
|
|
970
|
-
this.state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
|
|
1252
|
+
if(this._state===FromBTCLNAutoSwapState.PR_PAID) {
|
|
1253
|
+
if(quoteExpired) {
|
|
1254
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
971
1255
|
return true;
|
|
972
|
-
|
|
973
|
-
if(quoteExpired) {
|
|
974
|
-
this.state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
975
|
-
return true;
|
|
1256
|
+
}
|
|
976
1257
|
}
|
|
977
1258
|
}
|
|
978
1259
|
|
|
979
1260
|
return false;
|
|
980
1261
|
}
|
|
981
1262
|
|
|
982
|
-
|
|
983
|
-
|
|
1263
|
+
/**
|
|
1264
|
+
* @inheritDoc
|
|
1265
|
+
* @internal
|
|
1266
|
+
*/
|
|
1267
|
+
_shouldFetchOnchainState(): boolean {
|
|
1268
|
+
return this._state===FromBTCLNAutoSwapState.PR_PAID || this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED || this._state===FromBTCLNAutoSwapState.EXPIRED;
|
|
984
1269
|
}
|
|
985
1270
|
|
|
1271
|
+
/**
|
|
1272
|
+
* @inheritDoc
|
|
1273
|
+
* @internal
|
|
1274
|
+
*/
|
|
986
1275
|
_shouldFetchExpiryStatus(): boolean {
|
|
987
|
-
return this.
|
|
1276
|
+
return this._state===FromBTCLNAutoSwapState.PR_PAID;
|
|
988
1277
|
}
|
|
989
1278
|
|
|
1279
|
+
/**
|
|
1280
|
+
* @inheritDoc
|
|
1281
|
+
* @internal
|
|
1282
|
+
*/
|
|
990
1283
|
_shouldCheckIntermediary(): boolean {
|
|
991
|
-
return this.
|
|
1284
|
+
return this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
|
|
992
1285
|
}
|
|
993
1286
|
|
|
1287
|
+
/**
|
|
1288
|
+
* @inheritDoc
|
|
1289
|
+
* @internal
|
|
1290
|
+
*/
|
|
994
1291
|
async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState, skipLpCheck?: boolean): Promise<boolean> {
|
|
995
1292
|
let changed = false;
|
|
996
1293
|
|
|
997
|
-
if(this.
|
|
998
|
-
if(this.
|
|
999
|
-
this.
|
|
1294
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
|
|
1295
|
+
if(this._state!==FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.getQuoteExpiry()<Date.now()) {
|
|
1296
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
|
|
1000
1297
|
changed ||= true;
|
|
1001
1298
|
}
|
|
1002
1299
|
|
|
@@ -1007,9 +1304,9 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
1007
1304
|
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1008
1305
|
}
|
|
1009
1306
|
|
|
1010
|
-
if(this.
|
|
1307
|
+
if(this._state===FromBTCLNAutoSwapState.PR_CREATED || this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED) {
|
|
1011
1308
|
if(await this._verifyQuoteDefinitelyExpired()) {
|
|
1012
|
-
this.
|
|
1309
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
1013
1310
|
changed ||= true;
|
|
1014
1311
|
}
|
|
1015
1312
|
}
|
|
@@ -1017,54 +1314,110 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
1017
1314
|
|
|
1018
1315
|
if(await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus)) changed = true;
|
|
1019
1316
|
|
|
1317
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED) {
|
|
1318
|
+
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data!);
|
|
1319
|
+
if(expired) {
|
|
1320
|
+
this._state = FromBTCLNAutoSwapState.EXPIRED;
|
|
1321
|
+
changed = true;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1020
1325
|
if(save && changed) await this._saveAndEmit();
|
|
1021
1326
|
|
|
1022
|
-
if(this.
|
|
1327
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED && this.secret!=null) await this._broadcastSecret().catch(e => {
|
|
1023
1328
|
this.logger.error("_sync(): Error when broadcasting swap secret: ", e);
|
|
1024
1329
|
});
|
|
1025
1330
|
|
|
1026
1331
|
return changed;
|
|
1027
1332
|
}
|
|
1028
1333
|
|
|
1029
|
-
|
|
1334
|
+
/**
|
|
1335
|
+
* @inheritDoc
|
|
1336
|
+
* @internal
|
|
1337
|
+
*/
|
|
1338
|
+
async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
|
|
1339
|
+
switch(commitStatus?.type) {
|
|
1340
|
+
case SwapCommitStateType.PAID:
|
|
1341
|
+
if(this._claimTxId==null) this._claimTxId = await commitStatus.getClaimTxId();
|
|
1342
|
+
if(this.secret==null || this.pr==null) this._setSwapSecret(await commitStatus.getClaimResult());
|
|
1343
|
+
this._state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
|
|
1344
|
+
return true;
|
|
1345
|
+
case SwapCommitStateType.NOT_COMMITED:
|
|
1346
|
+
if(this._refundTxId==null && commitStatus.getRefundTxId!=null) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1347
|
+
if(this._refundTxId!=null) {
|
|
1348
|
+
this._state = FromBTCLNAutoSwapState.FAILED;
|
|
1349
|
+
return true;
|
|
1350
|
+
}
|
|
1351
|
+
break;
|
|
1352
|
+
case SwapCommitStateType.EXPIRED:
|
|
1353
|
+
if(this._refundTxId==null && commitStatus.getRefundTxId!=null) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1354
|
+
this._state = this._refundTxId==null ? FromBTCLNAutoSwapState.QUOTE_EXPIRED : FromBTCLNAutoSwapState.FAILED;
|
|
1355
|
+
return true;
|
|
1356
|
+
case SwapCommitStateType.COMMITED:
|
|
1357
|
+
if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED && this._state!==FromBTCLNAutoSwapState.EXPIRED) {
|
|
1358
|
+
this._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
|
|
1359
|
+
return true;
|
|
1360
|
+
}
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1030
1365
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1366
|
+
/**
|
|
1367
|
+
* Broadcasts the swap secret to the underlying data propagation layer (e.g. Nostr by default)
|
|
1368
|
+
*
|
|
1369
|
+
* @param noCheckExpiry Whether a swap expiration check should be skipped broadcasting
|
|
1370
|
+
* @param secret An optional secret pre-image for the swap to broadcast
|
|
1371
|
+
*
|
|
1372
|
+
* @internal
|
|
1373
|
+
*/
|
|
1374
|
+
async _broadcastSecret(noCheckExpiry?: boolean, secret?: string): Promise<void> {
|
|
1375
|
+
if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state to broadcast swap secret!");
|
|
1376
|
+
if(this._data==null) throw new Error("Unknown data, wrong state?");
|
|
1377
|
+
|
|
1378
|
+
const useSecret = secret ?? this.secret;
|
|
1379
|
+
if(useSecret==null)
|
|
1380
|
+
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
|
|
1381
|
+
if(!this.isValidSecretPreimage(useSecret))
|
|
1382
|
+
throw new Error("Invalid swap secret pre-image provided!");
|
|
1034
1383
|
|
|
1035
1384
|
if(!noCheckExpiry) {
|
|
1036
|
-
if(await this.wrapper.
|
|
1385
|
+
if(await this.wrapper._contract.isExpired(this._getInitiator(), this._data)) throw new Error("On-chain HTLC already expired!");
|
|
1037
1386
|
}
|
|
1038
|
-
await this.wrapper.
|
|
1387
|
+
await this.wrapper._messenger.broadcast(new SwapClaimWitnessMessage(this._data, useSecret));
|
|
1039
1388
|
}
|
|
1040
1389
|
|
|
1390
|
+
/**
|
|
1391
|
+
* @inheritDoc
|
|
1392
|
+
* @internal
|
|
1393
|
+
*/
|
|
1041
1394
|
async _tick(save?: boolean): Promise<boolean> {
|
|
1042
|
-
switch(this.
|
|
1395
|
+
switch(this._state) {
|
|
1043
1396
|
case FromBTCLNAutoSwapState.PR_CREATED:
|
|
1044
1397
|
if(this.getQuoteExpiry() < Date.now()) {
|
|
1045
|
-
this.
|
|
1398
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED;
|
|
1046
1399
|
if(save) await this._saveAndEmit();
|
|
1047
1400
|
return true;
|
|
1048
1401
|
}
|
|
1049
1402
|
break;
|
|
1050
1403
|
case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
|
|
1051
1404
|
if(this.getDefinitiveExpiryTime() < Date.now()) {
|
|
1052
|
-
this.
|
|
1405
|
+
this._state = FromBTCLNAutoSwapState.QUOTE_EXPIRED;
|
|
1053
1406
|
if(save) await this._saveAndEmit();
|
|
1054
1407
|
return true;
|
|
1055
1408
|
}
|
|
1056
1409
|
break;
|
|
1057
1410
|
case FromBTCLNAutoSwapState.PR_PAID:
|
|
1058
1411
|
case FromBTCLNAutoSwapState.CLAIM_COMMITED:
|
|
1059
|
-
const expired = await this.wrapper.
|
|
1412
|
+
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data!);
|
|
1060
1413
|
if(expired) {
|
|
1061
|
-
this.
|
|
1414
|
+
this._state = FromBTCLNAutoSwapState.EXPIRED;
|
|
1062
1415
|
if(save) await this._saveAndEmit();
|
|
1063
1416
|
return true;
|
|
1064
1417
|
}
|
|
1065
|
-
if(this.
|
|
1418
|
+
if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED) {
|
|
1066
1419
|
//Broadcast the secret over the provided messenger channel
|
|
1067
|
-
if(this.broadcastTickCounter===0) await this._broadcastSecret(true).catch(e => {
|
|
1420
|
+
if(this.broadcastTickCounter===0 && this.secret!=null) await this._broadcastSecret(true).catch(e => {
|
|
1068
1421
|
this.logger.warn("_tick(): Error when broadcasting swap secret: ", e);
|
|
1069
1422
|
});
|
|
1070
1423
|
this.broadcastTickCounter = (this.broadcastTickCounter + 1) % 3; //Broadcast every 3rd tick
|
|
@@ -1075,4 +1428,16 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
|
|
|
1075
1428
|
return false;
|
|
1076
1429
|
}
|
|
1077
1430
|
|
|
1431
|
+
/**
|
|
1432
|
+
* Forcibly sets the swap secret pre-image from on-chain data
|
|
1433
|
+
*
|
|
1434
|
+
* @internal
|
|
1435
|
+
*/
|
|
1436
|
+
_setSwapSecret(secret: string) {
|
|
1437
|
+
this.secret = secret;
|
|
1438
|
+
if(this.pr==null) {
|
|
1439
|
+
this.pr = Buffer.from(sha256(Buffer.from(secret, "hex"))).toString("hex");
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1078
1443
|
}
|