ord/
fee_rate.rs

1use super::*;
2
3#[derive(Debug, PartialEq, Clone, Copy)]
4pub struct FeeRate(f64);
5
6impl FromStr for FeeRate {
7  type Err = Error;
8
9  fn from_str(s: &str) -> Result<Self, Self::Err> {
10    Self::try_from(f64::from_str(s)?)
11  }
12}
13
14impl TryFrom<f64> for FeeRate {
15  type Error = Error;
16
17  fn try_from(rate: f64) -> Result<Self, Self::Error> {
18    if rate.is_sign_negative() | rate.is_nan() | rate.is_infinite() {
19      bail!("invalid fee rate: {rate}")
20    }
21    Ok(Self(rate))
22  }
23}
24
25impl FeeRate {
26  pub fn fee(&self, vsize: usize) -> Amount {
27    #[allow(clippy::cast_possible_truncation)]
28    #[allow(clippy::cast_sign_loss)]
29    Amount::from_sat((self.0 * vsize as f64).round() as u64)
30  }
31
32  pub(crate) fn n(&self) -> f64 {
33    self.0
34  }
35}
36
37#[cfg(test)]
38mod tests {
39  use super::*;
40
41  #[test]
42  fn parse() {
43    assert_eq!("1.1".parse::<FeeRate>().unwrap().0, 1.1);
44    assert_eq!("11.19".parse::<FeeRate>().unwrap().0, 11.19);
45    assert_eq!("11.1111".parse::<FeeRate>().unwrap().0, 11.1111);
46    assert!("-4.2".parse::<FeeRate>().is_err());
47    assert!(FeeRate::try_from(f64::INFINITY).is_err());
48    assert!(FeeRate::try_from(f64::NAN).is_err());
49  }
50
51  #[test]
52  fn fee() {
53    assert_eq!(
54      "2.5".parse::<FeeRate>().unwrap().fee(100),
55      Amount::from_sat(250)
56    );
57    assert_eq!(
58      "2.0".parse::<FeeRate>().unwrap().fee(1024),
59      Amount::from_sat(2048)
60    );
61    assert_eq!(
62      "1.1".parse::<FeeRate>().unwrap().fee(100),
63      Amount::from_sat(110)
64    );
65    assert_eq!(
66      "1.0".parse::<FeeRate>().unwrap().fee(123456789),
67      Amount::from_sat(123456789)
68    );
69  }
70}