@atomiqlabs/sdk 8.7.7 → 8.8.4
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/LICENSE +201 -201
- package/README.md +1760 -1760
- package/dist/SmartChainAssets.d.ts +181 -181
- package/dist/SmartChainAssets.js +181 -181
- package/dist/bitcoin/coinselect2/accumulative.d.ts +7 -6
- package/dist/bitcoin/coinselect2/accumulative.js +52 -52
- package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -6
- package/dist/bitcoin/coinselect2/blackjack.js +38 -38
- package/dist/bitcoin/coinselect2/index.d.ts +20 -19
- package/dist/bitcoin/coinselect2/index.js +69 -69
- package/dist/bitcoin/coinselect2/utils.d.ts +82 -77
- package/dist/bitcoin/coinselect2/utils.js +158 -123
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -130
- package/dist/bitcoin/wallet/BitcoinWallet.js +335 -322
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -78
- package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -101
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -190
- package/dist/enums/FeeType.d.ts +15 -15
- package/dist/enums/FeeType.js +19 -19
- package/dist/enums/SwapAmountType.d.ts +15 -15
- package/dist/enums/SwapAmountType.js +19 -19
- package/dist/enums/SwapDirection.d.ts +15 -15
- package/dist/enums/SwapDirection.js +19 -19
- package/dist/enums/SwapSide.d.ts +15 -15
- package/dist/enums/SwapSide.js +19 -19
- package/dist/enums/SwapType.d.ts +75 -75
- package/dist/enums/SwapType.js +79 -79
- package/dist/errors/IntermediaryError.d.ts +13 -13
- package/dist/errors/IntermediaryError.js +27 -27
- package/dist/errors/RequestError.d.ts +32 -32
- package/dist/errors/RequestError.js +54 -54
- package/dist/errors/UserError.d.ts +8 -8
- package/dist/errors/UserError.js +16 -16
- package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
- package/dist/events/UnifiedSwapEventListener.js +132 -132
- package/dist/http/HttpUtils.d.ts +27 -27
- package/dist/http/HttpUtils.js +91 -91
- package/dist/http/paramcoders/IParamReader.d.ts +8 -8
- package/dist/http/paramcoders/IParamReader.js +2 -2
- package/dist/http/paramcoders/ParamDecoder.d.ts +44 -44
- package/dist/http/paramcoders/ParamDecoder.js +137 -137
- package/dist/http/paramcoders/ParamEncoder.d.ts +20 -20
- package/dist/http/paramcoders/ParamEncoder.js +36 -36
- package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
- package/dist/http/paramcoders/SchemaVerifier.js +145 -145
- package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
- package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
- package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +13 -13
- package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
- package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
- package/dist/http/paramcoders/client/StreamingFetchPromise.js +174 -174
- package/dist/index.d.ts +85 -85
- package/dist/index.js +158 -158
- package/dist/intermediaries/Intermediary.d.ts +178 -178
- package/dist/intermediaries/Intermediary.js +166 -166
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +211 -211
- package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +450 -440
- package/dist/intermediaries/apis/IntermediaryAPI.js +618 -603
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
- package/dist/lnurl/LNURL.d.ts +102 -102
- package/dist/lnurl/LNURL.js +321 -321
- package/dist/prices/RedundantSwapPrice.d.ts +110 -110
- package/dist/prices/RedundantSwapPrice.js +222 -222
- package/dist/prices/SingleSwapPrice.d.ts +34 -34
- package/dist/prices/SingleSwapPrice.js +44 -44
- package/dist/prices/SwapPriceWithChain.d.ts +107 -107
- package/dist/prices/SwapPriceWithChain.js +128 -128
- package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
- package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
- package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
- package/dist/prices/abstract/IPriceProvider.js +74 -74
- package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
- package/dist/prices/abstract/ISwapPrice.js +279 -279
- package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
- package/dist/prices/providers/BinancePriceProvider.js +30 -30
- package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
- package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
- package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
- package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
- package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
- package/dist/prices/providers/CustomPriceProvider.js +35 -35
- package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
- package/dist/prices/providers/KrakenPriceProvider.js +45 -45
- package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
- package/dist/prices/providers/OKXPriceProvider.js +29 -29
- package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
- package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
- package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
- package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
- package/dist/storage/IUnifiedStorage.d.ts +85 -85
- package/dist/storage/IUnifiedStorage.js +2 -2
- package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
- package/dist/storage/UnifiedSwapStorage.js +116 -116
- package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
- package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
- package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
- package/dist/storage-browser/LocalStorageManager.js +93 -93
- package/dist/swapper/Swapper.d.ts +732 -692
- package/dist/swapper/Swapper.js +1713 -1657
- package/dist/swapper/SwapperFactory.d.ts +135 -135
- package/dist/swapper/SwapperFactory.js +162 -162
- package/dist/swapper/SwapperUtils.d.ts +206 -206
- package/dist/swapper/SwapperUtils.js +481 -481
- package/dist/swapper/SwapperWithChain.d.ts +404 -404
- package/dist/swapper/SwapperWithChain.js +469 -469
- package/dist/swapper/SwapperWithSigner.d.ts +322 -322
- package/dist/swapper/SwapperWithSigner.js +318 -318
- package/dist/swaps/IAddressSwap.d.ts +22 -22
- package/dist/swaps/IAddressSwap.js +14 -14
- package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
- package/dist/swaps/IBTCWalletSwap.js +18 -18
- package/dist/swaps/IClaimableSwap.d.ts +49 -49
- package/dist/swaps/IClaimableSwap.js +15 -15
- package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
- package/dist/swaps/IClaimableSwapWrapper.js +2 -2
- package/dist/swaps/IRefundableSwap.d.ts +43 -43
- package/dist/swaps/IRefundableSwap.js +14 -14
- package/dist/swaps/ISwap.d.ts +392 -392
- package/dist/swaps/ISwap.js +349 -349
- package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
- package/dist/swaps/ISwapWithGasDrop.js +12 -12
- package/dist/swaps/ISwapWrapper.d.ts +285 -285
- package/dist/swaps/ISwapWrapper.js +353 -353
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
- package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +139 -139
- package/dist/swaps/escrow_swaps/IEscrowSwap.js +170 -170
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +128 -128
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +105 -105
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +129 -129
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +64 -64
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +82 -82
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +531 -531
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +190 -190
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +583 -583
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +235 -235
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +202 -202
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +68 -68
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +117 -117
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +127 -127
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +251 -251
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +536 -536
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +132 -132
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +637 -631
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1448 -1444
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +257 -225
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -822
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
- package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
- package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +83 -83
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +69 -69
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +93 -93
- package/dist/types/AmountData.d.ts +10 -10
- package/dist/types/AmountData.js +2 -2
- package/dist/types/CustomPriceFunction.d.ts +11 -11
- package/dist/types/CustomPriceFunction.js +2 -2
- package/dist/types/PriceInfoType.d.ts +28 -28
- package/dist/types/PriceInfoType.js +57 -57
- package/dist/types/SwapExecutionAction.d.ts +88 -88
- package/dist/types/SwapExecutionAction.js +2 -2
- package/dist/types/SwapStateInfo.d.ts +5 -5
- package/dist/types/SwapStateInfo.js +2 -2
- package/dist/types/SwapWithSigner.d.ts +17 -17
- package/dist/types/SwapWithSigner.js +43 -43
- package/dist/types/Token.d.ts +99 -99
- package/dist/types/Token.js +76 -76
- package/dist/types/TokenAmount.d.ts +69 -69
- package/dist/types/TokenAmount.js +60 -60
- package/dist/types/fees/Fee.d.ts +50 -50
- package/dist/types/fees/Fee.js +2 -2
- package/dist/types/fees/FeeBreakdown.d.ts +11 -11
- package/dist/types/fees/FeeBreakdown.js +2 -2
- package/dist/types/fees/PercentagePPM.d.ts +17 -17
- package/dist/types/fees/PercentagePPM.js +18 -18
- package/dist/types/lnurl/LNURLPay.d.ts +61 -61
- package/dist/types/lnurl/LNURLPay.js +31 -31
- package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
- package/dist/types/lnurl/LNURLWithdraw.js +27 -27
- package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
- package/dist/types/wallets/LightningInvoiceCreateService.js +15 -15
- package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
- package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
- package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
- package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
- package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
- package/dist/utils/AutomaticClockDriftCorrection.js +70 -70
- package/dist/utils/BitcoinUtils.d.ts +16 -14
- package/dist/utils/BitcoinUtils.js +141 -102
- package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
- package/dist/utils/BitcoinWalletUtils.js +14 -14
- package/dist/utils/Logger.d.ts +7 -7
- package/dist/utils/Logger.js +12 -12
- package/dist/utils/RetryUtils.d.ts +22 -22
- package/dist/utils/RetryUtils.js +67 -67
- package/dist/utils/SwapUtils.d.ts +88 -88
- package/dist/utils/SwapUtils.js +72 -72
- package/dist/utils/TimeoutUtils.d.ts +17 -17
- package/dist/utils/TimeoutUtils.js +55 -55
- package/dist/utils/TokenUtils.d.ts +19 -19
- package/dist/utils/TokenUtils.js +37 -37
- package/dist/utils/TypeUtils.d.ts +7 -7
- package/dist/utils/TypeUtils.js +2 -2
- package/dist/utils/Utils.d.ts +67 -67
- package/dist/utils/Utils.js +208 -208
- package/package.json +43 -43
- package/src/SmartChainAssets.ts +186 -186
- package/src/bitcoin/coinselect2/accumulative.ts +69 -68
- package/src/bitcoin/coinselect2/blackjack.ts +50 -49
- package/src/bitcoin/coinselect2/index.ts +93 -92
- package/src/bitcoin/coinselect2/utils.ts +236 -195
- package/src/bitcoin/wallet/BitcoinWallet.ts +439 -427
- package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -99
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -217
- package/src/enums/FeeType.ts +15 -15
- package/src/enums/SwapAmountType.ts +16 -16
- package/src/enums/SwapDirection.ts +15 -15
- package/src/enums/SwapSide.ts +16 -16
- package/src/enums/SwapType.ts +75 -75
- package/src/errors/IntermediaryError.ts +28 -28
- package/src/errors/RequestError.ts +64 -64
- package/src/errors/UserError.ts +15 -15
- package/src/events/UnifiedSwapEventListener.ts +173 -173
- package/src/http/HttpUtils.ts +91 -91
- package/src/http/paramcoders/IParamReader.ts +9 -9
- package/src/http/paramcoders/ParamDecoder.ts +145 -145
- package/src/http/paramcoders/ParamEncoder.ts +40 -40
- package/src/http/paramcoders/SchemaVerifier.ts +153 -153
- package/src/http/paramcoders/client/ResponseParamDecoder.ts +57 -57
- package/src/http/paramcoders/client/StreamParamEncoder.ts +28 -28
- package/src/http/paramcoders/client/StreamingFetchPromise.ts +192 -192
- package/src/index.ts +140 -140
- package/src/intermediaries/Intermediary.ts +280 -280
- package/src/intermediaries/IntermediaryDiscovery.ts +541 -541
- package/src/intermediaries/apis/IntermediaryAPI.ts +963 -947
- package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +257 -257
- package/src/lnurl/LNURL.ts +402 -402
- package/src/prices/RedundantSwapPrice.ts +264 -264
- package/src/prices/SingleSwapPrice.ts +50 -50
- package/src/prices/SwapPriceWithChain.ts +194 -194
- package/src/prices/abstract/ICachedSwapPrice.ts +85 -85
- package/src/prices/abstract/IPriceProvider.ts +127 -127
- package/src/prices/abstract/ISwapPrice.ts +390 -390
- package/src/prices/providers/BinancePriceProvider.ts +48 -48
- package/src/prices/providers/CoinGeckoPriceProvider.ts +46 -46
- package/src/prices/providers/CoinPaprikaPriceProvider.ts +49 -49
- package/src/prices/providers/CustomPriceProvider.ts +40 -40
- package/src/prices/providers/KrakenPriceProvider.ts +83 -83
- package/src/prices/providers/OKXPriceProvider.ts +59 -59
- package/src/prices/providers/abstract/ExchangePriceProvider.ts +31 -31
- package/src/prices/providers/abstract/HttpPriceProvider.ts +14 -14
- package/src/storage/IUnifiedStorage.ts +95 -95
- package/src/storage/UnifiedSwapStorage.ts +141 -141
- package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
- package/src/storage-browser/LocalStorageManager.ts +106 -106
- package/src/swapper/Swapper.ts +2488 -2416
- package/src/swapper/SwapperFactory.ts +307 -307
- package/src/swapper/SwapperUtils.ts +570 -570
- package/src/swapper/SwapperWithChain.ts +707 -707
- package/src/swapper/SwapperWithSigner.ts +511 -511
- package/src/swaps/IAddressSwap.ts +30 -30
- package/src/swaps/IBTCWalletSwap.ts +92 -92
- package/src/swaps/IClaimableSwap.ts +65 -65
- package/src/swaps/IClaimableSwapWrapper.ts +17 -17
- package/src/swaps/IRefundableSwap.ts +58 -58
- package/src/swaps/ISwap.ts +703 -703
- package/src/swaps/ISwapWithGasDrop.ts +25 -25
- package/src/swaps/ISwapWrapper.ts +539 -539
- package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
- package/src/swaps/escrow_swaps/IEscrowSwap.ts +269 -269
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +282 -282
- package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +169 -169
- package/src/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.ts +300 -300
- package/src/swaps/escrow_swaps/frombtc/IFromBTCWrapper.ts +107 -107
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +1473 -1474
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +601 -601
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1582 -1582
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +750 -750
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1299 -1299
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +610 -610
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1096 -1096
- package/src/swaps/escrow_swaps/tobtc/IToBTCWrapper.ts +138 -138
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.ts +304 -304
- package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +786 -786
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +401 -401
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +1812 -1799
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1236 -1060
- package/src/swaps/trusted/ln/LnForGasSwap.ts +589 -589
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +91 -91
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +862 -862
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +131 -131
- package/src/types/AmountData.ts +9 -9
- package/src/types/CustomPriceFunction.ts +11 -11
- package/src/types/PriceInfoType.ts +66 -66
- package/src/types/SwapExecutionAction.ts +99 -99
- package/src/types/SwapStateInfo.ts +6 -6
- package/src/types/SwapWithSigner.ts +61 -61
- package/src/types/Token.ts +163 -163
- package/src/types/TokenAmount.ts +132 -132
- package/src/types/fees/Fee.ts +56 -56
- package/src/types/fees/FeeBreakdown.ts +11 -11
- package/src/types/fees/PercentagePPM.ts +26 -26
- package/src/types/lnurl/LNURLPay.ts +79 -79
- package/src/types/lnurl/LNURLWithdraw.ts +61 -61
- package/src/types/wallets/LightningInvoiceCreateService.ts +30 -30
- package/src/types/wallets/MinimalBitcoinWalletInterface.ts +21 -21
- package/src/types/wallets/MinimalLightningNetworkWalletInterface.ts +9 -9
- package/src/utils/AutomaticClockDriftCorrection.ts +71 -71
- package/src/utils/BitcoinUtils.ts +132 -91
- package/src/utils/BitcoinWalletUtils.ts +15 -15
- package/src/utils/Logger.ts +14 -14
- package/src/utils/RetryUtils.ts +78 -78
- package/src/utils/SwapUtils.ts +99 -99
- package/src/utils/TimeoutUtils.ts +49 -49
- package/src/utils/TokenUtils.ts +33 -33
- package/src/utils/TypeUtils.ts +8 -8
- package/src/utils/Utils.ts +212 -212
|
@@ -1,1096 +1,1096 @@
|
|
|
1
|
-
import {IToBTCDefinition, IToBTCWrapper} from "./IToBTCWrapper";
|
|
2
|
-
import {
|
|
3
|
-
ChainType,
|
|
4
|
-
isAbstractSigner,
|
|
5
|
-
SignatureData,
|
|
6
|
-
SignatureVerificationError,
|
|
7
|
-
SwapCommitState,
|
|
8
|
-
SwapCommitStateType,
|
|
9
|
-
SwapData
|
|
10
|
-
} from "@atomiqlabs/base";
|
|
11
|
-
import {
|
|
12
|
-
IntermediaryAPI,
|
|
13
|
-
RefundAuthorizationResponse,
|
|
14
|
-
RefundAuthorizationResponseCodes
|
|
15
|
-
} from "../../../intermediaries/apis/IntermediaryAPI";
|
|
16
|
-
import {IntermediaryError} from "../../../errors/IntermediaryError";
|
|
17
|
-
import {extendAbortController, toBigInt} from "../../../utils/Utils";
|
|
18
|
-
import {Fee} from "../../../types/fees/Fee";
|
|
19
|
-
import {IEscrowSelfInitSwap, IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../IEscrowSelfInitSwap";
|
|
20
|
-
import {IRefundableSwap} from "../../IRefundableSwap";
|
|
21
|
-
import {FeeType} from "../../../enums/FeeType";
|
|
22
|
-
import {ppmToPercentage} from "../../../types/fees/PercentagePPM";
|
|
23
|
-
import {TokenAmount, toTokenAmount} from "../../../types/TokenAmount";
|
|
24
|
-
import {BtcToken, SCToken} from "../../../types/Token";
|
|
25
|
-
import {timeoutPromise} from "../../../utils/TimeoutUtils";
|
|
26
|
-
import {SwapExecutionAction, SwapExecutionActionCommit} from "../../../types/SwapExecutionAction";
|
|
27
|
-
|
|
28
|
-
export type IToBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
|
|
29
|
-
signatureData?: SignatureData,
|
|
30
|
-
data: T,
|
|
31
|
-
networkFee: bigint,
|
|
32
|
-
networkFeeBtc: bigint
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export function isIToBTCSwapInit<T extends SwapData>(obj: any): obj is IToBTCSwapInit<T> {
|
|
36
|
-
return typeof(obj.networkFee) === "bigint" &&
|
|
37
|
-
typeof(obj.networkFeeBtc) === "bigint" &&
|
|
38
|
-
(obj.signatureData==null || (
|
|
39
|
-
typeof(obj.signatureData) === 'object' &&
|
|
40
|
-
typeof(obj.signatureData.prefix)==="string" &&
|
|
41
|
-
typeof(obj.signatureData.timeout)==="string" &&
|
|
42
|
-
typeof(obj.signatureData.signature)==="string"
|
|
43
|
-
)) &&
|
|
44
|
-
typeof(obj.data) === 'object' &&
|
|
45
|
-
isIEscrowSelfInitSwapInit<T>(obj);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* State enum for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
|
|
50
|
-
*
|
|
51
|
-
* @category Swaps/Smart chain → Bitcoin
|
|
52
|
-
*/
|
|
53
|
-
export enum ToBTCSwapState {
|
|
54
|
-
/**
|
|
55
|
-
* Intermediary (LP) was unable to process the swap and the funds were refunded on the
|
|
56
|
-
* source chain
|
|
57
|
-
*/
|
|
58
|
-
REFUNDED = -3,
|
|
59
|
-
/**
|
|
60
|
-
* Swap has expired for good and there is no way how it can be executed anymore
|
|
61
|
-
*/
|
|
62
|
-
QUOTE_EXPIRED = -2,
|
|
63
|
-
/**
|
|
64
|
-
* A swap is almost expired, and it should be presented to the user as expired, though
|
|
65
|
-
* there is still a chance that it will be processed
|
|
66
|
-
*/
|
|
67
|
-
QUOTE_SOFT_EXPIRED = -1,
|
|
68
|
-
/**
|
|
69
|
-
* Swap was created, use the {@link IToBTCSwap.commit} or {@link IToBTCSwap.txsCommit} to
|
|
70
|
-
* initiate it by creating the swap escrow on the source chain
|
|
71
|
-
*/
|
|
72
|
-
CREATED = 0,
|
|
73
|
-
/**
|
|
74
|
-
* Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will
|
|
75
|
-
* now process the swap. You can wait till that happens with the {@link IToBTCSwap.waitForPayment}
|
|
76
|
-
* function.
|
|
77
|
-
*/
|
|
78
|
-
COMMITED = 1,
|
|
79
|
-
/**
|
|
80
|
-
* The intermediary (LP) has processed the transaction and sent out the funds on the destination chain,
|
|
81
|
-
* but hasn't yet settled the escrow on the source chain.
|
|
82
|
-
*/
|
|
83
|
-
SOFT_CLAIMED = 2,
|
|
84
|
-
/**
|
|
85
|
-
* Swap was successfully settled by the intermediary (LP) on the source chain
|
|
86
|
-
*/
|
|
87
|
-
CLAIMED = 3,
|
|
88
|
-
/**
|
|
89
|
-
* Intermediary (LP) was unable to process the swap and the swap escrow on the source chain
|
|
90
|
-
* is refundable, call {@link IToBTCSwap.refund} or {@link IToBTCSwap.txsRefund} to refund
|
|
91
|
-
*/
|
|
92
|
-
REFUNDABLE = 4
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const ToBTCSwapStateDescription = {
|
|
96
|
-
[ToBTCSwapState.REFUNDED]: "Intermediary (LP) was unable to process the swap and the funds were refunded on the source chain",
|
|
97
|
-
[ToBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
|
|
98
|
-
[ToBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
|
|
99
|
-
[ToBTCSwapState.CREATED]: "Swap was created, initiate it by creating the swap escrow on the source chain",
|
|
100
|
-
[ToBTCSwapState.COMMITED]: "Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will now process the swap.",
|
|
101
|
-
[ToBTCSwapState.SOFT_CLAIMED]: "The intermediary (LP) has processed the transaction and sent out the funds on the destination chain, but hasn't yet settled the escrow on the source chain.",
|
|
102
|
-
[ToBTCSwapState.CLAIMED]: "Swap was successfully settled by the intermediary (LP) on the source chain",
|
|
103
|
-
[ToBTCSwapState.REFUNDABLE]: "Intermediary (LP) was unable to process the swap and the swap escrow on the source chain is refundable."
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Base class for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
|
|
108
|
-
*
|
|
109
|
-
* @category Swaps/Smart chain → Bitcoin
|
|
110
|
-
*/
|
|
111
|
-
export abstract class IToBTCSwap<
|
|
112
|
-
T extends ChainType = ChainType,
|
|
113
|
-
D extends IToBTCDefinition<T, IToBTCWrapper<T, D>, IToBTCSwap<T, D>> = IToBTCDefinition<T, IToBTCWrapper<T, any>, IToBTCSwap<T, any>>,
|
|
114
|
-
> extends IEscrowSelfInitSwap<T, D, ToBTCSwapState> implements IRefundableSwap<T, D, ToBTCSwapState> {
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* @internal
|
|
118
|
-
*/
|
|
119
|
-
protected readonly swapStateDescription = ToBTCSwapStateDescription;
|
|
120
|
-
/**
|
|
121
|
-
* @internal
|
|
122
|
-
*/
|
|
123
|
-
protected readonly swapStateName = (state: number) => ToBTCSwapState[state];
|
|
124
|
-
/**
|
|
125
|
-
* @internal
|
|
126
|
-
*/
|
|
127
|
-
protected readonly abstract outputToken: BtcToken;
|
|
128
|
-
/**
|
|
129
|
-
* @internal
|
|
130
|
-
*/
|
|
131
|
-
protected readonly networkFee: bigint;
|
|
132
|
-
/**
|
|
133
|
-
* @internal
|
|
134
|
-
*/
|
|
135
|
-
protected networkFeeBtc: bigint;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* @internal
|
|
139
|
-
*/
|
|
140
|
-
readonly _data!: T["Data"];
|
|
141
|
-
|
|
142
|
-
protected constructor(wrapper: D["Wrapper"], serializedObject: any);
|
|
143
|
-
protected constructor(wrapper: D["Wrapper"], init: IToBTCSwapInit<T["Data"]>);
|
|
144
|
-
protected constructor(
|
|
145
|
-
wrapper: D["Wrapper"],
|
|
146
|
-
initOrObject: IToBTCSwapInit<T["Data"]> | any
|
|
147
|
-
) {
|
|
148
|
-
super(wrapper, initOrObject);
|
|
149
|
-
if(isIToBTCSwapInit<T["Data"]>(initOrObject)) {
|
|
150
|
-
this._state = ToBTCSwapState.CREATED;
|
|
151
|
-
this.networkFee = initOrObject.networkFee;
|
|
152
|
-
this.networkFeeBtc = initOrObject.networkFeeBtc;
|
|
153
|
-
this._data = initOrObject.data;
|
|
154
|
-
this.signatureData = initOrObject.signatureData;
|
|
155
|
-
} else {
|
|
156
|
-
this.networkFee = toBigInt(initOrObject.networkFee);
|
|
157
|
-
this.networkFeeBtc = toBigInt(initOrObject.networkFeeBtc);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* @inheritDoc
|
|
163
|
-
* @internal
|
|
164
|
-
*/
|
|
165
|
-
protected getSwapData(): T["Data"] {
|
|
166
|
-
return this._data;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* @inheritDoc
|
|
171
|
-
* @internal
|
|
172
|
-
*/
|
|
173
|
-
protected upgradeVersion() {
|
|
174
|
-
if(this.version == null) {
|
|
175
|
-
switch(this._state) {
|
|
176
|
-
case -2:
|
|
177
|
-
this._state = ToBTCSwapState.REFUNDED
|
|
178
|
-
break;
|
|
179
|
-
case -1:
|
|
180
|
-
this._state = ToBTCSwapState.QUOTE_EXPIRED
|
|
181
|
-
break;
|
|
182
|
-
case 0:
|
|
183
|
-
this._state = ToBTCSwapState.CREATED
|
|
184
|
-
break;
|
|
185
|
-
case 1:
|
|
186
|
-
this._state = ToBTCSwapState.COMMITED
|
|
187
|
-
break;
|
|
188
|
-
case 2:
|
|
189
|
-
this._state = ToBTCSwapState.CLAIMED
|
|
190
|
-
break;
|
|
191
|
-
case 3:
|
|
192
|
-
this._state = ToBTCSwapState.REFUNDABLE
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
this.version = 1;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* @inheritDoc
|
|
201
|
-
* @internal
|
|
202
|
-
*/
|
|
203
|
-
protected tryRecomputeSwapPrice() {
|
|
204
|
-
const output = this.getOutput();
|
|
205
|
-
if(output.rawAmount!=null) {
|
|
206
|
-
if(this.swapFeeBtc==null) {
|
|
207
|
-
this.swapFeeBtc = this.swapFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
|
|
208
|
-
}
|
|
209
|
-
if(this.networkFeeBtc==null) {
|
|
210
|
-
this.networkFeeBtc = this.networkFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
super.tryRecomputeSwapPrice();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Returns the payment hash identifier to be sent to the LP for getStatus and getRefund
|
|
218
|
-
* @internal
|
|
219
|
-
*/
|
|
220
|
-
protected getLpIdentifier(): string {
|
|
221
|
-
return this.getClaimHash();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Sets the payment result for the swap, optionally also checking it (checking that tx exist or swap secret is valid)
|
|
226
|
-
*
|
|
227
|
-
* @param result Result returned by the LP
|
|
228
|
-
* @param check Whether to check the passed result
|
|
229
|
-
* @returns true if check passed, false if check failed with a soft error (e.g. tx not yet found in the mempool)
|
|
230
|
-
* @throws {IntermediaryError} When the data returned by the intermediary isn't valid
|
|
231
|
-
*
|
|
232
|
-
* @internal
|
|
233
|
-
*/
|
|
234
|
-
abstract _setPaymentResult(result: {secret?: string, txId?: string}, check?: boolean): Promise<boolean>;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
//////////////////////////////
|
|
238
|
-
//// Getters & utils
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* @inheritDoc
|
|
242
|
-
*/
|
|
243
|
-
getInputAddress(): string | null {
|
|
244
|
-
return this._getInitiator();
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* @inheritDoc
|
|
249
|
-
*/
|
|
250
|
-
getInputTxId(): string | null {
|
|
251
|
-
return this._commitTxId ?? null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* @inheritDoc
|
|
256
|
-
*/
|
|
257
|
-
requiresAction(): boolean {
|
|
258
|
-
return this.isRefundable();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* @inheritDoc
|
|
263
|
-
*/
|
|
264
|
-
isFinished(): boolean {
|
|
265
|
-
return this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.REFUNDED || this._state===ToBTCSwapState.QUOTE_EXPIRED;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* @inheritDoc
|
|
270
|
-
*/
|
|
271
|
-
isRefundable(): boolean {
|
|
272
|
-
return this._state===ToBTCSwapState.REFUNDABLE;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* @inheritDoc
|
|
277
|
-
*/
|
|
278
|
-
isQuoteExpired(): boolean {
|
|
279
|
-
return this._state===ToBTCSwapState.QUOTE_EXPIRED;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* @inheritDoc
|
|
284
|
-
*/
|
|
285
|
-
isQuoteSoftExpired(): boolean {
|
|
286
|
-
return this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* @inheritDoc
|
|
291
|
-
*/
|
|
292
|
-
isSuccessful(): boolean {
|
|
293
|
-
return this._state===ToBTCSwapState.CLAIMED;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* @inheritDoc
|
|
298
|
-
*/
|
|
299
|
-
isFailed(): boolean {
|
|
300
|
-
return this._state===ToBTCSwapState.REFUNDED;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* @inheritDoc
|
|
305
|
-
*/
|
|
306
|
-
isInProgress(): boolean {
|
|
307
|
-
return this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Returns the time (in UNIX milliseconds) at which the swap expires and the user is able to unilaterally
|
|
312
|
-
* refund it with the {@link refund} or {@link txsRefund} function.
|
|
313
|
-
*/
|
|
314
|
-
getExpiry(): number {
|
|
315
|
-
return Number(this._data.getExpiry())*1000;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* @inheritDoc
|
|
320
|
-
* @internal
|
|
321
|
-
*/
|
|
322
|
-
_getInitiator(): string {
|
|
323
|
-
return this._data.getOfferer();
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
//////////////////////////////
|
|
328
|
-
//// Amounts & fees
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Returns the swap fee charged by the intermediary (LP) on this swap
|
|
332
|
-
*
|
|
333
|
-
* @internal
|
|
334
|
-
*/
|
|
335
|
-
protected getSwapFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
336
|
-
if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
|
|
337
|
-
|
|
338
|
-
const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
|
|
339
|
-
const output = this.getOutput();
|
|
340
|
-
const swapFeePPM = output.rawAmount==null ? 0n : feeWithoutBaseFee * 1000000n / output.rawAmount;
|
|
341
|
-
|
|
342
|
-
const amountInDstToken = toTokenAmount(
|
|
343
|
-
this.swapFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
344
|
-
);
|
|
345
|
-
return {
|
|
346
|
-
amountInSrcToken: toTokenAmount(this.swapFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
|
|
347
|
-
amountInDstToken,
|
|
348
|
-
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
349
|
-
usdValue: amountInDstToken.usdValue,
|
|
350
|
-
pastUsdValue: amountInDstToken.pastUsdValue,
|
|
351
|
-
composition: {
|
|
352
|
-
base: toTokenAmount(this.pricingInfo.satsBaseFee, this.outputToken, this.wrapper._prices, this.pricingInfo),
|
|
353
|
-
percentage: ppmToPercentage(swapFeePPM)
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Returns network fee for on the destination chain for the swap
|
|
360
|
-
*
|
|
361
|
-
* @internal
|
|
362
|
-
*/
|
|
363
|
-
protected getNetworkFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
364
|
-
const amountInDstToken = toTokenAmount(
|
|
365
|
-
this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
366
|
-
);
|
|
367
|
-
return {
|
|
368
|
-
amountInSrcToken: toTokenAmount(
|
|
369
|
-
this.networkFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
|
|
370
|
-
),
|
|
371
|
-
amountInDstToken,
|
|
372
|
-
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
373
|
-
usdValue: amountInDstToken.usdValue,
|
|
374
|
-
pastUsdValue: amountInDstToken.pastUsdValue
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* @inheritDoc
|
|
380
|
-
*/
|
|
381
|
-
getFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
382
|
-
const amountInDstToken = toTokenAmount(
|
|
383
|
-
this.swapFeeBtc + this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
384
|
-
);
|
|
385
|
-
return {
|
|
386
|
-
amountInSrcToken: toTokenAmount(
|
|
387
|
-
this.swapFee + this.networkFee, this.wrapper._tokens[this._data.getToken()],
|
|
388
|
-
this.wrapper._prices, this.pricingInfo
|
|
389
|
-
),
|
|
390
|
-
amountInDstToken,
|
|
391
|
-
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
392
|
-
usdValue: amountInDstToken.usdValue,
|
|
393
|
-
pastUsdValue: amountInDstToken.pastUsdValue
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* @inheritDoc
|
|
399
|
-
*/
|
|
400
|
-
getFeeBreakdown(): [
|
|
401
|
-
{type: FeeType.SWAP, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
|
|
402
|
-
{type: FeeType.NETWORK_OUTPUT, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
|
|
403
|
-
] {
|
|
404
|
-
return [
|
|
405
|
-
{
|
|
406
|
-
type: FeeType.SWAP,
|
|
407
|
-
fee: this.getSwapFee()
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
type: FeeType.NETWORK_OUTPUT,
|
|
411
|
-
fee: this.getNetworkFee()
|
|
412
|
-
}
|
|
413
|
-
];
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* @inheritDoc
|
|
418
|
-
*/
|
|
419
|
-
getInputToken(): SCToken<T["ChainId"]> {
|
|
420
|
-
return this.wrapper._tokens[this._data.getToken()];
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* @inheritDoc
|
|
425
|
-
*/
|
|
426
|
-
getInput(): TokenAmount<SCToken<T["ChainId"]>, true> {
|
|
427
|
-
return toTokenAmount(
|
|
428
|
-
this._data.getAmount(), this.wrapper._tokens[this._data.getToken()],
|
|
429
|
-
this.wrapper._prices, this.pricingInfo
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* @inheritDoc
|
|
435
|
-
*/
|
|
436
|
-
getInputWithoutFee(): TokenAmount<SCToken<T["ChainId"]>, true> {
|
|
437
|
-
return toTokenAmount(
|
|
438
|
-
this._data.getAmount() - (this.swapFee + this.networkFee),
|
|
439
|
-
this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Checks if the initiator/sender on the source chain has enough balance to go through with the swap
|
|
445
|
-
*/
|
|
446
|
-
async hasEnoughBalance(): Promise<{
|
|
447
|
-
enoughBalance: boolean,
|
|
448
|
-
balance: TokenAmount<SCToken<T["ChainId"]>, true>,
|
|
449
|
-
required: TokenAmount<SCToken<T["ChainId"]>, true>
|
|
450
|
-
}> {
|
|
451
|
-
const [balance, commitFee] = await Promise.all([
|
|
452
|
-
this._contract.getBalance(this._getInitiator(), this._data.getToken(), false),
|
|
453
|
-
this._data.getToken()===this.wrapper._chain.getNativeCurrencyAddress() ? this.getCommitFee() : Promise.resolve(null)
|
|
454
|
-
]);
|
|
455
|
-
let required = this._data.getAmount();
|
|
456
|
-
if(commitFee!=null) required = required + commitFee;
|
|
457
|
-
return {
|
|
458
|
-
enoughBalance: balance >= required,
|
|
459
|
-
balance: toTokenAmount(balance, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
|
|
460
|
-
required: toTokenAmount(required, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo)
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Checks if the initiator/sender on the source chain has enough native token balance
|
|
466
|
-
* to cover the transaction fee of initiating the swap
|
|
467
|
-
*/
|
|
468
|
-
async hasEnoughForTxFees(): Promise<{
|
|
469
|
-
enoughBalance: boolean,
|
|
470
|
-
balance: TokenAmount<SCToken<T["ChainId"]>, true>,
|
|
471
|
-
required: TokenAmount<SCToken<T["ChainId"]>, true>
|
|
472
|
-
}> {
|
|
473
|
-
const [balance, commitFee] = await Promise.all([
|
|
474
|
-
this._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
|
|
475
|
-
this.getCommitFee()
|
|
476
|
-
]);
|
|
477
|
-
return {
|
|
478
|
-
enoughBalance: balance >= commitFee,
|
|
479
|
-
balance: toTokenAmount(balance, this.wrapper._getNativeToken(), this.wrapper._prices),
|
|
480
|
-
required: toTokenAmount(commitFee, this.wrapper._getNativeToken(), this.wrapper._prices)
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
//////////////////////////////
|
|
486
|
-
//// Execution
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Executes the swap with the provided smart chain wallet/signer
|
|
490
|
-
*
|
|
491
|
-
* @param signer Smart chain wallet/signer to use to sign the transaction on the source chain
|
|
492
|
-
* @param callbacks Callbacks to track the progress of the swap
|
|
493
|
-
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
494
|
-
*
|
|
495
|
-
* @returns {boolean} Whether the swap was successfully processed by the LP, in case `false` is returned
|
|
496
|
-
* the user can refund their funds back on the source chain by calling {@link refund}
|
|
497
|
-
*/
|
|
498
|
-
async execute(
|
|
499
|
-
signer: T["Signer"] | T["NativeSigner"],
|
|
500
|
-
callbacks?: {
|
|
501
|
-
onSourceTransactionSent?: (sourceTxId: string) => void,
|
|
502
|
-
onSourceTransactionConfirmed?: (sourceTxId: string) => void,
|
|
503
|
-
onSwapSettled?: (destinationTxId: string) => void
|
|
504
|
-
},
|
|
505
|
-
options?: {
|
|
506
|
-
abortSignal?: AbortSignal,
|
|
507
|
-
paymentCheckIntervalSeconds?: number,
|
|
508
|
-
maxWaitTillSwapProcessedSeconds?: number
|
|
509
|
-
}
|
|
510
|
-
): Promise<boolean> {
|
|
511
|
-
if(this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Quote expired");
|
|
512
|
-
if(this._state===ToBTCSwapState.REFUNDED) throw new Error("Swap already refunded");
|
|
513
|
-
if(this._state===ToBTCSwapState.REFUNDABLE) throw new Error("Swap refundable, refund with swap.refund()");
|
|
514
|
-
if(this._state===ToBTCSwapState.SOFT_CLAIMED || this._state===ToBTCSwapState.CLAIMED) throw new Error("Swap already settled!");
|
|
515
|
-
|
|
516
|
-
if(this._state===ToBTCSwapState.CREATED) {
|
|
517
|
-
const txId = await this.commit(signer, options?.abortSignal, false, callbacks?.onSourceTransactionSent);
|
|
518
|
-
if(callbacks?.onSourceTransactionConfirmed!=null) callbacks.onSourceTransactionConfirmed(txId);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// @ts-ignore
|
|
522
|
-
if(this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.SOFT_CLAIMED) return true;
|
|
523
|
-
|
|
524
|
-
if(this._state===ToBTCSwapState.COMMITED) {
|
|
525
|
-
const success = await this.waitForPayment(options?.maxWaitTillSwapProcessedSeconds ?? 120, options?.paymentCheckIntervalSeconds, options?.abortSignal);
|
|
526
|
-
if(success) {
|
|
527
|
-
if(callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
|
|
528
|
-
return true;
|
|
529
|
-
} else {
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
throw new Error("Unexpected state reached!");
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* @inheritDoc
|
|
539
|
-
*
|
|
540
|
-
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
541
|
-
* (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
|
|
542
|
-
*/
|
|
543
|
-
async txsExecute(options?: {
|
|
544
|
-
skipChecks?: boolean
|
|
545
|
-
}): Promise<[
|
|
546
|
-
SwapExecutionActionCommit<T>
|
|
547
|
-
]> {
|
|
548
|
-
if(this._state!==ToBTCSwapState.CREATED) throw new Error("Invalid swap state, needs to be CREATED!");
|
|
549
|
-
const txsCommit = await this.txsCommit(options?.skipChecks);
|
|
550
|
-
return [
|
|
551
|
-
{
|
|
552
|
-
name: "Commit" as const,
|
|
553
|
-
description: `Initiates the swap by commiting the funds to the escrow on the ${this.chainIdentifier} side`,
|
|
554
|
-
chain: this.chainIdentifier,
|
|
555
|
-
txs: txsCommit
|
|
556
|
-
}
|
|
557
|
-
];
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* @inheritDoc
|
|
562
|
-
*
|
|
563
|
-
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
564
|
-
* (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
|
|
565
|
-
* @param options.refundSmartChainSigner Optional smart chain signer to use when creating refunds transactions
|
|
566
|
-
*/
|
|
567
|
-
async getCurrentActions(options?: {
|
|
568
|
-
skipChecks?: boolean,
|
|
569
|
-
refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
|
|
570
|
-
}): Promise<SwapExecutionAction<T>[]> {
|
|
571
|
-
if(this._state===ToBTCSwapState.CREATED) {
|
|
572
|
-
try {
|
|
573
|
-
return await this.txsExecute(options);
|
|
574
|
-
} catch (e) {}
|
|
575
|
-
}
|
|
576
|
-
if(this.isRefundable()) {
|
|
577
|
-
return [{
|
|
578
|
-
name: "Refund" as const,
|
|
579
|
-
description: "Refund the swap after it failed to execute",
|
|
580
|
-
chain: this.chainIdentifier,
|
|
581
|
-
txs: await this.txsRefund(options?.refundSmartChainSigner)
|
|
582
|
-
}];
|
|
583
|
-
}
|
|
584
|
-
return [];
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
//////////////////////////////
|
|
588
|
-
//// Commit
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* @inheritDoc
|
|
592
|
-
*
|
|
593
|
-
* @throws {Error} When in invalid state (not {@link ToBTCSwapState.CREATED})
|
|
594
|
-
*/
|
|
595
|
-
async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
|
|
596
|
-
if(this._state!==ToBTCSwapState.CREATED && (!skipChecks || this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED)) throw new Error("Must be in CREATED state!");
|
|
597
|
-
if(this.signatureData==null) throw new Error("Init signature data not known, cannot commit!");
|
|
598
|
-
|
|
599
|
-
if(!this.initiated) {
|
|
600
|
-
this.initiated = true;
|
|
601
|
-
await this._saveAndEmit();
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return await this._contract.txsInit(
|
|
605
|
-
this._getInitiator(), this._data, this.signatureData, skipChecks, this.feeRate
|
|
606
|
-
).catch(e => Promise.reject(e instanceof SignatureVerificationError ? new Error("Request timed out") : e));
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* @inheritDoc
|
|
611
|
-
*
|
|
612
|
-
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
613
|
-
*/
|
|
614
|
-
async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
615
|
-
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
616
|
-
this.checkSigner(signer);
|
|
617
|
-
const txs = await this.txsCommit(skipChecks);
|
|
618
|
-
let txCount = 0;
|
|
619
|
-
const result = await this.wrapper._chain.sendAndConfirm(
|
|
620
|
-
signer, txs, true, abortSignal, false, (txId, rawTx) => {
|
|
621
|
-
txCount++;
|
|
622
|
-
if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
|
|
623
|
-
return Promise.resolve();
|
|
624
|
-
}
|
|
625
|
-
);
|
|
626
|
-
|
|
627
|
-
this._commitTxId = result[result.length-1];
|
|
628
|
-
if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
|
|
629
|
-
await this._saveAndEmit(ToBTCSwapState.COMMITED);
|
|
630
|
-
}
|
|
631
|
-
return this._commitTxId;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* @inheritDoc
|
|
636
|
-
*
|
|
637
|
-
* @throws {Error} If swap is not in the correct state (must be {@link ToBTCSwapState.CREATED})
|
|
638
|
-
*/
|
|
639
|
-
async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
|
|
640
|
-
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.CLAIMED) return Promise.resolve();
|
|
641
|
-
if(this._state!==ToBTCSwapState.CREATED && this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state (not CREATED)");
|
|
642
|
-
|
|
643
|
-
const abortController = extendAbortController(abortSignal);
|
|
644
|
-
let result: number | boolean;
|
|
645
|
-
try {
|
|
646
|
-
result = await Promise.race([
|
|
647
|
-
this.watchdogWaitTillCommited(undefined, abortController.signal),
|
|
648
|
-
this.waitTillState(ToBTCSwapState.COMMITED, "gte", abortController.signal).then(() => 0)
|
|
649
|
-
]);
|
|
650
|
-
abortController.abort();
|
|
651
|
-
} catch (e) {
|
|
652
|
-
abortController.abort();
|
|
653
|
-
throw e;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if(result===0) this.logger.debug("waitTillCommited(): Resolved from state change");
|
|
657
|
-
if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
658
|
-
if(result===false) {
|
|
659
|
-
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expiry");
|
|
660
|
-
if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED) {
|
|
661
|
-
await this._saveAndEmit(ToBTCSwapState.QUOTE_EXPIRED);
|
|
662
|
-
}
|
|
663
|
-
throw new Error("Quote expired while waiting for transaction confirmation!");
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
|
|
667
|
-
await this._saveAndEmit(ToBTCSwapState.COMMITED);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
//////////////////////////////
|
|
673
|
-
//// Payment
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* Waits till the swap is processed by the intermediary (LP)
|
|
677
|
-
*
|
|
678
|
-
* @param checkIntervalSeconds How often to poll the intermediary for status (5 seconds default)
|
|
679
|
-
* @param abortSignal Abort signal
|
|
680
|
-
* @internal
|
|
681
|
-
*/
|
|
682
|
-
protected async waitTillIntermediarySwapProcessed(
|
|
683
|
-
checkIntervalSeconds?: number,
|
|
684
|
-
abortSignal?: AbortSignal
|
|
685
|
-
): Promise<RefundAuthorizationResponse> {
|
|
686
|
-
if(this.url==null) throw new Error("LP URL not specified!");
|
|
687
|
-
checkIntervalSeconds ??= 5;
|
|
688
|
-
let resp: RefundAuthorizationResponse = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
|
|
689
|
-
while(!abortSignal?.aborted && (
|
|
690
|
-
resp.code===RefundAuthorizationResponseCodes.PENDING || resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
|
|
691
|
-
)) {
|
|
692
|
-
resp = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
693
|
-
if(resp.code===RefundAuthorizationResponseCodes.PAID) {
|
|
694
|
-
const validResponse = await this._setPaymentResult(resp.data, true);
|
|
695
|
-
if(validResponse) {
|
|
696
|
-
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE) {
|
|
697
|
-
await this._saveAndEmit(ToBTCSwapState.SOFT_CLAIMED);
|
|
698
|
-
}
|
|
699
|
-
} else {
|
|
700
|
-
resp = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if(
|
|
704
|
-
resp.code===RefundAuthorizationResponseCodes.PENDING ||
|
|
705
|
-
resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
|
|
706
|
-
) await timeoutPromise(checkIntervalSeconds*1000, abortSignal);
|
|
707
|
-
}
|
|
708
|
-
return resp;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
* Checks whether the swap was already processed by the LP and is either successful (requires proof which is
|
|
713
|
-
* either a HTLC pre-image for LN swaps or valid txId for on-chain swap) or failed and we can cooperatively
|
|
714
|
-
* refund.
|
|
715
|
-
*
|
|
716
|
-
* @param save whether to save the data
|
|
717
|
-
* @returns `true` if swap is processed, `false` if the swap is still ongoing
|
|
718
|
-
*
|
|
719
|
-
* @internal
|
|
720
|
-
*/
|
|
721
|
-
protected async checkIntermediarySwapProcessed(save: boolean = true): Promise<boolean> {
|
|
722
|
-
if(this._state===ToBTCSwapState.CREATED || this._state==ToBTCSwapState.QUOTE_EXPIRED || this.url==null) return false;
|
|
723
|
-
if(this.isFinished() || this.isRefundable()) return true;
|
|
724
|
-
//Check if that maybe already concluded according to the LP
|
|
725
|
-
const resp = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
726
|
-
switch(resp.code) {
|
|
727
|
-
case RefundAuthorizationResponseCodes.PAID:
|
|
728
|
-
const processed = await this._setPaymentResult(resp.data, true);
|
|
729
|
-
if(processed) {
|
|
730
|
-
this._state = ToBTCSwapState.SOFT_CLAIMED;
|
|
731
|
-
if(save) await this._saveAndEmit();
|
|
732
|
-
}
|
|
733
|
-
return processed;
|
|
734
|
-
case RefundAuthorizationResponseCodes.REFUND_DATA:
|
|
735
|
-
await this._contract.isValidRefundAuthorization(this._data, resp.data);
|
|
736
|
-
this._state = ToBTCSwapState.REFUNDABLE;
|
|
737
|
-
if(save) await this._saveAndEmit();
|
|
738
|
-
return true;
|
|
739
|
-
default:
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* A blocking promise resolving when swap was concluded by the intermediary (LP),
|
|
746
|
-
* rejecting in case of failure
|
|
747
|
-
*
|
|
748
|
-
* @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled, an error is thrown if the
|
|
749
|
-
* swap is taking too long to claim
|
|
750
|
-
* @param checkIntervalSeconds How often to poll the intermediary for answer
|
|
751
|
-
* @param abortSignal Abort signal
|
|
752
|
-
* @returns `true` if swap was successful, `false` if swap failed and we can refund
|
|
753
|
-
*
|
|
754
|
-
* @throws {IntermediaryError} If a swap is determined expired by the intermediary, but it is actually still valid
|
|
755
|
-
* @throws {SignatureVerificationError} If the swap should be cooperatively refundable but the intermediary returned
|
|
756
|
-
* invalid refund signature
|
|
757
|
-
* @throws {Error} When swap expires or if the swap has invalid state (must be {@link ToBTCSwapState.COMMITED})
|
|
758
|
-
*/
|
|
759
|
-
async waitForPayment(maxWaitTimeSeconds?: number, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
760
|
-
if(this._state===ToBTCSwapState.CLAIMED) return Promise.resolve(true);
|
|
761
|
-
if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.SOFT_CLAIMED) throw new Error("Invalid state (not COMMITED)");
|
|
762
|
-
|
|
763
|
-
const abortController = extendAbortController(abortSignal);
|
|
764
|
-
|
|
765
|
-
let timedOut: boolean = false;
|
|
766
|
-
if(maxWaitTimeSeconds!=null) {
|
|
767
|
-
const timeout = setTimeout(() => {
|
|
768
|
-
timedOut = true;
|
|
769
|
-
abortController.abort();
|
|
770
|
-
}, maxWaitTimeSeconds * 1000);
|
|
771
|
-
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
let result: void | RefundAuthorizationResponse;
|
|
775
|
-
try {
|
|
776
|
-
result = await Promise.race([
|
|
777
|
-
this.waitTillState(ToBTCSwapState.CLAIMED, "gte", abortController.signal),
|
|
778
|
-
this.waitTillIntermediarySwapProcessed(checkIntervalSeconds, abortController.signal)
|
|
779
|
-
]);
|
|
780
|
-
abortController.abort();
|
|
781
|
-
} catch (e) {
|
|
782
|
-
abortController.abort();
|
|
783
|
-
if(timedOut) {
|
|
784
|
-
throw new Error("Timed out while waiting for LP to process the swap, the LP might be unresponsive or offline!" +
|
|
785
|
-
` Please check later or wait till ${new Date(Number(this._data.getExpiry())*1000).toLocaleString()} to refund unilaterally!`);
|
|
786
|
-
}
|
|
787
|
-
throw e;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
if(typeof result !== "object") {
|
|
791
|
-
if((this._state as ToBTCSwapState)===ToBTCSwapState.REFUNDABLE) throw new Error("Swap expired");
|
|
792
|
-
this.logger.debug("waitTillRefunded(): Resolved from state change");
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
this.logger.debug("waitTillRefunded(): Resolved from intermediary response");
|
|
796
|
-
|
|
797
|
-
switch(result.code) {
|
|
798
|
-
case RefundAuthorizationResponseCodes.PAID:
|
|
799
|
-
return true;
|
|
800
|
-
case RefundAuthorizationResponseCodes.REFUND_DATA:
|
|
801
|
-
const resultData = result.data;
|
|
802
|
-
await this._contract.isValidRefundAuthorization(
|
|
803
|
-
this._data,
|
|
804
|
-
resultData
|
|
805
|
-
);
|
|
806
|
-
await this._saveAndEmit(ToBTCSwapState.REFUNDABLE);
|
|
807
|
-
return false;
|
|
808
|
-
case RefundAuthorizationResponseCodes.EXPIRED:
|
|
809
|
-
if(await this._contract.isExpired(this._getInitiator(), this._data)) throw new Error("Swap expired");
|
|
810
|
-
throw new IntermediaryError("Swap expired");
|
|
811
|
-
case RefundAuthorizationResponseCodes.NOT_FOUND:
|
|
812
|
-
if((this._state as ToBTCSwapState)===ToBTCSwapState.CLAIMED) return true;
|
|
813
|
-
throw new Error("LP swap not found");
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
throw new Error("Invalid response code returned by the LP");
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
//////////////////////////////
|
|
821
|
-
//// Refund
|
|
822
|
-
|
|
823
|
-
/**
|
|
824
|
-
* Get the estimated smart chain transaction fee of the refund transaction
|
|
825
|
-
*/
|
|
826
|
-
async getRefundNetworkFee(): Promise<TokenAmount<SCToken<T["ChainId"]>, true>> {
|
|
827
|
-
const swapContract: T["Contract"] = this._contract;
|
|
828
|
-
return toTokenAmount(
|
|
829
|
-
await swapContract.getRefundFee(this._getInitiator(), this._data),
|
|
830
|
-
this.wrapper._getNativeToken(),
|
|
831
|
-
this.wrapper._prices
|
|
832
|
-
);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
/**
|
|
836
|
-
* @inheritDoc
|
|
837
|
-
*
|
|
838
|
-
* @throws {IntermediaryError} If intermediary returns invalid response in case cooperative refund should be used
|
|
839
|
-
* @throws {SignatureVerificationError} If intermediary returned invalid cooperative refund signature
|
|
840
|
-
* @throws {Error} When state is not refundable
|
|
841
|
-
*/
|
|
842
|
-
async txsRefund(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
843
|
-
if(!this.isRefundable()) throw new Error("Must be in REFUNDABLE state or expired!");
|
|
844
|
-
|
|
845
|
-
let signer: string;
|
|
846
|
-
if(_signer!=null) {
|
|
847
|
-
if (typeof (_signer) === "string") {
|
|
848
|
-
signer = _signer;
|
|
849
|
-
} else if (isAbstractSigner(_signer)) {
|
|
850
|
-
signer = _signer.getAddress();
|
|
851
|
-
} else {
|
|
852
|
-
signer = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
|
|
853
|
-
}
|
|
854
|
-
} else {
|
|
855
|
-
signer = this._getInitiator();
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
if(await this._contract.isExpired(this._getInitiator(), this._data)) {
|
|
859
|
-
return await this._contract.txsRefund(signer, this._data, true, true);
|
|
860
|
-
} else {
|
|
861
|
-
if(this.url==null) throw new Error("LP URL not known, cannot get cooperative refund message, wait till expiry to refund!");
|
|
862
|
-
const res = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
863
|
-
if(res.code===RefundAuthorizationResponseCodes.REFUND_DATA) {
|
|
864
|
-
return await this._contract.txsRefundWithAuthorization(
|
|
865
|
-
signer,
|
|
866
|
-
this._data,
|
|
867
|
-
res.data,
|
|
868
|
-
true,
|
|
869
|
-
true
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
throw new IntermediaryError("Invalid intermediary cooperative message returned");
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
/**
|
|
877
|
-
* @inheritDoc
|
|
878
|
-
*
|
|
879
|
-
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
880
|
-
*/
|
|
881
|
-
async refund(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal): Promise<string> {
|
|
882
|
-
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
883
|
-
const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsRefund(signer.getAddress()), true, abortSignal)
|
|
884
|
-
|
|
885
|
-
this._refundTxId = result[0];
|
|
886
|
-
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE || this._state===ToBTCSwapState.SOFT_CLAIMED) {
|
|
887
|
-
await this._saveAndEmit(ToBTCSwapState.REFUNDED);
|
|
888
|
-
}
|
|
889
|
-
return result[0];
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* @inheritDoc
|
|
894
|
-
*
|
|
895
|
-
* @throws {Error} When swap is not in a valid state (must be {@link ToBTCSwapState.COMMITED} or
|
|
896
|
-
* {@link ToBTCSwapState.REFUNDABLE})
|
|
897
|
-
* @throws {Error} If we tried to refund but claimer was able to claim first
|
|
898
|
-
*/
|
|
899
|
-
async waitTillRefunded(abortSignal?: AbortSignal): Promise<void> {
|
|
900
|
-
if(this._state===ToBTCSwapState.REFUNDED) return Promise.resolve();
|
|
901
|
-
if(
|
|
902
|
-
this._state!==ToBTCSwapState.COMMITED &&
|
|
903
|
-
this._state!==ToBTCSwapState.SOFT_CLAIMED &&
|
|
904
|
-
this._state!==ToBTCSwapState.REFUNDABLE
|
|
905
|
-
) throw new Error("Invalid state (not COMMITED)");
|
|
906
|
-
|
|
907
|
-
const abortController = new AbortController();
|
|
908
|
-
if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
909
|
-
const res = await Promise.race([
|
|
910
|
-
this.watchdogWaitTillResult(undefined, abortController.signal),
|
|
911
|
-
this.waitTillState(ToBTCSwapState.REFUNDED, "eq", abortController.signal).then(() => 0 as const),
|
|
912
|
-
this.waitTillState(ToBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 1 as const),
|
|
913
|
-
]);
|
|
914
|
-
abortController.abort();
|
|
915
|
-
|
|
916
|
-
if(res===0) {
|
|
917
|
-
this.logger.debug("waitTillRefunded(): Resolved from state change (REFUNDED)");
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
if(res===1) {
|
|
921
|
-
this.logger.debug("waitTillRefunded(): Resolved from state change (CLAIMED)");
|
|
922
|
-
throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
|
|
923
|
-
}
|
|
924
|
-
this.logger.debug("waitTillRefunded(): Resolved from watchdog");
|
|
925
|
-
|
|
926
|
-
if(res?.type===SwapCommitStateType.PAID) {
|
|
927
|
-
if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
|
|
928
|
-
await this._saveAndEmit(ToBTCSwapState.CLAIMED);
|
|
929
|
-
throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
|
|
930
|
-
}
|
|
931
|
-
if(res?.type===SwapCommitStateType.NOT_COMMITED) {
|
|
932
|
-
if(this._refundTxId==null && res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
|
|
933
|
-
await this._saveAndEmit(ToBTCSwapState.REFUNDED);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
//////////////////////////////
|
|
939
|
-
//// Storage
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* @inheritDoc
|
|
943
|
-
*/
|
|
944
|
-
serialize(): any {
|
|
945
|
-
const obj = super.serialize();
|
|
946
|
-
return {
|
|
947
|
-
...obj,
|
|
948
|
-
networkFee: this.networkFee==null ? null : this.networkFee.toString(10),
|
|
949
|
-
networkFeeBtc: this.networkFeeBtc==null ? null : this.networkFeeBtc.toString(10)
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
//////////////////////////////
|
|
955
|
-
//// Swap ticks & sync
|
|
956
|
-
|
|
957
|
-
/**
|
|
958
|
-
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
959
|
-
* data
|
|
960
|
-
*
|
|
961
|
-
* @private
|
|
962
|
-
*/
|
|
963
|
-
private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
964
|
-
if(
|
|
965
|
-
this._state===ToBTCSwapState.CREATED ||
|
|
966
|
-
this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
967
|
-
this._state===ToBTCSwapState.COMMITED ||
|
|
968
|
-
this._state===ToBTCSwapState.SOFT_CLAIMED ||
|
|
969
|
-
this._state===ToBTCSwapState.REFUNDABLE
|
|
970
|
-
) {
|
|
971
|
-
let quoteExpired = false;
|
|
972
|
-
if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
973
|
-
//Check if quote is still valid
|
|
974
|
-
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
978
|
-
if(commitStatus!=null && await this._forciblySetOnchainState(commitStatus)) return true;
|
|
979
|
-
|
|
980
|
-
if((this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED)) {
|
|
981
|
-
if(quoteExpired) {
|
|
982
|
-
this._state = ToBTCSwapState.QUOTE_EXPIRED;
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
return false;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
/**
|
|
991
|
-
* @inheritDoc
|
|
992
|
-
* @internal
|
|
993
|
-
*/
|
|
994
|
-
_shouldFetchOnchainState(): boolean {
|
|
995
|
-
return this._state===ToBTCSwapState.CREATED ||
|
|
996
|
-
this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
997
|
-
this._state===ToBTCSwapState.COMMITED ||
|
|
998
|
-
this._state===ToBTCSwapState.SOFT_CLAIMED ||
|
|
999
|
-
this._state===ToBTCSwapState.REFUNDABLE;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
/**
|
|
1003
|
-
* @inheritDoc
|
|
1004
|
-
* @internal
|
|
1005
|
-
*/
|
|
1006
|
-
_shouldFetchExpiryStatus(): boolean {
|
|
1007
|
-
return this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
/**
|
|
1011
|
-
* @inheritDoc
|
|
1012
|
-
* @internal
|
|
1013
|
-
*/
|
|
1014
|
-
async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
1015
|
-
let changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
|
|
1016
|
-
|
|
1017
|
-
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED) {
|
|
1018
|
-
//Check if that maybe already concluded
|
|
1019
|
-
try {
|
|
1020
|
-
if(await this.checkIntermediarySwapProcessed(false)) changed = true;
|
|
1021
|
-
} catch (e) {
|
|
1022
|
-
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
if(save && changed) await this._saveAndEmit();
|
|
1027
|
-
|
|
1028
|
-
return changed;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
/**
|
|
1032
|
-
* @inheritDoc
|
|
1033
|
-
* @internal
|
|
1034
|
-
*/
|
|
1035
|
-
async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
|
|
1036
|
-
switch(commitStatus.type) {
|
|
1037
|
-
case SwapCommitStateType.PAID:
|
|
1038
|
-
if(this._claimTxId==null && commitStatus.getClaimTxId) this._claimTxId = await commitStatus.getClaimTxId();
|
|
1039
|
-
const eventResult = await commitStatus.getClaimResult();
|
|
1040
|
-
try {
|
|
1041
|
-
await this._setPaymentResult({secret: eventResult, txId: Buffer.from(eventResult, "hex").reverse().toString("hex")});
|
|
1042
|
-
} catch (e) {
|
|
1043
|
-
this.logger.error(`Failed to set payment result ${eventResult} on the swap!`);
|
|
1044
|
-
}
|
|
1045
|
-
this._state = ToBTCSwapState.CLAIMED;
|
|
1046
|
-
return true;
|
|
1047
|
-
case SwapCommitStateType.REFUNDABLE:
|
|
1048
|
-
this._state = ToBTCSwapState.REFUNDABLE;
|
|
1049
|
-
return true;
|
|
1050
|
-
case SwapCommitStateType.EXPIRED:
|
|
1051
|
-
if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1052
|
-
this._state = this._refundTxId==null ? ToBTCSwapState.QUOTE_EXPIRED : ToBTCSwapState.REFUNDED;
|
|
1053
|
-
return true;
|
|
1054
|
-
case SwapCommitStateType.NOT_COMMITED:
|
|
1055
|
-
if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1056
|
-
if(this._refundTxId!=null) {
|
|
1057
|
-
this._state = ToBTCSwapState.REFUNDED;
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
break;
|
|
1061
|
-
case SwapCommitStateType.COMMITED:
|
|
1062
|
-
if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.REFUNDABLE && this._state!==ToBTCSwapState.SOFT_CLAIMED) {
|
|
1063
|
-
this._state = ToBTCSwapState.COMMITED;
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
break;
|
|
1067
|
-
}
|
|
1068
|
-
return false;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
/**
|
|
1072
|
-
* @inheritDoc
|
|
1073
|
-
* @internal
|
|
1074
|
-
*/
|
|
1075
|
-
async _tick(save?: boolean): Promise<boolean> {
|
|
1076
|
-
switch(this._state) {
|
|
1077
|
-
case ToBTCSwapState.CREATED:
|
|
1078
|
-
if(this.expiry<Date.now()) {
|
|
1079
|
-
this._state = ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1080
|
-
if(save) await this._saveAndEmit();
|
|
1081
|
-
return true;
|
|
1082
|
-
}
|
|
1083
|
-
break;
|
|
1084
|
-
case ToBTCSwapState.COMMITED:
|
|
1085
|
-
case ToBTCSwapState.SOFT_CLAIMED:
|
|
1086
|
-
const expired = await this._contract.isExpired(this._getInitiator(), this._data);
|
|
1087
|
-
if(expired) {
|
|
1088
|
-
this._state = ToBTCSwapState.REFUNDABLE;
|
|
1089
|
-
if(save) await this._saveAndEmit();
|
|
1090
|
-
return true;
|
|
1091
|
-
}
|
|
1092
|
-
break;
|
|
1093
|
-
}
|
|
1094
|
-
return false;
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1
|
+
import {IToBTCDefinition, IToBTCWrapper} from "./IToBTCWrapper";
|
|
2
|
+
import {
|
|
3
|
+
ChainType,
|
|
4
|
+
isAbstractSigner,
|
|
5
|
+
SignatureData,
|
|
6
|
+
SignatureVerificationError,
|
|
7
|
+
SwapCommitState,
|
|
8
|
+
SwapCommitStateType,
|
|
9
|
+
SwapData
|
|
10
|
+
} from "@atomiqlabs/base";
|
|
11
|
+
import {
|
|
12
|
+
IntermediaryAPI,
|
|
13
|
+
RefundAuthorizationResponse,
|
|
14
|
+
RefundAuthorizationResponseCodes
|
|
15
|
+
} from "../../../intermediaries/apis/IntermediaryAPI";
|
|
16
|
+
import {IntermediaryError} from "../../../errors/IntermediaryError";
|
|
17
|
+
import {extendAbortController, toBigInt} from "../../../utils/Utils";
|
|
18
|
+
import {Fee} from "../../../types/fees/Fee";
|
|
19
|
+
import {IEscrowSelfInitSwap, IEscrowSelfInitSwapInit, isIEscrowSelfInitSwapInit} from "../IEscrowSelfInitSwap";
|
|
20
|
+
import {IRefundableSwap} from "../../IRefundableSwap";
|
|
21
|
+
import {FeeType} from "../../../enums/FeeType";
|
|
22
|
+
import {ppmToPercentage} from "../../../types/fees/PercentagePPM";
|
|
23
|
+
import {TokenAmount, toTokenAmount} from "../../../types/TokenAmount";
|
|
24
|
+
import {BtcToken, SCToken} from "../../../types/Token";
|
|
25
|
+
import {timeoutPromise} from "../../../utils/TimeoutUtils";
|
|
26
|
+
import {SwapExecutionAction, SwapExecutionActionCommit} from "../../../types/SwapExecutionAction";
|
|
27
|
+
|
|
28
|
+
export type IToBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
|
|
29
|
+
signatureData?: SignatureData,
|
|
30
|
+
data: T,
|
|
31
|
+
networkFee: bigint,
|
|
32
|
+
networkFeeBtc: bigint
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function isIToBTCSwapInit<T extends SwapData>(obj: any): obj is IToBTCSwapInit<T> {
|
|
36
|
+
return typeof(obj.networkFee) === "bigint" &&
|
|
37
|
+
typeof(obj.networkFeeBtc) === "bigint" &&
|
|
38
|
+
(obj.signatureData==null || (
|
|
39
|
+
typeof(obj.signatureData) === 'object' &&
|
|
40
|
+
typeof(obj.signatureData.prefix)==="string" &&
|
|
41
|
+
typeof(obj.signatureData.timeout)==="string" &&
|
|
42
|
+
typeof(obj.signatureData.signature)==="string"
|
|
43
|
+
)) &&
|
|
44
|
+
typeof(obj.data) === 'object' &&
|
|
45
|
+
isIEscrowSelfInitSwapInit<T>(obj);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* State enum for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
|
|
50
|
+
*
|
|
51
|
+
* @category Swaps/Smart chain → Bitcoin
|
|
52
|
+
*/
|
|
53
|
+
export enum ToBTCSwapState {
|
|
54
|
+
/**
|
|
55
|
+
* Intermediary (LP) was unable to process the swap and the funds were refunded on the
|
|
56
|
+
* source chain
|
|
57
|
+
*/
|
|
58
|
+
REFUNDED = -3,
|
|
59
|
+
/**
|
|
60
|
+
* Swap has expired for good and there is no way how it can be executed anymore
|
|
61
|
+
*/
|
|
62
|
+
QUOTE_EXPIRED = -2,
|
|
63
|
+
/**
|
|
64
|
+
* A swap is almost expired, and it should be presented to the user as expired, though
|
|
65
|
+
* there is still a chance that it will be processed
|
|
66
|
+
*/
|
|
67
|
+
QUOTE_SOFT_EXPIRED = -1,
|
|
68
|
+
/**
|
|
69
|
+
* Swap was created, use the {@link IToBTCSwap.commit} or {@link IToBTCSwap.txsCommit} to
|
|
70
|
+
* initiate it by creating the swap escrow on the source chain
|
|
71
|
+
*/
|
|
72
|
+
CREATED = 0,
|
|
73
|
+
/**
|
|
74
|
+
* Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will
|
|
75
|
+
* now process the swap. You can wait till that happens with the {@link IToBTCSwap.waitForPayment}
|
|
76
|
+
* function.
|
|
77
|
+
*/
|
|
78
|
+
COMMITED = 1,
|
|
79
|
+
/**
|
|
80
|
+
* The intermediary (LP) has processed the transaction and sent out the funds on the destination chain,
|
|
81
|
+
* but hasn't yet settled the escrow on the source chain.
|
|
82
|
+
*/
|
|
83
|
+
SOFT_CLAIMED = 2,
|
|
84
|
+
/**
|
|
85
|
+
* Swap was successfully settled by the intermediary (LP) on the source chain
|
|
86
|
+
*/
|
|
87
|
+
CLAIMED = 3,
|
|
88
|
+
/**
|
|
89
|
+
* Intermediary (LP) was unable to process the swap and the swap escrow on the source chain
|
|
90
|
+
* is refundable, call {@link IToBTCSwap.refund} or {@link IToBTCSwap.txsRefund} to refund
|
|
91
|
+
*/
|
|
92
|
+
REFUNDABLE = 4
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const ToBTCSwapStateDescription = {
|
|
96
|
+
[ToBTCSwapState.REFUNDED]: "Intermediary (LP) was unable to process the swap and the funds were refunded on the source chain",
|
|
97
|
+
[ToBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
|
|
98
|
+
[ToBTCSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
|
|
99
|
+
[ToBTCSwapState.CREATED]: "Swap was created, initiate it by creating the swap escrow on the source chain",
|
|
100
|
+
[ToBTCSwapState.COMMITED]: "Swap escrow was initiated (committed) on the source chain, the intermediary (LP) will now process the swap.",
|
|
101
|
+
[ToBTCSwapState.SOFT_CLAIMED]: "The intermediary (LP) has processed the transaction and sent out the funds on the destination chain, but hasn't yet settled the escrow on the source chain.",
|
|
102
|
+
[ToBTCSwapState.CLAIMED]: "Swap was successfully settled by the intermediary (LP) on the source chain",
|
|
103
|
+
[ToBTCSwapState.REFUNDABLE]: "Intermediary (LP) was unable to process the swap and the swap escrow on the source chain is refundable."
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Base class for escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
|
|
108
|
+
*
|
|
109
|
+
* @category Swaps/Smart chain → Bitcoin
|
|
110
|
+
*/
|
|
111
|
+
export abstract class IToBTCSwap<
|
|
112
|
+
T extends ChainType = ChainType,
|
|
113
|
+
D extends IToBTCDefinition<T, IToBTCWrapper<T, D>, IToBTCSwap<T, D>> = IToBTCDefinition<T, IToBTCWrapper<T, any>, IToBTCSwap<T, any>>,
|
|
114
|
+
> extends IEscrowSelfInitSwap<T, D, ToBTCSwapState> implements IRefundableSwap<T, D, ToBTCSwapState> {
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
protected readonly swapStateDescription = ToBTCSwapStateDescription;
|
|
120
|
+
/**
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
protected readonly swapStateName = (state: number) => ToBTCSwapState[state];
|
|
124
|
+
/**
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
protected readonly abstract outputToken: BtcToken;
|
|
128
|
+
/**
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
protected readonly networkFee: bigint;
|
|
132
|
+
/**
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
protected networkFeeBtc: bigint;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
140
|
+
readonly _data!: T["Data"];
|
|
141
|
+
|
|
142
|
+
protected constructor(wrapper: D["Wrapper"], serializedObject: any);
|
|
143
|
+
protected constructor(wrapper: D["Wrapper"], init: IToBTCSwapInit<T["Data"]>);
|
|
144
|
+
protected constructor(
|
|
145
|
+
wrapper: D["Wrapper"],
|
|
146
|
+
initOrObject: IToBTCSwapInit<T["Data"]> | any
|
|
147
|
+
) {
|
|
148
|
+
super(wrapper, initOrObject);
|
|
149
|
+
if(isIToBTCSwapInit<T["Data"]>(initOrObject)) {
|
|
150
|
+
this._state = ToBTCSwapState.CREATED;
|
|
151
|
+
this.networkFee = initOrObject.networkFee;
|
|
152
|
+
this.networkFeeBtc = initOrObject.networkFeeBtc;
|
|
153
|
+
this._data = initOrObject.data;
|
|
154
|
+
this.signatureData = initOrObject.signatureData;
|
|
155
|
+
} else {
|
|
156
|
+
this.networkFee = toBigInt(initOrObject.networkFee);
|
|
157
|
+
this.networkFeeBtc = toBigInt(initOrObject.networkFeeBtc);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @inheritDoc
|
|
163
|
+
* @internal
|
|
164
|
+
*/
|
|
165
|
+
protected getSwapData(): T["Data"] {
|
|
166
|
+
return this._data;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @inheritDoc
|
|
171
|
+
* @internal
|
|
172
|
+
*/
|
|
173
|
+
protected upgradeVersion() {
|
|
174
|
+
if(this.version == null) {
|
|
175
|
+
switch(this._state) {
|
|
176
|
+
case -2:
|
|
177
|
+
this._state = ToBTCSwapState.REFUNDED
|
|
178
|
+
break;
|
|
179
|
+
case -1:
|
|
180
|
+
this._state = ToBTCSwapState.QUOTE_EXPIRED
|
|
181
|
+
break;
|
|
182
|
+
case 0:
|
|
183
|
+
this._state = ToBTCSwapState.CREATED
|
|
184
|
+
break;
|
|
185
|
+
case 1:
|
|
186
|
+
this._state = ToBTCSwapState.COMMITED
|
|
187
|
+
break;
|
|
188
|
+
case 2:
|
|
189
|
+
this._state = ToBTCSwapState.CLAIMED
|
|
190
|
+
break;
|
|
191
|
+
case 3:
|
|
192
|
+
this._state = ToBTCSwapState.REFUNDABLE
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
this.version = 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @inheritDoc
|
|
201
|
+
* @internal
|
|
202
|
+
*/
|
|
203
|
+
protected tryRecomputeSwapPrice() {
|
|
204
|
+
const output = this.getOutput();
|
|
205
|
+
if(output.rawAmount!=null) {
|
|
206
|
+
if(this.swapFeeBtc==null) {
|
|
207
|
+
this.swapFeeBtc = this.swapFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
|
|
208
|
+
}
|
|
209
|
+
if(this.networkFeeBtc==null) {
|
|
210
|
+
this.networkFeeBtc = this.networkFee * output.rawAmount / this.getInputWithoutFee().rawAmount;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
super.tryRecomputeSwapPrice();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Returns the payment hash identifier to be sent to the LP for getStatus and getRefund
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
protected getLpIdentifier(): string {
|
|
221
|
+
return this.getClaimHash();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Sets the payment result for the swap, optionally also checking it (checking that tx exist or swap secret is valid)
|
|
226
|
+
*
|
|
227
|
+
* @param result Result returned by the LP
|
|
228
|
+
* @param check Whether to check the passed result
|
|
229
|
+
* @returns true if check passed, false if check failed with a soft error (e.g. tx not yet found in the mempool)
|
|
230
|
+
* @throws {IntermediaryError} When the data returned by the intermediary isn't valid
|
|
231
|
+
*
|
|
232
|
+
* @internal
|
|
233
|
+
*/
|
|
234
|
+
abstract _setPaymentResult(result: {secret?: string, txId?: string}, check?: boolean): Promise<boolean>;
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
//////////////////////////////
|
|
238
|
+
//// Getters & utils
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @inheritDoc
|
|
242
|
+
*/
|
|
243
|
+
getInputAddress(): string | null {
|
|
244
|
+
return this._getInitiator();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @inheritDoc
|
|
249
|
+
*/
|
|
250
|
+
getInputTxId(): string | null {
|
|
251
|
+
return this._commitTxId ?? null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @inheritDoc
|
|
256
|
+
*/
|
|
257
|
+
requiresAction(): boolean {
|
|
258
|
+
return this.isRefundable();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @inheritDoc
|
|
263
|
+
*/
|
|
264
|
+
isFinished(): boolean {
|
|
265
|
+
return this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.REFUNDED || this._state===ToBTCSwapState.QUOTE_EXPIRED;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @inheritDoc
|
|
270
|
+
*/
|
|
271
|
+
isRefundable(): boolean {
|
|
272
|
+
return this._state===ToBTCSwapState.REFUNDABLE;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @inheritDoc
|
|
277
|
+
*/
|
|
278
|
+
isQuoteExpired(): boolean {
|
|
279
|
+
return this._state===ToBTCSwapState.QUOTE_EXPIRED;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @inheritDoc
|
|
284
|
+
*/
|
|
285
|
+
isQuoteSoftExpired(): boolean {
|
|
286
|
+
return this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @inheritDoc
|
|
291
|
+
*/
|
|
292
|
+
isSuccessful(): boolean {
|
|
293
|
+
return this._state===ToBTCSwapState.CLAIMED;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @inheritDoc
|
|
298
|
+
*/
|
|
299
|
+
isFailed(): boolean {
|
|
300
|
+
return this._state===ToBTCSwapState.REFUNDED;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @inheritDoc
|
|
305
|
+
*/
|
|
306
|
+
isInProgress(): boolean {
|
|
307
|
+
return this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Returns the time (in UNIX milliseconds) at which the swap expires and the user is able to unilaterally
|
|
312
|
+
* refund it with the {@link refund} or {@link txsRefund} function.
|
|
313
|
+
*/
|
|
314
|
+
getExpiry(): number {
|
|
315
|
+
return Number(this._data.getExpiry())*1000;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @inheritDoc
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
_getInitiator(): string {
|
|
323
|
+
return this._data.getOfferer();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
//////////////////////////////
|
|
328
|
+
//// Amounts & fees
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Returns the swap fee charged by the intermediary (LP) on this swap
|
|
332
|
+
*
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
protected getSwapFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
336
|
+
if(this.pricingInfo==null) throw new Error("No pricing info known, cannot estimate fee!");
|
|
337
|
+
|
|
338
|
+
const feeWithoutBaseFee = this.swapFeeBtc - this.pricingInfo.satsBaseFee;
|
|
339
|
+
const output = this.getOutput();
|
|
340
|
+
const swapFeePPM = output.rawAmount==null ? 0n : feeWithoutBaseFee * 1000000n / output.rawAmount;
|
|
341
|
+
|
|
342
|
+
const amountInDstToken = toTokenAmount(
|
|
343
|
+
this.swapFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
344
|
+
);
|
|
345
|
+
return {
|
|
346
|
+
amountInSrcToken: toTokenAmount(this.swapFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
|
|
347
|
+
amountInDstToken,
|
|
348
|
+
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
349
|
+
usdValue: amountInDstToken.usdValue,
|
|
350
|
+
pastUsdValue: amountInDstToken.pastUsdValue,
|
|
351
|
+
composition: {
|
|
352
|
+
base: toTokenAmount(this.pricingInfo.satsBaseFee, this.outputToken, this.wrapper._prices, this.pricingInfo),
|
|
353
|
+
percentage: ppmToPercentage(swapFeePPM)
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Returns network fee for on the destination chain for the swap
|
|
360
|
+
*
|
|
361
|
+
* @internal
|
|
362
|
+
*/
|
|
363
|
+
protected getNetworkFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
364
|
+
const amountInDstToken = toTokenAmount(
|
|
365
|
+
this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
366
|
+
);
|
|
367
|
+
return {
|
|
368
|
+
amountInSrcToken: toTokenAmount(
|
|
369
|
+
this.networkFee, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
|
|
370
|
+
),
|
|
371
|
+
amountInDstToken,
|
|
372
|
+
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
373
|
+
usdValue: amountInDstToken.usdValue,
|
|
374
|
+
pastUsdValue: amountInDstToken.pastUsdValue
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @inheritDoc
|
|
380
|
+
*/
|
|
381
|
+
getFee(): Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken> {
|
|
382
|
+
const amountInDstToken = toTokenAmount(
|
|
383
|
+
this.swapFeeBtc + this.networkFeeBtc, this.outputToken, this.wrapper._prices, this.pricingInfo
|
|
384
|
+
);
|
|
385
|
+
return {
|
|
386
|
+
amountInSrcToken: toTokenAmount(
|
|
387
|
+
this.swapFee + this.networkFee, this.wrapper._tokens[this._data.getToken()],
|
|
388
|
+
this.wrapper._prices, this.pricingInfo
|
|
389
|
+
),
|
|
390
|
+
amountInDstToken,
|
|
391
|
+
currentUsdValue: amountInDstToken.currentUsdValue,
|
|
392
|
+
usdValue: amountInDstToken.usdValue,
|
|
393
|
+
pastUsdValue: amountInDstToken.pastUsdValue
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* @inheritDoc
|
|
399
|
+
*/
|
|
400
|
+
getFeeBreakdown(): [
|
|
401
|
+
{type: FeeType.SWAP, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
|
|
402
|
+
{type: FeeType.NETWORK_OUTPUT, fee: Fee<T["ChainId"], SCToken<T["ChainId"]>, BtcToken>},
|
|
403
|
+
] {
|
|
404
|
+
return [
|
|
405
|
+
{
|
|
406
|
+
type: FeeType.SWAP,
|
|
407
|
+
fee: this.getSwapFee()
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: FeeType.NETWORK_OUTPUT,
|
|
411
|
+
fee: this.getNetworkFee()
|
|
412
|
+
}
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* @inheritDoc
|
|
418
|
+
*/
|
|
419
|
+
getInputToken(): SCToken<T["ChainId"]> {
|
|
420
|
+
return this.wrapper._tokens[this._data.getToken()];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* @inheritDoc
|
|
425
|
+
*/
|
|
426
|
+
getInput(): TokenAmount<SCToken<T["ChainId"]>, true> {
|
|
427
|
+
return toTokenAmount(
|
|
428
|
+
this._data.getAmount(), this.wrapper._tokens[this._data.getToken()],
|
|
429
|
+
this.wrapper._prices, this.pricingInfo
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @inheritDoc
|
|
435
|
+
*/
|
|
436
|
+
getInputWithoutFee(): TokenAmount<SCToken<T["ChainId"]>, true> {
|
|
437
|
+
return toTokenAmount(
|
|
438
|
+
this._data.getAmount() - (this.swapFee + this.networkFee),
|
|
439
|
+
this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Checks if the initiator/sender on the source chain has enough balance to go through with the swap
|
|
445
|
+
*/
|
|
446
|
+
async hasEnoughBalance(): Promise<{
|
|
447
|
+
enoughBalance: boolean,
|
|
448
|
+
balance: TokenAmount<SCToken<T["ChainId"]>, true>,
|
|
449
|
+
required: TokenAmount<SCToken<T["ChainId"]>, true>
|
|
450
|
+
}> {
|
|
451
|
+
const [balance, commitFee] = await Promise.all([
|
|
452
|
+
this._contract.getBalance(this._getInitiator(), this._data.getToken(), false),
|
|
453
|
+
this._data.getToken()===this.wrapper._chain.getNativeCurrencyAddress() ? this.getCommitFee() : Promise.resolve(null)
|
|
454
|
+
]);
|
|
455
|
+
let required = this._data.getAmount();
|
|
456
|
+
if(commitFee!=null) required = required + commitFee;
|
|
457
|
+
return {
|
|
458
|
+
enoughBalance: balance >= required,
|
|
459
|
+
balance: toTokenAmount(balance, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo),
|
|
460
|
+
required: toTokenAmount(required, this.wrapper._tokens[this._data.getToken()], this.wrapper._prices, this.pricingInfo)
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Checks if the initiator/sender on the source chain has enough native token balance
|
|
466
|
+
* to cover the transaction fee of initiating the swap
|
|
467
|
+
*/
|
|
468
|
+
async hasEnoughForTxFees(): Promise<{
|
|
469
|
+
enoughBalance: boolean,
|
|
470
|
+
balance: TokenAmount<SCToken<T["ChainId"]>, true>,
|
|
471
|
+
required: TokenAmount<SCToken<T["ChainId"]>, true>
|
|
472
|
+
}> {
|
|
473
|
+
const [balance, commitFee] = await Promise.all([
|
|
474
|
+
this._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
|
|
475
|
+
this.getCommitFee()
|
|
476
|
+
]);
|
|
477
|
+
return {
|
|
478
|
+
enoughBalance: balance >= commitFee,
|
|
479
|
+
balance: toTokenAmount(balance, this.wrapper._getNativeToken(), this.wrapper._prices),
|
|
480
|
+
required: toTokenAmount(commitFee, this.wrapper._getNativeToken(), this.wrapper._prices)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
//////////////////////////////
|
|
486
|
+
//// Execution
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Executes the swap with the provided smart chain wallet/signer
|
|
490
|
+
*
|
|
491
|
+
* @param signer Smart chain wallet/signer to use to sign the transaction on the source chain
|
|
492
|
+
* @param callbacks Callbacks to track the progress of the swap
|
|
493
|
+
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
494
|
+
*
|
|
495
|
+
* @returns {boolean} Whether the swap was successfully processed by the LP, in case `false` is returned
|
|
496
|
+
* the user can refund their funds back on the source chain by calling {@link refund}
|
|
497
|
+
*/
|
|
498
|
+
async execute(
|
|
499
|
+
signer: T["Signer"] | T["NativeSigner"],
|
|
500
|
+
callbacks?: {
|
|
501
|
+
onSourceTransactionSent?: (sourceTxId: string) => void,
|
|
502
|
+
onSourceTransactionConfirmed?: (sourceTxId: string) => void,
|
|
503
|
+
onSwapSettled?: (destinationTxId: string) => void
|
|
504
|
+
},
|
|
505
|
+
options?: {
|
|
506
|
+
abortSignal?: AbortSignal,
|
|
507
|
+
paymentCheckIntervalSeconds?: number,
|
|
508
|
+
maxWaitTillSwapProcessedSeconds?: number
|
|
509
|
+
}
|
|
510
|
+
): Promise<boolean> {
|
|
511
|
+
if(this._state===ToBTCSwapState.QUOTE_EXPIRED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Quote expired");
|
|
512
|
+
if(this._state===ToBTCSwapState.REFUNDED) throw new Error("Swap already refunded");
|
|
513
|
+
if(this._state===ToBTCSwapState.REFUNDABLE) throw new Error("Swap refundable, refund with swap.refund()");
|
|
514
|
+
if(this._state===ToBTCSwapState.SOFT_CLAIMED || this._state===ToBTCSwapState.CLAIMED) throw new Error("Swap already settled!");
|
|
515
|
+
|
|
516
|
+
if(this._state===ToBTCSwapState.CREATED) {
|
|
517
|
+
const txId = await this.commit(signer, options?.abortSignal, false, callbacks?.onSourceTransactionSent);
|
|
518
|
+
if(callbacks?.onSourceTransactionConfirmed!=null) callbacks.onSourceTransactionConfirmed(txId);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// @ts-ignore
|
|
522
|
+
if(this._state===ToBTCSwapState.CLAIMED || this._state===ToBTCSwapState.SOFT_CLAIMED) return true;
|
|
523
|
+
|
|
524
|
+
if(this._state===ToBTCSwapState.COMMITED) {
|
|
525
|
+
const success = await this.waitForPayment(options?.maxWaitTillSwapProcessedSeconds ?? 120, options?.paymentCheckIntervalSeconds, options?.abortSignal);
|
|
526
|
+
if(success) {
|
|
527
|
+
if(callbacks?.onSwapSettled!=null) callbacks.onSwapSettled(this.getOutputTxId()!);
|
|
528
|
+
return true;
|
|
529
|
+
} else {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
throw new Error("Unexpected state reached!");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* @inheritDoc
|
|
539
|
+
*
|
|
540
|
+
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
541
|
+
* (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
|
|
542
|
+
*/
|
|
543
|
+
async txsExecute(options?: {
|
|
544
|
+
skipChecks?: boolean
|
|
545
|
+
}): Promise<[
|
|
546
|
+
SwapExecutionActionCommit<T>
|
|
547
|
+
]> {
|
|
548
|
+
if(this._state!==ToBTCSwapState.CREATED) throw new Error("Invalid swap state, needs to be CREATED!");
|
|
549
|
+
const txsCommit = await this.txsCommit(options?.skipChecks);
|
|
550
|
+
return [
|
|
551
|
+
{
|
|
552
|
+
name: "Commit" as const,
|
|
553
|
+
description: `Initiates the swap by commiting the funds to the escrow on the ${this.chainIdentifier} side`,
|
|
554
|
+
chain: this.chainIdentifier,
|
|
555
|
+
txs: txsCommit
|
|
556
|
+
}
|
|
557
|
+
];
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @inheritDoc
|
|
562
|
+
*
|
|
563
|
+
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
564
|
+
* (this is handled on swap creation, if you commit right after quoting, you can use `skipChecks=true`)
|
|
565
|
+
* @param options.refundSmartChainSigner Optional smart chain signer to use when creating refunds transactions
|
|
566
|
+
*/
|
|
567
|
+
async getCurrentActions(options?: {
|
|
568
|
+
skipChecks?: boolean,
|
|
569
|
+
refundSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
|
|
570
|
+
}): Promise<SwapExecutionAction<T>[]> {
|
|
571
|
+
if(this._state===ToBTCSwapState.CREATED) {
|
|
572
|
+
try {
|
|
573
|
+
return await this.txsExecute(options);
|
|
574
|
+
} catch (e) {}
|
|
575
|
+
}
|
|
576
|
+
if(this.isRefundable()) {
|
|
577
|
+
return [{
|
|
578
|
+
name: "Refund" as const,
|
|
579
|
+
description: "Refund the swap after it failed to execute",
|
|
580
|
+
chain: this.chainIdentifier,
|
|
581
|
+
txs: await this.txsRefund(options?.refundSmartChainSigner)
|
|
582
|
+
}];
|
|
583
|
+
}
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
//////////////////////////////
|
|
588
|
+
//// Commit
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* @inheritDoc
|
|
592
|
+
*
|
|
593
|
+
* @throws {Error} When in invalid state (not {@link ToBTCSwapState.CREATED})
|
|
594
|
+
*/
|
|
595
|
+
async txsCommit(skipChecks?: boolean): Promise<T["TX"][]> {
|
|
596
|
+
if(this._state!==ToBTCSwapState.CREATED && (!skipChecks || this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED)) throw new Error("Must be in CREATED state!");
|
|
597
|
+
if(this.signatureData==null) throw new Error("Init signature data not known, cannot commit!");
|
|
598
|
+
|
|
599
|
+
if(!this.initiated) {
|
|
600
|
+
this.initiated = true;
|
|
601
|
+
await this._saveAndEmit();
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return await this._contract.txsInit(
|
|
605
|
+
this._getInitiator(), this._data, this.signatureData, skipChecks, this.feeRate
|
|
606
|
+
).catch(e => Promise.reject(e instanceof SignatureVerificationError ? new Error("Request timed out") : e));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* @inheritDoc
|
|
611
|
+
*
|
|
612
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
613
|
+
*/
|
|
614
|
+
async commit(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal, skipChecks?: boolean, onBeforeTxSent?: (txId: string) => void): Promise<string> {
|
|
615
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
616
|
+
this.checkSigner(signer);
|
|
617
|
+
const txs = await this.txsCommit(skipChecks);
|
|
618
|
+
let txCount = 0;
|
|
619
|
+
const result = await this.wrapper._chain.sendAndConfirm(
|
|
620
|
+
signer, txs, true, abortSignal, false, (txId, rawTx) => {
|
|
621
|
+
txCount++;
|
|
622
|
+
if(onBeforeTxSent!=null && txCount===txs.length) onBeforeTxSent(txId);
|
|
623
|
+
return Promise.resolve();
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
this._commitTxId = result[result.length-1];
|
|
628
|
+
if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
|
|
629
|
+
await this._saveAndEmit(ToBTCSwapState.COMMITED);
|
|
630
|
+
}
|
|
631
|
+
return this._commitTxId;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* @inheritDoc
|
|
636
|
+
*
|
|
637
|
+
* @throws {Error} If swap is not in the correct state (must be {@link ToBTCSwapState.CREATED})
|
|
638
|
+
*/
|
|
639
|
+
async waitTillCommited(abortSignal?: AbortSignal): Promise<void> {
|
|
640
|
+
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.CLAIMED) return Promise.resolve();
|
|
641
|
+
if(this._state!==ToBTCSwapState.CREATED && this._state!==ToBTCSwapState.QUOTE_SOFT_EXPIRED) throw new Error("Invalid state (not CREATED)");
|
|
642
|
+
|
|
643
|
+
const abortController = extendAbortController(abortSignal);
|
|
644
|
+
let result: number | boolean;
|
|
645
|
+
try {
|
|
646
|
+
result = await Promise.race([
|
|
647
|
+
this.watchdogWaitTillCommited(undefined, abortController.signal),
|
|
648
|
+
this.waitTillState(ToBTCSwapState.COMMITED, "gte", abortController.signal).then(() => 0)
|
|
649
|
+
]);
|
|
650
|
+
abortController.abort();
|
|
651
|
+
} catch (e) {
|
|
652
|
+
abortController.abort();
|
|
653
|
+
throw e;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if(result===0) this.logger.debug("waitTillCommited(): Resolved from state change");
|
|
657
|
+
if(result===true) this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
658
|
+
if(result===false) {
|
|
659
|
+
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expiry");
|
|
660
|
+
if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED) {
|
|
661
|
+
await this._saveAndEmit(ToBTCSwapState.QUOTE_EXPIRED);
|
|
662
|
+
}
|
|
663
|
+
throw new Error("Quote expired while waiting for transaction confirmation!");
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if(this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED || this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_EXPIRED) {
|
|
667
|
+
await this._saveAndEmit(ToBTCSwapState.COMMITED);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
//////////////////////////////
|
|
673
|
+
//// Payment
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Waits till the swap is processed by the intermediary (LP)
|
|
677
|
+
*
|
|
678
|
+
* @param checkIntervalSeconds How often to poll the intermediary for status (5 seconds default)
|
|
679
|
+
* @param abortSignal Abort signal
|
|
680
|
+
* @internal
|
|
681
|
+
*/
|
|
682
|
+
protected async waitTillIntermediarySwapProcessed(
|
|
683
|
+
checkIntervalSeconds?: number,
|
|
684
|
+
abortSignal?: AbortSignal
|
|
685
|
+
): Promise<RefundAuthorizationResponse> {
|
|
686
|
+
if(this.url==null) throw new Error("LP URL not specified!");
|
|
687
|
+
checkIntervalSeconds ??= 5;
|
|
688
|
+
let resp: RefundAuthorizationResponse = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
|
|
689
|
+
while(!abortSignal?.aborted && (
|
|
690
|
+
resp.code===RefundAuthorizationResponseCodes.PENDING || resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
|
|
691
|
+
)) {
|
|
692
|
+
resp = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
693
|
+
if(resp.code===RefundAuthorizationResponseCodes.PAID) {
|
|
694
|
+
const validResponse = await this._setPaymentResult(resp.data, true);
|
|
695
|
+
if(validResponse) {
|
|
696
|
+
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE) {
|
|
697
|
+
await this._saveAndEmit(ToBTCSwapState.SOFT_CLAIMED);
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
resp = {code: RefundAuthorizationResponseCodes.PENDING, msg: ""};
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if(
|
|
704
|
+
resp.code===RefundAuthorizationResponseCodes.PENDING ||
|
|
705
|
+
resp.code===RefundAuthorizationResponseCodes.NOT_FOUND
|
|
706
|
+
) await timeoutPromise(checkIntervalSeconds*1000, abortSignal);
|
|
707
|
+
}
|
|
708
|
+
return resp;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Checks whether the swap was already processed by the LP and is either successful (requires proof which is
|
|
713
|
+
* either a HTLC pre-image for LN swaps or valid txId for on-chain swap) or failed and we can cooperatively
|
|
714
|
+
* refund.
|
|
715
|
+
*
|
|
716
|
+
* @param save whether to save the data
|
|
717
|
+
* @returns `true` if swap is processed, `false` if the swap is still ongoing
|
|
718
|
+
*
|
|
719
|
+
* @internal
|
|
720
|
+
*/
|
|
721
|
+
protected async checkIntermediarySwapProcessed(save: boolean = true): Promise<boolean> {
|
|
722
|
+
if(this._state===ToBTCSwapState.CREATED || this._state==ToBTCSwapState.QUOTE_EXPIRED || this.url==null) return false;
|
|
723
|
+
if(this.isFinished() || this.isRefundable()) return true;
|
|
724
|
+
//Check if that maybe already concluded according to the LP
|
|
725
|
+
const resp = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
726
|
+
switch(resp.code) {
|
|
727
|
+
case RefundAuthorizationResponseCodes.PAID:
|
|
728
|
+
const processed = await this._setPaymentResult(resp.data, true);
|
|
729
|
+
if(processed) {
|
|
730
|
+
this._state = ToBTCSwapState.SOFT_CLAIMED;
|
|
731
|
+
if(save) await this._saveAndEmit();
|
|
732
|
+
}
|
|
733
|
+
return processed;
|
|
734
|
+
case RefundAuthorizationResponseCodes.REFUND_DATA:
|
|
735
|
+
await this._contract.isValidRefundAuthorization(this._data, resp.data);
|
|
736
|
+
this._state = ToBTCSwapState.REFUNDABLE;
|
|
737
|
+
if(save) await this._saveAndEmit();
|
|
738
|
+
return true;
|
|
739
|
+
default:
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* A blocking promise resolving when swap was concluded by the intermediary (LP),
|
|
746
|
+
* rejecting in case of failure
|
|
747
|
+
*
|
|
748
|
+
* @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled, an error is thrown if the
|
|
749
|
+
* swap is taking too long to claim
|
|
750
|
+
* @param checkIntervalSeconds How often to poll the intermediary for answer
|
|
751
|
+
* @param abortSignal Abort signal
|
|
752
|
+
* @returns `true` if swap was successful, `false` if swap failed and we can refund
|
|
753
|
+
*
|
|
754
|
+
* @throws {IntermediaryError} If a swap is determined expired by the intermediary, but it is actually still valid
|
|
755
|
+
* @throws {SignatureVerificationError} If the swap should be cooperatively refundable but the intermediary returned
|
|
756
|
+
* invalid refund signature
|
|
757
|
+
* @throws {Error} When swap expires or if the swap has invalid state (must be {@link ToBTCSwapState.COMMITED})
|
|
758
|
+
*/
|
|
759
|
+
async waitForPayment(maxWaitTimeSeconds?: number, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
|
|
760
|
+
if(this._state===ToBTCSwapState.CLAIMED) return Promise.resolve(true);
|
|
761
|
+
if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.SOFT_CLAIMED) throw new Error("Invalid state (not COMMITED)");
|
|
762
|
+
|
|
763
|
+
const abortController = extendAbortController(abortSignal);
|
|
764
|
+
|
|
765
|
+
let timedOut: boolean = false;
|
|
766
|
+
if(maxWaitTimeSeconds!=null) {
|
|
767
|
+
const timeout = setTimeout(() => {
|
|
768
|
+
timedOut = true;
|
|
769
|
+
abortController.abort();
|
|
770
|
+
}, maxWaitTimeSeconds * 1000);
|
|
771
|
+
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
let result: void | RefundAuthorizationResponse;
|
|
775
|
+
try {
|
|
776
|
+
result = await Promise.race([
|
|
777
|
+
this.waitTillState(ToBTCSwapState.CLAIMED, "gte", abortController.signal),
|
|
778
|
+
this.waitTillIntermediarySwapProcessed(checkIntervalSeconds, abortController.signal)
|
|
779
|
+
]);
|
|
780
|
+
abortController.abort();
|
|
781
|
+
} catch (e) {
|
|
782
|
+
abortController.abort();
|
|
783
|
+
if(timedOut) {
|
|
784
|
+
throw new Error("Timed out while waiting for LP to process the swap, the LP might be unresponsive or offline!" +
|
|
785
|
+
` Please check later or wait till ${new Date(Number(this._data.getExpiry())*1000).toLocaleString()} to refund unilaterally!`);
|
|
786
|
+
}
|
|
787
|
+
throw e;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if(typeof result !== "object") {
|
|
791
|
+
if((this._state as ToBTCSwapState)===ToBTCSwapState.REFUNDABLE) throw new Error("Swap expired");
|
|
792
|
+
this.logger.debug("waitTillRefunded(): Resolved from state change");
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
this.logger.debug("waitTillRefunded(): Resolved from intermediary response");
|
|
796
|
+
|
|
797
|
+
switch(result.code) {
|
|
798
|
+
case RefundAuthorizationResponseCodes.PAID:
|
|
799
|
+
return true;
|
|
800
|
+
case RefundAuthorizationResponseCodes.REFUND_DATA:
|
|
801
|
+
const resultData = result.data;
|
|
802
|
+
await this._contract.isValidRefundAuthorization(
|
|
803
|
+
this._data,
|
|
804
|
+
resultData
|
|
805
|
+
);
|
|
806
|
+
await this._saveAndEmit(ToBTCSwapState.REFUNDABLE);
|
|
807
|
+
return false;
|
|
808
|
+
case RefundAuthorizationResponseCodes.EXPIRED:
|
|
809
|
+
if(await this._contract.isExpired(this._getInitiator(), this._data)) throw new Error("Swap expired");
|
|
810
|
+
throw new IntermediaryError("Swap expired");
|
|
811
|
+
case RefundAuthorizationResponseCodes.NOT_FOUND:
|
|
812
|
+
if((this._state as ToBTCSwapState)===ToBTCSwapState.CLAIMED) return true;
|
|
813
|
+
throw new Error("LP swap not found");
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
throw new Error("Invalid response code returned by the LP");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
//////////////////////////////
|
|
821
|
+
//// Refund
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Get the estimated smart chain transaction fee of the refund transaction
|
|
825
|
+
*/
|
|
826
|
+
async getRefundNetworkFee(): Promise<TokenAmount<SCToken<T["ChainId"]>, true>> {
|
|
827
|
+
const swapContract: T["Contract"] = this._contract;
|
|
828
|
+
return toTokenAmount(
|
|
829
|
+
await swapContract.getRefundFee(this._getInitiator(), this._data),
|
|
830
|
+
this.wrapper._getNativeToken(),
|
|
831
|
+
this.wrapper._prices
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* @inheritDoc
|
|
837
|
+
*
|
|
838
|
+
* @throws {IntermediaryError} If intermediary returns invalid response in case cooperative refund should be used
|
|
839
|
+
* @throws {SignatureVerificationError} If intermediary returned invalid cooperative refund signature
|
|
840
|
+
* @throws {Error} When state is not refundable
|
|
841
|
+
*/
|
|
842
|
+
async txsRefund(_signer?: string | T["Signer"] | T["NativeSigner"]): Promise<T["TX"][]> {
|
|
843
|
+
if(!this.isRefundable()) throw new Error("Must be in REFUNDABLE state or expired!");
|
|
844
|
+
|
|
845
|
+
let signer: string;
|
|
846
|
+
if(_signer!=null) {
|
|
847
|
+
if (typeof (_signer) === "string") {
|
|
848
|
+
signer = _signer;
|
|
849
|
+
} else if (isAbstractSigner(_signer)) {
|
|
850
|
+
signer = _signer.getAddress();
|
|
851
|
+
} else {
|
|
852
|
+
signer = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
signer = this._getInitiator();
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if(await this._contract.isExpired(this._getInitiator(), this._data)) {
|
|
859
|
+
return await this._contract.txsRefund(signer, this._data, true, true);
|
|
860
|
+
} else {
|
|
861
|
+
if(this.url==null) throw new Error("LP URL not known, cannot get cooperative refund message, wait till expiry to refund!");
|
|
862
|
+
const res = await IntermediaryAPI.getRefundAuthorization(this.url, this.getLpIdentifier(), this._data.getSequence());
|
|
863
|
+
if(res.code===RefundAuthorizationResponseCodes.REFUND_DATA) {
|
|
864
|
+
return await this._contract.txsRefundWithAuthorization(
|
|
865
|
+
signer,
|
|
866
|
+
this._data,
|
|
867
|
+
res.data,
|
|
868
|
+
true,
|
|
869
|
+
true
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
throw new IntermediaryError("Invalid intermediary cooperative message returned");
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* @inheritDoc
|
|
878
|
+
*
|
|
879
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
880
|
+
*/
|
|
881
|
+
async refund(_signer: T["Signer"] | T["NativeSigner"], abortSignal?: AbortSignal): Promise<string> {
|
|
882
|
+
const signer = isAbstractSigner(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
883
|
+
const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsRefund(signer.getAddress()), true, abortSignal)
|
|
884
|
+
|
|
885
|
+
this._refundTxId = result[0];
|
|
886
|
+
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.REFUNDABLE || this._state===ToBTCSwapState.SOFT_CLAIMED) {
|
|
887
|
+
await this._saveAndEmit(ToBTCSwapState.REFUNDED);
|
|
888
|
+
}
|
|
889
|
+
return result[0];
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* @inheritDoc
|
|
894
|
+
*
|
|
895
|
+
* @throws {Error} When swap is not in a valid state (must be {@link ToBTCSwapState.COMMITED} or
|
|
896
|
+
* {@link ToBTCSwapState.REFUNDABLE})
|
|
897
|
+
* @throws {Error} If we tried to refund but claimer was able to claim first
|
|
898
|
+
*/
|
|
899
|
+
async waitTillRefunded(abortSignal?: AbortSignal): Promise<void> {
|
|
900
|
+
if(this._state===ToBTCSwapState.REFUNDED) return Promise.resolve();
|
|
901
|
+
if(
|
|
902
|
+
this._state!==ToBTCSwapState.COMMITED &&
|
|
903
|
+
this._state!==ToBTCSwapState.SOFT_CLAIMED &&
|
|
904
|
+
this._state!==ToBTCSwapState.REFUNDABLE
|
|
905
|
+
) throw new Error("Invalid state (not COMMITED)");
|
|
906
|
+
|
|
907
|
+
const abortController = new AbortController();
|
|
908
|
+
if(abortSignal!=null) abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
909
|
+
const res = await Promise.race([
|
|
910
|
+
this.watchdogWaitTillResult(undefined, abortController.signal),
|
|
911
|
+
this.waitTillState(ToBTCSwapState.REFUNDED, "eq", abortController.signal).then(() => 0 as const),
|
|
912
|
+
this.waitTillState(ToBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 1 as const),
|
|
913
|
+
]);
|
|
914
|
+
abortController.abort();
|
|
915
|
+
|
|
916
|
+
if(res===0) {
|
|
917
|
+
this.logger.debug("waitTillRefunded(): Resolved from state change (REFUNDED)");
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if(res===1) {
|
|
921
|
+
this.logger.debug("waitTillRefunded(): Resolved from state change (CLAIMED)");
|
|
922
|
+
throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
|
|
923
|
+
}
|
|
924
|
+
this.logger.debug("waitTillRefunded(): Resolved from watchdog");
|
|
925
|
+
|
|
926
|
+
if(res?.type===SwapCommitStateType.PAID) {
|
|
927
|
+
if(this._claimTxId==null) this._claimTxId = await res.getClaimTxId();
|
|
928
|
+
await this._saveAndEmit(ToBTCSwapState.CLAIMED);
|
|
929
|
+
throw new Error("Tried to refund swap, but claimer claimed it in the meantime!");
|
|
930
|
+
}
|
|
931
|
+
if(res?.type===SwapCommitStateType.NOT_COMMITED) {
|
|
932
|
+
if(this._refundTxId==null && res.getRefundTxId!=null) this._refundTxId = await res.getRefundTxId();
|
|
933
|
+
await this._saveAndEmit(ToBTCSwapState.REFUNDED);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
//////////////////////////////
|
|
939
|
+
//// Storage
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* @inheritDoc
|
|
943
|
+
*/
|
|
944
|
+
serialize(): any {
|
|
945
|
+
const obj = super.serialize();
|
|
946
|
+
return {
|
|
947
|
+
...obj,
|
|
948
|
+
networkFee: this.networkFee==null ? null : this.networkFee.toString(10),
|
|
949
|
+
networkFeeBtc: this.networkFeeBtc==null ? null : this.networkFeeBtc.toString(10)
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
//////////////////////////////
|
|
955
|
+
//// Swap ticks & sync
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
959
|
+
* data
|
|
960
|
+
*
|
|
961
|
+
* @private
|
|
962
|
+
*/
|
|
963
|
+
private async syncStateFromChain(quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
964
|
+
if(
|
|
965
|
+
this._state===ToBTCSwapState.CREATED ||
|
|
966
|
+
this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
967
|
+
this._state===ToBTCSwapState.COMMITED ||
|
|
968
|
+
this._state===ToBTCSwapState.SOFT_CLAIMED ||
|
|
969
|
+
this._state===ToBTCSwapState.REFUNDABLE
|
|
970
|
+
) {
|
|
971
|
+
let quoteExpired = false;
|
|
972
|
+
if(this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
|
|
973
|
+
//Check if quote is still valid
|
|
974
|
+
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
commitStatus ??= await this._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
978
|
+
if(commitStatus!=null && await this._forciblySetOnchainState(commitStatus)) return true;
|
|
979
|
+
|
|
980
|
+
if((this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED)) {
|
|
981
|
+
if(quoteExpired) {
|
|
982
|
+
this._state = ToBTCSwapState.QUOTE_EXPIRED;
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* @inheritDoc
|
|
992
|
+
* @internal
|
|
993
|
+
*/
|
|
994
|
+
_shouldFetchOnchainState(): boolean {
|
|
995
|
+
return this._state===ToBTCSwapState.CREATED ||
|
|
996
|
+
this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED ||
|
|
997
|
+
this._state===ToBTCSwapState.COMMITED ||
|
|
998
|
+
this._state===ToBTCSwapState.SOFT_CLAIMED ||
|
|
999
|
+
this._state===ToBTCSwapState.REFUNDABLE;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* @inheritDoc
|
|
1004
|
+
* @internal
|
|
1005
|
+
*/
|
|
1006
|
+
_shouldFetchExpiryStatus(): boolean {
|
|
1007
|
+
return this._state===ToBTCSwapState.CREATED || this._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* @inheritDoc
|
|
1012
|
+
* @internal
|
|
1013
|
+
*/
|
|
1014
|
+
async _sync(save?: boolean, quoteDefinitelyExpired?: boolean, commitStatus?: SwapCommitState): Promise<boolean> {
|
|
1015
|
+
let changed = await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus);
|
|
1016
|
+
|
|
1017
|
+
if(this._state===ToBTCSwapState.COMMITED || this._state===ToBTCSwapState.SOFT_CLAIMED) {
|
|
1018
|
+
//Check if that maybe already concluded
|
|
1019
|
+
try {
|
|
1020
|
+
if(await this.checkIntermediarySwapProcessed(false)) changed = true;
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
if(save && changed) await this._saveAndEmit();
|
|
1027
|
+
|
|
1028
|
+
return changed;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* @inheritDoc
|
|
1033
|
+
* @internal
|
|
1034
|
+
*/
|
|
1035
|
+
async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
|
|
1036
|
+
switch(commitStatus.type) {
|
|
1037
|
+
case SwapCommitStateType.PAID:
|
|
1038
|
+
if(this._claimTxId==null && commitStatus.getClaimTxId) this._claimTxId = await commitStatus.getClaimTxId();
|
|
1039
|
+
const eventResult = await commitStatus.getClaimResult();
|
|
1040
|
+
try {
|
|
1041
|
+
await this._setPaymentResult({secret: eventResult, txId: Buffer.from(eventResult, "hex").reverse().toString("hex")});
|
|
1042
|
+
} catch (e) {
|
|
1043
|
+
this.logger.error(`Failed to set payment result ${eventResult} on the swap!`);
|
|
1044
|
+
}
|
|
1045
|
+
this._state = ToBTCSwapState.CLAIMED;
|
|
1046
|
+
return true;
|
|
1047
|
+
case SwapCommitStateType.REFUNDABLE:
|
|
1048
|
+
this._state = ToBTCSwapState.REFUNDABLE;
|
|
1049
|
+
return true;
|
|
1050
|
+
case SwapCommitStateType.EXPIRED:
|
|
1051
|
+
if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1052
|
+
this._state = this._refundTxId==null ? ToBTCSwapState.QUOTE_EXPIRED : ToBTCSwapState.REFUNDED;
|
|
1053
|
+
return true;
|
|
1054
|
+
case SwapCommitStateType.NOT_COMMITED:
|
|
1055
|
+
if(this._refundTxId==null && commitStatus.getRefundTxId) this._refundTxId = await commitStatus.getRefundTxId();
|
|
1056
|
+
if(this._refundTxId!=null) {
|
|
1057
|
+
this._state = ToBTCSwapState.REFUNDED;
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
break;
|
|
1061
|
+
case SwapCommitStateType.COMMITED:
|
|
1062
|
+
if(this._state!==ToBTCSwapState.COMMITED && this._state!==ToBTCSwapState.REFUNDABLE && this._state!==ToBTCSwapState.SOFT_CLAIMED) {
|
|
1063
|
+
this._state = ToBTCSwapState.COMMITED;
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* @inheritDoc
|
|
1073
|
+
* @internal
|
|
1074
|
+
*/
|
|
1075
|
+
async _tick(save?: boolean): Promise<boolean> {
|
|
1076
|
+
switch(this._state) {
|
|
1077
|
+
case ToBTCSwapState.CREATED:
|
|
1078
|
+
if(this.expiry<Date.now()) {
|
|
1079
|
+
this._state = ToBTCSwapState.QUOTE_SOFT_EXPIRED;
|
|
1080
|
+
if(save) await this._saveAndEmit();
|
|
1081
|
+
return true;
|
|
1082
|
+
}
|
|
1083
|
+
break;
|
|
1084
|
+
case ToBTCSwapState.COMMITED:
|
|
1085
|
+
case ToBTCSwapState.SOFT_CLAIMED:
|
|
1086
|
+
const expired = await this._contract.isExpired(this._getInitiator(), this._data);
|
|
1087
|
+
if(expired) {
|
|
1088
|
+
this._state = ToBTCSwapState.REFUNDABLE;
|
|
1089
|
+
if(save) await this._saveAndEmit();
|
|
1090
|
+
return true;
|
|
1091
|
+
}
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
return false;
|
|
1095
|
+
}
|
|
1096
|
+
}
|