Skip to content

Commit 8c52fba

Browse files
committed
chore(api)!: use FeeRate instead of f64 for fee estimates
This commit: - Changes `get_fee_estimates` to return `HashMap<u16, FeeRate>` instead of `HashMap<u16, f64>`. - Changes `convert_fee_rate` to accept `HashMap<u16, FeeRate>` and return `Option<FeeRate>` instead of `Option<f32>`. - Adds `sat_per_vbyte_to_feerate` to `api.rs` to convert the raw `f64` sat/vbyte values returned by the Esplora API into `FeeRate`. - Updates the `feerate_parsing` test accordingly.
1 parent 7ed720e commit 8c52fba

File tree

4 files changed

+70
-26
lines changed

4 files changed

+70
-26
lines changed

src/api.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,14 @@ pub struct MempoolFeesSubmitPackage {
470470
pub effective_includes: Option<Vec<Wtxid>>,
471471
}
472472

473+
/// Converts a [`HashMap`] of fee estimates in sat/vbyte (`f64`) to [`FeeRate`].
474+
pub(crate) fn sat_per_vbyte_to_feerate(estimates: HashMap<u16, f64>) -> HashMap<u16, FeeRate> {
475+
estimates
476+
.into_iter()
477+
.map(|(k, v)| (k, FeeRate::from_sat_per_kwu((v * 250_000.0) as u64)))
478+
.collect()
479+
}
480+
473481
/// Deserializes a witness from a list of hex-encoded strings.
474482
///
475483
/// The Esplora API represents witness data as an array of hex strings,

src/async.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ use bitcoin::consensus::encode::serialize_hex;
3737
use bitcoin::consensus::{deserialize, serialize, Decodable};
3838
use bitcoin::hashes::{sha256, Hash};
3939
use bitcoin::hex::{DisplayHex, FromHex};
40-
use bitcoin::{Address, Block, BlockHash, MerkleBlock, Script, Transaction, Txid};
40+
use bitcoin::{Address, Block, BlockHash, FeeRate, MerkleBlock, Script, Transaction, Txid};
4141

4242
use reqwest::{header, Body, Client, Response};
4343

4444
use crate::{
45-
AddressStats, BlockInfo, BlockStatus, Builder, Error, EsploraTx, MempoolRecentTx, MempoolStats,
46-
MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, TxStatus, Utxo,
47-
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
45+
sat_per_vbyte_to_feerate, AddressStats, BlockInfo, BlockStatus, Builder, Error, EsploraTx,
46+
MempoolRecentTx, MempoolStats, MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult,
47+
TxStatus, Utxo, BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
4848
};
4949

5050
/// Returns `true` if the given HTTP status code should trigger a retry.
@@ -629,9 +629,12 @@ impl<S: Sleeper> AsyncClient<S> {
629629
/// Get fee estimates for a range of confirmation targets.
630630
///
631631
/// Returns a [`HashMap`] where the key is the confirmation target in blocks
632-
/// and the value is the estimated fee rate in sat/vB.
633-
pub async fn get_fee_estimates(&self) -> Result<HashMap<u16, f64>, Error> {
634-
self.get_response_json("/fee-estimates").await
632+
/// and the value is the estimated [`FeeRate`].
633+
pub async fn get_fee_estimates(&self) -> Result<HashMap<u16, FeeRate>, Error> {
634+
let estimates_raw: HashMap<u16, f64> = self.get_response_json("/fee-estimates").await?;
635+
let estimates = sat_per_vbyte_to_feerate(estimates_raw);
636+
637+
Ok(estimates)
635638
}
636639

637640
// ----> ADDRESS

src/blocking.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ use bitcoin::consensus::encode::serialize_hex;
3636
use bitcoin::consensus::{deserialize, serialize, Decodable};
3737
use bitcoin::hashes::{sha256, Hash};
3838
use bitcoin::hex::{DisplayHex, FromHex};
39-
use bitcoin::{Address, Block, BlockHash, MerkleBlock, Script, Transaction, Txid};
39+
use bitcoin::{Address, Block, BlockHash, FeeRate, MerkleBlock, Script, Transaction, Txid};
4040

4141
use crate::{
42-
AddressStats, BlockInfo, BlockStatus, Builder, Error, EsploraTx, MempoolRecentTx, MempoolStats,
43-
MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, TxStatus, Utxo,
44-
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
42+
sat_per_vbyte_to_feerate, AddressStats, BlockInfo, BlockStatus, Builder, Error, EsploraTx,
43+
MempoolRecentTx, MempoolStats, MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult,
44+
TxStatus, Utxo, BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
4545
};
4646

4747
/// Returns `true` if the given HTTP status code indicates a successful response.
@@ -620,9 +620,12 @@ impl BlockingClient {
620620
/// Get fee estimates for a range of confirmation targets.
621621
///
622622
/// Returns a [`HashMap`] where the key is the confirmation target in blocks
623-
/// and the value is the estimated fee rate in sat/vB.
624-
pub fn get_fee_estimates(&self) -> Result<HashMap<u16, f64>, Error> {
625-
self.get_response_json("/fee-estimates")
623+
/// and the value is the estimated [`FeeRate`].
624+
pub fn get_fee_estimates(&self) -> Result<HashMap<u16, FeeRate>, Error> {
625+
let estimates_raw: HashMap<u16, f64> = self.get_response_json("/fee-estimates")?;
626+
let estimates = sat_per_vbyte_to_feerate(estimates_raw);
627+
628+
Ok(estimates)
626629
}
627630

628631
// ----> ADDRESS

src/lib.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,39 @@ const BASE_BACKOFF_MILLIS: Duration = Duration::from_millis(256);
103103
/// Default max retries.
104104
const DEFAULT_MAX_RETRIES: usize = 6;
105105

106-
/// Get a fee value in sats/vbytes from the estimates
107-
/// that matches the confirmation target set as parameter.
106+
/// Returns the [`FeeRate`] for the given confirmation target in blocks.
108107
///
109-
/// Returns `None` if no feerate estimate is found at or below `target`
110-
/// confirmations.
111-
pub fn convert_fee_rate(target: usize, estimates: HashMap<u16, f64>) -> Option<f32> {
108+
/// Selects the highest confirmation target from `estimates` that is at or
109+
/// below `target_blocks`, and returns its [`FeeRate`]. Returns `None` if no
110+
/// matching estimate is found.
111+
///
112+
/// # Example
113+
///
114+
/// ```rust
115+
/// use bitcoin::FeeRate;
116+
/// use esplora_client::convert_fee_rate;
117+
/// use std::collections::HashMap;
118+
///
119+
/// let mut estimates = HashMap::new();
120+
/// estimates.insert(1u16, FeeRate::from_sat_per_vb(10).unwrap());
121+
/// estimates.insert(6u16, FeeRate::from_sat_per_vb(5).unwrap());
122+
///
123+
/// assert_eq!(
124+
/// convert_fee_rate(6, estimates.clone()),
125+
/// Some(FeeRate::from_sat_per_vb(5).unwrap())
126+
/// );
127+
/// assert_eq!(
128+
/// convert_fee_rate(1, estimates.clone()),
129+
/// Some(FeeRate::from_sat_per_vb(10).unwrap())
130+
/// );
131+
/// assert_eq!(convert_fee_rate(0, estimates), None);
132+
/// ```
133+
pub fn convert_fee_rate(target_blocks: usize, estimates: HashMap<u16, FeeRate>) -> Option<FeeRate> {
112134
estimates
113135
.into_iter()
114-
.filter(|(k, _)| *k as usize <= target)
136+
.filter(|(k, _)| *k as usize <= target_blocks)
115137
.max_by_key(|(k, _)| *k)
116-
.map(|(_, v)| v as f32)
138+
.map(|(_, feerate)| feerate)
117139
}
118140

119141
/// A builder for an [`AsyncClient`] or [`BlockingClient`].
@@ -473,8 +495,8 @@ mod test {
473495
}
474496

475497
#[test]
476-
fn feerate_parsing() {
477-
let esplora_fees = serde_json::from_str::<HashMap<u16, f64>>(
498+
fn test_feerate_parsing() {
499+
let esplora_fees_raw = serde_json::from_str::<HashMap<u16, f64>>(
478500
r#"{
479501
"25": 1.015,
480502
"5": 2.3280000000000003,
@@ -508,11 +530,19 @@ mod test {
508530
"#,
509531
)
510532
.unwrap();
533+
534+
// Convert fees from sat/vB (`f64`) to `FeeRate`.
535+
// Note that `get_fee_estimates` already returns `HashMap<u16, FeeRate>`.
536+
let esplora_fees = sat_per_vbyte_to_feerate(esplora_fees_raw);
537+
511538
assert!(convert_fee_rate(1, HashMap::new()).is_none());
512-
assert_eq!(convert_fee_rate(6, esplora_fees.clone()).unwrap(), 2.236);
513539
assert_eq!(
514-
convert_fee_rate(26, esplora_fees.clone()).unwrap(),
515-
1.015,
540+
convert_fee_rate(6, esplora_fees.clone()),
541+
Some(FeeRate::from_sat_per_kwu((2.236_f64 * 250_000.0) as u64))
542+
);
543+
assert_eq!(
544+
convert_fee_rate(26, esplora_fees.clone()),
545+
Some(FeeRate::from_sat_per_kwu((1.015_f64 * 250_000.0) as u64)),
516546
"should inherit from value for 25"
517547
);
518548
assert!(

0 commit comments

Comments
 (0)