Skip to main content

bitcoin_units/
fee.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Calculate transaction fee ([`Amount`]) from a [`FeeRate`] and [`Weight`].
4//!
5//! The total fee for a transaction can be calculated by multiplying the transaction weight by the
6//! fee rate used to send the transaction.
7//!
8//! Either the weight or fee rate can be calculated if one knows the total fee and either of the
9//! other values. Note however that such calculations truncate (as for integer division).
10//!
11//! We provide `fee.div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate
12//! required to pay at least `fee` for transaction with `weight`.
13//!
14//! We support various `core::ops` traits all of which return [`NumOpResult<T>`].
15//!
16//! For specific methods see:
17//!
18//! * [`Amount::div_by_weight_floor`]
19//! * [`Amount::div_by_weight_ceil`]
20//! * [`Amount::div_by_fee_rate_floor`]
21//! * [`Amount::div_by_fee_rate_ceil`]
22//! * [`Weight::mul_by_fee_rate`]
23//! * [`FeeRate::mul_by_weight`]
24//! * [`FeeRate::to_fee`]
25
26use core::ops;
27
28use NumOpResult as R;
29
30use crate::{Amount, FeeRate, NumOpResult, Weight};
31
32crate::internal_macros::impl_op_for_references! {
33    impl ops::Mul<FeeRate> for Weight {
34        type Output = NumOpResult<Amount>;
35        fn mul(self, rhs: FeeRate) -> Self::Output { rhs.mul_by_weight(self) }
36    }
37    impl ops::Mul<FeeRate> for NumOpResult<Weight> {
38        type Output = NumOpResult<Amount>;
39        fn mul(self, rhs: FeeRate) -> Self::Output {
40            match self {
41                R::Valid(lhs) => lhs * rhs,
42                R::Error(e) => NumOpResult::Error(e),
43            }
44        }
45    }
46    impl ops::Mul<NumOpResult<FeeRate>> for Weight {
47        type Output = NumOpResult<Amount>;
48        fn mul(self, rhs: NumOpResult<FeeRate>) -> Self::Output {
49            match rhs {
50                R::Valid(fee_rate) => self * fee_rate,
51                R::Error(e) => NumOpResult::Error(e),
52            }
53        }
54    }
55    impl ops::Mul<NumOpResult<FeeRate>> for NumOpResult<Weight> {
56        type Output = NumOpResult<Amount>;
57        fn mul(self, rhs: NumOpResult<FeeRate>) -> Self::Output {
58            match self {
59                R::Valid(lhs) => { match rhs {
60                    R::Valid(fee_rate) => lhs * fee_rate,
61                    R::Error(e) => NumOpResult::Error(e),
62                }}
63                R::Error(e) => NumOpResult::Error(e),
64            }
65        }
66    }
67
68    impl ops::Mul<Weight> for FeeRate {
69        type Output = NumOpResult<Amount>;
70        fn mul(self, rhs: Weight) -> Self::Output { self.mul_by_weight(rhs) }
71    }
72    impl ops::Mul<Weight> for NumOpResult<FeeRate> {
73        type Output = NumOpResult<Amount>;
74        fn mul(self, rhs: Weight) -> Self::Output {
75            match self {
76                R::Valid(lhs) => lhs * rhs,
77                R::Error(e) => NumOpResult::Error(e),
78            }
79        }
80    }
81    impl ops::Mul<NumOpResult<Weight>> for FeeRate {
82        type Output = NumOpResult<Amount>;
83        fn mul(self, rhs: NumOpResult<Weight>) -> Self::Output {
84            match rhs {
85                R::Valid(weight) => self * weight,
86                R::Error(e) => NumOpResult::Error(e),
87            }
88        }
89    }
90    impl ops::Mul<NumOpResult<Weight>> for NumOpResult<FeeRate> {
91        type Output = NumOpResult<Amount>;
92        fn mul(self, rhs: NumOpResult<Weight>) -> Self::Output {
93            match self {
94                R::Valid(lhs) => { match rhs {
95                    R::Valid(weight) => lhs * weight,
96                    R::Error(e) => NumOpResult::Error(e),
97                }}
98                R::Error(e) => NumOpResult::Error(e),
99            }
100        }
101    }
102
103    impl ops::Div<Weight> for Amount {
104        type Output = NumOpResult<FeeRate>;
105
106        fn div(self, rhs: Weight) -> Self::Output {
107            self.div_by_weight_floor(rhs)
108        }
109    }
110    impl ops::Div<Weight> for NumOpResult<Amount> {
111        type Output = NumOpResult<FeeRate>;
112
113        fn div(self, rhs: Weight) -> Self::Output {
114            match self {
115                R::Valid(lhs) => lhs / rhs,
116                R::Error(e) => NumOpResult::Error(e),
117            }
118        }
119    }
120    impl ops::Div<NumOpResult<Weight>> for Amount {
121        type Output = NumOpResult<FeeRate>;
122
123        fn div(self, rhs: NumOpResult<Weight>) -> Self::Output {
124            match rhs {
125                R::Valid(weight) => self / weight,
126                R::Error(e) => NumOpResult::Error(e),
127            }
128        }
129    }
130    impl ops::Div<NumOpResult<Weight>> for NumOpResult<Amount> {
131        type Output = NumOpResult<FeeRate>;
132
133        fn div(self, rhs: NumOpResult<Weight>) -> Self::Output {
134            match self {
135                R::Valid(lhs) => { match rhs {
136                    R::Valid(weight) => lhs / weight,
137                    R::Error(e) => NumOpResult::Error(e),
138                }}
139                R::Error(e) => NumOpResult::Error(e),
140            }
141        }
142    }
143
144    impl ops::Div<FeeRate> for Amount {
145        type Output = NumOpResult<Weight>;
146
147        fn div(self, rhs: FeeRate) -> Self::Output {
148            self.div_by_fee_rate_floor(rhs)
149        }
150    }
151    impl ops::Div<FeeRate> for NumOpResult<Amount> {
152        type Output = NumOpResult<Weight>;
153
154        fn div(self, rhs: FeeRate) -> Self::Output {
155            match self {
156                R::Valid(lhs) => lhs / rhs,
157                R::Error(e) => NumOpResult::Error(e),
158            }
159        }
160    }
161    impl ops::Div<NumOpResult<FeeRate>> for Amount {
162        type Output = NumOpResult<Weight>;
163
164        fn div(self, rhs: NumOpResult<FeeRate>) -> Self::Output {
165            match rhs {
166                R::Valid(fee_rate) => self / fee_rate,
167                R::Error(e) => NumOpResult::Error(e),
168            }
169        }
170    }
171    impl ops::Div<NumOpResult<FeeRate>> for NumOpResult<Amount> {
172        type Output = NumOpResult<Weight>;
173
174        fn div(self, rhs: NumOpResult<FeeRate>) -> Self::Output {
175            match self {
176                R::Valid(lhs) => { match rhs {
177                    R::Valid(fee_rate) => lhs / fee_rate,
178                    R::Error(e) => NumOpResult::Error(e),
179                }}
180                R::Error(e) => NumOpResult::Error(e),
181            }
182        }
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn fee_rate_div_by_weight() {
192        let fee_rate = (Amount::from_sat_u32(329) / Weight::from_wu(381)).unwrap();
193        assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
194    }
195
196    #[test]
197    fn fee_wu() {
198        let fee_rate = FeeRate::from_sat_per_vb(2);
199        let weight = Weight::from_vb(3).unwrap();
200        assert_eq!(fee_rate.to_fee(weight), Amount::from_sat_u32(6));
201    }
202
203    #[test]
204    fn weight_mul() {
205        let weight = Weight::from_vb(10).unwrap();
206        let fee: Amount =
207            FeeRate::from_sat_per_vb(10).mul_by_weight(weight).expect("expected Amount");
208        assert_eq!(Amount::from_sat_u32(100), fee);
209
210        let fee = FeeRate::from_sat_per_kwu(10).mul_by_weight(Weight::MAX);
211        assert!(fee.is_error());
212
213        let weight = Weight::from_vb(3).unwrap();
214        let fee_rate = FeeRate::from_sat_per_vb(3);
215        let fee = fee_rate.mul_by_weight(weight).unwrap();
216        assert_eq!(Amount::from_sat_u32(9), fee);
217
218        let weight = Weight::from_wu(381);
219        let fee_rate = FeeRate::from_sat_per_kwu(864);
220        let fee = weight.mul_by_fee_rate(fee_rate).unwrap();
221        // 381 * 0.864 yields 329.18.
222        // The result is then rounded up to 330.
223        assert_eq!(fee, Amount::from_sat_u32(330));
224    }
225
226    #[test]
227    #[allow(clippy::op_ref)]
228    fn multiply() {
229        let two = FeeRate::from_sat_per_vb(2);
230        let three = Weight::from_vb(3).unwrap();
231        let six = Amount::from_sat_u32(6);
232
233        assert_eq!(two * three, six.into());
234
235        // Test reference operators
236        assert_eq!(&two * three, six.into());
237        assert_eq!(two * &three, six.into());
238        assert_eq!(&two * &three, six.into());
239    }
240
241    #[test]
242    #[allow(clippy::op_ref)]
243    fn amount_div_by_fee_rate() {
244        // Test exact division
245        let amount = Amount::from_sat_u32(1000);
246        let fee_rate = FeeRate::from_sat_per_kwu(2);
247        let weight = amount / fee_rate;
248        assert_eq!(weight.unwrap(), Weight::from_wu(500_000));
249
250        // Test reference division
251        let weight_ref = (&amount / fee_rate).unwrap();
252        assert_eq!(weight_ref, Weight::from_wu(500_000));
253        let weight_ref2 = (amount / &fee_rate).unwrap();
254        assert_eq!(weight_ref2, Weight::from_wu(500_000));
255        let weight_ref3 = (&amount / &fee_rate).unwrap();
256        assert_eq!(weight_ref3, Weight::from_wu(500_000));
257
258        // Test truncation behavior
259        let amount = Amount::from_sat_u32(1000);
260        let fee_rate = FeeRate::from_sat_per_kwu(3);
261        let weight = amount / fee_rate;
262        // 1000 * 1000 = 1,000,000 msats
263        // 1,000,000 / 3 = 333,333.33... wu
264        // Should truncate down to 333,333 wu
265        assert_eq!(weight.unwrap(), Weight::from_wu(333_333));
266
267        // Verify that ceiling division gives different result
268        let ceil_weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
269        assert_eq!(ceil_weight, Weight::from_wu(333_334));
270
271        // Test that division by zero returns None
272        let zero_rate = FeeRate::from_sat_per_kwu(0);
273        assert!(amount.div_by_fee_rate_floor(zero_rate).is_error());
274        assert!(amount.div_by_fee_rate_ceil(zero_rate).is_error());
275    }
276}