bitcoin_units/
fee_rate.rs1use core::fmt;
6use core::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::{Arbitrary, Unstructured};
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::amount::Amount;
14use crate::weight::Weight;
15
16#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[cfg_attr(feature = "serde", serde(transparent))]
23pub struct FeeRate(u64);
24
25impl FeeRate {
26    pub const ZERO: FeeRate = FeeRate(0);
30
31    pub const MIN: FeeRate = FeeRate::ZERO;
35
36    pub const MAX: FeeRate = FeeRate(u64::MAX);
38
39    pub const BROADCAST_MIN: FeeRate = FeeRate::from_sat_per_vb_unchecked(1);
43
44    pub const DUST: FeeRate = FeeRate::from_sat_per_vb_unchecked(3);
46
47    pub const fn from_sat_per_kwu(sat_kwu: u64) -> Self { FeeRate(sat_kwu) }
49
50    pub fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
56        Some(FeeRate(sat_vb.checked_mul(1000 / 4)?))
60    }
61
62    pub const fn from_sat_per_vb_unchecked(sat_vb: u64) -> Self { FeeRate(sat_vb * (1000 / 4)) }
64
65    pub const fn to_sat_per_kwu(self) -> u64 { self.0 }
69
70    pub const fn to_sat_per_vb_floor(self) -> u64 { self.0 / (1000 / 4) }
72
73    pub const fn to_sat_per_vb_ceil(self) -> u64 { (self.0 + (1000 / 4 - 1)) / (1000 / 4) }
75
76    pub fn checked_mul(self, rhs: u64) -> Option<Self> { self.0.checked_mul(rhs).map(Self) }
80
81    pub fn checked_div(self, rhs: u64) -> Option<Self> { self.0.checked_div(rhs).map(Self) }
85
86    pub fn checked_mul_by_weight(self, rhs: Weight) -> Option<Amount> {
95        let sats = self.0.checked_mul(rhs.to_wu())?.checked_add(999)? / 1000;
96        Some(Amount::from_sat(sats))
97    }
98
99    pub fn checked_add(self, rhs: u64) -> Option<Self> { self.0.checked_add(rhs).map(Self) }
103
104    pub fn checked_sub(self, rhs: u64) -> Option<Self> { self.0.checked_sub(rhs).map(Self) }
108
109    pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) }
114
115    pub fn fee_vb(self, vb: u64) -> Option<Amount> {
121        Weight::from_vb(vb).and_then(|w| self.fee_wu(w))
122    }
123}
124
125#[cfg(feature = "arbitrary")]
126impl<'a> Arbitrary<'a> for FeeRate {
127    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
128        let f = u64::arbitrary(u)?;
129        Ok(FeeRate(f))
130    }
131}
132
133impl fmt::Display for FeeRate {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        if f.alternate() {
137            write!(f, "{}.00 sat/vbyte", self.to_sat_per_vb_ceil())
138        } else {
139            fmt::Display::fmt(&self.0, f)
140        }
141    }
142}
143
144impl From<FeeRate> for u64 {
145    fn from(value: FeeRate) -> Self { value.to_sat_per_kwu() }
146}
147
148impl Add for FeeRate {
149    type Output = FeeRate;
150
151    fn add(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 + rhs.0) }
152}
153
154impl Add<FeeRate> for &FeeRate {
155    type Output = FeeRate;
156
157    fn add(self, other: FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
158}
159
160impl Add<&FeeRate> for FeeRate {
161    type Output = FeeRate;
162
163    fn add(self, other: &FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
164}
165
166impl<'a, 'b> Add<&'a FeeRate> for &'b FeeRate {
167    type Output = FeeRate;
168
169    fn add(self, other: &'a FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
170}
171
172impl Sub for FeeRate {
173    type Output = FeeRate;
174
175    fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 - rhs.0) }
176}
177
178impl Sub<FeeRate> for &FeeRate {
179    type Output = FeeRate;
180
181    fn sub(self, other: FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
182}
183
184impl Sub<&FeeRate> for FeeRate {
185    type Output = FeeRate;
186
187    fn sub(self, other: &FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
188}
189
190impl<'a, 'b> Sub<&'a FeeRate> for &'b FeeRate {
191    type Output = FeeRate;
192
193    fn sub(self, other: &'a FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
194}
195
196impl Mul<FeeRate> for Weight {
198    type Output = Amount;
199
200    fn mul(self, rhs: FeeRate) -> Self::Output {
201        Amount::from_sat((rhs.to_sat_per_kwu() * self.to_wu() + 999) / 1000)
202    }
203}
204
205impl Mul<Weight> for FeeRate {
206    type Output = Amount;
207
208    fn mul(self, rhs: Weight) -> Self::Output { rhs * self }
209}
210
211impl Div<Weight> for Amount {
212    type Output = FeeRate;
213
214    fn div(self, rhs: Weight) -> Self::Output { FeeRate(self.to_sat() * 1000 / rhs.to_wu()) }
215}
216
217impl AddAssign for FeeRate {
218    fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
219}
220
221impl AddAssign<&FeeRate> for FeeRate {
222    fn add_assign(&mut self, rhs: &FeeRate) { self.0 += rhs.0 }
223}
224
225impl SubAssign for FeeRate {
226    fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
227}
228
229impl SubAssign<&FeeRate> for FeeRate {
230    fn sub_assign(&mut self, rhs: &FeeRate) { self.0 -= rhs.0 }
231}
232
233crate::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu);
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    #[allow(clippy::op_ref)]
241    fn addition() {
242        let one = FeeRate(1);
243        let two = FeeRate(2);
244        let three = FeeRate(3);
245
246        assert!(one + two == three);
247        assert!(&one + two == three);
248        assert!(one + &two == three);
249        assert!(&one + &two == three);
250    }
251
252    #[test]
253    #[allow(clippy::op_ref)]
254    fn subtract() {
255        let one = FeeRate(1);
256        let two = FeeRate(2);
257        let three = FeeRate(3);
258
259        assert!(three - two == one);
260        assert!(&three - two == one);
261        assert!(three - &two == one);
262        assert!(&three - &two == one);
263    }
264
265    #[test]
266    fn add_assign() {
267        let mut f = FeeRate(1);
268        f += FeeRate(2);
269        assert_eq!(f, FeeRate(3));
270
271        let mut f = FeeRate(1);
272        f += &FeeRate(2);
273        assert_eq!(f, FeeRate(3));
274    }
275
276    #[test]
277    fn sub_assign() {
278        let mut f = FeeRate(3);
279        f -= FeeRate(2);
280        assert_eq!(f, FeeRate(1));
281
282        let mut f = FeeRate(3);
283        f -= &FeeRate(2);
284        assert_eq!(f, FeeRate(1));
285    }
286
287    #[test]
288    fn checked_add() {
289        let f = FeeRate(1).checked_add(2).unwrap();
290        assert_eq!(FeeRate(3), f);
291
292        let f = FeeRate(u64::MAX).checked_add(1);
293        assert!(f.is_none());
294    }
295
296    #[test]
297    fn checked_sub() {
298        let f = FeeRate(2).checked_sub(1).unwrap();
299        assert_eq!(FeeRate(1), f);
300
301        let f = FeeRate::ZERO.checked_sub(1);
302        assert!(f.is_none());
303    }
304
305    #[test]
306    fn fee_rate_const_test() {
307        assert_eq!(0, FeeRate::ZERO.to_sat_per_kwu());
308        assert_eq!(u64::MIN, FeeRate::MIN.to_sat_per_kwu());
309        assert_eq!(u64::MAX, FeeRate::MAX.to_sat_per_kwu());
310        assert_eq!(250, FeeRate::BROADCAST_MIN.to_sat_per_kwu());
311        assert_eq!(750, FeeRate::DUST.to_sat_per_kwu());
312    }
313
314    #[test]
315    fn fee_rate_from_sat_per_vb_test() {
316        let fee_rate = FeeRate::from_sat_per_vb(10).expect("expected feerate in sat/kwu");
317        assert_eq!(FeeRate(2500), fee_rate);
318    }
319
320    #[test]
321    fn fee_rate_from_sat_per_vb_overflow_test() {
322        let fee_rate = FeeRate::from_sat_per_vb(u64::MAX);
323        assert!(fee_rate.is_none());
324    }
325
326    #[test]
327    fn from_sat_per_vb_unchecked_test() {
328        let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
329        assert_eq!(FeeRate(2500), fee_rate);
330    }
331
332    #[test]
333    #[cfg(debug_assertions)]
334    #[should_panic]
335    fn from_sat_per_vb_unchecked_panic_test() { FeeRate::from_sat_per_vb_unchecked(u64::MAX); }
336
337    #[test]
338    fn raw_feerate_test() {
339        let fee_rate = FeeRate(333);
340        assert_eq!(333, fee_rate.to_sat_per_kwu());
341        assert_eq!(1, fee_rate.to_sat_per_vb_floor());
342        assert_eq!(2, fee_rate.to_sat_per_vb_ceil());
343    }
344
345    #[test]
346    fn checked_mul_test() {
347        let fee_rate = FeeRate(10).checked_mul(10).expect("expected feerate in sat/kwu");
348        assert_eq!(FeeRate(100), fee_rate);
349
350        let fee_rate = FeeRate(10).checked_mul(u64::MAX);
351        assert!(fee_rate.is_none());
352    }
353
354    #[test]
355    fn checked_weight_mul_test() {
356        let weight = Weight::from_vb(10).unwrap();
357        let fee: Amount = FeeRate::from_sat_per_vb(10)
358            .unwrap()
359            .checked_mul_by_weight(weight)
360            .expect("expected Amount");
361        assert_eq!(Amount::from_sat(100), fee);
362
363        let fee = FeeRate(10).checked_mul_by_weight(Weight::MAX);
364        assert!(fee.is_none());
365
366        let weight = Weight::from_vb(3).unwrap();
367        let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
368        let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
369        assert_eq!(Amount::from_sat(9), fee);
370    }
371
372    #[test]
373    fn checked_div_test() {
374        let fee_rate = FeeRate(10).checked_div(10).expect("expected feerate in sat/kwu");
375        assert_eq!(FeeRate(1), fee_rate);
376
377        let fee_rate = FeeRate(10).checked_div(0);
378        assert!(fee_rate.is_none());
379    }
380}