@atomiqlabs/sdk 8.1.8 → 8.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitcoin/wallet/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 +7 -0
- package/dist/enums/FeeType.js +7 -0
- 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 +4 -5
- package/dist/index.js +3 -4
- package/dist/intermediaries/Intermediary.d.ts +57 -10
- package/dist/intermediaries/Intermediary.js +37 -10
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +55 -22
- package/dist/intermediaries/IntermediaryDiscovery.js +35 -22
- package/dist/prices/RedundantSwapPrice.d.ts +24 -3
- package/dist/prices/RedundantSwapPrice.js +21 -1
- package/dist/prices/SingleSwapPrice.d.ts +9 -6
- package/dist/prices/SingleSwapPrice.js +10 -7
- package/dist/prices/SwapPriceWithChain.d.ts +54 -16
- package/dist/prices/SwapPriceWithChain.js +58 -20
- package/dist/prices/abstract/ISwapPrice.d.ts +94 -45
- package/dist/prices/abstract/ISwapPrice.js +103 -55
- package/dist/prices/providers/BinancePriceProvider.d.ts +7 -0
- package/dist/prices/providers/BinancePriceProvider.js +7 -0
- package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +6 -0
- package/dist/prices/providers/CoinGeckoPriceProvider.js +6 -0
- package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +6 -0
- package/dist/prices/providers/CoinPaprikaPriceProvider.js +6 -0
- package/dist/prices/providers/CustomPriceProvider.d.ts +11 -0
- package/dist/prices/providers/CustomPriceProvider.js +11 -0
- package/dist/prices/providers/KrakenPriceProvider.d.ts +9 -0
- package/dist/prices/providers/KrakenPriceProvider.js +9 -0
- package/dist/prices/providers/OKXPriceProvider.d.ts +6 -0
- package/dist/prices/providers/OKXPriceProvider.js +6 -0
- 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 +302 -221
- package/dist/swapper/Swapper.js +376 -344
- package/dist/swapper/SwapperFactory.d.ts +41 -17
- package/dist/swapper/SwapperFactory.js +23 -2
- 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 +10 -1
- package/dist/swaps/IAddressSwap.js +2 -1
- package/dist/swaps/IBTCWalletSwap.d.ts +24 -6
- package/dist/swaps/IBTCWalletSwap.js +2 -1
- package/dist/swaps/IClaimableSwap.d.ts +36 -4
- package/dist/swaps/IClaimableSwap.js +2 -1
- package/dist/swaps/IClaimableSwapWrapper.d.ts +11 -1
- package/dist/swaps/IRefundableSwap.d.ts +29 -3
- package/dist/swaps/IRefundableSwap.js +2 -1
- package/dist/swaps/ISwap.d.ts +159 -21
- package/dist/swaps/ISwap.js +90 -33
- package/dist/swaps/ISwapWithGasDrop.d.ts +6 -0
- package/dist/swaps/ISwapWithGasDrop.js +1 -0
- package/dist/swaps/ISwapWrapper.d.ts +157 -48
- package/dist/swaps/ISwapWrapper.js +130 -72
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +49 -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 +94 -29
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +90 -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 +275 -58
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +516 -239
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +76 -25
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +131 -49
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +311 -51
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +542 -193
- 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 +209 -53
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +449 -242
- 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 +197 -56
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +326 -189
- 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 +60 -19
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +74 -31
- 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 +36 -13
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +65 -19
- 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 +328 -92
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +460 -219
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +76 -24
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +244 -124
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +146 -18
- package/dist/swaps/trusted/ln/LnForGasSwap.js +173 -43
- 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 +200 -47
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +230 -78
- 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 +7 -1
- package/dist/types/SwapExecutionAction.d.ts +74 -4
- package/dist/types/SwapWithSigner.d.ts +4 -1
- 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 +2 -1
- package/dist/types/fees/FeeBreakdown.d.ts +2 -1
- package/dist/types/fees/PercentagePPM.d.ts +2 -0
- package/dist/types/fees/PercentagePPM.js +1 -0
- package/dist/types/lnurl/LNURLPay.d.ts +14 -6
- package/dist/types/lnurl/LNURLPay.js +6 -2
- package/dist/types/lnurl/LNURLWithdraw.d.ts +12 -5
- package/dist/types/lnurl/LNURLWithdraw.js +6 -2
- package/dist/types/wallets/LightningInvoiceCreateService.d.ts +20 -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 +3 -1
- package/dist/utils/BitcoinUtils.d.ts +1 -0
- package/dist/utils/BitcoinUtils.js +5 -1
- package/dist/utils/SwapUtils.d.ts +56 -1
- package/dist/utils/SwapUtils.js +53 -1
- package/dist/utils/TokenUtils.d.ts +10 -2
- package/dist/utils/TokenUtils.js +12 -4
- package/package.json +3 -3
- 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 +7 -0
- 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 +6 -5
- package/src/intermediaries/Intermediary.ts +57 -10
- package/src/intermediaries/IntermediaryDiscovery.ts +60 -27
- package/src/prices/RedundantSwapPrice.ts +24 -4
- package/src/prices/SingleSwapPrice.ts +10 -7
- package/src/prices/SwapPriceWithChain.ts +59 -21
- package/src/prices/abstract/ISwapPrice.ts +114 -65
- package/src/prices/providers/BinancePriceProvider.ts +7 -0
- package/src/prices/providers/CoinGeckoPriceProvider.ts +6 -0
- package/src/prices/providers/CoinPaprikaPriceProvider.ts +6 -0
- package/src/prices/providers/CustomPriceProvider.ts +11 -0
- package/src/prices/providers/KrakenPriceProvider.ts +9 -0
- package/src/prices/providers/OKXPriceProvider.ts +6 -0
- 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 +512 -378
- package/src/swapper/SwapperFactory.ts +44 -21
- 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 +11 -1
- package/src/swaps/IBTCWalletSwap.ts +24 -8
- package/src/swaps/IClaimableSwap.ts +39 -4
- package/src/swaps/IClaimableSwapWrapper.ts +11 -2
- package/src/swaps/IRefundableSwap.ts +32 -3
- package/src/swaps/ISwap.ts +221 -82
- package/src/swaps/ISwapWithGasDrop.ts +6 -0
- package/src/swaps/ISwapWrapper.ts +212 -94
- package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +62 -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 +120 -51
- package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +24 -11
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +559 -256
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +155 -61
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +590 -226
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +177 -74
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +470 -243
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +141 -59
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +352 -193
- package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +48 -23
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +86 -39
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +110 -110
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +88 -33
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +101 -31
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +534 -263
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +289 -148
- package/src/swaps/trusted/ln/LnForGasSwap.ts +184 -45
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +34 -15
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +260 -86
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +41 -19
- package/src/types/AmountData.ts +2 -1
- package/src/types/CustomPriceFunction.ts +7 -1
- package/src/types/SwapExecutionAction.ts +84 -5
- package/src/types/SwapWithSigner.ts +7 -3
- package/src/types/Token.ts +12 -5
- package/src/types/TokenAmount.ts +3 -0
- package/src/types/fees/Fee.ts +2 -1
- package/src/types/fees/FeeBreakdown.ts +2 -1
- package/src/types/fees/PercentagePPM.ts +2 -0
- package/src/types/lnurl/LNURLPay.ts +14 -6
- package/src/types/lnurl/LNURLWithdraw.ts +12 -5
- package/src/types/wallets/LightningInvoiceCreateService.ts +26 -0
- package/src/types/wallets/MinimalBitcoinWalletInterface.ts +3 -1
- package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +3 -1
- package/src/utils/BitcoinUtils.ts +5 -0
- package/src/utils/SwapUtils.ts +61 -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
|
@@ -12,15 +12,16 @@ import {
|
|
|
12
12
|
import {Buffer} from "buffer";
|
|
13
13
|
import {
|
|
14
14
|
extendAbortController,
|
|
15
|
-
getTxoHash
|
|
15
|
+
getTxoHash, toBigInt
|
|
16
16
|
} from "../../../../utils/Utils";
|
|
17
17
|
import {
|
|
18
|
+
fromOutputScript,
|
|
18
19
|
parsePsbtTransaction,
|
|
19
20
|
toOutputScript,
|
|
20
21
|
} from "../../../../utils/BitcoinUtils";
|
|
21
22
|
import {IBitcoinWallet, isIBitcoinWallet} from "../../../../bitcoin/wallet/IBitcoinWallet";
|
|
22
23
|
import {IBTCWalletSwap} from "../../../IBTCWalletSwap";
|
|
23
|
-
import {
|
|
24
|
+
import {Transaction} from "@scure/btc-signer";
|
|
24
25
|
import {SingleAddressBitcoinWallet} from "../../../../bitcoin/wallet/SingleAddressBitcoinWallet";
|
|
25
26
|
import {
|
|
26
27
|
MinimalBitcoinWalletInterface,
|
|
@@ -30,59 +31,111 @@ import {IClaimableSwap} from "../../../IClaimableSwap";
|
|
|
30
31
|
import {IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../../IEscrowSelfInitSwap";
|
|
31
32
|
import {IAddressSwap} from "../../../IAddressSwap";
|
|
32
33
|
import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
|
|
33
|
-
import {BitcoinTokens, BtcToken, SCToken
|
|
34
|
+
import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
|
|
34
35
|
import {getLogger, LoggerType} from "../../../../utils/Logger";
|
|
35
36
|
import {toBitcoinWallet} from "../../../../utils/BitcoinWalletUtils";
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
|
-
* State enum for
|
|
39
|
+
* State enum for legacy escrow based Bitcoin -> Smart chain swaps.
|
|
40
|
+
*
|
|
39
41
|
* @category Swaps
|
|
40
42
|
*/
|
|
41
43
|
export enum FromBTCSwapState {
|
|
44
|
+
/**
|
|
45
|
+
* Bitcoin swap address has expired and the intermediary (LP) has already refunded
|
|
46
|
+
* its funds. No BTC should be sent anymore!
|
|
47
|
+
*/
|
|
42
48
|
FAILED = -4,
|
|
49
|
+
/**
|
|
50
|
+
* Bitcoin swap address has expired, user should not send any BTC anymore! Though
|
|
51
|
+
* the intermediary (LP) hasn't refunded yet. So if there is a transaction already
|
|
52
|
+
* in-flight the swap might still succeed.
|
|
53
|
+
*/
|
|
43
54
|
EXPIRED = -3,
|
|
55
|
+
/**
|
|
56
|
+
* Swap has expired for good and there is no way how it can be executed anymore
|
|
57
|
+
*/
|
|
44
58
|
QUOTE_EXPIRED = -2,
|
|
59
|
+
/**
|
|
60
|
+
* A swap is almost expired, and it should be presented to the user as expired, though
|
|
61
|
+
* there is still a chance that it will be processed
|
|
62
|
+
*/
|
|
45
63
|
QUOTE_SOFT_EXPIRED = -1,
|
|
64
|
+
/**
|
|
65
|
+
* Swap quote was created, use the {@link FromBTCSwap.commit} or {@link FromBTCSwap.txsCommit} functions
|
|
66
|
+
* to initiate it by creating the swap escrow on the destination smart chain
|
|
67
|
+
*/
|
|
46
68
|
PR_CREATED = 0,
|
|
69
|
+
/**
|
|
70
|
+
* Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the
|
|
71
|
+
* swap address with the {@link FromBTCSwap.getFundedPsbt}, {@link FromBTCSwap.getAddress} or
|
|
72
|
+
* {@link FromBTCSwap.getHyperlink} functions.
|
|
73
|
+
*/
|
|
47
74
|
CLAIM_COMMITED = 1,
|
|
75
|
+
/**
|
|
76
|
+
* Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower
|
|
77
|
+
* or settle manually using the {@link FromBTCSwap.claim} or {@link FromBTCSwap.txsClaim}
|
|
78
|
+
* function.
|
|
79
|
+
*/
|
|
48
80
|
BTC_TX_CONFIRMED = 2,
|
|
81
|
+
/**
|
|
82
|
+
* Swap successfully settled and funds received on the destination chain
|
|
83
|
+
*/
|
|
49
84
|
CLAIM_CLAIMED = 3
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
export type FromBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
|
|
53
88
|
data: T;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
requiredConfirmations: number;
|
|
89
|
+
address?: string;
|
|
90
|
+
amount?: bigint;
|
|
91
|
+
requiredConfirmations?: number;
|
|
58
92
|
};
|
|
59
93
|
|
|
60
94
|
export function isFromBTCSwapInit<T extends SwapData>(obj: any): obj is FromBTCSwapInit<T> {
|
|
61
|
-
return typeof(obj.
|
|
62
|
-
typeof(obj.
|
|
63
|
-
typeof(obj.
|
|
64
|
-
typeof(obj.requiredConfirmations) === "number" &&
|
|
95
|
+
return typeof(obj.data) === "object" &&
|
|
96
|
+
(obj.address==null || typeof(obj.address) === "string") &&
|
|
97
|
+
(obj.amount==null || typeof(obj.amount) === "bigint") &&
|
|
98
|
+
(obj.requiredConfirmations==null || typeof(obj.requiredConfirmations) === "number") &&
|
|
65
99
|
isIEscrowSelfInitSwapInit<T>(obj);
|
|
66
100
|
}
|
|
67
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Legacy escrow (PrTLC) based swap for Bitcoin -> Smart chains, requires manual initiation
|
|
104
|
+
* of the swap escrow on the destination chain.
|
|
105
|
+
*
|
|
106
|
+
* @category Swaps
|
|
107
|
+
*/
|
|
68
108
|
export class FromBTCSwap<T extends ChainType = ChainType>
|
|
69
109
|
extends IFromBTCSelfInitSwap<T, FromBTCDefinition<T>, FromBTCSwapState>
|
|
70
110
|
implements IBTCWalletSwap, IClaimableSwap<T, FromBTCDefinition<T>, FromBTCSwapState>, IAddressSwap {
|
|
71
111
|
|
|
112
|
+
protected readonly TYPE: SwapType.FROM_BTC = SwapType.FROM_BTC;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
72
117
|
protected readonly logger: LoggerType;
|
|
118
|
+
/**
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
73
121
|
protected readonly inputToken: BtcToken<false> = BitcoinTokens.BTC;
|
|
74
|
-
|
|
122
|
+
/**
|
|
123
|
+
* @internal
|
|
124
|
+
*/
|
|
125
|
+
protected readonly feeRate!: string;
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
/**
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
readonly _data!: T["Data"];
|
|
78
131
|
|
|
79
|
-
address
|
|
80
|
-
amount
|
|
81
|
-
|
|
132
|
+
private address?: string;
|
|
133
|
+
private amount?: bigint;
|
|
134
|
+
private requiredConfirmations?: number;
|
|
82
135
|
|
|
83
|
-
senderAddress?: string;
|
|
84
|
-
txId?: string;
|
|
85
|
-
vout?: number;
|
|
136
|
+
private senderAddress?: string;
|
|
137
|
+
private txId?: string;
|
|
138
|
+
private vout?: number;
|
|
86
139
|
|
|
87
140
|
constructor(wrapper: FromBTCWrapper<T>, init: FromBTCSwapInit<T["Data"]>);
|
|
88
141
|
constructor(wrapper: FromBTCWrapper<T>, obj: any);
|
|
@@ -90,48 +143,56 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
90
143
|
if(isFromBTCSwapInit(initOrObject) && initOrObject.url!=null) initOrObject.url += "/frombtc";
|
|
91
144
|
super(wrapper, initOrObject);
|
|
92
145
|
if(isFromBTCSwapInit(initOrObject)) {
|
|
93
|
-
this.
|
|
94
|
-
this.
|
|
146
|
+
this._state = FromBTCSwapState.PR_CREATED;
|
|
147
|
+
this._data = initOrObject.data;
|
|
95
148
|
this.feeRate = initOrObject.feeRate;
|
|
96
149
|
this.address = initOrObject.address;
|
|
97
150
|
this.amount = initOrObject.amount;
|
|
98
151
|
this.requiredConfirmations = initOrObject.requiredConfirmations;
|
|
99
152
|
} else {
|
|
100
153
|
this.address = initOrObject.address;
|
|
101
|
-
this.amount =
|
|
154
|
+
this.amount = toBigInt(initOrObject.amount);
|
|
102
155
|
this.senderAddress = initOrObject.senderAddress;
|
|
103
156
|
this.txId = initOrObject.txId;
|
|
104
157
|
this.vout = initOrObject.vout;
|
|
105
|
-
this.requiredConfirmations = initOrObject.requiredConfirmations ?? this.
|
|
158
|
+
this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
|
|
106
159
|
}
|
|
107
160
|
this.tryRecomputeSwapPrice();
|
|
108
161
|
this.logger = getLogger("FromBTC("+this.getIdentifierHashString()+"): ");
|
|
109
162
|
}
|
|
110
163
|
|
|
164
|
+
/**
|
|
165
|
+
* @inheritDoc
|
|
166
|
+
* @internal
|
|
167
|
+
*/
|
|
111
168
|
protected getSwapData(): T["Data"] {
|
|
112
|
-
return this.
|
|
169
|
+
return this._data;
|
|
113
170
|
}
|
|
114
171
|
|
|
172
|
+
/**
|
|
173
|
+
* @inheritDoc
|
|
174
|
+
* @internal
|
|
175
|
+
*/
|
|
115
176
|
protected upgradeVersion() {
|
|
116
177
|
if(this.version == null) {
|
|
117
|
-
switch(this.
|
|
178
|
+
switch(this._state) {
|
|
118
179
|
case -2:
|
|
119
|
-
this.
|
|
180
|
+
this._state = FromBTCSwapState.FAILED
|
|
120
181
|
break;
|
|
121
182
|
case -1:
|
|
122
|
-
this.
|
|
183
|
+
this._state = FromBTCSwapState.QUOTE_EXPIRED
|
|
123
184
|
break;
|
|
124
185
|
case 0:
|
|
125
|
-
this.
|
|
186
|
+
this._state = FromBTCSwapState.PR_CREATED
|
|
126
187
|
break;
|
|
127
188
|
case 1:
|
|
128
|
-
this.
|
|
189
|
+
this._state = FromBTCSwapState.CLAIM_COMMITED
|
|
129
190
|
break;
|
|
130
191
|
case 2:
|
|
131
|
-
this.
|
|
192
|
+
this._state = FromBTCSwapState.BTC_TX_CONFIRMED
|
|
132
193
|
break;
|
|
133
194
|
case 3:
|
|
134
|
-
this.
|
|
195
|
+
this._state = FromBTCSwapState.CLAIM_CLAIMED
|
|
135
196
|
break;
|
|
136
197
|
}
|
|
137
198
|
this.version = 1;
|
|
@@ -146,8 +207,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
146
207
|
* Returns bitcoin address where the on-chain BTC should be sent to
|
|
147
208
|
*/
|
|
148
209
|
getAddress(): string {
|
|
149
|
-
if(this.
|
|
150
|
-
return this.address;
|
|
210
|
+
if(this._state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
|
|
211
|
+
return this.address ?? "";
|
|
151
212
|
}
|
|
152
213
|
|
|
153
214
|
/**
|
|
@@ -156,18 +217,27 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
156
217
|
* @private
|
|
157
218
|
*/
|
|
158
219
|
private _getHyperlink(): string {
|
|
159
|
-
return "bitcoin:"+this.address+"?amount="+encodeURIComponent((Number(this.amount) / 100000000).toString(10));
|
|
220
|
+
return this.address==null || this.amount==null ? "" : "bitcoin:"+this.address+"?amount="+encodeURIComponent((Number(this.amount) / 100000000).toString(10));
|
|
160
221
|
}
|
|
161
222
|
|
|
223
|
+
/**
|
|
224
|
+
* @inheritDoc
|
|
225
|
+
*/
|
|
162
226
|
getHyperlink(): string {
|
|
163
|
-
if(this.
|
|
227
|
+
if(this._state===FromBTCSwapState.PR_CREATED) throw new Error("Cannot get bitcoin address of non-initiated swaps! Initiate swap first with commit() or txsCommit().");
|
|
164
228
|
return this._getHyperlink();
|
|
165
229
|
}
|
|
166
230
|
|
|
231
|
+
/**
|
|
232
|
+
* @inheritDoc
|
|
233
|
+
*/
|
|
167
234
|
getInputAddress(): string | null {
|
|
168
235
|
return this.senderAddress ?? null;
|
|
169
236
|
}
|
|
170
237
|
|
|
238
|
+
/**
|
|
239
|
+
* @inheritDoc
|
|
240
|
+
*/
|
|
171
241
|
getInputTxId(): string | null {
|
|
172
242
|
return this.txId ?? null;
|
|
173
243
|
}
|
|
@@ -177,74 +247,136 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
177
247
|
* to that address anymore
|
|
178
248
|
*/
|
|
179
249
|
getTimeoutTime(): number {
|
|
180
|
-
return Number(this.wrapper.
|
|
250
|
+
return Number(this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations ?? 6)) * 1000;
|
|
181
251
|
}
|
|
182
252
|
|
|
253
|
+
/**
|
|
254
|
+
* @inheritDoc
|
|
255
|
+
*/
|
|
183
256
|
requiresAction(): boolean {
|
|
184
|
-
return this.isClaimable() || (this.
|
|
257
|
+
return this.isClaimable() || (this._state===FromBTCSwapState.CLAIM_COMMITED && this.getTimeoutTime()>Date.now() && this.txId==null);
|
|
185
258
|
}
|
|
186
259
|
|
|
260
|
+
/**
|
|
261
|
+
* @inheritDoc
|
|
262
|
+
*/
|
|
187
263
|
isFinished(): boolean {
|
|
188
|
-
return this.
|
|
264
|
+
return this._state===FromBTCSwapState.CLAIM_CLAIMED || this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.FAILED;
|
|
189
265
|
}
|
|
190
266
|
|
|
267
|
+
/**
|
|
268
|
+
* @inheritDoc
|
|
269
|
+
*/
|
|
191
270
|
isClaimable(): boolean {
|
|
192
|
-
return this.
|
|
271
|
+
return this._state===FromBTCSwapState.BTC_TX_CONFIRMED;
|
|
193
272
|
}
|
|
194
273
|
|
|
274
|
+
/**
|
|
275
|
+
* @inheritDoc
|
|
276
|
+
*/
|
|
195
277
|
isSuccessful(): boolean {
|
|
196
|
-
return this.
|
|
278
|
+
return this._state===FromBTCSwapState.CLAIM_CLAIMED;
|
|
197
279
|
}
|
|
198
280
|
|
|
281
|
+
/**
|
|
282
|
+
* @inheritDoc
|
|
283
|
+
*/
|
|
199
284
|
isFailed(): boolean {
|
|
200
|
-
return this.
|
|
285
|
+
return this._state===FromBTCSwapState.FAILED || this._state===FromBTCSwapState.EXPIRED;
|
|
201
286
|
}
|
|
202
287
|
|
|
288
|
+
/**
|
|
289
|
+
* @inheritDoc
|
|
290
|
+
*/
|
|
203
291
|
isQuoteExpired(): boolean {
|
|
204
|
-
return this.
|
|
292
|
+
return this._state===FromBTCSwapState.QUOTE_EXPIRED;
|
|
205
293
|
}
|
|
206
294
|
|
|
295
|
+
/**
|
|
296
|
+
* @inheritDoc
|
|
297
|
+
*/
|
|
207
298
|
isQuoteSoftExpired(): boolean {
|
|
208
|
-
return this.
|
|
299
|
+
return this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
209
300
|
}
|
|
210
301
|
|
|
302
|
+
/**
|
|
303
|
+
* @inheritDoc
|
|
304
|
+
* @internal
|
|
305
|
+
*/
|
|
211
306
|
protected canCommit(): boolean {
|
|
212
|
-
if(this.
|
|
213
|
-
|
|
307
|
+
if(this._state!==FromBTCSwapState.PR_CREATED) return false;
|
|
308
|
+
if(this.requiredConfirmations==null) return false;
|
|
309
|
+
const expiry = this.wrapper._getOnchainSendTimeout(this._data, this.requiredConfirmations);
|
|
214
310
|
const currentTimestamp = BigInt(Math.floor(Date.now()/1000));
|
|
215
311
|
|
|
216
|
-
return (expiry - currentTimestamp) >= this.wrapper.
|
|
312
|
+
return (expiry - currentTimestamp) >= this.wrapper._options.minSendWindow;
|
|
217
313
|
}
|
|
218
314
|
|
|
219
315
|
|
|
220
316
|
//////////////////////////////
|
|
221
317
|
//// Amounts & fees
|
|
222
318
|
|
|
319
|
+
/**
|
|
320
|
+
* @inheritDoc
|
|
321
|
+
*/
|
|
223
322
|
getInputToken(): BtcToken<false> {
|
|
224
323
|
return BitcoinTokens.BTC;
|
|
225
324
|
}
|
|
226
325
|
|
|
326
|
+
/**
|
|
327
|
+
* @inheritDoc
|
|
328
|
+
*/
|
|
227
329
|
getInput(): TokenAmount<T["ChainId"], BtcToken<false>> {
|
|
228
|
-
return toTokenAmount(this.amount, this.inputToken, this.wrapper.
|
|
330
|
+
return toTokenAmount(this.amount ?? null, this.inputToken, this.wrapper._prices);
|
|
229
331
|
}
|
|
230
332
|
|
|
231
333
|
/**
|
|
232
|
-
* Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically
|
|
334
|
+
* Returns claimer bounty, acting as a reward for watchtowers to claim the swap automatically,
|
|
335
|
+
* this amount is pre-funded by the user on the destination chain when the swap escrow
|
|
336
|
+
* is initiated. For total pre-funded deposit amount see {@link getTotalDeposit}.
|
|
233
337
|
*/
|
|
234
338
|
getClaimerBounty(): TokenAmount<T["ChainId"], SCToken<T["ChainId"]>, true> {
|
|
235
|
-
return toTokenAmount(this.
|
|
339
|
+
return toTokenAmount(this._data.getClaimerBounty(), this.wrapper._tokens[this._data.getDepositToken()], this.wrapper._prices);
|
|
236
340
|
}
|
|
237
341
|
|
|
238
342
|
|
|
239
343
|
//////////////////////////////
|
|
240
344
|
//// Bitcoin tx
|
|
241
345
|
|
|
346
|
+
/**
|
|
347
|
+
* If the required number of confirmations is not known, this function tries to infer it by looping through
|
|
348
|
+
* possible confirmation targets and comparing the claim hashes
|
|
349
|
+
*
|
|
350
|
+
* @param btcTx Bitcoin transaction
|
|
351
|
+
* @param vout Output index of the desired output in the bitcoin transaction
|
|
352
|
+
*
|
|
353
|
+
* @private
|
|
354
|
+
*/
|
|
355
|
+
private inferRequiredConfirmationsCount(btcTx: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout: number): number | undefined {
|
|
356
|
+
const txOut = btcTx.outs[vout];
|
|
357
|
+
for(let i=1;i<=20;i++) {
|
|
358
|
+
const computedClaimHash = this.wrapper._contract.getHashForOnchain(
|
|
359
|
+
Buffer.from(txOut.scriptPubKey.hex, "hex"),
|
|
360
|
+
BigInt(txOut.value),
|
|
361
|
+
i
|
|
362
|
+
);
|
|
363
|
+
if(computedClaimHash.toString("hex")===this._data.getClaimHash()) {
|
|
364
|
+
return i;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @inheritDoc
|
|
371
|
+
*/
|
|
242
372
|
getRequiredConfirmationsCount(): number {
|
|
243
|
-
return this.requiredConfirmations;
|
|
373
|
+
return this.requiredConfirmations ?? NaN;
|
|
244
374
|
}
|
|
245
375
|
|
|
246
376
|
/**
|
|
247
|
-
* Checks whether a bitcoin payment was already made, returns the payment or null when no payment has been made.
|
|
377
|
+
* Checks whether a bitcoin payment was already made, returns the payment or `null` when no payment has been made.
|
|
378
|
+
*
|
|
379
|
+
* @internal
|
|
248
380
|
*/
|
|
249
381
|
protected async getBitcoinPayment(): Promise<{
|
|
250
382
|
txId: string,
|
|
@@ -253,32 +385,39 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
253
385
|
targetConfirmations: number,
|
|
254
386
|
inputAddresses?: string[]
|
|
255
387
|
} | null> {
|
|
256
|
-
const txoHashHint = this.
|
|
257
|
-
if(txoHashHint==null) throw new Error("Swap data
|
|
388
|
+
const txoHashHint = this._data.getTxoHashHint();
|
|
389
|
+
if(txoHashHint==null) throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
|
|
390
|
+
if(this.address==null) throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
|
|
258
391
|
|
|
259
|
-
const result = await this.wrapper.
|
|
392
|
+
const result = await this.wrapper._btcRpc.checkAddressTxos(this.address, Buffer.from(txoHashHint, "hex"));
|
|
260
393
|
if(result==null) return null;
|
|
261
394
|
|
|
395
|
+
if(this.requiredConfirmations==null) {
|
|
396
|
+
this.requiredConfirmations = this.inferRequiredConfirmationsCount(result.tx, result.vout);
|
|
397
|
+
}
|
|
398
|
+
|
|
262
399
|
return {
|
|
263
400
|
inputAddresses: result.tx.inputAddresses,
|
|
264
401
|
txId: result.tx.txid,
|
|
265
402
|
vout: result.vout,
|
|
266
403
|
confirmations: result.tx.confirmations ?? 0,
|
|
267
|
-
targetConfirmations: this.
|
|
404
|
+
targetConfirmations: this.getRequiredConfirmationsCount()
|
|
268
405
|
}
|
|
269
406
|
}
|
|
270
407
|
|
|
271
408
|
/**
|
|
272
|
-
*
|
|
409
|
+
* Used to set the txId of the bitcoin payment from the on-chain events listener
|
|
273
410
|
*
|
|
274
|
-
* @param txId
|
|
411
|
+
* @param txId Transaction ID that settled the swap on the smart chain
|
|
412
|
+
*
|
|
413
|
+
* @internal
|
|
275
414
|
*/
|
|
276
415
|
async _setBitcoinTxId(txId: string) {
|
|
277
416
|
if(this.txId!==txId || this.address==null || this.vout==null || this.senderAddress==null || this.amount==null) {
|
|
278
|
-
const btcTx = await this.wrapper.
|
|
417
|
+
const btcTx = await this.wrapper._btcRpc.getTransaction(txId);
|
|
279
418
|
if(btcTx==null) return;
|
|
280
419
|
|
|
281
|
-
const txoHashHint = this.
|
|
420
|
+
const txoHashHint = this._data.getTxoHashHint();
|
|
282
421
|
if(txoHashHint!=null) {
|
|
283
422
|
const expectedTxoHash = Buffer.from(txoHashHint, "hex");
|
|
284
423
|
const vout = btcTx.outs.findIndex(out => getTxoHash(out.scriptPubKey.hex, out.value).equals(expectedTxoHash));
|
|
@@ -289,11 +428,13 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
289
428
|
// hence doesn't contain the address and amount data
|
|
290
429
|
if(this.amount==null) this.amount = BigInt(btcTx.outs[vout].value);
|
|
291
430
|
if(this.address==null) try {
|
|
292
|
-
|
|
293
|
-
this.address = Address(this.wrapper.options.bitcoinNetwork).encode(addressData);
|
|
431
|
+
this.address = fromOutputScript(this.wrapper._options.bitcoinNetwork, btcTx.outs[vout].scriptPubKey.hex);
|
|
294
432
|
} catch (e: any) {
|
|
295
433
|
this.logger.warn("_setBitcoinTxId(): Failed to parse address from output script: ", e);
|
|
296
434
|
}
|
|
435
|
+
if(this.requiredConfirmations==null) {
|
|
436
|
+
this.requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
|
|
437
|
+
}
|
|
297
438
|
}
|
|
298
439
|
}
|
|
299
440
|
|
|
@@ -306,40 +447,67 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
306
447
|
}
|
|
307
448
|
|
|
308
449
|
/**
|
|
309
|
-
*
|
|
450
|
+
* @inheritDoc
|
|
310
451
|
*
|
|
311
|
-
* @
|
|
312
|
-
* @param checkIntervalSeconds How often to check the bitcoin transaction
|
|
313
|
-
* @param abortSignal Abort signal
|
|
314
|
-
* @throws {Error} if in invalid state (must be CLAIM_COMMITED)
|
|
452
|
+
* @throws {Error} if in invalid state (must be {@link FromBTCSwapState.CLAIM_COMMITED})
|
|
315
453
|
*/
|
|
316
454
|
async waitForBitcoinTransaction(
|
|
317
455
|
updateCallback?: (txId?: string, confirmations?: number, targetConfirmations?: number, txEtaMs?: number) => void,
|
|
318
456
|
checkIntervalSeconds?: number,
|
|
319
457
|
abortSignal?: AbortSignal
|
|
320
458
|
): Promise<string> {
|
|
321
|
-
if(this.
|
|
322
|
-
const txoHashHint = this.
|
|
323
|
-
if(txoHashHint==null) throw new Error("Swap data
|
|
459
|
+
if(this._state!==FromBTCSwapState.CLAIM_COMMITED && this._state!==FromBTCSwapState.EXPIRED) throw new Error("Must be in COMMITED state!");
|
|
460
|
+
const txoHashHint = this._data.getTxoHashHint();
|
|
461
|
+
if(txoHashHint==null) throw new Error("Swap data doesn't include the txo hash hint! Cannot check bitcoin transaction!");
|
|
462
|
+
if(this.address==null) throw new Error("Cannot check bitcoin payment, because the address is not known! This can happen after a swap is recovered.");
|
|
463
|
+
|
|
464
|
+
let abortedDueToEnoughConfirmationsResult: {
|
|
465
|
+
tx: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout: number
|
|
466
|
+
} | undefined;
|
|
467
|
+
const abortController = extendAbortController(abortSignal);
|
|
324
468
|
|
|
325
|
-
const result = await this.wrapper.
|
|
469
|
+
const result = await this.wrapper._btcRpc.waitForAddressTxo(
|
|
326
470
|
this.address,
|
|
327
471
|
Buffer.from(txoHashHint, "hex"),
|
|
328
|
-
this.requiredConfirmations,
|
|
472
|
+
this.requiredConfirmations ?? 6, //In case confirmation count is not known, we use a conservative estimate
|
|
329
473
|
(btcTx?: Omit<BtcTxWithBlockheight, "hex" | "raw">, vout?: number, txEtaMs?: number) => {
|
|
330
|
-
|
|
331
|
-
|
|
474
|
+
let requiredConfirmations = this.requiredConfirmations;
|
|
475
|
+
|
|
476
|
+
if(btcTx!=null && vout!=null && requiredConfirmations==null) {
|
|
477
|
+
requiredConfirmations = this.inferRequiredConfirmationsCount(btcTx, vout);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if(btcTx!=null && (btcTx.txid!==this.txId || (this.requiredConfirmations==null && requiredConfirmations!=null))) {
|
|
332
481
|
this.txId = btcTx.txid;
|
|
333
482
|
this.vout = vout;
|
|
483
|
+
this.requiredConfirmations = requiredConfirmations;
|
|
334
484
|
if(btcTx.inputAddresses!=null) this.senderAddress = btcTx.inputAddresses[0];
|
|
335
485
|
this._saveAndEmit().catch(e => {
|
|
336
486
|
this.logger.error("waitForBitcoinTransaction(): Failed to save swap from within waitForAddressTxo callback:", e)
|
|
337
487
|
});
|
|
338
488
|
}
|
|
489
|
+
|
|
490
|
+
//Abort the loop as soon as the transaction gets enough confirmations, this is required in case
|
|
491
|
+
// we pass a default 6 confirmations to the fn, but then are able to infer the actual confirmation
|
|
492
|
+
// target from the prior block
|
|
493
|
+
if(btcTx?.confirmations!=null && requiredConfirmations!=null && requiredConfirmations<=btcTx.confirmations && vout!=null) {
|
|
494
|
+
abortedDueToEnoughConfirmationsResult = {
|
|
495
|
+
tx: btcTx,
|
|
496
|
+
vout
|
|
497
|
+
};
|
|
498
|
+
abortController.abort();
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if(updateCallback!=null) updateCallback(btcTx?.txid, btcTx==null ? undefined : (btcTx?.confirmations ?? 0), requiredConfirmations ?? NaN, txEtaMs);
|
|
339
503
|
},
|
|
340
|
-
|
|
504
|
+
abortController.signal,
|
|
341
505
|
checkIntervalSeconds
|
|
342
|
-
)
|
|
506
|
+
).catch(e => {
|
|
507
|
+
//We catch the case when the loop was aborted due to the transaction getting enough confirmations
|
|
508
|
+
if(abortedDueToEnoughConfirmationsResult!=null) return abortedDueToEnoughConfirmationsResult;
|
|
509
|
+
throw e;
|
|
510
|
+
});
|
|
343
511
|
|
|
344
512
|
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
345
513
|
|
|
@@ -348,10 +516,10 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
348
516
|
if(result.tx.inputAddresses!=null) this.senderAddress = result.tx.inputAddresses[0];
|
|
349
517
|
|
|
350
518
|
if(
|
|
351
|
-
(this.
|
|
352
|
-
(this.
|
|
519
|
+
(this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
|
|
520
|
+
(this._state as FromBTCSwapState)!==FromBTCSwapState.FAILED
|
|
353
521
|
) {
|
|
354
|
-
this.
|
|
522
|
+
this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
|
|
355
523
|
}
|
|
356
524
|
|
|
357
525
|
await this._saveAndEmit();
|
|
@@ -360,34 +528,26 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
360
528
|
}
|
|
361
529
|
|
|
362
530
|
/**
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
531
|
+
* Private getter of the funded PSBT that doesn't check current state
|
|
532
|
+
*
|
|
533
|
+
* @param _bitcoinWallet Bitcoin wallet to fund the PSBT with
|
|
534
|
+
* @param feeRate Optional bitcoin fee rate in sats/vB
|
|
535
|
+
* @param additionalOutputs Optional additional outputs that should also be included in the generated PSBT
|
|
366
536
|
*
|
|
367
|
-
* @
|
|
368
|
-
* @param feeRate Optional fee rate for the transaction, needs to be at least as big as {minimumBtcFeeRate} field
|
|
369
|
-
* @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
|
|
537
|
+
* @private
|
|
370
538
|
*/
|
|
371
|
-
getFundedPsbt(
|
|
372
|
-
_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
|
|
373
|
-
feeRate?: number,
|
|
374
|
-
additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
|
|
375
|
-
) {
|
|
376
|
-
if(this.state!==FromBTCSwapState.CLAIM_COMMITED)
|
|
377
|
-
throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
|
|
378
|
-
return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
539
|
private async _getFundedPsbt(
|
|
382
540
|
_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
|
|
383
541
|
feeRate?: number,
|
|
384
542
|
additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
|
|
385
543
|
): Promise<{psbt: Transaction, psbtHex: string, psbtBase64: string, signInputs: number[]}> {
|
|
544
|
+
if(this.address==null) throw new Error("Cannot create funded PSBT, because the address is not known! This can happen after a swap is recovered.");
|
|
545
|
+
|
|
386
546
|
let bitcoinWallet: IBitcoinWallet;
|
|
387
547
|
if(isIBitcoinWallet(_bitcoinWallet)) {
|
|
388
548
|
bitcoinWallet = _bitcoinWallet;
|
|
389
549
|
} else {
|
|
390
|
-
bitcoinWallet = new SingleAddressBitcoinWallet(this.wrapper.
|
|
550
|
+
bitcoinWallet = new SingleAddressBitcoinWallet(this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork, _bitcoinWallet);
|
|
391
551
|
}
|
|
392
552
|
//TODO: Maybe re-introduce fee rate check here if passed from the user
|
|
393
553
|
if(feeRate==null) {
|
|
@@ -400,12 +560,12 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
400
560
|
});
|
|
401
561
|
basePsbt.addOutput({
|
|
402
562
|
amount: this.amount,
|
|
403
|
-
script: toOutputScript(this.wrapper.
|
|
563
|
+
script: toOutputScript(this.wrapper._options.bitcoinNetwork, this.address)
|
|
404
564
|
});
|
|
405
565
|
if(additionalOutputs!=null) additionalOutputs.forEach(output => {
|
|
406
566
|
basePsbt.addOutput({
|
|
407
567
|
amount: output.amount,
|
|
408
|
-
script: (output as {outputScript: Uint8Array}).outputScript ?? toOutputScript(this.wrapper.
|
|
568
|
+
script: (output as {outputScript: Uint8Array}).outputScript ?? toOutputScript(this.wrapper._options.bitcoinNetwork, (output as {address: string}).address)
|
|
409
569
|
});
|
|
410
570
|
});
|
|
411
571
|
|
|
@@ -425,13 +585,27 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
425
585
|
}
|
|
426
586
|
|
|
427
587
|
/**
|
|
428
|
-
*
|
|
588
|
+
* @inheritDoc
|
|
589
|
+
*/
|
|
590
|
+
getFundedPsbt(
|
|
591
|
+
_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
|
|
592
|
+
feeRate?: number,
|
|
593
|
+
additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
|
|
594
|
+
) {
|
|
595
|
+
if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
|
|
596
|
+
throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
|
|
597
|
+
return this._getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* @inheritDoc
|
|
429
602
|
*
|
|
430
|
-
* @
|
|
603
|
+
* @throws {Error} if the swap is in invalid state (not in {@link FromBTCSwapState.CLAIM_COMMITED}), or if
|
|
604
|
+
* the swap bitcoin address already expired.
|
|
431
605
|
*/
|
|
432
606
|
async submitPsbt(_psbt: Transaction | string): Promise<string> {
|
|
433
607
|
const psbt = parsePsbtTransaction(_psbt);
|
|
434
|
-
if(this.
|
|
608
|
+
if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
|
|
435
609
|
throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
|
|
436
610
|
|
|
437
611
|
//Ensure not expired
|
|
@@ -440,26 +614,37 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
440
614
|
}
|
|
441
615
|
|
|
442
616
|
const output0 = psbt.getOutput(0);
|
|
443
|
-
if(output0.amount!==this.amount)
|
|
617
|
+
if(this.amount!=null && output0.amount!==this.amount)
|
|
444
618
|
throw new Error("PSBT output amount invalid, expected: "+this.amount+" got: "+output0.amount);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
619
|
+
if(this.address!=null) {
|
|
620
|
+
const expectedOutputScript = toOutputScript(this.wrapper._options.bitcoinNetwork, this.address);
|
|
621
|
+
if(output0.script==null || !expectedOutputScript.equals(output0.script))
|
|
622
|
+
throw new Error("PSBT output script invalid!");
|
|
623
|
+
}
|
|
448
624
|
|
|
449
625
|
if(!psbt.isFinal) psbt.finalize();
|
|
450
626
|
|
|
451
|
-
return await this.wrapper.
|
|
627
|
+
return await this.wrapper._btcRpc.sendRawTransaction(Buffer.from(psbt.toBytes(true, true)).toString("hex"));
|
|
452
628
|
}
|
|
453
629
|
|
|
630
|
+
/**
|
|
631
|
+
* @inheritDoc
|
|
632
|
+
*/
|
|
454
633
|
async estimateBitcoinFee(_bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface, feeRate?: number): Promise<TokenAmount<any, BtcToken<false>, true> | null> {
|
|
455
|
-
|
|
634
|
+
if(this.address==null || this.amount==null) return null;
|
|
635
|
+
const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
|
|
456
636
|
const txFee = await bitcoinWallet.getTransactionFee(this.address, this.amount, feeRate);
|
|
457
637
|
if(txFee==null) return null;
|
|
458
|
-
return toTokenAmount(BigInt(txFee), BitcoinTokens.BTC, this.wrapper.
|
|
638
|
+
return toTokenAmount(BigInt(txFee), BitcoinTokens.BTC, this.wrapper._prices);
|
|
459
639
|
}
|
|
460
640
|
|
|
641
|
+
/**
|
|
642
|
+
* @inheritDoc
|
|
643
|
+
*/
|
|
461
644
|
async sendBitcoinTransaction(wallet: IBitcoinWallet | MinimalBitcoinWalletInterfaceWithSigner, feeRate?: number): Promise<string> {
|
|
462
|
-
if(this.
|
|
645
|
+
if(this.address==null || this.amount==null) throw new Error("Cannot send bitcoin transaction, because the address is not known! This can happen after a swap is recovered.");
|
|
646
|
+
|
|
647
|
+
if(this._state!==FromBTCSwapState.CLAIM_COMMITED)
|
|
463
648
|
throw new Error("Swap not committed yet, please initiate the swap first with commit() call!");
|
|
464
649
|
|
|
465
650
|
//Ensure not expired
|
|
@@ -514,15 +699,15 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
514
699
|
maxWaitTillAutomaticSettlementSeconds?: number
|
|
515
700
|
}
|
|
516
701
|
): Promise<boolean> {
|
|
517
|
-
if(this.
|
|
518
|
-
if(this.
|
|
519
|
-
if(this.
|
|
520
|
-
if(this.
|
|
702
|
+
if(this._state===FromBTCSwapState.FAILED) throw new Error("Swap failed!");
|
|
703
|
+
if(this._state===FromBTCSwapState.EXPIRED) throw new Error("Swap address expired!");
|
|
704
|
+
if(this._state===FromBTCSwapState.QUOTE_EXPIRED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Swap quote expired!");
|
|
705
|
+
if(this._state===FromBTCSwapState.CLAIM_CLAIMED) throw new Error("Swap already settled!");
|
|
521
706
|
|
|
522
|
-
if(this.
|
|
707
|
+
if(this._state===FromBTCSwapState.PR_CREATED) {
|
|
523
708
|
await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
|
|
524
709
|
}
|
|
525
|
-
if(this.
|
|
710
|
+
if(this._state===FromBTCSwapState.CLAIM_COMMITED) {
|
|
526
711
|
if(wallet!=null) {
|
|
527
712
|
const bitcoinPaymentSent = await this.getBitcoinPayment();
|
|
528
713
|
|
|
@@ -538,9 +723,9 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
538
723
|
}
|
|
539
724
|
|
|
540
725
|
// @ts-ignore
|
|
541
|
-
if(this.
|
|
726
|
+
if(this._state===FromBTCSwapState.CLAIM_CLAIMED) return true;
|
|
542
727
|
|
|
543
|
-
if(this.
|
|
728
|
+
if(this._state===FromBTCSwapState.BTC_TX_CONFIRMED) {
|
|
544
729
|
const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
|
|
545
730
|
if(success && callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
|
|
546
731
|
return success;
|
|
@@ -549,12 +734,23 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
549
734
|
throw new Error("Invalid state reached!");
|
|
550
735
|
}
|
|
551
736
|
|
|
737
|
+
/**
|
|
738
|
+
* @inheritDoc
|
|
739
|
+
*
|
|
740
|
+
* @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
|
|
741
|
+
* psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
|
|
742
|
+
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap
|
|
743
|
+
* wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
|
|
744
|
+
* can use `skipChecks=true`)
|
|
745
|
+
*
|
|
746
|
+
* @throws {Error} if the swap or quote is expired, or if triggered in invalid state
|
|
747
|
+
*/
|
|
552
748
|
async txsExecute(options?: {
|
|
553
749
|
bitcoinWallet?: MinimalBitcoinWalletInterface,
|
|
554
750
|
skipChecks?: boolean
|
|
555
751
|
}) {
|
|
556
|
-
if(this.
|
|
557
|
-
if(!await this.
|
|
752
|
+
if(this._state===FromBTCSwapState.PR_CREATED) {
|
|
753
|
+
if(!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
|
|
558
754
|
if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
|
|
559
755
|
return [
|
|
560
756
|
{
|
|
@@ -566,38 +762,38 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
566
762
|
{
|
|
567
763
|
name: "Payment" as const,
|
|
568
764
|
description: "Send funds to the bitcoin swap address",
|
|
569
|
-
chain: "BITCOIN",
|
|
765
|
+
chain: "BITCOIN" as const,
|
|
570
766
|
txs: [
|
|
571
767
|
options?.bitcoinWallet==null ? {
|
|
572
|
-
address: this.address,
|
|
768
|
+
address: this.address ?? "",
|
|
573
769
|
amount: Number(this.amount),
|
|
574
770
|
hyperlink: this._getHyperlink(),
|
|
575
|
-
type: "ADDRESS"
|
|
771
|
+
type: "ADDRESS" as const
|
|
576
772
|
} : {
|
|
577
773
|
...await this.getFundedPsbt(options.bitcoinWallet),
|
|
578
|
-
type: "FUNDED_PSBT"
|
|
774
|
+
type: "FUNDED_PSBT" as const
|
|
579
775
|
}
|
|
580
776
|
]
|
|
581
777
|
}
|
|
582
778
|
];
|
|
583
779
|
}
|
|
584
780
|
|
|
585
|
-
if(this.
|
|
781
|
+
if(this._state===FromBTCSwapState.CLAIM_COMMITED) {
|
|
586
782
|
if(this.getTimeoutTime()<Date.now()) throw new Error("Swap address already expired or close to expiry!");
|
|
587
783
|
return [
|
|
588
784
|
{
|
|
589
785
|
name: "Payment" as const,
|
|
590
786
|
description: "Send funds to the bitcoin swap address",
|
|
591
|
-
chain: "BITCOIN",
|
|
787
|
+
chain: "BITCOIN" as const,
|
|
592
788
|
txs: [
|
|
593
789
|
options?.bitcoinWallet==null ? {
|
|
594
|
-
address: this.
|
|
790
|
+
address: this.getAddress(),
|
|
595
791
|
amount: Number(this.amount),
|
|
596
792
|
hyperlink: this._getHyperlink(),
|
|
597
|
-
type: "ADDRESS"
|
|
793
|
+
type: "ADDRESS" as const
|
|
598
794
|
} : {
|
|
599
795
|
...await this.getFundedPsbt(options.bitcoinWallet),
|
|
600
|
-
type: "FUNDED_PSBT"
|
|
796
|
+
type: "FUNDED_PSBT" as const
|
|
601
797
|
}
|
|
602
798
|
]
|
|
603
799
|
}
|
|
@@ -612,21 +808,16 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
612
808
|
//// Commit
|
|
613
809
|
|
|
614
810
|
/**
|
|
615
|
-
*
|
|
811
|
+
* @inheritDoc
|
|
616
812
|
*
|
|
617
|
-
* @param _signer Signer to sign the transactions with, must be the same as used in the initialization
|
|
618
|
-
* @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
|
|
619
|
-
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
620
|
-
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
621
|
-
* @param onBeforeTxSent
|
|
622
813
|
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
623
814
|
*/
|
|
624
815
|
async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
625
|
-
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.
|
|
816
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
626
817
|
this.checkSigner(signer);
|
|
627
818
|
let txCount = 0;
|
|
628
819
|
const txs = await this.txsCommit(skipChecks);
|
|
629
|
-
const result = await this.wrapper.
|
|
820
|
+
const result = await this.wrapper._chain.sendAndConfirm(
|
|
630
821
|
signer, txs, true, abortSignal, undefined, (txId: string) => {
|
|
631
822
|
txCount++;
|
|
632
823
|
if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
|
|
@@ -634,16 +825,19 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
634
825
|
}
|
|
635
826
|
);
|
|
636
827
|
|
|
637
|
-
this.
|
|
638
|
-
if(this.
|
|
828
|
+
this._commitTxId = result[result.length - 1];
|
|
829
|
+
if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
639
830
|
await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
|
|
640
831
|
}
|
|
641
|
-
return this.
|
|
832
|
+
return this._commitTxId;
|
|
642
833
|
}
|
|
643
834
|
|
|
835
|
+
/**
|
|
836
|
+
* @inheritDoc
|
|
837
|
+
*/
|
|
644
838
|
async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
|
|
645
|
-
if(this.
|
|
646
|
-
if(this.
|
|
839
|
+
if(this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve();
|
|
840
|
+
if(this._state!==FromBTCSwapState.PR_CREATED && this._state!==FromBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state");
|
|
647
841
|
|
|
648
842
|
const abortController = extendAbortController(abortSignal);
|
|
649
843
|
const result = await Promise.race([
|
|
@@ -656,13 +850,13 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
656
850
|
if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
657
851
|
if(result===false) {
|
|
658
852
|
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
|
|
659
|
-
if(this.
|
|
853
|
+
if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
660
854
|
await this._saveAndEmit(FromBTCSwapState.QUOTE_EXPIRED);
|
|
661
855
|
}
|
|
662
856
|
return;
|
|
663
857
|
}
|
|
664
858
|
|
|
665
|
-
if(this.
|
|
859
|
+
if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
666
860
|
await this._saveAndEmit(FromBTCSwapState.CLAIM_COMMITED);
|
|
667
861
|
}
|
|
668
862
|
}
|
|
@@ -672,10 +866,11 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
672
866
|
//// Claim
|
|
673
867
|
|
|
674
868
|
/**
|
|
675
|
-
*
|
|
676
|
-
* after a bitcoin transaction was sent and confirmed
|
|
869
|
+
* Might also return transactions necessary to sync the bitcoin light client.
|
|
677
870
|
*
|
|
678
|
-
* @
|
|
871
|
+
* @inheritDoc
|
|
872
|
+
*
|
|
873
|
+
* @throws {Error} If the swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
|
|
679
874
|
*/
|
|
680
875
|
async txsClaim(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
681
876
|
let signer: string | T["Signer"] | undefined = undefined;
|
|
@@ -685,41 +880,44 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
685
880
|
} else if (isAbstractSigner(_signer)) {
|
|
686
881
|
signer = _signer;
|
|
687
882
|
} else {
|
|
688
|
-
signer = await this.wrapper.
|
|
883
|
+
signer = await this.wrapper._chain.wrapSigner(_signer);
|
|
689
884
|
}
|
|
690
885
|
}
|
|
691
886
|
|
|
692
|
-
if(this.
|
|
887
|
+
if(this._state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Must be in BTC_TX_CONFIRMED state!");
|
|
693
888
|
if(this.txId==null || this.vout==null) throw new Error("Bitcoin transaction ID not known!");
|
|
694
889
|
|
|
695
|
-
const tx = await this.wrapper.
|
|
890
|
+
const tx = await this.wrapper._btcRpc.getTransaction(this.txId);
|
|
696
891
|
if(tx==null) throw new Error("Bitcoin transaction not found on the network!");
|
|
892
|
+
|
|
893
|
+
this.requiredConfirmations ??= this.inferRequiredConfirmationsCount(tx, this.vout);
|
|
894
|
+
if(this.requiredConfirmations==null)
|
|
895
|
+
throw new Error("Cannot create claim transaction, because required confirmations are not known and cannot be infered! This can happen after a swap is recovered.");
|
|
896
|
+
|
|
697
897
|
if(tx.blockhash==null || tx.confirmations==null || tx.blockheight==null || tx.confirmations<this.requiredConfirmations)
|
|
698
898
|
throw new Error("Bitcoin transaction not confirmed yet!");
|
|
699
899
|
|
|
700
|
-
return await this.wrapper.
|
|
900
|
+
return await this.wrapper._contract.txsClaimWithTxData(signer ?? this._getInitiator(), this._data, {
|
|
701
901
|
blockhash: tx.blockhash,
|
|
702
902
|
confirmations: tx.confirmations,
|
|
703
903
|
txid: tx.txid,
|
|
704
904
|
hex: tx.hex,
|
|
705
905
|
height: tx.blockheight
|
|
706
|
-
}, this.requiredConfirmations, this.vout, undefined, this.wrapper.
|
|
906
|
+
}, this.requiredConfirmations, this.vout, undefined, this.wrapper._synchronizer, true);
|
|
707
907
|
}
|
|
708
908
|
|
|
709
909
|
/**
|
|
710
|
-
*
|
|
910
|
+
* Might also sync the bitcoin light client. Signer can also be different to the initializer.
|
|
711
911
|
*
|
|
712
|
-
* @
|
|
713
|
-
* @param abortSignal Abort signal to stop waiting for transaction confirmation
|
|
714
|
-
* @param onBeforeTxSent
|
|
912
|
+
* @inheritDoc
|
|
715
913
|
*/
|
|
716
914
|
async claim(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
717
|
-
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.
|
|
915
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
718
916
|
let txIds: string[];
|
|
719
917
|
try {
|
|
720
918
|
let txCount = 0;
|
|
721
919
|
const txs = await this.txsClaim(signer);
|
|
722
|
-
txIds = await this.wrapper.
|
|
920
|
+
txIds = await this.wrapper._chain.sendAndConfirm(
|
|
723
921
|
signer, txs, true, abortSignal, undefined, (txId: string) => {
|
|
724
922
|
txCount++;
|
|
725
923
|
if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
|
|
@@ -728,26 +926,26 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
728
926
|
);
|
|
729
927
|
} catch (e) {
|
|
730
928
|
this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
|
|
731
|
-
if(this.
|
|
929
|
+
if(this._state===FromBTCSwapState.CLAIM_CLAIMED) {
|
|
732
930
|
this.logger.info("claim(): Transaction state is CLAIM_CLAIMED, swap was successfully claimed by the watchtower");
|
|
733
|
-
return this.
|
|
931
|
+
return this._claimTxId!;
|
|
734
932
|
}
|
|
735
|
-
const status = await this.wrapper.
|
|
933
|
+
const status = await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
736
934
|
if(status?.type===SwapCommitStateType.PAID) {
|
|
737
935
|
this.logger.info("claim(): Transaction commit status is PAID, swap was successfully claimed by the watchtower");
|
|
738
|
-
if(this.
|
|
936
|
+
if(this._claimTxId==null) this._claimTxId = await status.getClaimTxId();
|
|
739
937
|
const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
|
|
740
938
|
await this._setBitcoinTxId(txId);
|
|
741
939
|
await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
|
|
742
|
-
return this.
|
|
940
|
+
return this._claimTxId;
|
|
743
941
|
}
|
|
744
942
|
throw e;
|
|
745
943
|
}
|
|
746
944
|
|
|
747
|
-
this.
|
|
945
|
+
this._claimTxId = txIds[txIds.length - 1];
|
|
748
946
|
if(
|
|
749
|
-
this.
|
|
750
|
-
this.
|
|
947
|
+
this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
|
|
948
|
+
this._state===FromBTCSwapState.EXPIRED || this._state===FromBTCSwapState.FAILED
|
|
751
949
|
) {
|
|
752
950
|
await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
|
|
753
951
|
}
|
|
@@ -755,17 +953,16 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
755
953
|
}
|
|
756
954
|
|
|
757
955
|
/**
|
|
758
|
-
*
|
|
956
|
+
* @inheritDoc
|
|
759
957
|
*
|
|
760
|
-
* @
|
|
761
|
-
* @param abortSignal AbortSignal
|
|
762
|
-
* @throws {Error} If swap is in invalid state (must be BTC_TX_CONFIRMED)
|
|
958
|
+
* @throws {Error} If swap is in invalid state (must be {@link FromBTCSwapState.BTC_TX_CONFIRMED})
|
|
763
959
|
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
960
|
+
*
|
|
764
961
|
* @returns {boolean} whether the swap was claimed in time or not
|
|
765
962
|
*/
|
|
766
963
|
async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
767
|
-
if(this.
|
|
768
|
-
if(this.
|
|
964
|
+
if(this._state===FromBTCSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
|
|
965
|
+
if(this._state!==FromBTCSwapState.BTC_TX_CONFIRMED) throw new Error("Invalid state (not BTC_TX_CONFIRMED)");
|
|
769
966
|
|
|
770
967
|
const abortController = extendAbortController(abortSignal);
|
|
771
968
|
|
|
@@ -803,8 +1000,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
803
1000
|
this.logger.debug("waitTillClaimed(): Resolved from watchdog");
|
|
804
1001
|
|
|
805
1002
|
if(res?.type===SwapCommitStateType.PAID) {
|
|
806
|
-
if((this.
|
|
807
|
-
if(this.
|
|
1003
|
+
if((this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED) {
|
|
1004
|
+
if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
|
|
808
1005
|
const txId = Buffer.from(await res.getClaimResult(), "hex").reverse().toString("hex");
|
|
809
1006
|
await this._setBitcoinTxId(txId);
|
|
810
1007
|
await this._saveAndEmit(FromBTCSwapState.CLAIM_CLAIMED);
|
|
@@ -812,10 +1009,10 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
812
1009
|
}
|
|
813
1010
|
if(res?.type===SwapCommitStateType.NOT_COMMITED || res?.type===SwapCommitStateType.EXPIRED) {
|
|
814
1011
|
if(
|
|
815
|
-
(this.
|
|
816
|
-
(this.
|
|
1012
|
+
(this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
|
|
1013
|
+
(this._state as FromBTCSwapState)!==FromBTCSwapState.FAILED
|
|
817
1014
|
) {
|
|
818
|
-
if(res.getRefundTxId!=null) this.
|
|
1015
|
+
if(res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
|
|
819
1016
|
await this._saveAndEmit(FromBTCSwapState.FAILED);
|
|
820
1017
|
}
|
|
821
1018
|
throw new Error("Swap expired while waiting for claim!");
|
|
@@ -828,11 +1025,14 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
828
1025
|
//////////////////////////////
|
|
829
1026
|
//// Storage
|
|
830
1027
|
|
|
1028
|
+
/**
|
|
1029
|
+
* @inheritDoc
|
|
1030
|
+
*/
|
|
831
1031
|
serialize(): any {
|
|
832
1032
|
return {
|
|
833
1033
|
...super.serialize(),
|
|
834
1034
|
address: this.address,
|
|
835
|
-
amount: this.amount.toString(10),
|
|
1035
|
+
amount: this.amount==null ? null: this.amount.toString(10),
|
|
836
1036
|
requiredConfirmations: this.requiredConfirmations,
|
|
837
1037
|
senderAddress: this.senderAddress,
|
|
838
1038
|
txId: this.txId,
|
|
@@ -851,105 +1051,132 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
851
1051
|
* @private
|
|
852
1052
|
*/
|
|
853
1053
|
private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
854
|
-
if(
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
return true;
|
|
865
|
-
case SwapCommitStateType.PAID:
|
|
866
|
-
if(this.claimTxId==null) this.claimTxId = await status.getClaimTxId();
|
|
867
|
-
const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
|
|
868
|
-
await this._setBitcoinTxId(txId);
|
|
869
|
-
this.state = FromBTCSwapState.CLAIM_CLAIMED;
|
|
870
|
-
return true;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
if(quoteExpired) {
|
|
874
|
-
this.state = FromBTCSwapState.QUOTE_EXPIRED;
|
|
875
|
-
return true;
|
|
1054
|
+
if(
|
|
1055
|
+
this._state===FromBTCSwapState.PR_CREATED ||
|
|
1056
|
+
this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
1057
|
+
this._state===FromBTCSwapState.CLAIM_COMMITED ||
|
|
1058
|
+
this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
|
|
1059
|
+
this._state===FromBTCSwapState.EXPIRED
|
|
1060
|
+
) {
|
|
1061
|
+
let quoteExpired: boolean = false;
|
|
1062
|
+
if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
1063
|
+
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired(); //Make sure we check for expiry here, to prevent race conditions
|
|
876
1064
|
}
|
|
877
1065
|
|
|
878
|
-
|
|
879
|
-
|
|
1066
|
+
const status = commitStatus ?? await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
1067
|
+
if(status!=null && await this._forciblySetOnchainState(status)) return true;
|
|
880
1068
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
case SwapCommitStateType.PAID:
|
|
885
|
-
if(this.claimTxId==null) this.claimTxId = await status.getClaimTxId();
|
|
886
|
-
const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
|
|
887
|
-
await this._setBitcoinTxId(txId);
|
|
888
|
-
this.state = FromBTCSwapState.CLAIM_CLAIMED;
|
|
889
|
-
return true;
|
|
890
|
-
case SwapCommitStateType.NOT_COMMITED:
|
|
891
|
-
case SwapCommitStateType.EXPIRED:
|
|
892
|
-
if(this.refundTxId==null && status.getRefundTxId) this.refundTxId = await status.getRefundTxId();
|
|
893
|
-
this.state = FromBTCSwapState.FAILED;
|
|
1069
|
+
if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
1070
|
+
if(quoteExpired) {
|
|
1071
|
+
this._state = FromBTCSwapState.QUOTE_EXPIRED;
|
|
894
1072
|
return true;
|
|
895
|
-
|
|
896
|
-
const res = await this.getBitcoinPayment();
|
|
897
|
-
if(res!=null) {
|
|
898
|
-
let save: boolean = false;
|
|
899
|
-
if(this.txId!==res.txId) {
|
|
900
|
-
if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
|
|
901
|
-
this.txId = res.txId;
|
|
902
|
-
this.vout = res.vout;
|
|
903
|
-
save = true;
|
|
904
|
-
}
|
|
905
|
-
if(res.confirmations>=this.requiredConfirmations) {
|
|
906
|
-
this.state = FromBTCSwapState.BTC_TX_CONFIRMED;
|
|
907
|
-
save = true;
|
|
908
|
-
}
|
|
909
|
-
return save;
|
|
910
|
-
}
|
|
911
|
-
break;
|
|
1073
|
+
}
|
|
912
1074
|
}
|
|
913
1075
|
}
|
|
914
1076
|
|
|
915
1077
|
return false;
|
|
916
1078
|
}
|
|
917
1079
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1080
|
+
/**
|
|
1081
|
+
* @inheritDoc
|
|
1082
|
+
* @internal
|
|
1083
|
+
*/
|
|
1084
|
+
_shouldFetchOnchainState(): boolean {
|
|
1085
|
+
return this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
1086
|
+
this._state===FromBTCSwapState.CLAIM_COMMITED || this._state===FromBTCSwapState.BTC_TX_CONFIRMED ||
|
|
1087
|
+
this._state===FromBTCSwapState.EXPIRED;
|
|
922
1088
|
}
|
|
923
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* @inheritDoc
|
|
1092
|
+
* @internal
|
|
1093
|
+
*/
|
|
924
1094
|
_shouldFetchExpiryStatus(): boolean {
|
|
925
|
-
return this.
|
|
1095
|
+
return this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
926
1096
|
}
|
|
927
1097
|
|
|
1098
|
+
/**
|
|
1099
|
+
* @inheritDoc
|
|
1100
|
+
* @internal
|
|
1101
|
+
*/
|
|
928
1102
|
async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
929
1103
|
const changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
|
|
930
1104
|
if(changed && save) await this._saveAndEmit();
|
|
931
1105
|
return changed;
|
|
932
1106
|
}
|
|
933
1107
|
|
|
1108
|
+
/**
|
|
1109
|
+
* @inheritDoc
|
|
1110
|
+
* @internal
|
|
1111
|
+
*/
|
|
1112
|
+
async _forciblySetOnchainState(status: SwapCommitState): Promise<boolean> {
|
|
1113
|
+
switch(status.type) {
|
|
1114
|
+
case SwapCommitStateType.PAID:
|
|
1115
|
+
if(this._claimTxId==null) this._claimTxId = await status.getClaimTxId();
|
|
1116
|
+
const txId = Buffer.from(await status.getClaimResult(), "hex").reverse().toString("hex");
|
|
1117
|
+
await this._setBitcoinTxId(txId);
|
|
1118
|
+
this._state = FromBTCSwapState.CLAIM_CLAIMED;
|
|
1119
|
+
return true;
|
|
1120
|
+
case SwapCommitStateType.NOT_COMMITED:
|
|
1121
|
+
if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
|
|
1122
|
+
if(this._refundTxId!=null) {
|
|
1123
|
+
this._state = FromBTCSwapState.FAILED;
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
break;
|
|
1127
|
+
case SwapCommitStateType.EXPIRED:
|
|
1128
|
+
if(this._refundTxId==null && status.getRefundTxId) this._refundTxId = await status.getRefundTxId();
|
|
1129
|
+
this._state = this._refundTxId==null ? FromBTCSwapState.QUOTE_EXPIRED : FromBTCSwapState.FAILED;
|
|
1130
|
+
return true;
|
|
1131
|
+
case SwapCommitStateType.COMMITED:
|
|
1132
|
+
let save: boolean = false;
|
|
1133
|
+
if(this._state!==FromBTCSwapState.CLAIM_COMMITED && this._state!==FromBTCSwapState.BTC_TX_CONFIRMED && this._state!==FromBTCSwapState.EXPIRED) {
|
|
1134
|
+
this._state = FromBTCSwapState.CLAIM_COMMITED;
|
|
1135
|
+
save = true;
|
|
1136
|
+
}
|
|
1137
|
+
if(this.address==null) return save;
|
|
1138
|
+
|
|
1139
|
+
const res = await this.getBitcoinPayment();
|
|
1140
|
+
if(res!=null) {
|
|
1141
|
+
if(this.txId!==res.txId) {
|
|
1142
|
+
if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
|
|
1143
|
+
this.txId = res.txId;
|
|
1144
|
+
this.vout = res.vout;
|
|
1145
|
+
save = true;
|
|
1146
|
+
}
|
|
1147
|
+
if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
|
|
1148
|
+
this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
|
|
1149
|
+
save = true;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return save;
|
|
1153
|
+
}
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* @inheritDoc
|
|
1159
|
+
* @internal
|
|
1160
|
+
*/
|
|
934
1161
|
async _tick(save?: boolean): Promise<boolean> {
|
|
935
|
-
switch(this.
|
|
1162
|
+
switch(this._state) {
|
|
936
1163
|
case FromBTCSwapState.PR_CREATED:
|
|
937
1164
|
if(this.expiry<Date.now()) {
|
|
938
|
-
this.
|
|
1165
|
+
this._state = FromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
939
1166
|
if(save) await this._saveAndEmit();
|
|
940
1167
|
return true;
|
|
941
1168
|
}
|
|
942
1169
|
break;
|
|
943
1170
|
case FromBTCSwapState.CLAIM_COMMITED:
|
|
944
1171
|
if(this.getTimeoutTime()<Date.now()) {
|
|
945
|
-
this.
|
|
1172
|
+
this._state = FromBTCSwapState.EXPIRED;
|
|
946
1173
|
if(save) await this._saveAndEmit();
|
|
947
1174
|
return true;
|
|
948
1175
|
}
|
|
949
1176
|
case FromBTCSwapState.EXPIRED:
|
|
950
1177
|
//Check if bitcoin payment was received every 2 minutes
|
|
951
1178
|
if(Math.floor(Date.now()/1000)%120===0) {
|
|
952
|
-
try {
|
|
1179
|
+
if(this.address!=null) try {
|
|
953
1180
|
const res = await this.getBitcoinPayment();
|
|
954
1181
|
if(res!=null) {
|
|
955
1182
|
let shouldSave: boolean = false;
|
|
@@ -959,8 +1186,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
|
|
|
959
1186
|
if(res.inputAddresses!=null) this.senderAddress = res.inputAddresses[0];
|
|
960
1187
|
shouldSave = true;
|
|
961
1188
|
}
|
|
962
|
-
if(res.confirmations>=this.requiredConfirmations) {
|
|
963
|
-
this.
|
|
1189
|
+
if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
|
|
1190
|
+
this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
|
|
964
1191
|
if(save) await this._saveAndEmit();
|
|
965
1192
|
shouldSave = true;
|
|
966
1193
|
}
|