@atomiqlabs/sdk 8.9.0 → 8.9.2
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/api/index.d.ts +1 -1
- package/api/index.js +3 -3
- package/dist/ApiList.d.ts +37 -37
- package/dist/ApiList.js +30 -30
- package/dist/SmartChainAssets.d.ts +181 -181
- package/dist/SmartChainAssets.js +181 -181
- package/dist/api/ApiEndpoints.d.ts +393 -393
- package/dist/api/ApiEndpoints.js +2 -2
- package/dist/api/ApiParser.d.ts +10 -10
- package/dist/api/ApiParser.js +134 -134
- package/dist/api/ApiTypes.d.ts +157 -157
- package/dist/api/ApiTypes.js +75 -75
- package/dist/api/SerializedAction.d.ts +40 -40
- package/dist/api/SerializedAction.js +59 -59
- package/dist/api/SwapperApi.d.ts +50 -50
- package/dist/api/SwapperApi.js +431 -431
- package/dist/api/index.d.ts +5 -5
- package/dist/api/index.js +24 -24
- 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 +24 -24
- package/dist/events/UnifiedSwapEventListener.js +138 -138
- package/dist/http/HttpUtils.d.ts +29 -29
- package/dist/http/HttpUtils.js +97 -97
- 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 +17 -17
- package/dist/http/paramcoders/client/StreamingFetchPromise.js +175 -175
- package/dist/index.d.ts +86 -86
- package/dist/index.js +159 -159
- package/dist/intermediaries/Intermediary.d.ts +178 -178
- package/dist/intermediaries/Intermediary.js +166 -166
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +216 -216
- package/dist/intermediaries/IntermediaryDiscovery.js +424 -424
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +607 -607
- package/dist/intermediaries/apis/IntermediaryAPI.js +764 -764
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
- package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -14
- package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -68
- 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 +127 -127
- package/dist/storage/IUnifiedStorage.js +2 -2
- package/dist/storage/UnifiedSwapStorage.d.ts +120 -120
- package/dist/storage/UnifiedSwapStorage.js +154 -154
- 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 +765 -765
- package/dist/swapper/Swapper.js +1749 -1749
- package/dist/swapper/SwapperFactory.d.ts +135 -135
- package/dist/swapper/SwapperFactory.js +162 -162
- package/dist/swapper/SwapperUtils.d.ts +222 -222
- package/dist/swapper/SwapperUtils.js +519 -519
- 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 +453 -453
- package/dist/swaps/ISwap.js +371 -371
- package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
- package/dist/swaps/ISwapWithGasDrop.js +12 -12
- package/dist/swaps/ISwapWrapper.d.ts +295 -295
- package/dist/swaps/ISwapWrapper.js +373 -373
- 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 +172 -172
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +129 -129
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +167 -167
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +107 -107
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +130 -130
- 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 +547 -547
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1419 -1419
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +192 -192
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +432 -432
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +650 -650
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1577 -1577
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +237 -237
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +525 -525
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +491 -491
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1463 -1463
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +204 -204
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +406 -406
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +446 -446
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +1097 -1097
- 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 +252 -252
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +535 -535
- 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 +134 -134
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +286 -286
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +694 -694
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1687 -1687
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +259 -259
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +947 -947
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +302 -302
- package/dist/swaps/trusted/ln/LnForGasSwap.js +625 -625
- package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +82 -82
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +343 -343
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +698 -698
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +71 -71
- 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 +195 -195
- package/dist/types/SwapExecutionAction.js +106 -106
- package/dist/types/SwapExecutionStep.d.ts +144 -144
- package/dist/types/SwapExecutionStep.js +87 -87
- 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 +75 -75
- package/dist/types/TokenAmount.js +85 -85
- 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 +18 -18
- package/dist/utils/BitcoinUtils.js +174 -174
- 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 +69 -69
- package/dist/utils/Utils.js +214 -214
- package/package.json +46 -46
- package/src/SmartChainAssets.ts +186 -186
- package/src/api/ApiEndpoints.ts +427 -427
- package/src/api/ApiParser.ts +138 -138
- package/src/api/ApiTypes.ts +229 -229
- package/src/api/SerializedAction.ts +97 -97
- package/src/api/SwapperApi.ts +545 -545
- package/src/api/index.ts +5 -5
- 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 +181 -181
- package/src/http/HttpUtils.ts +97 -97
- 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 +194 -194
- package/src/index.ts +141 -141
- package/src/intermediaries/Intermediary.ts +280 -280
- package/src/intermediaries/IntermediaryDiscovery.ts +548 -548
- package/src/intermediaries/apis/IntermediaryAPI.ts +1247 -1247
- package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -69
- 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 +136 -136
- package/src/storage/UnifiedSwapStorage.ts +175 -175
- package/src/storage-browser/IndexedDBUnifiedStorage.ts +350 -350
- package/src/storage-browser/LocalStorageManager.ts +106 -106
- package/src/swapper/Swapper.ts +2557 -2557
- package/src/swapper/SwapperFactory.ts +307 -307
- package/src/swapper/SwapperUtils.ts +610 -610
- 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 +775 -775
- package/src/swaps/ISwapWithGasDrop.ts +25 -25
- package/src/swaps/ISwapWrapper.ts +564 -564
- package/src/swaps/escrow_swaps/IEscrowSelfInitSwap.ts +217 -217
- package/src/swaps/escrow_swaps/IEscrowSwap.ts +271 -271
- package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +284 -284
- package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +172 -172
- 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 +1670 -1671
- package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +603 -603
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +1883 -1883
- package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +752 -752
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +1753 -1753
- package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +612 -612
- package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +1327 -1327
- 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 +787 -787
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.ts +206 -206
- package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +403 -403
- package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +2148 -2148
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +1238 -1238
- package/src/swaps/trusted/ln/LnForGasSwap.ts +753 -753
- package/src/swaps/trusted/ln/LnForGasWrapper.ts +90 -90
- package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +843 -843
- package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +133 -133
- 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 +323 -323
- package/src/types/SwapExecutionStep.ts +224 -224
- 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 +167 -167
- 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 +164 -164
- 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 +221 -221
|
@@ -1,440 +1,440 @@
|
|
|
1
|
-
import {coinSelect, maxSendable, CoinselectAddressTypes, CoinselectTxInput} from "../coinselect2";
|
|
2
|
-
import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils"
|
|
3
|
-
import {p2wpkh, OutScript, Transaction, p2tr, Address} from "@scure/btc-signer";
|
|
4
|
-
import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "./IBitcoinWallet";
|
|
5
|
-
import {Buffer} from "buffer";
|
|
6
|
-
import {randomBytes} from "../../utils/Utils";
|
|
7
|
-
import {getDummyOutputScript, toCoinselectAddressType, toOutputScript} from "../../utils/BitcoinUtils";
|
|
8
|
-
import {TransactionInputUpdate} from "@scure/btc-signer/psbt";
|
|
9
|
-
import {getLogger} from "../../utils/Logger";
|
|
10
|
-
import {BitcoinNetwork, BitcoinRpcWithAddressIndex} from "@atomiqlabs/base";
|
|
11
|
-
import {utils} from "../coinselect2/utils";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Identifies the address type of a Bitcoin address
|
|
15
|
-
*
|
|
16
|
-
* @category Bitcoin
|
|
17
|
-
*/
|
|
18
|
-
export function identifyAddressType(address: string, network: BTC_NETWORK): CoinselectAddressTypes {
|
|
19
|
-
switch(Address(network).decode(address).type) {
|
|
20
|
-
case "pkh":
|
|
21
|
-
return "p2pkh";
|
|
22
|
-
case "wpkh":
|
|
23
|
-
return "p2wpkh";
|
|
24
|
-
case "tr":
|
|
25
|
-
return "p2tr";
|
|
26
|
-
case "sh":
|
|
27
|
-
return "p2sh-p2wpkh";
|
|
28
|
-
case "wsh":
|
|
29
|
-
return "p2wsh";
|
|
30
|
-
default:
|
|
31
|
-
throw new Error("Unknown address type of "+address);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const btcNetworkMapping = {
|
|
36
|
-
[BitcoinNetwork.MAINNET]: NETWORK,
|
|
37
|
-
[BitcoinNetwork.TESTNET]: TEST_NETWORK,
|
|
38
|
-
[BitcoinNetwork.TESTNET4]: TEST_NETWORK,
|
|
39
|
-
[BitcoinNetwork.REGTEST]: {
|
|
40
|
-
...TEST_NETWORK,
|
|
41
|
-
bech32: "bcrt"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const logger = getLogger("BitcoinWallet: ");
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Abstract base class for Bitcoin wallet implementations, using bitcoin rpc with address index
|
|
49
|
-
* as a backend for fetching balances, UTXOs, etc.
|
|
50
|
-
*
|
|
51
|
-
* @category Bitcoin
|
|
52
|
-
*/
|
|
53
|
-
export abstract class BitcoinWallet implements IBitcoinWallet {
|
|
54
|
-
|
|
55
|
-
protected readonly rpc: BitcoinRpcWithAddressIndex<any>;
|
|
56
|
-
protected readonly network: BTC_NETWORK;
|
|
57
|
-
protected feeMultiplier: number;
|
|
58
|
-
protected feeOverride?: number;
|
|
59
|
-
|
|
60
|
-
constructor(
|
|
61
|
-
mempoolApi: BitcoinRpcWithAddressIndex<any>,
|
|
62
|
-
network: BitcoinNetwork | BTC_NETWORK,
|
|
63
|
-
feeMultiplier: number = 1.25, feeOverride?: number
|
|
64
|
-
) {
|
|
65
|
-
this.rpc = mempoolApi;
|
|
66
|
-
this.network = typeof(network)==="object" ? network : BitcoinWallet.bitcoinNetworkToObject(network);
|
|
67
|
-
this.feeMultiplier = feeMultiplier;
|
|
68
|
-
this.feeOverride = feeOverride;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @inheritDoc
|
|
73
|
-
*/
|
|
74
|
-
async getFeeRate(): Promise<number> {
|
|
75
|
-
if(this.feeOverride!=null) {
|
|
76
|
-
return this.feeOverride;
|
|
77
|
-
}
|
|
78
|
-
return Math.floor((await this.rpc.getFeeRate())*this.feeMultiplier);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Internal helper function for sending a raw transaction through the underlying RPC
|
|
83
|
-
*
|
|
84
|
-
* @param rawHex Serialized bitcoin transaction in hexadecimal format
|
|
85
|
-
* @returns txId Transaction ID of the submitted bitcoin transaction
|
|
86
|
-
*
|
|
87
|
-
* @protected
|
|
88
|
-
*/
|
|
89
|
-
protected _sendTransaction(rawHex: string): Promise<string> {
|
|
90
|
-
return this.rpc.sendRawTransaction(rawHex);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Internal helper function for fetching the balance of the wallet given a specific bitcoin wallet address
|
|
95
|
-
*
|
|
96
|
-
* @param address
|
|
97
|
-
* @protected
|
|
98
|
-
*/
|
|
99
|
-
protected _getBalance(address: string): Promise<{ confirmedBalance: bigint; unconfirmedBalance: bigint }> {
|
|
100
|
-
return this.rpc.getAddressBalances(address);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Internal helper function for fetching the UTXO set of a given wallet address
|
|
105
|
-
*
|
|
106
|
-
* @param sendingAddress
|
|
107
|
-
* @param sendingAddressType
|
|
108
|
-
* @protected
|
|
109
|
-
*/
|
|
110
|
-
protected async _getUtxoPool(
|
|
111
|
-
sendingAddress: string,
|
|
112
|
-
sendingAddressType: CoinselectAddressTypes
|
|
113
|
-
): Promise<BitcoinWalletUtxo[]> {
|
|
114
|
-
const utxos = await this.rpc.getAddressUTXOs(sendingAddress);
|
|
115
|
-
|
|
116
|
-
let totalSpendable = 0;
|
|
117
|
-
|
|
118
|
-
const outputScript = toOutputScript(this.network, sendingAddress);
|
|
119
|
-
|
|
120
|
-
const utxoPool: BitcoinWalletUtxo[] = [];
|
|
121
|
-
|
|
122
|
-
for(let utxo of utxos) {
|
|
123
|
-
const value = Number(utxo.value);
|
|
124
|
-
totalSpendable += value;
|
|
125
|
-
utxoPool.push({
|
|
126
|
-
vout: utxo.vout,
|
|
127
|
-
txId: utxo.txid,
|
|
128
|
-
value: value,
|
|
129
|
-
type: sendingAddressType,
|
|
130
|
-
outputScript: outputScript,
|
|
131
|
-
address: sendingAddress,
|
|
132
|
-
cpfp: !utxo.confirmed ? await this.rpc.getCPFPData(utxo.txid).then((result) => {
|
|
133
|
-
if(result==null) return;
|
|
134
|
-
return {
|
|
135
|
-
txVsize: result.adjustedVsize,
|
|
136
|
-
txEffectiveFeeRate: result.effectiveFeePerVsize
|
|
137
|
-
}
|
|
138
|
-
}) : undefined,
|
|
139
|
-
confirmed: utxo.confirmed
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
logger.debug("_getUtxoPool(): Total spendable value: "+totalSpendable+" num utxos: "+utxoPool.length);
|
|
144
|
-
|
|
145
|
-
return utxoPool;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
*
|
|
150
|
-
* @param sendingAccounts
|
|
151
|
-
* @param recipient
|
|
152
|
-
* @param amount
|
|
153
|
-
* @param feeRate
|
|
154
|
-
* @protected
|
|
155
|
-
*/
|
|
156
|
-
protected async _getPsbt(
|
|
157
|
-
sendingAccounts: {
|
|
158
|
-
pubkey: string,
|
|
159
|
-
address: string,
|
|
160
|
-
addressType: CoinselectAddressTypes,
|
|
161
|
-
}[],
|
|
162
|
-
recipient: string,
|
|
163
|
-
amount: number,
|
|
164
|
-
feeRate?: number
|
|
165
|
-
): Promise<{
|
|
166
|
-
fee: number,
|
|
167
|
-
psbt?: Transaction,
|
|
168
|
-
inputAddressIndexes?: {[address: string]: number[]}
|
|
169
|
-
}> {
|
|
170
|
-
const psbt = new Transaction({PSBTVersion: 0});
|
|
171
|
-
psbt.addOutput({
|
|
172
|
-
amount: BigInt(amount),
|
|
173
|
-
script: toOutputScript(this.network, recipient)
|
|
174
|
-
});
|
|
175
|
-
return this._fundPsbt(sendingAccounts, psbt, feeRate);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
protected async _fundPsbt(
|
|
179
|
-
sendingAccounts: {
|
|
180
|
-
pubkey: string,
|
|
181
|
-
address: string,
|
|
182
|
-
addressType: CoinselectAddressTypes,
|
|
183
|
-
}[],
|
|
184
|
-
psbt: Transaction,
|
|
185
|
-
_feeRate?: number,
|
|
186
|
-
utxos?: BitcoinWalletUtxo[],
|
|
187
|
-
spendFully?: boolean
|
|
188
|
-
): Promise<{
|
|
189
|
-
fee: number,
|
|
190
|
-
psbt?: Transaction,
|
|
191
|
-
inputAddressIndexes?: {[address: string]: number[]}
|
|
192
|
-
}> {
|
|
193
|
-
const feeRate = _feeRate ?? await this.getFeeRate();
|
|
194
|
-
const utxoPool: BitcoinWalletUtxo[] = utxos ?? (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat();
|
|
195
|
-
|
|
196
|
-
if(spendFully && utxoPool==null) throw new Error("Cannot fully spend when no utxos are passed!");
|
|
197
|
-
|
|
198
|
-
logger.debug("_fundPsbt(): fee rate: "+feeRate+" utxo pool: ", utxoPool);
|
|
199
|
-
|
|
200
|
-
const accountPubkeys: Record<string, string> = {};
|
|
201
|
-
sendingAccounts.forEach(acc => accountPubkeys[acc.address] = acc.pubkey);
|
|
202
|
-
|
|
203
|
-
const requiredInputs: CoinselectTxInput[] = [];
|
|
204
|
-
for(let i=0;i<psbt.inputsLength;i++) {
|
|
205
|
-
const input = psbt.getInput(i);
|
|
206
|
-
if(input.index==null || input.txid==null) throw new Error("Inputs need txid & index!");
|
|
207
|
-
let amount: bigint;
|
|
208
|
-
let script: Uint8Array;
|
|
209
|
-
if(input.witnessUtxo!=null) {
|
|
210
|
-
amount = input.witnessUtxo.amount as bigint;
|
|
211
|
-
script = input.witnessUtxo.script as Uint8Array;
|
|
212
|
-
} else if(input.nonWitnessUtxo!=null) {
|
|
213
|
-
amount = input.nonWitnessUtxo.outputs[input.index].amount;
|
|
214
|
-
script = input.nonWitnessUtxo.outputs[input.index].script;
|
|
215
|
-
} else throw new Error("Either witnessUtxo or nonWitnessUtxo has to be defined!");
|
|
216
|
-
requiredInputs.push({
|
|
217
|
-
txId: Buffer.from(input.txid).toString('hex'),
|
|
218
|
-
vout: input.index,
|
|
219
|
-
value: Number(amount),
|
|
220
|
-
type: toCoinselectAddressType(script)
|
|
221
|
-
})
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const targets: {value: number, script: Buffer}[] = [];
|
|
225
|
-
for(let i=0;i<psbt.outputsLength;i++) {
|
|
226
|
-
const output = psbt.getOutput(i);
|
|
227
|
-
if(output.amount==null || output.script==null) throw new Error("Outputs need amount & script defined!");
|
|
228
|
-
targets.push({
|
|
229
|
-
value: Number(output.amount),
|
|
230
|
-
script: Buffer.from(output.script)
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
logger.debug("_fundPsbt(): Coinselect targets: ", targets);
|
|
234
|
-
|
|
235
|
-
let coinselectResult = spendFully
|
|
236
|
-
? utils.finalize(requiredInputs.concat(utxoPool.filter(utxo => !utils.isDetrimentalInput(feeRate, utxo))), targets, feeRate, null)
|
|
237
|
-
: coinSelect(utxoPool, targets, feeRate, sendingAccounts[0].addressType, requiredInputs);
|
|
238
|
-
logger.debug("_fundPsbt(): Coinselect result: ", coinselectResult);
|
|
239
|
-
|
|
240
|
-
if(coinselectResult.inputs==null || coinselectResult.outputs==null || coinselectResult.effectiveFeeRate==null) {
|
|
241
|
-
return {
|
|
242
|
-
fee: coinselectResult.fee
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if(spendFully && feeRate!=null) {
|
|
247
|
-
const maximumAllowedFeeRate = (1.5*feeRate) + 10;
|
|
248
|
-
if(coinselectResult.effectiveFeeRate > maximumAllowedFeeRate)
|
|
249
|
-
throw new Error(`Effective fee rate too high, feeRate: ${coinselectResult.effectiveFeeRate} sats/vB, maximum: ${maximumAllowedFeeRate} sats/vB!`);
|
|
250
|
-
const minimumAllowedFeeRate = 0.9*feeRate;
|
|
251
|
-
if(coinselectResult.effectiveFeeRate < minimumAllowedFeeRate)
|
|
252
|
-
throw new Error(`Effective fee rate too low, feeRate: ${coinselectResult.effectiveFeeRate} sats/vB, minimum: ${minimumAllowedFeeRate} sats/vB!`);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Remove in/outs that are already in the PSBT
|
|
256
|
-
coinselectResult.inputs.splice(0, psbt.inputsLength);
|
|
257
|
-
coinselectResult.outputs.splice(0, psbt.outputsLength);
|
|
258
|
-
|
|
259
|
-
const inputAddressIndexes: {[address: string]: number[]} = {};
|
|
260
|
-
coinselectResult.inputs.forEach((input, index) => {
|
|
261
|
-
inputAddressIndexes[input.address!] ??= [];
|
|
262
|
-
inputAddressIndexes[input.address!].push(index);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const formattedInputs: TransactionInputUpdate[] = await Promise.all<TransactionInputUpdate>(coinselectResult.inputs.map(async (input) => {
|
|
266
|
-
switch(input.type) {
|
|
267
|
-
case "p2tr":
|
|
268
|
-
const parsed = p2tr(Buffer.from(accountPubkeys[input.address!], "hex"));
|
|
269
|
-
return {
|
|
270
|
-
txid: input.txId,
|
|
271
|
-
index: input.vout,
|
|
272
|
-
witnessUtxo: {
|
|
273
|
-
script: input.outputScript!,
|
|
274
|
-
amount: BigInt(input.value)
|
|
275
|
-
},
|
|
276
|
-
tapInternalKey: parsed.tapInternalKey,
|
|
277
|
-
tapMerkleRoot: parsed.tapMerkleRoot,
|
|
278
|
-
tapLeafScript: parsed.tapLeafScript
|
|
279
|
-
};
|
|
280
|
-
case "p2wpkh":
|
|
281
|
-
return {
|
|
282
|
-
txid: input.txId,
|
|
283
|
-
index: input.vout,
|
|
284
|
-
witnessUtxo: {
|
|
285
|
-
script: input.outputScript!,
|
|
286
|
-
amount: BigInt(input.value)
|
|
287
|
-
},
|
|
288
|
-
sighashType: 0x01
|
|
289
|
-
};
|
|
290
|
-
case "p2sh-p2wpkh":
|
|
291
|
-
return {
|
|
292
|
-
txid: input.txId,
|
|
293
|
-
index: input.vout,
|
|
294
|
-
witnessUtxo: {
|
|
295
|
-
script: input.outputScript!,
|
|
296
|
-
amount: BigInt(input.value)
|
|
297
|
-
},
|
|
298
|
-
redeemScript: p2wpkh(Buffer.from(accountPubkeys[input.address!], "hex"), this.network).script,
|
|
299
|
-
sighashType: 0x01
|
|
300
|
-
};
|
|
301
|
-
case "p2pkh":
|
|
302
|
-
const tx = await this.rpc.getTransaction(input.txId);
|
|
303
|
-
if(tx==null) throw new Error("Cannot fetch existing tx "+input.txId);
|
|
304
|
-
return {
|
|
305
|
-
txid: input.txId,
|
|
306
|
-
index: input.vout,
|
|
307
|
-
nonWitnessUtxo: tx.raw,
|
|
308
|
-
sighashType: 0x01
|
|
309
|
-
};
|
|
310
|
-
default:
|
|
311
|
-
throw new Error("Invalid input type: "+input.type);
|
|
312
|
-
}
|
|
313
|
-
}));
|
|
314
|
-
|
|
315
|
-
formattedInputs.forEach(input => psbt.addInput(input));
|
|
316
|
-
|
|
317
|
-
coinselectResult.outputs.forEach(output => {
|
|
318
|
-
if(output.script==null && output.address==null) {
|
|
319
|
-
//Change output
|
|
320
|
-
psbt.addOutput({
|
|
321
|
-
script: toOutputScript(this.network, sendingAccounts[0].address),
|
|
322
|
-
amount: BigInt(Math.floor(output.value))
|
|
323
|
-
});
|
|
324
|
-
} else {
|
|
325
|
-
psbt.addOutput({
|
|
326
|
-
script: output.script ?? toOutputScript(this.network, output.address!),
|
|
327
|
-
amount: BigInt(output.value)
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
psbt,
|
|
334
|
-
fee: coinselectResult.fee,
|
|
335
|
-
inputAddressIndexes
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
protected async _getSpendableBalance(
|
|
340
|
-
sendingAccounts: {
|
|
341
|
-
address: string,
|
|
342
|
-
addressType: CoinselectAddressTypes,
|
|
343
|
-
}[],
|
|
344
|
-
psbt?: Transaction,
|
|
345
|
-
feeRate?: number,
|
|
346
|
-
outputAddressType?: CoinselectAddressTypes,
|
|
347
|
-
utxoPool?: BitcoinWalletUtxoBase[]
|
|
348
|
-
): Promise<{
|
|
349
|
-
balance: bigint,
|
|
350
|
-
feeRate: number,
|
|
351
|
-
totalFee: number
|
|
352
|
-
}> {
|
|
353
|
-
feeRate ??= await this.getFeeRate();
|
|
354
|
-
utxoPool ??= (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat();
|
|
355
|
-
|
|
356
|
-
return {
|
|
357
|
-
...BitcoinWallet.getSpendableBalance(
|
|
358
|
-
utxoPool ?? (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat(),
|
|
359
|
-
feeRate ?? await this.getFeeRate(),
|
|
360
|
-
psbt,
|
|
361
|
-
outputAddressType
|
|
362
|
-
),
|
|
363
|
-
feeRate
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
abstract sendTransaction(address: string, amount: bigint, feeRate?: number): Promise<string>;
|
|
368
|
-
abstract fundPsbt(psbt: Transaction, feeRate?: number, utxos?: BitcoinWalletUtxo[], spendFully?: boolean): Promise<Transaction>;
|
|
369
|
-
abstract signPsbt(psbt: Transaction, signInputs: number[]): Promise<Transaction>;
|
|
370
|
-
|
|
371
|
-
abstract getTransactionFee(address: string, amount: bigint, feeRate?: number): Promise<number>;
|
|
372
|
-
abstract getFundedPsbtFee(psbt: Transaction, feeRate?: number): Promise<number>;
|
|
373
|
-
|
|
374
|
-
abstract getReceiveAddress(): string;
|
|
375
|
-
abstract getBalance(): Promise<{
|
|
376
|
-
confirmedBalance: bigint,
|
|
377
|
-
unconfirmedBalance: bigint
|
|
378
|
-
}>;
|
|
379
|
-
abstract getSpendableBalance(psbt?: Transaction, feeRate?: number): Promise<{
|
|
380
|
-
balance: bigint,
|
|
381
|
-
feeRate: number,
|
|
382
|
-
totalFee: number
|
|
383
|
-
}>;
|
|
384
|
-
|
|
385
|
-
static bitcoinNetworkToObject(network: BitcoinNetwork): BTC_NETWORK {
|
|
386
|
-
return btcNetworkMapping[network];
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
static getSpendableBalance(
|
|
390
|
-
utxoPool: BitcoinWalletUtxoBase[],
|
|
391
|
-
feeRate: number,
|
|
392
|
-
psbt?: Transaction,
|
|
393
|
-
outputAddressType?: CoinselectAddressTypes
|
|
394
|
-
): {
|
|
395
|
-
balance: bigint,
|
|
396
|
-
totalFee: number
|
|
397
|
-
} {
|
|
398
|
-
const requiredInputs: CoinselectTxInput[] = [];
|
|
399
|
-
if(psbt!=null) for(let i=0;i<psbt.inputsLength;i++) {
|
|
400
|
-
const input = psbt.getInput(i);
|
|
401
|
-
if(input.index==null || input.txid==null) throw new Error("Inputs need txid & index!");
|
|
402
|
-
let amount: bigint;
|
|
403
|
-
let script: Uint8Array;
|
|
404
|
-
if(input.witnessUtxo!=null) {
|
|
405
|
-
amount = input.witnessUtxo.amount as bigint;
|
|
406
|
-
script = input.witnessUtxo.script as Uint8Array;
|
|
407
|
-
} else if(input.nonWitnessUtxo!=null) {
|
|
408
|
-
amount = input.nonWitnessUtxo.outputs[input.index].amount;
|
|
409
|
-
script = input.nonWitnessUtxo.outputs[input.index].script;
|
|
410
|
-
} else throw new Error("Either witnessUtxo or nonWitnessUtxo has to be defined!");
|
|
411
|
-
requiredInputs.push({
|
|
412
|
-
txId: Buffer.from(input.txid).toString('hex'),
|
|
413
|
-
vout: input.index,
|
|
414
|
-
value: Number(amount),
|
|
415
|
-
type: toCoinselectAddressType(script)
|
|
416
|
-
})
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const additionalOutputs: {value: number, script: Buffer}[] = [];
|
|
420
|
-
if(psbt!=null) for(let i=0;i<psbt.outputsLength;i++) {
|
|
421
|
-
const output = psbt.getOutput(i);
|
|
422
|
-
if(output.amount==null || output.script==null) throw new Error("Outputs need amount & script!");
|
|
423
|
-
additionalOutputs.push({
|
|
424
|
-
value: Number(output.amount),
|
|
425
|
-
script: Buffer.from(output.script)
|
|
426
|
-
})
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const target: Uint8Array = getDummyOutputScript(outputAddressType ?? "p2wsh");
|
|
430
|
-
let coinselectResult = maxSendable(utxoPool, {script: Buffer.from(target), type: outputAddressType ?? "p2wsh"}, feeRate, requiredInputs, additionalOutputs);
|
|
431
|
-
|
|
432
|
-
logger.debug("_getSpendableBalance(): Max spendable result: ", coinselectResult);
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
balance: BigInt(Math.floor(coinselectResult.value)),
|
|
436
|
-
totalFee: coinselectResult.fee
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
1
|
+
import {coinSelect, maxSendable, CoinselectAddressTypes, CoinselectTxInput} from "../coinselect2";
|
|
2
|
+
import {BTC_NETWORK, NETWORK, TEST_NETWORK} from "@scure/btc-signer/utils"
|
|
3
|
+
import {p2wpkh, OutScript, Transaction, p2tr, Address} from "@scure/btc-signer";
|
|
4
|
+
import {BitcoinWalletUtxo, BitcoinWalletUtxoBase, IBitcoinWallet} from "./IBitcoinWallet";
|
|
5
|
+
import {Buffer} from "buffer";
|
|
6
|
+
import {randomBytes} from "../../utils/Utils";
|
|
7
|
+
import {getDummyOutputScript, toCoinselectAddressType, toOutputScript} from "../../utils/BitcoinUtils";
|
|
8
|
+
import {TransactionInputUpdate} from "@scure/btc-signer/psbt";
|
|
9
|
+
import {getLogger} from "../../utils/Logger";
|
|
10
|
+
import {BitcoinNetwork, BitcoinRpcWithAddressIndex} from "@atomiqlabs/base";
|
|
11
|
+
import {utils} from "../coinselect2/utils";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Identifies the address type of a Bitcoin address
|
|
15
|
+
*
|
|
16
|
+
* @category Bitcoin
|
|
17
|
+
*/
|
|
18
|
+
export function identifyAddressType(address: string, network: BTC_NETWORK): CoinselectAddressTypes {
|
|
19
|
+
switch(Address(network).decode(address).type) {
|
|
20
|
+
case "pkh":
|
|
21
|
+
return "p2pkh";
|
|
22
|
+
case "wpkh":
|
|
23
|
+
return "p2wpkh";
|
|
24
|
+
case "tr":
|
|
25
|
+
return "p2tr";
|
|
26
|
+
case "sh":
|
|
27
|
+
return "p2sh-p2wpkh";
|
|
28
|
+
case "wsh":
|
|
29
|
+
return "p2wsh";
|
|
30
|
+
default:
|
|
31
|
+
throw new Error("Unknown address type of "+address);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const btcNetworkMapping = {
|
|
36
|
+
[BitcoinNetwork.MAINNET]: NETWORK,
|
|
37
|
+
[BitcoinNetwork.TESTNET]: TEST_NETWORK,
|
|
38
|
+
[BitcoinNetwork.TESTNET4]: TEST_NETWORK,
|
|
39
|
+
[BitcoinNetwork.REGTEST]: {
|
|
40
|
+
...TEST_NETWORK,
|
|
41
|
+
bech32: "bcrt"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const logger = getLogger("BitcoinWallet: ");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Abstract base class for Bitcoin wallet implementations, using bitcoin rpc with address index
|
|
49
|
+
* as a backend for fetching balances, UTXOs, etc.
|
|
50
|
+
*
|
|
51
|
+
* @category Bitcoin
|
|
52
|
+
*/
|
|
53
|
+
export abstract class BitcoinWallet implements IBitcoinWallet {
|
|
54
|
+
|
|
55
|
+
protected readonly rpc: BitcoinRpcWithAddressIndex<any>;
|
|
56
|
+
protected readonly network: BTC_NETWORK;
|
|
57
|
+
protected feeMultiplier: number;
|
|
58
|
+
protected feeOverride?: number;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
mempoolApi: BitcoinRpcWithAddressIndex<any>,
|
|
62
|
+
network: BitcoinNetwork | BTC_NETWORK,
|
|
63
|
+
feeMultiplier: number = 1.25, feeOverride?: number
|
|
64
|
+
) {
|
|
65
|
+
this.rpc = mempoolApi;
|
|
66
|
+
this.network = typeof(network)==="object" ? network : BitcoinWallet.bitcoinNetworkToObject(network);
|
|
67
|
+
this.feeMultiplier = feeMultiplier;
|
|
68
|
+
this.feeOverride = feeOverride;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @inheritDoc
|
|
73
|
+
*/
|
|
74
|
+
async getFeeRate(): Promise<number> {
|
|
75
|
+
if(this.feeOverride!=null) {
|
|
76
|
+
return this.feeOverride;
|
|
77
|
+
}
|
|
78
|
+
return Math.floor((await this.rpc.getFeeRate())*this.feeMultiplier);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Internal helper function for sending a raw transaction through the underlying RPC
|
|
83
|
+
*
|
|
84
|
+
* @param rawHex Serialized bitcoin transaction in hexadecimal format
|
|
85
|
+
* @returns txId Transaction ID of the submitted bitcoin transaction
|
|
86
|
+
*
|
|
87
|
+
* @protected
|
|
88
|
+
*/
|
|
89
|
+
protected _sendTransaction(rawHex: string): Promise<string> {
|
|
90
|
+
return this.rpc.sendRawTransaction(rawHex);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Internal helper function for fetching the balance of the wallet given a specific bitcoin wallet address
|
|
95
|
+
*
|
|
96
|
+
* @param address
|
|
97
|
+
* @protected
|
|
98
|
+
*/
|
|
99
|
+
protected _getBalance(address: string): Promise<{ confirmedBalance: bigint; unconfirmedBalance: bigint }> {
|
|
100
|
+
return this.rpc.getAddressBalances(address);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Internal helper function for fetching the UTXO set of a given wallet address
|
|
105
|
+
*
|
|
106
|
+
* @param sendingAddress
|
|
107
|
+
* @param sendingAddressType
|
|
108
|
+
* @protected
|
|
109
|
+
*/
|
|
110
|
+
protected async _getUtxoPool(
|
|
111
|
+
sendingAddress: string,
|
|
112
|
+
sendingAddressType: CoinselectAddressTypes
|
|
113
|
+
): Promise<BitcoinWalletUtxo[]> {
|
|
114
|
+
const utxos = await this.rpc.getAddressUTXOs(sendingAddress);
|
|
115
|
+
|
|
116
|
+
let totalSpendable = 0;
|
|
117
|
+
|
|
118
|
+
const outputScript = toOutputScript(this.network, sendingAddress);
|
|
119
|
+
|
|
120
|
+
const utxoPool: BitcoinWalletUtxo[] = [];
|
|
121
|
+
|
|
122
|
+
for(let utxo of utxos) {
|
|
123
|
+
const value = Number(utxo.value);
|
|
124
|
+
totalSpendable += value;
|
|
125
|
+
utxoPool.push({
|
|
126
|
+
vout: utxo.vout,
|
|
127
|
+
txId: utxo.txid,
|
|
128
|
+
value: value,
|
|
129
|
+
type: sendingAddressType,
|
|
130
|
+
outputScript: outputScript,
|
|
131
|
+
address: sendingAddress,
|
|
132
|
+
cpfp: !utxo.confirmed ? await this.rpc.getCPFPData(utxo.txid).then((result) => {
|
|
133
|
+
if(result==null) return;
|
|
134
|
+
return {
|
|
135
|
+
txVsize: result.adjustedVsize,
|
|
136
|
+
txEffectiveFeeRate: result.effectiveFeePerVsize
|
|
137
|
+
}
|
|
138
|
+
}) : undefined,
|
|
139
|
+
confirmed: utxo.confirmed
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
logger.debug("_getUtxoPool(): Total spendable value: "+totalSpendable+" num utxos: "+utxoPool.length);
|
|
144
|
+
|
|
145
|
+
return utxoPool;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
*
|
|
150
|
+
* @param sendingAccounts
|
|
151
|
+
* @param recipient
|
|
152
|
+
* @param amount
|
|
153
|
+
* @param feeRate
|
|
154
|
+
* @protected
|
|
155
|
+
*/
|
|
156
|
+
protected async _getPsbt(
|
|
157
|
+
sendingAccounts: {
|
|
158
|
+
pubkey: string,
|
|
159
|
+
address: string,
|
|
160
|
+
addressType: CoinselectAddressTypes,
|
|
161
|
+
}[],
|
|
162
|
+
recipient: string,
|
|
163
|
+
amount: number,
|
|
164
|
+
feeRate?: number
|
|
165
|
+
): Promise<{
|
|
166
|
+
fee: number,
|
|
167
|
+
psbt?: Transaction,
|
|
168
|
+
inputAddressIndexes?: {[address: string]: number[]}
|
|
169
|
+
}> {
|
|
170
|
+
const psbt = new Transaction({PSBTVersion: 0});
|
|
171
|
+
psbt.addOutput({
|
|
172
|
+
amount: BigInt(amount),
|
|
173
|
+
script: toOutputScript(this.network, recipient)
|
|
174
|
+
});
|
|
175
|
+
return this._fundPsbt(sendingAccounts, psbt, feeRate);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
protected async _fundPsbt(
|
|
179
|
+
sendingAccounts: {
|
|
180
|
+
pubkey: string,
|
|
181
|
+
address: string,
|
|
182
|
+
addressType: CoinselectAddressTypes,
|
|
183
|
+
}[],
|
|
184
|
+
psbt: Transaction,
|
|
185
|
+
_feeRate?: number,
|
|
186
|
+
utxos?: BitcoinWalletUtxo[],
|
|
187
|
+
spendFully?: boolean
|
|
188
|
+
): Promise<{
|
|
189
|
+
fee: number,
|
|
190
|
+
psbt?: Transaction,
|
|
191
|
+
inputAddressIndexes?: {[address: string]: number[]}
|
|
192
|
+
}> {
|
|
193
|
+
const feeRate = _feeRate ?? await this.getFeeRate();
|
|
194
|
+
const utxoPool: BitcoinWalletUtxo[] = utxos ?? (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat();
|
|
195
|
+
|
|
196
|
+
if(spendFully && utxoPool==null) throw new Error("Cannot fully spend when no utxos are passed!");
|
|
197
|
+
|
|
198
|
+
logger.debug("_fundPsbt(): fee rate: "+feeRate+" utxo pool: ", utxoPool);
|
|
199
|
+
|
|
200
|
+
const accountPubkeys: Record<string, string> = {};
|
|
201
|
+
sendingAccounts.forEach(acc => accountPubkeys[acc.address] = acc.pubkey);
|
|
202
|
+
|
|
203
|
+
const requiredInputs: CoinselectTxInput[] = [];
|
|
204
|
+
for(let i=0;i<psbt.inputsLength;i++) {
|
|
205
|
+
const input = psbt.getInput(i);
|
|
206
|
+
if(input.index==null || input.txid==null) throw new Error("Inputs need txid & index!");
|
|
207
|
+
let amount: bigint;
|
|
208
|
+
let script: Uint8Array;
|
|
209
|
+
if(input.witnessUtxo!=null) {
|
|
210
|
+
amount = input.witnessUtxo.amount as bigint;
|
|
211
|
+
script = input.witnessUtxo.script as Uint8Array;
|
|
212
|
+
} else if(input.nonWitnessUtxo!=null) {
|
|
213
|
+
amount = input.nonWitnessUtxo.outputs[input.index].amount;
|
|
214
|
+
script = input.nonWitnessUtxo.outputs[input.index].script;
|
|
215
|
+
} else throw new Error("Either witnessUtxo or nonWitnessUtxo has to be defined!");
|
|
216
|
+
requiredInputs.push({
|
|
217
|
+
txId: Buffer.from(input.txid).toString('hex'),
|
|
218
|
+
vout: input.index,
|
|
219
|
+
value: Number(amount),
|
|
220
|
+
type: toCoinselectAddressType(script)
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const targets: {value: number, script: Buffer}[] = [];
|
|
225
|
+
for(let i=0;i<psbt.outputsLength;i++) {
|
|
226
|
+
const output = psbt.getOutput(i);
|
|
227
|
+
if(output.amount==null || output.script==null) throw new Error("Outputs need amount & script defined!");
|
|
228
|
+
targets.push({
|
|
229
|
+
value: Number(output.amount),
|
|
230
|
+
script: Buffer.from(output.script)
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
logger.debug("_fundPsbt(): Coinselect targets: ", targets);
|
|
234
|
+
|
|
235
|
+
let coinselectResult = spendFully
|
|
236
|
+
? utils.finalize(requiredInputs.concat(utxoPool.filter(utxo => !utils.isDetrimentalInput(feeRate, utxo))), targets, feeRate, null)
|
|
237
|
+
: coinSelect(utxoPool, targets, feeRate, sendingAccounts[0].addressType, requiredInputs);
|
|
238
|
+
logger.debug("_fundPsbt(): Coinselect result: ", coinselectResult);
|
|
239
|
+
|
|
240
|
+
if(coinselectResult.inputs==null || coinselectResult.outputs==null || coinselectResult.effectiveFeeRate==null) {
|
|
241
|
+
return {
|
|
242
|
+
fee: coinselectResult.fee
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if(spendFully && feeRate!=null) {
|
|
247
|
+
const maximumAllowedFeeRate = (1.5*feeRate) + 10;
|
|
248
|
+
if(coinselectResult.effectiveFeeRate > maximumAllowedFeeRate)
|
|
249
|
+
throw new Error(`Effective fee rate too high, feeRate: ${coinselectResult.effectiveFeeRate} sats/vB, maximum: ${maximumAllowedFeeRate} sats/vB!`);
|
|
250
|
+
const minimumAllowedFeeRate = 0.9*feeRate;
|
|
251
|
+
if(coinselectResult.effectiveFeeRate < minimumAllowedFeeRate)
|
|
252
|
+
throw new Error(`Effective fee rate too low, feeRate: ${coinselectResult.effectiveFeeRate} sats/vB, minimum: ${minimumAllowedFeeRate} sats/vB!`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Remove in/outs that are already in the PSBT
|
|
256
|
+
coinselectResult.inputs.splice(0, psbt.inputsLength);
|
|
257
|
+
coinselectResult.outputs.splice(0, psbt.outputsLength);
|
|
258
|
+
|
|
259
|
+
const inputAddressIndexes: {[address: string]: number[]} = {};
|
|
260
|
+
coinselectResult.inputs.forEach((input, index) => {
|
|
261
|
+
inputAddressIndexes[input.address!] ??= [];
|
|
262
|
+
inputAddressIndexes[input.address!].push(index);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const formattedInputs: TransactionInputUpdate[] = await Promise.all<TransactionInputUpdate>(coinselectResult.inputs.map(async (input) => {
|
|
266
|
+
switch(input.type) {
|
|
267
|
+
case "p2tr":
|
|
268
|
+
const parsed = p2tr(Buffer.from(accountPubkeys[input.address!], "hex"));
|
|
269
|
+
return {
|
|
270
|
+
txid: input.txId,
|
|
271
|
+
index: input.vout,
|
|
272
|
+
witnessUtxo: {
|
|
273
|
+
script: input.outputScript!,
|
|
274
|
+
amount: BigInt(input.value)
|
|
275
|
+
},
|
|
276
|
+
tapInternalKey: parsed.tapInternalKey,
|
|
277
|
+
tapMerkleRoot: parsed.tapMerkleRoot,
|
|
278
|
+
tapLeafScript: parsed.tapLeafScript
|
|
279
|
+
};
|
|
280
|
+
case "p2wpkh":
|
|
281
|
+
return {
|
|
282
|
+
txid: input.txId,
|
|
283
|
+
index: input.vout,
|
|
284
|
+
witnessUtxo: {
|
|
285
|
+
script: input.outputScript!,
|
|
286
|
+
amount: BigInt(input.value)
|
|
287
|
+
},
|
|
288
|
+
sighashType: 0x01
|
|
289
|
+
};
|
|
290
|
+
case "p2sh-p2wpkh":
|
|
291
|
+
return {
|
|
292
|
+
txid: input.txId,
|
|
293
|
+
index: input.vout,
|
|
294
|
+
witnessUtxo: {
|
|
295
|
+
script: input.outputScript!,
|
|
296
|
+
amount: BigInt(input.value)
|
|
297
|
+
},
|
|
298
|
+
redeemScript: p2wpkh(Buffer.from(accountPubkeys[input.address!], "hex"), this.network).script,
|
|
299
|
+
sighashType: 0x01
|
|
300
|
+
};
|
|
301
|
+
case "p2pkh":
|
|
302
|
+
const tx = await this.rpc.getTransaction(input.txId);
|
|
303
|
+
if(tx==null) throw new Error("Cannot fetch existing tx "+input.txId);
|
|
304
|
+
return {
|
|
305
|
+
txid: input.txId,
|
|
306
|
+
index: input.vout,
|
|
307
|
+
nonWitnessUtxo: tx.raw,
|
|
308
|
+
sighashType: 0x01
|
|
309
|
+
};
|
|
310
|
+
default:
|
|
311
|
+
throw new Error("Invalid input type: "+input.type);
|
|
312
|
+
}
|
|
313
|
+
}));
|
|
314
|
+
|
|
315
|
+
formattedInputs.forEach(input => psbt.addInput(input));
|
|
316
|
+
|
|
317
|
+
coinselectResult.outputs.forEach(output => {
|
|
318
|
+
if(output.script==null && output.address==null) {
|
|
319
|
+
//Change output
|
|
320
|
+
psbt.addOutput({
|
|
321
|
+
script: toOutputScript(this.network, sendingAccounts[0].address),
|
|
322
|
+
amount: BigInt(Math.floor(output.value))
|
|
323
|
+
});
|
|
324
|
+
} else {
|
|
325
|
+
psbt.addOutput({
|
|
326
|
+
script: output.script ?? toOutputScript(this.network, output.address!),
|
|
327
|
+
amount: BigInt(output.value)
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
psbt,
|
|
334
|
+
fee: coinselectResult.fee,
|
|
335
|
+
inputAddressIndexes
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
protected async _getSpendableBalance(
|
|
340
|
+
sendingAccounts: {
|
|
341
|
+
address: string,
|
|
342
|
+
addressType: CoinselectAddressTypes,
|
|
343
|
+
}[],
|
|
344
|
+
psbt?: Transaction,
|
|
345
|
+
feeRate?: number,
|
|
346
|
+
outputAddressType?: CoinselectAddressTypes,
|
|
347
|
+
utxoPool?: BitcoinWalletUtxoBase[]
|
|
348
|
+
): Promise<{
|
|
349
|
+
balance: bigint,
|
|
350
|
+
feeRate: number,
|
|
351
|
+
totalFee: number
|
|
352
|
+
}> {
|
|
353
|
+
feeRate ??= await this.getFeeRate();
|
|
354
|
+
utxoPool ??= (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat();
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
...BitcoinWallet.getSpendableBalance(
|
|
358
|
+
utxoPool ?? (await Promise.all(sendingAccounts.map(acc => this._getUtxoPool(acc.address, acc.addressType)))).flat(),
|
|
359
|
+
feeRate ?? await this.getFeeRate(),
|
|
360
|
+
psbt,
|
|
361
|
+
outputAddressType
|
|
362
|
+
),
|
|
363
|
+
feeRate
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
abstract sendTransaction(address: string, amount: bigint, feeRate?: number): Promise<string>;
|
|
368
|
+
abstract fundPsbt(psbt: Transaction, feeRate?: number, utxos?: BitcoinWalletUtxo[], spendFully?: boolean): Promise<Transaction>;
|
|
369
|
+
abstract signPsbt(psbt: Transaction, signInputs: number[]): Promise<Transaction>;
|
|
370
|
+
|
|
371
|
+
abstract getTransactionFee(address: string, amount: bigint, feeRate?: number): Promise<number>;
|
|
372
|
+
abstract getFundedPsbtFee(psbt: Transaction, feeRate?: number): Promise<number>;
|
|
373
|
+
|
|
374
|
+
abstract getReceiveAddress(): string;
|
|
375
|
+
abstract getBalance(): Promise<{
|
|
376
|
+
confirmedBalance: bigint,
|
|
377
|
+
unconfirmedBalance: bigint
|
|
378
|
+
}>;
|
|
379
|
+
abstract getSpendableBalance(psbt?: Transaction, feeRate?: number): Promise<{
|
|
380
|
+
balance: bigint,
|
|
381
|
+
feeRate: number,
|
|
382
|
+
totalFee: number
|
|
383
|
+
}>;
|
|
384
|
+
|
|
385
|
+
static bitcoinNetworkToObject(network: BitcoinNetwork): BTC_NETWORK {
|
|
386
|
+
return btcNetworkMapping[network];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
static getSpendableBalance(
|
|
390
|
+
utxoPool: BitcoinWalletUtxoBase[],
|
|
391
|
+
feeRate: number,
|
|
392
|
+
psbt?: Transaction,
|
|
393
|
+
outputAddressType?: CoinselectAddressTypes
|
|
394
|
+
): {
|
|
395
|
+
balance: bigint,
|
|
396
|
+
totalFee: number
|
|
397
|
+
} {
|
|
398
|
+
const requiredInputs: CoinselectTxInput[] = [];
|
|
399
|
+
if(psbt!=null) for(let i=0;i<psbt.inputsLength;i++) {
|
|
400
|
+
const input = psbt.getInput(i);
|
|
401
|
+
if(input.index==null || input.txid==null) throw new Error("Inputs need txid & index!");
|
|
402
|
+
let amount: bigint;
|
|
403
|
+
let script: Uint8Array;
|
|
404
|
+
if(input.witnessUtxo!=null) {
|
|
405
|
+
amount = input.witnessUtxo.amount as bigint;
|
|
406
|
+
script = input.witnessUtxo.script as Uint8Array;
|
|
407
|
+
} else if(input.nonWitnessUtxo!=null) {
|
|
408
|
+
amount = input.nonWitnessUtxo.outputs[input.index].amount;
|
|
409
|
+
script = input.nonWitnessUtxo.outputs[input.index].script;
|
|
410
|
+
} else throw new Error("Either witnessUtxo or nonWitnessUtxo has to be defined!");
|
|
411
|
+
requiredInputs.push({
|
|
412
|
+
txId: Buffer.from(input.txid).toString('hex'),
|
|
413
|
+
vout: input.index,
|
|
414
|
+
value: Number(amount),
|
|
415
|
+
type: toCoinselectAddressType(script)
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const additionalOutputs: {value: number, script: Buffer}[] = [];
|
|
420
|
+
if(psbt!=null) for(let i=0;i<psbt.outputsLength;i++) {
|
|
421
|
+
const output = psbt.getOutput(i);
|
|
422
|
+
if(output.amount==null || output.script==null) throw new Error("Outputs need amount & script!");
|
|
423
|
+
additionalOutputs.push({
|
|
424
|
+
value: Number(output.amount),
|
|
425
|
+
script: Buffer.from(output.script)
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const target: Uint8Array = getDummyOutputScript(outputAddressType ?? "p2wsh");
|
|
430
|
+
let coinselectResult = maxSendable(utxoPool, {script: Buffer.from(target), type: outputAddressType ?? "p2wsh"}, feeRate, requiredInputs, additionalOutputs);
|
|
431
|
+
|
|
432
|
+
logger.debug("_getSpendableBalance(): Max spendable result: ", coinselectResult);
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
balance: BigInt(Math.floor(coinselectResult.value)),
|
|
436
|
+
totalFee: coinselectResult.fee
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
440
|
}
|