bitcoin_units/
fee_rate.rs1use core::fmt;
6use core::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::{Arbitrary, Unstructured};
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::amount::Amount;
14use crate::weight::Weight;
15
16#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[cfg_attr(feature = "serde", serde(transparent))]
23pub struct FeeRate(u64);
24
25impl FeeRate {
26 pub const ZERO: FeeRate = FeeRate(0);
30
31 pub const MIN: FeeRate = FeeRate::ZERO;
35
36 pub const MAX: FeeRate = FeeRate(u64::MAX);
38
39 pub const BROADCAST_MIN: FeeRate = FeeRate::from_sat_per_vb_unchecked(1);
43
44 pub const DUST: FeeRate = FeeRate::from_sat_per_vb_unchecked(3);
46
47 pub const fn from_sat_per_kwu(sat_kwu: u64) -> Self { FeeRate(sat_kwu) }
49
50 pub fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
56 Some(FeeRate(sat_vb.checked_mul(1000 / 4)?))
60 }
61
62 pub const fn from_sat_per_vb_unchecked(sat_vb: u64) -> Self { FeeRate(sat_vb * (1000 / 4)) }
64
65 pub const fn to_sat_per_kwu(self) -> u64 { self.0 }
69
70 pub const fn to_sat_per_vb_floor(self) -> u64 { self.0 / (1000 / 4) }
72
73 pub const fn to_sat_per_vb_ceil(self) -> u64 { (self.0 + (1000 / 4 - 1)) / (1000 / 4) }
75
76 pub fn checked_mul(self, rhs: u64) -> Option<Self> { self.0.checked_mul(rhs).map(Self) }
80
81 pub fn checked_div(self, rhs: u64) -> Option<Self> { self.0.checked_div(rhs).map(Self) }
85
86 pub fn checked_mul_by_weight(self, rhs: Weight) -> Option<Amount> {
95 let sats = self.0.checked_mul(rhs.to_wu())?.checked_add(999)? / 1000;
96 Some(Amount::from_sat(sats))
97 }
98
99 pub fn checked_add(self, rhs: u64) -> Option<Self> { self.0.checked_add(rhs).map(Self) }
103
104 pub fn checked_sub(self, rhs: u64) -> Option<Self> { self.0.checked_sub(rhs).map(Self) }
108
109 pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) }
114
115 pub fn fee_vb(self, vb: u64) -> Option<Amount> {
121 Weight::from_vb(vb).and_then(|w| self.fee_wu(w))
122 }
123}
124
125#[cfg(feature = "arbitrary")]
126impl<'a> Arbitrary<'a> for FeeRate {
127 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
128 let f = u64::arbitrary(u)?;
129 Ok(FeeRate(f))
130 }
131}
132
133impl fmt::Display for FeeRate {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 if f.alternate() {
137 write!(f, "{}.00 sat/vbyte", self.to_sat_per_vb_ceil())
138 } else {
139 fmt::Display::fmt(&self.0, f)
140 }
141 }
142}
143
144impl From<FeeRate> for u64 {
145 fn from(value: FeeRate) -> Self { value.to_sat_per_kwu() }
146}
147
148impl Add for FeeRate {
149 type Output = FeeRate;
150
151 fn add(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 + rhs.0) }
152}
153
154impl Add<FeeRate> for &FeeRate {
155 type Output = FeeRate;
156
157 fn add(self, other: FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
158}
159
160impl Add<&FeeRate> for FeeRate {
161 type Output = FeeRate;
162
163 fn add(self, other: &FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
164}
165
166impl<'a, 'b> Add<&'a FeeRate> for &'b FeeRate {
167 type Output = FeeRate;
168
169 fn add(self, other: &'a FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 + other.0) }
170}
171
172impl Sub for FeeRate {
173 type Output = FeeRate;
174
175 fn sub(self, rhs: FeeRate) -> Self::Output { FeeRate(self.0 - rhs.0) }
176}
177
178impl Sub<FeeRate> for &FeeRate {
179 type Output = FeeRate;
180
181 fn sub(self, other: FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
182}
183
184impl Sub<&FeeRate> for FeeRate {
185 type Output = FeeRate;
186
187 fn sub(self, other: &FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
188}
189
190impl<'a, 'b> Sub<&'a FeeRate> for &'b FeeRate {
191 type Output = FeeRate;
192
193 fn sub(self, other: &'a FeeRate) -> <FeeRate as Add>::Output { FeeRate(self.0 - other.0) }
194}
195
196impl Mul<FeeRate> for Weight {
198 type Output = Amount;
199
200 fn mul(self, rhs: FeeRate) -> Self::Output {
201 Amount::from_sat((rhs.to_sat_per_kwu() * self.to_wu() + 999) / 1000)
202 }
203}
204
205impl Mul<Weight> for FeeRate {
206 type Output = Amount;
207
208 fn mul(self, rhs: Weight) -> Self::Output { rhs * self }
209}
210
211impl Div<Weight> for Amount {
212 type Output = FeeRate;
213
214 fn div(self, rhs: Weight) -> Self::Output { FeeRate(self.to_sat() * 1000 / rhs.to_wu()) }
215}
216
217impl AddAssign for FeeRate {
218 fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
219}
220
221impl AddAssign<&FeeRate> for FeeRate {
222 fn add_assign(&mut self, rhs: &FeeRate) { self.0 += rhs.0 }
223}
224
225impl SubAssign for FeeRate {
226 fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
227}
228
229impl SubAssign<&FeeRate> for FeeRate {
230 fn sub_assign(&mut self, rhs: &FeeRate) { self.0 -= rhs.0 }
231}
232
233crate::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu);
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 #[allow(clippy::op_ref)]
241 fn addition() {
242 let one = FeeRate(1);
243 let two = FeeRate(2);
244 let three = FeeRate(3);
245
246 assert!(one + two == three);
247 assert!(&one + two == three);
248 assert!(one + &two == three);
249 assert!(&one + &two == three);
250 }
251
252 #[test]
253 #[allow(clippy::op_ref)]
254 fn subtract() {
255 let one = FeeRate(1);
256 let two = FeeRate(2);
257 let three = FeeRate(3);
258
259 assert!(three - two == one);
260 assert!(&three - two == one);
261 assert!(three - &two == one);
262 assert!(&three - &two == one);
263 }
264
265 #[test]
266 fn add_assign() {
267 let mut f = FeeRate(1);
268 f += FeeRate(2);
269 assert_eq!(f, FeeRate(3));
270
271 let mut f = FeeRate(1);
272 f += &FeeRate(2);
273 assert_eq!(f, FeeRate(3));
274 }
275
276 #[test]
277 fn sub_assign() {
278 let mut f = FeeRate(3);
279 f -= FeeRate(2);
280 assert_eq!(f, FeeRate(1));
281
282 let mut f = FeeRate(3);
283 f -= &FeeRate(2);
284 assert_eq!(f, FeeRate(1));
285 }
286
287 #[test]
288 fn checked_add() {
289 let f = FeeRate(1).checked_add(2).unwrap();
290 assert_eq!(FeeRate(3), f);
291
292 let f = FeeRate(u64::MAX).checked_add(1);
293 assert!(f.is_none());
294 }
295
296 #[test]
297 fn checked_sub() {
298 let f = FeeRate(2).checked_sub(1).unwrap();
299 assert_eq!(FeeRate(1), f);
300
301 let f = FeeRate::ZERO.checked_sub(1);
302 assert!(f.is_none());
303 }
304
305 #[test]
306 fn fee_rate_const_test() {
307 assert_eq!(0, FeeRate::ZERO.to_sat_per_kwu());
308 assert_eq!(u64::MIN, FeeRate::MIN.to_sat_per_kwu());
309 assert_eq!(u64::MAX, FeeRate::MAX.to_sat_per_kwu());
310 assert_eq!(250, FeeRate::BROADCAST_MIN.to_sat_per_kwu());
311 assert_eq!(750, FeeRate::DUST.to_sat_per_kwu());
312 }
313
314 #[test]
315 fn fee_rate_from_sat_per_vb_test() {
316 let fee_rate = FeeRate::from_sat_per_vb(10).expect("expected feerate in sat/kwu");
317 assert_eq!(FeeRate(2500), fee_rate);
318 }
319
320 #[test]
321 fn fee_rate_from_sat_per_vb_overflow_test() {
322 let fee_rate = FeeRate::from_sat_per_vb(u64::MAX);
323 assert!(fee_rate.is_none());
324 }
325
326 #[test]
327 fn from_sat_per_vb_unchecked_test() {
328 let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
329 assert_eq!(FeeRate(2500), fee_rate);
330 }
331
332 #[test]
333 #[cfg(debug_assertions)]
334 #[should_panic]
335 fn from_sat_per_vb_unchecked_panic_test() { FeeRate::from_sat_per_vb_unchecked(u64::MAX); }
336
337 #[test]
338 fn raw_feerate_test() {
339 let fee_rate = FeeRate(333);
340 assert_eq!(333, fee_rate.to_sat_per_kwu());
341 assert_eq!(1, fee_rate.to_sat_per_vb_floor());
342 assert_eq!(2, fee_rate.to_sat_per_vb_ceil());
343 }
344
345 #[test]
346 fn checked_mul_test() {
347 let fee_rate = FeeRate(10).checked_mul(10).expect("expected feerate in sat/kwu");
348 assert_eq!(FeeRate(100), fee_rate);
349
350 let fee_rate = FeeRate(10).checked_mul(u64::MAX);
351 assert!(fee_rate.is_none());
352 }
353
354 #[test]
355 fn checked_weight_mul_test() {
356 let weight = Weight::from_vb(10).unwrap();
357 let fee: Amount = FeeRate::from_sat_per_vb(10)
358 .unwrap()
359 .checked_mul_by_weight(weight)
360 .expect("expected Amount");
361 assert_eq!(Amount::from_sat(100), fee);
362
363 let fee = FeeRate(10).checked_mul_by_weight(Weight::MAX);
364 assert!(fee.is_none());
365
366 let weight = Weight::from_vb(3).unwrap();
367 let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
368 let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
369 assert_eq!(Amount::from_sat(9), fee);
370 }
371
372 #[test]
373 fn checked_div_test() {
374 let fee_rate = FeeRate(10).checked_div(10).expect("expected feerate in sat/kwu");
375 assert_eq!(FeeRate(1), fee_rate);
376
377 let fee_rate = FeeRate(10).checked_div(0);
378 assert!(fee_rate.is_none());
379 }
380}