bitcoin_units/fee_rate/
mod.rs1#[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 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
28 pub struct FeeRate(u64);
29
30 impl FeeRate {
31 #[inline]
33 pub(crate) const fn from_sat_per_mvb(sat_mvb: u64) -> Self { Self(sat_mvb) }
34
35 #[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 pub const ZERO: Self = Self::from_sat_per_mvb(0);
49
50 pub const MIN: Self = Self::ZERO;
54
55 pub const MAX: Self = Self::from_sat_per_mvb(u64::MAX);
57
58 pub const BROADCAST_MIN: Self = Self::from_sat_per_vb(1);
62
63 pub const DUST: Self = Self::from_sat_per_vb(3);
65
66 #[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 #[inline]
75 pub const fn from_per_kwu(rate: Amount) -> NumOpResult<Self> {
76 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 #[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 #[inline]
92 pub const fn from_per_vb(rate: Amount) -> NumOpResult<Self> {
93 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 #[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 #[inline]
109 pub const fn from_per_kvb(rate: Amount) -> NumOpResult<Self> {
110 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 #[inline]
119 pub const fn to_sat_per_kwu_floor(self) -> u64 { self.to_sat_per_mvb() / 4_000 }
120
121 #[inline]
123 pub const fn to_sat_per_kwu_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(4_000) }
124
125 #[inline]
127 pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000_000 }
128
129 #[inline]
131 pub const fn to_sat_per_vb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000_000) }
132
133 #[inline]
135 pub const fn to_sat_per_kvb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000 }
136
137 #[inline]
139 pub const fn to_sat_per_kvb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000) }
140
141 #[inline]
145 #[must_use]
146 pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
147 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 #[inline]
158 #[must_use]
159 pub const fn checked_div(self, rhs: u64) -> Option<Self> {
160 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 #[inline]
171 #[must_use]
172 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
173 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 #[inline]
184 #[must_use]
185 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
186 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 #[inline]
203 pub const fn to_fee(self, weight: Weight) -> Amount {
204 match self.mul_by_weight(weight) {
206 NumOpResult::Valid(fee) => fee,
207 NumOpResult::Error(_) => Amount::MAX,
208 }
209 }
210
211 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 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 assert_eq!(fee_rate.to_sat_per_kwu_floor(), 500);
415 assert_eq!(fee_rate.to_sat_per_kwu_ceil(), 501);
416
417 assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
419 assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
420
421 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}