From 6fbaf9f9ceba47a4ece87da4fdcfcbd0394a1b1e Mon Sep 17 00:00:00 2001 From: benthecarman Date: Wed, 28 Apr 2021 16:43:20 -0500 Subject: [PATCH] Add estimate fee cli command (#2983) --- .../scala/org/bitcoins/cli/ConsoleCli.scala | 7 ++++ .../org/bitcoins/server/WalletRoutes.scala | 7 ++++ .../bitcoins/core/wallet/fee/FeeUnit.scala | 41 +++++++++++++++++-- docs/applications/server.md | 1 + 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala index 790dd3f8ce..f73a923852 100644 --- a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala +++ b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala @@ -1333,6 +1333,9 @@ object ConsoleCli { case other => other })) ), + cmd("estimatefee") + .action((_, conf) => conf.copy(command = EstimateFee)) + .text("Returns the recommended fee rate using the fee provider"), checkConfig { case Config(NoCommand, _, _, _) => failure("You need to provide a command!") @@ -1614,6 +1617,7 @@ object ConsoleCli { Seq(up.writeJs(requiredKeys), up.writeJs(keys), up.writeJs(addressType))) + case EstimateFee => RequestParam("estimatefee") case GetVersion => // skip sending to server and just return version number of cli @@ -1928,12 +1932,15 @@ object CliCommand { case class ConvertToPSBT(transaction: Transaction) extends AppServerCliCommand case class AnalyzePSBT(psbt: PSBT) extends AppServerCliCommand + // Util case class CreateMultisig( requiredKeys: Int, keys: Vector[ECPublicKey], addressType: AddressType) extends AppServerCliCommand + case object EstimateFee extends AppServerCliCommand + // Oracle case object GetPublicKey extends OracleServerCliCommand case object GetStakingAddress extends OracleServerCliCommand diff --git a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala index 20f2c5af88..effb72a98c 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -557,6 +557,13 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit Server.httpSuccess(ujson.Null) } } + + case ServerCommand("estimatefee", _) => + complete { + wallet.getFeeRate.map { fee => + Server.httpSuccess(fee.toString) + } + } } /** Returns information about the state of our wallet */ diff --git a/core/src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala b/core/src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala index fd02a223e9..e1048ea4ef 100644 --- a/core/src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala +++ b/core/src/main/scala/org/bitcoins/core/wallet/fee/FeeUnit.scala @@ -2,6 +2,7 @@ package org.bitcoins.core.wallet.fee import org.bitcoins.core.currency._ import org.bitcoins.core.protocol.transaction.Transaction +import org.bitcoins.crypto.StringFactory /** This is meant to be an abstract type that represents different fee unit measurements for * blockchains @@ -32,10 +33,15 @@ sealed abstract class FeeUnit { /** Calculates the fee for the transaction using this fee rate, rounds down satoshis */ final def calc(tx: Transaction): CurrencyUnit = this * txSizeForCalc(tx) def toLong: Long = currencyUnit.satoshis.toLong + + override def toString: String = s"$toLong ${factory.unitString}" } trait FeeUnitFactory[+T <: FeeUnit] { + /** String to identify this fee unit, for example "sats/byte" */ + def unitString: String + /** The coefficient the denominator in the unit is multiplied by, * for example sats/kilobyte -> 1000 */ @@ -78,8 +84,6 @@ case class SatoshisPerByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit { } override def factory: FeeUnitFactory[SatoshisPerByte] = SatoshisPerByte - - override def toString: String = s"$toLong sats/byte" } object SatoshisPerByte extends FeeUnitFactory[SatoshisPerByte] { @@ -99,6 +103,8 @@ object SatoshisPerByte extends FeeUnitFactory[SatoshisPerByte] { val zero: SatoshisPerByte = SatoshisPerByte(Satoshis.zero) val one: SatoshisPerByte = SatoshisPerByte(Satoshis.one) + + override val unitString: String = "sats/byte" } /** KiloBytes here are defined as 1000 bytes. @@ -129,7 +135,6 @@ case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit) override def factory: FeeUnitFactory[SatoshisPerKiloByte] = SatoshisPerKiloByte - override def toString: String = s"$toLong sats/kb" } object SatoshisPerKiloByte extends FeeUnitFactory[SatoshisPerKiloByte] { @@ -149,6 +154,8 @@ object SatoshisPerKiloByte extends FeeUnitFactory[SatoshisPerKiloByte] { val zero: SatoshisPerKiloByte = SatoshisPerKiloByte(Satoshis.zero) val one: SatoshisPerKiloByte = SatoshisPerKiloByte(Satoshis.one) + + override val unitString: String = "sats/kb" } /** A 'virtual byte' (also known as virtual size) is a new weight measurement that @@ -184,6 +191,8 @@ object SatoshisPerVirtualByte extends FeeUnitFactory[SatoshisPerVirtualByte] { val zero: SatoshisPerVirtualByte = SatoshisPerVirtualByte(CurrencyUnits.zero) val one: SatoshisPerVirtualByte = SatoshisPerVirtualByte(Satoshis.one) + + override val unitString: String = "sats/vbyte" } /** Weight is used to indicate how 'expensive' the transaction is on the blockchain. @@ -220,4 +229,30 @@ object SatoshisPerKW extends FeeUnitFactory[SatoshisPerKW] { val zero: SatoshisPerKW = SatoshisPerKW(CurrencyUnits.zero) val one: SatoshisPerKW = SatoshisPerKW(Satoshis.one) + + override val unitString: String = "sats/kw" +} + +object FeeUnit extends StringFactory[FeeUnit] { + + val factories = Vector(SatoshisPerVirtualByte, + SatoshisPerByte, + SatoshisPerKiloByte, + SatoshisPerKW) + + override def fromString(string: String): FeeUnit = { + val arr = string.split(" ") + + val unit = arr.last + val feeUnitOpt = factories.find(_.unitString == unit).map { fac => + val long = arr.head.toLong + fac.fromLong(long) + } + + feeUnitOpt match { + case Some(feeUnit) => feeUnit + case None => + sys.error(s"Could not parse $string as a fee unit") + } + } } diff --git a/docs/applications/server.md b/docs/applications/server.md index 65f0907075..4324be2ba3 100644 --- a/docs/applications/server.md +++ b/docs/applications/server.md @@ -245,6 +245,7 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `nrequired` - The number of required signatures out of the n keys. - `keys` - The hex-encoded public keys. - `address_type` -The address type to use. Options are "legacy", "p2sh-segwit", and "bech32" + - `estimatefee` - Returns the recommended fee rate using the fee provider ## Sign PSBT with Wallet Example