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 `to_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 1,000 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 1,000 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 amount 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    /// Checked weight multiplication.
212    ///
213    /// Computes the absolute fee amount for a given [`Weight`] at this fee rate. When the resulting
214    /// fee is a non-integer amount, the amount is rounded up, ensuring that the transaction fee is
215    /// enough instead of falling short if rounded down.
216    pub const fn mul_by_weight(self, weight: Weight) -> NumOpResult<Amount> {
217        let wu = weight.to_wu();
218        if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) {
219            let fee = fee_kwu.div_ceil(1_000);
220            if let Ok(fee_amount) = Amount::from_sat(fee) {
221                return NumOpResult::Valid(fee_amount);
222            }
223        }
224        NumOpResult::Error(E::while_doing(MathOp::Mul))
225    }
226}
227
228crate::internal_macros::impl_op_for_references! {
229    impl ops::Add<FeeRate> for FeeRate {
230        type Output = FeeRate;
231
232        fn add(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() + rhs.to_sat_per_mvb()) }
233    }
234
235    impl ops::Sub<FeeRate> for FeeRate {
236        type Output = FeeRate;
237
238        fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate::from_sat_per_mvb(self.to_sat_per_mvb() - rhs.to_sat_per_mvb()) }
239    }
240
241    impl ops::Div<NonZeroU64> for FeeRate {
242        type Output = FeeRate;
243
244        fn div(self, rhs: NonZeroU64) -> Self::Output{ Self::from_sat_per_mvb(self.to_sat_per_mvb() / rhs.get()) }
245    }
246}
247crate::internal_macros::impl_add_assign!(FeeRate);
248crate::internal_macros::impl_sub_assign!(FeeRate);
249
250impl core::iter::Sum for FeeRate {
251    #[inline]
252    fn sum<I>(iter: I) -> Self
253    where
254        I: Iterator<Item = Self>,
255    {
256        Self::from_sat_per_mvb(iter.map(Self::to_sat_per_mvb).sum())
257    }
258}
259
260impl<'a> core::iter::Sum<&'a Self> for FeeRate {
261    #[inline]
262    fn sum<I>(iter: I) -> Self
263    where
264        I: Iterator<Item = &'a Self>,
265    {
266        Self::from_sat_per_mvb(iter.map(|f| Self::to_sat_per_mvb(*f)).sum())
267    }
268}
269
270#[cfg(feature = "arbitrary")]
271impl<'a> Arbitrary<'a> for FeeRate {
272    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
273        let choice = u.int_in_range(0..=4)?;
274        match choice {
275            0 => Ok(Self::MIN),
276            1 => Ok(Self::BROADCAST_MIN),
277            2 => Ok(Self::DUST),
278            3 => Ok(Self::MAX),
279            _ => Ok(Self::from_sat_per_mvb(u64::arbitrary(u)?)),
280        }
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use core::num::NonZeroU64;
287
288    use super::*;
289
290    #[test]
291    #[allow(clippy::op_ref)]
292    fn feerate_div_nonzero() {
293        let rate = FeeRate::from_sat_per_kwu(200);
294        let divisor = NonZeroU64::new(2).unwrap();
295        assert_eq!(rate / divisor, FeeRate::from_sat_per_kwu(100));
296        assert_eq!(&rate / &divisor, FeeRate::from_sat_per_kwu(100));
297    }
298
299    #[test]
300    #[allow(clippy::op_ref)]
301    fn addition() {
302        let one = FeeRate::from_sat_per_kwu(1);
303        let two = FeeRate::from_sat_per_kwu(2);
304        let three = FeeRate::from_sat_per_kwu(3);
305
306        assert!(one + two == three);
307        assert!(&one + two == three);
308        assert!(one + &two == three);
309        assert!(&one + &two == three);
310    }
311
312    #[test]
313    #[allow(clippy::op_ref)]
314    fn subtract() {
315        let three = FeeRate::from_sat_per_kwu(3);
316        let seven = FeeRate::from_sat_per_kwu(7);
317        let ten = FeeRate::from_sat_per_kwu(10);
318
319        assert_eq!(ten - seven, three);
320        assert_eq!(&ten - seven, three);
321        assert_eq!(ten - &seven, three);
322        assert_eq!(&ten - &seven, three);
323    }
324
325    #[test]
326    fn add_assign() {
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 = FeeRate::from_sat_per_kwu(1);
332        f += &FeeRate::from_sat_per_kwu(2);
333        assert_eq!(f, FeeRate::from_sat_per_kwu(3));
334
335        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(1));
336        f += FeeRate::from_sat_per_kwu(2);
337        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(3)));
338
339        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(1));
340        f += NumOpResult::Valid(FeeRate::from_sat_per_kwu(2));
341        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(3)));
342    }
343
344    #[test]
345    fn sub_assign() {
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 = FeeRate::from_sat_per_kwu(3);
351        f -= &FeeRate::from_sat_per_kwu(2);
352        assert_eq!(f, FeeRate::from_sat_per_kwu(1));
353
354        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(3));
355        f -= FeeRate::from_sat_per_kwu(2);
356        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(1)));
357
358        let mut f = NumOpResult::Valid(FeeRate::from_sat_per_kwu(3));
359        f -= NumOpResult::Valid(FeeRate::from_sat_per_kwu(2));
360        assert_eq!(f, NumOpResult::Valid(FeeRate::from_sat_per_kwu(1)));
361    }
362
363    #[test]
364    fn checked_add() {
365        let one = FeeRate::from_sat_per_kwu(1);
366        let two = FeeRate::from_sat_per_kwu(2);
367        let three = FeeRate::from_sat_per_kwu(3);
368
369        assert_eq!(one.checked_add(two).unwrap(), three);
370
371        // Sanity check - no overflow adding one to per kvb max.
372        let _ = FeeRate::from_sat_per_kvb(u32::MAX).checked_add(one).unwrap();
373        let fee_rate = FeeRate::from_sat_per_mvb(u64::MAX).checked_add(one);
374        assert!(fee_rate.is_none());
375    }
376
377    #[test]
378    fn checked_sub() {
379        let one = FeeRate::from_sat_per_kwu(1);
380        let two = FeeRate::from_sat_per_kwu(2);
381        let three = FeeRate::from_sat_per_kwu(3);
382        assert_eq!(three.checked_sub(two).unwrap(), one);
383
384        let fee_rate = FeeRate::ZERO.checked_sub(one);
385        assert!(fee_rate.is_none());
386    }
387
388    #[test]
389    fn fee_rate_const() {
390        assert_eq!(FeeRate::ZERO.to_sat_per_kwu_floor(), 0);
391        assert_eq!(FeeRate::MIN.to_sat_per_kwu_floor(), u64::MIN);
392        assert_eq!(FeeRate::MAX.to_sat_per_kwu_floor(), u64::MAX / 4_000);
393        assert_eq!(FeeRate::BROADCAST_MIN.to_sat_per_kwu_floor(), 250);
394        assert_eq!(FeeRate::DUST.to_sat_per_kwu_floor(), 750);
395    }
396
397    #[test]
398    fn fee_rate_from_sat_per_vb() {
399        let fee_rate = FeeRate::from_sat_per_vb(10);
400        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(2500));
401    }
402
403    #[test]
404    fn fee_rate_from_sat_per_kvb() {
405        let fee_rate = FeeRate::from_sat_per_kvb(11);
406        assert_eq!(fee_rate, FeeRate::from_sat_per_mvb(11_000));
407    }
408
409    #[test]
410    fn fee_rate_to_sat_per_x() {
411        let fee_rate = FeeRate::from_sat_per_mvb(2_000_400);
412
413        // sat/kwu: 2_000_400 / 4_000 = 500.1
414        assert_eq!(fee_rate.to_sat_per_kwu_floor(), 500);
415        assert_eq!(fee_rate.to_sat_per_kwu_ceil(), 501);
416
417        // sat/vB: 2_000_400 / 1_000_000 = 2.0004
418        assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
419        assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
420
421        // sat/kvb: 2_000_400 / 1_000 = 2_000.4
422        assert_eq!(fee_rate.to_sat_per_kvb_floor(), 2_000);
423        assert_eq!(fee_rate.to_sat_per_kvb_ceil(), 2_001);
424
425        let max = FeeRate::MAX;
426        assert_eq!(max.to_sat_per_kwu_ceil(), u64::MAX / 4_000 + 1);
427        assert_eq!(max.to_sat_per_vb_ceil(), u64::MAX / 1_000_000 + 1);
428        assert_eq!(max.to_sat_per_kvb_ceil(), u64::MAX / 1_000 + 1);
429    }
430
431    #[test]
432    fn checked_mul() {
433        let fee_rate =
434            FeeRate::from_sat_per_kwu(10).checked_mul(10).expect("expected feerate in sat/kwu");
435        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(100));
436
437        let fee_rate = FeeRate::from_sat_per_kwu(10).checked_mul(u64::MAX);
438        assert!(fee_rate.is_none());
439    }
440
441    #[test]
442    fn checked_div() {
443        let fee_rate =
444            FeeRate::from_sat_per_kwu(10).checked_div(10).expect("expected feerate in sat/kwu");
445        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1));
446
447        let fee_rate = FeeRate::from_sat_per_kwu(10).checked_div(0);
448        assert!(fee_rate.is_none());
449    }
450
451    #[test]
452    fn mvb() {
453        let fee_rate = FeeRate::from_sat_per_mvb(1_234_567);
454        let got = fee_rate.to_sat_per_mvb();
455        assert_eq!(got, 1_234_567);
456    }
457}