@atomiqlabs/sdk 7.0.12 → 8.0.8
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/README.md +45 -29
- package/dist/SmartChainAssets.d.ts +11 -3
- package/dist/SmartChainAssets.js +7 -3
- package/dist/bitcoin/BitcoinRpcWithAddressIndex.d.ts +68 -0
- package/dist/bitcoin/BitcoinRpcWithAddressIndex.js +2 -0
- package/dist/bitcoin/LightningNetworkApi.d.ts +12 -0
- package/dist/bitcoin/LightningNetworkApi.js +2 -0
- package/dist/bitcoin/coinselect2/accumulative.d.ts +6 -0
- package/dist/bitcoin/coinselect2/accumulative.js +52 -0
- package/dist/bitcoin/coinselect2/blackjack.d.ts +6 -0
- package/dist/bitcoin/coinselect2/blackjack.js +38 -0
- package/dist/bitcoin/coinselect2/index.d.ts +19 -0
- package/dist/bitcoin/coinselect2/index.js +69 -0
- package/dist/bitcoin/coinselect2/utils.d.ts +71 -0
- package/dist/bitcoin/coinselect2/utils.js +123 -0
- package/dist/bitcoin/mempool/MempoolApi.d.ts +350 -0
- package/dist/bitcoin/mempool/MempoolApi.js +311 -0
- package/dist/bitcoin/mempool/MempoolBitcoinBlock.d.ts +44 -0
- package/dist/bitcoin/mempool/MempoolBitcoinBlock.js +48 -0
- package/dist/bitcoin/mempool/MempoolBitcoinRpc.d.ts +119 -0
- package/dist/bitcoin/mempool/MempoolBitcoinRpc.js +361 -0
- package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.d.ts +22 -0
- package/dist/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.js +105 -0
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +93 -0
- package/dist/bitcoin/wallet/BitcoinWallet.js +273 -0
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +28 -0
- package/dist/bitcoin/wallet/IBitcoinWallet.js +20 -0
- package/dist/bitcoin/wallet/MinimalBitcoinWalletInterface.d.ts +21 -0
- package/dist/bitcoin/wallet/MinimalBitcoinWalletInterface.js +2 -0
- package/dist/bitcoin/wallet/MinimalLightningNetworkWalletInterface.d.ts +7 -0
- package/dist/bitcoin/wallet/MinimalLightningNetworkWalletInterface.js +2 -0
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +40 -0
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +86 -0
- package/dist/enums/FeeType.d.ts +8 -0
- package/dist/enums/FeeType.js +12 -0
- package/dist/enums/SwapAmountType.d.ts +8 -0
- package/dist/enums/SwapAmountType.js +12 -0
- package/dist/enums/SwapDirection.d.ts +8 -0
- package/dist/enums/SwapDirection.js +12 -0
- package/dist/enums/SwapType.d.ts +14 -0
- package/dist/enums/SwapType.js +18 -0
- package/dist/errors/IntermediaryError.d.ts +9 -0
- package/dist/errors/IntermediaryError.js +26 -0
- package/dist/errors/PaymentAuthError.d.ts +11 -0
- package/dist/errors/PaymentAuthError.js +23 -0
- package/dist/errors/RequestError.d.ts +18 -0
- package/dist/errors/RequestError.js +46 -0
- package/dist/errors/UserError.d.ts +7 -0
- package/dist/errors/UserError.js +15 -0
- package/dist/events/UnifiedSwapEventListener.d.ts +23 -0
- package/dist/events/UnifiedSwapEventListener.js +130 -0
- package/dist/http/HttpUtils.d.ts +27 -0
- package/dist/http/HttpUtils.js +91 -0
- package/dist/http/paramcoders/IParamReader.d.ts +8 -0
- package/dist/http/paramcoders/IParamReader.js +2 -0
- package/dist/http/paramcoders/ParamDecoder.d.ts +44 -0
- package/dist/http/paramcoders/ParamDecoder.js +132 -0
- package/dist/http/paramcoders/ParamEncoder.d.ts +20 -0
- package/dist/http/paramcoders/ParamEncoder.js +31 -0
- package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -0
- package/dist/http/paramcoders/SchemaVerifier.js +145 -0
- package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -0
- package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -0
- package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -0
- package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -0
- package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -0
- package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -0
- package/dist/index.d.ts +82 -4
- package/dist/index.js +128 -4
- package/dist/intermediaries/Intermediary.d.ts +111 -0
- package/dist/intermediaries/Intermediary.js +115 -0
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +166 -0
- package/dist/intermediaries/IntermediaryDiscovery.js +390 -0
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +436 -0
- package/dist/intermediaries/apis/IntermediaryAPI.js +600 -0
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +154 -0
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +136 -0
- package/dist/lnurl/LNURL.d.ts +102 -0
- package/dist/lnurl/LNURL.js +321 -0
- package/dist/prices/RedundantSwapPrice.d.ts +89 -0
- package/dist/prices/RedundantSwapPrice.js +202 -0
- package/dist/prices/SingleSwapPrice.d.ts +31 -0
- package/dist/prices/SingleSwapPrice.js +41 -0
- package/dist/prices/SwapPriceWithChain.d.ts +70 -0
- package/dist/prices/SwapPriceWithChain.js +91 -0
- package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -0
- package/dist/prices/abstract/ICachedSwapPrice.js +62 -0
- package/dist/prices/abstract/IPriceProvider.d.ts +81 -0
- package/dist/prices/abstract/IPriceProvider.js +74 -0
- package/dist/prices/abstract/ISwapPrice.d.ts +117 -0
- package/dist/prices/abstract/ISwapPrice.js +219 -0
- package/dist/prices/providers/BinancePriceProvider.d.ts +16 -0
- package/dist/prices/providers/BinancePriceProvider.js +23 -0
- package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +17 -0
- package/dist/prices/providers/CoinGeckoPriceProvider.js +23 -0
- package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +19 -0
- package/dist/prices/providers/CoinPaprikaPriceProvider.js +23 -0
- package/dist/prices/providers/CustomPriceProvider.d.ts +13 -0
- package/dist/prices/providers/CustomPriceProvider.js +24 -0
- package/dist/prices/providers/KrakenPriceProvider.d.ts +29 -0
- package/dist/prices/providers/KrakenPriceProvider.js +36 -0
- package/dist/prices/providers/OKXPriceProvider.d.ts +28 -0
- package/dist/prices/providers/OKXPriceProvider.js +23 -0
- package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +14 -0
- package/dist/prices/providers/abstract/ExchangePriceProvider.js +18 -0
- package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -0
- package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -0
- package/dist/storage/IUnifiedStorage.d.ts +73 -0
- package/dist/storage/IUnifiedStorage.js +2 -0
- package/dist/storage/UnifiedSwapStorage.d.ts +82 -0
- package/dist/storage/UnifiedSwapStorage.js +83 -0
- package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +39 -0
- package/dist/storage-browser/IndexedDBUnifiedStorage.js +275 -0
- package/dist/{storage → storage-browser}/LocalStorageManager.d.ts +1 -0
- package/dist/{storage → storage-browser}/LocalStorageManager.js +2 -1
- package/dist/swapper/Swapper.d.ts +533 -0
- package/dist/swapper/Swapper.js +1566 -0
- package/dist/swapper/SwapperFactory.d.ts +87 -0
- package/dist/{SwapperFactory.js → swapper/SwapperFactory.js} +37 -19
- package/dist/swapper/SwapperUtils.d.ts +153 -0
- package/dist/swapper/SwapperUtils.js +420 -0
- package/dist/swapper/SwapperWithChain.d.ts +214 -0
- package/dist/swapper/SwapperWithChain.js +315 -0
- package/dist/swapper/SwapperWithSigner.d.ts +178 -0
- package/dist/swapper/SwapperWithSigner.js +172 -0
- package/dist/swaps/IAddressSwap.d.ts +13 -0
- package/dist/swaps/IAddressSwap.js +13 -0
- package/dist/swaps/IBTCWalletSwap.d.ts +55 -0
- package/dist/swaps/IBTCWalletSwap.js +17 -0
- package/dist/swaps/IClaimableSwap.d.ts +17 -0
- package/dist/swaps/IClaimableSwap.js +14 -0
- package/dist/swaps/IClaimableSwapWrapper.d.ts +5 -0
- package/dist/swaps/IClaimableSwapWrapper.js +2 -0
- package/dist/swaps/IRefundableSwap.d.ts +17 -0
- package/dist/swaps/IRefundableSwap.js +13 -0
- package/dist/swaps/ISwap.d.ts +207 -0
- package/dist/swaps/ISwap.js +264 -0
- package/dist/swaps/ISwapWithGasDrop.d.ts +15 -0
- package/dist/swaps/ISwapWithGasDrop.js +11 -0
- package/dist/swaps/ISwapWrapper.d.ts +153 -0
- package/dist/swaps/ISwapWrapper.js +227 -0
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +53 -0
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +116 -0
- package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +70 -0
- package/dist/swaps/escrow_swaps/IEscrowSwap.js +132 -0
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +85 -0
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +122 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +86 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +115 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +93 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +121 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +45 -0
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +65 -0
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +263 -0
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +933 -0
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +110 -0
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +307 -0
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +236 -0
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +898 -0
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +125 -0
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +393 -0
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +245 -0
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +841 -0
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +120 -0
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +294 -0
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +228 -0
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +721 -0
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +37 -0
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +93 -0
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +86 -0
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +213 -0
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +170 -0
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +520 -0
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +50 -0
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +109 -0
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +93 -0
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +217 -0
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +315 -0
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1098 -0
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +125 -0
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +631 -0
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +107 -0
- package/dist/swaps/trusted/ln/LnForGasSwap.js +343 -0
- package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +21 -0
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +62 -0
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +164 -0
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +520 -0
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +48 -0
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +74 -0
- package/dist/types/AmountData.d.ts +9 -0
- package/dist/types/AmountData.js +2 -0
- package/dist/types/CustomPriceFunction.d.ts +5 -0
- package/dist/types/CustomPriceFunction.js +2 -0
- package/dist/types/PriceInfoType.d.ts +28 -0
- package/dist/types/PriceInfoType.js +57 -0
- package/dist/types/SwapExecutionAction.d.ts +7 -0
- package/dist/types/SwapExecutionAction.js +2 -0
- package/dist/types/SwapWithSigner.d.ts +14 -0
- package/dist/types/SwapWithSigner.js +40 -0
- package/dist/types/Token.d.ts +53 -0
- package/dist/types/Token.js +58 -0
- package/dist/types/TokenAmount.d.ts +57 -0
- package/dist/types/TokenAmount.js +47 -0
- package/dist/types/fees/Fee.d.ts +49 -0
- package/dist/types/fees/Fee.js +2 -0
- package/dist/types/fees/FeeBreakdown.d.ts +10 -0
- package/dist/types/fees/FeeBreakdown.js +2 -0
- package/dist/types/fees/PercentagePPM.d.ts +15 -0
- package/dist/types/fees/PercentagePPM.js +17 -0
- package/dist/types/lnurl/LNURLPay.d.ts +54 -0
- package/dist/types/lnurl/LNURLPay.js +28 -0
- package/dist/types/lnurl/LNURLWithdraw.d.ts +42 -0
- package/dist/types/lnurl/LNURLWithdraw.js +24 -0
- package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -0
- package/dist/utils/AutomaticClockDriftCorrection.js +70 -0
- package/dist/utils/BitcoinUtils.d.ts +13 -0
- package/dist/utils/BitcoinUtils.js +98 -0
- package/dist/utils/BitcoinWalletUtils.d.ts +7 -0
- package/dist/utils/BitcoinWalletUtils.js +14 -0
- package/dist/utils/Logger.d.ts +7 -0
- package/dist/utils/Logger.js +12 -0
- package/dist/utils/RetryUtils.d.ts +21 -0
- package/dist/utils/RetryUtils.js +66 -0
- package/dist/utils/SwapUtils.d.ts +31 -0
- package/dist/utils/SwapUtils.js +18 -0
- package/dist/{Utils.d.ts → utils/TimeoutUtils.d.ts} +9 -3
- package/dist/utils/TimeoutUtils.js +55 -0
- package/dist/utils/TokenUtils.d.ts +11 -0
- package/dist/utils/TokenUtils.js +29 -0
- package/dist/utils/TypeUtils.d.ts +7 -0
- package/dist/utils/TypeUtils.js +2 -0
- package/dist/utils/Utils.d.ts +57 -0
- package/dist/utils/Utils.js +178 -0
- package/package.json +14 -6
- package/src/SmartChainAssets.ts +11 -3
- package/src/bitcoin/BitcoinRpcWithAddressIndex.ts +87 -0
- package/src/bitcoin/LightningNetworkApi.ts +16 -0
- package/src/bitcoin/coinselect2/accumulative.ts +68 -0
- package/src/bitcoin/coinselect2/blackjack.ts +49 -0
- package/src/bitcoin/coinselect2/index.ts +92 -0
- package/src/bitcoin/coinselect2/utils.ts +189 -0
- package/src/bitcoin/mempool/MempoolApi.ts +554 -0
- package/src/bitcoin/mempool/MempoolBitcoinBlock.ts +88 -0
- package/src/bitcoin/mempool/MempoolBitcoinRpc.ts +437 -0
- package/src/bitcoin/mempool/synchronizer/MempoolBtcRelaySynchronizer.ts +134 -0
- package/src/bitcoin/wallet/BitcoinWallet.ts +375 -0
- package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -0
- package/src/bitcoin/wallet/MinimalBitcoinWalletInterface.ts +19 -0
- package/src/bitcoin/wallet/MinimalLightningNetworkWalletInterface.ts +7 -0
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +108 -0
- package/src/enums/FeeType.ts +9 -0
- package/src/enums/SwapAmountType.ts +9 -0
- package/src/enums/SwapDirection.ts +9 -0
- package/src/enums/SwapType.ts +15 -0
- package/src/errors/IntermediaryError.ts +24 -0
- package/src/errors/PaymentAuthError.ts +26 -0
- package/src/errors/RequestError.ts +51 -0
- package/src/errors/UserError.ts +14 -0
- package/src/events/UnifiedSwapEventListener.ts +171 -0
- package/src/http/HttpUtils.ts +92 -0
- package/src/http/paramcoders/IParamReader.ts +10 -0
- package/src/http/paramcoders/ParamDecoder.ts +142 -0
- package/src/http/paramcoders/ParamEncoder.ts +37 -0
- package/src/http/paramcoders/SchemaVerifier.ts +153 -0
- package/src/http/paramcoders/client/ResponseParamDecoder.ts +58 -0
- package/src/http/paramcoders/client/StreamParamEncoder.ts +29 -0
- package/src/http/paramcoders/client/StreamingFetchPromise.ts +193 -0
- package/src/index.ts +102 -4
- package/src/intermediaries/Intermediary.ts +204 -0
- package/src/intermediaries/IntermediaryDiscovery.ts +485 -0
- package/src/intermediaries/apis/IntermediaryAPI.ts +940 -0
- package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -0
- package/src/lnurl/LNURL.ts +403 -0
- package/src/prices/RedundantSwapPrice.ts +245 -0
- package/src/prices/SingleSwapPrice.ts +47 -0
- package/src/prices/SwapPriceWithChain.ts +157 -0
- package/src/prices/abstract/ICachedSwapPrice.ts +86 -0
- package/src/prices/abstract/IPriceProvider.ts +128 -0
- package/src/prices/abstract/ISwapPrice.ts +328 -0
- package/src/prices/providers/BinancePriceProvider.ts +41 -0
- package/src/prices/providers/CoinGeckoPriceProvider.ts +40 -0
- package/src/prices/providers/CoinPaprikaPriceProvider.ts +44 -0
- package/src/prices/providers/CustomPriceProvider.ts +29 -0
- package/src/prices/providers/KrakenPriceProvider.ts +74 -0
- package/src/prices/providers/OKXPriceProvider.ts +53 -0
- package/src/prices/providers/abstract/ExchangePriceProvider.ts +29 -0
- package/src/prices/providers/abstract/HttpPriceProvider.ts +15 -0
- package/src/storage/IUnifiedStorage.ts +83 -0
- package/src/storage/UnifiedSwapStorage.ts +104 -0
- package/src/storage-browser/IndexedDBUnifiedStorage.ts +328 -0
- package/src/{storage → storage-browser}/LocalStorageManager.ts +2 -1
- package/src/swapper/Swapper.ts +2107 -0
- package/src/{SwapperFactory.ts → swapper/SwapperFactory.ts} +113 -72
- package/src/swapper/SwapperUtils.ts +510 -0
- package/src/swapper/SwapperWithChain.ts +464 -0
- package/src/swapper/SwapperWithSigner.ts +300 -0
- package/src/swaps/IAddressSwap.ts +20 -0
- package/src/swaps/IBTCWalletSwap.ts +77 -0
- package/src/swaps/IClaimableSwap.ts +30 -0
- package/src/swaps/IClaimableSwapWrapper.ts +9 -0
- package/src/swaps/IRefundableSwap.ts +29 -0
- package/src/swaps/ISwap.ts +490 -0
- package/src/swaps/ISwapWithGasDrop.ts +19 -0
- package/src/swaps/ISwapWrapper.ts +344 -0
- package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +168 -0
- package/src/swaps/escrow_swaps/IEscrowSwap.ts +197 -0
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +210 -0
- package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +150 -0
- package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +219 -0
- package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +84 -0
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1082 -0
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +429 -0
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1078 -0
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +549 -0
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +974 -0
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +443 -0
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +860 -0
- package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +104 -0
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +256 -0
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +716 -0
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +151 -0
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +299 -0
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1394 -0
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +796 -0
- package/src/swaps/trusted/ln/LnForGasSwap.ts +402 -0
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +70 -0
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +633 -0
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +110 -0
- package/src/types/AmountData.ts +9 -0
- package/src/types/CustomPriceFunction.ts +5 -0
- package/src/types/PriceInfoType.ts +67 -0
- package/src/types/SwapExecutionAction.ts +8 -0
- package/src/types/SwapWithSigner.ts +57 -0
- package/src/types/Token.ts +90 -0
- package/src/types/TokenAmount.ts +110 -0
- package/src/types/fees/Fee.ts +55 -0
- package/src/types/fees/FeeBreakdown.ts +11 -0
- package/src/types/fees/PercentagePPM.ts +24 -0
- package/src/types/lnurl/LNURLPay.ts +72 -0
- package/src/types/lnurl/LNURLWithdraw.ts +55 -0
- package/src/utils/AutomaticClockDriftCorrection.ts +71 -0
- package/src/utils/BitcoinUtils.ts +86 -0
- package/src/utils/BitcoinWalletUtils.ts +16 -0
- package/src/utils/Logger.ts +15 -0
- package/src/utils/RetryUtils.ts +71 -0
- package/src/utils/SwapUtils.ts +38 -0
- package/src/utils/TimeoutUtils.ts +50 -0
- package/src/utils/TokenUtils.ts +25 -0
- package/src/utils/TypeUtils.ts +9 -0
- package/src/utils/Utils.ts +182 -0
- package/dist/SwapperFactory.d.ts +0 -52
- package/dist/Utils.js +0 -37
- package/dist/fs-storage/FileSystemStorageManager.d.ts +0 -15
- package/dist/fs-storage/FileSystemStorageManager.js +0 -60
- package/dist/fs-storage/index.d.ts +0 -1
- package/dist/fs-storage/index.js +0 -17
- package/src/SmartChainAssets.js +0 -75
- package/src/SwapperFactory.js +0 -120
- package/src/Utils.js +0 -37
- package/src/Utils.ts +0 -31
- package/src/fs-storage/FileSystemStorageManager.ts +0 -71
- package/src/fs-storage/index.ts +0 -1
- package/src/index.js +0 -21
- package/src/storage/LocalStorageManager.js +0 -72
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpvFromBTCSwap = exports.isSpvFromBTCSwapInit = exports.SpvFromBTCSwapState = void 0;
|
|
4
|
+
const ISwap_1 = require("../ISwap");
|
|
5
|
+
const base_1 = require("@atomiqlabs/base");
|
|
6
|
+
const SwapType_1 = require("../../enums/SwapType");
|
|
7
|
+
const Utils_1 = require("../../utils/Utils");
|
|
8
|
+
const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
|
|
9
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
10
|
+
const buffer_1 = require("buffer");
|
|
11
|
+
const IBitcoinWallet_1 = require("../../bitcoin/wallet/IBitcoinWallet");
|
|
12
|
+
const IntermediaryAPI_1 = require("../../intermediaries/apis/IntermediaryAPI");
|
|
13
|
+
const FeeType_1 = require("../../enums/FeeType");
|
|
14
|
+
const PercentagePPM_1 = require("../../types/fees/PercentagePPM");
|
|
15
|
+
const TokenAmount_1 = require("../../types/TokenAmount");
|
|
16
|
+
const Token_1 = require("../../types/Token");
|
|
17
|
+
const Logger_1 = require("../../utils/Logger");
|
|
18
|
+
const TimeoutUtils_1 = require("../../utils/TimeoutUtils");
|
|
19
|
+
const PriceInfoType_1 = require("../../types/PriceInfoType");
|
|
20
|
+
const BitcoinWalletUtils_1 = require("../../utils/BitcoinWalletUtils");
|
|
21
|
+
/**
|
|
22
|
+
* State enum for SPV FromBTC swaps
|
|
23
|
+
* @category Swaps
|
|
24
|
+
*/
|
|
25
|
+
var SpvFromBTCSwapState;
|
|
26
|
+
(function (SpvFromBTCSwapState) {
|
|
27
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["CLOSED"] = -5] = "CLOSED";
|
|
28
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["FAILED"] = -4] = "FAILED";
|
|
29
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["DECLINED"] = -3] = "DECLINED";
|
|
30
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_EXPIRED"] = -2] = "QUOTE_EXPIRED";
|
|
31
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["QUOTE_SOFT_EXPIRED"] = -1] = "QUOTE_SOFT_EXPIRED";
|
|
32
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["CREATED"] = 0] = "CREATED";
|
|
33
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["SIGNED"] = 1] = "SIGNED";
|
|
34
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["POSTED"] = 2] = "POSTED";
|
|
35
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["BROADCASTED"] = 3] = "BROADCASTED";
|
|
36
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["FRONTED"] = 4] = "FRONTED";
|
|
37
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["BTC_TX_CONFIRMED"] = 5] = "BTC_TX_CONFIRMED";
|
|
38
|
+
SpvFromBTCSwapState[SpvFromBTCSwapState["CLAIMED"] = 6] = "CLAIMED"; //Funds claimed
|
|
39
|
+
})(SpvFromBTCSwapState = exports.SpvFromBTCSwapState || (exports.SpvFromBTCSwapState = {}));
|
|
40
|
+
function isSpvFromBTCSwapInit(obj) {
|
|
41
|
+
return typeof obj === "object" &&
|
|
42
|
+
typeof (obj.quoteId) === "string" &&
|
|
43
|
+
typeof (obj.recipient) === "string" &&
|
|
44
|
+
typeof (obj.vaultOwner) === "string" &&
|
|
45
|
+
typeof (obj.vaultId) === "bigint" &&
|
|
46
|
+
typeof (obj.vaultRequiredConfirmations) === "number" &&
|
|
47
|
+
Array.isArray(obj.vaultTokenMultipliers) && obj.vaultTokenMultipliers.reduce((prev, curr) => prev && typeof (curr) === "bigint", true) &&
|
|
48
|
+
typeof (obj.vaultBtcAddress) === "string" &&
|
|
49
|
+
typeof (obj.vaultUtxo) === "string" &&
|
|
50
|
+
typeof (obj.vaultUtxoValue) === "bigint" &&
|
|
51
|
+
typeof (obj.btcDestinationAddress) === "string" &&
|
|
52
|
+
typeof (obj.btcAmount) === "bigint" &&
|
|
53
|
+
typeof (obj.btcAmountSwap) === "bigint" &&
|
|
54
|
+
typeof (obj.btcAmountGas) === "bigint" &&
|
|
55
|
+
typeof (obj.minimumBtcFeeRate) === "number" &&
|
|
56
|
+
typeof (obj.outputTotalSwap) === "bigint" &&
|
|
57
|
+
typeof (obj.outputSwapToken) === "string" &&
|
|
58
|
+
typeof (obj.outputTotalGas) === "bigint" &&
|
|
59
|
+
typeof (obj.outputGasToken) === "string" &&
|
|
60
|
+
typeof (obj.gasSwapFeeBtc) === "bigint" &&
|
|
61
|
+
typeof (obj.gasSwapFee) === "bigint" &&
|
|
62
|
+
typeof (obj.callerFeeShare) === "bigint" &&
|
|
63
|
+
typeof (obj.frontingFeeShare) === "bigint" &&
|
|
64
|
+
typeof (obj.executionFeeShare) === "bigint" &&
|
|
65
|
+
typeof (obj.genesisSmartChainBlockHeight) === "number" &&
|
|
66
|
+
(obj.gasPricingInfo == null || (0, PriceInfoType_1.isPriceInfoType)(obj.gasPricingInfo)) &&
|
|
67
|
+
(0, ISwap_1.isISwapInit)(obj);
|
|
68
|
+
}
|
|
69
|
+
exports.isSpvFromBTCSwapInit = isSpvFromBTCSwapInit;
|
|
70
|
+
class SpvFromBTCSwap extends ISwap_1.ISwap {
|
|
71
|
+
constructor(wrapper, initOrObject) {
|
|
72
|
+
if (isSpvFromBTCSwapInit(initOrObject) && initOrObject.url != null)
|
|
73
|
+
initOrObject.url += "/frombtc_spv";
|
|
74
|
+
super(wrapper, initOrObject);
|
|
75
|
+
this.TYPE = SwapType_1.SwapType.SPV_VAULT_FROM_BTC;
|
|
76
|
+
if (isSpvFromBTCSwapInit(initOrObject)) {
|
|
77
|
+
this.state = SpvFromBTCSwapState.CREATED;
|
|
78
|
+
this.quoteId = initOrObject.quoteId;
|
|
79
|
+
this.recipient = initOrObject.recipient;
|
|
80
|
+
this.vaultOwner = initOrObject.vaultOwner;
|
|
81
|
+
this.vaultId = initOrObject.vaultId;
|
|
82
|
+
this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
|
|
83
|
+
this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers;
|
|
84
|
+
this.vaultBtcAddress = initOrObject.vaultBtcAddress;
|
|
85
|
+
this.vaultUtxo = initOrObject.vaultUtxo;
|
|
86
|
+
this.vaultUtxoValue = initOrObject.vaultUtxoValue;
|
|
87
|
+
this.btcDestinationAddress = initOrObject.btcDestinationAddress;
|
|
88
|
+
this.btcAmount = initOrObject.btcAmount;
|
|
89
|
+
this.btcAmountSwap = initOrObject.btcAmountSwap;
|
|
90
|
+
this.btcAmountGas = initOrObject.btcAmountGas;
|
|
91
|
+
this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
|
|
92
|
+
this.outputTotalSwap = initOrObject.outputTotalSwap;
|
|
93
|
+
this.outputSwapToken = initOrObject.outputSwapToken;
|
|
94
|
+
this.outputTotalGas = initOrObject.outputTotalGas;
|
|
95
|
+
this.outputGasToken = initOrObject.outputGasToken;
|
|
96
|
+
this.gasSwapFeeBtc = initOrObject.gasSwapFeeBtc;
|
|
97
|
+
this.gasSwapFee = initOrObject.gasSwapFee;
|
|
98
|
+
this.callerFeeShare = initOrObject.callerFeeShare;
|
|
99
|
+
this.frontingFeeShare = initOrObject.frontingFeeShare;
|
|
100
|
+
this.executionFeeShare = initOrObject.executionFeeShare;
|
|
101
|
+
this.genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
|
|
102
|
+
this.gasPricingInfo = initOrObject.gasPricingInfo;
|
|
103
|
+
const vaultAddressType = (0, BitcoinUtils_1.toCoinselectAddressType)((0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, this.vaultBtcAddress));
|
|
104
|
+
if (vaultAddressType !== "p2tr" && vaultAddressType !== "p2wpkh" && vaultAddressType !== "p2wsh")
|
|
105
|
+
throw new Error("Vault address type must be of witness type: p2tr, p2wpkh, p2wsh");
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.quoteId = initOrObject.quoteId;
|
|
109
|
+
this.recipient = initOrObject.recipient;
|
|
110
|
+
this.vaultOwner = initOrObject.vaultOwner;
|
|
111
|
+
this.vaultId = BigInt(initOrObject.vaultId);
|
|
112
|
+
this.vaultRequiredConfirmations = initOrObject.vaultRequiredConfirmations;
|
|
113
|
+
this.vaultTokenMultipliers = initOrObject.vaultTokenMultipliers.map((val) => BigInt(val));
|
|
114
|
+
this.vaultBtcAddress = initOrObject.vaultBtcAddress;
|
|
115
|
+
this.vaultUtxo = initOrObject.vaultUtxo;
|
|
116
|
+
this.vaultUtxoValue = BigInt(initOrObject.vaultUtxoValue);
|
|
117
|
+
this.btcDestinationAddress = initOrObject.btcDestinationAddress;
|
|
118
|
+
this.btcAmount = BigInt(initOrObject.btcAmount);
|
|
119
|
+
this.btcAmountSwap = BigInt(initOrObject.btcAmountSwap);
|
|
120
|
+
this.btcAmountGas = BigInt(initOrObject.btcAmountGas);
|
|
121
|
+
this.minimumBtcFeeRate = initOrObject.minimumBtcFeeRate;
|
|
122
|
+
this.outputTotalSwap = BigInt(initOrObject.outputTotalSwap);
|
|
123
|
+
this.outputSwapToken = initOrObject.outputSwapToken;
|
|
124
|
+
this.outputTotalGas = BigInt(initOrObject.outputTotalGas);
|
|
125
|
+
this.outputGasToken = initOrObject.outputGasToken;
|
|
126
|
+
this.gasSwapFeeBtc = BigInt(initOrObject.gasSwapFeeBtc);
|
|
127
|
+
this.gasSwapFee = BigInt(initOrObject.gasSwapFee);
|
|
128
|
+
this.callerFeeShare = BigInt(initOrObject.callerFeeShare);
|
|
129
|
+
this.frontingFeeShare = BigInt(initOrObject.frontingFeeShare);
|
|
130
|
+
this.executionFeeShare = BigInt(initOrObject.executionFeeShare);
|
|
131
|
+
this.genesisSmartChainBlockHeight = initOrObject.genesisSmartChainBlockHeight;
|
|
132
|
+
this.senderAddress = initOrObject.senderAddress;
|
|
133
|
+
this.claimTxId = initOrObject.claimTxId;
|
|
134
|
+
this.frontTxId = initOrObject.frontTxId;
|
|
135
|
+
this.gasPricingInfo = (0, PriceInfoType_1.deserializePriceInfoType)(initOrObject.gasPricingInfo);
|
|
136
|
+
if (initOrObject.data != null)
|
|
137
|
+
this.data = new this.wrapper.spvWithdrawalDataDeserializer(initOrObject.data);
|
|
138
|
+
}
|
|
139
|
+
this.tryCalculateSwapFee();
|
|
140
|
+
this.logger = (0, Logger_1.getLogger)("SPVFromBTC(" + this.getId() + "): ");
|
|
141
|
+
}
|
|
142
|
+
upgradeVersion() { }
|
|
143
|
+
/**
|
|
144
|
+
* In case swapFee in BTC is not supplied it recalculates it based on swap price
|
|
145
|
+
* @protected
|
|
146
|
+
*/
|
|
147
|
+
tryCalculateSwapFee() {
|
|
148
|
+
if (this.swapFeeBtc == null && this.swapFee != null) {
|
|
149
|
+
this.swapFeeBtc = this.swapFee * this.btcAmountSwap / this.getOutputWithoutFee().rawAmount;
|
|
150
|
+
}
|
|
151
|
+
if (this.pricingInfo != null && this.pricingInfo.swapPriceUSatPerToken == null) {
|
|
152
|
+
const priceUsdPerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
153
|
+
this.pricingInfo = this.wrapper.prices.recomputePriceInfoReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken);
|
|
154
|
+
this.pricingInfo.realPriceUsdPerBitcoin = priceUsdPerBtc;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//////////////////////////////
|
|
158
|
+
//// Pricing
|
|
159
|
+
async refreshPriceData() {
|
|
160
|
+
if (this.pricingInfo == null)
|
|
161
|
+
return;
|
|
162
|
+
const usdPricePerBtc = this.pricingInfo.realPriceUsdPerBitcoin;
|
|
163
|
+
this.pricingInfo = await this.wrapper.prices.isValidAmountReceive(this.chainIdentifier, this.btcAmountSwap, this.pricingInfo.satsBaseFee, this.pricingInfo.feePPM, this.getOutputWithoutFee().rawAmount, this.outputSwapToken);
|
|
164
|
+
this.pricingInfo.realPriceUsdPerBitcoin = usdPricePerBtc;
|
|
165
|
+
}
|
|
166
|
+
//////////////////////////////
|
|
167
|
+
//// Getters & utils
|
|
168
|
+
_getInitiator() {
|
|
169
|
+
return this.recipient;
|
|
170
|
+
}
|
|
171
|
+
_getEscrowHash() {
|
|
172
|
+
return this.data?.btcTx?.txid ?? null;
|
|
173
|
+
}
|
|
174
|
+
getId() {
|
|
175
|
+
return this.quoteId + this.randomNonce;
|
|
176
|
+
}
|
|
177
|
+
getQuoteExpiry() {
|
|
178
|
+
return this.expiry - 20 * 1000;
|
|
179
|
+
}
|
|
180
|
+
verifyQuoteValid() {
|
|
181
|
+
return Promise.resolve(this.expiry > Date.now() && (this.state === SpvFromBTCSwapState.CREATED || this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED));
|
|
182
|
+
}
|
|
183
|
+
getOutputAddress() {
|
|
184
|
+
return this.recipient;
|
|
185
|
+
}
|
|
186
|
+
getOutputTxId() {
|
|
187
|
+
return this.frontTxId ?? this.claimTxId ?? null;
|
|
188
|
+
}
|
|
189
|
+
getInputAddress() {
|
|
190
|
+
return this.senderAddress ?? null;
|
|
191
|
+
}
|
|
192
|
+
getInputTxId() {
|
|
193
|
+
return this.data?.btcTx?.txid ?? null;
|
|
194
|
+
}
|
|
195
|
+
requiresAction() {
|
|
196
|
+
return this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
|
|
197
|
+
}
|
|
198
|
+
isFinished() {
|
|
199
|
+
return this.state === SpvFromBTCSwapState.CLAIMED || this.state === SpvFromBTCSwapState.QUOTE_EXPIRED || this.state === SpvFromBTCSwapState.FAILED;
|
|
200
|
+
}
|
|
201
|
+
isClaimable() {
|
|
202
|
+
return this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED;
|
|
203
|
+
}
|
|
204
|
+
isSuccessful() {
|
|
205
|
+
return this.state === SpvFromBTCSwapState.FRONTED || this.state === SpvFromBTCSwapState.CLAIMED;
|
|
206
|
+
}
|
|
207
|
+
isFailed() {
|
|
208
|
+
return this.state === SpvFromBTCSwapState.FAILED || this.state === SpvFromBTCSwapState.DECLINED || this.state === SpvFromBTCSwapState.CLOSED;
|
|
209
|
+
}
|
|
210
|
+
isQuoteExpired() {
|
|
211
|
+
return this.state === SpvFromBTCSwapState.QUOTE_EXPIRED;
|
|
212
|
+
}
|
|
213
|
+
isQuoteSoftExpired() {
|
|
214
|
+
return this.state === SpvFromBTCSwapState.QUOTE_EXPIRED || this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
215
|
+
}
|
|
216
|
+
//////////////////////////////
|
|
217
|
+
//// Amounts & fees
|
|
218
|
+
getInputSwapAmountWithoutFee() {
|
|
219
|
+
return (this.btcAmountSwap - this.swapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare);
|
|
220
|
+
}
|
|
221
|
+
getInputGasAmountWithoutFee() {
|
|
222
|
+
return (this.btcAmountGas - this.gasSwapFeeBtc) * 100000n / (100000n + this.callerFeeShare + this.frontingFeeShare);
|
|
223
|
+
}
|
|
224
|
+
getInputAmountWithoutFee() {
|
|
225
|
+
return this.getInputSwapAmountWithoutFee() + this.getInputGasAmountWithoutFee();
|
|
226
|
+
}
|
|
227
|
+
getOutputWithoutFee() {
|
|
228
|
+
return (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (100000n + this.callerFeeShare + this.frontingFeeShare + this.executionFeeShare) / 100000n) + (this.swapFee ?? 0n), this.wrapper.tokens[this.outputSwapToken], this.wrapper.prices, this.pricingInfo);
|
|
229
|
+
}
|
|
230
|
+
getSwapFee() {
|
|
231
|
+
if (this.pricingInfo == null)
|
|
232
|
+
throw new Error("No pricing info known, cannot estimate fee!");
|
|
233
|
+
const outputToken = this.wrapper.tokens[this.outputSwapToken];
|
|
234
|
+
const gasSwapFeeInOutputToken = this.gasSwapFeeBtc
|
|
235
|
+
* (10n ** BigInt(outputToken.decimals))
|
|
236
|
+
* 1000000n
|
|
237
|
+
/ this.pricingInfo.swapPriceUSatPerToken;
|
|
238
|
+
const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
|
|
239
|
+
const swapFeePPM = feeWithoutBaseFee * 1000000n / (this.btcAmount - this.swapFeeBtc - this.gasSwapFeeBtc);
|
|
240
|
+
const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(this.swapFeeBtc + this.gasSwapFeeBtc, Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
241
|
+
return {
|
|
242
|
+
amountInSrcToken,
|
|
243
|
+
amountInDstToken: (0, TokenAmount_1.toTokenAmount)(this.swapFee + gasSwapFeeInOutputToken, outputToken, this.wrapper.prices, this.pricingInfo),
|
|
244
|
+
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
245
|
+
usdValue: amountInSrcToken.usdValue,
|
|
246
|
+
pastUsdValue: amountInSrcToken.pastUsdValue,
|
|
247
|
+
composition: {
|
|
248
|
+
base: (0, TokenAmount_1.toTokenAmount)(this.pricingInfo.satsBaseFee, Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo),
|
|
249
|
+
percentage: (0, PercentagePPM_1.ppmToPercentage)(swapFeePPM)
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
getWatchtowerFee() {
|
|
254
|
+
if (this.pricingInfo == null)
|
|
255
|
+
throw new Error("No pricing info known, cannot estimate fee!");
|
|
256
|
+
const totalFeeShare = this.callerFeeShare + this.frontingFeeShare;
|
|
257
|
+
const outputToken = this.wrapper.tokens[this.outputSwapToken];
|
|
258
|
+
const watchtowerFeeInOutputToken = this.getInputGasAmountWithoutFee() * totalFeeShare
|
|
259
|
+
* (10n ** BigInt(outputToken.decimals))
|
|
260
|
+
* 1000000n
|
|
261
|
+
/ this.pricingInfo.swapPriceUSatPerToken
|
|
262
|
+
/ 100000n;
|
|
263
|
+
const feeBtc = this.getInputAmountWithoutFee() * (totalFeeShare + this.executionFeeShare) / 100000n;
|
|
264
|
+
const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(feeBtc, Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
265
|
+
return {
|
|
266
|
+
amountInSrcToken,
|
|
267
|
+
amountInDstToken: (0, TokenAmount_1.toTokenAmount)((this.outputTotalSwap * (totalFeeShare + this.executionFeeShare) / 100000n) + watchtowerFeeInOutputToken, outputToken, this.wrapper.prices, this.pricingInfo),
|
|
268
|
+
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
269
|
+
usdValue: amountInSrcToken.usdValue,
|
|
270
|
+
pastUsdValue: amountInSrcToken.pastUsdValue
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
getFee() {
|
|
274
|
+
const swapFee = this.getSwapFee();
|
|
275
|
+
const watchtowerFee = this.getWatchtowerFee();
|
|
276
|
+
const amountInSrcToken = (0, TokenAmount_1.toTokenAmount)(swapFee.amountInSrcToken.rawAmount + watchtowerFee.amountInSrcToken.rawAmount, Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
277
|
+
return {
|
|
278
|
+
amountInSrcToken,
|
|
279
|
+
amountInDstToken: (0, TokenAmount_1.toTokenAmount)(swapFee.amountInDstToken.rawAmount + watchtowerFee.amountInDstToken.rawAmount, this.wrapper.tokens[this.outputSwapToken], this.wrapper.prices, this.pricingInfo),
|
|
280
|
+
currentUsdValue: amountInSrcToken.currentUsdValue,
|
|
281
|
+
usdValue: amountInSrcToken.usdValue,
|
|
282
|
+
pastUsdValue: amountInSrcToken.pastUsdValue
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
getFeeBreakdown() {
|
|
286
|
+
return [
|
|
287
|
+
{
|
|
288
|
+
type: FeeType_1.FeeType.SWAP,
|
|
289
|
+
fee: this.getSwapFee()
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: FeeType_1.FeeType.NETWORK_OUTPUT,
|
|
293
|
+
fee: this.getWatchtowerFee()
|
|
294
|
+
}
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
getOutputToken() {
|
|
298
|
+
return this.wrapper.tokens[this.outputSwapToken];
|
|
299
|
+
}
|
|
300
|
+
getOutput() {
|
|
301
|
+
return (0, TokenAmount_1.toTokenAmount)(this.outputTotalSwap, this.wrapper.tokens[this.outputSwapToken], this.wrapper.prices, this.pricingInfo);
|
|
302
|
+
}
|
|
303
|
+
getGasDropOutput() {
|
|
304
|
+
return (0, TokenAmount_1.toTokenAmount)(this.outputTotalGas, this.wrapper.tokens[this.outputGasToken], this.wrapper.prices, this.gasPricingInfo);
|
|
305
|
+
}
|
|
306
|
+
getInputWithoutFee() {
|
|
307
|
+
return (0, TokenAmount_1.toTokenAmount)(this.getInputAmountWithoutFee(), Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
308
|
+
}
|
|
309
|
+
getInputToken() {
|
|
310
|
+
return Token_1.BitcoinTokens.BTC;
|
|
311
|
+
}
|
|
312
|
+
getInput() {
|
|
313
|
+
return (0, TokenAmount_1.toTokenAmount)(this.btcAmount, Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
314
|
+
}
|
|
315
|
+
//////////////////////////////
|
|
316
|
+
//// Bitcoin tx
|
|
317
|
+
getRequiredConfirmationsCount() {
|
|
318
|
+
return this.vaultRequiredConfirmations;
|
|
319
|
+
}
|
|
320
|
+
async getTransactionDetails() {
|
|
321
|
+
const [txId, voutStr] = this.vaultUtxo.split(":");
|
|
322
|
+
const vaultScript = (0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, this.vaultBtcAddress);
|
|
323
|
+
const out2script = (0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, this.btcDestinationAddress);
|
|
324
|
+
const opReturnData = this.wrapper.contract.toOpReturnData(this.recipient, [
|
|
325
|
+
this.outputTotalSwap / this.vaultTokenMultipliers[0],
|
|
326
|
+
this.outputTotalGas / this.vaultTokenMultipliers[1]
|
|
327
|
+
]);
|
|
328
|
+
const out1script = buffer_1.Buffer.concat([
|
|
329
|
+
opReturnData.length > 75 ? buffer_1.Buffer.from([0x6a, 0x4c, opReturnData.length]) : buffer_1.Buffer.from([0x6a, opReturnData.length]),
|
|
330
|
+
opReturnData
|
|
331
|
+
]);
|
|
332
|
+
if (this.callerFeeShare < 0n || this.callerFeeShare > 0xfffffn)
|
|
333
|
+
throw new Error("Caller fee out of bounds!");
|
|
334
|
+
if (this.frontingFeeShare < 0n || this.frontingFeeShare > 0xfffffn)
|
|
335
|
+
throw new Error("Fronting fee out of bounds!");
|
|
336
|
+
if (this.executionFeeShare < 0n || this.executionFeeShare > 0xfffffn)
|
|
337
|
+
throw new Error("Execution fee out of bounds!");
|
|
338
|
+
const nSequence0 = 0x80000000n | (this.callerFeeShare & 0xfffffn) | (this.frontingFeeShare & 1047552n) << 10n;
|
|
339
|
+
const nSequence1 = 0x80000000n | (this.executionFeeShare & 0xfffffn) | (this.frontingFeeShare & 1023n) << 20n;
|
|
340
|
+
return {
|
|
341
|
+
in0txid: txId,
|
|
342
|
+
in0vout: parseInt(voutStr),
|
|
343
|
+
in0sequence: Number(nSequence0),
|
|
344
|
+
vaultAmount: this.vaultUtxoValue,
|
|
345
|
+
vaultScript,
|
|
346
|
+
in1sequence: Number(nSequence1),
|
|
347
|
+
out1script,
|
|
348
|
+
out2amount: this.btcAmount,
|
|
349
|
+
out2script,
|
|
350
|
+
locktime: 500000000 + Math.floor(Math.random() * 1000000000) //Use this as a random salt to make the btc txId unique!
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Returns the raw PSBT (not funded), the wallet should fund the PSBT (add its inputs), set the nSequence field of the
|
|
355
|
+
* 2nd input (input 1 - indexing from 0) to the value returned in `in1sequence`, sign the PSBT and then pass
|
|
356
|
+
* it back to the SDK with `swap.submitPsbt()`
|
|
357
|
+
*/
|
|
358
|
+
async getPsbt() {
|
|
359
|
+
const res = await this.getTransactionDetails();
|
|
360
|
+
const psbt = new btc_signer_1.Transaction({
|
|
361
|
+
allowUnknownOutputs: true,
|
|
362
|
+
allowLegacyWitnessUtxo: true,
|
|
363
|
+
lockTime: res.locktime
|
|
364
|
+
});
|
|
365
|
+
psbt.addInput({
|
|
366
|
+
txid: res.in0txid,
|
|
367
|
+
index: res.in0vout,
|
|
368
|
+
witnessUtxo: {
|
|
369
|
+
amount: res.vaultAmount,
|
|
370
|
+
script: res.vaultScript
|
|
371
|
+
},
|
|
372
|
+
sequence: res.in0sequence
|
|
373
|
+
});
|
|
374
|
+
psbt.addOutput({
|
|
375
|
+
amount: res.vaultAmount,
|
|
376
|
+
script: res.vaultScript
|
|
377
|
+
});
|
|
378
|
+
psbt.addOutput({
|
|
379
|
+
amount: 0n,
|
|
380
|
+
script: res.out1script
|
|
381
|
+
});
|
|
382
|
+
psbt.addOutput({
|
|
383
|
+
amount: res.out2amount,
|
|
384
|
+
script: res.out2script
|
|
385
|
+
});
|
|
386
|
+
const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
|
|
387
|
+
return {
|
|
388
|
+
psbt,
|
|
389
|
+
psbtHex: serializedPsbt.toString("hex"),
|
|
390
|
+
psbtBase64: serializedPsbt.toString("base64"),
|
|
391
|
+
in1sequence: res.in1sequence
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Returns the PSBT that is already funded with wallet's UTXOs (runs a coin-selection algorithm to choose UTXOs to use),
|
|
396
|
+
* also returns inputs indices that need to be signed by the wallet before submitting the PSBT back to the SDK with
|
|
397
|
+
* `swap.submitPsbt()`
|
|
398
|
+
*
|
|
399
|
+
* @param _bitcoinWallet Sender's bitcoin wallet
|
|
400
|
+
* @param feeRate Optional fee rate for the transaction, needs to be at least as big as {minimumBtcFeeRate} field
|
|
401
|
+
* @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
|
|
402
|
+
*/
|
|
403
|
+
async getFundedPsbt(_bitcoinWallet, feeRate, additionalOutputs) {
|
|
404
|
+
const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper.btcRpc, this.wrapper.options.bitcoinNetwork);
|
|
405
|
+
if (feeRate != null) {
|
|
406
|
+
if (feeRate < this.minimumBtcFeeRate)
|
|
407
|
+
throw new Error("Bitcoin tx fee needs to be at least " + this.minimumBtcFeeRate + " sats/vB");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
feeRate = Math.max(this.minimumBtcFeeRate, await bitcoinWallet.getFeeRate());
|
|
411
|
+
}
|
|
412
|
+
let { psbt, in1sequence } = await this.getPsbt();
|
|
413
|
+
if (additionalOutputs != null)
|
|
414
|
+
additionalOutputs.forEach(output => {
|
|
415
|
+
psbt.addOutput({
|
|
416
|
+
amount: output.amount,
|
|
417
|
+
script: output.outputScript ?? (0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, output.address)
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
psbt = await bitcoinWallet.fundPsbt(psbt, feeRate);
|
|
421
|
+
psbt.updateInput(1, { sequence: in1sequence });
|
|
422
|
+
//Sign every input except the first one
|
|
423
|
+
const signInputs = [];
|
|
424
|
+
for (let i = 1; i < psbt.inputsLength; i++) {
|
|
425
|
+
signInputs.push(i);
|
|
426
|
+
}
|
|
427
|
+
const serializedPsbt = buffer_1.Buffer.from(psbt.toPSBT());
|
|
428
|
+
return {
|
|
429
|
+
psbt,
|
|
430
|
+
psbtHex: serializedPsbt.toString("hex"),
|
|
431
|
+
psbtBase64: serializedPsbt.toString("base64"),
|
|
432
|
+
signInputs
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Submits a PSBT signed by the wallet back to the SDK
|
|
437
|
+
*
|
|
438
|
+
* @param _psbt A psbt - either a Transaction object or a hex or base64 encoded PSBT string
|
|
439
|
+
*/
|
|
440
|
+
async submitPsbt(_psbt) {
|
|
441
|
+
const psbt = (0, BitcoinUtils_1.parsePsbtTransaction)(_psbt);
|
|
442
|
+
//Ensure not expired
|
|
443
|
+
if (this.expiry < Date.now()) {
|
|
444
|
+
throw new Error("Quote expired!");
|
|
445
|
+
}
|
|
446
|
+
//Ensure valid state
|
|
447
|
+
if (this.state !== SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this.state !== SpvFromBTCSwapState.CREATED) {
|
|
448
|
+
throw new Error("Invalid swap state!");
|
|
449
|
+
}
|
|
450
|
+
if (this.url == null)
|
|
451
|
+
throw new Error("LP URL not known, cannot submit PSBT!");
|
|
452
|
+
//Ensure all inputs except the 1st are finalized
|
|
453
|
+
for (let i = 1; i < psbt.inputsLength; i++) {
|
|
454
|
+
if ((0, btc_signer_1.getInputType)(psbt.getInput(i)).txType === "legacy")
|
|
455
|
+
throw new Error("Legacy (non-segwit) inputs are not allowed in the transaction!");
|
|
456
|
+
psbt.finalizeIdx(i);
|
|
457
|
+
}
|
|
458
|
+
const btcTx = await this.wrapper.btcRpc.parseTransaction(buffer_1.Buffer.from(psbt.toBytes(true)).toString("hex"));
|
|
459
|
+
const data = await this.wrapper.contract.getWithdrawalData(btcTx);
|
|
460
|
+
this.logger.debug("submitPsbt(): parsed withdrawal data: ", data);
|
|
461
|
+
//Verify correct withdrawal data
|
|
462
|
+
if (!data.isRecipient(this.recipient) ||
|
|
463
|
+
data.rawAmounts[0] * this.vaultTokenMultipliers[0] !== this.outputTotalSwap ||
|
|
464
|
+
(data.rawAmounts[1] ?? 0n) * this.vaultTokenMultipliers[1] !== this.outputTotalGas ||
|
|
465
|
+
data.callerFeeRate !== this.callerFeeShare ||
|
|
466
|
+
data.frontingFeeRate !== this.frontingFeeShare ||
|
|
467
|
+
data.executionFeeRate !== this.executionFeeShare ||
|
|
468
|
+
data.getSpentVaultUtxo() !== this.vaultUtxo ||
|
|
469
|
+
BigInt(data.getNewVaultBtcAmount()) !== this.vaultUtxoValue ||
|
|
470
|
+
!data.getNewVaultScript().equals((0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, this.vaultBtcAddress)) ||
|
|
471
|
+
data.getExecutionData() != null) {
|
|
472
|
+
throw new Error("Invalid withdrawal tx data submitted!");
|
|
473
|
+
}
|
|
474
|
+
//Verify correct LP output
|
|
475
|
+
const lpOutput = psbt.getOutput(2);
|
|
476
|
+
if (lpOutput.script == null ||
|
|
477
|
+
lpOutput.amount !== this.btcAmount ||
|
|
478
|
+
!(0, BitcoinUtils_1.toOutputScript)(this.wrapper.options.bitcoinNetwork, this.btcDestinationAddress).equals(buffer_1.Buffer.from(lpOutput.script))) {
|
|
479
|
+
throw new Error("Invalid LP bitcoin output in transaction!");
|
|
480
|
+
}
|
|
481
|
+
//Verify vault utxo not spent yet
|
|
482
|
+
if (await this.wrapper.btcRpc.isSpent(this.vaultUtxo)) {
|
|
483
|
+
throw new Error("Vault UTXO already spent, please create new swap!");
|
|
484
|
+
}
|
|
485
|
+
//Verify tx is parsable by the contract
|
|
486
|
+
try {
|
|
487
|
+
await this.wrapper.contract.checkWithdrawalTx(data);
|
|
488
|
+
}
|
|
489
|
+
catch (e) {
|
|
490
|
+
throw new Error("Transaction not parsable by the contract: " + (e.message ?? e.toString()));
|
|
491
|
+
}
|
|
492
|
+
//Ensure still not expired
|
|
493
|
+
if (this.expiry < Date.now()) {
|
|
494
|
+
throw new Error("Quote expired!");
|
|
495
|
+
}
|
|
496
|
+
this.data = data;
|
|
497
|
+
this.initiated = true;
|
|
498
|
+
await this._saveAndEmit(SpvFromBTCSwapState.SIGNED);
|
|
499
|
+
try {
|
|
500
|
+
await IntermediaryAPI_1.IntermediaryAPI.initSpvFromBTC(this.chainIdentifier, this.url, {
|
|
501
|
+
quoteId: this.quoteId,
|
|
502
|
+
psbtHex: buffer_1.Buffer.from(psbt.toPSBT(0)).toString("hex")
|
|
503
|
+
});
|
|
504
|
+
await this._saveAndEmit(SpvFromBTCSwapState.POSTED);
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
await this._saveAndEmit(SpvFromBTCSwapState.DECLINED);
|
|
508
|
+
throw e;
|
|
509
|
+
}
|
|
510
|
+
return this.data.getTxId();
|
|
511
|
+
}
|
|
512
|
+
async estimateBitcoinFee(_bitcoinWallet, feeRate) {
|
|
513
|
+
const bitcoinWallet = (0, BitcoinWalletUtils_1.toBitcoinWallet)(_bitcoinWallet, this.wrapper.btcRpc, this.wrapper.options.bitcoinNetwork);
|
|
514
|
+
const txFee = await bitcoinWallet.getFundedPsbtFee((await this.getPsbt()).psbt, feeRate);
|
|
515
|
+
if (txFee == null)
|
|
516
|
+
return null;
|
|
517
|
+
return (0, TokenAmount_1.toTokenAmount)(BigInt(txFee), Token_1.BitcoinTokens.BTC, this.wrapper.prices, this.pricingInfo);
|
|
518
|
+
}
|
|
519
|
+
async sendBitcoinTransaction(wallet, feeRate) {
|
|
520
|
+
const { psbt, psbtBase64, psbtHex, signInputs } = await this.getFundedPsbt(wallet, feeRate);
|
|
521
|
+
let signedPsbt;
|
|
522
|
+
if ((0, IBitcoinWallet_1.isIBitcoinWallet)(wallet)) {
|
|
523
|
+
signedPsbt = await wallet.signPsbt(psbt, signInputs);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
signedPsbt = await wallet.signPsbt({
|
|
527
|
+
psbt, psbtHex, psbtBase64
|
|
528
|
+
}, signInputs);
|
|
529
|
+
}
|
|
530
|
+
return await this.submitPsbt(signedPsbt);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Executes the swap with the provided bitcoin wallet,
|
|
534
|
+
*
|
|
535
|
+
* @param wallet Bitcoin wallet to use to sign the bitcoin transaction
|
|
536
|
+
* @param callbacks Callbacks to track the progress of the swap
|
|
537
|
+
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
538
|
+
*
|
|
539
|
+
* @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
|
|
540
|
+
* user, in case `false` is returned the user should call `swap.claim()` to settle the swap on the destination manually
|
|
541
|
+
*/
|
|
542
|
+
async execute(wallet, callbacks, options) {
|
|
543
|
+
if (this.state === SpvFromBTCSwapState.CLOSED)
|
|
544
|
+
throw new Error("Swap encountered a catastrophic failure!");
|
|
545
|
+
if (this.state === SpvFromBTCSwapState.FAILED)
|
|
546
|
+
throw new Error("Swap failed!");
|
|
547
|
+
if (this.state === SpvFromBTCSwapState.DECLINED)
|
|
548
|
+
throw new Error("Swap execution already declined by the LP!");
|
|
549
|
+
if (this.state === SpvFromBTCSwapState.QUOTE_EXPIRED || this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED)
|
|
550
|
+
throw new Error("Swap quote expired!");
|
|
551
|
+
if (this.state === SpvFromBTCSwapState.CLAIMED || this.state === SpvFromBTCSwapState.FRONTED)
|
|
552
|
+
throw new Error("Swap already settled or fronted!");
|
|
553
|
+
if (this.state === SpvFromBTCSwapState.CREATED) {
|
|
554
|
+
const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
|
|
555
|
+
if (callbacks?.onSourceTransactionSent != null)
|
|
556
|
+
callbacks.onSourceTransactionSent(txId);
|
|
557
|
+
}
|
|
558
|
+
if (this.state === SpvFromBTCSwapState.POSTED || this.state === SpvFromBTCSwapState.BROADCASTED) {
|
|
559
|
+
const txId = await this.waitForBitcoinTransaction(callbacks?.onSourceTransactionConfirmationStatus, options?.btcTxCheckIntervalSeconds, options?.abortSignal);
|
|
560
|
+
if (callbacks?.onSourceTransactionConfirmed != null)
|
|
561
|
+
callbacks.onSourceTransactionConfirmed(txId);
|
|
562
|
+
}
|
|
563
|
+
// @ts-ignore
|
|
564
|
+
if (this.state === SpvFromBTCSwapState.CLAIMED || this.state === SpvFromBTCSwapState.FRONTED)
|
|
565
|
+
return true;
|
|
566
|
+
if (this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
|
|
567
|
+
const success = await this.waitTillClaimedOrFronted(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal);
|
|
568
|
+
if (success && callbacks?.onSwapSettled != null)
|
|
569
|
+
callbacks.onSwapSettled(this.getOutputTxId());
|
|
570
|
+
return success;
|
|
571
|
+
}
|
|
572
|
+
throw new Error("Unexpected state reached!");
|
|
573
|
+
}
|
|
574
|
+
async txsExecute(options) {
|
|
575
|
+
if (this.state === SpvFromBTCSwapState.CREATED) {
|
|
576
|
+
if (!await this.verifyQuoteValid())
|
|
577
|
+
throw new Error("Quote already expired or close to expiry!");
|
|
578
|
+
return [
|
|
579
|
+
{
|
|
580
|
+
name: "Payment",
|
|
581
|
+
description: "Send funds to the bitcoin swap address",
|
|
582
|
+
chain: "BITCOIN",
|
|
583
|
+
txs: [
|
|
584
|
+
options?.bitcoinWallet == null
|
|
585
|
+
? { ...await this.getPsbt(), type: "RAW_PSBT" }
|
|
586
|
+
: { ...await this.getFundedPsbt(options.bitcoinWallet), type: "FUNDED_PSBT" }
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
];
|
|
590
|
+
}
|
|
591
|
+
throw new Error("Invalid swap state to obtain execution txns, required CREATED");
|
|
592
|
+
}
|
|
593
|
+
//////////////////////////////
|
|
594
|
+
//// Bitcoin tx listener
|
|
595
|
+
/**
|
|
596
|
+
* Checks whether a bitcoin payment was already made, returns the payment or null when no payment has been made.
|
|
597
|
+
*/
|
|
598
|
+
async getBitcoinPayment() {
|
|
599
|
+
if (this.data?.btcTx?.txid == null)
|
|
600
|
+
return null;
|
|
601
|
+
const result = await this.wrapper.btcRpc.getTransaction(this.data?.btcTx?.txid);
|
|
602
|
+
if (result == null)
|
|
603
|
+
return null;
|
|
604
|
+
return {
|
|
605
|
+
txId: result.txid,
|
|
606
|
+
confirmations: result.confirmations ?? 0,
|
|
607
|
+
targetConfirmations: this.vaultRequiredConfirmations,
|
|
608
|
+
inputAddresses: result.inputAddresses
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Waits till the bitcoin transaction confirms and swap becomes claimable
|
|
613
|
+
*
|
|
614
|
+
* @param updateCallback Callback called when txId is found, and also called with subsequent confirmations
|
|
615
|
+
* @param checkIntervalSeconds How often to check the bitcoin transaction
|
|
616
|
+
* @param abortSignal Abort signal
|
|
617
|
+
* @throws {Error} if in invalid state (must be CLAIM_COMMITED)
|
|
618
|
+
*/
|
|
619
|
+
async waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal) {
|
|
620
|
+
if (this.state !== SpvFromBTCSwapState.POSTED &&
|
|
621
|
+
this.state !== SpvFromBTCSwapState.BROADCASTED &&
|
|
622
|
+
!(this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && this.initiated))
|
|
623
|
+
throw new Error("Must be in POSTED or BROADCASTED state!");
|
|
624
|
+
if (this.data == null)
|
|
625
|
+
throw new Error("Expected swap to have withdrawal data filled!");
|
|
626
|
+
const result = await this.wrapper.btcRpc.waitForTransaction(this.data.btcTx.txid, this.vaultRequiredConfirmations, (btcTx, txEtaMs) => {
|
|
627
|
+
if (updateCallback != null)
|
|
628
|
+
updateCallback(btcTx?.txid, btcTx?.confirmations, this.vaultRequiredConfirmations, txEtaMs);
|
|
629
|
+
if (btcTx == null)
|
|
630
|
+
return;
|
|
631
|
+
let save = false;
|
|
632
|
+
if (btcTx.inputAddresses != null && this.senderAddress == null) {
|
|
633
|
+
this.senderAddress = btcTx.inputAddresses[1];
|
|
634
|
+
save = true;
|
|
635
|
+
}
|
|
636
|
+
if (this.state === SpvFromBTCSwapState.POSTED || this.state == SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
637
|
+
this.state = SpvFromBTCSwapState.BROADCASTED;
|
|
638
|
+
save = true;
|
|
639
|
+
}
|
|
640
|
+
if (save)
|
|
641
|
+
this._saveAndEmit();
|
|
642
|
+
}, abortSignal, checkIntervalSeconds);
|
|
643
|
+
if (abortSignal != null)
|
|
644
|
+
abortSignal.throwIfAborted();
|
|
645
|
+
let save = false;
|
|
646
|
+
if (result.inputAddresses != null && this.senderAddress == null) {
|
|
647
|
+
this.senderAddress = result.inputAddresses[1];
|
|
648
|
+
save = true;
|
|
649
|
+
}
|
|
650
|
+
if (this.state !== SpvFromBTCSwapState.FRONTED &&
|
|
651
|
+
this.state !== SpvFromBTCSwapState.CLAIMED) {
|
|
652
|
+
this.state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
|
|
653
|
+
save = true;
|
|
654
|
+
}
|
|
655
|
+
if (save)
|
|
656
|
+
await this._saveAndEmit();
|
|
657
|
+
return result.txid;
|
|
658
|
+
}
|
|
659
|
+
//////////////////////////////
|
|
660
|
+
//// Claim
|
|
661
|
+
/**
|
|
662
|
+
* Returns transactions required to claim the swap on-chain (and possibly also sync the bitcoin light client)
|
|
663
|
+
* after a bitcoin transaction was sent and confirmed
|
|
664
|
+
*
|
|
665
|
+
* @throws {Error} If the swap is in invalid state (must be BTC_TX_CONFIRMED)
|
|
666
|
+
*/
|
|
667
|
+
async txsClaim(_signer) {
|
|
668
|
+
let address = undefined;
|
|
669
|
+
if (_signer != null) {
|
|
670
|
+
if (typeof (_signer) === "string") {
|
|
671
|
+
address = _signer;
|
|
672
|
+
}
|
|
673
|
+
else if ((0, base_1.isAbstractSigner)(_signer)) {
|
|
674
|
+
address = _signer.getAddress();
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
address = (await this.wrapper.chain.wrapSigner(_signer)).getAddress();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (!this.isClaimable())
|
|
681
|
+
throw new Error("Must be in BTC_TX_CONFIRMED state!");
|
|
682
|
+
if (this.data == null)
|
|
683
|
+
throw new Error("Expected swap to have withdrawal data filled!");
|
|
684
|
+
const vaultData = await this.wrapper.contract.getVaultData(this.vaultOwner, this.vaultId);
|
|
685
|
+
if (vaultData == null)
|
|
686
|
+
throw new Error(`Vault data for ${this.vaultOwner}:${this.vaultId.toString(10)} not found (already closed???)!`);
|
|
687
|
+
const btcTx = await this.wrapper.btcRpc.getTransaction(this.data.btcTx.txid);
|
|
688
|
+
if (btcTx == null)
|
|
689
|
+
throw new Error(`Bitcoin transaction ${this.data.btcTx.txid} not found!`);
|
|
690
|
+
const txs = [btcTx];
|
|
691
|
+
//Trace back from current tx to the vaultData-specified UTXO
|
|
692
|
+
const vaultUtxo = vaultData.getUtxo();
|
|
693
|
+
while (txs[0].ins[0].txid + ":" + txs[0].ins[0].vout !== vaultUtxo) {
|
|
694
|
+
const btcTx = await this.wrapper.btcRpc.getTransaction(txs[0].ins[0].txid);
|
|
695
|
+
if (btcTx == null)
|
|
696
|
+
throw new Error(`Prior withdrawal bitcoin transaction ${this.data.btcTx.txid} not found!`);
|
|
697
|
+
txs.unshift(btcTx);
|
|
698
|
+
}
|
|
699
|
+
//Parse transactions to withdrawal data
|
|
700
|
+
const withdrawalData = [];
|
|
701
|
+
for (let tx of txs) {
|
|
702
|
+
withdrawalData.push(await this.wrapper.contract.getWithdrawalData(tx));
|
|
703
|
+
}
|
|
704
|
+
return await this.wrapper.contract.txsClaim(address ?? this._getInitiator(), vaultData, withdrawalData.map(tx => { return { tx }; }), this.wrapper.synchronizer, true);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Claims and finishes the swap
|
|
708
|
+
*
|
|
709
|
+
* @param _signer Signer to sign the transactions with, can also be different to the initializer
|
|
710
|
+
* @param abortSignal Abort signal to stop waiting for transaction confirmation
|
|
711
|
+
*/
|
|
712
|
+
async claim(_signer, abortSignal) {
|
|
713
|
+
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
|
|
714
|
+
let txIds;
|
|
715
|
+
try {
|
|
716
|
+
txIds = await this.wrapper.chain.sendAndConfirm(signer, await this.txsClaim(signer), true, abortSignal);
|
|
717
|
+
}
|
|
718
|
+
catch (e) {
|
|
719
|
+
if (this.data == null)
|
|
720
|
+
throw e;
|
|
721
|
+
this.logger.info("claim(): Failed to claim ourselves, checking swap claim state...");
|
|
722
|
+
if (this.state === SpvFromBTCSwapState.CLAIMED) {
|
|
723
|
+
this.logger.info("claim(): Transaction state is CLAIMED, swap was successfully claimed by the watchtower");
|
|
724
|
+
return this.claimTxId;
|
|
725
|
+
}
|
|
726
|
+
const withdrawalState = await this.wrapper.contract.getWithdrawalState(this.data, this.genesisSmartChainBlockHeight);
|
|
727
|
+
if (withdrawalState != null && withdrawalState.type === base_1.SpvWithdrawalStateType.CLAIMED) {
|
|
728
|
+
this.logger.info("claim(): Transaction status is CLAIMED, swap was successfully claimed by the watchtower");
|
|
729
|
+
this.claimTxId = withdrawalState.txId;
|
|
730
|
+
await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
|
|
731
|
+
return withdrawalState.txId;
|
|
732
|
+
}
|
|
733
|
+
throw e;
|
|
734
|
+
}
|
|
735
|
+
this.claimTxId = txIds[0];
|
|
736
|
+
if (this.state === SpvFromBTCSwapState.POSTED || this.state === SpvFromBTCSwapState.BROADCASTED ||
|
|
737
|
+
this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED || this.state === SpvFromBTCSwapState.FAILED ||
|
|
738
|
+
this.state === SpvFromBTCSwapState.FRONTED) {
|
|
739
|
+
await this._saveAndEmit(SpvFromBTCSwapState.CLAIMED);
|
|
740
|
+
}
|
|
741
|
+
return txIds[0];
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Periodically checks the chain to see whether the swap was finished (claimed or refunded)
|
|
745
|
+
*
|
|
746
|
+
* @param abortSignal
|
|
747
|
+
* @param interval How often to check (in seconds), default to 5s
|
|
748
|
+
* @protected
|
|
749
|
+
*/
|
|
750
|
+
async watchdogWaitTillResult(abortSignal, interval = 5) {
|
|
751
|
+
if (this.data == null)
|
|
752
|
+
throw new Error("Cannot await the result before the btc transaction is sent!");
|
|
753
|
+
let status = { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
|
|
754
|
+
while (status.type === base_1.SpvWithdrawalStateType.NOT_FOUND) {
|
|
755
|
+
await (0, TimeoutUtils_1.timeoutPromise)(interval * 1000, abortSignal);
|
|
756
|
+
try {
|
|
757
|
+
//Be smart about checking withdrawal state
|
|
758
|
+
if (await this._shouldCheckWithdrawalState()) {
|
|
759
|
+
status = await this.wrapper.contract.getWithdrawalState(this.data, this.genesisSmartChainBlockHeight) ?? { type: base_1.SpvWithdrawalStateType.NOT_FOUND };
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
catch (e) {
|
|
763
|
+
this.logger.error("watchdogWaitTillResult(): Error when fetching commit status: ", e);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (abortSignal != null)
|
|
767
|
+
abortSignal.throwIfAborted();
|
|
768
|
+
return status;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Waits till the swap is successfully executed
|
|
772
|
+
*
|
|
773
|
+
* @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
|
|
774
|
+
* @param abortSignal
|
|
775
|
+
* @throws {Error} If swap is in invalid state (must be BTC_TX_CONFIRMED)
|
|
776
|
+
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
777
|
+
* @returns {boolean} whether the swap was claimed or fronted automatically or not, if the swap was not claimed
|
|
778
|
+
* the user can claim manually through `swap.claim()`
|
|
779
|
+
*/
|
|
780
|
+
async waitTillClaimedOrFronted(maxWaitTimeSeconds, abortSignal) {
|
|
781
|
+
if (this.state === SpvFromBTCSwapState.CLAIMED || this.state === SpvFromBTCSwapState.FRONTED)
|
|
782
|
+
return Promise.resolve(true);
|
|
783
|
+
const abortController = (0, Utils_1.extendAbortController)(abortSignal);
|
|
784
|
+
let timedOut = false;
|
|
785
|
+
if (maxWaitTimeSeconds != null) {
|
|
786
|
+
const timeout = setTimeout(() => {
|
|
787
|
+
timedOut = true;
|
|
788
|
+
abortController.abort();
|
|
789
|
+
}, maxWaitTimeSeconds * 1000);
|
|
790
|
+
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
791
|
+
}
|
|
792
|
+
let res;
|
|
793
|
+
try {
|
|
794
|
+
res = await Promise.race([
|
|
795
|
+
this.watchdogWaitTillResult(abortController.signal),
|
|
796
|
+
this.waitTillState(SpvFromBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 0),
|
|
797
|
+
this.waitTillState(SpvFromBTCSwapState.FRONTED, "eq", abortController.signal).then(() => 1),
|
|
798
|
+
this.waitTillState(SpvFromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 2),
|
|
799
|
+
]);
|
|
800
|
+
abortController.abort();
|
|
801
|
+
}
|
|
802
|
+
catch (e) {
|
|
803
|
+
abortController.abort();
|
|
804
|
+
if (timedOut)
|
|
805
|
+
return false;
|
|
806
|
+
throw e;
|
|
807
|
+
}
|
|
808
|
+
if (typeof (res) === "number") {
|
|
809
|
+
if (res === 0) {
|
|
810
|
+
this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (CLAIMED)");
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
if (res === 1) {
|
|
814
|
+
this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FRONTED)");
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
if (res === 2) {
|
|
818
|
+
this.logger.debug("waitTillClaimedOrFronted(): Resolved from state change (FAILED)");
|
|
819
|
+
throw new Error("Swap failed while waiting for claim or front");
|
|
820
|
+
}
|
|
821
|
+
throw new Error("Invalid numeric response, this should never happen!");
|
|
822
|
+
}
|
|
823
|
+
this.logger.debug("waitTillClaimedOrFronted(): Resolved from watchdog");
|
|
824
|
+
if (res.type === base_1.SpvWithdrawalStateType.FRONTED) {
|
|
825
|
+
if (this.state !== SpvFromBTCSwapState.FRONTED ||
|
|
826
|
+
this.state !== SpvFromBTCSwapState.CLAIMED) {
|
|
827
|
+
this.frontTxId = res.txId;
|
|
828
|
+
await this._saveAndEmit(SpvFromBTCSwapState.FRONTED);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (res.type === base_1.SpvWithdrawalStateType.CLAIMED) {
|
|
832
|
+
if (this.state !== SpvFromBTCSwapState.CLAIMED) {
|
|
833
|
+
this.claimTxId = res.txId;
|
|
834
|
+
await this._saveAndEmit(SpvFromBTCSwapState.FRONTED);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (res.type === base_1.SpvWithdrawalStateType.CLOSED) {
|
|
838
|
+
if (this.state !== SpvFromBTCSwapState.CLOSED)
|
|
839
|
+
await this._saveAndEmit(SpvFromBTCSwapState.CLOSED);
|
|
840
|
+
throw new Error("Swap failed with catastrophic error!");
|
|
841
|
+
}
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Waits till the bitcoin transaction confirms and swap is claimed
|
|
846
|
+
*
|
|
847
|
+
* @param abortSignal Abort signal
|
|
848
|
+
* @param checkIntervalSeconds How often to check the bitcoin transaction
|
|
849
|
+
* @param updateCallback Callback called when txId is found, and also called with subsequent confirmations
|
|
850
|
+
* @throws {Error} if in invalid state (must be CLAIM_COMMITED)
|
|
851
|
+
*/
|
|
852
|
+
async waitTillExecuted(updateCallback, checkIntervalSeconds, abortSignal) {
|
|
853
|
+
await this.waitForBitcoinTransaction(updateCallback, checkIntervalSeconds, abortSignal);
|
|
854
|
+
await this.waitTillClaimedOrFronted(undefined, abortSignal);
|
|
855
|
+
}
|
|
856
|
+
//////////////////////////////
|
|
857
|
+
//// Storage
|
|
858
|
+
serialize() {
|
|
859
|
+
return {
|
|
860
|
+
...super.serialize(),
|
|
861
|
+
quoteId: this.quoteId,
|
|
862
|
+
recipient: this.recipient,
|
|
863
|
+
vaultOwner: this.vaultOwner,
|
|
864
|
+
vaultId: this.vaultId.toString(10),
|
|
865
|
+
vaultRequiredConfirmations: this.vaultRequiredConfirmations,
|
|
866
|
+
vaultTokenMultipliers: this.vaultTokenMultipliers.map(val => val.toString(10)),
|
|
867
|
+
vaultBtcAddress: this.vaultBtcAddress,
|
|
868
|
+
vaultUtxo: this.vaultUtxo,
|
|
869
|
+
vaultUtxoValue: this.vaultUtxoValue.toString(10),
|
|
870
|
+
btcDestinationAddress: this.btcDestinationAddress,
|
|
871
|
+
btcAmount: this.btcAmount.toString(10),
|
|
872
|
+
btcAmountSwap: this.btcAmountSwap.toString(10),
|
|
873
|
+
btcAmountGas: this.btcAmountGas.toString(10),
|
|
874
|
+
minimumBtcFeeRate: this.minimumBtcFeeRate,
|
|
875
|
+
outputTotalSwap: this.outputTotalSwap.toString(10),
|
|
876
|
+
outputSwapToken: this.outputSwapToken,
|
|
877
|
+
outputTotalGas: this.outputTotalGas.toString(10),
|
|
878
|
+
outputGasToken: this.outputGasToken,
|
|
879
|
+
gasSwapFeeBtc: this.gasSwapFeeBtc.toString(10),
|
|
880
|
+
gasSwapFee: this.gasSwapFee.toString(10),
|
|
881
|
+
callerFeeShare: this.callerFeeShare.toString(10),
|
|
882
|
+
frontingFeeShare: this.frontingFeeShare.toString(10),
|
|
883
|
+
executionFeeShare: this.executionFeeShare.toString(10),
|
|
884
|
+
genesisSmartChainBlockHeight: this.genesisSmartChainBlockHeight,
|
|
885
|
+
gasPricingInfo: (0, PriceInfoType_1.serializePriceInfoType)(this.gasPricingInfo),
|
|
886
|
+
senderAddress: this.senderAddress,
|
|
887
|
+
claimTxId: this.claimTxId,
|
|
888
|
+
frontTxId: this.frontTxId,
|
|
889
|
+
data: this.data?.serialize()
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
//////////////////////////////
|
|
893
|
+
//// Swap ticks & sync
|
|
894
|
+
/**
|
|
895
|
+
* For internal use! Used to set the txId of the bitcoin payment from the on-chain events listener
|
|
896
|
+
*
|
|
897
|
+
* @param txId
|
|
898
|
+
*/
|
|
899
|
+
async _setBitcoinTxId(txId) {
|
|
900
|
+
if (this.data == null)
|
|
901
|
+
return;
|
|
902
|
+
if (txId != this.data.btcTx.txid)
|
|
903
|
+
return;
|
|
904
|
+
if (this.senderAddress != null)
|
|
905
|
+
return;
|
|
906
|
+
const btcTx = await this.wrapper.btcRpc.getTransaction(txId);
|
|
907
|
+
if (btcTx == null || btcTx.inputAddresses == null)
|
|
908
|
+
return;
|
|
909
|
+
this.senderAddress = btcTx.inputAddresses[1];
|
|
910
|
+
}
|
|
911
|
+
async _syncStateFromBitcoin(save) {
|
|
912
|
+
if (this.data?.btcTx == null)
|
|
913
|
+
return false;
|
|
914
|
+
//Check if bitcoin payment was confirmed
|
|
915
|
+
const res = await this.getBitcoinPayment();
|
|
916
|
+
if (res == null) {
|
|
917
|
+
//Check inputs double-spent
|
|
918
|
+
for (let input of this.data.btcTx.ins) {
|
|
919
|
+
if (await this.wrapper.btcRpc.isSpent(input.txid + ":" + input.vout, true)) {
|
|
920
|
+
if (this.state === SpvFromBTCSwapState.SIGNED ||
|
|
921
|
+
this.state === SpvFromBTCSwapState.POSTED ||
|
|
922
|
+
this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
923
|
+
this.state === SpvFromBTCSwapState.DECLINED) {
|
|
924
|
+
//One of the inputs was double-spent
|
|
925
|
+
this.state = SpvFromBTCSwapState.QUOTE_EXPIRED;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
//One of the inputs was double-spent
|
|
929
|
+
this.state = SpvFromBTCSwapState.FAILED;
|
|
930
|
+
}
|
|
931
|
+
if (save)
|
|
932
|
+
await this._saveAndEmit();
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
let needsSave = false;
|
|
939
|
+
if (res.inputAddresses != null && this.senderAddress == null) {
|
|
940
|
+
this.senderAddress = res.inputAddresses[1];
|
|
941
|
+
needsSave = true;
|
|
942
|
+
}
|
|
943
|
+
if (res.confirmations >= this.vaultRequiredConfirmations) {
|
|
944
|
+
if (this.state !== SpvFromBTCSwapState.BTC_TX_CONFIRMED &&
|
|
945
|
+
this.state !== SpvFromBTCSwapState.FRONTED &&
|
|
946
|
+
this.state !== SpvFromBTCSwapState.CLAIMED) {
|
|
947
|
+
this.state = SpvFromBTCSwapState.BTC_TX_CONFIRMED;
|
|
948
|
+
needsSave = true;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
else if (this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
952
|
+
this.state === SpvFromBTCSwapState.POSTED ||
|
|
953
|
+
this.state === SpvFromBTCSwapState.SIGNED ||
|
|
954
|
+
this.state === SpvFromBTCSwapState.DECLINED) {
|
|
955
|
+
this.state = SpvFromBTCSwapState.BROADCASTED;
|
|
956
|
+
needsSave = true;
|
|
957
|
+
}
|
|
958
|
+
if (needsSave && save)
|
|
959
|
+
await this._saveAndEmit();
|
|
960
|
+
return needsSave;
|
|
961
|
+
}
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
966
|
+
* data
|
|
967
|
+
*
|
|
968
|
+
* @private
|
|
969
|
+
*/
|
|
970
|
+
async syncStateFromChain() {
|
|
971
|
+
let changed = false;
|
|
972
|
+
if (this.state === SpvFromBTCSwapState.SIGNED ||
|
|
973
|
+
this.state === SpvFromBTCSwapState.POSTED ||
|
|
974
|
+
this.state === SpvFromBTCSwapState.BROADCASTED ||
|
|
975
|
+
this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
976
|
+
this.state === SpvFromBTCSwapState.DECLINED ||
|
|
977
|
+
this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
|
|
978
|
+
//Check BTC transaction
|
|
979
|
+
if (await this._syncStateFromBitcoin(false))
|
|
980
|
+
changed ||= true;
|
|
981
|
+
}
|
|
982
|
+
if (this.state === SpvFromBTCSwapState.BROADCASTED || this.state === SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
|
|
983
|
+
if (await this._shouldCheckWithdrawalState()) {
|
|
984
|
+
const status = await this.wrapper.contract.getWithdrawalState(this.data, this.genesisSmartChainBlockHeight);
|
|
985
|
+
this.logger.debug("syncStateFromChain(): status of " + this.data.btcTx.txid, status);
|
|
986
|
+
switch (status?.type) {
|
|
987
|
+
case base_1.SpvWithdrawalStateType.FRONTED:
|
|
988
|
+
this.frontTxId = status.txId;
|
|
989
|
+
this.state = SpvFromBTCSwapState.FRONTED;
|
|
990
|
+
changed ||= true;
|
|
991
|
+
break;
|
|
992
|
+
case base_1.SpvWithdrawalStateType.CLAIMED:
|
|
993
|
+
this.claimTxId = status.txId;
|
|
994
|
+
this.state = SpvFromBTCSwapState.CLAIMED;
|
|
995
|
+
changed ||= true;
|
|
996
|
+
break;
|
|
997
|
+
case base_1.SpvWithdrawalStateType.CLOSED:
|
|
998
|
+
this.state = SpvFromBTCSwapState.CLOSED;
|
|
999
|
+
changed ||= true;
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (this.state === SpvFromBTCSwapState.CREATED ||
|
|
1005
|
+
this.state === SpvFromBTCSwapState.SIGNED ||
|
|
1006
|
+
this.state === SpvFromBTCSwapState.POSTED) {
|
|
1007
|
+
if (this.expiry < Date.now()) {
|
|
1008
|
+
if (this.state === SpvFromBTCSwapState.CREATED) {
|
|
1009
|
+
this.state = SpvFromBTCSwapState.QUOTE_EXPIRED;
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
this.state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1013
|
+
}
|
|
1014
|
+
changed ||= true;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return changed;
|
|
1018
|
+
}
|
|
1019
|
+
async _sync(save) {
|
|
1020
|
+
const changed = await this.syncStateFromChain();
|
|
1021
|
+
if (changed && save)
|
|
1022
|
+
await this._saveAndEmit();
|
|
1023
|
+
return changed;
|
|
1024
|
+
}
|
|
1025
|
+
async _tick(save) {
|
|
1026
|
+
if (this.state === SpvFromBTCSwapState.CREATED ||
|
|
1027
|
+
this.state === SpvFromBTCSwapState.SIGNED) {
|
|
1028
|
+
if (this.getQuoteExpiry() < Date.now()) {
|
|
1029
|
+
this.state = SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1030
|
+
if (save)
|
|
1031
|
+
await this._saveAndEmit();
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (this.state === SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED && !this.initiated) {
|
|
1036
|
+
if (this.expiry < Date.now()) {
|
|
1037
|
+
this.state = SpvFromBTCSwapState.QUOTE_EXPIRED;
|
|
1038
|
+
if (save)
|
|
1039
|
+
await this._saveAndEmit();
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
if (Math.floor(Date.now() / 1000) % 120 === 0) {
|
|
1044
|
+
if (this.state === SpvFromBTCSwapState.POSTED ||
|
|
1045
|
+
this.state === SpvFromBTCSwapState.BROADCASTED) {
|
|
1046
|
+
try {
|
|
1047
|
+
//Check if bitcoin payment was confirmed
|
|
1048
|
+
return await this._syncStateFromBitcoin(save);
|
|
1049
|
+
}
|
|
1050
|
+
catch (e) {
|
|
1051
|
+
this.logger.error("tickSwap(" + this.getId() + "): ", e);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
async _shouldCheckWithdrawalState(frontingAddress, vaultDataUtxo) {
|
|
1058
|
+
if (frontingAddress === undefined)
|
|
1059
|
+
frontingAddress = await this.wrapper.contract.getFronterAddress(this.vaultOwner, this.vaultId, this.data);
|
|
1060
|
+
if (vaultDataUtxo === undefined)
|
|
1061
|
+
vaultDataUtxo = await this.wrapper.contract.getVaultLatestUtxo(this.vaultOwner, this.vaultId);
|
|
1062
|
+
if (frontingAddress != null)
|
|
1063
|
+
return true; //In case the swap is fronted there will for sure be a fronted event
|
|
1064
|
+
if (vaultDataUtxo == null)
|
|
1065
|
+
return true; //Vault UTXO is null (the vault closed)
|
|
1066
|
+
const [txId, _] = vaultDataUtxo.split(":");
|
|
1067
|
+
//Don't check both txns if their txId is equal
|
|
1068
|
+
if (this.data.btcTx.txid === txId)
|
|
1069
|
+
return true;
|
|
1070
|
+
const [btcTx, latestVaultTx] = await Promise.all([
|
|
1071
|
+
this.wrapper.btcRpc.getTransaction(this.data.btcTx.txid),
|
|
1072
|
+
this.wrapper.btcRpc.getTransaction(txId)
|
|
1073
|
+
]);
|
|
1074
|
+
if (latestVaultTx == null || latestVaultTx.blockheight == null) {
|
|
1075
|
+
//Something must've gone horribly wrong, the latest vault utxo tx of the vault either
|
|
1076
|
+
// cannot be found on bitcoin network or is not even confirmed yet
|
|
1077
|
+
this.logger.debug(`_shouldCheckWithdrawalState(): Latest vault utxo not found or not confirmed on bitcoin ${txId}`);
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
if (btcTx != null) {
|
|
1081
|
+
const btcTxHeight = btcTx.blockheight;
|
|
1082
|
+
const latestVaultTxHeight = latestVaultTx.blockheight;
|
|
1083
|
+
//We also need to cover the case where bitcoin tx isn't confirmed yet (hence btxTxHeight==null)
|
|
1084
|
+
if (btcTxHeight == null || latestVaultTxHeight < btcTxHeight) {
|
|
1085
|
+
//Definitely not claimed!
|
|
1086
|
+
this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, latestVaultTxHeight: ${latestVaultTx.blockheight}, btcTxHeight: ${btcTxHeight} and not fronted!`);
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
//Definitely not claimed because the transaction was probably double-spent (or evicted from mempool)
|
|
1092
|
+
this.logger.debug(`_shouldCheckWithdrawalState(): Skipped checking withdrawal state, btc tx probably replaced or evicted: ${this.data.btcTx.txid} and not fronted`);
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
exports.SpvFromBTCSwap = SpvFromBTCSwap;
|