@atomiqlabs/sdk 8.8.3 → 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 -7
- package/dist/bitcoin/coinselect2/accumulative.js +52 -52
- package/dist/bitcoin/coinselect2/blackjack.d.ts +7 -7
- package/dist/bitcoin/coinselect2/blackjack.js +38 -38
- package/dist/bitcoin/coinselect2/index.d.ts +20 -20
- package/dist/bitcoin/coinselect2/index.js +69 -69
- package/dist/bitcoin/coinselect2/utils.d.ts +82 -82
- package/dist/bitcoin/coinselect2/utils.js +158 -158
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +113 -113
- package/dist/bitcoin/wallet/BitcoinWallet.js +335 -335
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +116 -116
- package/dist/bitcoin/wallet/IBitcoinWallet.js +21 -21
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +106 -106
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +196 -196
- 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 -450
- package/dist/intermediaries/apis/IntermediaryAPI.js +618 -618
- 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 -732
- package/dist/swapper/Swapper.js +1713 -1713
- 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 -637
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1448 -1448
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +257 -257
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -947
- 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 -16
- package/dist/utils/BitcoinUtils.js +141 -141
- 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 -69
- package/src/bitcoin/coinselect2/blackjack.ts +50 -50
- package/src/bitcoin/coinselect2/index.ts +93 -93
- package/src/bitcoin/coinselect2/utils.ts +236 -236
- package/src/bitcoin/wallet/BitcoinWallet.ts +439 -439
- package/src/bitcoin/wallet/IBitcoinWallet.ts +140 -140
- package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +225 -225
- 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 -963
- 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 -2488
- 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 -1812
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1236 -1236
- 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 -132
- 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
package/src/swapper/Swapper.ts
CHANGED
|
@@ -1,2488 +1,2488 @@
|
|
|
1
|
-
import {ISwapPrice} from "../prices/abstract/ISwapPrice";
|
|
2
|
-
import {
|
|
3
|
-
BitcoinNetwork, BitcoinRpc, BitcoinRpcWithAddressIndex, BtcBlock,
|
|
4
|
-
BtcRelay,
|
|
5
|
-
ChainData,
|
|
6
|
-
ChainSwapType,
|
|
7
|
-
ChainType, LightningNetworkApi,
|
|
8
|
-
Messenger,
|
|
9
|
-
RelaySynchronizer, SpvWithdrawalClaimedState, SpvWithdrawalFrontedState, SwapCommitState, SwapContract, SwapData
|
|
10
|
-
} from "@atomiqlabs/base";
|
|
11
|
-
import {
|
|
12
|
-
ToBTCLNOptions,
|
|
13
|
-
ToBTCLNWrapper
|
|
14
|
-
} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper";
|
|
15
|
-
import {ToBTCOptions, ToBTCWrapper} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper";
|
|
16
|
-
import {FromBTCLNOptions, FromBTCLNWrapper} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper";
|
|
17
|
-
import {FromBTCOptions, FromBTCWrapper} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper";
|
|
18
|
-
import {IntermediaryDiscovery, MultichainSwapBounds, SwapBounds} from "../intermediaries/IntermediaryDiscovery";
|
|
19
|
-
import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
|
|
20
|
-
import {ISwap} from "../swaps/ISwap";
|
|
21
|
-
import {IntermediaryError} from "../errors/IntermediaryError";
|
|
22
|
-
import {SwapType} from "../enums/SwapType";
|
|
23
|
-
import {FromBTCLNSwap} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap";
|
|
24
|
-
import {FromBTCSwap} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCSwap";
|
|
25
|
-
import {ToBTCLNSwap} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap";
|
|
26
|
-
import {ToBTCSwap} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCSwap";
|
|
27
|
-
import {LnForGasWrapper} from "../swaps/trusted/ln/LnForGasWrapper";
|
|
28
|
-
import {LnForGasSwap} from "../swaps/trusted/ln/LnForGasSwap";
|
|
29
|
-
import {EventEmitter} from "events";
|
|
30
|
-
import {Intermediary} from "../intermediaries/Intermediary";
|
|
31
|
-
import {ISwapWrapper, WrapperCtorTokens} from "../swaps/ISwapWrapper";
|
|
32
|
-
import {bigIntCompare, bigIntMax, bigIntMin, fromDecimal, objectMap, randomBytes} from "../utils/Utils";
|
|
33
|
-
import {OutOfBoundsError} from "../errors/RequestError";
|
|
34
|
-
import {SwapperWithChain} from "./SwapperWithChain";
|
|
35
|
-
import {OnchainForGasSwap} from "../swaps/trusted/onchain/OnchainForGasSwap";
|
|
36
|
-
import {OnchainForGasWrapper} from "../swaps/trusted/onchain/OnchainForGasWrapper";
|
|
37
|
-
import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils";
|
|
38
|
-
import {IUnifiedStorage, QueryParams} from "../storage/IUnifiedStorage";
|
|
39
|
-
import {
|
|
40
|
-
UnifiedSwapStorage,
|
|
41
|
-
UnifiedSwapStorageCompositeIndexes,
|
|
42
|
-
UnifiedSwapStorageIndexes
|
|
43
|
-
} from "../storage/UnifiedSwapStorage";
|
|
44
|
-
import {UnifiedSwapEventListener} from "../events/UnifiedSwapEventListener";
|
|
45
|
-
import {IToBTCSwap} from "../swaps/escrow_swaps/tobtc/IToBTCSwap";
|
|
46
|
-
import {SpvFromBTCOptions, SpvFromBTCWrapper} from "../swaps/spv_swaps/SpvFromBTCWrapper";
|
|
47
|
-
import {SpvFromBTCSwap} from "../swaps/spv_swaps/SpvFromBTCSwap";
|
|
48
|
-
import {SwapperUtils} from "./SwapperUtils";
|
|
49
|
-
import {FromBTCLNAutoOptions, FromBTCLNAutoWrapper} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper";
|
|
50
|
-
import {FromBTCLNAutoSwap} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap";
|
|
51
|
-
import {UserError} from "../errors/UserError";
|
|
52
|
-
import {SwapAmountType} from "../enums/SwapAmountType";
|
|
53
|
-
import {IClaimableSwap} from "../swaps/IClaimableSwap";
|
|
54
|
-
import {correctClock} from "../utils/AutomaticClockDriftCorrection";
|
|
55
|
-
import {isSwapType, SwapProtocolInfo, SwapTypeMapping} from "../utils/SwapUtils";
|
|
56
|
-
import {IndexedDBUnifiedStorage} from "../storage-browser/IndexedDBUnifiedStorage";
|
|
57
|
-
import {TokenAmount, toTokenAmount} from "../types/TokenAmount";
|
|
58
|
-
import {BitcoinTokens, BtcToken, isBtcToken, isSCToken, SCToken, Token} from "../types/Token";
|
|
59
|
-
import {AmountData} from "../types/AmountData";
|
|
60
|
-
import {getLogger} from "../utils/Logger";
|
|
61
|
-
import {isLNURLWithdraw, LNURLWithdraw} from "../types/lnurl/LNURLWithdraw";
|
|
62
|
-
import {isLNURLPay, LNURLPay} from "../types/lnurl/LNURLPay";
|
|
63
|
-
import {tryWithRetries} from "../utils/RetryUtils";
|
|
64
|
-
import {NotNever} from "../utils/TypeUtils";
|
|
65
|
-
import {IEscrowSwap} from "../swaps/escrow_swaps/IEscrowSwap";
|
|
66
|
-
import {LightningInvoiceCreateService, isLightningInvoiceCreateService} from "../types/wallets/LightningInvoiceCreateService";
|
|
67
|
-
import {SwapSide} from "../enums/SwapSide";
|
|
68
|
-
import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "../bitcoin/wallet/IBitcoinWallet";
|
|
69
|
-
import {MinimalBitcoinWalletInterface} from "../types/wallets/MinimalBitcoinWalletInterface";
|
|
70
|
-
import {toBitcoinWallet} from "../utils/BitcoinWalletUtils";
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Configuration options for the Swapper
|
|
74
|
-
* @category Core
|
|
75
|
-
*/
|
|
76
|
-
export type SwapperOptions = {
|
|
77
|
-
/**
|
|
78
|
-
* Manual override for the intermediary (LP) URLs for the SDK to use, by default these are fetched automatically
|
|
79
|
-
* from the registry
|
|
80
|
-
*/
|
|
81
|
-
intermediaryUrl?: string | string[],
|
|
82
|
-
/**
|
|
83
|
-
* Registry URL for where to look for active intermediary (LP) endpoint URLs
|
|
84
|
-
*/
|
|
85
|
-
registryUrl?: string,
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Bitcoin network to use for the swaps,
|
|
89
|
-
*/
|
|
90
|
-
bitcoinNetwork?: BitcoinNetwork,
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Timeout (in milliseconds) for HTTP GET requests done by the SDK
|
|
94
|
-
*/
|
|
95
|
-
getRequestTimeout?: number,
|
|
96
|
-
/**
|
|
97
|
-
* Timeout (in milliseconds) for HTTP POST requests done by the SDK
|
|
98
|
-
*/
|
|
99
|
-
postRequestTimeout?: number,
|
|
100
|
-
/**
|
|
101
|
-
* Additional parameters to be sent to the intermediaries (LPs), when requesting quotes from them
|
|
102
|
-
*/
|
|
103
|
-
defaultAdditionalParameters?: {[key: string]: any},
|
|
104
|
-
/**
|
|
105
|
-
* Optional name prefix to use when creating a swap storage, you can use this to create separate storage
|
|
106
|
-
* instances that don't overlap.
|
|
107
|
-
*/
|
|
108
|
-
storagePrefix?: string,
|
|
109
|
-
/**
|
|
110
|
-
* Sets the default intermediary (LP) to use for the trusted gas swaps, if not set the SDK uses a default one
|
|
111
|
-
*/
|
|
112
|
-
defaultTrustedIntermediaryUrl?: string,
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* A function callback to retrieve a specific named storage container for swap persistency. If not present, the
|
|
116
|
-
* default IndexedDB storage adapter is used. When you use the SDK in non-browser based environments you need to
|
|
117
|
-
* provide this callback such that the SDK is able to use a custom storage adapter.
|
|
118
|
-
*
|
|
119
|
-
* @param storageName Name of the container to retrieve
|
|
120
|
-
*/
|
|
121
|
-
swapStorage?: (storageName: string) => IUnifiedStorage<UnifiedSwapStorageIndexes, UnifiedSwapStorageCompositeIndexes>,
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* By setting this flag, the swapper doesn't schedule automatic tick timers. To make sure the swap states are
|
|
125
|
-
* properly updated (e.g. the expired swaps properly move to the expired state), you should call the
|
|
126
|
-
* {@link Swapper._syncSwaps} function periodically. This flag should be set when you run an environment that
|
|
127
|
-
* doesn't support long-running timers - e.g. serverless environments like Azure Function Apps or AWS Lambda
|
|
128
|
-
*/
|
|
129
|
-
noTimers?: boolean,
|
|
130
|
-
/**
|
|
131
|
-
* By setting this flag, the swapper doesn't subscribe to on-chain events. To make sure the swap states are
|
|
132
|
-
* properly updated you should call the {@link Swapper._syncSwaps} function periodically. This flag should be
|
|
133
|
-
* set when you run an environment that doesn't support long-running timers and websocket connections - e.g.
|
|
134
|
-
* serverless environments like Azure Function Apps or AWS Lambda
|
|
135
|
-
*/
|
|
136
|
-
noEvents?: boolean,
|
|
137
|
-
/**
|
|
138
|
-
* By setting this flag, the swap objects will not be cached in the SDK and instead will always be loaded from
|
|
139
|
-
* the persistent storage. By default, the SDK uses a `WeakRef` mapping of swaps, to ensure that when the same
|
|
140
|
-
* swap is loaded concurrently, it returns the same object reference to both, making the changes on the object
|
|
141
|
-
* atomic. This flag should be set to `true` when running in an environment where multiple instances of the SDK
|
|
142
|
-
* access the same swap database - e.g. serverless environments like Azure Function Apps or AWS Lambda
|
|
143
|
-
*/
|
|
144
|
-
noSwapCache?: boolean,
|
|
145
|
-
/**
|
|
146
|
-
* Skip checking past swaps when the swapper is initiated with {@link Swapper.init}, you can call the
|
|
147
|
-
* {@link Swapper._syncSwaps} function later, to check the swaps. By default, the SDK checks the state
|
|
148
|
-
* of all the known swaps during init.
|
|
149
|
-
*/
|
|
150
|
-
dontCheckPastSwaps?: boolean,
|
|
151
|
-
/**
|
|
152
|
-
* Skip fetching the LPs when the swapper is initiated with {@link Swapper.init}, this means the list of available
|
|
153
|
-
* tokens and swap limits won't be available immediately. LPs will be fetched automatically later, when a swap
|
|
154
|
-
* is requested
|
|
155
|
-
*/
|
|
156
|
-
dontFetchLPs?: boolean,
|
|
157
|
-
/**
|
|
158
|
-
* Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
|
|
159
|
-
* `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
|
|
160
|
-
* want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
|
|
161
|
-
*
|
|
162
|
-
* Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
|
|
163
|
-
* `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
|
|
164
|
-
* might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
|
|
165
|
-
* frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
|
|
166
|
-
* necessitating the saving of the swap data to the persistent storage until it is actually initiated.
|
|
167
|
-
*/
|
|
168
|
-
saveUninitializedSwaps?: boolean,
|
|
169
|
-
/**
|
|
170
|
-
* Automatically checks system time on initialize, if the system time drifts too far from the actual time
|
|
171
|
-
* (as checked from multiple server sources) it adjusts the `Date.now()` function to return proper actual time.
|
|
172
|
-
*/
|
|
173
|
-
automaticClockDriftCorrection?: boolean
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Type representing multiple blockchain configurations
|
|
178
|
-
* @category Core
|
|
179
|
-
*/
|
|
180
|
-
export type MultiChain = {
|
|
181
|
-
[chainIdentifier in string]: ChainType;
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
type ChainSpecificData<T extends ChainType> = {
|
|
185
|
-
wrappers: {
|
|
186
|
-
[SwapType.TO_BTCLN]: ToBTCLNWrapper<T>,
|
|
187
|
-
[SwapType.TO_BTC]: ToBTCWrapper<T>,
|
|
188
|
-
[SwapType.FROM_BTCLN]: FromBTCLNWrapper<T>,
|
|
189
|
-
[SwapType.FROM_BTC]: FromBTCWrapper<T>,
|
|
190
|
-
[SwapType.TRUSTED_FROM_BTCLN]: LnForGasWrapper<T>,
|
|
191
|
-
[SwapType.TRUSTED_FROM_BTC]: OnchainForGasWrapper<T>,
|
|
192
|
-
[SwapType.SPV_VAULT_FROM_BTC]: SpvFromBTCWrapper<T>,
|
|
193
|
-
[SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>
|
|
194
|
-
}
|
|
195
|
-
chainEvents: T["Events"],
|
|
196
|
-
chainInterface: T["ChainInterface"],
|
|
197
|
-
unifiedChainEvents: UnifiedSwapEventListener<T>,
|
|
198
|
-
unifiedSwapStorage: UnifiedSwapStorage<T>,
|
|
199
|
-
reviver: (val: any) => ISwap<T>,
|
|
200
|
-
defaultVersion: string,
|
|
201
|
-
|
|
202
|
-
versionedContracts: {
|
|
203
|
-
[contractVersion: string]: {
|
|
204
|
-
swapContract: T["Contract"],
|
|
205
|
-
spvVaultContract: T["SpvVaultContract"],
|
|
206
|
-
btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
|
|
207
|
-
synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
type MultiChainData<T extends MultiChain> = {
|
|
213
|
-
[chainIdentifier in keyof T]: ChainSpecificData<T[chainIdentifier]>
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
type CtorMultiChainData<T extends MultiChain> = {
|
|
217
|
-
[chainIdentifier in keyof T]: ChainData<T[chainIdentifier]>
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
type SwapperCtorTokens<T extends MultiChain = MultiChain> = {
|
|
221
|
-
ticker: string,
|
|
222
|
-
name: string,
|
|
223
|
-
chains: {[chainId in ChainIds<T>]?: {
|
|
224
|
-
address: string,
|
|
225
|
-
decimals: number,
|
|
226
|
-
displayDecimals?: number
|
|
227
|
-
}}
|
|
228
|
-
}[];
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Type extracting chain identifiers from a MultiChain type
|
|
232
|
-
* @category Core
|
|
233
|
-
*/
|
|
234
|
-
export type ChainIds<T extends MultiChain> = keyof T & string;
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Type helper to check if a chain supports a specific swap type
|
|
238
|
-
* @category Core
|
|
239
|
-
*/
|
|
240
|
-
export type SupportsSwapType<
|
|
241
|
-
C extends ChainType,
|
|
242
|
-
Type extends SwapType
|
|
243
|
-
> = Type extends SwapType.SPV_VAULT_FROM_BTC ?
|
|
244
|
-
NotNever<C["SpvVaultContract"]> :
|
|
245
|
-
Type extends (SwapType.TRUSTED_FROM_BTCLN | SwapType.TRUSTED_FROM_BTC) ?
|
|
246
|
-
true :
|
|
247
|
-
Type extends SwapType.FROM_BTCLN_AUTO ? (C["Contract"]["supportsInitWithoutClaimer"] extends true ? true : false) :
|
|
248
|
-
NotNever<C["Contract"]>;
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Core orchestrator for all atomiq swap operations
|
|
252
|
-
*
|
|
253
|
-
* @category Core
|
|
254
|
-
*/
|
|
255
|
-
export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
256
|
-
lpsRemoved: [Intermediary[]],
|
|
257
|
-
lpsAdded: [Intermediary[]],
|
|
258
|
-
swapState: [ISwap],
|
|
259
|
-
swapLimitsChanged: []
|
|
260
|
-
}> {
|
|
261
|
-
private readonly logger = getLogger(this.constructor.name+": ");
|
|
262
|
-
private readonly swapStateListener: (swap: ISwap) => void;
|
|
263
|
-
|
|
264
|
-
private defaultTrustedIntermediary?: Intermediary;
|
|
265
|
-
private readonly bitcoinNetwork: BitcoinNetwork;
|
|
266
|
-
private readonly options: SwapperOptions;
|
|
267
|
-
/**
|
|
268
|
-
* Data propagation layer used for broadcasting messages to watchtowers
|
|
269
|
-
*/
|
|
270
|
-
private readonly messenger: Messenger;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* A dictionary of smart chains used by the SDK
|
|
274
|
-
* @internal
|
|
275
|
-
*/
|
|
276
|
-
readonly _chains: MultiChainData<T>;
|
|
277
|
-
/**
|
|
278
|
-
* Bitcoin RPC for fetching bitcoin chain data
|
|
279
|
-
* @internal
|
|
280
|
-
*/
|
|
281
|
-
readonly _bitcoinRpc: BitcoinRpcWithAddressIndex<any>;
|
|
282
|
-
/**
|
|
283
|
-
* Bitcoin network specification
|
|
284
|
-
* @internal
|
|
285
|
-
*/
|
|
286
|
-
readonly _btcNetwork: BTC_NETWORK;
|
|
287
|
-
/**
|
|
288
|
-
* Token data indexed by chain identifier and token addresses
|
|
289
|
-
* @internal
|
|
290
|
-
*/
|
|
291
|
-
readonly _tokens: {
|
|
292
|
-
[chainId: string]: {
|
|
293
|
-
[tokenAddress: string]: SCToken
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
/**
|
|
297
|
-
* Token data indexed by chain identifier and token tickers
|
|
298
|
-
* @internal
|
|
299
|
-
*/
|
|
300
|
-
readonly _tokensByTicker: {
|
|
301
|
-
[chainId: string]: {
|
|
302
|
-
[tokenTicker: string]: SCToken
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Pricing API used by the SDK
|
|
308
|
-
*/
|
|
309
|
-
readonly prices: ISwapPrice<T>;
|
|
310
|
-
/**
|
|
311
|
-
* Intermediary discovery instance
|
|
312
|
-
*/
|
|
313
|
-
readonly intermediaryDiscovery: IntermediaryDiscovery;
|
|
314
|
-
/**
|
|
315
|
-
* Miscellaneous utility functions
|
|
316
|
-
*/
|
|
317
|
-
readonly Utils: SwapperUtils<T>;
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* @internal
|
|
321
|
-
*/
|
|
322
|
-
constructor(
|
|
323
|
-
bitcoinRpc: BitcoinRpcWithAddressIndex<any>,
|
|
324
|
-
lightningApi: LightningNetworkApi,
|
|
325
|
-
bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
|
|
326
|
-
chainsData: CtorMultiChainData<T>,
|
|
327
|
-
pricing: ISwapPrice<T>,
|
|
328
|
-
tokens: SCToken[],
|
|
329
|
-
messenger: Messenger,
|
|
330
|
-
options?: SwapperOptions
|
|
331
|
-
) {
|
|
332
|
-
super();
|
|
333
|
-
const storagePrefix = options?.storagePrefix ?? "atomiq-";
|
|
334
|
-
|
|
335
|
-
options ??= {};
|
|
336
|
-
options.saveUninitializedSwaps ??= true;
|
|
337
|
-
options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
|
|
338
|
-
const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
|
|
339
|
-
|
|
340
|
-
this.options = options;
|
|
341
|
-
|
|
342
|
-
this.bitcoinNetwork = options.bitcoinNetwork;
|
|
343
|
-
this._btcNetwork = options.bitcoinNetwork===BitcoinNetwork.MAINNET ? NETWORK :
|
|
344
|
-
(options.bitcoinNetwork===BitcoinNetwork.TESTNET || options.bitcoinNetwork===BitcoinNetwork.TESTNET4) ? TEST_NETWORK : {
|
|
345
|
-
bech32: 'bcrt',
|
|
346
|
-
pubKeyHash: 111,
|
|
347
|
-
scriptHash: 196,
|
|
348
|
-
wif: 239
|
|
349
|
-
};
|
|
350
|
-
this.Utils = new SwapperUtils(this);
|
|
351
|
-
|
|
352
|
-
this.prices = pricing;
|
|
353
|
-
this._bitcoinRpc = bitcoinRpc;
|
|
354
|
-
this.messenger = messenger;
|
|
355
|
-
|
|
356
|
-
this._tokens = {};
|
|
357
|
-
this._tokensByTicker = {};
|
|
358
|
-
for(let tokenData of tokens) {
|
|
359
|
-
const chainId = tokenData.chainId;
|
|
360
|
-
this._tokens[chainId] ??= {};
|
|
361
|
-
this._tokensByTicker[chainId] ??= {};
|
|
362
|
-
this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
this.swapStateListener = (swap: ISwap) => {
|
|
366
|
-
this.emit("swapState", swap);
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
this._chains = objectMap<CtorMultiChainData<T>, MultiChainData<T>>(chainsData, <InputKey extends keyof CtorMultiChainData<T>>(chainData: CtorMultiChainData<T>[InputKey], key: string) => {
|
|
370
|
-
let {
|
|
371
|
-
chainInterface, chainEvents, chainId,
|
|
372
|
-
btcRelay,
|
|
373
|
-
swapContract, swapDataConstructor,
|
|
374
|
-
spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor,
|
|
375
|
-
defaultVersion, versions
|
|
376
|
-
} = chainData;
|
|
377
|
-
|
|
378
|
-
defaultVersion ??= "v1";
|
|
379
|
-
|
|
380
|
-
if(versions==null) {
|
|
381
|
-
versions = {
|
|
382
|
-
[defaultVersion]: {
|
|
383
|
-
btcRelay,
|
|
384
|
-
swapContract,
|
|
385
|
-
swapDataConstructor,
|
|
386
|
-
spvVaultContract,
|
|
387
|
-
spvVaultDataConstructor,
|
|
388
|
-
spvVaultWithdrawalDataConstructor
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const versionedContracts = objectMap(versions, (value, key) => {
|
|
394
|
-
return {
|
|
395
|
-
swapContract: value.swapContract,
|
|
396
|
-
spvVaultContract: value.spvVaultContract,
|
|
397
|
-
btcRelay: value.btcRelay,
|
|
398
|
-
synchronizer: bitcoinSynchronizer(value.btcRelay)
|
|
399
|
-
};
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
const storageHandler = swapStorage(storagePrefix + chainId);
|
|
403
|
-
const unifiedSwapStorage = new UnifiedSwapStorage<T[InputKey]>(storageHandler, this.options.noSwapCache);
|
|
404
|
-
const unifiedChainEvents = new UnifiedSwapEventListener<T[InputKey]>(unifiedSwapStorage, chainEvents);
|
|
405
|
-
|
|
406
|
-
const wrappers: any = {};
|
|
407
|
-
|
|
408
|
-
wrappers[SwapType.TO_BTCLN] = new ToBTCLNWrapper<T[InputKey]>(
|
|
409
|
-
key,
|
|
410
|
-
unifiedSwapStorage,
|
|
411
|
-
unifiedChainEvents,
|
|
412
|
-
chainInterface,
|
|
413
|
-
pricing,
|
|
414
|
-
this._tokens[chainId],
|
|
415
|
-
versions,
|
|
416
|
-
{
|
|
417
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
418
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
419
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
420
|
-
}
|
|
421
|
-
);
|
|
422
|
-
wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
|
|
423
|
-
key,
|
|
424
|
-
unifiedSwapStorage,
|
|
425
|
-
unifiedChainEvents,
|
|
426
|
-
chainInterface,
|
|
427
|
-
pricing,
|
|
428
|
-
this._tokens[chainId],
|
|
429
|
-
versions,
|
|
430
|
-
this._bitcoinRpc,
|
|
431
|
-
{
|
|
432
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
433
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
434
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
435
|
-
bitcoinNetwork: this._btcNetwork
|
|
436
|
-
}
|
|
437
|
-
);
|
|
438
|
-
wrappers[SwapType.FROM_BTCLN] = new FromBTCLNWrapper<T[InputKey]>(
|
|
439
|
-
key,
|
|
440
|
-
unifiedSwapStorage,
|
|
441
|
-
unifiedChainEvents,
|
|
442
|
-
chainInterface,
|
|
443
|
-
pricing,
|
|
444
|
-
this._tokens[chainId],
|
|
445
|
-
versions,
|
|
446
|
-
lightningApi,
|
|
447
|
-
{
|
|
448
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
449
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
450
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
451
|
-
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
452
|
-
}
|
|
453
|
-
);
|
|
454
|
-
wrappers[SwapType.FROM_BTC] = new FromBTCWrapper<T[InputKey]>(
|
|
455
|
-
key,
|
|
456
|
-
unifiedSwapStorage,
|
|
457
|
-
unifiedChainEvents,
|
|
458
|
-
chainInterface,
|
|
459
|
-
pricing,
|
|
460
|
-
this._tokens[chainId],
|
|
461
|
-
versions,
|
|
462
|
-
versionedContracts,
|
|
463
|
-
this._bitcoinRpc,
|
|
464
|
-
{
|
|
465
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
466
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
467
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
468
|
-
bitcoinNetwork: this._btcNetwork
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
wrappers[SwapType.TRUSTED_FROM_BTCLN] = new LnForGasWrapper<T[InputKey]>(
|
|
472
|
-
key,
|
|
473
|
-
unifiedSwapStorage,
|
|
474
|
-
unifiedChainEvents,
|
|
475
|
-
chainInterface,
|
|
476
|
-
pricing,
|
|
477
|
-
this._tokens[chainId],
|
|
478
|
-
{
|
|
479
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
480
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
481
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
482
|
-
}
|
|
483
|
-
);
|
|
484
|
-
wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
|
|
485
|
-
key,
|
|
486
|
-
unifiedSwapStorage,
|
|
487
|
-
unifiedChainEvents,
|
|
488
|
-
chainInterface,
|
|
489
|
-
pricing,
|
|
490
|
-
this._tokens[chainId],
|
|
491
|
-
bitcoinRpc,
|
|
492
|
-
{
|
|
493
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
494
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
495
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
496
|
-
bitcoinNetwork: this._btcNetwork
|
|
497
|
-
}
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
// This is gated on the default version of the contracts
|
|
501
|
-
if(spvVaultContract!=null) {
|
|
502
|
-
wrappers[SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper<T[InputKey]>(
|
|
503
|
-
key,
|
|
504
|
-
unifiedSwapStorage,
|
|
505
|
-
unifiedChainEvents,
|
|
506
|
-
chainInterface,
|
|
507
|
-
pricing,
|
|
508
|
-
this._tokens[chainId],
|
|
509
|
-
versions,
|
|
510
|
-
versionedContracts,
|
|
511
|
-
bitcoinRpc,
|
|
512
|
-
{
|
|
513
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
514
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
515
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
516
|
-
bitcoinNetwork: this._btcNetwork
|
|
517
|
-
}
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// This is gated on the default version of the contracts
|
|
522
|
-
if(swapContract.supportsInitWithoutClaimer) {
|
|
523
|
-
wrappers[SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper<T[InputKey]>(
|
|
524
|
-
key,
|
|
525
|
-
unifiedSwapStorage,
|
|
526
|
-
unifiedChainEvents,
|
|
527
|
-
chainInterface,
|
|
528
|
-
pricing,
|
|
529
|
-
this._tokens[chainId],
|
|
530
|
-
versions,
|
|
531
|
-
lightningApi,
|
|
532
|
-
this.messenger,
|
|
533
|
-
{
|
|
534
|
-
getRequestTimeout: this.options.getRequestTimeout,
|
|
535
|
-
postRequestTimeout: this.options.postRequestTimeout,
|
|
536
|
-
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
537
|
-
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
538
|
-
}
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
Object.keys(wrappers).forEach(key => wrappers[key].events.on("swapState", this.swapStateListener));
|
|
543
|
-
|
|
544
|
-
const reviver = (val: any) => {
|
|
545
|
-
const wrapper: ISwapWrapper<any, any> = wrappers[val.type];
|
|
546
|
-
if(wrapper==null) return null;
|
|
547
|
-
return new wrapper._swapDeserializer(wrapper, val);
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
return {
|
|
551
|
-
chainEvents,
|
|
552
|
-
chainInterface,
|
|
553
|
-
|
|
554
|
-
wrappers,
|
|
555
|
-
|
|
556
|
-
unifiedChainEvents,
|
|
557
|
-
unifiedSwapStorage,
|
|
558
|
-
|
|
559
|
-
defaultVersion,
|
|
560
|
-
|
|
561
|
-
reviver,
|
|
562
|
-
|
|
563
|
-
versionedContracts
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
const contracts = objectMap(chainsData, (data) => data.versions ?? {[data.defaultVersion ?? "v1"]: {swapContract: data.swapContract, spvVaultContract: data.spvVaultContract}});
|
|
568
|
-
if(options.intermediaryUrl!=null) {
|
|
569
|
-
this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
|
|
570
|
-
} else {
|
|
571
|
-
this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, undefined, options.getRequestTimeout);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
this.intermediaryDiscovery.on("removed", (intermediaries: Intermediary[]) => {
|
|
575
|
-
this.emit("lpsRemoved", intermediaries);
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
this.intermediaryDiscovery.on("added", (intermediaries: Intermediary[]) => {
|
|
579
|
-
this.emit("lpsAdded", intermediaries);
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
private async _init(): Promise<void> {
|
|
584
|
-
this.logger.debug("init(): Initializing swapper...");
|
|
585
|
-
|
|
586
|
-
const abortController = new AbortController();
|
|
587
|
-
|
|
588
|
-
const promises: Promise<void>[] = [];
|
|
589
|
-
let automaticClockDriftCorrectionPromise: Promise<void> | undefined = undefined;
|
|
590
|
-
if(this.options.automaticClockDriftCorrection) {
|
|
591
|
-
promises.push(automaticClockDriftCorrectionPromise = tryWithRetries(correctClock, undefined, undefined, abortController.signal).catch((err) => {
|
|
592
|
-
abortController.abort(err);
|
|
593
|
-
}));
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
this.logger.debug("init(): Initializing intermediary discovery");
|
|
597
|
-
if(!this.options.dontFetchLPs) promises.push(this.intermediaryDiscovery.init(abortController.signal).catch(err => {
|
|
598
|
-
if(abortController.signal.aborted) return;
|
|
599
|
-
this.logger.error("init(): Failed to fetch intermediaries/LPs: ", err);
|
|
600
|
-
}));
|
|
601
|
-
|
|
602
|
-
if(this.options.defaultTrustedIntermediaryUrl!=null) {
|
|
603
|
-
promises.push(
|
|
604
|
-
this.intermediaryDiscovery.getIntermediary(this.options.defaultTrustedIntermediaryUrl, abortController.signal)
|
|
605
|
-
.then(val => {
|
|
606
|
-
if(val==null) throw new Error("Cannot get trusted LP");
|
|
607
|
-
this.defaultTrustedIntermediary = val;
|
|
608
|
-
})
|
|
609
|
-
.catch(err => {
|
|
610
|
-
if(abortController.signal.aborted) return;
|
|
611
|
-
this.logger.error("init(): Failed to contact trusted LP url: ", err);
|
|
612
|
-
})
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if(automaticClockDriftCorrectionPromise!=null) {
|
|
617
|
-
//We should await the promises here before checking the swaps
|
|
618
|
-
await automaticClockDriftCorrectionPromise;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const chainPromises = [];
|
|
622
|
-
for(let chainIdentifier in this._chains) {
|
|
623
|
-
chainPromises.push((async() => {
|
|
624
|
-
const {
|
|
625
|
-
chainInterface,
|
|
626
|
-
versionedContracts,
|
|
627
|
-
unifiedChainEvents,
|
|
628
|
-
unifiedSwapStorage,
|
|
629
|
-
wrappers,
|
|
630
|
-
reviver
|
|
631
|
-
} = this._chains[chainIdentifier];
|
|
632
|
-
|
|
633
|
-
const _chainInterface: any = chainInterface;
|
|
634
|
-
if(_chainInterface.verifyNetwork!=null) {
|
|
635
|
-
await _chainInterface.verifyNetwork(this.bitcoinNetwork);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
for(let contractVersion in versionedContracts) {
|
|
639
|
-
await versionedContracts[contractVersion].swapContract.start();
|
|
640
|
-
this.logger.debug("init(): Intialized swap contract: "+chainIdentifier+` version: ${contractVersion}`);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
await unifiedSwapStorage.init();
|
|
644
|
-
if(unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage) {
|
|
645
|
-
//Try to migrate the data here
|
|
646
|
-
const storagePrefix = chainIdentifier==="SOLANA" ?
|
|
647
|
-
"SOLv4-"+this.bitcoinNetwork+"-Swaps-" :
|
|
648
|
-
"atomiqsdk-"+this.bitcoinNetwork+chainIdentifier+"-Swaps-";
|
|
649
|
-
await unifiedSwapStorage.storage.tryMigrate(
|
|
650
|
-
[
|
|
651
|
-
[storagePrefix+"FromBTC", SwapType.FROM_BTC],
|
|
652
|
-
[storagePrefix+"FromBTCLN", SwapType.FROM_BTCLN],
|
|
653
|
-
[storagePrefix+"ToBTC", SwapType.TO_BTC],
|
|
654
|
-
[storagePrefix+"ToBTCLN", SwapType.TO_BTCLN]
|
|
655
|
-
],
|
|
656
|
-
(obj: any) => {
|
|
657
|
-
const swap = reviver(obj);
|
|
658
|
-
if(swap._randomNonce==null) {
|
|
659
|
-
const oldIdentifierHash = swap.getId();
|
|
660
|
-
swap._randomNonce = randomBytes(16).toString("hex");
|
|
661
|
-
const newIdentifierHash = swap.getId();
|
|
662
|
-
this.logger.info("init(): Found older swap version without randomNonce, replacing, old hash: "+oldIdentifierHash+
|
|
663
|
-
" new hash: "+newIdentifierHash);
|
|
664
|
-
}
|
|
665
|
-
return swap;
|
|
666
|
-
}
|
|
667
|
-
)
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if(!this.options.noEvents) await unifiedChainEvents.start();
|
|
671
|
-
this.logger.debug("init(): Intialized events: "+chainIdentifier);
|
|
672
|
-
|
|
673
|
-
for(let key in wrappers) {
|
|
674
|
-
// this.logger.debug("init(): Initializing "+SwapType[key]+": "+chainIdentifier);
|
|
675
|
-
await wrappers[key as unknown as SwapType].init(this.options.noTimers, this.options.dontCheckPastSwaps);
|
|
676
|
-
}
|
|
677
|
-
})());
|
|
678
|
-
}
|
|
679
|
-
await Promise.all(chainPromises);
|
|
680
|
-
|
|
681
|
-
await Promise.all(promises);
|
|
682
|
-
|
|
683
|
-
this.logger.debug("init(): Initializing messenger");
|
|
684
|
-
await this.messenger.init();
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
private initPromise?: Promise<void>;
|
|
688
|
-
private initialized: boolean = false;
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Initializes the swap storage and loads existing swaps, needs to be called before any other action
|
|
692
|
-
*/
|
|
693
|
-
async init(): Promise<void> {
|
|
694
|
-
if(this.initialized) return;
|
|
695
|
-
if(this.initPromise!=null) {
|
|
696
|
-
await this.initPromise;
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
try {
|
|
701
|
-
const promise = this._init();
|
|
702
|
-
this.initPromise = promise;
|
|
703
|
-
await promise;
|
|
704
|
-
delete this.initPromise;
|
|
705
|
-
this.initialized = true;
|
|
706
|
-
} catch (e) {
|
|
707
|
-
delete this.initPromise;
|
|
708
|
-
throw e;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* Stops listening for onchain events and closes this Swapper instance
|
|
714
|
-
*/
|
|
715
|
-
async stop() {
|
|
716
|
-
if(this.initPromise) await this.initPromise;
|
|
717
|
-
for(let chainIdentifier in this._chains) {
|
|
718
|
-
const {
|
|
719
|
-
wrappers,
|
|
720
|
-
unifiedChainEvents
|
|
721
|
-
} = this._chains[chainIdentifier];
|
|
722
|
-
for(let key in wrappers) {
|
|
723
|
-
const wrapper = wrappers[key as unknown as SwapType];
|
|
724
|
-
wrapper.events.removeListener("swapState", this.swapStateListener);
|
|
725
|
-
await wrapper.stop();
|
|
726
|
-
}
|
|
727
|
-
await unifiedChainEvents.stop();
|
|
728
|
-
await this.messenger.stop();
|
|
729
|
-
}
|
|
730
|
-
this.initialized = false;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Creates swap & handles intermediary, quote selection
|
|
735
|
-
*
|
|
736
|
-
* @param chainIdentifier
|
|
737
|
-
* @param create Callback to create the
|
|
738
|
-
* @param amountData Amount data as passed to the function
|
|
739
|
-
* @param swapType Swap type of the execution
|
|
740
|
-
* @param maxWaitTimeMS Maximum waiting time after the first intermediary returns the quote
|
|
741
|
-
* @private
|
|
742
|
-
* @throws {Error} when no intermediary was found
|
|
743
|
-
* @throws {Error} if the chain with the provided identifier cannot be found
|
|
744
|
-
*/
|
|
745
|
-
private async createSwap<ChainIdentifier extends ChainIds<T>, S extends ISwap<T[ChainIdentifier]>>(
|
|
746
|
-
chainIdentifier: ChainIdentifier,
|
|
747
|
-
create: (candidates: Intermediary[], abortSignal: AbortSignal, chain: ChainSpecificData<T[ChainIdentifier]>) => Promise<{
|
|
748
|
-
quote: Promise<S>,
|
|
749
|
-
intermediary: Intermediary
|
|
750
|
-
}[]>,
|
|
751
|
-
amountData: { amount?: bigint, token: string, exactIn: boolean },
|
|
752
|
-
swapType: SwapType,
|
|
753
|
-
maxWaitTimeMS: number = 2000
|
|
754
|
-
): Promise<S> {
|
|
755
|
-
if(!this.initialized) throw new Error("Swapper not initialized, init first with swapper.init()!");
|
|
756
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
757
|
-
let candidates: Intermediary[];
|
|
758
|
-
|
|
759
|
-
const inBtc: boolean = swapType===SwapType.TO_BTCLN || swapType===SwapType.TO_BTC ? !amountData.exactIn : amountData.exactIn;
|
|
760
|
-
|
|
761
|
-
if(!inBtc || amountData.amount==null) {
|
|
762
|
-
//Get candidates not based on the amount
|
|
763
|
-
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
|
|
764
|
-
} else {
|
|
765
|
-
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
let swapLimitsChanged = false;
|
|
769
|
-
|
|
770
|
-
if(candidates.length===0) {
|
|
771
|
-
this.logger.warn("createSwap(): No valid intermediary found to execute the swap with, reloading intermediary database...");
|
|
772
|
-
await this.intermediaryDiscovery.reloadIntermediaries();
|
|
773
|
-
swapLimitsChanged = true;
|
|
774
|
-
|
|
775
|
-
if(!inBtc || amountData.amount==null) {
|
|
776
|
-
//Get candidates not based on the amount
|
|
777
|
-
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
|
|
778
|
-
} else {
|
|
779
|
-
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
|
|
780
|
-
|
|
781
|
-
if(candidates.length===0) {
|
|
782
|
-
const min = this.intermediaryDiscovery.getSwapMinimum(chainIdentifier, swapType, amountData.token);
|
|
783
|
-
const max = this.intermediaryDiscovery.getSwapMaximum(chainIdentifier, swapType, amountData.token);
|
|
784
|
-
if(min!=null && max!=null) {
|
|
785
|
-
if(amountData.amount < BigInt(min)) throw new OutOfBoundsError("Swap amount too low! Try swapping a higher amount.", 200, BigInt(min), BigInt(max));
|
|
786
|
-
if(amountData.amount > BigInt(max)) throw new OutOfBoundsError("Swap amount too high! Try swapping a lower amount.", 200, BigInt(min), BigInt(max));
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
if(candidates.length===0) throw new Error("No intermediary found for the requested pair and amount! You can try swapping different pair or higher/lower amount.");
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
const abortController = new AbortController();
|
|
795
|
-
this.logger.debug("createSwap() Swap candidates: ", candidates.map(lp => lp.url).join());
|
|
796
|
-
const quotePromises: {quote: Promise<S>, intermediary: Intermediary}[] = await create(candidates, abortController.signal, this._chains[chainIdentifier]);
|
|
797
|
-
|
|
798
|
-
const promiseAll = new Promise<{
|
|
799
|
-
quote: S,
|
|
800
|
-
intermediary: Intermediary
|
|
801
|
-
}[]>((resolve, reject) => {
|
|
802
|
-
let min: bigint;
|
|
803
|
-
let max: bigint;
|
|
804
|
-
let error: Error;
|
|
805
|
-
let numResolved = 0;
|
|
806
|
-
let quotes: {
|
|
807
|
-
quote: S,
|
|
808
|
-
intermediary: Intermediary
|
|
809
|
-
}[] = [];
|
|
810
|
-
let timeout: NodeJS.Timeout;
|
|
811
|
-
|
|
812
|
-
quotePromises.forEach(data => {
|
|
813
|
-
data.quote.then(quote => {
|
|
814
|
-
if(numResolved===0) {
|
|
815
|
-
timeout = setTimeout(() => {
|
|
816
|
-
abortController.abort(new Error("Timed out waiting for quote!"));
|
|
817
|
-
resolve(quotes);
|
|
818
|
-
}, maxWaitTimeMS);
|
|
819
|
-
}
|
|
820
|
-
numResolved++;
|
|
821
|
-
quotes.push({
|
|
822
|
-
quote,
|
|
823
|
-
intermediary: data.intermediary
|
|
824
|
-
});
|
|
825
|
-
if(numResolved===quotePromises.length) {
|
|
826
|
-
clearTimeout(timeout);
|
|
827
|
-
resolve(quotes);
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
}).catch(e => {
|
|
831
|
-
numResolved++;
|
|
832
|
-
if(e instanceof IntermediaryError) {
|
|
833
|
-
//Blacklist that node
|
|
834
|
-
this.intermediaryDiscovery.removeIntermediary(data.intermediary);
|
|
835
|
-
swapLimitsChanged = true;
|
|
836
|
-
} else if(e instanceof OutOfBoundsError) {
|
|
837
|
-
if(min==null || max==null) {
|
|
838
|
-
min = e.min;
|
|
839
|
-
max = e.max;
|
|
840
|
-
} else {
|
|
841
|
-
min = bigIntMin(min, e.min);
|
|
842
|
-
max = bigIntMax(max, e.max);
|
|
843
|
-
}
|
|
844
|
-
data.intermediary.swapBounds[swapType] ??= {};
|
|
845
|
-
data.intermediary.swapBounds[swapType]![chainIdentifier] ??= {};
|
|
846
|
-
const tokenBoundsData = (data.intermediary.swapBounds[swapType]![chainIdentifier]![amountData.token] ??= {input: {}, output: {}});
|
|
847
|
-
if(amountData.exactIn) {
|
|
848
|
-
tokenBoundsData.input = {min: e.min, max: e.max};
|
|
849
|
-
} else {
|
|
850
|
-
tokenBoundsData.output = {min: e.min, max: e.max};
|
|
851
|
-
}
|
|
852
|
-
swapLimitsChanged = true;
|
|
853
|
-
}
|
|
854
|
-
this.logger.warn("createSwap(): Intermediary "+data.intermediary.url+" error: ", e);
|
|
855
|
-
error = e;
|
|
856
|
-
|
|
857
|
-
if(numResolved===quotePromises.length) {
|
|
858
|
-
if(timeout!=null) clearTimeout(timeout);
|
|
859
|
-
if(quotes.length>0) {
|
|
860
|
-
resolve(quotes);
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
if(min!=null && max!=null) {
|
|
864
|
-
let msg = "Swap amount too high or too low! Try swapping a different amount.";
|
|
865
|
-
if(amountData.amount!=null) {
|
|
866
|
-
if(min > amountData.amount) msg = "Swap amount too low! Try swapping a higher amount.";
|
|
867
|
-
if(max < amountData.amount) msg = "Swap amount too high! Try swapping a lower amount.";
|
|
868
|
-
}
|
|
869
|
-
reject(new OutOfBoundsError(msg, 400, min, max));
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
reject(error);
|
|
873
|
-
}
|
|
874
|
-
});
|
|
875
|
-
});
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
try {
|
|
879
|
-
const quotes = await promiseAll;
|
|
880
|
-
|
|
881
|
-
//TODO: Intermediary's reputation is not taken into account!
|
|
882
|
-
quotes.sort((a, b) => {
|
|
883
|
-
if(amountData.exactIn) {
|
|
884
|
-
//Compare outputs
|
|
885
|
-
return bigIntCompare(b.quote.getOutput().rawAmount!, a.quote.getOutput().rawAmount!);
|
|
886
|
-
} else {
|
|
887
|
-
//Compare inputs
|
|
888
|
-
return bigIntCompare(a.quote.getInput().rawAmount!, b.quote.getInput().rawAmount!);
|
|
889
|
-
}
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
this.logger.debug("createSwap(): Sorted quotes, best price to worst: ", quotes);
|
|
893
|
-
|
|
894
|
-
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
895
|
-
|
|
896
|
-
const quote = quotes[0].quote;
|
|
897
|
-
await quote._save();
|
|
898
|
-
return quote;
|
|
899
|
-
} catch (e) {
|
|
900
|
-
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
901
|
-
throw e;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
/**
|
|
906
|
-
* Creates Smart chain -> Bitcoin ({@link SwapType.TO_BTC}) swap
|
|
907
|
-
*
|
|
908
|
-
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
909
|
-
* @param signer Signer's address on the source chain
|
|
910
|
-
* @param tokenAddress Token address to pay with
|
|
911
|
-
* @param address Recipient's bitcoin address
|
|
912
|
-
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
913
|
-
* @param exactIn Whether to use exact in instead of exact out
|
|
914
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
915
|
-
* @param options Additional options for the swap
|
|
916
|
-
*/
|
|
917
|
-
createToBTCSwap<ChainIdentifier extends ChainIds<T>>(
|
|
918
|
-
chainIdentifier: ChainIdentifier,
|
|
919
|
-
signer: string,
|
|
920
|
-
tokenAddress: string,
|
|
921
|
-
address: string,
|
|
922
|
-
amount: bigint,
|
|
923
|
-
exactIn: boolean = false,
|
|
924
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
925
|
-
options?: ToBTCOptions
|
|
926
|
-
): Promise<ToBTCSwap<T[ChainIdentifier]>> {
|
|
927
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
928
|
-
if(address.startsWith("bitcoin:")) {
|
|
929
|
-
address = address.substring(8).split("?")[0];
|
|
930
|
-
}
|
|
931
|
-
if(!this.Utils.isValidBitcoinAddress(address)) throw new Error("Invalid bitcoin address");
|
|
932
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
933
|
-
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
934
|
-
const amountData = {
|
|
935
|
-
amount,
|
|
936
|
-
token: tokenAddress,
|
|
937
|
-
exactIn
|
|
938
|
-
};
|
|
939
|
-
return this.createSwap(
|
|
940
|
-
chainIdentifier as ChainIdentifier,
|
|
941
|
-
(candidates: Intermediary[], abortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.TO_BTC].create(
|
|
942
|
-
signer,
|
|
943
|
-
address,
|
|
944
|
-
amountData,
|
|
945
|
-
candidates,
|
|
946
|
-
options,
|
|
947
|
-
additionalParams,
|
|
948
|
-
abortSignal
|
|
949
|
-
)),
|
|
950
|
-
amountData,
|
|
951
|
-
SwapType.TO_BTC
|
|
952
|
-
);
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
/**
|
|
956
|
-
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap
|
|
957
|
-
*
|
|
958
|
-
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
959
|
-
* @param signer Signer's address on the source chain
|
|
960
|
-
* @param tokenAddress Token address to pay with
|
|
961
|
-
* @param paymentRequest BOLT11 lightning network invoice to be paid (needs to have a fixed amount), and the swap
|
|
962
|
-
* amount is taken from this fixed amount, hence only exact output swaps are supported
|
|
963
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
964
|
-
* @param options Additional options for the swap
|
|
965
|
-
*/
|
|
966
|
-
async createToBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
|
|
967
|
-
chainIdentifier: ChainIdentifier,
|
|
968
|
-
signer: string,
|
|
969
|
-
tokenAddress: string,
|
|
970
|
-
paymentRequest: string,
|
|
971
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
972
|
-
options?: ToBTCLNOptions
|
|
973
|
-
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
974
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
975
|
-
if(paymentRequest.startsWith("lightning:")) paymentRequest = paymentRequest.substring(10);
|
|
976
|
-
if(!this.Utils.isValidLightningInvoice(paymentRequest)) throw new Error("Invalid lightning network invoice");
|
|
977
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
978
|
-
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
979
|
-
const parsedPR = bolt11Decode(paymentRequest);
|
|
980
|
-
if(parsedPR.millisatoshis==null) throw new Error("Invalid lightning network invoice, no msat value field!");
|
|
981
|
-
const amountData = {
|
|
982
|
-
amount: (BigInt(parsedPR.millisatoshis) + 999n) / 1000n,
|
|
983
|
-
token: tokenAddress,
|
|
984
|
-
exactIn: false as const
|
|
985
|
-
};
|
|
986
|
-
return this.createSwap(
|
|
987
|
-
chainIdentifier as ChainIdentifier,
|
|
988
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].create(
|
|
989
|
-
signer,
|
|
990
|
-
paymentRequest,
|
|
991
|
-
amountData,
|
|
992
|
-
candidates,
|
|
993
|
-
options,
|
|
994
|
-
additionalParams,
|
|
995
|
-
abortSignal
|
|
996
|
-
),
|
|
997
|
-
amountData,
|
|
998
|
-
SwapType.TO_BTCLN
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
/**
|
|
1003
|
-
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via LNURL-pay link
|
|
1004
|
-
*
|
|
1005
|
-
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
1006
|
-
* @param signer Signer's address on the source chain
|
|
1007
|
-
* @param tokenAddress Token address to pay with
|
|
1008
|
-
* @param lnurlPay LNURL-pay link to use for the payment
|
|
1009
|
-
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
1010
|
-
* @param exactIn Whether to do an exact in swap instead of exact out
|
|
1011
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1012
|
-
* @param options Additional options for the swap
|
|
1013
|
-
*/
|
|
1014
|
-
async createToBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1015
|
-
chainIdentifier: ChainIdentifier,
|
|
1016
|
-
signer: string,
|
|
1017
|
-
tokenAddress: string,
|
|
1018
|
-
lnurlPay: string | LNURLPay,
|
|
1019
|
-
amount: bigint,
|
|
1020
|
-
exactIn: boolean = false,
|
|
1021
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1022
|
-
options?: ToBTCLNOptions & {comment?: string}
|
|
1023
|
-
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
1024
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1025
|
-
if(typeof(lnurlPay)==="string" && !this.Utils.isValidLNURL(lnurlPay)) throw new Error("Invalid LNURL-pay link");
|
|
1026
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1027
|
-
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
1028
|
-
const amountData = {
|
|
1029
|
-
amount,
|
|
1030
|
-
token: tokenAddress,
|
|
1031
|
-
exactIn
|
|
1032
|
-
};
|
|
1033
|
-
return this.createSwap(
|
|
1034
|
-
chainIdentifier as ChainIdentifier,
|
|
1035
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaLNURL(
|
|
1036
|
-
signer,
|
|
1037
|
-
typeof(lnurlPay)==="string" ? (lnurlPay.startsWith("lightning:") ? lnurlPay.substring(10): lnurlPay) : lnurlPay.params,
|
|
1038
|
-
amountData,
|
|
1039
|
-
candidates,
|
|
1040
|
-
options,
|
|
1041
|
-
additionalParams,
|
|
1042
|
-
abortSignal
|
|
1043
|
-
),
|
|
1044
|
-
amountData,
|
|
1045
|
-
SwapType.TO_BTCLN
|
|
1046
|
-
);
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
/**
|
|
1050
|
-
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via {@link LightningInvoiceCreateService}
|
|
1051
|
-
*
|
|
1052
|
-
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
1053
|
-
* @param signer Signer's address on the source chain
|
|
1054
|
-
* @param tokenAddress Token address to pay with
|
|
1055
|
-
* @param service Invoice create service object which facilitates the creation of fixed amount LN invoices
|
|
1056
|
-
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
1057
|
-
* @param exactIn Whether to do an exact in swap instead of exact out
|
|
1058
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1059
|
-
* @param options Additional options for the swap
|
|
1060
|
-
*/
|
|
1061
|
-
async createToBTCLNSwapViaInvoiceCreateService<ChainIdentifier extends ChainIds<T>>(
|
|
1062
|
-
chainIdentifier: ChainIdentifier,
|
|
1063
|
-
signer: string,
|
|
1064
|
-
tokenAddress: string,
|
|
1065
|
-
service: LightningInvoiceCreateService,
|
|
1066
|
-
amount: bigint,
|
|
1067
|
-
exactIn: boolean = false,
|
|
1068
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1069
|
-
options?: ToBTCLNOptions
|
|
1070
|
-
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
1071
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1072
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1073
|
-
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
1074
|
-
const amountData = {
|
|
1075
|
-
amount,
|
|
1076
|
-
token: tokenAddress,
|
|
1077
|
-
exactIn
|
|
1078
|
-
};
|
|
1079
|
-
return this.createSwap(
|
|
1080
|
-
chainIdentifier as ChainIdentifier,
|
|
1081
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaInvoiceCreateService(
|
|
1082
|
-
signer,
|
|
1083
|
-
Promise.resolve(service),
|
|
1084
|
-
amountData,
|
|
1085
|
-
candidates,
|
|
1086
|
-
options,
|
|
1087
|
-
additionalParams,
|
|
1088
|
-
abortSignal
|
|
1089
|
-
),
|
|
1090
|
-
amountData,
|
|
1091
|
-
SwapType.TO_BTCLN
|
|
1092
|
-
);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
/**
|
|
1096
|
-
* Creates Bitcoin -> Smart chain ({@link SwapType.SPV_VAULT_FROM_BTC}) swap
|
|
1097
|
-
*
|
|
1098
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1099
|
-
* @param recipient Recipient address on the destination chain
|
|
1100
|
-
* @param tokenAddress Token address to receive
|
|
1101
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1102
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1103
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1104
|
-
* @param options Additional options for the swap
|
|
1105
|
-
*/
|
|
1106
|
-
async createFromBTCSwapNew<ChainIdentifier extends ChainIds<T>>(
|
|
1107
|
-
chainIdentifier: ChainIdentifier,
|
|
1108
|
-
recipient: string,
|
|
1109
|
-
tokenAddress: string,
|
|
1110
|
-
amount: bigint | null,
|
|
1111
|
-
exactOut: boolean = false,
|
|
1112
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1113
|
-
options?: SpvFromBTCOptions
|
|
1114
|
-
): Promise<SpvFromBTCSwap<T[ChainIdentifier]>> {
|
|
1115
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1116
|
-
if(this._chains[chainIdentifier].wrappers[SwapType.SPV_VAULT_FROM_BTC]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new BTC swap protocol (spv vault swaps)!");
|
|
1117
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1118
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1119
|
-
const amountData = {
|
|
1120
|
-
amount: amount ?? undefined,
|
|
1121
|
-
token: tokenAddress,
|
|
1122
|
-
exactIn: !exactOut
|
|
1123
|
-
};
|
|
1124
|
-
return this.createSwap(
|
|
1125
|
-
chainIdentifier as ChainIdentifier,
|
|
1126
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.SPV_VAULT_FROM_BTC].create(
|
|
1127
|
-
recipient,
|
|
1128
|
-
amountData,
|
|
1129
|
-
candidates,
|
|
1130
|
-
options,
|
|
1131
|
-
additionalParams,
|
|
1132
|
-
abortSignal
|
|
1133
|
-
)),
|
|
1134
|
-
amountData,
|
|
1135
|
-
SwapType.SPV_VAULT_FROM_BTC
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
/**
|
|
1140
|
-
* Creates LEGACY Bitcoin -> Smart chain ({@link SwapType.FROM_BTC}) swap
|
|
1141
|
-
*
|
|
1142
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1143
|
-
* @param recipient Recipient address on the destination chain
|
|
1144
|
-
* @param tokenAddress Token address to receive
|
|
1145
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1146
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1147
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1148
|
-
* @param options Additional options for the swap
|
|
1149
|
-
*/
|
|
1150
|
-
async createFromBTCSwap<ChainIdentifier extends ChainIds<T>>(
|
|
1151
|
-
chainIdentifier: ChainIdentifier,
|
|
1152
|
-
recipient: string,
|
|
1153
|
-
tokenAddress: string,
|
|
1154
|
-
amount: bigint,
|
|
1155
|
-
exactOut: boolean = false,
|
|
1156
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1157
|
-
options?: FromBTCOptions
|
|
1158
|
-
): Promise<FromBTCSwap<T[ChainIdentifier]>> {
|
|
1159
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1160
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1161
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1162
|
-
const amountData = {
|
|
1163
|
-
amount,
|
|
1164
|
-
token: tokenAddress,
|
|
1165
|
-
exactIn: !exactOut
|
|
1166
|
-
};
|
|
1167
|
-
return this.createSwap(
|
|
1168
|
-
chainIdentifier as ChainIdentifier,
|
|
1169
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTC].create(
|
|
1170
|
-
recipient,
|
|
1171
|
-
amountData,
|
|
1172
|
-
candidates,
|
|
1173
|
-
options,
|
|
1174
|
-
additionalParams,
|
|
1175
|
-
abortSignal
|
|
1176
|
-
)),
|
|
1177
|
-
amountData,
|
|
1178
|
-
SwapType.FROM_BTC
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap
|
|
1184
|
-
*
|
|
1185
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1186
|
-
* @param recipient Recipient address on the destination chain
|
|
1187
|
-
* @param tokenAddress Token address to receive
|
|
1188
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1189
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1190
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1191
|
-
* @param options Additional options for the swap
|
|
1192
|
-
*/
|
|
1193
|
-
async createFromBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
|
|
1194
|
-
chainIdentifier: ChainIdentifier,
|
|
1195
|
-
recipient: string,
|
|
1196
|
-
tokenAddress: string,
|
|
1197
|
-
amount: bigint,
|
|
1198
|
-
exactOut: boolean = false,
|
|
1199
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1200
|
-
options?: FromBTCLNOptions
|
|
1201
|
-
): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
|
|
1202
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1203
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1204
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1205
|
-
const amountData = {
|
|
1206
|
-
amount,
|
|
1207
|
-
token: tokenAddress,
|
|
1208
|
-
exactIn: !exactOut
|
|
1209
|
-
};
|
|
1210
|
-
return this.createSwap(
|
|
1211
|
-
chainIdentifier as ChainIdentifier,
|
|
1212
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN].create(
|
|
1213
|
-
recipient,
|
|
1214
|
-
amountData,
|
|
1215
|
-
candidates,
|
|
1216
|
-
options,
|
|
1217
|
-
additionalParams,
|
|
1218
|
-
abortSignal
|
|
1219
|
-
)),
|
|
1220
|
-
amountData,
|
|
1221
|
-
SwapType.FROM_BTCLN
|
|
1222
|
-
);
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
/**
|
|
1226
|
-
* Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap, withdrawing from
|
|
1227
|
-
* an LNURL-withdraw link
|
|
1228
|
-
*
|
|
1229
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1230
|
-
* @param recipient Recipient address on the destination chain
|
|
1231
|
-
* @param tokenAddress Token address to receive
|
|
1232
|
-
* @param lnurl LNURL-withdraw link to pull the funds from
|
|
1233
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1234
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1235
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1236
|
-
* @param options Additional options for the swap
|
|
1237
|
-
*/
|
|
1238
|
-
async createFromBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1239
|
-
chainIdentifier: ChainIdentifier,
|
|
1240
|
-
recipient: string,
|
|
1241
|
-
tokenAddress: string,
|
|
1242
|
-
lnurl: string | LNURLWithdraw,
|
|
1243
|
-
amount: bigint,
|
|
1244
|
-
exactOut: boolean = false,
|
|
1245
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1246
|
-
options?: FromBTCLNOptions
|
|
1247
|
-
): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
|
|
1248
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1249
|
-
if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
|
|
1250
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1251
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1252
|
-
const amountData = {
|
|
1253
|
-
amount,
|
|
1254
|
-
token: tokenAddress,
|
|
1255
|
-
exactIn: !exactOut
|
|
1256
|
-
};
|
|
1257
|
-
return this.createSwap(
|
|
1258
|
-
chainIdentifier as ChainIdentifier,
|
|
1259
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN].createViaLNURL(
|
|
1260
|
-
recipient,
|
|
1261
|
-
typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
|
|
1262
|
-
amountData,
|
|
1263
|
-
candidates,
|
|
1264
|
-
options,
|
|
1265
|
-
additionalParams,
|
|
1266
|
-
abortSignal
|
|
1267
|
-
),
|
|
1268
|
-
amountData,
|
|
1269
|
-
SwapType.FROM_BTCLN
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
/**
|
|
1274
|
-
* Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap
|
|
1275
|
-
*
|
|
1276
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1277
|
-
* @param recipient Recipient address on the destination chain
|
|
1278
|
-
* @param tokenAddress Token address to receive
|
|
1279
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1280
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1281
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1282
|
-
* @param options Additional options for the swap
|
|
1283
|
-
*/
|
|
1284
|
-
async createFromBTCLNSwapNew<ChainIdentifier extends ChainIds<T>>(
|
|
1285
|
-
chainIdentifier: ChainIdentifier,
|
|
1286
|
-
recipient: string,
|
|
1287
|
-
tokenAddress: string,
|
|
1288
|
-
amount: bigint,
|
|
1289
|
-
exactOut: boolean = false,
|
|
1290
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1291
|
-
options?: FromBTCLNAutoOptions
|
|
1292
|
-
): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
|
|
1293
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1294
|
-
if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
|
|
1295
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1296
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1297
|
-
const amountData = {
|
|
1298
|
-
amount,
|
|
1299
|
-
token: tokenAddress,
|
|
1300
|
-
exactIn: !exactOut
|
|
1301
|
-
};
|
|
1302
|
-
return this.createSwap(
|
|
1303
|
-
chainIdentifier as ChainIdentifier,
|
|
1304
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN_AUTO].create(
|
|
1305
|
-
recipient,
|
|
1306
|
-
amountData,
|
|
1307
|
-
candidates,
|
|
1308
|
-
options,
|
|
1309
|
-
additionalParams,
|
|
1310
|
-
abortSignal
|
|
1311
|
-
)),
|
|
1312
|
-
amountData,
|
|
1313
|
-
SwapType.FROM_BTCLN_AUTO
|
|
1314
|
-
);
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
/**
|
|
1318
|
-
* Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap, withdrawing from
|
|
1319
|
-
* an LNURL-withdraw link
|
|
1320
|
-
*
|
|
1321
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1322
|
-
* @param recipient Recipient address on the destination chain
|
|
1323
|
-
* @param tokenAddress Token address to receive
|
|
1324
|
-
* @param lnurl LNURL-withdraw link to pull the funds from
|
|
1325
|
-
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1326
|
-
* @param exactOut Whether to use a exact out instead of exact in
|
|
1327
|
-
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1328
|
-
* @param options Additional options for the swap
|
|
1329
|
-
*/
|
|
1330
|
-
async createFromBTCLNSwapNewViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1331
|
-
chainIdentifier: ChainIdentifier,
|
|
1332
|
-
recipient: string,
|
|
1333
|
-
tokenAddress: string,
|
|
1334
|
-
lnurl: string | LNURLWithdraw,
|
|
1335
|
-
amount: bigint,
|
|
1336
|
-
exactOut: boolean = false,
|
|
1337
|
-
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1338
|
-
options?: FromBTCLNAutoOptions
|
|
1339
|
-
): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
|
|
1340
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1341
|
-
if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
|
|
1342
|
-
if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
|
|
1343
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1344
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1345
|
-
const amountData = {
|
|
1346
|
-
amount,
|
|
1347
|
-
token: tokenAddress,
|
|
1348
|
-
exactIn: !exactOut
|
|
1349
|
-
};
|
|
1350
|
-
return this.createSwap(
|
|
1351
|
-
chainIdentifier as ChainIdentifier,
|
|
1352
|
-
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN_AUTO].createViaLNURL(
|
|
1353
|
-
recipient,
|
|
1354
|
-
typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
|
|
1355
|
-
amountData,
|
|
1356
|
-
candidates,
|
|
1357
|
-
options,
|
|
1358
|
-
additionalParams,
|
|
1359
|
-
abortSignal
|
|
1360
|
-
),
|
|
1361
|
-
amountData,
|
|
1362
|
-
SwapType.FROM_BTCLN_AUTO
|
|
1363
|
-
);
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
/**
|
|
1367
|
-
* Creates a trusted Bitcoin Lightning -> Smart chain ({@link SwapType.TRUSTED_FROM_BTCLN}) gas swap
|
|
1368
|
-
*
|
|
1369
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1370
|
-
* @param recipient Recipient address on the destination chain
|
|
1371
|
-
* @param amount Amount of native token to receive, in base units
|
|
1372
|
-
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1373
|
-
* @throws {Error} If no trusted intermediary specified
|
|
1374
|
-
*/
|
|
1375
|
-
async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
|
|
1376
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1377
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1378
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1379
|
-
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1380
|
-
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1381
|
-
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
|
|
1382
|
-
await swap._save();
|
|
1383
|
-
return swap;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
/**
|
|
1387
|
-
* Creates a trusted Bitcoin -> Smart chain ({@link SwapType.TRUSTED_FROM_BTC}) gas swap
|
|
1388
|
-
*
|
|
1389
|
-
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1390
|
-
* @param recipient Recipient address on the destination chain
|
|
1391
|
-
* @param amount Amount of native token to receive, in base units
|
|
1392
|
-
* @param refundAddress Bitcoin refund address, in case the swap fails the funds are refunded here
|
|
1393
|
-
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1394
|
-
* @throws {Error} If no trusted intermediary specified
|
|
1395
|
-
*/
|
|
1396
|
-
async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
|
|
1397
|
-
chainIdentifier: C, recipient: string,
|
|
1398
|
-
amount: bigint, refundAddress?: string,
|
|
1399
|
-
trustedIntermediaryOrUrl?: Intermediary | string
|
|
1400
|
-
): Promise<OnchainForGasSwap<T[C]>> {
|
|
1401
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1402
|
-
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1403
|
-
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1404
|
-
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1405
|
-
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1406
|
-
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
|
|
1407
|
-
await swap._save();
|
|
1408
|
-
return swap;
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
/**
|
|
1412
|
-
* @internal
|
|
1413
|
-
*/
|
|
1414
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<true>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean, lnurlWithdraw?: string | LNURLWithdraw): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
|
|
1415
|
-
/**
|
|
1416
|
-
* @internal
|
|
1417
|
-
*/
|
|
1418
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<false>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
|
|
1419
|
-
/**
|
|
1420
|
-
* @internal
|
|
1421
|
-
*/
|
|
1422
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<false>, amount: bigint, exactIn: boolean, address: string): Promise<ToBTCSwap<T[C]>>;
|
|
1423
|
-
/**
|
|
1424
|
-
* @internal
|
|
1425
|
-
*/
|
|
1426
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: bigint, exactIn: boolean, lnurlPay: string | LNURLPay): Promise<ToBTCLNSwap<T[C]>>;
|
|
1427
|
-
/**
|
|
1428
|
-
* @internal
|
|
1429
|
-
*/
|
|
1430
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: undefined, exactIn: false, lightningInvoice: string): Promise<ToBTCLNSwap<T[C]>>;
|
|
1431
|
-
/**
|
|
1432
|
-
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
|
|
1433
|
-
* or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1434
|
-
* invoice's pre-set amount is used instead.
|
|
1435
|
-
* @deprecated Use {@link swap} instead
|
|
1436
|
-
*
|
|
1437
|
-
* @param signer Smartchain (Solana, Starknet, etc.) address of the user
|
|
1438
|
-
* @param srcToken Source token of the swap, user pays this token
|
|
1439
|
-
* @param dstToken Destination token of the swap, user receives this token
|
|
1440
|
-
* @param amount Amount of the swap
|
|
1441
|
-
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1442
|
-
* @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
|
|
1443
|
-
* LNURL-withdrawal to withdraw money from
|
|
1444
|
-
*/
|
|
1445
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>>;
|
|
1446
|
-
/**
|
|
1447
|
-
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
|
|
1448
|
-
* or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1449
|
-
* invoice's pre-set amount is used instead.
|
|
1450
|
-
* @deprecated Use {@link swap} instead
|
|
1451
|
-
*
|
|
1452
|
-
* @param signer Smartchain (Solana, Starknet, etc.) address of the user
|
|
1453
|
-
* @param srcToken Source token of the swap, user pays this token
|
|
1454
|
-
* @param dstToken Destination token of the swap, user receives this token
|
|
1455
|
-
* @param amount Amount of the swap
|
|
1456
|
-
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1457
|
-
* @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
|
|
1458
|
-
* LNURL-withdrawal to withdraw money from
|
|
1459
|
-
*/
|
|
1460
|
-
create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>> {
|
|
1461
|
-
if(srcToken.chain==="BTC") {
|
|
1462
|
-
return this.swap(srcToken, dstToken, amount, exactIn, addressLnurlLightningInvoice as any, signer);
|
|
1463
|
-
} else {
|
|
1464
|
-
return this.swap(srcToken, dstToken, amount, exactIn, signer, addressLnurlLightningInvoice as any);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
/**
|
|
1469
|
-
* @internal
|
|
1470
|
-
*/
|
|
1471
|
-
swap<C extends ChainIds<T>>(srcToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoOptions : FromBTCLNOptions)): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
|
|
1472
|
-
/**
|
|
1473
|
-
* @internal
|
|
1474
|
-
*/
|
|
1475
|
-
swap<C extends ChainIds<T>>(srcToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCOptions : FromBTCOptions)): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
|
|
1476
|
-
/**
|
|
1477
|
-
* @internal
|
|
1478
|
-
*/
|
|
1479
|
-
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstAddress: string, options?: ToBTCOptions): Promise<ToBTCSwap<T[C]>>;
|
|
1480
|
-
/**
|
|
1481
|
-
* @internal
|
|
1482
|
-
*/
|
|
1483
|
-
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstLnurlPayOrInvoiceCreateService: string | LNURLPay | LightningInvoiceCreateService, options?: ToBTCLNOptions & {comment?: string}): Promise<ToBTCLNSwap<T[C]>>;
|
|
1484
|
-
/**
|
|
1485
|
-
* @internal
|
|
1486
|
-
*/
|
|
1487
|
-
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: undefined, exactIn: false | SwapAmountType.EXACT_OUT, src: string, dstLightningInvoice: string, options?: ToBTCLNOptions): Promise<ToBTCLNSwap<T[C]>>;
|
|
1488
|
-
/**
|
|
1489
|
-
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
|
|
1490
|
-
* or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1491
|
-
* invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
|
|
1492
|
-
*
|
|
1493
|
-
* @param srcToken Source token of the swap, user pays this token
|
|
1494
|
-
* @param dstToken Destination token of the swap, user receives this token
|
|
1495
|
-
* @param amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
|
|
1496
|
-
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1497
|
-
* @param src Source wallet/lnurl-withdraw of the swap
|
|
1498
|
-
* @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
|
|
1499
|
-
* @param options Options for the swap
|
|
1500
|
-
*/
|
|
1501
|
-
swap<C extends ChainIds<T>>(srcToken: Token<C> | string, dstToken: Token<C> | string, amount: bigint | string | undefined, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dst: string | LNURLPay | LightningInvoiceCreateService, options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions): Promise<ISwap<T[C]>>;
|
|
1502
|
-
/**
|
|
1503
|
-
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
|
|
1504
|
-
* or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1505
|
-
* invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
|
|
1506
|
-
*
|
|
1507
|
-
* @param _srcToken Source token of the swap, user pays this token
|
|
1508
|
-
* @param _dstToken Destination token of the swap, user receives this token
|
|
1509
|
-
* @param _amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
|
|
1510
|
-
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1511
|
-
* @param src Source wallet/lnurl-withdraw of the swap
|
|
1512
|
-
* @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
|
|
1513
|
-
* @param options Options for the swap
|
|
1514
|
-
*/
|
|
1515
|
-
swap<C extends ChainIds<T>>(
|
|
1516
|
-
_srcToken: Token<C> | string,
|
|
1517
|
-
_dstToken: Token<C> | string,
|
|
1518
|
-
_amount: bigint | string | undefined,
|
|
1519
|
-
exactIn: boolean | SwapAmountType,
|
|
1520
|
-
src: undefined | string | LNURLWithdraw,
|
|
1521
|
-
dst: string | LNURLPay | LightningInvoiceCreateService,
|
|
1522
|
-
options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions
|
|
1523
|
-
): Promise<ISwap<T[C]>> {
|
|
1524
|
-
const srcToken = typeof(_srcToken)==="string" ? this.getToken(_srcToken) as Token<C> : _srcToken;
|
|
1525
|
-
const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
|
|
1526
|
-
const amount = _amount==null ? null : (typeof(_amount)==="bigint" ? _amount : fromDecimal(_amount, exactIn ? srcToken.decimals : dstToken.decimals));
|
|
1527
|
-
if(isBtcToken(srcToken)) {
|
|
1528
|
-
if(isSCToken<C>(dstToken)) {
|
|
1529
|
-
if(typeof(dst)!=="string") throw new Error("Destination for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
|
|
1530
|
-
if(amount==null) throw new Error("Amount cannot be null for from btc swaps!");
|
|
1531
|
-
if(srcToken.lightning) {
|
|
1532
|
-
//FROM_BTCLN
|
|
1533
|
-
if(src!=null) {
|
|
1534
|
-
if(typeof(src)!=="string" && !isLNURLWithdraw(src)) throw new Error("LNURL must be a string or LNURLWithdraw object!");
|
|
1535
|
-
return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
|
|
1536
|
-
this.createFromBTCLNSwapNewViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any) :
|
|
1537
|
-
this.createFromBTCLNSwapViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any);
|
|
1538
|
-
} else {
|
|
1539
|
-
return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
|
|
1540
|
-
this.createFromBTCLNSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any):
|
|
1541
|
-
this.createFromBTCLNSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1542
|
-
}
|
|
1543
|
-
} else {
|
|
1544
|
-
//FROM_BTC
|
|
1545
|
-
if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
|
|
1546
|
-
return this.createFromBTCSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1547
|
-
} else {
|
|
1548
|
-
return this.createFromBTCSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
} else if(isSCToken<C>(srcToken)) {
|
|
1553
|
-
if(isBtcToken(dstToken)) {
|
|
1554
|
-
if(typeof(src)!=="string") throw new Error("Source address for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
|
|
1555
|
-
if(dstToken.lightning) {
|
|
1556
|
-
//TO_BTCLN
|
|
1557
|
-
if(typeof(dst)!=="string" && !isLNURLPay(dst)) throw new Error("Destination LNURL link/lightning invoice must be a string or LNURLPay object!");
|
|
1558
|
-
if(isLNURLPay(dst) || this.Utils.isValidLNURL(dst)) {
|
|
1559
|
-
if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via LNURL-pay!");
|
|
1560
|
-
return this.createToBTCLNSwapViaLNURL(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1561
|
-
} else if(isLightningInvoiceCreateService(dst)) {
|
|
1562
|
-
if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via InvoiceCreateService!");
|
|
1563
|
-
return this.createToBTCLNSwapViaInvoiceCreateService(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1564
|
-
} else if(this.Utils.isLightningInvoice(dst)) {
|
|
1565
|
-
if(!this.Utils.isValidLightningInvoice(dst))
|
|
1566
|
-
throw new Error("Invalid lightning invoice specified, lightning invoice MUST contain pre-set amount!");
|
|
1567
|
-
if(exactIn)
|
|
1568
|
-
throw new Error("Only exact out swaps are possible with lightning invoices, use LNURL links for exact in lightning swaps!");
|
|
1569
|
-
return this.createToBTCLNSwap(srcToken.chainId, src, srcToken.address, dst, undefined, options as any);
|
|
1570
|
-
} else {
|
|
1571
|
-
throw new Error("Supplied parameter is not LNURL link nor lightning invoice (bolt11)!");
|
|
1572
|
-
}
|
|
1573
|
-
} else {
|
|
1574
|
-
//TO_BTC
|
|
1575
|
-
if(typeof(dst)!=="string") throw new Error("Destination bitcoin address must be a string!");
|
|
1576
|
-
if(amount==null) throw new Error("Amount cannot be null for to btc swaps!");
|
|
1577
|
-
return this.createToBTCSwap(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
throw new Error("Unsupported swap type");
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
/**
|
|
1585
|
-
* A helper function to sweep all the funds from a given wallet in a single swap, after getting the quote you can
|
|
1586
|
-
* execute the swap by passing the returned `feeRate` and `utxos` to the {@link SpvFromBTCSwap.execute},
|
|
1587
|
-
* {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.sendBitcoinTransaction} functions along
|
|
1588
|
-
* with `spendFully=true`.
|
|
1589
|
-
*
|
|
1590
|
-
* @example
|
|
1591
|
-
* Create the swap first using this function
|
|
1592
|
-
* ```ts
|
|
1593
|
-
* const {swap, utxos, btcFeeRate} = await swapper.sweepBitcoinWallet(wallet, Tokens.CITREA.CBTC, dstAddress);
|
|
1594
|
-
* ```
|
|
1595
|
-
* Then execute it using one of these execution paths - ensure that you supply the returned `utxos`, `btcFeeRate`
|
|
1596
|
-
* params and also set `spendFully` to `true`!
|
|
1597
|
-
*
|
|
1598
|
-
* a) Execute and pass the returned utxos and btcFeeRate:
|
|
1599
|
-
* ```ts
|
|
1600
|
-
* await swap.execute(wallet, undefined, {feeRate: btcFeeRate, utxos: utxos, spendFully: true});
|
|
1601
|
-
* ```
|
|
1602
|
-
*
|
|
1603
|
-
* b) Get funded PSBT to sign externally:
|
|
1604
|
-
* ```ts
|
|
1605
|
-
* const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt(wallet, btcFeeRate, undefined, utxos, true);
|
|
1606
|
-
* // Sign the psbt at the specified signInputs indices
|
|
1607
|
-
* const signedPsbt = ...;
|
|
1608
|
-
* // Then submit back to the SDK
|
|
1609
|
-
* await swap.submitPsbt(signedPsbt);
|
|
1610
|
-
* ```
|
|
1611
|
-
*
|
|
1612
|
-
* c) Only sign and send the signed PSBT with the provided wallet:
|
|
1613
|
-
* ```ts
|
|
1614
|
-
* await swap.sendBitcoinTransaction(wallet, btcFeeRate, utxos, true);
|
|
1615
|
-
* ```
|
|
1616
|
-
*/
|
|
1617
|
-
async sweepBitcoinWallet<C extends ChainIds<T>>(
|
|
1618
|
-
srcWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
|
|
1619
|
-
_dstToken: SCToken<C> | string,
|
|
1620
|
-
dstAddress: string,
|
|
1621
|
-
options?: SpvFromBTCOptions
|
|
1622
|
-
): Promise<{
|
|
1623
|
-
swap: SpvFromBTCSwap<T[C]>,
|
|
1624
|
-
utxos: BitcoinWalletUtxo[],
|
|
1625
|
-
btcFeeRate: number
|
|
1626
|
-
}> {
|
|
1627
|
-
const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
|
|
1628
|
-
if(!isSCToken<C>(dstToken)) throw new Error("Destination token must be a smart chain token!");
|
|
1629
|
-
|
|
1630
|
-
const wallet = toBitcoinWallet(srcWallet, this._bitcoinRpc, this.bitcoinNetwork);
|
|
1631
|
-
if(wallet.getUtxoPool==null) throw new Error("Wallet needs to support the `getUtxoPool()` function!");
|
|
1632
|
-
|
|
1633
|
-
const walletUtxosPromise = wallet.getUtxoPool();
|
|
1634
|
-
const bitcoinFeeRatePromise = options?.bitcoinFeeRate ?? wallet.getFeeRate();
|
|
1635
|
-
|
|
1636
|
-
const swap = await this.createFromBTCSwapNew(
|
|
1637
|
-
dstToken.chainId, dstAddress, dstToken.address, null, false, undefined, {
|
|
1638
|
-
...options,
|
|
1639
|
-
sourceWalletUtxos: walletUtxosPromise,
|
|
1640
|
-
bitcoinFeeRate: bitcoinFeeRatePromise
|
|
1641
|
-
}
|
|
1642
|
-
);
|
|
1643
|
-
|
|
1644
|
-
return {
|
|
1645
|
-
swap,
|
|
1646
|
-
utxos: await walletUtxosPromise,
|
|
1647
|
-
btcFeeRate: Math.max(swap.minimumBtcFeeRate, await bitcoinFeeRatePromise)
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
/**
|
|
1652
|
-
* Returns all swaps
|
|
1653
|
-
*/
|
|
1654
|
-
getAllSwaps(): Promise<ISwap[]>;
|
|
1655
|
-
/**
|
|
1656
|
-
* Returns all swaps for the specific chain, and optionally also for a specific signer's address
|
|
1657
|
-
*/
|
|
1658
|
-
getAllSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
|
|
1659
|
-
async getAllSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
|
|
1660
|
-
const queryParams: QueryParams[] = [];
|
|
1661
|
-
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1662
|
-
|
|
1663
|
-
if(chainId==null) {
|
|
1664
|
-
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1665
|
-
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1666
|
-
return unifiedSwapStorage.query([queryParams], reviver);
|
|
1667
|
-
}));
|
|
1668
|
-
return res.flat();
|
|
1669
|
-
} else {
|
|
1670
|
-
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1671
|
-
return await unifiedSwapStorage.query([queryParams], reviver);
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
/**
|
|
1676
|
-
* Returns all swaps where an action is required (either claim or refund)
|
|
1677
|
-
*/
|
|
1678
|
-
getActionableSwaps(): Promise<ISwap[]>;
|
|
1679
|
-
/**
|
|
1680
|
-
* Returns swaps where an action is required (either claim or refund) for the specific chain, and optionally also for a specific signer's address
|
|
1681
|
-
*/
|
|
1682
|
-
getActionableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
|
|
1683
|
-
async getActionableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
|
|
1684
|
-
if(chainId==null) {
|
|
1685
|
-
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1686
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1687
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1688
|
-
for(let key in wrappers) {
|
|
1689
|
-
const wrapper = wrappers[key as unknown as SwapType];
|
|
1690
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1691
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1692
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1693
|
-
queryParams.push(swapTypeQueryParams);
|
|
1694
|
-
}
|
|
1695
|
-
return unifiedSwapStorage.query(queryParams, reviver);
|
|
1696
|
-
}));
|
|
1697
|
-
return res.flat().filter(swap => swap.requiresAction());
|
|
1698
|
-
} else {
|
|
1699
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1700
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1701
|
-
for(let key in wrappers) {
|
|
1702
|
-
const wrapper = wrappers[key as unknown as SwapType];
|
|
1703
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1704
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1705
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1706
|
-
queryParams.push(swapTypeQueryParams);
|
|
1707
|
-
}
|
|
1708
|
-
return (await unifiedSwapStorage.query(queryParams, reviver)).filter(swap => swap.requiresAction());
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
/**
|
|
1713
|
-
* Returns all swaps that are refundable
|
|
1714
|
-
*/
|
|
1715
|
-
getRefundableSwaps(): Promise<IToBTCSwap[]>;
|
|
1716
|
-
/**
|
|
1717
|
-
* Returns swaps which are refundable for the specific chain, and optionally also for a specific signer's address
|
|
1718
|
-
*/
|
|
1719
|
-
getRefundableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IToBTCSwap<T[C]>[]>;
|
|
1720
|
-
async getRefundableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IToBTCSwap[]> {
|
|
1721
|
-
if(chainId==null) {
|
|
1722
|
-
const res: IToBTCSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1723
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1724
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1725
|
-
for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
|
|
1726
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1727
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1728
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
|
|
1729
|
-
queryParams.push(swapTypeQueryParams);
|
|
1730
|
-
}
|
|
1731
|
-
return unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
|
|
1732
|
-
}));
|
|
1733
|
-
return res.flat().filter(swap => swap.isRefundable());
|
|
1734
|
-
} else {
|
|
1735
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1736
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1737
|
-
for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
|
|
1738
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1739
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1740
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
|
|
1741
|
-
queryParams.push(swapTypeQueryParams);
|
|
1742
|
-
}
|
|
1743
|
-
const result = await unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
|
|
1744
|
-
return result.filter(swap => swap.isRefundable());
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
/**
|
|
1749
|
-
* Returns all swaps that are manually claimable
|
|
1750
|
-
*/
|
|
1751
|
-
getClaimableSwaps(): Promise<IClaimableSwap[]>;
|
|
1752
|
-
/**
|
|
1753
|
-
* Returns all swaps that are manually claimable for the specific chain, and optionally also for a specific signer's address
|
|
1754
|
-
*/
|
|
1755
|
-
getClaimableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IClaimableSwap<T[C]>[]>;
|
|
1756
|
-
async getClaimableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IClaimableSwap[]> {
|
|
1757
|
-
if(chainId==null) {
|
|
1758
|
-
const res: IClaimableSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1759
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1760
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1761
|
-
for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
|
|
1762
|
-
if(wrapper==null) continue;
|
|
1763
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1764
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1765
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
|
|
1766
|
-
queryParams.push(swapTypeQueryParams);
|
|
1767
|
-
}
|
|
1768
|
-
return unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
|
|
1769
|
-
}));
|
|
1770
|
-
return res.flat().filter(swap => swap.isClaimable());
|
|
1771
|
-
} else {
|
|
1772
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1773
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1774
|
-
for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
|
|
1775
|
-
if(wrapper==null) continue;
|
|
1776
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1777
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1778
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
|
|
1779
|
-
queryParams.push(swapTypeQueryParams);
|
|
1780
|
-
}
|
|
1781
|
-
const result = await unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
|
|
1782
|
-
return result.filter(swap => swap.isClaimable());
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
/**
|
|
1787
|
-
* Returns swap with a specific id (identifier)
|
|
1788
|
-
*/
|
|
1789
|
-
getSwapById(id: string): Promise<ISwap>;
|
|
1790
|
-
/**
|
|
1791
|
-
* Returns swap with a specific id (identifier) on a specific chain and optionally with a signer
|
|
1792
|
-
*/
|
|
1793
|
-
getSwapById<C extends ChainIds<T>>(id: string, chainId: C, signer?: string): Promise<ISwap<T[C]>>;
|
|
1794
|
-
async getSwapById<C extends ChainIds<T>>(id: string, chainId?: C, signer?: string): Promise<ISwap> {
|
|
1795
|
-
//Check in pending swaps first
|
|
1796
|
-
if(chainId!=null) {
|
|
1797
|
-
for(let key in this._chains[chainId].wrappers) {
|
|
1798
|
-
const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
|
|
1799
|
-
const result = wrapper._getPendingSwap(id);
|
|
1800
|
-
if(result!=null) {
|
|
1801
|
-
if (signer != null) {
|
|
1802
|
-
if (result._getInitiator() === signer) return result;
|
|
1803
|
-
} else {
|
|
1804
|
-
return result;
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
} else {
|
|
1809
|
-
for(let chainId in this._chains) {
|
|
1810
|
-
for(let key in this._chains[chainId].wrappers) {
|
|
1811
|
-
const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
|
|
1812
|
-
const result = wrapper._getPendingSwap(id);
|
|
1813
|
-
if(result!=null) {
|
|
1814
|
-
if(signer!=null) {
|
|
1815
|
-
if(result._getInitiator()===signer) return result;
|
|
1816
|
-
} else {
|
|
1817
|
-
return result;
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
const queryParams: QueryParams[] = [];
|
|
1825
|
-
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1826
|
-
queryParams.push({key: "id", value: id});
|
|
1827
|
-
if(chainId==null) {
|
|
1828
|
-
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1829
|
-
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1830
|
-
return unifiedSwapStorage.query([queryParams], reviver);
|
|
1831
|
-
}));
|
|
1832
|
-
return res.flat()[0];
|
|
1833
|
-
} else {
|
|
1834
|
-
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1835
|
-
return (await unifiedSwapStorage.query([queryParams], reviver))[0];
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
/**
|
|
1840
|
-
* Returns the swap with a proper return type, or `undefined` if not found or has wrong type
|
|
1841
|
-
*
|
|
1842
|
-
* @param id An ID of the swap ({@link ISwap.getId})
|
|
1843
|
-
* @param chainId Chain identifier of the smart chain where the swap was initiated
|
|
1844
|
-
* @param swapType Type of the swap
|
|
1845
|
-
* @param signer An optional required smart chain signer address to fetch the swap for
|
|
1846
|
-
*/
|
|
1847
|
-
async getTypedSwapById<C extends ChainIds<T>, S extends SwapType>(id: string, chainId: C, swapType: S, signer?: string): Promise<SwapTypeMapping<T[C]>[S] | undefined> {
|
|
1848
|
-
let _swapType: SwapType = swapType;
|
|
1849
|
-
if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC))
|
|
1850
|
-
_swapType = SwapType.SPV_VAULT_FROM_BTC;
|
|
1851
|
-
if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO))
|
|
1852
|
-
_swapType = SwapType.FROM_BTCLN_AUTO;
|
|
1853
|
-
|
|
1854
|
-
const wrapper = this._chains[chainId].wrappers[_swapType];
|
|
1855
|
-
if(wrapper==null) return;
|
|
1856
|
-
|
|
1857
|
-
const result = wrapper._getPendingSwap(id);
|
|
1858
|
-
if(result!=null) {
|
|
1859
|
-
if (signer != null) {
|
|
1860
|
-
if (result._getInitiator() === signer) return result as any;
|
|
1861
|
-
} else {
|
|
1862
|
-
return result as any;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
const queryParams: QueryParams[] = [];
|
|
1867
|
-
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1868
|
-
queryParams.push({key: "id", value: id});
|
|
1869
|
-
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1870
|
-
const swap = (await unifiedSwapStorage.query([queryParams], reviver))[0];
|
|
1871
|
-
if(isSwapType(swap, swapType)) return swap;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
private async syncSwapsForChain<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<void> {
|
|
1875
|
-
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1876
|
-
const queryParams: Array<QueryParams[]> = [];
|
|
1877
|
-
for(let key in wrappers) {
|
|
1878
|
-
const wrapper = wrappers[key as unknown as SwapType];
|
|
1879
|
-
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1880
|
-
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1881
|
-
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1882
|
-
queryParams.push(swapTypeQueryParams);
|
|
1883
|
-
}
|
|
1884
|
-
this.logger.debug("_syncSwaps(): Querying swaps swaps for chain "+chainId+"!");
|
|
1885
|
-
const swaps = await unifiedSwapStorage.query(queryParams, reviver);
|
|
1886
|
-
this.logger.debug("_syncSwaps(): Syncing "+swaps.length+" swaps!");
|
|
1887
|
-
|
|
1888
|
-
const changedSwaps: ISwap<T[C]>[] = [];
|
|
1889
|
-
const removeSwaps: ISwap<T[C]>[] = [];
|
|
1890
|
-
|
|
1891
|
-
const assortedSwaps: {[swapType in SwapType]?: ISwap<T[string]>[]} = {};
|
|
1892
|
-
swaps.forEach(swap => {
|
|
1893
|
-
(assortedSwaps[swap.getType()] ??= []).push(swap);
|
|
1894
|
-
});
|
|
1895
|
-
|
|
1896
|
-
for(let key in assortedSwaps) {
|
|
1897
|
-
const swapType = key as unknown as SwapType;
|
|
1898
|
-
const wrapperSwaps = assortedSwaps[swapType];
|
|
1899
|
-
const wrapper: ISwapWrapper<T[C], any> = wrappers[swapType];
|
|
1900
|
-
const result = await wrapper.checkPastSwaps(wrapperSwaps, true);
|
|
1901
|
-
changedSwaps.push(...result.changedSwaps);
|
|
1902
|
-
removeSwaps.push(...result.removeSwaps);
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
this.logger.debug("_syncSwaps(): Done syncing "+swaps.length+" swaps, saving "+changedSwaps.length+" changed swaps, removing "+removeSwaps.length+" swaps!");
|
|
1906
|
-
await unifiedSwapStorage.saveAll(changedSwaps);
|
|
1907
|
-
await unifiedSwapStorage.removeAll(removeSwaps);
|
|
1908
|
-
|
|
1909
|
-
changedSwaps.forEach(swap => swap._emitEvent());
|
|
1910
|
-
removeSwaps.forEach(swap => swap._emitEvent());
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
/**
|
|
1914
|
-
* Deletes the swaps from the persistent storage backend. Note that some data (like lightning network
|
|
1915
|
-
* amounts and bolt11 invoices) are purely off-chain and can never be recovered later just from
|
|
1916
|
-
* on-chain data!
|
|
1917
|
-
*
|
|
1918
|
-
* @param chainId Optional, to only delete swaps for this smart chain
|
|
1919
|
-
* @param signer Optional, to only delete swaps for this smart chain signer (`chainId` param must be
|
|
1920
|
-
* set to delete only signer's swaps)
|
|
1921
|
-
*/
|
|
1922
|
-
async wipeStorage<C extends ChainIds<T>>(chainId?: C, signer?: string) {
|
|
1923
|
-
if(chainId==null) {
|
|
1924
|
-
const swaps = await this.getAllSwaps();
|
|
1925
|
-
const chainSwaps: {
|
|
1926
|
-
[chainId: string]: ISwap<any>[]
|
|
1927
|
-
} = {};
|
|
1928
|
-
swaps.forEach(swap => (chainSwaps[swap.chainIdentifier] ??= []).push(swap));
|
|
1929
|
-
for(let chainId in chainSwaps) {
|
|
1930
|
-
const currentChainSwaps = chainSwaps[chainId];
|
|
1931
|
-
if(this._chains[chainId]==null) {
|
|
1932
|
-
this.logger.warn(`wipeStorage(): Attempted to remove ${currentChainSwaps.length} swaps on ${chainId}, but smart chain not known!`);
|
|
1933
|
-
continue;
|
|
1934
|
-
}
|
|
1935
|
-
await this._chains[chainId].unifiedSwapStorage.removeAll(currentChainSwaps);
|
|
1936
|
-
this.logger.debug(`wipeStorage(): Successfully removed ${currentChainSwaps.length} swaps on ${chainId}!`);
|
|
1937
|
-
}
|
|
1938
|
-
} else {
|
|
1939
|
-
if(this._chains[chainId]==null) throw new Error(`wipeStorage(): Smart chain with identifier ${chainId} not found!`);
|
|
1940
|
-
const swaps = await this.getAllSwaps(chainId, signer);
|
|
1941
|
-
await this._chains[chainId].unifiedSwapStorage.removeAll(swaps);
|
|
1942
|
-
this.logger.debug(`wipeStorage(): Successfully removed ${swaps.length} swaps on ${chainId}!`);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
/**
|
|
1947
|
-
* Synchronizes swaps from on-chain, this is ran automatically when SDK is initialized, hence
|
|
1948
|
-
* should only be ran manually when `dontCheckPastSwaps=true` is passed in the swapper options,
|
|
1949
|
-
* also deletes expired quotes
|
|
1950
|
-
*
|
|
1951
|
-
* @param chainId Optional chain identifier to only run swap sync for a single smart chain
|
|
1952
|
-
* @param signer Optional signer to only run swap sync for swaps initiated by this signer
|
|
1953
|
-
*/
|
|
1954
|
-
async _syncSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<void> {
|
|
1955
|
-
if(chainId==null) {
|
|
1956
|
-
await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1957
|
-
return this.syncSwapsForChain(chainId, signer);
|
|
1958
|
-
}));
|
|
1959
|
-
} else {
|
|
1960
|
-
await this.syncSwapsForChain(chainId, signer);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
/**
|
|
1965
|
-
* Recovers swaps from on-chain historical data.
|
|
1966
|
-
*
|
|
1967
|
-
* Please note that the recovered swaps might not be complete (i.e. missing amounts or addresses), as some
|
|
1968
|
-
* of the swap data is purely off-chain and can never be recovered purely from on-chain data. This
|
|
1969
|
-
* functions tries to recover as much swap data as possible.
|
|
1970
|
-
*
|
|
1971
|
-
* @param chainId Smart chain identifier string to recover the swaps from
|
|
1972
|
-
* @param signer Signer address to recover the swaps for
|
|
1973
|
-
* @param startBlockheight Optional starting blockheight for swap data recovery, will only check swaps
|
|
1974
|
-
* initiated after this blockheight
|
|
1975
|
-
*/
|
|
1976
|
-
async recoverSwaps<C extends ChainIds<T>>(chainId: C, signer: string, startBlockheight?: number): Promise<ISwap<T[C]>[]> {
|
|
1977
|
-
//TODO: Recover swaps from all the known contract versions
|
|
1978
|
-
const {versionedContracts, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1979
|
-
|
|
1980
|
-
const recoveredSwaps: ISwap<T[C]>[] = [];
|
|
1981
|
-
let someVersionSupportsRecovery = false;
|
|
1982
|
-
const recoveredEscrowStates: {
|
|
1983
|
-
[p: string]: {
|
|
1984
|
-
init?: {
|
|
1985
|
-
data: SwapData
|
|
1986
|
-
getInitTxId: () => Promise<string>
|
|
1987
|
-
getTxBlock: () => Promise<{
|
|
1988
|
-
blockTime: number
|
|
1989
|
-
blockHeight: number
|
|
1990
|
-
}>
|
|
1991
|
-
},
|
|
1992
|
-
state: SwapCommitState
|
|
1993
|
-
contractVersion: string
|
|
1994
|
-
}
|
|
1995
|
-
} = {};
|
|
1996
|
-
const recoveredSpvStates: {
|
|
1997
|
-
[contractVersion: string]: {
|
|
1998
|
-
[escrowHash: string]: SpvWithdrawalClaimedState | SpvWithdrawalFrontedState
|
|
1999
|
-
}
|
|
2000
|
-
} = {};
|
|
2001
|
-
|
|
2002
|
-
for(let contractVersion in versionedContracts) {
|
|
2003
|
-
const {swapContract, spvVaultContract} = versionedContracts[contractVersion];
|
|
2004
|
-
|
|
2005
|
-
if(
|
|
2006
|
-
swapContract.getHistoricalSwaps==null ||
|
|
2007
|
-
(spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
|
|
2008
|
-
) {
|
|
2009
|
-
this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
|
|
2010
|
-
continue;
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
someVersionSupportsRecovery = true;
|
|
2014
|
-
|
|
2015
|
-
const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
|
|
2016
|
-
const spvVaultData = wrappers[SwapType.SPV_VAULT_FROM_BTC]==null
|
|
2017
|
-
? undefined
|
|
2018
|
-
: await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
|
|
2019
|
-
|
|
2020
|
-
for(let key in swaps) recoveredEscrowStates[key] = {...swaps[key], contractVersion};
|
|
2021
|
-
if(spvVaultData!=null) for(let key in spvVaultData.withdrawals) (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
|
|
2022
|
-
}
|
|
2023
|
-
if(!someVersionSupportsRecovery) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
|
|
2024
|
-
|
|
2025
|
-
const escrowHashes = Object.keys(recoveredEscrowStates);
|
|
2026
|
-
for(let contractVersion in recoveredSpvStates) Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
|
|
2027
|
-
this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
|
|
2028
|
-
this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
|
|
2029
|
-
const knownSwapsArray = await unifiedSwapStorage.query([[{key: "escrowHash", value: escrowHashes}]], reviver);
|
|
2030
|
-
const knownSwaps: {[escrowHash: string]: ISwap<T[C]>} = {};
|
|
2031
|
-
knownSwapsArray.forEach(val => {
|
|
2032
|
-
const escrowHash = val._getEscrowHash();
|
|
2033
|
-
if(escrowHash!=null) knownSwaps[escrowHash] = val;
|
|
2034
|
-
});
|
|
2035
|
-
this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
|
|
2036
|
-
|
|
2037
|
-
for(let escrowHash in recoveredEscrowStates) {
|
|
2038
|
-
const {init, state, contractVersion} = recoveredEscrowStates[escrowHash];
|
|
2039
|
-
const knownSwap = knownSwaps[escrowHash];
|
|
2040
|
-
const { swapContract } = versionedContracts[contractVersion];
|
|
2041
|
-
|
|
2042
|
-
if(knownSwap==null) {
|
|
2043
|
-
if(init==null) {
|
|
2044
|
-
this.logger.warn(`recoverSwaps(escrow): Fetched ${escrowHash} swap state, but swap not found locally!`);
|
|
2045
|
-
continue;
|
|
2046
|
-
}
|
|
2047
|
-
} else if(knownSwap instanceof IEscrowSwap) {
|
|
2048
|
-
this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
|
|
2049
|
-
if((knownSwap._contractVersion ?? "v1")!==contractVersion) {
|
|
2050
|
-
this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
|
|
2051
|
-
continue;
|
|
2052
|
-
}
|
|
2053
|
-
if(await knownSwap._forciblySetOnchainState(state)) {
|
|
2054
|
-
await knownSwap._save();
|
|
2055
|
-
}
|
|
2056
|
-
continue;
|
|
2057
|
-
} else {
|
|
2058
|
-
this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap already known and in local storage!`);
|
|
2059
|
-
continue;
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
const data = init.data;
|
|
2063
|
-
|
|
2064
|
-
//Classify swap
|
|
2065
|
-
let swap: ISwap<T[C]> | undefined | null;
|
|
2066
|
-
let typeIdentified: boolean = false;
|
|
2067
|
-
if(data.getType()===ChainSwapType.HTLC) {
|
|
2068
|
-
if(data.isOfferer(signer)) {
|
|
2069
|
-
//To BTCLN
|
|
2070
|
-
typeIdentified = true;
|
|
2071
|
-
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
|
|
2072
|
-
swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2073
|
-
} else if(data.isClaimer(signer)) {
|
|
2074
|
-
//From BTCLN
|
|
2075
|
-
typeIdentified = true;
|
|
2076
|
-
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
|
|
2077
|
-
if(swapContract.supportsInitWithoutClaimer && wrappers[SwapType.FROM_BTCLN_AUTO]!=null) {
|
|
2078
|
-
swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2079
|
-
} else {
|
|
2080
|
-
swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
} else if(data.getType()===ChainSwapType.CHAIN_NONCED) {
|
|
2084
|
-
//To BTC
|
|
2085
|
-
typeIdentified = true;
|
|
2086
|
-
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
|
|
2087
|
-
swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2088
|
-
} else if(data.getType()===ChainSwapType.CHAIN) {
|
|
2089
|
-
//From BTC
|
|
2090
|
-
typeIdentified = true;
|
|
2091
|
-
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
|
|
2092
|
-
swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
if(swap!=null) {
|
|
2096
|
-
recoveredSwaps.push(swap);
|
|
2097
|
-
} else {
|
|
2098
|
-
if(typeIdentified) this.logger.debug(`recoverSwaps(escrow): Swap data type correctly identified but swap returned is null for swap ${escrowHash}`);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
|
|
2102
|
-
for(let contractVersion in recoveredSpvStates) {
|
|
2103
|
-
const { spvVaultContract } = versionedContracts[contractVersion];
|
|
2104
|
-
const spvVaultData = recoveredSpvStates[contractVersion];
|
|
2105
|
-
|
|
2106
|
-
const vaultsData = await spvVaultContract.getMultipleVaultData(
|
|
2107
|
-
Object.keys(spvVaultData)
|
|
2108
|
-
.map(btcTxId => ({
|
|
2109
|
-
owner: spvVaultData[btcTxId].owner,
|
|
2110
|
-
vaultId: spvVaultData[btcTxId].vaultId
|
|
2111
|
-
}))
|
|
2112
|
-
);
|
|
2113
|
-
|
|
2114
|
-
for(let btcTxId in spvVaultData) {
|
|
2115
|
-
const state = spvVaultData[btcTxId];
|
|
2116
|
-
const knownSwap = knownSwaps[btcTxId];
|
|
2117
|
-
|
|
2118
|
-
if(knownSwap!=null) {
|
|
2119
|
-
if(knownSwap instanceof SpvFromBTCSwap) {
|
|
2120
|
-
this.logger.debug(`recoverSwaps(spv_vault): Forcibly updating ${btcTxId} swap: swap already known and in local storage!`);
|
|
2121
|
-
//TODO: Forcibly set on-chain state to the swap
|
|
2122
|
-
// if(await knownSwap._forciblySetOnchainState(state)) {
|
|
2123
|
-
// await knownSwap._save();
|
|
2124
|
-
// }
|
|
2125
|
-
continue;
|
|
2126
|
-
} else {
|
|
2127
|
-
this.logger.debug(`recoverSwaps(spv_vault): Skipping ${btcTxId} swap: swap already known and in local storage!`);
|
|
2128
|
-
continue;
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
const lp = this.intermediaryDiscovery.intermediaries.find(
|
|
2133
|
-
val => val.supportsChain(chainId) && state.owner.toLowerCase()===val.getAddress(chainId).toLowerCase()
|
|
2134
|
-
);
|
|
2135
|
-
const swap = await wrappers[SwapType.SPV_VAULT_FROM_BTC].recoverFromState(
|
|
2136
|
-
state,
|
|
2137
|
-
contractVersion,
|
|
2138
|
-
vaultsData[state.owner]?.[state.vaultId.toString(10)],
|
|
2139
|
-
lp
|
|
2140
|
-
);
|
|
2141
|
-
if(swap!=null) {
|
|
2142
|
-
recoveredSwaps.push(swap);
|
|
2143
|
-
} else {
|
|
2144
|
-
this.logger.debug(`recoverSwaps(spv_vault): Swap data type correctly identified but swap returned is null for swap ${btcTxId}`);
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
this.logger.debug(`recoverSwaps(): Successfully recovered ${recoveredSwaps.length} swaps!`);
|
|
2150
|
-
|
|
2151
|
-
return recoveredSwaps;
|
|
2152
|
-
}
|
|
2153
|
-
|
|
2154
|
-
getToken(ticker: "BTC" | "BITCOIN-BTC"): BtcToken<false>;
|
|
2155
|
-
getToken(ticker: "BTCLN" | "BTC-LN" | "LIGHTNING-BTC"): BtcToken<true>;
|
|
2156
|
-
getToken<ChainIdentifier extends ChainIds<T>>(ticker: `${ChainIdentifier}-${string}`): SCToken<ChainIdentifier>;
|
|
2157
|
-
getToken(tickerOrAddress: string): Token<ChainIds<T>>;
|
|
2158
|
-
/**
|
|
2159
|
-
* Returns the {@link Token} object for a given token
|
|
2160
|
-
*
|
|
2161
|
-
* @param tickerOrAddress Token to return the object for, can use multiple formats:
|
|
2162
|
-
* - a) token ticker, such as `"BTC"`, `"SOL"`, etc.
|
|
2163
|
-
* - b) token ticker prefixed with smart chain identifier, such as `"SOLANA-SOL"`, `"SOLANA-USDC"`, etc.
|
|
2164
|
-
* - c) token address
|
|
2165
|
-
*/
|
|
2166
|
-
getToken(tickerOrAddress: string): Token<ChainIds<T>> {
|
|
2167
|
-
//Btc tokens - BTC, BTCLN, BTC-LN
|
|
2168
|
-
if(tickerOrAddress==="BTC" || tickerOrAddress==="BITCOIN-BTC") return BitcoinTokens.BTC;
|
|
2169
|
-
if(tickerOrAddress==="BTCLN" || tickerOrAddress==="BTC-LN" || tickerOrAddress==="LIGHTNING-BTC") return BitcoinTokens.BTCLN;
|
|
2170
|
-
|
|
2171
|
-
//Check if the ticker is in format <chainId>-<ticker>, i.e. SOLANA-USDC, STARKNET-WBTC
|
|
2172
|
-
if(tickerOrAddress.includes("-")) {
|
|
2173
|
-
const [chainId, ticker] = tickerOrAddress.split("-");
|
|
2174
|
-
const token = this._tokensByTicker[chainId]?.[ticker];
|
|
2175
|
-
if(token==null) throw new UserError(`Not found ticker: ${ticker} for chainId: ${chainId}`);
|
|
2176
|
-
return token;
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
const possibleTokens: SCToken[] = [];
|
|
2180
|
-
for(let chainId in this._chains) {
|
|
2181
|
-
const chain = this._chains[chainId];
|
|
2182
|
-
if(chain.chainInterface.isValidToken(tickerOrAddress)) {
|
|
2183
|
-
//Try to find in known token addresses
|
|
2184
|
-
const token = this._tokens[chainId]?.[tickerOrAddress];
|
|
2185
|
-
if(token!=null) return token;
|
|
2186
|
-
} else {
|
|
2187
|
-
//Check in known tickers
|
|
2188
|
-
const token = this._tokensByTicker[chainId]?.[tickerOrAddress];
|
|
2189
|
-
if(token!=null) possibleTokens.push(token);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
if(possibleTokens.length===0) throw new UserError(`Specified token address or ticker ${tickerOrAddress} not found!`);
|
|
2194
|
-
//In case we've found the token in multiple chains
|
|
2195
|
-
if(possibleTokens.length>1) throw new UserError(`A ticker ${tickerOrAddress} has been found in multiple chains, narrow it down by using <chainId>-${tickerOrAddress} notation`)
|
|
2196
|
-
return possibleTokens[0];
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
/**
|
|
2200
|
-
* Creates a child swapper instance with a given smart chain
|
|
2201
|
-
*
|
|
2202
|
-
* @param chainIdentifier Smart chain identifier for the created child swapper instance
|
|
2203
|
-
*/
|
|
2204
|
-
withChain<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier): SwapperWithChain<T, ChainIdentifier> {
|
|
2205
|
-
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
2206
|
-
return new SwapperWithChain<T, ChainIdentifier>(this, chainIdentifier as ChainIdentifier);
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
/**
|
|
2210
|
-
* Returns an array of all the supported smart chains
|
|
2211
|
-
*/
|
|
2212
|
-
getSmartChains(): ChainIds<T>[] {
|
|
2213
|
-
return Object.keys(this._chains);
|
|
2214
|
-
}
|
|
2215
|
-
|
|
2216
|
-
/**
|
|
2217
|
-
* Returns whether the SDK supports a given swap type on a given chain based on currently known LPs
|
|
2218
|
-
*
|
|
2219
|
-
* @param chainId Smart chain identifier string
|
|
2220
|
-
* @param swapType Swap protocol type
|
|
2221
|
-
*/
|
|
2222
|
-
supportsSwapType<
|
|
2223
|
-
ChainIdentifier extends ChainIds<T>,
|
|
2224
|
-
Type extends SwapType
|
|
2225
|
-
>(chainId: ChainIdentifier, swapType: Type): SupportsSwapType<T[ChainIdentifier], Type> {
|
|
2226
|
-
return (this._chains[chainId]?.wrappers[swapType] != null) as any;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
/**
|
|
2230
|
-
* @internal
|
|
2231
|
-
*/
|
|
2232
|
-
getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<true>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN);
|
|
2233
|
-
/**
|
|
2234
|
-
* @internal
|
|
2235
|
-
*/
|
|
2236
|
-
getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<false>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC);
|
|
2237
|
-
/**
|
|
2238
|
-
* @internal
|
|
2239
|
-
*/
|
|
2240
|
-
getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<false>): SwapType.TO_BTC;
|
|
2241
|
-
/**
|
|
2242
|
-
* @internal
|
|
2243
|
-
*/
|
|
2244
|
-
getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<true>): SwapType.TO_BTCLN;
|
|
2245
|
-
/**
|
|
2246
|
-
* Returns type of the swap based on input and output tokens specified
|
|
2247
|
-
*
|
|
2248
|
-
* @param srcToken Source token
|
|
2249
|
-
* @param dstToken Destination token
|
|
2250
|
-
*/
|
|
2251
|
-
getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN;
|
|
2252
|
-
/**
|
|
2253
|
-
* Returns type of the swap based on input and output tokens specified
|
|
2254
|
-
*
|
|
2255
|
-
* @param srcToken Source token
|
|
2256
|
-
* @param dstToken Destination token
|
|
2257
|
-
*/
|
|
2258
|
-
getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN {
|
|
2259
|
-
if(isSCToken(srcToken)) {
|
|
2260
|
-
if(!isBtcToken(dstToken)) throw new Error("Swap not supported");
|
|
2261
|
-
if(dstToken.lightning) {
|
|
2262
|
-
return SwapType.TO_BTCLN;
|
|
2263
|
-
} else {
|
|
2264
|
-
return SwapType.TO_BTC;
|
|
2265
|
-
}
|
|
2266
|
-
} else if(isBtcToken(srcToken)) {
|
|
2267
|
-
if(!isSCToken(dstToken)) throw new Error("Swap not supported");
|
|
2268
|
-
if(srcToken.lightning) {
|
|
2269
|
-
if(this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO)) {
|
|
2270
|
-
return SwapType.FROM_BTCLN_AUTO;
|
|
2271
|
-
} else {
|
|
2272
|
-
return SwapType.FROM_BTCLN;
|
|
2273
|
-
}
|
|
2274
|
-
} else {
|
|
2275
|
-
if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
|
|
2276
|
-
return SwapType.SPV_VAULT_FROM_BTC;
|
|
2277
|
-
} else {
|
|
2278
|
-
return SwapType.FROM_BTC;
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
throw new Error("Swap not supported");
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
/**
|
|
2286
|
-
* Helper information about various swap protocol and their features:
|
|
2287
|
-
* - `requiresInputWallet`: Whether a swap requires a connected wallet on the input chain able to sign
|
|
2288
|
-
* arbitrary transaction
|
|
2289
|
-
* - `requiresOutputWallet`: Whether a swap requires a connected wallet on the output chain able to sign
|
|
2290
|
-
* arbitrary transactions
|
|
2291
|
-
* - `supportsGasDrop`: Whether a swap supports the "gas drop" feature, allowing to user to receive a small
|
|
2292
|
-
* amount of native token as part of the swap when swapping to smart chains
|
|
2293
|
-
*
|
|
2294
|
-
* Uses a `Record` type here, use the {@link SwapProtocolInfo} import for a literal readonly type, with
|
|
2295
|
-
* pre-filled exact values in the type.
|
|
2296
|
-
*/
|
|
2297
|
-
readonly SwapTypeInfo = SwapProtocolInfo as Record<SwapType, {
|
|
2298
|
-
requiresInputWallet: boolean,
|
|
2299
|
-
requiresOutputWallet: boolean,
|
|
2300
|
-
supportsGasDrop: boolean
|
|
2301
|
-
}>;
|
|
2302
|
-
|
|
2303
|
-
/**
|
|
2304
|
-
* Returns minimum/maximum limits for inputs and outputs for a swap between given tokens
|
|
2305
|
-
*
|
|
2306
|
-
* @param srcToken Source token
|
|
2307
|
-
* @param dstToken Destination token
|
|
2308
|
-
*/
|
|
2309
|
-
getSwapLimits<C extends ChainIds<T>, A extends Token<C>, B extends Token<C>>(srcToken: A, dstToken: B): {
|
|
2310
|
-
input: {min: TokenAmount<A>, max?: TokenAmount<A>},
|
|
2311
|
-
output: {min: TokenAmount<B>, max?: TokenAmount<B>}
|
|
2312
|
-
} {
|
|
2313
|
-
const swapType = this.getSwapType(srcToken, dstToken);
|
|
2314
|
-
const scToken = isSCToken(srcToken) ? srcToken : isSCToken(dstToken) ? dstToken : null;
|
|
2315
|
-
if(scToken==null) throw new Error("At least one token needs to be a smart chain token!");
|
|
2316
|
-
const result: {input: {min?: bigint, max?: bigint}, output: {min?: bigint, max?: bigint}} = {
|
|
2317
|
-
input: {},
|
|
2318
|
-
output: {}
|
|
2319
|
-
};
|
|
2320
|
-
for(let lp of this.intermediaryDiscovery.intermediaries) {
|
|
2321
|
-
const lpMinMax = lp.getSwapLimits(swapType, scToken.chainId, scToken.address);
|
|
2322
|
-
if(lpMinMax==null) continue;
|
|
2323
|
-
result.input.min = result.input.min==null ? lpMinMax.input.min : bigIntMin(result.input.min, lpMinMax.input.min);
|
|
2324
|
-
result.input.max = result.input.max==null ? lpMinMax.input.max : bigIntMax(result.input.max, lpMinMax.input.max);
|
|
2325
|
-
result.output.min = result.output.min==null ? lpMinMax.output.min : bigIntMin(result.output.min, lpMinMax.output.min);
|
|
2326
|
-
result.output.max = result.output.max==null ? lpMinMax.output.max : bigIntMax(result.output.max, lpMinMax.output.max);
|
|
2327
|
-
}
|
|
2328
|
-
return {
|
|
2329
|
-
input: {
|
|
2330
|
-
min: toTokenAmount(result.input.min ?? 1n, srcToken, this.prices),
|
|
2331
|
-
max: result.input.max==null ? undefined : toTokenAmount(result.input.max, srcToken, this.prices),
|
|
2332
|
-
},
|
|
2333
|
-
output: {
|
|
2334
|
-
min: toTokenAmount(result.output.min ?? 1n, dstToken, this.prices),
|
|
2335
|
-
max: result.output.max==null ? undefined : toTokenAmount(result.output.max, dstToken, this.prices),
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
/**
|
|
2341
|
-
* Returns an array of supported tokens either on the input or on the output of a swap
|
|
2342
|
-
*
|
|
2343
|
-
* @param input Whether to return input tokens or output tokens
|
|
2344
|
-
*/
|
|
2345
|
-
getSupportedTokens(input: SwapSide | boolean): Token[] {
|
|
2346
|
-
const tokens: {[chainId: string]: Set<string>} = {};
|
|
2347
|
-
let lightning = false;
|
|
2348
|
-
let btc = false;
|
|
2349
|
-
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2350
|
-
for(let swapType of [SwapType.TO_BTC, SwapType.TO_BTCLN, SwapType.FROM_BTC, SwapType.FROM_BTCLN, SwapType.SPV_VAULT_FROM_BTC, SwapType.FROM_BTCLN_AUTO]) {
|
|
2351
|
-
if(lp.services[swapType]?.chainTokens==null) continue;
|
|
2352
|
-
for(let chainId of this.getSmartChains()) {
|
|
2353
|
-
if(this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC) ? swapType===SwapType.FROM_BTC : swapType===SwapType.SPV_VAULT_FROM_BTC) continue;
|
|
2354
|
-
if(this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO) ? swapType===SwapType.FROM_BTCLN : swapType===SwapType.FROM_BTCLN_AUTO) continue;
|
|
2355
|
-
const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
|
|
2356
|
-
if(chainTokens==null) continue;
|
|
2357
|
-
for (let tokenAddress of chainTokens) {
|
|
2358
|
-
if(input) {
|
|
2359
|
-
if(swapType===SwapType.TO_BTC || swapType===SwapType.TO_BTCLN) {
|
|
2360
|
-
tokens[chainId] ??= new Set();
|
|
2361
|
-
tokens[chainId].add(tokenAddress);
|
|
2362
|
-
}
|
|
2363
|
-
if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTCLN_AUTO) {
|
|
2364
|
-
lightning = true;
|
|
2365
|
-
}
|
|
2366
|
-
if(swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC) {
|
|
2367
|
-
btc = true;
|
|
2368
|
-
}
|
|
2369
|
-
} else {
|
|
2370
|
-
if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC || swapType===SwapType.FROM_BTCLN_AUTO) {
|
|
2371
|
-
tokens[chainId] ??= new Set();
|
|
2372
|
-
tokens[chainId].add(tokenAddress);
|
|
2373
|
-
}
|
|
2374
|
-
if(swapType===SwapType.TO_BTCLN) {
|
|
2375
|
-
lightning = true;
|
|
2376
|
-
}
|
|
2377
|
-
if(swapType===SwapType.TO_BTC) {
|
|
2378
|
-
btc = true;
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
});
|
|
2385
|
-
const output: Token[] = [];
|
|
2386
|
-
if(lightning) output.push(BitcoinTokens.BTCLN);
|
|
2387
|
-
if(btc) output.push(BitcoinTokens.BTC);
|
|
2388
|
-
for(let chainId in tokens) {
|
|
2389
|
-
tokens[chainId].forEach(tokenAddress => {
|
|
2390
|
-
const token = this._tokens?.[chainId]?.[tokenAddress];
|
|
2391
|
-
if(token!=null) output.push(token);
|
|
2392
|
-
})
|
|
2393
|
-
}
|
|
2394
|
-
return output;
|
|
2395
|
-
}
|
|
2396
|
-
|
|
2397
|
-
/**
|
|
2398
|
-
* Returns a set of supported tokens by all the intermediaries offering a specific swap service
|
|
2399
|
-
*
|
|
2400
|
-
* @param _swapType Swap service type to check supported tokens for
|
|
2401
|
-
*/
|
|
2402
|
-
private getSupportedTokensForSwapType(_swapType: SwapType): SCToken[] {
|
|
2403
|
-
const tokens: {[chainId: string]: Set<string>} = {};
|
|
2404
|
-
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2405
|
-
for(let chainId of this.getSmartChains()) {
|
|
2406
|
-
let swapType = _swapType;
|
|
2407
|
-
if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC)) swapType = SwapType.SPV_VAULT_FROM_BTC;
|
|
2408
|
-
if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO)) swapType = SwapType.FROM_BTCLN_AUTO;
|
|
2409
|
-
if(lp.services[swapType]?.chainTokens==null) break;
|
|
2410
|
-
const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
|
|
2411
|
-
if(chainTokens==null) continue;
|
|
2412
|
-
for(let tokenAddress of chainTokens) {
|
|
2413
|
-
tokens[chainId] ??= new Set();
|
|
2414
|
-
tokens[chainId].add(tokenAddress);
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
});
|
|
2418
|
-
const output: SCToken[] = [];
|
|
2419
|
-
for(let chainId in tokens) {
|
|
2420
|
-
tokens[chainId].forEach(tokenAddress => {
|
|
2421
|
-
const token = this._tokens?.[chainId]?.[tokenAddress];
|
|
2422
|
-
if(token!=null) output.push(token);
|
|
2423
|
-
})
|
|
2424
|
-
}
|
|
2425
|
-
return output;
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
/**
|
|
2429
|
-
* Returns the set of supported token addresses by all the intermediaries we know of offering a specific swapType service
|
|
2430
|
-
*
|
|
2431
|
-
* @param chainIdentifier Chain identifier string
|
|
2432
|
-
* @param swapType Specific swap type for which to obtain supported tokens
|
|
2433
|
-
*/
|
|
2434
|
-
private getSupportedTokenAddresses<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier, swapType: SwapType): Set<string> {
|
|
2435
|
-
const set = new Set<string>();
|
|
2436
|
-
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2437
|
-
const chainTokens = lp.services[swapType]?.chainTokens?.[chainIdentifier];
|
|
2438
|
-
if(chainTokens==null) return;
|
|
2439
|
-
chainTokens.forEach(token => set.add(token));
|
|
2440
|
-
});
|
|
2441
|
-
return set;
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
|
-
/**
|
|
2445
|
-
* Returns tokens that you can swap to (if input=true) from a given token,
|
|
2446
|
-
* or tokens that you can swap from (if input=false) to a given token
|
|
2447
|
-
*/
|
|
2448
|
-
getSwapCounterTokens(token: Token, input: SwapSide | boolean): Token[] {
|
|
2449
|
-
if(isSCToken(token)) {
|
|
2450
|
-
const result: Token[] = [];
|
|
2451
|
-
if(input) {
|
|
2452
|
-
//TO_BTC or TO_BTCLN
|
|
2453
|
-
if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTCLN).has(token.address)) {
|
|
2454
|
-
result.push(BitcoinTokens.BTCLN);
|
|
2455
|
-
}
|
|
2456
|
-
if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTC).has(token.address)) {
|
|
2457
|
-
result.push(BitcoinTokens.BTC);
|
|
2458
|
-
}
|
|
2459
|
-
} else {
|
|
2460
|
-
//FROM_BTC or FROM_BTCLN
|
|
2461
|
-
const fromLightningSwapType = this.supportsSwapType(token.chainId, SwapType.FROM_BTCLN_AUTO) ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN;
|
|
2462
|
-
if(this.getSupportedTokenAddresses(token.chainId, fromLightningSwapType).has(token.address)) {
|
|
2463
|
-
result.push(BitcoinTokens.BTCLN);
|
|
2464
|
-
}
|
|
2465
|
-
const fromOnchainSwapType = this.supportsSwapType(token.chainId, SwapType.SPV_VAULT_FROM_BTC) ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC;
|
|
2466
|
-
if(this.getSupportedTokenAddresses(token.chainId, fromOnchainSwapType).has(token.address)) {
|
|
2467
|
-
result.push(BitcoinTokens.BTC);
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
return result;
|
|
2471
|
-
} else {
|
|
2472
|
-
if(input) {
|
|
2473
|
-
if(token.lightning) {
|
|
2474
|
-
return this.getSupportedTokensForSwapType(SwapType.FROM_BTCLN);
|
|
2475
|
-
} else {
|
|
2476
|
-
return this.getSupportedTokensForSwapType(SwapType.FROM_BTC);
|
|
2477
|
-
}
|
|
2478
|
-
} else {
|
|
2479
|
-
if(token.lightning) {
|
|
2480
|
-
return this.getSupportedTokensForSwapType(SwapType.TO_BTCLN);
|
|
2481
|
-
} else {
|
|
2482
|
-
return this.getSupportedTokensForSwapType(SwapType.TO_BTC);
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
|
-
}
|
|
1
|
+
import {ISwapPrice} from "../prices/abstract/ISwapPrice";
|
|
2
|
+
import {
|
|
3
|
+
BitcoinNetwork, BitcoinRpc, BitcoinRpcWithAddressIndex, BtcBlock,
|
|
4
|
+
BtcRelay,
|
|
5
|
+
ChainData,
|
|
6
|
+
ChainSwapType,
|
|
7
|
+
ChainType, LightningNetworkApi,
|
|
8
|
+
Messenger,
|
|
9
|
+
RelaySynchronizer, SpvWithdrawalClaimedState, SpvWithdrawalFrontedState, SwapCommitState, SwapContract, SwapData
|
|
10
|
+
} from "@atomiqlabs/base";
|
|
11
|
+
import {
|
|
12
|
+
ToBTCLNOptions,
|
|
13
|
+
ToBTCLNWrapper
|
|
14
|
+
} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper";
|
|
15
|
+
import {ToBTCOptions, ToBTCWrapper} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper";
|
|
16
|
+
import {FromBTCLNOptions, FromBTCLNWrapper} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper";
|
|
17
|
+
import {FromBTCOptions, FromBTCWrapper} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper";
|
|
18
|
+
import {IntermediaryDiscovery, MultichainSwapBounds, SwapBounds} from "../intermediaries/IntermediaryDiscovery";
|
|
19
|
+
import {decode as bolt11Decode} from "@atomiqlabs/bolt11";
|
|
20
|
+
import {ISwap} from "../swaps/ISwap";
|
|
21
|
+
import {IntermediaryError} from "../errors/IntermediaryError";
|
|
22
|
+
import {SwapType} from "../enums/SwapType";
|
|
23
|
+
import {FromBTCLNSwap} from "../swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap";
|
|
24
|
+
import {FromBTCSwap} from "../swaps/escrow_swaps/frombtc/onchain/FromBTCSwap";
|
|
25
|
+
import {ToBTCLNSwap} from "../swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap";
|
|
26
|
+
import {ToBTCSwap} from "../swaps/escrow_swaps/tobtc/onchain/ToBTCSwap";
|
|
27
|
+
import {LnForGasWrapper} from "../swaps/trusted/ln/LnForGasWrapper";
|
|
28
|
+
import {LnForGasSwap} from "../swaps/trusted/ln/LnForGasSwap";
|
|
29
|
+
import {EventEmitter} from "events";
|
|
30
|
+
import {Intermediary} from "../intermediaries/Intermediary";
|
|
31
|
+
import {ISwapWrapper, WrapperCtorTokens} from "../swaps/ISwapWrapper";
|
|
32
|
+
import {bigIntCompare, bigIntMax, bigIntMin, fromDecimal, objectMap, randomBytes} from "../utils/Utils";
|
|
33
|
+
import {OutOfBoundsError} from "../errors/RequestError";
|
|
34
|
+
import {SwapperWithChain} from "./SwapperWithChain";
|
|
35
|
+
import {OnchainForGasSwap} from "../swaps/trusted/onchain/OnchainForGasSwap";
|
|
36
|
+
import {OnchainForGasWrapper} from "../swaps/trusted/onchain/OnchainForGasWrapper";
|
|
37
|
+
import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils";
|
|
38
|
+
import {IUnifiedStorage, QueryParams} from "../storage/IUnifiedStorage";
|
|
39
|
+
import {
|
|
40
|
+
UnifiedSwapStorage,
|
|
41
|
+
UnifiedSwapStorageCompositeIndexes,
|
|
42
|
+
UnifiedSwapStorageIndexes
|
|
43
|
+
} from "../storage/UnifiedSwapStorage";
|
|
44
|
+
import {UnifiedSwapEventListener} from "../events/UnifiedSwapEventListener";
|
|
45
|
+
import {IToBTCSwap} from "../swaps/escrow_swaps/tobtc/IToBTCSwap";
|
|
46
|
+
import {SpvFromBTCOptions, SpvFromBTCWrapper} from "../swaps/spv_swaps/SpvFromBTCWrapper";
|
|
47
|
+
import {SpvFromBTCSwap} from "../swaps/spv_swaps/SpvFromBTCSwap";
|
|
48
|
+
import {SwapperUtils} from "./SwapperUtils";
|
|
49
|
+
import {FromBTCLNAutoOptions, FromBTCLNAutoWrapper} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper";
|
|
50
|
+
import {FromBTCLNAutoSwap} from "../swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap";
|
|
51
|
+
import {UserError} from "../errors/UserError";
|
|
52
|
+
import {SwapAmountType} from "../enums/SwapAmountType";
|
|
53
|
+
import {IClaimableSwap} from "../swaps/IClaimableSwap";
|
|
54
|
+
import {correctClock} from "../utils/AutomaticClockDriftCorrection";
|
|
55
|
+
import {isSwapType, SwapProtocolInfo, SwapTypeMapping} from "../utils/SwapUtils";
|
|
56
|
+
import {IndexedDBUnifiedStorage} from "../storage-browser/IndexedDBUnifiedStorage";
|
|
57
|
+
import {TokenAmount, toTokenAmount} from "../types/TokenAmount";
|
|
58
|
+
import {BitcoinTokens, BtcToken, isBtcToken, isSCToken, SCToken, Token} from "../types/Token";
|
|
59
|
+
import {AmountData} from "../types/AmountData";
|
|
60
|
+
import {getLogger} from "../utils/Logger";
|
|
61
|
+
import {isLNURLWithdraw, LNURLWithdraw} from "../types/lnurl/LNURLWithdraw";
|
|
62
|
+
import {isLNURLPay, LNURLPay} from "../types/lnurl/LNURLPay";
|
|
63
|
+
import {tryWithRetries} from "../utils/RetryUtils";
|
|
64
|
+
import {NotNever} from "../utils/TypeUtils";
|
|
65
|
+
import {IEscrowSwap} from "../swaps/escrow_swaps/IEscrowSwap";
|
|
66
|
+
import {LightningInvoiceCreateService, isLightningInvoiceCreateService} from "../types/wallets/LightningInvoiceCreateService";
|
|
67
|
+
import {SwapSide} from "../enums/SwapSide";
|
|
68
|
+
import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "../bitcoin/wallet/IBitcoinWallet";
|
|
69
|
+
import {MinimalBitcoinWalletInterface} from "../types/wallets/MinimalBitcoinWalletInterface";
|
|
70
|
+
import {toBitcoinWallet} from "../utils/BitcoinWalletUtils";
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configuration options for the Swapper
|
|
74
|
+
* @category Core
|
|
75
|
+
*/
|
|
76
|
+
export type SwapperOptions = {
|
|
77
|
+
/**
|
|
78
|
+
* Manual override for the intermediary (LP) URLs for the SDK to use, by default these are fetched automatically
|
|
79
|
+
* from the registry
|
|
80
|
+
*/
|
|
81
|
+
intermediaryUrl?: string | string[],
|
|
82
|
+
/**
|
|
83
|
+
* Registry URL for where to look for active intermediary (LP) endpoint URLs
|
|
84
|
+
*/
|
|
85
|
+
registryUrl?: string,
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Bitcoin network to use for the swaps,
|
|
89
|
+
*/
|
|
90
|
+
bitcoinNetwork?: BitcoinNetwork,
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Timeout (in milliseconds) for HTTP GET requests done by the SDK
|
|
94
|
+
*/
|
|
95
|
+
getRequestTimeout?: number,
|
|
96
|
+
/**
|
|
97
|
+
* Timeout (in milliseconds) for HTTP POST requests done by the SDK
|
|
98
|
+
*/
|
|
99
|
+
postRequestTimeout?: number,
|
|
100
|
+
/**
|
|
101
|
+
* Additional parameters to be sent to the intermediaries (LPs), when requesting quotes from them
|
|
102
|
+
*/
|
|
103
|
+
defaultAdditionalParameters?: {[key: string]: any},
|
|
104
|
+
/**
|
|
105
|
+
* Optional name prefix to use when creating a swap storage, you can use this to create separate storage
|
|
106
|
+
* instances that don't overlap.
|
|
107
|
+
*/
|
|
108
|
+
storagePrefix?: string,
|
|
109
|
+
/**
|
|
110
|
+
* Sets the default intermediary (LP) to use for the trusted gas swaps, if not set the SDK uses a default one
|
|
111
|
+
*/
|
|
112
|
+
defaultTrustedIntermediaryUrl?: string,
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A function callback to retrieve a specific named storage container for swap persistency. If not present, the
|
|
116
|
+
* default IndexedDB storage adapter is used. When you use the SDK in non-browser based environments you need to
|
|
117
|
+
* provide this callback such that the SDK is able to use a custom storage adapter.
|
|
118
|
+
*
|
|
119
|
+
* @param storageName Name of the container to retrieve
|
|
120
|
+
*/
|
|
121
|
+
swapStorage?: (storageName: string) => IUnifiedStorage<UnifiedSwapStorageIndexes, UnifiedSwapStorageCompositeIndexes>,
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* By setting this flag, the swapper doesn't schedule automatic tick timers. To make sure the swap states are
|
|
125
|
+
* properly updated (e.g. the expired swaps properly move to the expired state), you should call the
|
|
126
|
+
* {@link Swapper._syncSwaps} function periodically. This flag should be set when you run an environment that
|
|
127
|
+
* doesn't support long-running timers - e.g. serverless environments like Azure Function Apps or AWS Lambda
|
|
128
|
+
*/
|
|
129
|
+
noTimers?: boolean,
|
|
130
|
+
/**
|
|
131
|
+
* By setting this flag, the swapper doesn't subscribe to on-chain events. To make sure the swap states are
|
|
132
|
+
* properly updated you should call the {@link Swapper._syncSwaps} function periodically. This flag should be
|
|
133
|
+
* set when you run an environment that doesn't support long-running timers and websocket connections - e.g.
|
|
134
|
+
* serverless environments like Azure Function Apps or AWS Lambda
|
|
135
|
+
*/
|
|
136
|
+
noEvents?: boolean,
|
|
137
|
+
/**
|
|
138
|
+
* By setting this flag, the swap objects will not be cached in the SDK and instead will always be loaded from
|
|
139
|
+
* the persistent storage. By default, the SDK uses a `WeakRef` mapping of swaps, to ensure that when the same
|
|
140
|
+
* swap is loaded concurrently, it returns the same object reference to both, making the changes on the object
|
|
141
|
+
* atomic. This flag should be set to `true` when running in an environment where multiple instances of the SDK
|
|
142
|
+
* access the same swap database - e.g. serverless environments like Azure Function Apps or AWS Lambda
|
|
143
|
+
*/
|
|
144
|
+
noSwapCache?: boolean,
|
|
145
|
+
/**
|
|
146
|
+
* Skip checking past swaps when the swapper is initiated with {@link Swapper.init}, you can call the
|
|
147
|
+
* {@link Swapper._syncSwaps} function later, to check the swaps. By default, the SDK checks the state
|
|
148
|
+
* of all the known swaps during init.
|
|
149
|
+
*/
|
|
150
|
+
dontCheckPastSwaps?: boolean,
|
|
151
|
+
/**
|
|
152
|
+
* Skip fetching the LPs when the swapper is initiated with {@link Swapper.init}, this means the list of available
|
|
153
|
+
* tokens and swap limits won't be available immediately. LPs will be fetched automatically later, when a swap
|
|
154
|
+
* is requested
|
|
155
|
+
*/
|
|
156
|
+
dontFetchLPs?: boolean,
|
|
157
|
+
/**
|
|
158
|
+
* Defaults to `true`, this means every swap regardless of it being initiated (i.e. when `commit()`, `execute()` or
|
|
159
|
+
* `waitTillPayment` is called) is saved to the persistent storage. This is a reasonable default for when you
|
|
160
|
+
* want to only create a swap, and then later on retrieve it with the `swapper.getSwapById()` function.
|
|
161
|
+
*
|
|
162
|
+
* Setting this to `false` means the SDK only saves and persists swaps that are considered initiated, i.e. when
|
|
163
|
+
* `commit()`, `execute()` or `waitTillPayment` is called (or their respective txs... prefixed variations). This
|
|
164
|
+
* might save calls to the persistent storage for swaps that are never initiated. This is useful in e.g.
|
|
165
|
+
* frontend implementations where the frontend holds the swap object reference until it is initiated anyway, not
|
|
166
|
+
* necessitating the saving of the swap data to the persistent storage until it is actually initiated.
|
|
167
|
+
*/
|
|
168
|
+
saveUninitializedSwaps?: boolean,
|
|
169
|
+
/**
|
|
170
|
+
* Automatically checks system time on initialize, if the system time drifts too far from the actual time
|
|
171
|
+
* (as checked from multiple server sources) it adjusts the `Date.now()` function to return proper actual time.
|
|
172
|
+
*/
|
|
173
|
+
automaticClockDriftCorrection?: boolean
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Type representing multiple blockchain configurations
|
|
178
|
+
* @category Core
|
|
179
|
+
*/
|
|
180
|
+
export type MultiChain = {
|
|
181
|
+
[chainIdentifier in string]: ChainType;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
type ChainSpecificData<T extends ChainType> = {
|
|
185
|
+
wrappers: {
|
|
186
|
+
[SwapType.TO_BTCLN]: ToBTCLNWrapper<T>,
|
|
187
|
+
[SwapType.TO_BTC]: ToBTCWrapper<T>,
|
|
188
|
+
[SwapType.FROM_BTCLN]: FromBTCLNWrapper<T>,
|
|
189
|
+
[SwapType.FROM_BTC]: FromBTCWrapper<T>,
|
|
190
|
+
[SwapType.TRUSTED_FROM_BTCLN]: LnForGasWrapper<T>,
|
|
191
|
+
[SwapType.TRUSTED_FROM_BTC]: OnchainForGasWrapper<T>,
|
|
192
|
+
[SwapType.SPV_VAULT_FROM_BTC]: SpvFromBTCWrapper<T>,
|
|
193
|
+
[SwapType.FROM_BTCLN_AUTO]: FromBTCLNAutoWrapper<T>
|
|
194
|
+
}
|
|
195
|
+
chainEvents: T["Events"],
|
|
196
|
+
chainInterface: T["ChainInterface"],
|
|
197
|
+
unifiedChainEvents: UnifiedSwapEventListener<T>,
|
|
198
|
+
unifiedSwapStorage: UnifiedSwapStorage<T>,
|
|
199
|
+
reviver: (val: any) => ISwap<T>,
|
|
200
|
+
defaultVersion: string,
|
|
201
|
+
|
|
202
|
+
versionedContracts: {
|
|
203
|
+
[contractVersion: string]: {
|
|
204
|
+
swapContract: T["Contract"],
|
|
205
|
+
spvVaultContract: T["SpvVaultContract"],
|
|
206
|
+
btcRelay: BtcRelay<any, T["TX"], BtcBlock, T["Signer"]>,
|
|
207
|
+
synchronizer: RelaySynchronizer<any, T["TX"], BtcBlock>,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
type MultiChainData<T extends MultiChain> = {
|
|
213
|
+
[chainIdentifier in keyof T]: ChainSpecificData<T[chainIdentifier]>
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
type CtorMultiChainData<T extends MultiChain> = {
|
|
217
|
+
[chainIdentifier in keyof T]: ChainData<T[chainIdentifier]>
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
type SwapperCtorTokens<T extends MultiChain = MultiChain> = {
|
|
221
|
+
ticker: string,
|
|
222
|
+
name: string,
|
|
223
|
+
chains: {[chainId in ChainIds<T>]?: {
|
|
224
|
+
address: string,
|
|
225
|
+
decimals: number,
|
|
226
|
+
displayDecimals?: number
|
|
227
|
+
}}
|
|
228
|
+
}[];
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Type extracting chain identifiers from a MultiChain type
|
|
232
|
+
* @category Core
|
|
233
|
+
*/
|
|
234
|
+
export type ChainIds<T extends MultiChain> = keyof T & string;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Type helper to check if a chain supports a specific swap type
|
|
238
|
+
* @category Core
|
|
239
|
+
*/
|
|
240
|
+
export type SupportsSwapType<
|
|
241
|
+
C extends ChainType,
|
|
242
|
+
Type extends SwapType
|
|
243
|
+
> = Type extends SwapType.SPV_VAULT_FROM_BTC ?
|
|
244
|
+
NotNever<C["SpvVaultContract"]> :
|
|
245
|
+
Type extends (SwapType.TRUSTED_FROM_BTCLN | SwapType.TRUSTED_FROM_BTC) ?
|
|
246
|
+
true :
|
|
247
|
+
Type extends SwapType.FROM_BTCLN_AUTO ? (C["Contract"]["supportsInitWithoutClaimer"] extends true ? true : false) :
|
|
248
|
+
NotNever<C["Contract"]>;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Core orchestrator for all atomiq swap operations
|
|
252
|
+
*
|
|
253
|
+
* @category Core
|
|
254
|
+
*/
|
|
255
|
+
export class Swapper<T extends MultiChain> extends EventEmitter<{
|
|
256
|
+
lpsRemoved: [Intermediary[]],
|
|
257
|
+
lpsAdded: [Intermediary[]],
|
|
258
|
+
swapState: [ISwap],
|
|
259
|
+
swapLimitsChanged: []
|
|
260
|
+
}> {
|
|
261
|
+
private readonly logger = getLogger(this.constructor.name+": ");
|
|
262
|
+
private readonly swapStateListener: (swap: ISwap) => void;
|
|
263
|
+
|
|
264
|
+
private defaultTrustedIntermediary?: Intermediary;
|
|
265
|
+
private readonly bitcoinNetwork: BitcoinNetwork;
|
|
266
|
+
private readonly options: SwapperOptions;
|
|
267
|
+
/**
|
|
268
|
+
* Data propagation layer used for broadcasting messages to watchtowers
|
|
269
|
+
*/
|
|
270
|
+
private readonly messenger: Messenger;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* A dictionary of smart chains used by the SDK
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
readonly _chains: MultiChainData<T>;
|
|
277
|
+
/**
|
|
278
|
+
* Bitcoin RPC for fetching bitcoin chain data
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
readonly _bitcoinRpc: BitcoinRpcWithAddressIndex<any>;
|
|
282
|
+
/**
|
|
283
|
+
* Bitcoin network specification
|
|
284
|
+
* @internal
|
|
285
|
+
*/
|
|
286
|
+
readonly _btcNetwork: BTC_NETWORK;
|
|
287
|
+
/**
|
|
288
|
+
* Token data indexed by chain identifier and token addresses
|
|
289
|
+
* @internal
|
|
290
|
+
*/
|
|
291
|
+
readonly _tokens: {
|
|
292
|
+
[chainId: string]: {
|
|
293
|
+
[tokenAddress: string]: SCToken
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Token data indexed by chain identifier and token tickers
|
|
298
|
+
* @internal
|
|
299
|
+
*/
|
|
300
|
+
readonly _tokensByTicker: {
|
|
301
|
+
[chainId: string]: {
|
|
302
|
+
[tokenTicker: string]: SCToken
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Pricing API used by the SDK
|
|
308
|
+
*/
|
|
309
|
+
readonly prices: ISwapPrice<T>;
|
|
310
|
+
/**
|
|
311
|
+
* Intermediary discovery instance
|
|
312
|
+
*/
|
|
313
|
+
readonly intermediaryDiscovery: IntermediaryDiscovery;
|
|
314
|
+
/**
|
|
315
|
+
* Miscellaneous utility functions
|
|
316
|
+
*/
|
|
317
|
+
readonly Utils: SwapperUtils<T>;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
constructor(
|
|
323
|
+
bitcoinRpc: BitcoinRpcWithAddressIndex<any>,
|
|
324
|
+
lightningApi: LightningNetworkApi,
|
|
325
|
+
bitcoinSynchronizer: (btcRelay: BtcRelay<any, any, any>) => RelaySynchronizer<any, any, any>,
|
|
326
|
+
chainsData: CtorMultiChainData<T>,
|
|
327
|
+
pricing: ISwapPrice<T>,
|
|
328
|
+
tokens: SCToken[],
|
|
329
|
+
messenger: Messenger,
|
|
330
|
+
options?: SwapperOptions
|
|
331
|
+
) {
|
|
332
|
+
super();
|
|
333
|
+
const storagePrefix = options?.storagePrefix ?? "atomiq-";
|
|
334
|
+
|
|
335
|
+
options ??= {};
|
|
336
|
+
options.saveUninitializedSwaps ??= true;
|
|
337
|
+
options.bitcoinNetwork = options.bitcoinNetwork==null ? BitcoinNetwork.TESTNET : options.bitcoinNetwork;
|
|
338
|
+
const swapStorage = options.swapStorage ??= (name: string) => new IndexedDBUnifiedStorage(name);
|
|
339
|
+
|
|
340
|
+
this.options = options;
|
|
341
|
+
|
|
342
|
+
this.bitcoinNetwork = options.bitcoinNetwork;
|
|
343
|
+
this._btcNetwork = options.bitcoinNetwork===BitcoinNetwork.MAINNET ? NETWORK :
|
|
344
|
+
(options.bitcoinNetwork===BitcoinNetwork.TESTNET || options.bitcoinNetwork===BitcoinNetwork.TESTNET4) ? TEST_NETWORK : {
|
|
345
|
+
bech32: 'bcrt',
|
|
346
|
+
pubKeyHash: 111,
|
|
347
|
+
scriptHash: 196,
|
|
348
|
+
wif: 239
|
|
349
|
+
};
|
|
350
|
+
this.Utils = new SwapperUtils(this);
|
|
351
|
+
|
|
352
|
+
this.prices = pricing;
|
|
353
|
+
this._bitcoinRpc = bitcoinRpc;
|
|
354
|
+
this.messenger = messenger;
|
|
355
|
+
|
|
356
|
+
this._tokens = {};
|
|
357
|
+
this._tokensByTicker = {};
|
|
358
|
+
for(let tokenData of tokens) {
|
|
359
|
+
const chainId = tokenData.chainId;
|
|
360
|
+
this._tokens[chainId] ??= {};
|
|
361
|
+
this._tokensByTicker[chainId] ??= {};
|
|
362
|
+
this._tokens[chainId][tokenData.address] = this._tokensByTicker[chainId][tokenData.ticker] = tokenData;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
this.swapStateListener = (swap: ISwap) => {
|
|
366
|
+
this.emit("swapState", swap);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
this._chains = objectMap<CtorMultiChainData<T>, MultiChainData<T>>(chainsData, <InputKey extends keyof CtorMultiChainData<T>>(chainData: CtorMultiChainData<T>[InputKey], key: string) => {
|
|
370
|
+
let {
|
|
371
|
+
chainInterface, chainEvents, chainId,
|
|
372
|
+
btcRelay,
|
|
373
|
+
swapContract, swapDataConstructor,
|
|
374
|
+
spvVaultContract, spvVaultWithdrawalDataConstructor, spvVaultDataConstructor,
|
|
375
|
+
defaultVersion, versions
|
|
376
|
+
} = chainData;
|
|
377
|
+
|
|
378
|
+
defaultVersion ??= "v1";
|
|
379
|
+
|
|
380
|
+
if(versions==null) {
|
|
381
|
+
versions = {
|
|
382
|
+
[defaultVersion]: {
|
|
383
|
+
btcRelay,
|
|
384
|
+
swapContract,
|
|
385
|
+
swapDataConstructor,
|
|
386
|
+
spvVaultContract,
|
|
387
|
+
spvVaultDataConstructor,
|
|
388
|
+
spvVaultWithdrawalDataConstructor
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const versionedContracts = objectMap(versions, (value, key) => {
|
|
394
|
+
return {
|
|
395
|
+
swapContract: value.swapContract,
|
|
396
|
+
spvVaultContract: value.spvVaultContract,
|
|
397
|
+
btcRelay: value.btcRelay,
|
|
398
|
+
synchronizer: bitcoinSynchronizer(value.btcRelay)
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const storageHandler = swapStorage(storagePrefix + chainId);
|
|
403
|
+
const unifiedSwapStorage = new UnifiedSwapStorage<T[InputKey]>(storageHandler, this.options.noSwapCache);
|
|
404
|
+
const unifiedChainEvents = new UnifiedSwapEventListener<T[InputKey]>(unifiedSwapStorage, chainEvents);
|
|
405
|
+
|
|
406
|
+
const wrappers: any = {};
|
|
407
|
+
|
|
408
|
+
wrappers[SwapType.TO_BTCLN] = new ToBTCLNWrapper<T[InputKey]>(
|
|
409
|
+
key,
|
|
410
|
+
unifiedSwapStorage,
|
|
411
|
+
unifiedChainEvents,
|
|
412
|
+
chainInterface,
|
|
413
|
+
pricing,
|
|
414
|
+
this._tokens[chainId],
|
|
415
|
+
versions,
|
|
416
|
+
{
|
|
417
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
418
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
419
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
wrappers[SwapType.TO_BTC] = new ToBTCWrapper<T[InputKey]>(
|
|
423
|
+
key,
|
|
424
|
+
unifiedSwapStorage,
|
|
425
|
+
unifiedChainEvents,
|
|
426
|
+
chainInterface,
|
|
427
|
+
pricing,
|
|
428
|
+
this._tokens[chainId],
|
|
429
|
+
versions,
|
|
430
|
+
this._bitcoinRpc,
|
|
431
|
+
{
|
|
432
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
433
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
434
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
435
|
+
bitcoinNetwork: this._btcNetwork
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
wrappers[SwapType.FROM_BTCLN] = new FromBTCLNWrapper<T[InputKey]>(
|
|
439
|
+
key,
|
|
440
|
+
unifiedSwapStorage,
|
|
441
|
+
unifiedChainEvents,
|
|
442
|
+
chainInterface,
|
|
443
|
+
pricing,
|
|
444
|
+
this._tokens[chainId],
|
|
445
|
+
versions,
|
|
446
|
+
lightningApi,
|
|
447
|
+
{
|
|
448
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
449
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
450
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
451
|
+
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
wrappers[SwapType.FROM_BTC] = new FromBTCWrapper<T[InputKey]>(
|
|
455
|
+
key,
|
|
456
|
+
unifiedSwapStorage,
|
|
457
|
+
unifiedChainEvents,
|
|
458
|
+
chainInterface,
|
|
459
|
+
pricing,
|
|
460
|
+
this._tokens[chainId],
|
|
461
|
+
versions,
|
|
462
|
+
versionedContracts,
|
|
463
|
+
this._bitcoinRpc,
|
|
464
|
+
{
|
|
465
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
466
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
467
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
468
|
+
bitcoinNetwork: this._btcNetwork
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
wrappers[SwapType.TRUSTED_FROM_BTCLN] = new LnForGasWrapper<T[InputKey]>(
|
|
472
|
+
key,
|
|
473
|
+
unifiedSwapStorage,
|
|
474
|
+
unifiedChainEvents,
|
|
475
|
+
chainInterface,
|
|
476
|
+
pricing,
|
|
477
|
+
this._tokens[chainId],
|
|
478
|
+
{
|
|
479
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
480
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
481
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
wrappers[SwapType.TRUSTED_FROM_BTC] = new OnchainForGasWrapper<T[InputKey]>(
|
|
485
|
+
key,
|
|
486
|
+
unifiedSwapStorage,
|
|
487
|
+
unifiedChainEvents,
|
|
488
|
+
chainInterface,
|
|
489
|
+
pricing,
|
|
490
|
+
this._tokens[chainId],
|
|
491
|
+
bitcoinRpc,
|
|
492
|
+
{
|
|
493
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
494
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
495
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
496
|
+
bitcoinNetwork: this._btcNetwork
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// This is gated on the default version of the contracts
|
|
501
|
+
if(spvVaultContract!=null) {
|
|
502
|
+
wrappers[SwapType.SPV_VAULT_FROM_BTC] = new SpvFromBTCWrapper<T[InputKey]>(
|
|
503
|
+
key,
|
|
504
|
+
unifiedSwapStorage,
|
|
505
|
+
unifiedChainEvents,
|
|
506
|
+
chainInterface,
|
|
507
|
+
pricing,
|
|
508
|
+
this._tokens[chainId],
|
|
509
|
+
versions,
|
|
510
|
+
versionedContracts,
|
|
511
|
+
bitcoinRpc,
|
|
512
|
+
{
|
|
513
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
514
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
515
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
516
|
+
bitcoinNetwork: this._btcNetwork
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// This is gated on the default version of the contracts
|
|
522
|
+
if(swapContract.supportsInitWithoutClaimer) {
|
|
523
|
+
wrappers[SwapType.FROM_BTCLN_AUTO] = new FromBTCLNAutoWrapper<T[InputKey]>(
|
|
524
|
+
key,
|
|
525
|
+
unifiedSwapStorage,
|
|
526
|
+
unifiedChainEvents,
|
|
527
|
+
chainInterface,
|
|
528
|
+
pricing,
|
|
529
|
+
this._tokens[chainId],
|
|
530
|
+
versions,
|
|
531
|
+
lightningApi,
|
|
532
|
+
this.messenger,
|
|
533
|
+
{
|
|
534
|
+
getRequestTimeout: this.options.getRequestTimeout,
|
|
535
|
+
postRequestTimeout: this.options.postRequestTimeout,
|
|
536
|
+
saveUninitializedSwaps: this.options.saveUninitializedSwaps,
|
|
537
|
+
unsafeSkipLnNodeCheck: this.bitcoinNetwork===BitcoinNetwork.TESTNET4 || this.bitcoinNetwork===BitcoinNetwork.REGTEST
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
Object.keys(wrappers).forEach(key => wrappers[key].events.on("swapState", this.swapStateListener));
|
|
543
|
+
|
|
544
|
+
const reviver = (val: any) => {
|
|
545
|
+
const wrapper: ISwapWrapper<any, any> = wrappers[val.type];
|
|
546
|
+
if(wrapper==null) return null;
|
|
547
|
+
return new wrapper._swapDeserializer(wrapper, val);
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
chainEvents,
|
|
552
|
+
chainInterface,
|
|
553
|
+
|
|
554
|
+
wrappers,
|
|
555
|
+
|
|
556
|
+
unifiedChainEvents,
|
|
557
|
+
unifiedSwapStorage,
|
|
558
|
+
|
|
559
|
+
defaultVersion,
|
|
560
|
+
|
|
561
|
+
reviver,
|
|
562
|
+
|
|
563
|
+
versionedContracts
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
const contracts = objectMap(chainsData, (data) => data.versions ?? {[data.defaultVersion ?? "v1"]: {swapContract: data.swapContract, spvVaultContract: data.spvVaultContract}});
|
|
568
|
+
if(options.intermediaryUrl!=null) {
|
|
569
|
+
this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, Array.isArray(options.intermediaryUrl) ? options.intermediaryUrl : [options.intermediaryUrl], options.getRequestTimeout);
|
|
570
|
+
} else {
|
|
571
|
+
this.intermediaryDiscovery = new IntermediaryDiscovery(contracts, options.registryUrl, undefined, options.getRequestTimeout);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
this.intermediaryDiscovery.on("removed", (intermediaries: Intermediary[]) => {
|
|
575
|
+
this.emit("lpsRemoved", intermediaries);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
this.intermediaryDiscovery.on("added", (intermediaries: Intermediary[]) => {
|
|
579
|
+
this.emit("lpsAdded", intermediaries);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private async _init(): Promise<void> {
|
|
584
|
+
this.logger.debug("init(): Initializing swapper...");
|
|
585
|
+
|
|
586
|
+
const abortController = new AbortController();
|
|
587
|
+
|
|
588
|
+
const promises: Promise<void>[] = [];
|
|
589
|
+
let automaticClockDriftCorrectionPromise: Promise<void> | undefined = undefined;
|
|
590
|
+
if(this.options.automaticClockDriftCorrection) {
|
|
591
|
+
promises.push(automaticClockDriftCorrectionPromise = tryWithRetries(correctClock, undefined, undefined, abortController.signal).catch((err) => {
|
|
592
|
+
abortController.abort(err);
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
this.logger.debug("init(): Initializing intermediary discovery");
|
|
597
|
+
if(!this.options.dontFetchLPs) promises.push(this.intermediaryDiscovery.init(abortController.signal).catch(err => {
|
|
598
|
+
if(abortController.signal.aborted) return;
|
|
599
|
+
this.logger.error("init(): Failed to fetch intermediaries/LPs: ", err);
|
|
600
|
+
}));
|
|
601
|
+
|
|
602
|
+
if(this.options.defaultTrustedIntermediaryUrl!=null) {
|
|
603
|
+
promises.push(
|
|
604
|
+
this.intermediaryDiscovery.getIntermediary(this.options.defaultTrustedIntermediaryUrl, abortController.signal)
|
|
605
|
+
.then(val => {
|
|
606
|
+
if(val==null) throw new Error("Cannot get trusted LP");
|
|
607
|
+
this.defaultTrustedIntermediary = val;
|
|
608
|
+
})
|
|
609
|
+
.catch(err => {
|
|
610
|
+
if(abortController.signal.aborted) return;
|
|
611
|
+
this.logger.error("init(): Failed to contact trusted LP url: ", err);
|
|
612
|
+
})
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if(automaticClockDriftCorrectionPromise!=null) {
|
|
617
|
+
//We should await the promises here before checking the swaps
|
|
618
|
+
await automaticClockDriftCorrectionPromise;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const chainPromises = [];
|
|
622
|
+
for(let chainIdentifier in this._chains) {
|
|
623
|
+
chainPromises.push((async() => {
|
|
624
|
+
const {
|
|
625
|
+
chainInterface,
|
|
626
|
+
versionedContracts,
|
|
627
|
+
unifiedChainEvents,
|
|
628
|
+
unifiedSwapStorage,
|
|
629
|
+
wrappers,
|
|
630
|
+
reviver
|
|
631
|
+
} = this._chains[chainIdentifier];
|
|
632
|
+
|
|
633
|
+
const _chainInterface: any = chainInterface;
|
|
634
|
+
if(_chainInterface.verifyNetwork!=null) {
|
|
635
|
+
await _chainInterface.verifyNetwork(this.bitcoinNetwork);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
for(let contractVersion in versionedContracts) {
|
|
639
|
+
await versionedContracts[contractVersion].swapContract.start();
|
|
640
|
+
this.logger.debug("init(): Intialized swap contract: "+chainIdentifier+` version: ${contractVersion}`);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
await unifiedSwapStorage.init();
|
|
644
|
+
if(unifiedSwapStorage.storage instanceof IndexedDBUnifiedStorage) {
|
|
645
|
+
//Try to migrate the data here
|
|
646
|
+
const storagePrefix = chainIdentifier==="SOLANA" ?
|
|
647
|
+
"SOLv4-"+this.bitcoinNetwork+"-Swaps-" :
|
|
648
|
+
"atomiqsdk-"+this.bitcoinNetwork+chainIdentifier+"-Swaps-";
|
|
649
|
+
await unifiedSwapStorage.storage.tryMigrate(
|
|
650
|
+
[
|
|
651
|
+
[storagePrefix+"FromBTC", SwapType.FROM_BTC],
|
|
652
|
+
[storagePrefix+"FromBTCLN", SwapType.FROM_BTCLN],
|
|
653
|
+
[storagePrefix+"ToBTC", SwapType.TO_BTC],
|
|
654
|
+
[storagePrefix+"ToBTCLN", SwapType.TO_BTCLN]
|
|
655
|
+
],
|
|
656
|
+
(obj: any) => {
|
|
657
|
+
const swap = reviver(obj);
|
|
658
|
+
if(swap._randomNonce==null) {
|
|
659
|
+
const oldIdentifierHash = swap.getId();
|
|
660
|
+
swap._randomNonce = randomBytes(16).toString("hex");
|
|
661
|
+
const newIdentifierHash = swap.getId();
|
|
662
|
+
this.logger.info("init(): Found older swap version without randomNonce, replacing, old hash: "+oldIdentifierHash+
|
|
663
|
+
" new hash: "+newIdentifierHash);
|
|
664
|
+
}
|
|
665
|
+
return swap;
|
|
666
|
+
}
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if(!this.options.noEvents) await unifiedChainEvents.start();
|
|
671
|
+
this.logger.debug("init(): Intialized events: "+chainIdentifier);
|
|
672
|
+
|
|
673
|
+
for(let key in wrappers) {
|
|
674
|
+
// this.logger.debug("init(): Initializing "+SwapType[key]+": "+chainIdentifier);
|
|
675
|
+
await wrappers[key as unknown as SwapType].init(this.options.noTimers, this.options.dontCheckPastSwaps);
|
|
676
|
+
}
|
|
677
|
+
})());
|
|
678
|
+
}
|
|
679
|
+
await Promise.all(chainPromises);
|
|
680
|
+
|
|
681
|
+
await Promise.all(promises);
|
|
682
|
+
|
|
683
|
+
this.logger.debug("init(): Initializing messenger");
|
|
684
|
+
await this.messenger.init();
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
private initPromise?: Promise<void>;
|
|
688
|
+
private initialized: boolean = false;
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Initializes the swap storage and loads existing swaps, needs to be called before any other action
|
|
692
|
+
*/
|
|
693
|
+
async init(): Promise<void> {
|
|
694
|
+
if(this.initialized) return;
|
|
695
|
+
if(this.initPromise!=null) {
|
|
696
|
+
await this.initPromise;
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
const promise = this._init();
|
|
702
|
+
this.initPromise = promise;
|
|
703
|
+
await promise;
|
|
704
|
+
delete this.initPromise;
|
|
705
|
+
this.initialized = true;
|
|
706
|
+
} catch (e) {
|
|
707
|
+
delete this.initPromise;
|
|
708
|
+
throw e;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Stops listening for onchain events and closes this Swapper instance
|
|
714
|
+
*/
|
|
715
|
+
async stop() {
|
|
716
|
+
if(this.initPromise) await this.initPromise;
|
|
717
|
+
for(let chainIdentifier in this._chains) {
|
|
718
|
+
const {
|
|
719
|
+
wrappers,
|
|
720
|
+
unifiedChainEvents
|
|
721
|
+
} = this._chains[chainIdentifier];
|
|
722
|
+
for(let key in wrappers) {
|
|
723
|
+
const wrapper = wrappers[key as unknown as SwapType];
|
|
724
|
+
wrapper.events.removeListener("swapState", this.swapStateListener);
|
|
725
|
+
await wrapper.stop();
|
|
726
|
+
}
|
|
727
|
+
await unifiedChainEvents.stop();
|
|
728
|
+
await this.messenger.stop();
|
|
729
|
+
}
|
|
730
|
+
this.initialized = false;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Creates swap & handles intermediary, quote selection
|
|
735
|
+
*
|
|
736
|
+
* @param chainIdentifier
|
|
737
|
+
* @param create Callback to create the
|
|
738
|
+
* @param amountData Amount data as passed to the function
|
|
739
|
+
* @param swapType Swap type of the execution
|
|
740
|
+
* @param maxWaitTimeMS Maximum waiting time after the first intermediary returns the quote
|
|
741
|
+
* @private
|
|
742
|
+
* @throws {Error} when no intermediary was found
|
|
743
|
+
* @throws {Error} if the chain with the provided identifier cannot be found
|
|
744
|
+
*/
|
|
745
|
+
private async createSwap<ChainIdentifier extends ChainIds<T>, S extends ISwap<T[ChainIdentifier]>>(
|
|
746
|
+
chainIdentifier: ChainIdentifier,
|
|
747
|
+
create: (candidates: Intermediary[], abortSignal: AbortSignal, chain: ChainSpecificData<T[ChainIdentifier]>) => Promise<{
|
|
748
|
+
quote: Promise<S>,
|
|
749
|
+
intermediary: Intermediary
|
|
750
|
+
}[]>,
|
|
751
|
+
amountData: { amount?: bigint, token: string, exactIn: boolean },
|
|
752
|
+
swapType: SwapType,
|
|
753
|
+
maxWaitTimeMS: number = 2000
|
|
754
|
+
): Promise<S> {
|
|
755
|
+
if(!this.initialized) throw new Error("Swapper not initialized, init first with swapper.init()!");
|
|
756
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
757
|
+
let candidates: Intermediary[];
|
|
758
|
+
|
|
759
|
+
const inBtc: boolean = swapType===SwapType.TO_BTCLN || swapType===SwapType.TO_BTC ? !amountData.exactIn : amountData.exactIn;
|
|
760
|
+
|
|
761
|
+
if(!inBtc || amountData.amount==null) {
|
|
762
|
+
//Get candidates not based on the amount
|
|
763
|
+
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
|
|
764
|
+
} else {
|
|
765
|
+
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
let swapLimitsChanged = false;
|
|
769
|
+
|
|
770
|
+
if(candidates.length===0) {
|
|
771
|
+
this.logger.warn("createSwap(): No valid intermediary found to execute the swap with, reloading intermediary database...");
|
|
772
|
+
await this.intermediaryDiscovery.reloadIntermediaries();
|
|
773
|
+
swapLimitsChanged = true;
|
|
774
|
+
|
|
775
|
+
if(!inBtc || amountData.amount==null) {
|
|
776
|
+
//Get candidates not based on the amount
|
|
777
|
+
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token);
|
|
778
|
+
} else {
|
|
779
|
+
candidates = this.intermediaryDiscovery.getSwapCandidates(chainIdentifier, swapType, amountData.token, amountData.amount);
|
|
780
|
+
|
|
781
|
+
if(candidates.length===0) {
|
|
782
|
+
const min = this.intermediaryDiscovery.getSwapMinimum(chainIdentifier, swapType, amountData.token);
|
|
783
|
+
const max = this.intermediaryDiscovery.getSwapMaximum(chainIdentifier, swapType, amountData.token);
|
|
784
|
+
if(min!=null && max!=null) {
|
|
785
|
+
if(amountData.amount < BigInt(min)) throw new OutOfBoundsError("Swap amount too low! Try swapping a higher amount.", 200, BigInt(min), BigInt(max));
|
|
786
|
+
if(amountData.amount > BigInt(max)) throw new OutOfBoundsError("Swap amount too high! Try swapping a lower amount.", 200, BigInt(min), BigInt(max));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if(candidates.length===0) throw new Error("No intermediary found for the requested pair and amount! You can try swapping different pair or higher/lower amount.");
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const abortController = new AbortController();
|
|
795
|
+
this.logger.debug("createSwap() Swap candidates: ", candidates.map(lp => lp.url).join());
|
|
796
|
+
const quotePromises: {quote: Promise<S>, intermediary: Intermediary}[] = await create(candidates, abortController.signal, this._chains[chainIdentifier]);
|
|
797
|
+
|
|
798
|
+
const promiseAll = new Promise<{
|
|
799
|
+
quote: S,
|
|
800
|
+
intermediary: Intermediary
|
|
801
|
+
}[]>((resolve, reject) => {
|
|
802
|
+
let min: bigint;
|
|
803
|
+
let max: bigint;
|
|
804
|
+
let error: Error;
|
|
805
|
+
let numResolved = 0;
|
|
806
|
+
let quotes: {
|
|
807
|
+
quote: S,
|
|
808
|
+
intermediary: Intermediary
|
|
809
|
+
}[] = [];
|
|
810
|
+
let timeout: NodeJS.Timeout;
|
|
811
|
+
|
|
812
|
+
quotePromises.forEach(data => {
|
|
813
|
+
data.quote.then(quote => {
|
|
814
|
+
if(numResolved===0) {
|
|
815
|
+
timeout = setTimeout(() => {
|
|
816
|
+
abortController.abort(new Error("Timed out waiting for quote!"));
|
|
817
|
+
resolve(quotes);
|
|
818
|
+
}, maxWaitTimeMS);
|
|
819
|
+
}
|
|
820
|
+
numResolved++;
|
|
821
|
+
quotes.push({
|
|
822
|
+
quote,
|
|
823
|
+
intermediary: data.intermediary
|
|
824
|
+
});
|
|
825
|
+
if(numResolved===quotePromises.length) {
|
|
826
|
+
clearTimeout(timeout);
|
|
827
|
+
resolve(quotes);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
}).catch(e => {
|
|
831
|
+
numResolved++;
|
|
832
|
+
if(e instanceof IntermediaryError) {
|
|
833
|
+
//Blacklist that node
|
|
834
|
+
this.intermediaryDiscovery.removeIntermediary(data.intermediary);
|
|
835
|
+
swapLimitsChanged = true;
|
|
836
|
+
} else if(e instanceof OutOfBoundsError) {
|
|
837
|
+
if(min==null || max==null) {
|
|
838
|
+
min = e.min;
|
|
839
|
+
max = e.max;
|
|
840
|
+
} else {
|
|
841
|
+
min = bigIntMin(min, e.min);
|
|
842
|
+
max = bigIntMax(max, e.max);
|
|
843
|
+
}
|
|
844
|
+
data.intermediary.swapBounds[swapType] ??= {};
|
|
845
|
+
data.intermediary.swapBounds[swapType]![chainIdentifier] ??= {};
|
|
846
|
+
const tokenBoundsData = (data.intermediary.swapBounds[swapType]![chainIdentifier]![amountData.token] ??= {input: {}, output: {}});
|
|
847
|
+
if(amountData.exactIn) {
|
|
848
|
+
tokenBoundsData.input = {min: e.min, max: e.max};
|
|
849
|
+
} else {
|
|
850
|
+
tokenBoundsData.output = {min: e.min, max: e.max};
|
|
851
|
+
}
|
|
852
|
+
swapLimitsChanged = true;
|
|
853
|
+
}
|
|
854
|
+
this.logger.warn("createSwap(): Intermediary "+data.intermediary.url+" error: ", e);
|
|
855
|
+
error = e;
|
|
856
|
+
|
|
857
|
+
if(numResolved===quotePromises.length) {
|
|
858
|
+
if(timeout!=null) clearTimeout(timeout);
|
|
859
|
+
if(quotes.length>0) {
|
|
860
|
+
resolve(quotes);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if(min!=null && max!=null) {
|
|
864
|
+
let msg = "Swap amount too high or too low! Try swapping a different amount.";
|
|
865
|
+
if(amountData.amount!=null) {
|
|
866
|
+
if(min > amountData.amount) msg = "Swap amount too low! Try swapping a higher amount.";
|
|
867
|
+
if(max < amountData.amount) msg = "Swap amount too high! Try swapping a lower amount.";
|
|
868
|
+
}
|
|
869
|
+
reject(new OutOfBoundsError(msg, 400, min, max));
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
reject(error);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
try {
|
|
879
|
+
const quotes = await promiseAll;
|
|
880
|
+
|
|
881
|
+
//TODO: Intermediary's reputation is not taken into account!
|
|
882
|
+
quotes.sort((a, b) => {
|
|
883
|
+
if(amountData.exactIn) {
|
|
884
|
+
//Compare outputs
|
|
885
|
+
return bigIntCompare(b.quote.getOutput().rawAmount!, a.quote.getOutput().rawAmount!);
|
|
886
|
+
} else {
|
|
887
|
+
//Compare inputs
|
|
888
|
+
return bigIntCompare(a.quote.getInput().rawAmount!, b.quote.getInput().rawAmount!);
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
this.logger.debug("createSwap(): Sorted quotes, best price to worst: ", quotes);
|
|
893
|
+
|
|
894
|
+
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
895
|
+
|
|
896
|
+
const quote = quotes[0].quote;
|
|
897
|
+
await quote._save();
|
|
898
|
+
return quote;
|
|
899
|
+
} catch (e) {
|
|
900
|
+
if(swapLimitsChanged) this.emit("swapLimitsChanged");
|
|
901
|
+
throw e;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Creates Smart chain -> Bitcoin ({@link SwapType.TO_BTC}) swap
|
|
907
|
+
*
|
|
908
|
+
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
909
|
+
* @param signer Signer's address on the source chain
|
|
910
|
+
* @param tokenAddress Token address to pay with
|
|
911
|
+
* @param address Recipient's bitcoin address
|
|
912
|
+
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
913
|
+
* @param exactIn Whether to use exact in instead of exact out
|
|
914
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
915
|
+
* @param options Additional options for the swap
|
|
916
|
+
*/
|
|
917
|
+
createToBTCSwap<ChainIdentifier extends ChainIds<T>>(
|
|
918
|
+
chainIdentifier: ChainIdentifier,
|
|
919
|
+
signer: string,
|
|
920
|
+
tokenAddress: string,
|
|
921
|
+
address: string,
|
|
922
|
+
amount: bigint,
|
|
923
|
+
exactIn: boolean = false,
|
|
924
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
925
|
+
options?: ToBTCOptions
|
|
926
|
+
): Promise<ToBTCSwap<T[ChainIdentifier]>> {
|
|
927
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
928
|
+
if(address.startsWith("bitcoin:")) {
|
|
929
|
+
address = address.substring(8).split("?")[0];
|
|
930
|
+
}
|
|
931
|
+
if(!this.Utils.isValidBitcoinAddress(address)) throw new Error("Invalid bitcoin address");
|
|
932
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
933
|
+
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
934
|
+
const amountData = {
|
|
935
|
+
amount,
|
|
936
|
+
token: tokenAddress,
|
|
937
|
+
exactIn
|
|
938
|
+
};
|
|
939
|
+
return this.createSwap(
|
|
940
|
+
chainIdentifier as ChainIdentifier,
|
|
941
|
+
(candidates: Intermediary[], abortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.TO_BTC].create(
|
|
942
|
+
signer,
|
|
943
|
+
address,
|
|
944
|
+
amountData,
|
|
945
|
+
candidates,
|
|
946
|
+
options,
|
|
947
|
+
additionalParams,
|
|
948
|
+
abortSignal
|
|
949
|
+
)),
|
|
950
|
+
amountData,
|
|
951
|
+
SwapType.TO_BTC
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap
|
|
957
|
+
*
|
|
958
|
+
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
959
|
+
* @param signer Signer's address on the source chain
|
|
960
|
+
* @param tokenAddress Token address to pay with
|
|
961
|
+
* @param paymentRequest BOLT11 lightning network invoice to be paid (needs to have a fixed amount), and the swap
|
|
962
|
+
* amount is taken from this fixed amount, hence only exact output swaps are supported
|
|
963
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
964
|
+
* @param options Additional options for the swap
|
|
965
|
+
*/
|
|
966
|
+
async createToBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
|
|
967
|
+
chainIdentifier: ChainIdentifier,
|
|
968
|
+
signer: string,
|
|
969
|
+
tokenAddress: string,
|
|
970
|
+
paymentRequest: string,
|
|
971
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
972
|
+
options?: ToBTCLNOptions
|
|
973
|
+
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
974
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
975
|
+
if(paymentRequest.startsWith("lightning:")) paymentRequest = paymentRequest.substring(10);
|
|
976
|
+
if(!this.Utils.isValidLightningInvoice(paymentRequest)) throw new Error("Invalid lightning network invoice");
|
|
977
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
978
|
+
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
979
|
+
const parsedPR = bolt11Decode(paymentRequest);
|
|
980
|
+
if(parsedPR.millisatoshis==null) throw new Error("Invalid lightning network invoice, no msat value field!");
|
|
981
|
+
const amountData = {
|
|
982
|
+
amount: (BigInt(parsedPR.millisatoshis) + 999n) / 1000n,
|
|
983
|
+
token: tokenAddress,
|
|
984
|
+
exactIn: false as const
|
|
985
|
+
};
|
|
986
|
+
return this.createSwap(
|
|
987
|
+
chainIdentifier as ChainIdentifier,
|
|
988
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].create(
|
|
989
|
+
signer,
|
|
990
|
+
paymentRequest,
|
|
991
|
+
amountData,
|
|
992
|
+
candidates,
|
|
993
|
+
options,
|
|
994
|
+
additionalParams,
|
|
995
|
+
abortSignal
|
|
996
|
+
),
|
|
997
|
+
amountData,
|
|
998
|
+
SwapType.TO_BTCLN
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via LNURL-pay link
|
|
1004
|
+
*
|
|
1005
|
+
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
1006
|
+
* @param signer Signer's address on the source chain
|
|
1007
|
+
* @param tokenAddress Token address to pay with
|
|
1008
|
+
* @param lnurlPay LNURL-pay link to use for the payment
|
|
1009
|
+
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
1010
|
+
* @param exactIn Whether to do an exact in swap instead of exact out
|
|
1011
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1012
|
+
* @param options Additional options for the swap
|
|
1013
|
+
*/
|
|
1014
|
+
async createToBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1015
|
+
chainIdentifier: ChainIdentifier,
|
|
1016
|
+
signer: string,
|
|
1017
|
+
tokenAddress: string,
|
|
1018
|
+
lnurlPay: string | LNURLPay,
|
|
1019
|
+
amount: bigint,
|
|
1020
|
+
exactIn: boolean = false,
|
|
1021
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1022
|
+
options?: ToBTCLNOptions & {comment?: string}
|
|
1023
|
+
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
1024
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1025
|
+
if(typeof(lnurlPay)==="string" && !this.Utils.isValidLNURL(lnurlPay)) throw new Error("Invalid LNURL-pay link");
|
|
1026
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1027
|
+
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
1028
|
+
const amountData = {
|
|
1029
|
+
amount,
|
|
1030
|
+
token: tokenAddress,
|
|
1031
|
+
exactIn
|
|
1032
|
+
};
|
|
1033
|
+
return this.createSwap(
|
|
1034
|
+
chainIdentifier as ChainIdentifier,
|
|
1035
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaLNURL(
|
|
1036
|
+
signer,
|
|
1037
|
+
typeof(lnurlPay)==="string" ? (lnurlPay.startsWith("lightning:") ? lnurlPay.substring(10): lnurlPay) : lnurlPay.params,
|
|
1038
|
+
amountData,
|
|
1039
|
+
candidates,
|
|
1040
|
+
options,
|
|
1041
|
+
additionalParams,
|
|
1042
|
+
abortSignal
|
|
1043
|
+
),
|
|
1044
|
+
amountData,
|
|
1045
|
+
SwapType.TO_BTCLN
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Creates Smart chain -> Bitcoin Lightning ({@link SwapType.TO_BTCLN}) swap via {@link LightningInvoiceCreateService}
|
|
1051
|
+
*
|
|
1052
|
+
* @param chainIdentifier Chain identifier string of the source smart chain
|
|
1053
|
+
* @param signer Signer's address on the source chain
|
|
1054
|
+
* @param tokenAddress Token address to pay with
|
|
1055
|
+
* @param service Invoice create service object which facilitates the creation of fixed amount LN invoices
|
|
1056
|
+
* @param amount Amount to send in token based units (if `exactIn=true`) or receive in satoshis (if `exactIn=false`)
|
|
1057
|
+
* @param exactIn Whether to do an exact in swap instead of exact out
|
|
1058
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1059
|
+
* @param options Additional options for the swap
|
|
1060
|
+
*/
|
|
1061
|
+
async createToBTCLNSwapViaInvoiceCreateService<ChainIdentifier extends ChainIds<T>>(
|
|
1062
|
+
chainIdentifier: ChainIdentifier,
|
|
1063
|
+
signer: string,
|
|
1064
|
+
tokenAddress: string,
|
|
1065
|
+
service: LightningInvoiceCreateService,
|
|
1066
|
+
amount: bigint,
|
|
1067
|
+
exactIn: boolean = false,
|
|
1068
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1069
|
+
options?: ToBTCLNOptions
|
|
1070
|
+
): Promise<ToBTCLNSwap<T[ChainIdentifier]>> {
|
|
1071
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1072
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(signer, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1073
|
+
signer = this._chains[chainIdentifier].chainInterface.normalizeAddress(signer);
|
|
1074
|
+
const amountData = {
|
|
1075
|
+
amount,
|
|
1076
|
+
token: tokenAddress,
|
|
1077
|
+
exactIn
|
|
1078
|
+
};
|
|
1079
|
+
return this.createSwap(
|
|
1080
|
+
chainIdentifier as ChainIdentifier,
|
|
1081
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.TO_BTCLN].createViaInvoiceCreateService(
|
|
1082
|
+
signer,
|
|
1083
|
+
Promise.resolve(service),
|
|
1084
|
+
amountData,
|
|
1085
|
+
candidates,
|
|
1086
|
+
options,
|
|
1087
|
+
additionalParams,
|
|
1088
|
+
abortSignal
|
|
1089
|
+
),
|
|
1090
|
+
amountData,
|
|
1091
|
+
SwapType.TO_BTCLN
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Creates Bitcoin -> Smart chain ({@link SwapType.SPV_VAULT_FROM_BTC}) swap
|
|
1097
|
+
*
|
|
1098
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1099
|
+
* @param recipient Recipient address on the destination chain
|
|
1100
|
+
* @param tokenAddress Token address to receive
|
|
1101
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1102
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1103
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1104
|
+
* @param options Additional options for the swap
|
|
1105
|
+
*/
|
|
1106
|
+
async createFromBTCSwapNew<ChainIdentifier extends ChainIds<T>>(
|
|
1107
|
+
chainIdentifier: ChainIdentifier,
|
|
1108
|
+
recipient: string,
|
|
1109
|
+
tokenAddress: string,
|
|
1110
|
+
amount: bigint | null,
|
|
1111
|
+
exactOut: boolean = false,
|
|
1112
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1113
|
+
options?: SpvFromBTCOptions
|
|
1114
|
+
): Promise<SpvFromBTCSwap<T[ChainIdentifier]>> {
|
|
1115
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1116
|
+
if(this._chains[chainIdentifier].wrappers[SwapType.SPV_VAULT_FROM_BTC]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new BTC swap protocol (spv vault swaps)!");
|
|
1117
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1118
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1119
|
+
const amountData = {
|
|
1120
|
+
amount: amount ?? undefined,
|
|
1121
|
+
token: tokenAddress,
|
|
1122
|
+
exactIn: !exactOut
|
|
1123
|
+
};
|
|
1124
|
+
return this.createSwap(
|
|
1125
|
+
chainIdentifier as ChainIdentifier,
|
|
1126
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.SPV_VAULT_FROM_BTC].create(
|
|
1127
|
+
recipient,
|
|
1128
|
+
amountData,
|
|
1129
|
+
candidates,
|
|
1130
|
+
options,
|
|
1131
|
+
additionalParams,
|
|
1132
|
+
abortSignal
|
|
1133
|
+
)),
|
|
1134
|
+
amountData,
|
|
1135
|
+
SwapType.SPV_VAULT_FROM_BTC
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Creates LEGACY Bitcoin -> Smart chain ({@link SwapType.FROM_BTC}) swap
|
|
1141
|
+
*
|
|
1142
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1143
|
+
* @param recipient Recipient address on the destination chain
|
|
1144
|
+
* @param tokenAddress Token address to receive
|
|
1145
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1146
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1147
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1148
|
+
* @param options Additional options for the swap
|
|
1149
|
+
*/
|
|
1150
|
+
async createFromBTCSwap<ChainIdentifier extends ChainIds<T>>(
|
|
1151
|
+
chainIdentifier: ChainIdentifier,
|
|
1152
|
+
recipient: string,
|
|
1153
|
+
tokenAddress: string,
|
|
1154
|
+
amount: bigint,
|
|
1155
|
+
exactOut: boolean = false,
|
|
1156
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1157
|
+
options?: FromBTCOptions
|
|
1158
|
+
): Promise<FromBTCSwap<T[ChainIdentifier]>> {
|
|
1159
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1160
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1161
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1162
|
+
const amountData = {
|
|
1163
|
+
amount,
|
|
1164
|
+
token: tokenAddress,
|
|
1165
|
+
exactIn: !exactOut
|
|
1166
|
+
};
|
|
1167
|
+
return this.createSwap(
|
|
1168
|
+
chainIdentifier as ChainIdentifier,
|
|
1169
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTC].create(
|
|
1170
|
+
recipient,
|
|
1171
|
+
amountData,
|
|
1172
|
+
candidates,
|
|
1173
|
+
options,
|
|
1174
|
+
additionalParams,
|
|
1175
|
+
abortSignal
|
|
1176
|
+
)),
|
|
1177
|
+
amountData,
|
|
1178
|
+
SwapType.FROM_BTC
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap
|
|
1184
|
+
*
|
|
1185
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1186
|
+
* @param recipient Recipient address on the destination chain
|
|
1187
|
+
* @param tokenAddress Token address to receive
|
|
1188
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1189
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1190
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1191
|
+
* @param options Additional options for the swap
|
|
1192
|
+
*/
|
|
1193
|
+
async createFromBTCLNSwap<ChainIdentifier extends ChainIds<T>>(
|
|
1194
|
+
chainIdentifier: ChainIdentifier,
|
|
1195
|
+
recipient: string,
|
|
1196
|
+
tokenAddress: string,
|
|
1197
|
+
amount: bigint,
|
|
1198
|
+
exactOut: boolean = false,
|
|
1199
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1200
|
+
options?: FromBTCLNOptions
|
|
1201
|
+
): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
|
|
1202
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1203
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1204
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1205
|
+
const amountData = {
|
|
1206
|
+
amount,
|
|
1207
|
+
token: tokenAddress,
|
|
1208
|
+
exactIn: !exactOut
|
|
1209
|
+
};
|
|
1210
|
+
return this.createSwap(
|
|
1211
|
+
chainIdentifier as ChainIdentifier,
|
|
1212
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN].create(
|
|
1213
|
+
recipient,
|
|
1214
|
+
amountData,
|
|
1215
|
+
candidates,
|
|
1216
|
+
options,
|
|
1217
|
+
additionalParams,
|
|
1218
|
+
abortSignal
|
|
1219
|
+
)),
|
|
1220
|
+
amountData,
|
|
1221
|
+
SwapType.FROM_BTCLN
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Creates LEGACY Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN}) swap, withdrawing from
|
|
1227
|
+
* an LNURL-withdraw link
|
|
1228
|
+
*
|
|
1229
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1230
|
+
* @param recipient Recipient address on the destination chain
|
|
1231
|
+
* @param tokenAddress Token address to receive
|
|
1232
|
+
* @param lnurl LNURL-withdraw link to pull the funds from
|
|
1233
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1234
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1235
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1236
|
+
* @param options Additional options for the swap
|
|
1237
|
+
*/
|
|
1238
|
+
async createFromBTCLNSwapViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1239
|
+
chainIdentifier: ChainIdentifier,
|
|
1240
|
+
recipient: string,
|
|
1241
|
+
tokenAddress: string,
|
|
1242
|
+
lnurl: string | LNURLWithdraw,
|
|
1243
|
+
amount: bigint,
|
|
1244
|
+
exactOut: boolean = false,
|
|
1245
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1246
|
+
options?: FromBTCLNOptions
|
|
1247
|
+
): Promise<FromBTCLNSwap<T[ChainIdentifier]>> {
|
|
1248
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1249
|
+
if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
|
|
1250
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1251
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1252
|
+
const amountData = {
|
|
1253
|
+
amount,
|
|
1254
|
+
token: tokenAddress,
|
|
1255
|
+
exactIn: !exactOut
|
|
1256
|
+
};
|
|
1257
|
+
return this.createSwap(
|
|
1258
|
+
chainIdentifier as ChainIdentifier,
|
|
1259
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN].createViaLNURL(
|
|
1260
|
+
recipient,
|
|
1261
|
+
typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
|
|
1262
|
+
amountData,
|
|
1263
|
+
candidates,
|
|
1264
|
+
options,
|
|
1265
|
+
additionalParams,
|
|
1266
|
+
abortSignal
|
|
1267
|
+
),
|
|
1268
|
+
amountData,
|
|
1269
|
+
SwapType.FROM_BTCLN
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap
|
|
1275
|
+
*
|
|
1276
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1277
|
+
* @param recipient Recipient address on the destination chain
|
|
1278
|
+
* @param tokenAddress Token address to receive
|
|
1279
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1280
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1281
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1282
|
+
* @param options Additional options for the swap
|
|
1283
|
+
*/
|
|
1284
|
+
async createFromBTCLNSwapNew<ChainIdentifier extends ChainIds<T>>(
|
|
1285
|
+
chainIdentifier: ChainIdentifier,
|
|
1286
|
+
recipient: string,
|
|
1287
|
+
tokenAddress: string,
|
|
1288
|
+
amount: bigint,
|
|
1289
|
+
exactOut: boolean = false,
|
|
1290
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1291
|
+
options?: FromBTCLNAutoOptions
|
|
1292
|
+
): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
|
|
1293
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1294
|
+
if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
|
|
1295
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1296
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1297
|
+
const amountData = {
|
|
1298
|
+
amount,
|
|
1299
|
+
token: tokenAddress,
|
|
1300
|
+
exactIn: !exactOut
|
|
1301
|
+
};
|
|
1302
|
+
return this.createSwap(
|
|
1303
|
+
chainIdentifier as ChainIdentifier,
|
|
1304
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => Promise.resolve(chain.wrappers[SwapType.FROM_BTCLN_AUTO].create(
|
|
1305
|
+
recipient,
|
|
1306
|
+
amountData,
|
|
1307
|
+
candidates,
|
|
1308
|
+
options,
|
|
1309
|
+
additionalParams,
|
|
1310
|
+
abortSignal
|
|
1311
|
+
)),
|
|
1312
|
+
amountData,
|
|
1313
|
+
SwapType.FROM_BTCLN_AUTO
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Creates Bitcoin Lightning -> Smart chain ({@link SwapType.FROM_BTCLN_AUTO}) swap, withdrawing from
|
|
1319
|
+
* an LNURL-withdraw link
|
|
1320
|
+
*
|
|
1321
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1322
|
+
* @param recipient Recipient address on the destination chain
|
|
1323
|
+
* @param tokenAddress Token address to receive
|
|
1324
|
+
* @param lnurl LNURL-withdraw link to pull the funds from
|
|
1325
|
+
* @param amount Amount to send in satoshis (if `exactOut=false`) or receive in token based units (if `exactOut=true`)
|
|
1326
|
+
* @param exactOut Whether to use a exact out instead of exact in
|
|
1327
|
+
* @param additionalParams Additional parameters sent to the LP when creating the swap
|
|
1328
|
+
* @param options Additional options for the swap
|
|
1329
|
+
*/
|
|
1330
|
+
async createFromBTCLNSwapNewViaLNURL<ChainIdentifier extends ChainIds<T>>(
|
|
1331
|
+
chainIdentifier: ChainIdentifier,
|
|
1332
|
+
recipient: string,
|
|
1333
|
+
tokenAddress: string,
|
|
1334
|
+
lnurl: string | LNURLWithdraw,
|
|
1335
|
+
amount: bigint,
|
|
1336
|
+
exactOut: boolean = false,
|
|
1337
|
+
additionalParams: Record<string, any> | undefined = this.options.defaultAdditionalParameters,
|
|
1338
|
+
options?: FromBTCLNAutoOptions
|
|
1339
|
+
): Promise<FromBTCLNAutoSwap<T[ChainIdentifier]>> {
|
|
1340
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1341
|
+
if(this._chains[chainIdentifier].wrappers[SwapType.FROM_BTCLN_AUTO]==null) throw new Error("Chain "+chainIdentifier+" doesn't support new lightning swap protocol (from btcln auto)!");
|
|
1342
|
+
if(typeof(lnurl)==="string" && !this.Utils.isValidLNURL(lnurl)) throw new Error("Invalid LNURL-withdraw link");
|
|
1343
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1344
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1345
|
+
const amountData = {
|
|
1346
|
+
amount,
|
|
1347
|
+
token: tokenAddress,
|
|
1348
|
+
exactIn: !exactOut
|
|
1349
|
+
};
|
|
1350
|
+
return this.createSwap(
|
|
1351
|
+
chainIdentifier as ChainIdentifier,
|
|
1352
|
+
(candidates: Intermediary[], abortSignal: AbortSignal, chain) => chain.wrappers[SwapType.FROM_BTCLN_AUTO].createViaLNURL(
|
|
1353
|
+
recipient,
|
|
1354
|
+
typeof(lnurl)==="string" ? (lnurl.startsWith("lightning:") ? lnurl.substring(10): lnurl) : lnurl.params,
|
|
1355
|
+
amountData,
|
|
1356
|
+
candidates,
|
|
1357
|
+
options,
|
|
1358
|
+
additionalParams,
|
|
1359
|
+
abortSignal
|
|
1360
|
+
),
|
|
1361
|
+
amountData,
|
|
1362
|
+
SwapType.FROM_BTCLN_AUTO
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Creates a trusted Bitcoin Lightning -> Smart chain ({@link SwapType.TRUSTED_FROM_BTCLN}) gas swap
|
|
1368
|
+
*
|
|
1369
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1370
|
+
* @param recipient Recipient address on the destination chain
|
|
1371
|
+
* @param amount Amount of native token to receive, in base units
|
|
1372
|
+
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1373
|
+
* @throws {Error} If no trusted intermediary specified
|
|
1374
|
+
*/
|
|
1375
|
+
async createTrustedLNForGasSwap<C extends ChainIds<T>>(chainIdentifier: C, recipient: string, amount: bigint, trustedIntermediaryOrUrl?: Intermediary | string): Promise<LnForGasSwap<T[C]>> {
|
|
1376
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1377
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1378
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1379
|
+
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1380
|
+
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1381
|
+
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTCLN].create(recipient, amount, useUrl);
|
|
1382
|
+
await swap._save();
|
|
1383
|
+
return swap;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Creates a trusted Bitcoin -> Smart chain ({@link SwapType.TRUSTED_FROM_BTC}) gas swap
|
|
1388
|
+
*
|
|
1389
|
+
* @param chainIdentifier Chain identifier string of the destination smart chain
|
|
1390
|
+
* @param recipient Recipient address on the destination chain
|
|
1391
|
+
* @param amount Amount of native token to receive, in base units
|
|
1392
|
+
* @param refundAddress Bitcoin refund address, in case the swap fails the funds are refunded here
|
|
1393
|
+
* @param trustedIntermediaryOrUrl URL or Intermediary object of the trusted intermediary to use, otherwise uses default
|
|
1394
|
+
* @throws {Error} If no trusted intermediary specified
|
|
1395
|
+
*/
|
|
1396
|
+
async createTrustedOnchainForGasSwap<C extends ChainIds<T>>(
|
|
1397
|
+
chainIdentifier: C, recipient: string,
|
|
1398
|
+
amount: bigint, refundAddress?: string,
|
|
1399
|
+
trustedIntermediaryOrUrl?: Intermediary | string
|
|
1400
|
+
): Promise<OnchainForGasSwap<T[C]>> {
|
|
1401
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
1402
|
+
if(!this._chains[chainIdentifier].chainInterface.isValidAddress(recipient, true)) throw new Error("Invalid "+chainIdentifier+" address");
|
|
1403
|
+
recipient = this._chains[chainIdentifier].chainInterface.normalizeAddress(recipient);
|
|
1404
|
+
const useUrl = trustedIntermediaryOrUrl ?? this.defaultTrustedIntermediary ?? this.options.defaultTrustedIntermediaryUrl;
|
|
1405
|
+
if(useUrl==null) throw new Error("No trusted intermediary specified!");
|
|
1406
|
+
const swap = await this._chains[chainIdentifier as C].wrappers[SwapType.TRUSTED_FROM_BTC].create(recipient, amount, useUrl, refundAddress);
|
|
1407
|
+
await swap._save();
|
|
1408
|
+
return swap;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* @internal
|
|
1413
|
+
*/
|
|
1414
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<true>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean, lnurlWithdraw?: string | LNURLWithdraw): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
|
|
1415
|
+
/**
|
|
1416
|
+
* @internal
|
|
1417
|
+
*/
|
|
1418
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: BtcToken<false>, dstToken: SCToken<C>, amount: bigint, exactIn: boolean): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
|
|
1419
|
+
/**
|
|
1420
|
+
* @internal
|
|
1421
|
+
*/
|
|
1422
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<false>, amount: bigint, exactIn: boolean, address: string): Promise<ToBTCSwap<T[C]>>;
|
|
1423
|
+
/**
|
|
1424
|
+
* @internal
|
|
1425
|
+
*/
|
|
1426
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: bigint, exactIn: boolean, lnurlPay: string | LNURLPay): Promise<ToBTCLNSwap<T[C]>>;
|
|
1427
|
+
/**
|
|
1428
|
+
* @internal
|
|
1429
|
+
*/
|
|
1430
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: SCToken<C>, dstToken: BtcToken<true>, amount: undefined, exactIn: false, lightningInvoice: string): Promise<ToBTCLNSwap<T[C]>>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
|
|
1433
|
+
* or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1434
|
+
* invoice's pre-set amount is used instead.
|
|
1435
|
+
* @deprecated Use {@link swap} instead
|
|
1436
|
+
*
|
|
1437
|
+
* @param signer Smartchain (Solana, Starknet, etc.) address of the user
|
|
1438
|
+
* @param srcToken Source token of the swap, user pays this token
|
|
1439
|
+
* @param dstToken Destination token of the swap, user receives this token
|
|
1440
|
+
* @param amount Amount of the swap
|
|
1441
|
+
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1442
|
+
* @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
|
|
1443
|
+
* LNURL-withdrawal to withdraw money from
|
|
1444
|
+
*/
|
|
1445
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>>;
|
|
1446
|
+
/**
|
|
1447
|
+
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (exactIn=true)
|
|
1448
|
+
* or output amount (exactIn=false), NOTE: For regular -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1449
|
+
* invoice's pre-set amount is used instead.
|
|
1450
|
+
* @deprecated Use {@link swap} instead
|
|
1451
|
+
*
|
|
1452
|
+
* @param signer Smartchain (Solana, Starknet, etc.) address of the user
|
|
1453
|
+
* @param srcToken Source token of the swap, user pays this token
|
|
1454
|
+
* @param dstToken Destination token of the swap, user receives this token
|
|
1455
|
+
* @param amount Amount of the swap
|
|
1456
|
+
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1457
|
+
* @param addressLnurlLightningInvoice Bitcoin on-chain address, lightning invoice, LNURL-pay to pay or
|
|
1458
|
+
* LNURL-withdrawal to withdraw money from
|
|
1459
|
+
*/
|
|
1460
|
+
create<C extends ChainIds<T>>(signer: string, srcToken: Token<C>, dstToken: Token<C>, amount: bigint | undefined, exactIn: boolean, addressLnurlLightningInvoice?: string | LNURLWithdraw | LNURLPay): Promise<ISwap<T[C]>> {
|
|
1461
|
+
if(srcToken.chain==="BTC") {
|
|
1462
|
+
return this.swap(srcToken, dstToken, amount, exactIn, addressLnurlLightningInvoice as any, signer);
|
|
1463
|
+
} else {
|
|
1464
|
+
return this.swap(srcToken, dstToken, amount, exactIn, signer, addressLnurlLightningInvoice as any);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* @internal
|
|
1470
|
+
*/
|
|
1471
|
+
swap<C extends ChainIds<T>>(srcToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoOptions : FromBTCLNOptions)): Promise<(SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? FromBTCLNAutoSwap<T[C]> : FromBTCLNSwap<T[C]>)>;
|
|
1472
|
+
/**
|
|
1473
|
+
* @internal
|
|
1474
|
+
*/
|
|
1475
|
+
swap<C extends ChainIds<T>>(srcToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", dstToken: SCToken<C> | string, amount: bigint | string, exactIn: boolean | SwapAmountType, src: undefined | string, dstSmartchainWallet: string, options?: (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCOptions : FromBTCOptions)): Promise<(SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SpvFromBTCSwap<T[C]> : FromBTCSwap<T[C]>)>;
|
|
1476
|
+
/**
|
|
1477
|
+
* @internal
|
|
1478
|
+
*/
|
|
1479
|
+
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<false> | "BTC" | "BITCOIN-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstAddress: string, options?: ToBTCOptions): Promise<ToBTCSwap<T[C]>>;
|
|
1480
|
+
/**
|
|
1481
|
+
* @internal
|
|
1482
|
+
*/
|
|
1483
|
+
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: bigint | string, exactIn: boolean | SwapAmountType, src: string, dstLnurlPayOrInvoiceCreateService: string | LNURLPay | LightningInvoiceCreateService, options?: ToBTCLNOptions & {comment?: string}): Promise<ToBTCLNSwap<T[C]>>;
|
|
1484
|
+
/**
|
|
1485
|
+
* @internal
|
|
1486
|
+
*/
|
|
1487
|
+
swap<C extends ChainIds<T>>(srcToken: SCToken<C> | string, dstToken: BtcToken<true> | "BTCLN" | "BTC-LN" | "LIGHTNING-BTC", amount: undefined, exactIn: false | SwapAmountType.EXACT_OUT, src: string, dstLightningInvoice: string, options?: ToBTCLNOptions): Promise<ToBTCLNSwap<T[C]>>;
|
|
1488
|
+
/**
|
|
1489
|
+
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
|
|
1490
|
+
* or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1491
|
+
* invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
|
|
1492
|
+
*
|
|
1493
|
+
* @param srcToken Source token of the swap, user pays this token
|
|
1494
|
+
* @param dstToken Destination token of the swap, user receives this token
|
|
1495
|
+
* @param amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
|
|
1496
|
+
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1497
|
+
* @param src Source wallet/lnurl-withdraw of the swap
|
|
1498
|
+
* @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
|
|
1499
|
+
* @param options Options for the swap
|
|
1500
|
+
*/
|
|
1501
|
+
swap<C extends ChainIds<T>>(srcToken: Token<C> | string, dstToken: Token<C> | string, amount: bigint | string | undefined, exactIn: boolean | SwapAmountType, src: undefined | string | LNURLWithdraw, dst: string | LNURLPay | LightningInvoiceCreateService, options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions): Promise<ISwap<T[C]>>;
|
|
1502
|
+
/**
|
|
1503
|
+
* Creates a swap from srcToken to dstToken, of a specific token amount, either specifying input amount (if `exactIn=true`)
|
|
1504
|
+
* or output amount (if `exactIn=false`), NOTE: For regular Smart chain -> BTC-LN (lightning) swaps the passed amount is ignored and
|
|
1505
|
+
* invoice's pre-set amount is used instead, use LNURL-pay links for dynamic amounts
|
|
1506
|
+
*
|
|
1507
|
+
* @param _srcToken Source token of the swap, user pays this token
|
|
1508
|
+
* @param _dstToken Destination token of the swap, user receives this token
|
|
1509
|
+
* @param _amount Amount of the swap either in base units as {bigint} or in human readable format (with decimals) as {string}
|
|
1510
|
+
* @param exactIn Whether the amount specified is an input amount (exactIn=true) or an output amount (exactIn=false)
|
|
1511
|
+
* @param src Source wallet/lnurl-withdraw of the swap
|
|
1512
|
+
* @param dst Destination smart chain address, bitcoin on-chain address, lightning invoice, LNURL-pay
|
|
1513
|
+
* @param options Options for the swap
|
|
1514
|
+
*/
|
|
1515
|
+
swap<C extends ChainIds<T>>(
|
|
1516
|
+
_srcToken: Token<C> | string,
|
|
1517
|
+
_dstToken: Token<C> | string,
|
|
1518
|
+
_amount: bigint | string | undefined,
|
|
1519
|
+
exactIn: boolean | SwapAmountType,
|
|
1520
|
+
src: undefined | string | LNURLWithdraw,
|
|
1521
|
+
dst: string | LNURLPay | LightningInvoiceCreateService,
|
|
1522
|
+
options?: FromBTCLNOptions | SpvFromBTCOptions | FromBTCOptions | ToBTCOptions | (ToBTCLNOptions & {comment?: string}) | FromBTCLNAutoOptions
|
|
1523
|
+
): Promise<ISwap<T[C]>> {
|
|
1524
|
+
const srcToken = typeof(_srcToken)==="string" ? this.getToken(_srcToken) as Token<C> : _srcToken;
|
|
1525
|
+
const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
|
|
1526
|
+
const amount = _amount==null ? null : (typeof(_amount)==="bigint" ? _amount : fromDecimal(_amount, exactIn ? srcToken.decimals : dstToken.decimals));
|
|
1527
|
+
if(isBtcToken(srcToken)) {
|
|
1528
|
+
if(isSCToken<C>(dstToken)) {
|
|
1529
|
+
if(typeof(dst)!=="string") throw new Error("Destination for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
|
|
1530
|
+
if(amount==null) throw new Error("Amount cannot be null for from btc swaps!");
|
|
1531
|
+
if(srcToken.lightning) {
|
|
1532
|
+
//FROM_BTCLN
|
|
1533
|
+
if(src!=null) {
|
|
1534
|
+
if(typeof(src)!=="string" && !isLNURLWithdraw(src)) throw new Error("LNURL must be a string or LNURLWithdraw object!");
|
|
1535
|
+
return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
|
|
1536
|
+
this.createFromBTCLNSwapNewViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any) :
|
|
1537
|
+
this.createFromBTCLNSwapViaLNURL(dstToken.chainId, dst, dstToken.address, src, amount, !exactIn, undefined, options as any);
|
|
1538
|
+
} else {
|
|
1539
|
+
return this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO) ?
|
|
1540
|
+
this.createFromBTCLNSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any):
|
|
1541
|
+
this.createFromBTCLNSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1542
|
+
}
|
|
1543
|
+
} else {
|
|
1544
|
+
//FROM_BTC
|
|
1545
|
+
if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
|
|
1546
|
+
return this.createFromBTCSwapNew(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1547
|
+
} else {
|
|
1548
|
+
return this.createFromBTCSwap(dstToken.chainId, dst, dstToken.address, amount, !exactIn, undefined, options as any);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
} else if(isSCToken<C>(srcToken)) {
|
|
1553
|
+
if(isBtcToken(dstToken)) {
|
|
1554
|
+
if(typeof(src)!=="string") throw new Error("Source address for BTC/BTC-LN -> smart chain swaps must be a smart chain address!");
|
|
1555
|
+
if(dstToken.lightning) {
|
|
1556
|
+
//TO_BTCLN
|
|
1557
|
+
if(typeof(dst)!=="string" && !isLNURLPay(dst)) throw new Error("Destination LNURL link/lightning invoice must be a string or LNURLPay object!");
|
|
1558
|
+
if(isLNURLPay(dst) || this.Utils.isValidLNURL(dst)) {
|
|
1559
|
+
if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via LNURL-pay!");
|
|
1560
|
+
return this.createToBTCLNSwapViaLNURL(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1561
|
+
} else if(isLightningInvoiceCreateService(dst)) {
|
|
1562
|
+
if(amount==null) throw new Error("Amount cannot be null for to btcln swaps via InvoiceCreateService!");
|
|
1563
|
+
return this.createToBTCLNSwapViaInvoiceCreateService(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1564
|
+
} else if(this.Utils.isLightningInvoice(dst)) {
|
|
1565
|
+
if(!this.Utils.isValidLightningInvoice(dst))
|
|
1566
|
+
throw new Error("Invalid lightning invoice specified, lightning invoice MUST contain pre-set amount!");
|
|
1567
|
+
if(exactIn)
|
|
1568
|
+
throw new Error("Only exact out swaps are possible with lightning invoices, use LNURL links for exact in lightning swaps!");
|
|
1569
|
+
return this.createToBTCLNSwap(srcToken.chainId, src, srcToken.address, dst, undefined, options as any);
|
|
1570
|
+
} else {
|
|
1571
|
+
throw new Error("Supplied parameter is not LNURL link nor lightning invoice (bolt11)!");
|
|
1572
|
+
}
|
|
1573
|
+
} else {
|
|
1574
|
+
//TO_BTC
|
|
1575
|
+
if(typeof(dst)!=="string") throw new Error("Destination bitcoin address must be a string!");
|
|
1576
|
+
if(amount==null) throw new Error("Amount cannot be null for to btc swaps!");
|
|
1577
|
+
return this.createToBTCSwap(srcToken.chainId, src, srcToken.address, dst, amount, !!exactIn, undefined, options as any);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
throw new Error("Unsupported swap type");
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* A helper function to sweep all the funds from a given wallet in a single swap, after getting the quote you can
|
|
1586
|
+
* execute the swap by passing the returned `feeRate` and `utxos` to the {@link SpvFromBTCSwap.execute},
|
|
1587
|
+
* {@link SpvFromBTCSwap.getFundedPsbt} or {@link SpvFromBTCSwap.sendBitcoinTransaction} functions along
|
|
1588
|
+
* with `spendFully=true`.
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* Create the swap first using this function
|
|
1592
|
+
* ```ts
|
|
1593
|
+
* const {swap, utxos, btcFeeRate} = await swapper.sweepBitcoinWallet(wallet, Tokens.CITREA.CBTC, dstAddress);
|
|
1594
|
+
* ```
|
|
1595
|
+
* Then execute it using one of these execution paths - ensure that you supply the returned `utxos`, `btcFeeRate`
|
|
1596
|
+
* params and also set `spendFully` to `true`!
|
|
1597
|
+
*
|
|
1598
|
+
* a) Execute and pass the returned utxos and btcFeeRate:
|
|
1599
|
+
* ```ts
|
|
1600
|
+
* await swap.execute(wallet, undefined, {feeRate: btcFeeRate, utxos: utxos, spendFully: true});
|
|
1601
|
+
* ```
|
|
1602
|
+
*
|
|
1603
|
+
* b) Get funded PSBT to sign externally:
|
|
1604
|
+
* ```ts
|
|
1605
|
+
* const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt(wallet, btcFeeRate, undefined, utxos, true);
|
|
1606
|
+
* // Sign the psbt at the specified signInputs indices
|
|
1607
|
+
* const signedPsbt = ...;
|
|
1608
|
+
* // Then submit back to the SDK
|
|
1609
|
+
* await swap.submitPsbt(signedPsbt);
|
|
1610
|
+
* ```
|
|
1611
|
+
*
|
|
1612
|
+
* c) Only sign and send the signed PSBT with the provided wallet:
|
|
1613
|
+
* ```ts
|
|
1614
|
+
* await swap.sendBitcoinTransaction(wallet, btcFeeRate, utxos, true);
|
|
1615
|
+
* ```
|
|
1616
|
+
*/
|
|
1617
|
+
async sweepBitcoinWallet<C extends ChainIds<T>>(
|
|
1618
|
+
srcWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
|
|
1619
|
+
_dstToken: SCToken<C> | string,
|
|
1620
|
+
dstAddress: string,
|
|
1621
|
+
options?: SpvFromBTCOptions
|
|
1622
|
+
): Promise<{
|
|
1623
|
+
swap: SpvFromBTCSwap<T[C]>,
|
|
1624
|
+
utxos: BitcoinWalletUtxo[],
|
|
1625
|
+
btcFeeRate: number
|
|
1626
|
+
}> {
|
|
1627
|
+
const dstToken = typeof(_dstToken)==="string" ? this.getToken(_dstToken) as Token<C> : _dstToken;
|
|
1628
|
+
if(!isSCToken<C>(dstToken)) throw new Error("Destination token must be a smart chain token!");
|
|
1629
|
+
|
|
1630
|
+
const wallet = toBitcoinWallet(srcWallet, this._bitcoinRpc, this.bitcoinNetwork);
|
|
1631
|
+
if(wallet.getUtxoPool==null) throw new Error("Wallet needs to support the `getUtxoPool()` function!");
|
|
1632
|
+
|
|
1633
|
+
const walletUtxosPromise = wallet.getUtxoPool();
|
|
1634
|
+
const bitcoinFeeRatePromise = options?.bitcoinFeeRate ?? wallet.getFeeRate();
|
|
1635
|
+
|
|
1636
|
+
const swap = await this.createFromBTCSwapNew(
|
|
1637
|
+
dstToken.chainId, dstAddress, dstToken.address, null, false, undefined, {
|
|
1638
|
+
...options,
|
|
1639
|
+
sourceWalletUtxos: walletUtxosPromise,
|
|
1640
|
+
bitcoinFeeRate: bitcoinFeeRatePromise
|
|
1641
|
+
}
|
|
1642
|
+
);
|
|
1643
|
+
|
|
1644
|
+
return {
|
|
1645
|
+
swap,
|
|
1646
|
+
utxos: await walletUtxosPromise,
|
|
1647
|
+
btcFeeRate: Math.max(swap.minimumBtcFeeRate, await bitcoinFeeRatePromise)
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* Returns all swaps
|
|
1653
|
+
*/
|
|
1654
|
+
getAllSwaps(): Promise<ISwap[]>;
|
|
1655
|
+
/**
|
|
1656
|
+
* Returns all swaps for the specific chain, and optionally also for a specific signer's address
|
|
1657
|
+
*/
|
|
1658
|
+
getAllSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
|
|
1659
|
+
async getAllSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
|
|
1660
|
+
const queryParams: QueryParams[] = [];
|
|
1661
|
+
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1662
|
+
|
|
1663
|
+
if(chainId==null) {
|
|
1664
|
+
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1665
|
+
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1666
|
+
return unifiedSwapStorage.query([queryParams], reviver);
|
|
1667
|
+
}));
|
|
1668
|
+
return res.flat();
|
|
1669
|
+
} else {
|
|
1670
|
+
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1671
|
+
return await unifiedSwapStorage.query([queryParams], reviver);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Returns all swaps where an action is required (either claim or refund)
|
|
1677
|
+
*/
|
|
1678
|
+
getActionableSwaps(): Promise<ISwap[]>;
|
|
1679
|
+
/**
|
|
1680
|
+
* Returns swaps where an action is required (either claim or refund) for the specific chain, and optionally also for a specific signer's address
|
|
1681
|
+
*/
|
|
1682
|
+
getActionableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<ISwap<T[C]>[]>;
|
|
1683
|
+
async getActionableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<ISwap[]> {
|
|
1684
|
+
if(chainId==null) {
|
|
1685
|
+
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1686
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1687
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1688
|
+
for(let key in wrappers) {
|
|
1689
|
+
const wrapper = wrappers[key as unknown as SwapType];
|
|
1690
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1691
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1692
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1693
|
+
queryParams.push(swapTypeQueryParams);
|
|
1694
|
+
}
|
|
1695
|
+
return unifiedSwapStorage.query(queryParams, reviver);
|
|
1696
|
+
}));
|
|
1697
|
+
return res.flat().filter(swap => swap.requiresAction());
|
|
1698
|
+
} else {
|
|
1699
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1700
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1701
|
+
for(let key in wrappers) {
|
|
1702
|
+
const wrapper = wrappers[key as unknown as SwapType];
|
|
1703
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1704
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1705
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1706
|
+
queryParams.push(swapTypeQueryParams);
|
|
1707
|
+
}
|
|
1708
|
+
return (await unifiedSwapStorage.query(queryParams, reviver)).filter(swap => swap.requiresAction());
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
/**
|
|
1713
|
+
* Returns all swaps that are refundable
|
|
1714
|
+
*/
|
|
1715
|
+
getRefundableSwaps(): Promise<IToBTCSwap[]>;
|
|
1716
|
+
/**
|
|
1717
|
+
* Returns swaps which are refundable for the specific chain, and optionally also for a specific signer's address
|
|
1718
|
+
*/
|
|
1719
|
+
getRefundableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IToBTCSwap<T[C]>[]>;
|
|
1720
|
+
async getRefundableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IToBTCSwap[]> {
|
|
1721
|
+
if(chainId==null) {
|
|
1722
|
+
const res: IToBTCSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1723
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1724
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1725
|
+
for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
|
|
1726
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1727
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1728
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
|
|
1729
|
+
queryParams.push(swapTypeQueryParams);
|
|
1730
|
+
}
|
|
1731
|
+
return unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
|
|
1732
|
+
}));
|
|
1733
|
+
return res.flat().filter(swap => swap.isRefundable());
|
|
1734
|
+
} else {
|
|
1735
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1736
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1737
|
+
for(let wrapper of [wrappers[SwapType.TO_BTCLN], wrappers[SwapType.TO_BTC]]) {
|
|
1738
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1739
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1740
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._refundableSwapStates});
|
|
1741
|
+
queryParams.push(swapTypeQueryParams);
|
|
1742
|
+
}
|
|
1743
|
+
const result = await unifiedSwapStorage.query<IToBTCSwap<T[C]>>(queryParams, reviver as (val: any) => IToBTCSwap<T[C]>);
|
|
1744
|
+
return result.filter(swap => swap.isRefundable());
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* Returns all swaps that are manually claimable
|
|
1750
|
+
*/
|
|
1751
|
+
getClaimableSwaps(): Promise<IClaimableSwap[]>;
|
|
1752
|
+
/**
|
|
1753
|
+
* Returns all swaps that are manually claimable for the specific chain, and optionally also for a specific signer's address
|
|
1754
|
+
*/
|
|
1755
|
+
getClaimableSwaps<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<IClaimableSwap<T[C]>[]>;
|
|
1756
|
+
async getClaimableSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<IClaimableSwap[]> {
|
|
1757
|
+
if(chainId==null) {
|
|
1758
|
+
const res: IClaimableSwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1759
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1760
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1761
|
+
for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
|
|
1762
|
+
if(wrapper==null) continue;
|
|
1763
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1764
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1765
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
|
|
1766
|
+
queryParams.push(swapTypeQueryParams);
|
|
1767
|
+
}
|
|
1768
|
+
return unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
|
|
1769
|
+
}));
|
|
1770
|
+
return res.flat().filter(swap => swap.isClaimable());
|
|
1771
|
+
} else {
|
|
1772
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1773
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1774
|
+
for(let wrapper of [wrappers[SwapType.FROM_BTC], wrappers[SwapType.FROM_BTCLN], wrappers[SwapType.SPV_VAULT_FROM_BTC], wrappers[SwapType.FROM_BTCLN_AUTO]]) {
|
|
1775
|
+
if(wrapper==null) continue;
|
|
1776
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1777
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1778
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._claimableSwapStates});
|
|
1779
|
+
queryParams.push(swapTypeQueryParams);
|
|
1780
|
+
}
|
|
1781
|
+
const result = await unifiedSwapStorage.query<IClaimableSwap<T[C]>>(queryParams, reviver as any as (val: any) => IClaimableSwap<T[C]>);
|
|
1782
|
+
return result.filter(swap => swap.isClaimable());
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Returns swap with a specific id (identifier)
|
|
1788
|
+
*/
|
|
1789
|
+
getSwapById(id: string): Promise<ISwap>;
|
|
1790
|
+
/**
|
|
1791
|
+
* Returns swap with a specific id (identifier) on a specific chain and optionally with a signer
|
|
1792
|
+
*/
|
|
1793
|
+
getSwapById<C extends ChainIds<T>>(id: string, chainId: C, signer?: string): Promise<ISwap<T[C]>>;
|
|
1794
|
+
async getSwapById<C extends ChainIds<T>>(id: string, chainId?: C, signer?: string): Promise<ISwap> {
|
|
1795
|
+
//Check in pending swaps first
|
|
1796
|
+
if(chainId!=null) {
|
|
1797
|
+
for(let key in this._chains[chainId].wrappers) {
|
|
1798
|
+
const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
|
|
1799
|
+
const result = wrapper._getPendingSwap(id);
|
|
1800
|
+
if(result!=null) {
|
|
1801
|
+
if (signer != null) {
|
|
1802
|
+
if (result._getInitiator() === signer) return result;
|
|
1803
|
+
} else {
|
|
1804
|
+
return result;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
} else {
|
|
1809
|
+
for(let chainId in this._chains) {
|
|
1810
|
+
for(let key in this._chains[chainId].wrappers) {
|
|
1811
|
+
const wrapper = this._chains[chainId].wrappers[key as unknown as SwapType];
|
|
1812
|
+
const result = wrapper._getPendingSwap(id);
|
|
1813
|
+
if(result!=null) {
|
|
1814
|
+
if(signer!=null) {
|
|
1815
|
+
if(result._getInitiator()===signer) return result;
|
|
1816
|
+
} else {
|
|
1817
|
+
return result;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
const queryParams: QueryParams[] = [];
|
|
1825
|
+
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1826
|
+
queryParams.push({key: "id", value: id});
|
|
1827
|
+
if(chainId==null) {
|
|
1828
|
+
const res: ISwap[][] = await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1829
|
+
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1830
|
+
return unifiedSwapStorage.query([queryParams], reviver);
|
|
1831
|
+
}));
|
|
1832
|
+
return res.flat()[0];
|
|
1833
|
+
} else {
|
|
1834
|
+
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1835
|
+
return (await unifiedSwapStorage.query([queryParams], reviver))[0];
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Returns the swap with a proper return type, or `undefined` if not found or has wrong type
|
|
1841
|
+
*
|
|
1842
|
+
* @param id An ID of the swap ({@link ISwap.getId})
|
|
1843
|
+
* @param chainId Chain identifier of the smart chain where the swap was initiated
|
|
1844
|
+
* @param swapType Type of the swap
|
|
1845
|
+
* @param signer An optional required smart chain signer address to fetch the swap for
|
|
1846
|
+
*/
|
|
1847
|
+
async getTypedSwapById<C extends ChainIds<T>, S extends SwapType>(id: string, chainId: C, swapType: S, signer?: string): Promise<SwapTypeMapping<T[C]>[S] | undefined> {
|
|
1848
|
+
let _swapType: SwapType = swapType;
|
|
1849
|
+
if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC))
|
|
1850
|
+
_swapType = SwapType.SPV_VAULT_FROM_BTC;
|
|
1851
|
+
if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO))
|
|
1852
|
+
_swapType = SwapType.FROM_BTCLN_AUTO;
|
|
1853
|
+
|
|
1854
|
+
const wrapper = this._chains[chainId].wrappers[_swapType];
|
|
1855
|
+
if(wrapper==null) return;
|
|
1856
|
+
|
|
1857
|
+
const result = wrapper._getPendingSwap(id);
|
|
1858
|
+
if(result!=null) {
|
|
1859
|
+
if (signer != null) {
|
|
1860
|
+
if (result._getInitiator() === signer) return result as any;
|
|
1861
|
+
} else {
|
|
1862
|
+
return result as any;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
const queryParams: QueryParams[] = [];
|
|
1867
|
+
if(signer!=null) queryParams.push({key: "initiator", value: signer});
|
|
1868
|
+
queryParams.push({key: "id", value: id});
|
|
1869
|
+
const {unifiedSwapStorage, reviver} = this._chains[chainId];
|
|
1870
|
+
const swap = (await unifiedSwapStorage.query([queryParams], reviver))[0];
|
|
1871
|
+
if(isSwapType(swap, swapType)) return swap;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
private async syncSwapsForChain<C extends ChainIds<T>>(chainId: C, signer?: string): Promise<void> {
|
|
1875
|
+
const {unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1876
|
+
const queryParams: Array<QueryParams[]> = [];
|
|
1877
|
+
for(let key in wrappers) {
|
|
1878
|
+
const wrapper = wrappers[key as unknown as SwapType];
|
|
1879
|
+
const swapTypeQueryParams: QueryParams[] = [{key: "type", value: wrapper.TYPE}];
|
|
1880
|
+
if(signer!=null) swapTypeQueryParams.push({key: "initiator", value: signer});
|
|
1881
|
+
swapTypeQueryParams.push({key: "state", value: wrapper._pendingSwapStates});
|
|
1882
|
+
queryParams.push(swapTypeQueryParams);
|
|
1883
|
+
}
|
|
1884
|
+
this.logger.debug("_syncSwaps(): Querying swaps swaps for chain "+chainId+"!");
|
|
1885
|
+
const swaps = await unifiedSwapStorage.query(queryParams, reviver);
|
|
1886
|
+
this.logger.debug("_syncSwaps(): Syncing "+swaps.length+" swaps!");
|
|
1887
|
+
|
|
1888
|
+
const changedSwaps: ISwap<T[C]>[] = [];
|
|
1889
|
+
const removeSwaps: ISwap<T[C]>[] = [];
|
|
1890
|
+
|
|
1891
|
+
const assortedSwaps: {[swapType in SwapType]?: ISwap<T[string]>[]} = {};
|
|
1892
|
+
swaps.forEach(swap => {
|
|
1893
|
+
(assortedSwaps[swap.getType()] ??= []).push(swap);
|
|
1894
|
+
});
|
|
1895
|
+
|
|
1896
|
+
for(let key in assortedSwaps) {
|
|
1897
|
+
const swapType = key as unknown as SwapType;
|
|
1898
|
+
const wrapperSwaps = assortedSwaps[swapType];
|
|
1899
|
+
const wrapper: ISwapWrapper<T[C], any> = wrappers[swapType];
|
|
1900
|
+
const result = await wrapper.checkPastSwaps(wrapperSwaps, true);
|
|
1901
|
+
changedSwaps.push(...result.changedSwaps);
|
|
1902
|
+
removeSwaps.push(...result.removeSwaps);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
this.logger.debug("_syncSwaps(): Done syncing "+swaps.length+" swaps, saving "+changedSwaps.length+" changed swaps, removing "+removeSwaps.length+" swaps!");
|
|
1906
|
+
await unifiedSwapStorage.saveAll(changedSwaps);
|
|
1907
|
+
await unifiedSwapStorage.removeAll(removeSwaps);
|
|
1908
|
+
|
|
1909
|
+
changedSwaps.forEach(swap => swap._emitEvent());
|
|
1910
|
+
removeSwaps.forEach(swap => swap._emitEvent());
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* Deletes the swaps from the persistent storage backend. Note that some data (like lightning network
|
|
1915
|
+
* amounts and bolt11 invoices) are purely off-chain and can never be recovered later just from
|
|
1916
|
+
* on-chain data!
|
|
1917
|
+
*
|
|
1918
|
+
* @param chainId Optional, to only delete swaps for this smart chain
|
|
1919
|
+
* @param signer Optional, to only delete swaps for this smart chain signer (`chainId` param must be
|
|
1920
|
+
* set to delete only signer's swaps)
|
|
1921
|
+
*/
|
|
1922
|
+
async wipeStorage<C extends ChainIds<T>>(chainId?: C, signer?: string) {
|
|
1923
|
+
if(chainId==null) {
|
|
1924
|
+
const swaps = await this.getAllSwaps();
|
|
1925
|
+
const chainSwaps: {
|
|
1926
|
+
[chainId: string]: ISwap<any>[]
|
|
1927
|
+
} = {};
|
|
1928
|
+
swaps.forEach(swap => (chainSwaps[swap.chainIdentifier] ??= []).push(swap));
|
|
1929
|
+
for(let chainId in chainSwaps) {
|
|
1930
|
+
const currentChainSwaps = chainSwaps[chainId];
|
|
1931
|
+
if(this._chains[chainId]==null) {
|
|
1932
|
+
this.logger.warn(`wipeStorage(): Attempted to remove ${currentChainSwaps.length} swaps on ${chainId}, but smart chain not known!`);
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
await this._chains[chainId].unifiedSwapStorage.removeAll(currentChainSwaps);
|
|
1936
|
+
this.logger.debug(`wipeStorage(): Successfully removed ${currentChainSwaps.length} swaps on ${chainId}!`);
|
|
1937
|
+
}
|
|
1938
|
+
} else {
|
|
1939
|
+
if(this._chains[chainId]==null) throw new Error(`wipeStorage(): Smart chain with identifier ${chainId} not found!`);
|
|
1940
|
+
const swaps = await this.getAllSwaps(chainId, signer);
|
|
1941
|
+
await this._chains[chainId].unifiedSwapStorage.removeAll(swaps);
|
|
1942
|
+
this.logger.debug(`wipeStorage(): Successfully removed ${swaps.length} swaps on ${chainId}!`);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
/**
|
|
1947
|
+
* Synchronizes swaps from on-chain, this is ran automatically when SDK is initialized, hence
|
|
1948
|
+
* should only be ran manually when `dontCheckPastSwaps=true` is passed in the swapper options,
|
|
1949
|
+
* also deletes expired quotes
|
|
1950
|
+
*
|
|
1951
|
+
* @param chainId Optional chain identifier to only run swap sync for a single smart chain
|
|
1952
|
+
* @param signer Optional signer to only run swap sync for swaps initiated by this signer
|
|
1953
|
+
*/
|
|
1954
|
+
async _syncSwaps<C extends ChainIds<T>>(chainId?: C, signer?: string): Promise<void> {
|
|
1955
|
+
if(chainId==null) {
|
|
1956
|
+
await Promise.all(Object.keys(this._chains).map((chainId) => {
|
|
1957
|
+
return this.syncSwapsForChain(chainId, signer);
|
|
1958
|
+
}));
|
|
1959
|
+
} else {
|
|
1960
|
+
await this.syncSwapsForChain(chainId, signer);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/**
|
|
1965
|
+
* Recovers swaps from on-chain historical data.
|
|
1966
|
+
*
|
|
1967
|
+
* Please note that the recovered swaps might not be complete (i.e. missing amounts or addresses), as some
|
|
1968
|
+
* of the swap data is purely off-chain and can never be recovered purely from on-chain data. This
|
|
1969
|
+
* functions tries to recover as much swap data as possible.
|
|
1970
|
+
*
|
|
1971
|
+
* @param chainId Smart chain identifier string to recover the swaps from
|
|
1972
|
+
* @param signer Signer address to recover the swaps for
|
|
1973
|
+
* @param startBlockheight Optional starting blockheight for swap data recovery, will only check swaps
|
|
1974
|
+
* initiated after this blockheight
|
|
1975
|
+
*/
|
|
1976
|
+
async recoverSwaps<C extends ChainIds<T>>(chainId: C, signer: string, startBlockheight?: number): Promise<ISwap<T[C]>[]> {
|
|
1977
|
+
//TODO: Recover swaps from all the known contract versions
|
|
1978
|
+
const {versionedContracts, unifiedSwapStorage, reviver, wrappers} = this._chains[chainId];
|
|
1979
|
+
|
|
1980
|
+
const recoveredSwaps: ISwap<T[C]>[] = [];
|
|
1981
|
+
let someVersionSupportsRecovery = false;
|
|
1982
|
+
const recoveredEscrowStates: {
|
|
1983
|
+
[p: string]: {
|
|
1984
|
+
init?: {
|
|
1985
|
+
data: SwapData
|
|
1986
|
+
getInitTxId: () => Promise<string>
|
|
1987
|
+
getTxBlock: () => Promise<{
|
|
1988
|
+
blockTime: number
|
|
1989
|
+
blockHeight: number
|
|
1990
|
+
}>
|
|
1991
|
+
},
|
|
1992
|
+
state: SwapCommitState
|
|
1993
|
+
contractVersion: string
|
|
1994
|
+
}
|
|
1995
|
+
} = {};
|
|
1996
|
+
const recoveredSpvStates: {
|
|
1997
|
+
[contractVersion: string]: {
|
|
1998
|
+
[escrowHash: string]: SpvWithdrawalClaimedState | SpvWithdrawalFrontedState
|
|
1999
|
+
}
|
|
2000
|
+
} = {};
|
|
2001
|
+
|
|
2002
|
+
for(let contractVersion in versionedContracts) {
|
|
2003
|
+
const {swapContract, spvVaultContract} = versionedContracts[contractVersion];
|
|
2004
|
+
|
|
2005
|
+
if(
|
|
2006
|
+
swapContract.getHistoricalSwaps==null ||
|
|
2007
|
+
(spvVaultContract!=null && spvVaultContract.getHistoricalWithdrawalStates==null)
|
|
2008
|
+
) {
|
|
2009
|
+
this.logger.warn(`recoverSwaps(): Swap data recovery not supported on ${chainId}, with contract version ${contractVersion}`);
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
someVersionSupportsRecovery = true;
|
|
2014
|
+
|
|
2015
|
+
const {swaps} = await swapContract.getHistoricalSwaps(signer, startBlockheight);
|
|
2016
|
+
const spvVaultData = wrappers[SwapType.SPV_VAULT_FROM_BTC]==null
|
|
2017
|
+
? undefined
|
|
2018
|
+
: await spvVaultContract?.getHistoricalWithdrawalStates!(signer, startBlockheight);
|
|
2019
|
+
|
|
2020
|
+
for(let key in swaps) recoveredEscrowStates[key] = {...swaps[key], contractVersion};
|
|
2021
|
+
if(spvVaultData!=null) for(let key in spvVaultData.withdrawals) (recoveredSpvStates[contractVersion] ??= {})[key] = spvVaultData.withdrawals[key];
|
|
2022
|
+
}
|
|
2023
|
+
if(!someVersionSupportsRecovery) throw new Error(`Historical swap recovery is not supported for ${chainId}`);
|
|
2024
|
+
|
|
2025
|
+
const escrowHashes = Object.keys(recoveredEscrowStates);
|
|
2026
|
+
for(let contractVersion in recoveredSpvStates) Object.keys(recoveredSpvStates[contractVersion]).forEach(btcTxId => escrowHashes.push(btcTxId));
|
|
2027
|
+
this.logger.debug(`recoverSwaps(): Loaded on-chain data for ${escrowHashes.length} swaps`);
|
|
2028
|
+
this.logger.debug(`recoverSwaps(): Fetching if swap escrowHashes are known: ${escrowHashes.join(", ")}`);
|
|
2029
|
+
const knownSwapsArray = await unifiedSwapStorage.query([[{key: "escrowHash", value: escrowHashes}]], reviver);
|
|
2030
|
+
const knownSwaps: {[escrowHash: string]: ISwap<T[C]>} = {};
|
|
2031
|
+
knownSwapsArray.forEach(val => {
|
|
2032
|
+
const escrowHash = val._getEscrowHash();
|
|
2033
|
+
if(escrowHash!=null) knownSwaps[escrowHash] = val;
|
|
2034
|
+
});
|
|
2035
|
+
this.logger.debug(`recoverSwaps(): Fetched known swaps escrowHashes: ${Object.keys(knownSwaps).join(", ")}`);
|
|
2036
|
+
|
|
2037
|
+
for(let escrowHash in recoveredEscrowStates) {
|
|
2038
|
+
const {init, state, contractVersion} = recoveredEscrowStates[escrowHash];
|
|
2039
|
+
const knownSwap = knownSwaps[escrowHash];
|
|
2040
|
+
const { swapContract } = versionedContracts[contractVersion];
|
|
2041
|
+
|
|
2042
|
+
if(knownSwap==null) {
|
|
2043
|
+
if(init==null) {
|
|
2044
|
+
this.logger.warn(`recoverSwaps(escrow): Fetched ${escrowHash} swap state, but swap not found locally!`);
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
} else if(knownSwap instanceof IEscrowSwap) {
|
|
2048
|
+
this.logger.debug(`recoverSwaps(escrow): Forcibly updating ${escrowHash} swap: swap already known and in local storage!`);
|
|
2049
|
+
if((knownSwap._contractVersion ?? "v1")!==contractVersion) {
|
|
2050
|
+
this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap uses contract version ${knownSwap._contractVersion ?? "v1"}, but state comes from ${contractVersion}!`);
|
|
2051
|
+
continue;
|
|
2052
|
+
}
|
|
2053
|
+
if(await knownSwap._forciblySetOnchainState(state)) {
|
|
2054
|
+
await knownSwap._save();
|
|
2055
|
+
}
|
|
2056
|
+
continue;
|
|
2057
|
+
} else {
|
|
2058
|
+
this.logger.debug(`recoverSwaps(escrow): Skipping ${escrowHash} swap: swap already known and in local storage!`);
|
|
2059
|
+
continue;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
const data = init.data;
|
|
2063
|
+
|
|
2064
|
+
//Classify swap
|
|
2065
|
+
let swap: ISwap<T[C]> | undefined | null;
|
|
2066
|
+
let typeIdentified: boolean = false;
|
|
2067
|
+
if(data.getType()===ChainSwapType.HTLC) {
|
|
2068
|
+
if(data.isOfferer(signer)) {
|
|
2069
|
+
//To BTCLN
|
|
2070
|
+
typeIdentified = true;
|
|
2071
|
+
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
|
|
2072
|
+
swap = await wrappers[SwapType.TO_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2073
|
+
} else if(data.isClaimer(signer)) {
|
|
2074
|
+
//From BTCLN
|
|
2075
|
+
typeIdentified = true;
|
|
2076
|
+
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
|
|
2077
|
+
if(swapContract.supportsInitWithoutClaimer && wrappers[SwapType.FROM_BTCLN_AUTO]!=null) {
|
|
2078
|
+
swap = await wrappers[SwapType.FROM_BTCLN_AUTO].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2079
|
+
} else {
|
|
2080
|
+
swap = await wrappers[SwapType.FROM_BTCLN].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
} else if(data.getType()===ChainSwapType.CHAIN_NONCED) {
|
|
2084
|
+
//To BTC
|
|
2085
|
+
typeIdentified = true;
|
|
2086
|
+
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isClaimer(val.getAddress(chainId)));
|
|
2087
|
+
swap = await wrappers[SwapType.TO_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2088
|
+
} else if(data.getType()===ChainSwapType.CHAIN) {
|
|
2089
|
+
//From BTC
|
|
2090
|
+
typeIdentified = true;
|
|
2091
|
+
const lp = this.intermediaryDiscovery.intermediaries.find(val => val.supportsChain(chainId) && data.isOfferer(val.getAddress(chainId)));
|
|
2092
|
+
swap = await wrappers[SwapType.FROM_BTC].recoverFromSwapDataAndState(init, state, contractVersion, lp);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
if(swap!=null) {
|
|
2096
|
+
recoveredSwaps.push(swap);
|
|
2097
|
+
} else {
|
|
2098
|
+
if(typeIdentified) this.logger.debug(`recoverSwaps(escrow): Swap data type correctly identified but swap returned is null for swap ${escrowHash}`);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
for(let contractVersion in recoveredSpvStates) {
|
|
2103
|
+
const { spvVaultContract } = versionedContracts[contractVersion];
|
|
2104
|
+
const spvVaultData = recoveredSpvStates[contractVersion];
|
|
2105
|
+
|
|
2106
|
+
const vaultsData = await spvVaultContract.getMultipleVaultData(
|
|
2107
|
+
Object.keys(spvVaultData)
|
|
2108
|
+
.map(btcTxId => ({
|
|
2109
|
+
owner: spvVaultData[btcTxId].owner,
|
|
2110
|
+
vaultId: spvVaultData[btcTxId].vaultId
|
|
2111
|
+
}))
|
|
2112
|
+
);
|
|
2113
|
+
|
|
2114
|
+
for(let btcTxId in spvVaultData) {
|
|
2115
|
+
const state = spvVaultData[btcTxId];
|
|
2116
|
+
const knownSwap = knownSwaps[btcTxId];
|
|
2117
|
+
|
|
2118
|
+
if(knownSwap!=null) {
|
|
2119
|
+
if(knownSwap instanceof SpvFromBTCSwap) {
|
|
2120
|
+
this.logger.debug(`recoverSwaps(spv_vault): Forcibly updating ${btcTxId} swap: swap already known and in local storage!`);
|
|
2121
|
+
//TODO: Forcibly set on-chain state to the swap
|
|
2122
|
+
// if(await knownSwap._forciblySetOnchainState(state)) {
|
|
2123
|
+
// await knownSwap._save();
|
|
2124
|
+
// }
|
|
2125
|
+
continue;
|
|
2126
|
+
} else {
|
|
2127
|
+
this.logger.debug(`recoverSwaps(spv_vault): Skipping ${btcTxId} swap: swap already known and in local storage!`);
|
|
2128
|
+
continue;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
const lp = this.intermediaryDiscovery.intermediaries.find(
|
|
2133
|
+
val => val.supportsChain(chainId) && state.owner.toLowerCase()===val.getAddress(chainId).toLowerCase()
|
|
2134
|
+
);
|
|
2135
|
+
const swap = await wrappers[SwapType.SPV_VAULT_FROM_BTC].recoverFromState(
|
|
2136
|
+
state,
|
|
2137
|
+
contractVersion,
|
|
2138
|
+
vaultsData[state.owner]?.[state.vaultId.toString(10)],
|
|
2139
|
+
lp
|
|
2140
|
+
);
|
|
2141
|
+
if(swap!=null) {
|
|
2142
|
+
recoveredSwaps.push(swap);
|
|
2143
|
+
} else {
|
|
2144
|
+
this.logger.debug(`recoverSwaps(spv_vault): Swap data type correctly identified but swap returned is null for swap ${btcTxId}`);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
this.logger.debug(`recoverSwaps(): Successfully recovered ${recoveredSwaps.length} swaps!`);
|
|
2150
|
+
|
|
2151
|
+
return recoveredSwaps;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
getToken(ticker: "BTC" | "BITCOIN-BTC"): BtcToken<false>;
|
|
2155
|
+
getToken(ticker: "BTCLN" | "BTC-LN" | "LIGHTNING-BTC"): BtcToken<true>;
|
|
2156
|
+
getToken<ChainIdentifier extends ChainIds<T>>(ticker: `${ChainIdentifier}-${string}`): SCToken<ChainIdentifier>;
|
|
2157
|
+
getToken(tickerOrAddress: string): Token<ChainIds<T>>;
|
|
2158
|
+
/**
|
|
2159
|
+
* Returns the {@link Token} object for a given token
|
|
2160
|
+
*
|
|
2161
|
+
* @param tickerOrAddress Token to return the object for, can use multiple formats:
|
|
2162
|
+
* - a) token ticker, such as `"BTC"`, `"SOL"`, etc.
|
|
2163
|
+
* - b) token ticker prefixed with smart chain identifier, such as `"SOLANA-SOL"`, `"SOLANA-USDC"`, etc.
|
|
2164
|
+
* - c) token address
|
|
2165
|
+
*/
|
|
2166
|
+
getToken(tickerOrAddress: string): Token<ChainIds<T>> {
|
|
2167
|
+
//Btc tokens - BTC, BTCLN, BTC-LN
|
|
2168
|
+
if(tickerOrAddress==="BTC" || tickerOrAddress==="BITCOIN-BTC") return BitcoinTokens.BTC;
|
|
2169
|
+
if(tickerOrAddress==="BTCLN" || tickerOrAddress==="BTC-LN" || tickerOrAddress==="LIGHTNING-BTC") return BitcoinTokens.BTCLN;
|
|
2170
|
+
|
|
2171
|
+
//Check if the ticker is in format <chainId>-<ticker>, i.e. SOLANA-USDC, STARKNET-WBTC
|
|
2172
|
+
if(tickerOrAddress.includes("-")) {
|
|
2173
|
+
const [chainId, ticker] = tickerOrAddress.split("-");
|
|
2174
|
+
const token = this._tokensByTicker[chainId]?.[ticker];
|
|
2175
|
+
if(token==null) throw new UserError(`Not found ticker: ${ticker} for chainId: ${chainId}`);
|
|
2176
|
+
return token;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
const possibleTokens: SCToken[] = [];
|
|
2180
|
+
for(let chainId in this._chains) {
|
|
2181
|
+
const chain = this._chains[chainId];
|
|
2182
|
+
if(chain.chainInterface.isValidToken(tickerOrAddress)) {
|
|
2183
|
+
//Try to find in known token addresses
|
|
2184
|
+
const token = this._tokens[chainId]?.[tickerOrAddress];
|
|
2185
|
+
if(token!=null) return token;
|
|
2186
|
+
} else {
|
|
2187
|
+
//Check in known tickers
|
|
2188
|
+
const token = this._tokensByTicker[chainId]?.[tickerOrAddress];
|
|
2189
|
+
if(token!=null) possibleTokens.push(token);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
if(possibleTokens.length===0) throw new UserError(`Specified token address or ticker ${tickerOrAddress} not found!`);
|
|
2194
|
+
//In case we've found the token in multiple chains
|
|
2195
|
+
if(possibleTokens.length>1) throw new UserError(`A ticker ${tickerOrAddress} has been found in multiple chains, narrow it down by using <chainId>-${tickerOrAddress} notation`)
|
|
2196
|
+
return possibleTokens[0];
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
/**
|
|
2200
|
+
* Creates a child swapper instance with a given smart chain
|
|
2201
|
+
*
|
|
2202
|
+
* @param chainIdentifier Smart chain identifier for the created child swapper instance
|
|
2203
|
+
*/
|
|
2204
|
+
withChain<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier): SwapperWithChain<T, ChainIdentifier> {
|
|
2205
|
+
if(this._chains[chainIdentifier]==null) throw new Error("Invalid chain identifier! Unknown chain: "+chainIdentifier);
|
|
2206
|
+
return new SwapperWithChain<T, ChainIdentifier>(this, chainIdentifier as ChainIdentifier);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
/**
|
|
2210
|
+
* Returns an array of all the supported smart chains
|
|
2211
|
+
*/
|
|
2212
|
+
getSmartChains(): ChainIds<T>[] {
|
|
2213
|
+
return Object.keys(this._chains);
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
/**
|
|
2217
|
+
* Returns whether the SDK supports a given swap type on a given chain based on currently known LPs
|
|
2218
|
+
*
|
|
2219
|
+
* @param chainId Smart chain identifier string
|
|
2220
|
+
* @param swapType Swap protocol type
|
|
2221
|
+
*/
|
|
2222
|
+
supportsSwapType<
|
|
2223
|
+
ChainIdentifier extends ChainIds<T>,
|
|
2224
|
+
Type extends SwapType
|
|
2225
|
+
>(chainId: ChainIdentifier, swapType: Type): SupportsSwapType<T[ChainIdentifier], Type> {
|
|
2226
|
+
return (this._chains[chainId]?.wrappers[swapType] != null) as any;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
/**
|
|
2230
|
+
* @internal
|
|
2231
|
+
*/
|
|
2232
|
+
getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<true>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.FROM_BTCLN_AUTO> extends true ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN);
|
|
2233
|
+
/**
|
|
2234
|
+
* @internal
|
|
2235
|
+
*/
|
|
2236
|
+
getSwapType<C extends ChainIds<T>>(srcToken: BtcToken<false>, dstToken: SCToken<C>): (SupportsSwapType<T[C], SwapType.SPV_VAULT_FROM_BTC> extends true ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC);
|
|
2237
|
+
/**
|
|
2238
|
+
* @internal
|
|
2239
|
+
*/
|
|
2240
|
+
getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<false>): SwapType.TO_BTC;
|
|
2241
|
+
/**
|
|
2242
|
+
* @internal
|
|
2243
|
+
*/
|
|
2244
|
+
getSwapType<C extends ChainIds<T>>(srcToken: SCToken<C>, dstToken: BtcToken<true>): SwapType.TO_BTCLN;
|
|
2245
|
+
/**
|
|
2246
|
+
* Returns type of the swap based on input and output tokens specified
|
|
2247
|
+
*
|
|
2248
|
+
* @param srcToken Source token
|
|
2249
|
+
* @param dstToken Destination token
|
|
2250
|
+
*/
|
|
2251
|
+
getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN;
|
|
2252
|
+
/**
|
|
2253
|
+
* Returns type of the swap based on input and output tokens specified
|
|
2254
|
+
*
|
|
2255
|
+
* @param srcToken Source token
|
|
2256
|
+
* @param dstToken Destination token
|
|
2257
|
+
*/
|
|
2258
|
+
getSwapType<C extends ChainIds<T>>(srcToken: Token<C>, dstToken: Token<C>): SwapType.FROM_BTCLN_AUTO | SwapType.FROM_BTCLN | SwapType.SPV_VAULT_FROM_BTC | SwapType.FROM_BTC | SwapType.TO_BTC | SwapType.TO_BTCLN {
|
|
2259
|
+
if(isSCToken(srcToken)) {
|
|
2260
|
+
if(!isBtcToken(dstToken)) throw new Error("Swap not supported");
|
|
2261
|
+
if(dstToken.lightning) {
|
|
2262
|
+
return SwapType.TO_BTCLN;
|
|
2263
|
+
} else {
|
|
2264
|
+
return SwapType.TO_BTC;
|
|
2265
|
+
}
|
|
2266
|
+
} else if(isBtcToken(srcToken)) {
|
|
2267
|
+
if(!isSCToken(dstToken)) throw new Error("Swap not supported");
|
|
2268
|
+
if(srcToken.lightning) {
|
|
2269
|
+
if(this.supportsSwapType(dstToken.chainId, SwapType.FROM_BTCLN_AUTO)) {
|
|
2270
|
+
return SwapType.FROM_BTCLN_AUTO;
|
|
2271
|
+
} else {
|
|
2272
|
+
return SwapType.FROM_BTCLN;
|
|
2273
|
+
}
|
|
2274
|
+
} else {
|
|
2275
|
+
if(this.supportsSwapType(dstToken.chainId, SwapType.SPV_VAULT_FROM_BTC)) {
|
|
2276
|
+
return SwapType.SPV_VAULT_FROM_BTC;
|
|
2277
|
+
} else {
|
|
2278
|
+
return SwapType.FROM_BTC;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
throw new Error("Swap not supported");
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* Helper information about various swap protocol and their features:
|
|
2287
|
+
* - `requiresInputWallet`: Whether a swap requires a connected wallet on the input chain able to sign
|
|
2288
|
+
* arbitrary transaction
|
|
2289
|
+
* - `requiresOutputWallet`: Whether a swap requires a connected wallet on the output chain able to sign
|
|
2290
|
+
* arbitrary transactions
|
|
2291
|
+
* - `supportsGasDrop`: Whether a swap supports the "gas drop" feature, allowing to user to receive a small
|
|
2292
|
+
* amount of native token as part of the swap when swapping to smart chains
|
|
2293
|
+
*
|
|
2294
|
+
* Uses a `Record` type here, use the {@link SwapProtocolInfo} import for a literal readonly type, with
|
|
2295
|
+
* pre-filled exact values in the type.
|
|
2296
|
+
*/
|
|
2297
|
+
readonly SwapTypeInfo = SwapProtocolInfo as Record<SwapType, {
|
|
2298
|
+
requiresInputWallet: boolean,
|
|
2299
|
+
requiresOutputWallet: boolean,
|
|
2300
|
+
supportsGasDrop: boolean
|
|
2301
|
+
}>;
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Returns minimum/maximum limits for inputs and outputs for a swap between given tokens
|
|
2305
|
+
*
|
|
2306
|
+
* @param srcToken Source token
|
|
2307
|
+
* @param dstToken Destination token
|
|
2308
|
+
*/
|
|
2309
|
+
getSwapLimits<C extends ChainIds<T>, A extends Token<C>, B extends Token<C>>(srcToken: A, dstToken: B): {
|
|
2310
|
+
input: {min: TokenAmount<A>, max?: TokenAmount<A>},
|
|
2311
|
+
output: {min: TokenAmount<B>, max?: TokenAmount<B>}
|
|
2312
|
+
} {
|
|
2313
|
+
const swapType = this.getSwapType(srcToken, dstToken);
|
|
2314
|
+
const scToken = isSCToken(srcToken) ? srcToken : isSCToken(dstToken) ? dstToken : null;
|
|
2315
|
+
if(scToken==null) throw new Error("At least one token needs to be a smart chain token!");
|
|
2316
|
+
const result: {input: {min?: bigint, max?: bigint}, output: {min?: bigint, max?: bigint}} = {
|
|
2317
|
+
input: {},
|
|
2318
|
+
output: {}
|
|
2319
|
+
};
|
|
2320
|
+
for(let lp of this.intermediaryDiscovery.intermediaries) {
|
|
2321
|
+
const lpMinMax = lp.getSwapLimits(swapType, scToken.chainId, scToken.address);
|
|
2322
|
+
if(lpMinMax==null) continue;
|
|
2323
|
+
result.input.min = result.input.min==null ? lpMinMax.input.min : bigIntMin(result.input.min, lpMinMax.input.min);
|
|
2324
|
+
result.input.max = result.input.max==null ? lpMinMax.input.max : bigIntMax(result.input.max, lpMinMax.input.max);
|
|
2325
|
+
result.output.min = result.output.min==null ? lpMinMax.output.min : bigIntMin(result.output.min, lpMinMax.output.min);
|
|
2326
|
+
result.output.max = result.output.max==null ? lpMinMax.output.max : bigIntMax(result.output.max, lpMinMax.output.max);
|
|
2327
|
+
}
|
|
2328
|
+
return {
|
|
2329
|
+
input: {
|
|
2330
|
+
min: toTokenAmount(result.input.min ?? 1n, srcToken, this.prices),
|
|
2331
|
+
max: result.input.max==null ? undefined : toTokenAmount(result.input.max, srcToken, this.prices),
|
|
2332
|
+
},
|
|
2333
|
+
output: {
|
|
2334
|
+
min: toTokenAmount(result.output.min ?? 1n, dstToken, this.prices),
|
|
2335
|
+
max: result.output.max==null ? undefined : toTokenAmount(result.output.max, dstToken, this.prices),
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
/**
|
|
2341
|
+
* Returns an array of supported tokens either on the input or on the output of a swap
|
|
2342
|
+
*
|
|
2343
|
+
* @param input Whether to return input tokens or output tokens
|
|
2344
|
+
*/
|
|
2345
|
+
getSupportedTokens(input: SwapSide | boolean): Token[] {
|
|
2346
|
+
const tokens: {[chainId: string]: Set<string>} = {};
|
|
2347
|
+
let lightning = false;
|
|
2348
|
+
let btc = false;
|
|
2349
|
+
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2350
|
+
for(let swapType of [SwapType.TO_BTC, SwapType.TO_BTCLN, SwapType.FROM_BTC, SwapType.FROM_BTCLN, SwapType.SPV_VAULT_FROM_BTC, SwapType.FROM_BTCLN_AUTO]) {
|
|
2351
|
+
if(lp.services[swapType]?.chainTokens==null) continue;
|
|
2352
|
+
for(let chainId of this.getSmartChains()) {
|
|
2353
|
+
if(this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC) ? swapType===SwapType.FROM_BTC : swapType===SwapType.SPV_VAULT_FROM_BTC) continue;
|
|
2354
|
+
if(this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO) ? swapType===SwapType.FROM_BTCLN : swapType===SwapType.FROM_BTCLN_AUTO) continue;
|
|
2355
|
+
const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
|
|
2356
|
+
if(chainTokens==null) continue;
|
|
2357
|
+
for (let tokenAddress of chainTokens) {
|
|
2358
|
+
if(input) {
|
|
2359
|
+
if(swapType===SwapType.TO_BTC || swapType===SwapType.TO_BTCLN) {
|
|
2360
|
+
tokens[chainId] ??= new Set();
|
|
2361
|
+
tokens[chainId].add(tokenAddress);
|
|
2362
|
+
}
|
|
2363
|
+
if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTCLN_AUTO) {
|
|
2364
|
+
lightning = true;
|
|
2365
|
+
}
|
|
2366
|
+
if(swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC) {
|
|
2367
|
+
btc = true;
|
|
2368
|
+
}
|
|
2369
|
+
} else {
|
|
2370
|
+
if(swapType===SwapType.FROM_BTCLN || swapType===SwapType.FROM_BTC || swapType===SwapType.SPV_VAULT_FROM_BTC || swapType===SwapType.FROM_BTCLN_AUTO) {
|
|
2371
|
+
tokens[chainId] ??= new Set();
|
|
2372
|
+
tokens[chainId].add(tokenAddress);
|
|
2373
|
+
}
|
|
2374
|
+
if(swapType===SwapType.TO_BTCLN) {
|
|
2375
|
+
lightning = true;
|
|
2376
|
+
}
|
|
2377
|
+
if(swapType===SwapType.TO_BTC) {
|
|
2378
|
+
btc = true;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2385
|
+
const output: Token[] = [];
|
|
2386
|
+
if(lightning) output.push(BitcoinTokens.BTCLN);
|
|
2387
|
+
if(btc) output.push(BitcoinTokens.BTC);
|
|
2388
|
+
for(let chainId in tokens) {
|
|
2389
|
+
tokens[chainId].forEach(tokenAddress => {
|
|
2390
|
+
const token = this._tokens?.[chainId]?.[tokenAddress];
|
|
2391
|
+
if(token!=null) output.push(token);
|
|
2392
|
+
})
|
|
2393
|
+
}
|
|
2394
|
+
return output;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/**
|
|
2398
|
+
* Returns a set of supported tokens by all the intermediaries offering a specific swap service
|
|
2399
|
+
*
|
|
2400
|
+
* @param _swapType Swap service type to check supported tokens for
|
|
2401
|
+
*/
|
|
2402
|
+
private getSupportedTokensForSwapType(_swapType: SwapType): SCToken[] {
|
|
2403
|
+
const tokens: {[chainId: string]: Set<string>} = {};
|
|
2404
|
+
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2405
|
+
for(let chainId of this.getSmartChains()) {
|
|
2406
|
+
let swapType = _swapType;
|
|
2407
|
+
if(swapType===SwapType.FROM_BTC && this.supportsSwapType(chainId, SwapType.SPV_VAULT_FROM_BTC)) swapType = SwapType.SPV_VAULT_FROM_BTC;
|
|
2408
|
+
if(swapType===SwapType.FROM_BTCLN && this.supportsSwapType(chainId, SwapType.FROM_BTCLN_AUTO)) swapType = SwapType.FROM_BTCLN_AUTO;
|
|
2409
|
+
if(lp.services[swapType]?.chainTokens==null) break;
|
|
2410
|
+
const chainTokens = lp.services[swapType]?.chainTokens?.[chainId];
|
|
2411
|
+
if(chainTokens==null) continue;
|
|
2412
|
+
for(let tokenAddress of chainTokens) {
|
|
2413
|
+
tokens[chainId] ??= new Set();
|
|
2414
|
+
tokens[chainId].add(tokenAddress);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
const output: SCToken[] = [];
|
|
2419
|
+
for(let chainId in tokens) {
|
|
2420
|
+
tokens[chainId].forEach(tokenAddress => {
|
|
2421
|
+
const token = this._tokens?.[chainId]?.[tokenAddress];
|
|
2422
|
+
if(token!=null) output.push(token);
|
|
2423
|
+
})
|
|
2424
|
+
}
|
|
2425
|
+
return output;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
/**
|
|
2429
|
+
* Returns the set of supported token addresses by all the intermediaries we know of offering a specific swapType service
|
|
2430
|
+
*
|
|
2431
|
+
* @param chainIdentifier Chain identifier string
|
|
2432
|
+
* @param swapType Specific swap type for which to obtain supported tokens
|
|
2433
|
+
*/
|
|
2434
|
+
private getSupportedTokenAddresses<ChainIdentifier extends ChainIds<T>>(chainIdentifier: ChainIdentifier, swapType: SwapType): Set<string> {
|
|
2435
|
+
const set = new Set<string>();
|
|
2436
|
+
this.intermediaryDiscovery.intermediaries.forEach(lp => {
|
|
2437
|
+
const chainTokens = lp.services[swapType]?.chainTokens?.[chainIdentifier];
|
|
2438
|
+
if(chainTokens==null) return;
|
|
2439
|
+
chainTokens.forEach(token => set.add(token));
|
|
2440
|
+
});
|
|
2441
|
+
return set;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
/**
|
|
2445
|
+
* Returns tokens that you can swap to (if input=true) from a given token,
|
|
2446
|
+
* or tokens that you can swap from (if input=false) to a given token
|
|
2447
|
+
*/
|
|
2448
|
+
getSwapCounterTokens(token: Token, input: SwapSide | boolean): Token[] {
|
|
2449
|
+
if(isSCToken(token)) {
|
|
2450
|
+
const result: Token[] = [];
|
|
2451
|
+
if(input) {
|
|
2452
|
+
//TO_BTC or TO_BTCLN
|
|
2453
|
+
if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTCLN).has(token.address)) {
|
|
2454
|
+
result.push(BitcoinTokens.BTCLN);
|
|
2455
|
+
}
|
|
2456
|
+
if(this.getSupportedTokenAddresses(token.chainId, SwapType.TO_BTC).has(token.address)) {
|
|
2457
|
+
result.push(BitcoinTokens.BTC);
|
|
2458
|
+
}
|
|
2459
|
+
} else {
|
|
2460
|
+
//FROM_BTC or FROM_BTCLN
|
|
2461
|
+
const fromLightningSwapType = this.supportsSwapType(token.chainId, SwapType.FROM_BTCLN_AUTO) ? SwapType.FROM_BTCLN_AUTO : SwapType.FROM_BTCLN;
|
|
2462
|
+
if(this.getSupportedTokenAddresses(token.chainId, fromLightningSwapType).has(token.address)) {
|
|
2463
|
+
result.push(BitcoinTokens.BTCLN);
|
|
2464
|
+
}
|
|
2465
|
+
const fromOnchainSwapType = this.supportsSwapType(token.chainId, SwapType.SPV_VAULT_FROM_BTC) ? SwapType.SPV_VAULT_FROM_BTC : SwapType.FROM_BTC;
|
|
2466
|
+
if(this.getSupportedTokenAddresses(token.chainId, fromOnchainSwapType).has(token.address)) {
|
|
2467
|
+
result.push(BitcoinTokens.BTC);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
return result;
|
|
2471
|
+
} else {
|
|
2472
|
+
if(input) {
|
|
2473
|
+
if(token.lightning) {
|
|
2474
|
+
return this.getSupportedTokensForSwapType(SwapType.FROM_BTCLN);
|
|
2475
|
+
} else {
|
|
2476
|
+
return this.getSupportedTokensForSwapType(SwapType.FROM_BTC);
|
|
2477
|
+
}
|
|
2478
|
+
} else {
|
|
2479
|
+
if(token.lightning) {
|
|
2480
|
+
return this.getSupportedTokensForSwapType(SwapType.TO_BTCLN);
|
|
2481
|
+
} else {
|
|
2482
|
+
return this.getSupportedTokensForSwapType(SwapType.TO_BTC);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
}
|