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 pub(crate) const fn from_sat_per_mvb(sat_mvb: u64) -> Self { Self(sat_mvb) }
33
34 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 pub const ZERO: Self = Self::from_sat_per_mvb(0);
47
48 pub const MIN: Self = Self::ZERO;
52
53 pub const MAX: Self = Self::from_sat_per_mvb(u64::MAX);
55
56 pub const BROADCAST_MIN: Self = Self::from_sat_per_vb(1);
60
61 pub const DUST: Self = Self::from_sat_per_vb(3);
63
64 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 pub const fn from_per_kwu(rate: Amount) -> NumOpResult<Self> {
72 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 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 pub const fn from_per_vb(rate: Amount) -> NumOpResult<Self> {
87 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 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 pub const fn from_per_kvb(rate: Amount) -> NumOpResult<Self> {
102 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 pub const fn to_sat_per_kwu_floor(self) -> u64 { self.to_sat_per_mvb() / 4_000 }
111
112 pub const fn to_sat_per_kwu_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(4_000) }
114
115 pub const fn to_sat_per_vb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000_000 }
117
118 pub const fn to_sat_per_vb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000_000) }
120
121 pub const fn to_sat_per_kvb_floor(self) -> u64 { self.to_sat_per_mvb() / 1_000 }
123
124 pub const fn to_sat_per_kvb_ceil(self) -> u64 { self.to_sat_per_mvb().div_ceil(1_000) }
126
127 #[must_use]
131 pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
132 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 #[must_use]
143 pub const fn checked_div(self, rhs: u64) -> Option<Self> {
144 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 #[must_use]
155 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
156 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 #[must_use]
167 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
168 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 pub const fn to_fee(self, weight: Weight) -> Amount {
185 match self.mul_by_weight(weight) {
187 NumOpResult::Valid(fee) => fee,
188 NumOpResult::Error(_) => Amount::MAX,
189 }
190 }
191
192 #[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 #[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 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 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 assert_eq!(fee_rate.to_sat_per_kwu_floor(), 500);
411 assert_eq!(fee_rate.to_sat_per_kwu_ceil(), 501);
412
413 assert_eq!(fee_rate.to_sat_per_vb_floor(), 2);
415 assert_eq!(fee_rate.to_sat_per_vb_ceil(), 3);
416
417 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}