@atomiqlabs/sdk 7.0.12 → 8.0.7
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,1082 @@
|
|
|
1
|
+
import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
|
|
2
|
+
import {FromBTCLNDefinition, FromBTCLNWrapper} from "./FromBTCLNWrapper";
|
|
3
|
+
import {IFromBTCSelfInitSwap} from "../IFromBTCSelfInitSwap";
|
|
4
|
+
import {SwapType} from "../../../../enums/SwapType";
|
|
5
|
+
import {
|
|
6
|
+
ChainSwapType,
|
|
7
|
+
ChainType, isAbstractSigner,
|
|
8
|
+
SignatureData,
|
|
9
|
+
SignatureVerificationError,
|
|
10
|
+
SwapCommitState,
|
|
11
|
+
SwapCommitStateType,
|
|
12
|
+
SwapData
|
|
13
|
+
} from "@atomiqlabs/base";
|
|
14
|
+
import {Buffer} from "buffer";
|
|
15
|
+
import {LNURL} from "../../../../lnurl/LNURL";
|
|
16
|
+
import {UserError} from "../../../../errors/UserError";
|
|
17
|
+
import {
|
|
18
|
+
IntermediaryAPI,
|
|
19
|
+
PaymentAuthorizationResponse,
|
|
20
|
+
PaymentAuthorizationResponseCodes
|
|
21
|
+
} from "../../../../intermediaries/apis/IntermediaryAPI";
|
|
22
|
+
import {IntermediaryError} from "../../../../errors/IntermediaryError";
|
|
23
|
+
import {extendAbortController} from "../../../../utils/Utils";
|
|
24
|
+
import {MinimalLightningNetworkWalletInterface} from "../../../../bitcoin/wallet/MinimalLightningNetworkWalletInterface";
|
|
25
|
+
import {IClaimableSwap} from "../../../IClaimableSwap";
|
|
26
|
+
import {IAddressSwap} from "../../../IAddressSwap";
|
|
27
|
+
import {IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../../IEscrowSelfInitSwap";
|
|
28
|
+
import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
|
|
29
|
+
import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
|
|
30
|
+
import {getLogger, LoggerType} from "../../../../utils/Logger";
|
|
31
|
+
import {timeoutPromise} from "../../../../utils/TimeoutUtils";
|
|
32
|
+
import {isLNURLWithdraw, LNURLWithdraw, LNURLWithdrawParamsWithUrl} from "../../../../types/lnurl/LNURLWithdraw";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* State enum for FromBTCLN swaps
|
|
36
|
+
* @category Swaps
|
|
37
|
+
*/
|
|
38
|
+
export enum FromBTCLNSwapState {
|
|
39
|
+
FAILED = -4,
|
|
40
|
+
QUOTE_EXPIRED = -3,
|
|
41
|
+
QUOTE_SOFT_EXPIRED = -2,
|
|
42
|
+
EXPIRED = -1,
|
|
43
|
+
PR_CREATED = 0,
|
|
44
|
+
PR_PAID = 1,
|
|
45
|
+
CLAIM_COMMITED = 2,
|
|
46
|
+
CLAIM_CLAIMED = 3
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type FromBTCLNSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
|
|
50
|
+
pr: string,
|
|
51
|
+
secret: string,
|
|
52
|
+
initialSwapData: T,
|
|
53
|
+
lnurl?: string,
|
|
54
|
+
lnurlK1?: string,
|
|
55
|
+
lnurlCallback?: string
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export function isFromBTCLNSwapInit<T extends SwapData>(obj: any): obj is FromBTCLNSwapInit<T> {
|
|
59
|
+
return typeof obj.pr==="string" &&
|
|
60
|
+
typeof obj.secret==="string" &&
|
|
61
|
+
(obj.lnurl==null || typeof(obj.lnurl)==="string") &&
|
|
62
|
+
(obj.lnurlK1==null || typeof(obj.lnurlK1)==="string") &&
|
|
63
|
+
(obj.lnurlCallback==null || typeof(obj.lnurlCallback)==="string") &&
|
|
64
|
+
isIEscrowSelfInitSwapInit(obj);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class FromBTCLNSwap<T extends ChainType = ChainType>
|
|
68
|
+
extends IFromBTCSelfInitSwap<T, FromBTCLNDefinition<T>, FromBTCLNSwapState>
|
|
69
|
+
implements IAddressSwap, IClaimableSwap<T, FromBTCLNDefinition<T>, FromBTCLNSwapState> {
|
|
70
|
+
|
|
71
|
+
protected readonly logger: LoggerType;
|
|
72
|
+
protected readonly inputToken: BtcToken<true> = BitcoinTokens.BTCLN;
|
|
73
|
+
protected readonly TYPE = SwapType.FROM_BTCLN;
|
|
74
|
+
|
|
75
|
+
protected readonly lnurlFailSignal: AbortController = new AbortController();
|
|
76
|
+
|
|
77
|
+
protected readonly pr: string;
|
|
78
|
+
protected readonly secret: string;
|
|
79
|
+
protected initialSwapData: T["Data"];
|
|
80
|
+
|
|
81
|
+
lnurl?: string;
|
|
82
|
+
lnurlK1?: string;
|
|
83
|
+
lnurlCallback?: string;
|
|
84
|
+
prPosted?: boolean = false;
|
|
85
|
+
|
|
86
|
+
protected getSwapData(): T["Data"] {
|
|
87
|
+
return this.data ?? this.initialSwapData;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
constructor(wrapper: FromBTCLNWrapper<T>, init: FromBTCLNSwapInit<T["Data"]>);
|
|
91
|
+
constructor(wrapper: FromBTCLNWrapper<T>, obj: any);
|
|
92
|
+
constructor(
|
|
93
|
+
wrapper: FromBTCLNWrapper<T>,
|
|
94
|
+
initOrObject: FromBTCLNSwapInit<T["Data"]> | any
|
|
95
|
+
) {
|
|
96
|
+
if(isFromBTCLNSwapInit(initOrObject) && initOrObject.url!=null) initOrObject.url += "/frombtcln";
|
|
97
|
+
super(wrapper, initOrObject);
|
|
98
|
+
if(isFromBTCLNSwapInit(initOrObject)) {
|
|
99
|
+
this.state = FromBTCLNSwapState.PR_CREATED;
|
|
100
|
+
this.pr = initOrObject.pr;
|
|
101
|
+
this.secret = initOrObject.secret;
|
|
102
|
+
this.initialSwapData = initOrObject.initialSwapData;
|
|
103
|
+
this.lnurl = initOrObject.lnurl;
|
|
104
|
+
this.lnurlK1 = initOrObject.lnurlK1;
|
|
105
|
+
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
106
|
+
} else {
|
|
107
|
+
this.pr = initOrObject.pr;
|
|
108
|
+
this.secret = initOrObject.secret;
|
|
109
|
+
|
|
110
|
+
if(initOrObject.initialSwapData==null) {
|
|
111
|
+
this.initialSwapData = this.data!;
|
|
112
|
+
} else {
|
|
113
|
+
this.initialSwapData = SwapData.deserialize<T["Data"]>(initOrObject.initialSwapData);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.lnurl = initOrObject.lnurl;
|
|
117
|
+
this.lnurlK1 = initOrObject.lnurlK1;
|
|
118
|
+
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
119
|
+
this.prPosted = initOrObject.prPosted;
|
|
120
|
+
|
|
121
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED && this.data!=null) {
|
|
122
|
+
this.initialSwapData = this.data;
|
|
123
|
+
delete this.data;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.tryRecomputeSwapPrice();
|
|
127
|
+
this.logger = getLogger("FromBTCLN("+this.getIdentifierHashString()+"): ");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
protected upgradeVersion() {
|
|
131
|
+
if (this.version == null) {
|
|
132
|
+
switch (this.state) {
|
|
133
|
+
case -2:
|
|
134
|
+
this.state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
135
|
+
break;
|
|
136
|
+
case -1:
|
|
137
|
+
this.state = FromBTCLNSwapState.FAILED;
|
|
138
|
+
break;
|
|
139
|
+
case 0:
|
|
140
|
+
this.state = FromBTCLNSwapState.PR_CREATED
|
|
141
|
+
break;
|
|
142
|
+
case 1:
|
|
143
|
+
this.state = FromBTCLNSwapState.PR_PAID
|
|
144
|
+
break;
|
|
145
|
+
case 2:
|
|
146
|
+
this.state = FromBTCLNSwapState.CLAIM_COMMITED
|
|
147
|
+
break;
|
|
148
|
+
case 3:
|
|
149
|
+
this.state = FromBTCLNSwapState.CLAIM_CLAIMED
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
this.version = 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
//////////////////////////////
|
|
157
|
+
//// Getters & utils
|
|
158
|
+
|
|
159
|
+
protected getIdentifierHash(): Buffer {
|
|
160
|
+
const paymentHashBuffer = this.getPaymentHash();
|
|
161
|
+
if(this.randomNonce==null) return paymentHashBuffer;
|
|
162
|
+
return Buffer.concat([paymentHashBuffer, Buffer.from(this.randomNonce, "hex")]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
protected getPaymentHash(): Buffer {
|
|
166
|
+
const decodedPR = bolt11Decode(this.pr);
|
|
167
|
+
if(decodedPR.tagsObject.payment_hash==null) throw new Error("Swap invoice doesn't contain payment hash field!");
|
|
168
|
+
return Buffer.from(decodedPR.tagsObject.payment_hash, "hex");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected canCommit(): boolean {
|
|
172
|
+
return this.state===FromBTCLNSwapState.PR_PAID;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getInputAddress(): string | null {
|
|
176
|
+
return this.lnurl ?? this.pr;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
getInputTxId(): string {
|
|
180
|
+
return this.getPaymentHash().toString("hex");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap
|
|
185
|
+
*/
|
|
186
|
+
getAddress(): string {
|
|
187
|
+
return this.pr;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getHyperlink(): string {
|
|
191
|
+
return "lightning:"+this.pr.toUpperCase();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns the timeout time (in UNIX milliseconds) when the swap will definitelly be considered as expired
|
|
196
|
+
* if the LP doesn't make it expired sooner
|
|
197
|
+
*/
|
|
198
|
+
getDefinitiveExpiryTime(): number {
|
|
199
|
+
const decoded = bolt11Decode(this.pr);
|
|
200
|
+
if(decoded.timeExpireDate==null) throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
201
|
+
const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
|
|
202
|
+
const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper.options.bitcoinBlocktime * this.wrapper.options.safetyFactor;
|
|
203
|
+
return (decoded.timeExpireDate + finalCltvExpiryDelay)*1000;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Returns timeout time (in UNIX milliseconds) when the LN invoice will expire
|
|
208
|
+
*/
|
|
209
|
+
getTimeoutTime(): number {
|
|
210
|
+
const decoded = bolt11Decode(this.pr);
|
|
211
|
+
if(decoded.timeExpireDate==null) throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
212
|
+
return (decoded.timeExpireDate*1000);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
|
|
217
|
+
*/
|
|
218
|
+
getHtlcTimeoutTime(): number {
|
|
219
|
+
if(this.data==null) return -1;
|
|
220
|
+
return Number(this.wrapper.getHtlcTimeout(this.data))*1000;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
isFinished(): boolean {
|
|
224
|
+
return this.state===FromBTCLNSwapState.CLAIM_CLAIMED || this.state===FromBTCLNSwapState.QUOTE_EXPIRED || this.state===FromBTCLNSwapState.FAILED;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
isClaimable(): boolean {
|
|
228
|
+
return this.state===FromBTCLNSwapState.CLAIM_COMMITED;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
isSuccessful(): boolean {
|
|
232
|
+
return this.state===FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
isFailed(): boolean {
|
|
236
|
+
return this.state===FromBTCLNSwapState.FAILED || this.state===FromBTCLNSwapState.EXPIRED;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
isQuoteExpired(): boolean {
|
|
240
|
+
return this.state===FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
isQuoteSoftExpired(): boolean {
|
|
244
|
+
return this.state===FromBTCLNSwapState.QUOTE_EXPIRED || this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_verifyQuoteDefinitelyExpired(): Promise<boolean> {
|
|
248
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData==null)) {
|
|
249
|
+
return Promise.resolve(this.getDefinitiveExpiryTime()<Date.now());
|
|
250
|
+
}
|
|
251
|
+
return super._verifyQuoteDefinitelyExpired();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
verifyQuoteValid(): Promise<boolean> {
|
|
255
|
+
if(
|
|
256
|
+
this.state===FromBTCLNSwapState.PR_CREATED ||
|
|
257
|
+
(this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData==null)
|
|
258
|
+
) {
|
|
259
|
+
return Promise.resolve(this.getTimeoutTime()>Date.now());
|
|
260
|
+
}
|
|
261
|
+
return super.verifyQuoteValid();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
//////////////////////////////
|
|
266
|
+
//// Amounts & fees
|
|
267
|
+
|
|
268
|
+
getInputToken(): BtcToken<true> {
|
|
269
|
+
return BitcoinTokens.BTCLN;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getInput(): TokenAmount<T["ChainId"], BtcToken<true>> {
|
|
273
|
+
const parsed = bolt11Decode(this.pr);
|
|
274
|
+
if(parsed.millisatoshis==null) throw new Error("Swap invoice doesn't contain msat amount field!");
|
|
275
|
+
const amount = (BigInt(parsed.millisatoshis) + 999n) / 1000n;
|
|
276
|
+
return toTokenAmount(amount, this.inputToken, this.wrapper.prices, this.pricingInfo);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async getSmartChainNetworkFee(): Promise<TokenAmount<T["ChainId"], SCToken<T["ChainId"]>>> {
|
|
280
|
+
return toTokenAmount(await this.getCommitAndClaimFee(), this.wrapper.getNativeToken(), this.wrapper.prices, this.pricingInfo);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async hasEnoughForTxFees(): Promise<{enoughBalance: boolean, balance: TokenAmount, required: TokenAmount}> {
|
|
284
|
+
const [balance, feeRate] = await Promise.all([
|
|
285
|
+
this.wrapper.contract.getBalance(this._getInitiator(), this.wrapper.chain.getNativeCurrencyAddress(), false),
|
|
286
|
+
this.feeRate!=null ? Promise.resolve<string>(this.feeRate) : this.wrapper.contract.getInitFeeRate(
|
|
287
|
+
this.getSwapData().getOfferer(),
|
|
288
|
+
this.getSwapData().getClaimer(),
|
|
289
|
+
this.getSwapData().getToken(),
|
|
290
|
+
this.getSwapData().getClaimHash()
|
|
291
|
+
)
|
|
292
|
+
]);
|
|
293
|
+
const commitFee = await this.wrapper.contract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
294
|
+
const claimFee = await this.wrapper.contract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
295
|
+
const totalFee = commitFee + claimFee + this.getSwapData().getTotalDeposit();
|
|
296
|
+
return {
|
|
297
|
+
enoughBalance: balance >= totalFee,
|
|
298
|
+
balance: toTokenAmount(balance, this.wrapper.getNativeToken(), this.wrapper.prices, this.pricingInfo),
|
|
299
|
+
required: toTokenAmount(totalFee, this.wrapper.getNativeToken(), this.wrapper.prices, this.pricingInfo)
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
//////////////////////////////
|
|
305
|
+
//// Execution
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Executes the swap with the provided bitcoin lightning network wallet or LNURL
|
|
309
|
+
*
|
|
310
|
+
* @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
|
|
311
|
+
* quote was created, this is required for legacy swaps because the destination wallet needs to actively claim
|
|
312
|
+
* the swap funds on the destination (this also means you need native token to cover gas costs)
|
|
313
|
+
* @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
|
|
314
|
+
* link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
|
|
315
|
+
* @param callbacks Callbacks to track the progress of the swap
|
|
316
|
+
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
317
|
+
*/
|
|
318
|
+
async execute(
|
|
319
|
+
dstSigner: T["Signer"] | T["NativeSigner"],
|
|
320
|
+
walletOrLnurlWithdraw?: MinimalLightningNetworkWalletInterface | LNURLWithdraw | string | null | undefined,
|
|
321
|
+
callbacks?: {
|
|
322
|
+
onSourceTransactionReceived?: (sourceTxId: string) => void,
|
|
323
|
+
onDestinationCommitSent?: (destinationCommitTxId: string) => void,
|
|
324
|
+
onDestinationClaimSent?: (destinationClaimTxId: string) => void,
|
|
325
|
+
onSwapSettled?: (destinationTxId: string) => void
|
|
326
|
+
},
|
|
327
|
+
options?: {
|
|
328
|
+
abortSignal?: AbortSignal,
|
|
329
|
+
lightningTxCheckIntervalSeconds?: number,
|
|
330
|
+
delayBetweenCommitAndClaimSeconds?: number
|
|
331
|
+
}
|
|
332
|
+
): Promise<void> {
|
|
333
|
+
if(this.state===FromBTCLNSwapState.FAILED) throw new Error("Swap failed!");
|
|
334
|
+
if(this.state===FromBTCLNSwapState.EXPIRED) throw new Error("Swap HTLC expired!");
|
|
335
|
+
if(this.state===FromBTCLNSwapState.QUOTE_EXPIRED || this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Swap quote expired!");
|
|
336
|
+
if(this.state===FromBTCLNSwapState.CLAIM_CLAIMED) throw new Error("Swap already settled!");
|
|
337
|
+
|
|
338
|
+
let abortSignal = options?.abortSignal;
|
|
339
|
+
|
|
340
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED) {
|
|
341
|
+
if(walletOrLnurlWithdraw!=null && this.lnurl==null) {
|
|
342
|
+
if(typeof(walletOrLnurlWithdraw)==="string" || isLNURLWithdraw(walletOrLnurlWithdraw)) {
|
|
343
|
+
await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
|
|
344
|
+
} else {
|
|
345
|
+
const paymentPromise = walletOrLnurlWithdraw.payInvoice(this.pr);
|
|
346
|
+
|
|
347
|
+
const abortController = new AbortController();
|
|
348
|
+
paymentPromise.catch(e => abortController.abort(e));
|
|
349
|
+
if(options?.abortSignal!=null) options.abortSignal.addEventListener("abort", () => abortController.abort(options?.abortSignal?.reason));
|
|
350
|
+
abortSignal = abortController.signal;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
|
|
354
|
+
if (!paymentSuccess) throw new Error("Failed to receive lightning network payment");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if(this.state===FromBTCLNSwapState.PR_PAID || this.state===FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
358
|
+
if(this.canCommitAndClaimInOneShot()) {
|
|
359
|
+
await this.commitAndClaim(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent, callbacks?.onDestinationClaimSent);
|
|
360
|
+
} else {
|
|
361
|
+
if(this.state===FromBTCLNSwapState.PR_PAID) {
|
|
362
|
+
await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
|
|
363
|
+
if(options?.delayBetweenCommitAndClaimSeconds!=null) await timeoutPromise(options.delayBetweenCommitAndClaimSeconds * 1000, options?.abortSignal);
|
|
364
|
+
}
|
|
365
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
366
|
+
await this.claim(dstSigner, options?.abortSignal, callbacks?.onDestinationClaimSent);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// @ts-ignore
|
|
372
|
+
if(this.state===FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
373
|
+
if(callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async txsExecute(options?: {
|
|
378
|
+
skipChecks?: boolean
|
|
379
|
+
}) {
|
|
380
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED) {
|
|
381
|
+
if(!await this.verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
|
|
382
|
+
return [
|
|
383
|
+
{
|
|
384
|
+
name: "Payment" as const,
|
|
385
|
+
description: "Initiates the swap by paying up the lightning network invoice",
|
|
386
|
+
chain: "LIGHTNING",
|
|
387
|
+
txs: [
|
|
388
|
+
{
|
|
389
|
+
address: this.pr,
|
|
390
|
+
hyperlink: this.getHyperlink()
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if(this.state===FromBTCLNSwapState.PR_PAID) {
|
|
398
|
+
if(!await this.verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
|
|
399
|
+
const txsCommit = await this.txsCommit(options?.skipChecks);
|
|
400
|
+
const txsClaim = await this._txsClaim(undefined);
|
|
401
|
+
return [
|
|
402
|
+
{
|
|
403
|
+
name: "Commit" as const,
|
|
404
|
+
description: `Creates the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
405
|
+
chain: this.chainIdentifier,
|
|
406
|
+
txs: txsCommit
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "Claim" as const,
|
|
410
|
+
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
411
|
+
chain: this.chainIdentifier,
|
|
412
|
+
txs: txsClaim
|
|
413
|
+
},
|
|
414
|
+
];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
418
|
+
const txsClaim = await this.txsClaim();
|
|
419
|
+
return [
|
|
420
|
+
{
|
|
421
|
+
name: "Claim" as const,
|
|
422
|
+
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
423
|
+
chain: this.chainIdentifier,
|
|
424
|
+
txs: txsClaim
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED, PR_PAID or CLAIM_COMMITED");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
//////////////////////////////
|
|
434
|
+
//// Payment
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Checks whether the LP received the LN payment and we can continue by committing & claiming the HTLC on-chain
|
|
438
|
+
*
|
|
439
|
+
* @param save If the new swap state should be saved
|
|
440
|
+
*/
|
|
441
|
+
async _checkIntermediaryPaymentReceived(save: boolean = true): Promise<boolean | null> {
|
|
442
|
+
if(
|
|
443
|
+
this.state===FromBTCLNSwapState.PR_PAID ||
|
|
444
|
+
this.state===FromBTCLNSwapState.CLAIM_COMMITED ||
|
|
445
|
+
this.state===FromBTCLNSwapState.CLAIM_CLAIMED ||
|
|
446
|
+
this.state===FromBTCLNSwapState.FAILED
|
|
447
|
+
) return true;
|
|
448
|
+
if(this.state===FromBTCLNSwapState.QUOTE_EXPIRED || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null)) return false;
|
|
449
|
+
if(this.url==null) return false;
|
|
450
|
+
const resp = await IntermediaryAPI.getPaymentAuthorization(this.url, this.getPaymentHash().toString("hex"));
|
|
451
|
+
switch(resp.code) {
|
|
452
|
+
case PaymentAuthorizationResponseCodes.AUTH_DATA:
|
|
453
|
+
const data = new this.wrapper.swapDataDeserializer(resp.data.data);
|
|
454
|
+
try {
|
|
455
|
+
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), data, resp.data);
|
|
456
|
+
this.expiry = await this.wrapper.contract.getInitAuthorizationExpiry(
|
|
457
|
+
data,
|
|
458
|
+
resp.data
|
|
459
|
+
);
|
|
460
|
+
this.state = FromBTCLNSwapState.PR_PAID;
|
|
461
|
+
this.data = data;
|
|
462
|
+
this.signatureData = {
|
|
463
|
+
prefix: resp.data.prefix,
|
|
464
|
+
timeout: resp.data.timeout,
|
|
465
|
+
signature: resp.data.signature
|
|
466
|
+
};
|
|
467
|
+
this.initiated = true;
|
|
468
|
+
if(save) await this._saveAndEmit();
|
|
469
|
+
return true;
|
|
470
|
+
} catch (e) {}
|
|
471
|
+
return null;
|
|
472
|
+
case PaymentAuthorizationResponseCodes.EXPIRED:
|
|
473
|
+
this.state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
474
|
+
this.initiated = true;
|
|
475
|
+
if(save) await this._saveAndEmit();
|
|
476
|
+
return false;
|
|
477
|
+
default:
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Checks the data returned by the intermediary in the payment auth request
|
|
484
|
+
*
|
|
485
|
+
* @param signer Smart chain signer's address initiating the swap
|
|
486
|
+
* @param data Parsed swap data as returned by the intermediary
|
|
487
|
+
* @param signature Signature data as returned by the intermediary
|
|
488
|
+
* @protected
|
|
489
|
+
* @throws {IntermediaryError} If the returned are not valid
|
|
490
|
+
* @throws {SignatureVerificationError} If the returned signature is not valid
|
|
491
|
+
* @throws {Error} If the swap is already committed on-chain
|
|
492
|
+
*/
|
|
493
|
+
protected async checkIntermediaryReturnedAuthData(signer: string, data: T["Data"], signature: SignatureData): Promise<void> {
|
|
494
|
+
data.setClaimer(signer);
|
|
495
|
+
|
|
496
|
+
if (data.getType() !== ChainSwapType.HTLC) throw new IntermediaryError("Invalid swap type");
|
|
497
|
+
if (!data.isOfferer(this.getSwapData().getOfferer())) throw new IntermediaryError("Invalid offerer used");
|
|
498
|
+
if (!data.isClaimer(this._getInitiator())) throw new IntermediaryError("Invalid claimer used");
|
|
499
|
+
if (!data.isToken(this.getSwapData().getToken())) throw new IntermediaryError("Invalid token used");
|
|
500
|
+
if (data.getSecurityDeposit() > this.getSwapData().getSecurityDeposit()) throw new IntermediaryError("Invalid security deposit!");
|
|
501
|
+
if (data.getClaimerBounty() !== 0n) throw new IntermediaryError("Invalid claimer bounty!");
|
|
502
|
+
if (data.getAmount() < this.getSwapData().getAmount()) throw new IntermediaryError("Invalid amount received!");
|
|
503
|
+
if (data.getClaimHash() !== this.getSwapData().getClaimHash()) throw new IntermediaryError("Invalid payment hash used!");
|
|
504
|
+
if (!data.isDepositToken(this.getSwapData().getDepositToken())) throw new IntermediaryError("Invalid deposit token used!");
|
|
505
|
+
if (data.hasSuccessAction()) throw new IntermediaryError("Invalid has success action");
|
|
506
|
+
|
|
507
|
+
await Promise.all([
|
|
508
|
+
this.wrapper.contract.isValidInitAuthorization(this._getInitiator(), data, signature, this.feeRate),
|
|
509
|
+
this.wrapper.contract.getCommitStatus(data.getClaimer(), data)
|
|
510
|
+
.then(status => {
|
|
511
|
+
if (status?.type !== SwapCommitStateType.NOT_COMMITED)
|
|
512
|
+
throw new Error("Swap already committed on-chain!");
|
|
513
|
+
})
|
|
514
|
+
]);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Waits till an LN payment is received by the intermediary and client can continue commiting & claiming the HTLC
|
|
519
|
+
*
|
|
520
|
+
* @param onPaymentReceived Callback as for when the LP reports having received the ln payment
|
|
521
|
+
* @param abortSignal Abort signal to stop waiting for payment
|
|
522
|
+
* @param checkIntervalSeconds How often to poll the intermediary for answer
|
|
523
|
+
*/
|
|
524
|
+
async waitForPayment(onPaymentReceived?: (txId: string) => void, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
525
|
+
checkIntervalSeconds ??= 5;
|
|
526
|
+
if(
|
|
527
|
+
this.state!==FromBTCLNSwapState.PR_CREATED &&
|
|
528
|
+
(this.state!==FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData!=null)
|
|
529
|
+
) throw new Error("Must be in PR_CREATED state!");
|
|
530
|
+
if(this.url==null) throw new Error("LP URL not known, cannot await the payment!");
|
|
531
|
+
|
|
532
|
+
const abortController = new AbortController();
|
|
533
|
+
if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
534
|
+
|
|
535
|
+
let save = false;
|
|
536
|
+
|
|
537
|
+
if(this.lnurl!=null && this.lnurlK1!=null && this.lnurlCallback!=null && !this.prPosted) {
|
|
538
|
+
LNURL.postInvoiceToLNURLWithdraw({k1: this.lnurlK1, callback: this.lnurlCallback}, this.pr).catch(e => {
|
|
539
|
+
this.lnurlFailSignal.abort(e);
|
|
540
|
+
});
|
|
541
|
+
this.prPosted = true;
|
|
542
|
+
save ||= true;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if(!this.initiated) {
|
|
546
|
+
this.initiated = true;
|
|
547
|
+
save ||= true;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if(save) await this._saveAndEmit();
|
|
551
|
+
|
|
552
|
+
let lnurlFailListener = () => abortController.abort(this.lnurlFailSignal.signal.reason);
|
|
553
|
+
this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
|
|
554
|
+
this.lnurlFailSignal.signal.throwIfAborted();
|
|
555
|
+
|
|
556
|
+
let resp: PaymentAuthorizationResponse = {code: PaymentAuthorizationResponseCodes.PENDING, msg: ""};
|
|
557
|
+
while(!abortController.signal.aborted && resp.code===PaymentAuthorizationResponseCodes.PENDING) {
|
|
558
|
+
resp = await IntermediaryAPI.getPaymentAuthorization(this.url, this.getPaymentHash().toString("hex"));
|
|
559
|
+
if(resp.code===PaymentAuthorizationResponseCodes.PENDING)
|
|
560
|
+
await timeoutPromise(checkIntervalSeconds*1000, abortController.signal);
|
|
561
|
+
}
|
|
562
|
+
this.lnurlFailSignal.signal.removeEventListener("abort", lnurlFailListener);
|
|
563
|
+
abortController.signal.throwIfAborted();
|
|
564
|
+
|
|
565
|
+
if(resp.code===PaymentAuthorizationResponseCodes.AUTH_DATA) {
|
|
566
|
+
const sigData = resp.data;
|
|
567
|
+
const swapData = new this.wrapper.swapDataDeserializer(resp.data.data);
|
|
568
|
+
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), swapData, sigData);
|
|
569
|
+
this.expiry = await this.wrapper.contract.getInitAuthorizationExpiry(
|
|
570
|
+
swapData,
|
|
571
|
+
sigData
|
|
572
|
+
);
|
|
573
|
+
if(onPaymentReceived!=null) onPaymentReceived(this.getInputTxId());
|
|
574
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED || this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
575
|
+
this.data = swapData;
|
|
576
|
+
this.signatureData = {
|
|
577
|
+
prefix: sigData.prefix,
|
|
578
|
+
timeout: sigData.timeout,
|
|
579
|
+
signature: sigData.signature
|
|
580
|
+
};
|
|
581
|
+
await this._saveAndEmit(FromBTCLNSwapState.PR_PAID);
|
|
582
|
+
}
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED || this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
587
|
+
if(resp.code===PaymentAuthorizationResponseCodes.EXPIRED) {
|
|
588
|
+
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
throw new IntermediaryError("Invalid response from the LP");
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
//////////////////////////////
|
|
599
|
+
//// Commit
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Commits the swap on-chain, locking the tokens from the intermediary in an HTLC
|
|
603
|
+
*
|
|
604
|
+
* @param _signer Signer to sign the transactions with, must be the same as used in the initialization
|
|
605
|
+
* @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
|
|
606
|
+
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
607
|
+
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
608
|
+
* @param onBeforeTxSent
|
|
609
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
610
|
+
*/
|
|
611
|
+
async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
612
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
|
|
613
|
+
this.checkSigner(signer);
|
|
614
|
+
let txCount = 0;
|
|
615
|
+
const txs = await this.txsCommit(skipChecks);
|
|
616
|
+
const result = await this.wrapper.chain.sendAndConfirm(
|
|
617
|
+
signer, txs, true, abortSignal, undefined, (txId: string) => {
|
|
618
|
+
txCount++;
|
|
619
|
+
if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
|
|
620
|
+
return Promise.resolve();
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
this.commitTxId = result[result.length-1];
|
|
625
|
+
if(this.state===FromBTCLNSwapState.PR_PAID || this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
626
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
627
|
+
}
|
|
628
|
+
return this.commitTxId;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
|
|
632
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED || this.state===FromBTCLNSwapState.CLAIM_CLAIMED) return Promise.resolve();
|
|
633
|
+
if(this.state!==FromBTCLNSwapState.PR_PAID && (this.state!==FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null)) throw new Error("Invalid state");
|
|
634
|
+
|
|
635
|
+
const abortController = extendAbortController(abortSignal);
|
|
636
|
+
const result = await Promise.race([
|
|
637
|
+
this.watchdogWaitTillCommited(undefined, abortController.signal),
|
|
638
|
+
this.waitTillState(FromBTCLNSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
|
|
639
|
+
]);
|
|
640
|
+
abortController.abort();
|
|
641
|
+
|
|
642
|
+
if(result===0) this.logger.debug("waitTillCommited(): Resolved from state changed");
|
|
643
|
+
if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
644
|
+
if(result===false) {
|
|
645
|
+
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
|
|
646
|
+
if(
|
|
647
|
+
this.state===FromBTCLNSwapState.PR_PAID ||
|
|
648
|
+
this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED
|
|
649
|
+
) {
|
|
650
|
+
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if(
|
|
656
|
+
this.state===FromBTCLNSwapState.PR_PAID ||
|
|
657
|
+
this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED
|
|
658
|
+
) {
|
|
659
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
//////////////////////////////
|
|
665
|
+
//// Claim
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Unsafe txs claim getter without state checking!
|
|
669
|
+
*
|
|
670
|
+
* @param _signer
|
|
671
|
+
* @private
|
|
672
|
+
*/
|
|
673
|
+
private async _txsClaim(_signer?: T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
674
|
+
if(this.data==null) throw new Error("Unknown data, wrong state?");
|
|
675
|
+
return this.wrapper.contract.txsClaimWithSecret(
|
|
676
|
+
_signer==null ?
|
|
677
|
+
this._getInitiator() :
|
|
678
|
+
(isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer)),
|
|
679
|
+
this.data, this.secret, true, true
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Returns transactions required for claiming the HTLC and finishing the swap by revealing the HTLC secret
|
|
685
|
+
* (hash preimage)
|
|
686
|
+
*
|
|
687
|
+
* @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
|
|
688
|
+
* @throws {Error} If in invalid state (must be CLAIM_COMMITED)
|
|
689
|
+
*/
|
|
690
|
+
async txsClaim(_signer?: T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
691
|
+
if(this.state!==FromBTCLNSwapState.CLAIM_COMMITED) throw new Error("Must be in CLAIM_COMMITED state!");
|
|
692
|
+
return this._txsClaim(_signer);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Claims and finishes the swap
|
|
697
|
+
*
|
|
698
|
+
* @param _signer Signer to sign the transactions with, can also be different to the initializer
|
|
699
|
+
* @param abortSignal Abort signal to stop waiting for transaction confirmation
|
|
700
|
+
* @param onBeforeTxSent
|
|
701
|
+
*/
|
|
702
|
+
async claim(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
703
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
|
|
704
|
+
let txCount = 0;
|
|
705
|
+
const result = await this.wrapper.chain.sendAndConfirm(
|
|
706
|
+
signer, await this.txsClaim(), true, abortSignal, undefined, (txId: string) => {
|
|
707
|
+
txCount++;
|
|
708
|
+
if(onBeforeTxSent!=null && txCount===1) onBeforeTxSent(txId);
|
|
709
|
+
return Promise.resolve();
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
this.claimTxId = result[0];
|
|
714
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED || this.state===FromBTCLNSwapState.EXPIRED || this.state===FromBTCLNSwapState.FAILED) {
|
|
715
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
716
|
+
}
|
|
717
|
+
return result[0];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Waits till the swap is successfully claimed
|
|
722
|
+
*
|
|
723
|
+
* @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled
|
|
724
|
+
* @param abortSignal AbortSignal
|
|
725
|
+
* @throws {Error} If swap is in invalid state (must be BTC_TX_CONFIRMED)
|
|
726
|
+
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
727
|
+
* @returns {boolean} whether the swap was claimed in time or not
|
|
728
|
+
*/
|
|
729
|
+
async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
730
|
+
if(this.state===FromBTCLNSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
|
|
731
|
+
if(this.state!==FromBTCLNSwapState.CLAIM_COMMITED) throw new Error("Invalid state (not CLAIM_COMMITED)");
|
|
732
|
+
|
|
733
|
+
const abortController = new AbortController();
|
|
734
|
+
if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
735
|
+
let timedOut: boolean = false;
|
|
736
|
+
if(maxWaitTimeSeconds!=null) {
|
|
737
|
+
const timeout = setTimeout(() => {
|
|
738
|
+
timedOut = true;
|
|
739
|
+
abortController.abort();
|
|
740
|
+
}, maxWaitTimeSeconds * 1000);
|
|
741
|
+
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
let res: 0 | 1 | SwapCommitState;
|
|
745
|
+
try {
|
|
746
|
+
res = await Promise.race([
|
|
747
|
+
this.watchdogWaitTillResult(undefined, abortController.signal),
|
|
748
|
+
this.waitTillState(FromBTCLNSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0 as const),
|
|
749
|
+
this.waitTillState(FromBTCLNSwapState.EXPIRED, "eq", abortController.signal).then(() => 1 as const),
|
|
750
|
+
]);
|
|
751
|
+
abortController.abort();
|
|
752
|
+
} catch (e) {
|
|
753
|
+
abortController.abort();
|
|
754
|
+
if(timedOut) return false;
|
|
755
|
+
throw e;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if(res===0) {
|
|
759
|
+
this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
if(res===1) {
|
|
763
|
+
this.logger.debug("waitTillClaimed(): Resolved from state change (EXPIRED)");
|
|
764
|
+
throw new Error("Swap expired during claiming");
|
|
765
|
+
}
|
|
766
|
+
this.logger.debug("waitTillClaimed(): Resolved from watchdog");
|
|
767
|
+
|
|
768
|
+
if(res?.type===SwapCommitStateType.PAID) {
|
|
769
|
+
if((this.state as FromBTCLNSwapState)!==FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
770
|
+
this.claimTxId = await res.getClaimTxId();
|
|
771
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if(res?.type===SwapCommitStateType.NOT_COMMITED || res?.type===SwapCommitStateType.EXPIRED) {
|
|
775
|
+
if(
|
|
776
|
+
(this.state as FromBTCLNSwapState)!==FromBTCLNSwapState.CLAIM_CLAIMED &&
|
|
777
|
+
(this.state as FromBTCLNSwapState)!==FromBTCLNSwapState.FAILED
|
|
778
|
+
) {
|
|
779
|
+
if(res.getRefundTxId!=null) this.refundTxId = await res.getRefundTxId();
|
|
780
|
+
await this._saveAndEmit(FromBTCLNSwapState.FAILED);
|
|
781
|
+
}
|
|
782
|
+
throw new Error("Swap expired while waiting for claim!");
|
|
783
|
+
}
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
//////////////////////////////
|
|
789
|
+
//// Commit & claim
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Estimated transaction fee for commit & claim txs combined
|
|
793
|
+
*/
|
|
794
|
+
async getCommitAndClaimFee(): Promise<bigint> {
|
|
795
|
+
const swapContract: T["Contract"] = this.wrapper.contract;
|
|
796
|
+
const feeRate = this.feeRate ?? await swapContract.getInitFeeRate(
|
|
797
|
+
this.getSwapData().getOfferer(),
|
|
798
|
+
this.getSwapData().getClaimer(),
|
|
799
|
+
this.getSwapData().getToken(),
|
|
800
|
+
this.getSwapData().getClaimHash()
|
|
801
|
+
);
|
|
802
|
+
const commitFee = await (
|
|
803
|
+
swapContract.getRawCommitFee!=null ?
|
|
804
|
+
swapContract.getRawCommitFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
805
|
+
swapContract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate)
|
|
806
|
+
);
|
|
807
|
+
const claimFee = await (
|
|
808
|
+
swapContract.getRawClaimFee!=null ?
|
|
809
|
+
swapContract.getRawClaimFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
810
|
+
swapContract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate)
|
|
811
|
+
);
|
|
812
|
+
return commitFee + claimFee;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
canCommitAndClaimInOneShot(): boolean {
|
|
816
|
+
return this.wrapper.contract.initAndClaimWithSecret!=null;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Returns transactions for both commit & claim operation together, such that they can be signed all at once by
|
|
821
|
+
* the wallet. CAUTION: transactions must be sent sequentially, such that the claim (2nd) transaction is only
|
|
822
|
+
* sent after the commit (1st) transaction confirms. Failure to do so can reveal the HTLC pre-image too soon,
|
|
823
|
+
* opening a possibility for the LP to steal funds.
|
|
824
|
+
*
|
|
825
|
+
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
826
|
+
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
827
|
+
*
|
|
828
|
+
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
829
|
+
*/
|
|
830
|
+
async txsCommitAndClaim(skipChecks?: boolean): Promise<T["TX"][]> {
|
|
831
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED) return await this.txsClaim();
|
|
832
|
+
if(
|
|
833
|
+
this.state!==FromBTCLNSwapState.PR_PAID &&
|
|
834
|
+
(this.state!==FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData==null)
|
|
835
|
+
) throw new Error("Must be in PR_PAID state!");
|
|
836
|
+
if(this.data==null) throw new Error("Unknown data, wrong state?");
|
|
837
|
+
|
|
838
|
+
const initTxs = await this.txsCommit(skipChecks);
|
|
839
|
+
const claimTxs = await this.wrapper.contract.txsClaimWithSecret(
|
|
840
|
+
this._getInitiator(), this.data, this.secret,
|
|
841
|
+
true, true, undefined,
|
|
842
|
+
true
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
return initTxs.concat(claimTxs);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Commits and claims the swap, in a way that the transactions can be signed together by the underlying provider and
|
|
850
|
+
* then sent sequentially
|
|
851
|
+
*
|
|
852
|
+
* @param _signer Signer to sign the transactions with, must be the same as used in the initialization
|
|
853
|
+
* @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
|
|
854
|
+
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
855
|
+
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
856
|
+
* @param onBeforeCommitTxSent
|
|
857
|
+
* @param onBeforeClaimTxSent
|
|
858
|
+
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
859
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
860
|
+
*/
|
|
861
|
+
async commitAndClaim(
|
|
862
|
+
_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean,
|
|
863
|
+
onBeforeCommitTxSent?: (txId: string) => void, onBeforeClaimTxSent?: (txId: string) => void
|
|
864
|
+
): Promise<string[]> {
|
|
865
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper.chain.wrapSigner(_signer);
|
|
866
|
+
if(!this.canCommitAndClaimInOneShot()) throw new Error("Cannot commitAndClaim in single action, please run commit and claim separately!");
|
|
867
|
+
this.checkSigner(signer);
|
|
868
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED) return [await this.claim(signer, abortSignal, onBeforeClaimTxSent)];
|
|
869
|
+
|
|
870
|
+
let txCount = 0;
|
|
871
|
+
const txs = await this.txsCommitAndClaim(skipChecks);
|
|
872
|
+
const result = await this.wrapper.chain.sendAndConfirm(
|
|
873
|
+
signer, txs, true, abortSignal, undefined, (txId: string) => {
|
|
874
|
+
txCount++;
|
|
875
|
+
if(onBeforeCommitTxSent!=null && txCount===1) onBeforeCommitTxSent(txId);
|
|
876
|
+
if(onBeforeClaimTxSent!=null && txCount===txs.length) onBeforeClaimTxSent(txId);
|
|
877
|
+
return Promise.resolve();
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
|
|
881
|
+
this.commitTxId = result[0] ?? this.commitTxId;
|
|
882
|
+
this.claimTxId = result[result.length-1] ?? this.claimTxId;
|
|
883
|
+
if(this.state!==FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
884
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
//////////////////////////////
|
|
892
|
+
//// LNURL
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Is this an LNURL-withdraw swap?
|
|
896
|
+
*/
|
|
897
|
+
isLNURL(): boolean {
|
|
898
|
+
return this.lnurl!=null;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Gets the used LNURL or null if this is not an LNURL-withdraw swap
|
|
903
|
+
*/
|
|
904
|
+
getLNURL(): string | null {
|
|
905
|
+
return this.lnurl ?? null;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Pay the generated lightning network invoice with LNURL-withdraw
|
|
910
|
+
*/
|
|
911
|
+
async settleWithLNURLWithdraw(lnurl: string | LNURLWithdraw): Promise<void> {
|
|
912
|
+
if(this.lnurl!=null) throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
|
|
913
|
+
let lnurlParams: LNURLWithdrawParamsWithUrl;
|
|
914
|
+
if(typeof(lnurl)==="string") {
|
|
915
|
+
const parsedLNURL = await LNURL.getLNURL(lnurl);
|
|
916
|
+
if(parsedLNURL==null || parsedLNURL.tag!=="withdrawRequest")
|
|
917
|
+
throw new UserError("Invalid LNURL-withdraw to settle the swap");
|
|
918
|
+
lnurlParams = parsedLNURL;
|
|
919
|
+
} else {
|
|
920
|
+
lnurlParams = lnurl.params;
|
|
921
|
+
}
|
|
922
|
+
LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
|
|
923
|
+
this.lnurl = lnurlParams.url;
|
|
924
|
+
this.lnurlCallback = lnurlParams.callback;
|
|
925
|
+
this.lnurlK1 = lnurlParams.k1;
|
|
926
|
+
this.prPosted = true;
|
|
927
|
+
await this._saveAndEmit();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
//////////////////////////////
|
|
932
|
+
//// Storage
|
|
933
|
+
|
|
934
|
+
serialize(): any {
|
|
935
|
+
return {
|
|
936
|
+
...super.serialize(),
|
|
937
|
+
pr: this.pr,
|
|
938
|
+
secret: this.secret,
|
|
939
|
+
lnurl: this.lnurl,
|
|
940
|
+
lnurlK1: this.lnurlK1,
|
|
941
|
+
lnurlCallback: this.lnurlCallback,
|
|
942
|
+
prPosted: this.prPosted,
|
|
943
|
+
initialSwapData: this.initialSwapData.serialize()
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
//////////////////////////////
|
|
949
|
+
//// Swap ticks & sync
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
953
|
+
* data
|
|
954
|
+
*
|
|
955
|
+
* @private
|
|
956
|
+
*/
|
|
957
|
+
private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
958
|
+
//Check for expiry before the getCommitStatus to prevent race conditions
|
|
959
|
+
let quoteExpired: boolean = false;
|
|
960
|
+
if(this.state===FromBTCLNSwapState.PR_PAID || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null)) {
|
|
961
|
+
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if(this.state===FromBTCLNSwapState.CLAIM_COMMITED || this.state===FromBTCLNSwapState.EXPIRED) {
|
|
965
|
+
//Check if it's already successfully paid
|
|
966
|
+
commitStatus ??= await this.wrapper.contract.getCommitStatus(this._getInitiator(), this.data!);
|
|
967
|
+
if(commitStatus?.type===SwapCommitStateType.PAID) {
|
|
968
|
+
if(this.claimTxId==null) this.claimTxId = await commitStatus.getClaimTxId();
|
|
969
|
+
this.state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if(commitStatus?.type===SwapCommitStateType.NOT_COMMITED || commitStatus?.type===SwapCommitStateType.EXPIRED) {
|
|
974
|
+
if(this.refundTxId==null && commitStatus.getRefundTxId) this.refundTxId = await commitStatus.getRefundTxId();
|
|
975
|
+
this.state = FromBTCLNSwapState.FAILED;
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if(this.state===FromBTCLNSwapState.PR_PAID || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null)) {
|
|
981
|
+
//Check if it's already committed
|
|
982
|
+
commitStatus ??= await this.wrapper.contract.getCommitStatus(this._getInitiator(), this.data!);
|
|
983
|
+
switch(commitStatus?.type) {
|
|
984
|
+
case SwapCommitStateType.COMMITED:
|
|
985
|
+
this.state = FromBTCLNSwapState.CLAIM_COMMITED;
|
|
986
|
+
return true;
|
|
987
|
+
case SwapCommitStateType.EXPIRED:
|
|
988
|
+
if(this.refundTxId==null && commitStatus.getRefundTxId) this.refundTxId = await commitStatus.getRefundTxId();
|
|
989
|
+
this.state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
990
|
+
return true;
|
|
991
|
+
case SwapCommitStateType.PAID:
|
|
992
|
+
if(this.claimTxId==null && commitStatus.getClaimTxId) this.claimTxId = await commitStatus.getClaimTxId();
|
|
993
|
+
this.state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
//Set the state on expiry here
|
|
999
|
+
if(this.state===FromBTCLNSwapState.PR_PAID || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null)) {
|
|
1000
|
+
if(quoteExpired) {
|
|
1001
|
+
this.state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1002
|
+
return true;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
_shouldFetchExpiryStatus(): boolean {
|
|
1010
|
+
return this.state===FromBTCLNSwapState.PR_PAID || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
_shouldFetchCommitStatus(): boolean {
|
|
1014
|
+
return this.state===FromBTCLNSwapState.PR_PAID || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData!=null) ||
|
|
1015
|
+
this.state===FromBTCLNSwapState.CLAIM_COMMITED || this.state===FromBTCLNSwapState.EXPIRED;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
_shouldCheckIntermediary(): boolean {
|
|
1019
|
+
return this.state===FromBTCLNSwapState.PR_CREATED || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData==null);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState, skipLpCheck?: boolean): Promise<boolean> {
|
|
1023
|
+
let changed = false;
|
|
1024
|
+
|
|
1025
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData==null)) {
|
|
1026
|
+
if(this.state!=FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.getTimeoutTime()<Date.now()) {
|
|
1027
|
+
this.state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1028
|
+
changed ||= true;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if(!skipLpCheck) try {
|
|
1032
|
+
const result = await this._checkIntermediaryPaymentReceived(false);
|
|
1033
|
+
if(result!==null) changed ||= true;
|
|
1034
|
+
} catch (e) {
|
|
1035
|
+
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if(this.state===FromBTCLNSwapState.PR_CREATED || (this.state===FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData==null)) {
|
|
1039
|
+
if(await this._verifyQuoteDefinitelyExpired()) {
|
|
1040
|
+
this.state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1041
|
+
changed ||= true;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if(await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus)) changed = true;
|
|
1047
|
+
|
|
1048
|
+
if(save && changed) await this._saveAndEmit();
|
|
1049
|
+
|
|
1050
|
+
return changed;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async _tick(save?: boolean): Promise<boolean> {
|
|
1054
|
+
switch(this.state) {
|
|
1055
|
+
case FromBTCLNSwapState.PR_CREATED:
|
|
1056
|
+
if(this.getTimeoutTime()<Date.now()) {
|
|
1057
|
+
this.state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1058
|
+
if(save) await this._saveAndEmit();
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
break;
|
|
1062
|
+
case FromBTCLNSwapState.PR_PAID:
|
|
1063
|
+
if(this.expiry<Date.now()) {
|
|
1064
|
+
this.state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1065
|
+
if(save) await this._saveAndEmit();
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
1068
|
+
break;
|
|
1069
|
+
case FromBTCLNSwapState.CLAIM_COMMITED:
|
|
1070
|
+
const expired = await this.wrapper.contract.isExpired(this._getInitiator(), this.data!);
|
|
1071
|
+
if(expired) {
|
|
1072
|
+
this.state = FromBTCLNSwapState.EXPIRED;
|
|
1073
|
+
if(save) await this._saveAndEmit();
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
}
|