@@ -15,6 +15,7 @@ use super::{
1515 parse_signed_to_satoshi, split_amount_and_denomination, Denomination , Display , DisplayStyle ,
1616 OutOfRangeError , ParseAmountError , ParseError , SignedAmount ,
1717} ;
18+ use crate :: { FeeRate , Weight } ;
1819
1920mod encapsulate {
2021 use super :: OutOfRangeError ;
@@ -402,6 +403,106 @@ impl Amount {
402403 SignedAmount :: from_sat ( self . to_sat ( ) as i64 ) // Cast ok, signed amount and amount share positive range.
403404 . expect ( "range of Amount is within range of SignedAmount" )
404405 }
406+
407+ /// Checked weight floor division.
408+ ///
409+ /// Be aware that integer division loses the remainder if no exact division
410+ /// can be made. See also [`Self::checked_div_by_weight_ceil`].
411+ ///
412+ /// Returns [`None`] if overflow occurred.
413+ #[ must_use]
414+ pub const fn checked_div_by_weight_floor ( self , weight : Weight ) -> Option < FeeRate > {
415+ let wu = weight. to_wu ( ) ;
416+ if wu == 0 {
417+ return None ;
418+ }
419+
420+ // Mul by 1,000 because we use per/kwu.
421+ match self . to_sat ( ) . checked_mul ( 1_000 ) {
422+ Some ( sats) => {
423+ let fee_rate = sats / wu;
424+ FeeRate :: from_sat_per_kwu ( fee_rate)
425+ }
426+ None => None ,
427+ }
428+ }
429+
430+ /// Checked weight ceiling division.
431+ ///
432+ /// Be aware that integer division loses the remainder if no exact division
433+ /// can be made. This method rounds up ensuring the transaction fee rate is
434+ /// sufficient. See also [`Self::checked_div_by_weight_floor`].
435+ ///
436+ /// Returns [`None`] if overflow occurred.
437+ ///
438+ /// # Examples
439+ ///
440+ /// ```
441+ /// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
442+ /// let amount = Amount::from_sat(10)?;
443+ /// let weight = Weight::from_wu(300);
444+ /// let fee_rate = amount.checked_div_by_weight_ceil(weight);
445+ /// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
446+ /// # Ok::<_, amount::OutOfRangeError>(())
447+ /// ```
448+ #[ must_use]
449+ pub const fn checked_div_by_weight_ceil ( self , weight : Weight ) -> Option < FeeRate > {
450+ let wu = weight. to_wu ( ) ;
451+ if wu == 0 {
452+ return None ;
453+ }
454+
455+ // Mul by 1,000 because we use per/kwu.
456+ if let Some ( sats) = self . to_sat ( ) . checked_mul ( 1_000 ) {
457+ // No need to used checked arithmetic because wu is non-zero.
458+ if let Some ( bump) = sats. checked_add ( wu - 1 ) {
459+ let fee_rate = bump / wu;
460+ return FeeRate :: from_sat_per_kwu ( fee_rate) ;
461+ }
462+ }
463+ None
464+ }
465+
466+ /// Checked fee rate floor division.
467+ ///
468+ /// Computes the maximum weight that would result in a fee less than or equal to this amount
469+ /// at the given `fee_rate`. Uses floor division to ensure the resulting weight doesn't cause
470+ /// the fee to exceed the amount.
471+ ///
472+ /// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
473+ #[ must_use]
474+ pub const fn checked_div_by_fee_rate_floor ( self , fee_rate : FeeRate ) -> Option < Weight > {
475+ if let Some ( msats) = self . to_sat ( ) . checked_mul ( 1000 ) {
476+ if let Some ( wu) = msats. checked_div ( fee_rate. to_sat_per_kwu_ceil ( ) ) {
477+ return Some ( Weight :: from_wu ( wu) ) ;
478+ }
479+ }
480+ None
481+ }
482+
483+ /// Checked fee rate ceiling division.
484+ ///
485+ /// Computes the minimum weight that would result in a fee greater than or equal to this amount
486+ /// at the given `fee_rate`. Uses ceiling division to ensure the resulting weight is sufficient.
487+ ///
488+ /// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
489+ #[ must_use]
490+ pub const fn checked_div_by_fee_rate_ceil ( self , fee_rate : FeeRate ) -> Option < Weight > {
491+ // Use ceil because result is used as the divisor.
492+ let rate = fee_rate. to_sat_per_kwu_ceil ( ) ;
493+ if rate == 0 {
494+ return None ;
495+ }
496+
497+ if let Some ( msats) = self . to_sat ( ) . checked_mul ( 1000 ) {
498+ // No need to used checked arithmetic because rate is non-zero.
499+ if let Some ( bump) = msats. checked_add ( rate - 1 ) {
500+ let wu = bump / rate;
501+ return Some ( Weight :: from_wu ( wu) ) ;
502+ }
503+ }
504+ None
505+ }
405506}
406507
407508impl default:: Default for Amount {
0 commit comments