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