Skip to main content

bitcoin_units/fee_rate/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Implements `FeeRate` and associated features.
4
5#[cfg(feature = "serde")]
6pub mod serde;
7
8use core::num::NonZeroU64;
9use core::ops;
10
11#[cfg(feature = "arbitrary")]
12use arbitrary::{Arbitrary, Unstructured};
13use NumOpResult as R;
14
15use crate::result::{MathOp, NumOpError as E, NumOpResult};
16use crate::{Amount, Weight};
17
18mod encapsulate {
19    /// Fee rate.
20    ///
21    /// This is an integer newtype representing fee rate. It provides protection
22    /// against mixing up the types, conversion functions, and basic formatting.
23    ///
24    /// NOTE: `FeeRate` explicitly does not have any format/display trait implementations, as it
25    /// doesn't have a standard unit for measure. Users are expected to format it on their own by
26    /// extracting values in desired units with `from_sat_per*` functions.
27    #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
28    pub struct FeeRate(u64);
29
30    impl FeeRate {
31        /// Constructs a new [`FeeRate`] from satoshis per 1,000,000 virtual bytes.
32        pub(crate) const fn from_sat_per_mvb(sat_mvb: u64) -> Self { Self(sat_mvb) }
33
34        /// Converts to sat/MvB.
35        pub(crate) const fn to_sat_per_mvb(self) -> u64 { self.0 }
36    }
37}
38#[doc(inline)]
39pub use encapsulate::FeeRate;
40use internals::const_casts;
41
42impl FeeRate {
43    /// The zero fee rate.
44    ///
45    /// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
46    pub const ZERO: Self = Self::from_sat_per_mvb(0);
47
48    /// The minimum possible value.
49    ///
50    /// Equivalent to [`ZERO`](Self::ZERO), may better express intent in some contexts.
51    pub const MIN: Self = Self::ZERO;
52
53    /// The maximum possible value.
54    pub const MAX: Self = Self::from_sat_per_mvb(u64::MAX);
55
56    /// The minimum fee rate required to broadcast a transaction.
57    ///
58    /// The value matches the default Bitcoin Core policy at the time of library release.
59    pub const BROADCAST_MIN: Self = Self::from_sat_per_vb(1);
60
61    /// The fee rate used to compute dust amount.
62    pub const DUST: Self = Self::from_sat_per_vb(3);
63
64    /// Constructs a new [`FeeRate`] from satoshis per 1000 weight units.
65    pub const fn from_sat_per_kwu(sat_kwu: u32) -> Self {
66        let fee_rate = (const_casts::u32_to_u64(sat_kwu)) * 4_000;
67        Self::from_sat_per_mvb(fee_rate)
68    }
69
70    /// Constructs a new [`FeeRate`] from amount per 1000 weight units.
71    pub const fn from_per_kwu(rate: Amount) -> NumOpResult<Self> {
72        // No `map()` in const context.
73        match rate.checked_mul(4_000) {
74            Some(per_mvb) => R::Valid(Self::from_sat_per_mvb(per_mvb.to_sat())),
75            None => R::Error(E::while_doing(MathOp::Mul)),
76        }
77    }
78
79    /// Constructs a new [`FeeRate`] from satoshis per virtual byte.
80    pub const fn from_sat_per_vb(sat_vb: u32) -> Self {
81        let fee_rate = (const_casts::u32_to_u64(sat_vb)) * 1_000_000;
82        Self::from_sat_per_mvb(fee_rate)
83    }
84
85    /// Constructs a new [`FeeRate`] from amount per virtual byte.
86    pub const fn from_per_vb(rate: Amount) -> NumOpResult<Self> {
87        // No `map()` in const context.
88        match rate.checked_mul(1_000_000) {
89            Some(per_mvb) => R::Valid(Self::from_sat_per_mvb(per_mvb.to_sat())),
90            None => R::Error(E::while_doing(MathOp::Mul)),
91        }
92    }
93
94    /// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
95    pub const fn from_sat_per_kvb(sat_kvb: u32) -> Self {
96        let fee_rate = (const_casts::u32_to_u64(sat_kvb)) * 1_000;
97        Self::from_sat_per_mvb(fee_rate)
98    }
99
100    /// Constructs a new [`FeeRate`] from satoshis per kilo virtual bytes (1,000 vbytes).
101    pub const fn from_per_kvb(rate: Amount) -> NumOpResult<Self> {
102        // No `map()` in const context.
103        match rate.checked_mul(1_000) {
104            Some(per_mvb) => R::Valid(Self::from_sat_per_mvb(per_mvb.to_sat())),
105            None => R::Error(E::while_doing(MathOp::Mul)),
106        }
107    }
108
109    /// Converts to sat/kwu rounding down.
110    pub const fn to_sat_per_kwu_floor(self) -> u64 { self.to_sat_per_mvb() / 4_000 }
111
112    /// Converts to sat/kwu rounding up.
113    pub const fn to_sat_per_kwu_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(4_000) }
114
115    /// Converts to sat/vB rounding down.
116    pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000_000 }
117
118    /// Converts to sat/vB rounding up.
119    pub const fn to_sat_per_vb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000_000) }
120
121    /// Converts to sat/kvb rounding down.
122    pub const fn to_sat_per_kvb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000 }
123
124    /// Converts to sat/kvb rounding up.
125    pub const fn to_sat_per_kvb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000) }
126
127    /// Checked multiplication.
128    ///
129    /// Computes `self * rhs`, returning [`None`] if overflow occurred.
130    #[must_use]
131    pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
132        // No `map()` in const context.
133        match self.to_sat_per_mvb().checked_mul(rhs) {
134            Some(res) => Some(Self::from_sat_per_mvb(res)),
135            None => None,
136        }
137    }
138
139    /// Checked division.
140    ///
141    /// Computes `self / rhs` returning [`None`] if `rhs == 0`.
142    #[must_use]
143    pub const fn checked_div(self, rhs: u64) -> Option<Self> {
144        // No `map()` in const context.
145        match self.to_sat_per_mvb().checked_div(rhs) {
146            Some(res) => Some(Self::from_sat_per_mvb(res)),
147            None => None,
148        }
149    }
150
151    /// Checked addition.
152    ///
153    /// Computes `self + rhs` returning [`None`] in case of overflow.
154    #[must_use]
155    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
156        // No `map()` in const context.
157        match self.to_sat_per_mvb().checked_add(rhs.to_sat_per_mvb()) {
158            Some(res) => Some(Self::from_sat_per_mvb(res)),
159            None => None,
160        }
161    }
162
163    /// Checked subtraction.
164    ///
165    /// Computes `self - rhs`, returning [`None`] if overflow occurred.
166    #[must_use]
167    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
168        // No `map()` in const context.
169        match self.to_sat_per_mvb().checked_sub(rhs.to_sat_per_mvb()) {
170            Some(res) => Some(Self::from_sat_per_mvb(res)),
171            None => None,
172        }
173    }
174
175    /// Calculates the fee by multiplying this fee rate by weight.
176    ///
177    /// Computes the absolute fee amount for a given [`Weight`] at this fee rate. When the resulting
178    /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is
179    /// enough instead of falling short if rounded down.
180    ///
181    /// If the calculation would overflow we saturate to [`Amount::MAX`]. Since such a fee can never
182    /// be paid this is meaningful as an error case while still removing the possibility of silently
183    /// wrapping.
184    pub const fn to_fee(self, weight: Weight) -> Amount {
185        // No `unwrap_or()` in const context.
186        match self.mul_by_weight(weight) {
187            NumOpResult::Valid(fee) => fee,
188            NumOpResult::Error(_) => Amount::MAX,
189        }
190    }
191
192    /// Calculates the fee by multiplying this fee rate by weight, in weight units, returning [`None`]
193    /// if an overflow occurred.
194    ///
195    /// This is equivalent to `Self::mul_by_weight(weight).ok()`.
196    #[must_use]
197    #[deprecated(since = "1.0.0-rc.0", note = "use `to_fee()` instead")]
198    pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.mul_by_weight(weight).ok() }
199
200    /// Calculates the fee by multiplying this fee rate by weight, in virtual bytes, returning [`None`]
201    /// if `vb` cannot be represented as [`Weight`].
202    ///
203    /// This is equivalent to converting `vb` to [`Weight`] using [`Weight::from_vb`] and then calling
204    /// [`Self::to_fee`].
205    #[must_use]
206    #[deprecated(since = "1.0.0-rc.0", note = "use Weight::from_vb and then `to_fee()` instead")]
207    pub fn fee_vb(self, vb: u64) -> Option<Amount> { Weight::from_vb(vb).map(|w| self.to_fee(w)) }
208
209    /// Checked weight multiplication.
210    ///
211    /// Computes the absolute fee amount for a given [`Weight`] at this fee rate. When the resulting
212    /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is
213    /// enough instead of falling short if rounded down.
214    pub const fn mul_by_weight(self, weight: Weight) -> NumOpResult<Amount> {
215        let wu = weight.to_wu();
216        if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) {
217            let fee = fee_kwu.div_ceil(1_000);
218            if let Ok(fee_amount) = Amount::from_sat(fee) {
219                return NumOpResult::Valid(fee_amount);
220            }
221        }
222        NumOpResult::Error(E::while_doing(MathOp::Mul))
223    }
224}
225
226crate::internal_macros::impl_op_for_references! {
227    impl ops::Add<FeeRate> for FeeRate {
228        type Output = FeeRate;
229
230        fn add(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() + rhs.to_sat_per_mvb()) }
231    }
232
233    impl ops::Sub<FeeRate> for FeeRate {
234        type Output = FeeRate;
235
236        fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() - rhs.to_sat_per_mvb()) }
237    }
238
239    impl ops::Div<NonZeroU64> for FeeRate {
240        type Output = FeeRate;
241
242        fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_sat_per_mvb(self.to_sat_per_mvb() / rhs.get()) }
243    }
244}
245crate::internal_macros::impl_add_assign!(FeeRate);
246crate::internal_macros::impl_sub_assign!(FeeRate);
247
248impl core::iter::Sum for FeeRate {
249    fn sum<I>(iter: I) -> Self
250    where
251        I: Iterator<Item = Self>,
252    {
253        Self::from_sat_per_mvb(iter.map(Self::to_sat_per_mvb).sum())
254    }
255}
256
257impl<'a> core::iter::Sum<&'a Self> for FeeRate {
258    fn sum<I>(iter: I) -> Self
259    where
260        I: Iterator<Item = &'a Self>,
261    {
262        Self::from_sat_per_mvb(iter.map(|f| Self::to_sat_per_mvb(*f)).sum())
263    }
264}
265
266#[cfg(feature = "arbitrary")]
267impl<'a> Arbitrary<'a> for FeeRate {
268    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
269        let choice = u.int_in_range(0..=4)?;
270        match choice {
271            0 => Ok(Self::MIN),
272            1 => Ok(Self::BROADCAST_MIN),
273            2 => Ok(Self::DUST),
274            3 => Ok(Self::MAX),
275            _ => Ok(Self::from_sat_per_mvb(u64::arbitrary(u)?)),
276        }
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use core::num::NonZeroU64;
283
284    use super::*;
285
286    #[test]
287    #[allow(clippy::op_ref)]
288    fn feerate_div_nonzero() {
289        let rate = FeeRate::from_sat_per_kwu(200);
290        let divisor = NonZeroU64::new(2).unwrap();
291        assert_eq!(rate / divisor, FeeRate::from_sat_per_kwu(100));
292        assert_eq!(&rate / &divisor, FeeRate::from_sat_per_kwu(100));
293    }
294
295    #[test]
296    #[allow(clippy::op_ref)]
297    fn addition() {
298        let one = FeeRate::from_sat_per_kwu(1);
299        let two = FeeRate::from_sat_per_kwu(2);
300        let three = FeeRate::from_sat_per_kwu(3);
301
302        assert!(one + two == three);
303        assert!(&one + two == three);
304        assert!(one + &two == three);
305        assert!(&one + &two == three);
306    }
307
308    #[test]
309    #[allow(clippy::op_ref)]
310    fn subtract() {
311        let three = FeeRate::from_sat_per_kwu(3);
312        let seven = FeeRate::from_sat_per_kwu(7);
313        let ten = FeeRate::from_sat_per_kwu(10);
314
315        assert_eq!(ten - seven, three);
316        assert_eq!(&ten - seven, three);
317        assert_eq!(ten - &seven, three);
318        assert_eq!(&ten - &seven, three);
319    }
320
321    #[test]
322    fn add_assign() {
323        let mut f = FeeRate::from_sat_per_kwu(1);
324        f += FeeRate::from_sat_per_kwu(2);
325        assert_eq!(f, FeeRate::from_sat_per_kwu(3));
326
327        let mut f = FeeRate::from_sat_per_kwu(1);
328        f += &FeeRate::from_sat_per_kwu(2);
329        assert_eq!(f, FeeRate::from_sat_per_kwu(3));
330
331        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(1));
332        f += FeeRate::from_sat_per_kwu(2);
333        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(3)));
334
335        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(1));
336        f += NumOpResult::Valid(FeeRate::from_sat_per_kwu(2));
337        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(3)));
338    }
339
340    #[test]
341    fn sub_assign() {
342        let mut f = FeeRate::from_sat_per_kwu(3);
343        f -= FeeRate::from_sat_per_kwu(2);
344        assert_eq!(f, FeeRate::from_sat_per_kwu(1));
345
346        let mut f = FeeRate::from_sat_per_kwu(3);
347        f -= &FeeRate::from_sat_per_kwu(2);
348        assert_eq!(f, FeeRate::from_sat_per_kwu(1));
349
350        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(3));
351        f -= FeeRate::from_sat_per_kwu(2);
352        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(1)));
353
354        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(3));
355        f -= NumOpResult::Valid(FeeRate::from_sat_per_kwu(2));
356        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(1)));
357    }
358
359    #[test]
360    fn checked_add() {
361        let one = FeeRate::from_sat_per_kwu(1);
362        let two = FeeRate::from_sat_per_kwu(2);
363        let three = FeeRate::from_sat_per_kwu(3);
364
365        assert_eq!(one.checked_add(two).unwrap(), three);
366
367        // Sanity check - no overflow adding one to per kvb max.
368        let _ = FeeRate::from_sat_per_kvb(u32::MAX).checked_add(one).unwrap();
369        let fee_rate = FeeRate::from_sat_per_mvb(u64::MAX).checked_add(one);
370        assert!(fee_rate.is_none());
371    }
372
373    #[test]
374    fn checked_sub() {
375        let one = FeeRate::from_sat_per_kwu(1);
376        let two = FeeRate::from_sat_per_kwu(2);
377        let three = FeeRate::from_sat_per_kwu(3);
378        assert_eq!(three.checked_sub(two).unwrap(), one);
379
380        let fee_rate = FeeRate::ZERO.checked_sub(one);
381        assert!(fee_rate.is_none());
382    }
383
384    #[test]
385    fn fee_rate_const() {
386        assert_eq!(FeeRate::ZERO.to_sat_per_kwu_floor(), 0);
387        assert_eq!(FeeRate::MIN.to_sat_per_kwu_floor(), u64::MIN);
388        assert_eq!(FeeRate::MAX.to_sat_per_kwu_floor(), u64::MAX / 4_000);
389        assert_eq!(FeeRate::BROADCAST_MIN.to_sat_per_kwu_floor(), 250);
390        assert_eq!(FeeRate::DUST.to_sat_per_kwu_floor(), 750);
391    }
392
393    #[test]
394    fn fee_rate_from_sat_per_vb() {
395        let fee_rate = FeeRate::from_sat_per_vb(10);
396        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500));
397    }
398
399    #[test]
400    fn fee_rate_from_sat_per_kvb() {
401        let fee_rate = FeeRate::from_sat_per_kvb(11);
402        assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
403    }
404
405    #[test]
406    fn fee_rate_to_sat_per_x() {
407        let fee_rate = FeeRate::from_sat_per_mvb(2_000_400);
408
409        // sat/kwu: 2_000_400 / 4_000 = 500.1
410        assert_eq!(fee_rate.to_sat_per_kwu_floor(), 500);
411        assert_eq!(fee_rate.to_sat_per_kwu_ceil(), 501);
412
413        // sat/vB: 2_000_400 / 1_000_000 = 2.0004
414        assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
415        assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
416
417        // sat/kvb: 2_000_400 / 1_000 = 2_000.4
418        assert_eq!(fee_rate.to_sat_per_kvb_floor(), 2_000);
419        assert_eq!(fee_rate.to_sat_per_kvb_ceil(), 2_001);
420
421        let max = FeeRate::MAX;
422        assert_eq!(max.to_sat_per_kwu_ceil(), u64::MAX / 4_000 + 1);
423        assert_eq!(max.to_sat_per_vb_ceil(), u64::MAX / 1_000_000 + 1);
424        assert_eq!(max.to_sat_per_kvb_ceil(), u64::MAX / 1_000 + 1);
425    }
426
427    #[test]
428    fn checked_mul() {
429        let fee_rate =
430            FeeRate::from_sat_per_kwu(10).checked_mul(10).expect("expected feerate in sat/kwu");
431        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100));
432
433        let fee_rate = FeeRate::from_sat_per_kwu(10).checked_mul(u64::MAX);
434        assert!(fee_rate.is_none());
435    }
436
437    #[test]
438    fn checked_div() {
439        let fee_rate =
440            FeeRate::from_sat_per_kwu(10).checked_div(10).expect("expected feerate in sat/kwu");
441        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
442
443        let fee_rate = FeeRate::from_sat_per_kwu(10).checked_div(0);
444        assert!(fee_rate.is_none());
445    }
446
447    #[test]
448    fn mvb() {
449        let fee_rate = FeeRate::from_sat_per_mvb(1_234_567);
450        let got = fee_rate.to_sat_per_mvb();
451        assert_eq!(got, 1_234_567);
452    }
453}