Skip to main content

bitcoin_units/amount/
unsigned.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! An unsigned bitcoin amount.
4
5#[cfg(feature = "alloc")]
6use alloc::string::{String, ToString};
7use core::str::FromStr;
8use core::{default, fmt};
9
10#[cfg(feature = "arbitrary")]
11use arbitrary::{Arbitrary, Unstructured};
12use internals::const_casts;
13use NumOpResult as R;
14
15#[cfg(feature = "encoding")]
16use super::error::AmountDecoderError;
17use super::error::{ParseAmountErrorInner, ParseErrorInner};
18use super::{
19    parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
20    OutOfRangeError, ParseAmountError, ParseError, SignedAmount,
21};
22use crate::result::{MathOp, NumOpError as E, NumOpResult};
23use crate::{FeeRate, Weight};
24
25mod encapsulate {
26    use super::OutOfRangeError;
27
28    /// An amount.
29    ///
30    /// The [`Amount`] type can be used to express Bitcoin amounts that support arithmetic and
31    /// conversion to various denominations. The [`Amount`] type does not implement [`serde`] traits
32    /// but we do provide modules for serializing as satoshis or bitcoin.
33    ///
34    /// **Warning!**
35    ///
36    /// This type implements several arithmetic operations from [`core::ops`].
37    /// To prevent errors due to an overflow when using these operations,
38    /// it is advised to instead use the checked arithmetic methods whose names
39    /// start with `checked_`. The operations from [`core::ops`] that [`Amount`]
40    /// implements will panic when an overflow occurs.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// # #[cfg(feature = "serde")] {
46    /// use serde::{Serialize, Deserialize};
47    /// use bitcoin_units::Amount;
48    ///
49    /// #[derive(Serialize, Deserialize)]
50    /// struct Foo {
51    ///     // If you are using `rust-bitcoin` then `bitcoin::amount::serde::as_sat` also works.
52    ///     #[serde(with = "bitcoin_units::amount::serde::as_sat")]  // Also `serde::as_btc`.
53    ///     amount: Amount,
54    /// }
55    /// # }
56    /// ```
57    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58    pub struct Amount(u64);
59
60    impl Amount {
61        /// The maximum value of an amount.
62        pub const MAX: Self = Self(21_000_000 * 100_000_000);
63        /// The minimum value of an amount.
64        pub const MIN: Self = Self(0);
65
66        /// Gets the number of satoshis in this [`Amount`].
67        ///
68        /// # Examples
69        ///
70        /// ```
71        /// # use bitcoin_units::Amount;
72        /// assert_eq!(Amount::ONE_BTC.to_sat(), 100_000_000);
73        /// ```
74        pub const fn to_sat(self) -> u64 { self.0 }
75
76        /// Constructs a new [`Amount`] from the given number of satoshis.
77        ///
78        /// # Errors
79        ///
80        /// If `satoshi` is outside of valid range (greater than [`Self::MAX_MONEY`]).
81        ///
82        /// # Examples
83        ///
84        /// ```
85        /// # use bitcoin_units::{amount, Amount};
86        /// # let sat = 100_000;
87        /// let amount = Amount::from_sat(sat)?;
88        /// assert_eq!(amount.to_sat(), sat);
89        /// # Ok::<_, amount::OutOfRangeError>(())
90        /// ```
91        pub const fn from_sat(satoshi: u64) -> Result<Self, OutOfRangeError> {
92            if satoshi > Self::MAX_MONEY.to_sat() {
93                Err(OutOfRangeError { is_signed: false, is_greater_than_max: true })
94            } else {
95                Ok(Self(satoshi))
96            }
97        }
98    }
99}
100#[doc(inline)]
101pub use encapsulate::Amount;
102
103impl Amount {
104    /// The zero amount.
105    pub const ZERO: Self = Self::from_sat_u32(0);
106    /// Exactly one satoshi.
107    pub const ONE_SAT: Self = Self::from_sat_u32(1);
108    /// Exactly one bitcoin.
109    pub const ONE_BTC: Self = Self::from_btc_u16(1);
110    /// Exactly fifty bitcoin.
111    pub const FIFTY_BTC: Self = Self::from_btc_u16(50);
112    /// The maximum value allowed as an amount. Useful for sanity checking.
113    pub const MAX_MONEY: Self = Self::MAX;
114    /// The number of bytes that an amount contributes to the size of a transaction.
115    pub const SIZE: usize = 8; // Serialized length of a u64.
116
117    /// Constructs a new [`Amount`] with satoshi precision and the given number of satoshis.
118    ///
119    /// Accepts an `u32` which is guaranteed to be in range for the type, but which can only
120    /// represent roughly 0 to 42.95 BTC.
121    #[allow(clippy::missing_panics_doc)]
122    pub const fn from_sat_u32(satoshi: u32) -> Self {
123        let sats = const_casts::u32_to_u64(satoshi);
124        match Self::from_sat(sats) {
125            Ok(amount) => amount,
126            Err(_) =>
127                panic!("unreachable - u32 input [0 to 4,294,967,295 satoshis] is within range"),
128        }
129    }
130
131    /// Converts from a value expressing a decimal number of bitcoin to an [`Amount`].
132    ///
133    /// # Errors
134    ///
135    /// If the amount is too precise, negative, or greater than 21,000,000.
136    ///
137    /// Please be aware of the risk of using floating-point numbers.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// # use bitcoin_units::{amount, Amount};
143    /// let amount = Amount::from_btc(0.01)?;
144    /// assert_eq!(amount.to_sat(), 1_000_000);
145    /// # Ok::<_, amount::ParseAmountError>(())
146    /// ```
147    #[cfg(feature = "alloc")]
148    pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
149        Self::from_float_in(btc, Denomination::Bitcoin)
150    }
151
152    /// Converts from a value expressing a whole number of bitcoin to an [`Amount`].
153    #[allow(clippy::missing_panics_doc)]
154    pub fn from_int_btc<T: Into<u16>>(whole_bitcoin: T) -> Self {
155        Self::from_btc_u16(whole_bitcoin.into())
156    }
157
158    /// Converts from a value expressing a whole number of bitcoin to an [`Amount`]
159    /// in const context.
160    #[allow(clippy::missing_panics_doc)]
161    pub const fn from_btc_u16(whole_bitcoin: u16) -> Self {
162        let btc = const_casts::u16_to_u64(whole_bitcoin);
163        let sats = btc * 100_000_000;
164
165        match Self::from_sat(sats) {
166            Ok(amount) => amount,
167            Err(_) => panic!("unreachable - 65,535 BTC is within range"),
168        }
169    }
170
171    /// Parses a decimal string as a value in the given [`Denomination`].
172    ///
173    /// Note: This only parses the value string. If you want to parse a string
174    /// containing the value with denomination, use [`FromStr`].
175    ///
176    /// # Errors
177    ///
178    /// If the amount is too precise, negative, or greater than 21,000,000.
179    pub fn from_str_in(s: &str, denom: Denomination) -> Result<Self, ParseAmountError> {
180        let (is_neg, amount) =
181            parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
182        if is_neg {
183            return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
184                OutOfRangeError::negative(),
185            )));
186        }
187        Self::try_from(amount).map_err(|e| ParseAmountError(ParseAmountErrorInner::OutOfRange(e)))
188    }
189
190    /// Parses amounts with denomination suffix as produced by [`Self::to_string_with_denomination`]
191    /// or with [`fmt::Display`].
192    ///
193    /// If you want to parse only the amount without the denomination, use [`Self::from_str_in`].
194    ///
195    /// # Errors
196    ///
197    /// If the amount is too big, too precise or negative.
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// # use bitcoin_units::{amount, Amount};
203    /// let amount = Amount::from_str_with_denomination("0.1 BTC")?;
204    /// assert_eq!(amount, Amount::from_sat(10_000_000)?);
205    /// # Ok::<_, amount::ParseError>(())
206    /// ```
207    pub fn from_str_with_denomination(s: &str) -> Result<Self, ParseError> {
208        let (amt, denom) = split_amount_and_denomination(s)?;
209        Self::from_str_in(amt, denom).map_err(Into::into)
210    }
211
212    /// Expresses this [`Amount`] as a floating-point value in the given [`Denomination`].
213    ///
214    /// Please be aware of the risk of using floating-point numbers.
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// # use bitcoin_units::amount::{self, Amount, Denomination};
220    /// let amount = Amount::from_sat(100_000)?;
221    /// assert_eq!(amount.to_float_in(Denomination::Bitcoin), 0.001);
222    /// # Ok::<_, amount::ParseError>(())
223    /// ```
224    #[cfg(feature = "alloc")]
225    #[allow(clippy::missing_panics_doc)]
226    pub fn to_float_in(self, denom: Denomination) -> f64 {
227        self.to_string_in(denom).parse::<f64>().unwrap()
228    }
229
230    /// Expresses this [`Amount`] as a floating-point value in Bitcoin.
231    ///
232    /// Please be aware of the risk of using floating-point numbers.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// # use bitcoin_units::amount::{self, Amount, Denomination};
238    /// let amount = Amount::from_sat(100_000)?;
239    /// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin));
240    /// # Ok::<_, amount::ParseError>(())
241    /// ```
242    #[cfg(feature = "alloc")]
243    pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
244
245    /// Converts this [`Amount`] in floating-point notation in the given [`Denomination`].
246    ///
247    /// # Errors
248    ///
249    /// If the amount is too big, too precise or negative.
250    ///
251    /// Please be aware of the risk of using floating-point numbers.
252    #[cfg(feature = "alloc")]
253    pub fn from_float_in(value: f64, denom: Denomination) -> Result<Self, ParseAmountError> {
254        if value < 0.0 {
255            return Err(OutOfRangeError::negative().into());
256        }
257        // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
258        // Any performance-critical application should not be dealing with floats.
259        Self::from_str_in(&value.to_string(), denom)
260    }
261
262    /// Constructs a new object that implements [`fmt::Display`] in the given [`Denomination`].
263    ///
264    /// This function is useful if you do not wish to allocate. See also [`Self::to_string_in`].
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// # use bitcoin_units::amount::{self, Amount, Denomination};
270    /// # use std::fmt::Write;
271    /// let amount = Amount::from_sat(10_000_000)?;
272    /// let mut output = String::new();
273    /// let _ = write!(&mut output, "{}", amount.display_in(Denomination::Bitcoin));
274    /// assert_eq!(output, "0.1");
275    /// # Ok::<_, amount::OutOfRangeError>(())
276    /// ```
277    #[must_use]
278    pub fn display_in(self, denomination: Denomination) -> Display {
279        Display {
280            sats_abs: self.to_sat(),
281            is_negative: false,
282            style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
283        }
284    }
285
286    /// Constructs a new object that implements [`fmt::Display`] dynamically selecting
287    /// [`Denomination`].
288    ///
289    /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
290    /// avoid confusion the denomination is always shown.
291    #[must_use]
292    pub fn display_dynamic(self) -> Display {
293        Display {
294            sats_abs: self.to_sat(),
295            is_negative: false,
296            style: DisplayStyle::DynamicDenomination,
297        }
298    }
299
300    /// Returns a formatted string representing this [`Amount`] in the given [`Denomination`].
301    ///
302    /// Returned string does not include the denomination.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// # use bitcoin_units::amount::{self, Amount, Denomination};
308    /// let amount = Amount::from_sat(10_000_000)?;
309    /// assert_eq!(amount.to_string_in(Denomination::Bitcoin), "0.1");
310    /// # Ok::<_, amount::OutOfRangeError>(())
311    /// ```
312    #[cfg(feature = "alloc")]
313    pub fn to_string_in(self, denom: Denomination) -> String { self.display_in(denom).to_string() }
314
315    /// Returns a formatted string representing this [`Amount`] in the given [`Denomination`],
316    /// suffixed with the abbreviation for the denomination.
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// # use bitcoin_units::amount::{self, Amount, Denomination};
322    /// let amount = Amount::from_sat(10_000_000)?;
323    /// assert_eq!(amount.to_string_with_denomination(Denomination::Bitcoin), "0.1 BTC");
324    /// # Ok::<_, amount::OutOfRangeError>(())
325    /// ```
326    #[cfg(feature = "alloc")]
327    pub fn to_string_with_denomination(self, denom: Denomination) -> String {
328        self.display_in(denom).show_denomination().to_string()
329    }
330
331    /// Checked addition.
332    ///
333    /// Returns [`None`] if the sum is larger than [`Amount::MAX`].
334    #[must_use]
335    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
336        // No `map()` in const context.
337        // Unchecked add ok, adding two values less than `MAX_MONEY` cannot overflow an `i64`.
338        match Self::from_sat(self.to_sat() + rhs.to_sat()) {
339            Ok(amount) => Some(amount),
340            Err(_) => None,
341        }
342    }
343
344    /// Checked subtraction.
345    ///
346    /// Returns [`None`] if overflow occurred.
347    #[must_use]
348    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
349        // No `map()` in const context.
350        match self.to_sat().checked_sub(rhs.to_sat()) {
351            Some(res) => match Self::from_sat(res) {
352                Ok(amount) => Some(amount),
353                Err(_) => None, // Unreachable because of checked_sub above.
354            },
355            None => None,
356        }
357    }
358
359    /// Checked multiplication.
360    ///
361    /// Returns [`None`] if the product is larger than [`Amount::MAX`].
362    #[must_use]
363    pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
364        // No `map()` in const context.
365        match self.to_sat().checked_mul(rhs) {
366            Some(res) => match Self::from_sat(res) {
367                Ok(amount) => Some(amount),
368                Err(_) => None,
369            },
370            None => None,
371        }
372    }
373
374    /// Checked integer division.
375    ///
376    /// Be aware that integer division loses the remainder if no exact division can be made.
377    ///
378    /// Returns [`None`] if overflow occurred.
379    #[must_use]
380    pub const fn checked_div(self, rhs: u64) -> Option<Self> {
381        // No `map()` in const context.
382        match self.to_sat().checked_div(rhs) {
383            Some(res) => match Self::from_sat(res) {
384                Ok(amount) => Some(amount),
385                Err(_) => None, // Unreachable because of checked_div above.
386            },
387            None => None,
388        }
389    }
390
391    /// Checked remainder.
392    ///
393    /// Returns [`None`] if overflow occurred.
394    #[must_use]
395    pub const fn checked_rem(self, rhs: u64) -> Option<Self> {
396        // No `map()` in const context.
397        match self.to_sat().checked_rem(rhs) {
398            Some(res) => match Self::from_sat(res) {
399                Ok(amount) => Some(amount),
400                Err(_) => None, // Unreachable because of checked_rem above.
401            },
402            None => None,
403        }
404    }
405
406    /// Converts to a signed amount.
407    #[rustfmt::skip] // Moves code comments to the wrong line.
408    #[allow(clippy::missing_panics_doc)]
409    pub fn to_signed(self) -> SignedAmount {
410        SignedAmount::from_sat(self.to_sat() as i64) // Cast ok, signed amount and amount share positive range.
411            .expect("range of Amount is within range of SignedAmount")
412    }
413
414    /// Infallibly subtracts one `Amount` from another returning a [`SignedAmount`].
415    ///
416    /// Since `SignedAmount::MIN` is equivalent to `-Amount::MAX` subtraction of two signed amounts
417    /// can never overflow a `SignedAmount`.
418    #[must_use]
419    pub fn signed_sub(self, rhs: Self) -> SignedAmount {
420        (self.to_signed() - rhs.to_signed())
421            .expect("difference of two amounts is always within SignedAmount range")
422    }
423
424    /// Checked weight floor division.
425    ///
426    /// Be aware that integer division loses the remainder if no exact division
427    /// can be made. See also [`Self::div_by_weight_ceil`].
428    pub const fn div_by_weight_floor(self, weight: Weight) -> NumOpResult<FeeRate> {
429        let wu = weight.to_wu();
430
431        // Mul by 1,000 because we use per/kwu.
432        if let Some(sats) = self.to_sat().checked_mul(1_000) {
433            match sats.checked_div(wu) {
434                Some(fee_rate) =>
435                    if let Ok(amount) = Self::from_sat(fee_rate) {
436                        return FeeRate::from_per_kwu(amount);
437                    },
438                None => return R::Error(E::while_doing(MathOp::Div)),
439            }
440        }
441        // Use `MathOp::Mul` because `Div` implies div by zero.
442        R::Error(E::while_doing(MathOp::Mul))
443    }
444
445    /// Checked weight ceiling division.
446    ///
447    /// Be aware that integer division loses the remainder if no exact division
448    /// can be made. This method rounds up ensuring the transaction fee rate is
449    /// sufficient. See also [`Self::div_by_weight_floor`].
450    ///
451    /// # Examples
452    ///
453    /// ```
454    /// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
455    /// let amount = Amount::from_sat(10)?;
456    /// let weight = Weight::from_wu(300);
457    /// let fee_rate = amount.div_by_weight_ceil(weight).expect("valid fee rate");
458    /// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
459    /// # Ok::<_, amount::OutOfRangeError>(())
460    /// ```
461    pub const fn div_by_weight_ceil(self, weight: Weight) -> NumOpResult<FeeRate> {
462        let wu = weight.to_wu();
463        if wu == 0 {
464            return R::Error(E::while_doing(MathOp::Div));
465        }
466
467        // Mul by 1,000 because we use per/kwu.
468        if let Some(sats) = self.to_sat().checked_mul(1_000) {
469            // No need to use checked arithmetic because wu is non-zero.
470            let fee_rate = sats.div_ceil(wu);
471            if let Ok(amount) = Self::from_sat(fee_rate) {
472                return FeeRate::from_per_kwu(amount);
473            }
474        }
475        // Use `MathOp::Mul` because `Div` implies div by zero.
476        R::Error(E::while_doing(MathOp::Mul))
477    }
478
479    /// Checked fee rate floor division.
480    ///
481    /// Computes the maximum weight that would result in a fee less than or equal to this amount
482    /// at the given `fee_rate`. Uses floor division to ensure the resulting weight doesn't cause
483    /// the fee to exceed the amount.
484    pub const fn div_by_fee_rate_floor(self, fee_rate: FeeRate) -> NumOpResult<Weight> {
485        debug_assert!(Self::MAX.to_sat().checked_mul(1_000).is_some());
486        let msats = self.to_sat() * 1_000;
487        match msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) {
488            Some(wu) => R::Valid(Weight::from_wu(wu)),
489            None => R::Error(E::while_doing(MathOp::Div)),
490        }
491    }
492
493    /// Checked fee rate ceiling division.
494    ///
495    /// Computes the minimum weight that would result in a fee greater than or equal to this amount
496    /// at the given `fee_rate`. Uses ceiling division to ensure the resulting weight is sufficient.
497    pub const fn div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> NumOpResult<Weight> {
498        // Use ceil because result is used as the divisor.
499        let rate = fee_rate.to_sat_per_kwu_ceil();
500        // Early return so we do not have to use checked arithmetic below.
501        if rate == 0 {
502            return R::Error(E::while_doing(MathOp::Div));
503        }
504
505        debug_assert!(Self::MAX.to_sat().checked_mul(1_000).is_some());
506        let msats = self.to_sat() * 1_000;
507        NumOpResult::Valid(Weight::from_wu(msats.div_ceil(rate)))
508    }
509}
510
511crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(Amount, to_sat);
512
513impl default::Default for Amount {
514    fn default() -> Self { Self::ZERO }
515}
516
517impl fmt::Debug for Amount {
518    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519        write!(f, "Amount({} SAT)", self.to_sat())
520    }
521}
522
523// No one should depend on a binding contract for Display for this type.
524// Just using Bitcoin denominated string.
525impl fmt::Display for Amount {
526    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
527        fmt::Display::fmt(&self.display_in(Denomination::Bitcoin).show_denomination(), f)
528    }
529}
530
531impl FromStr for Amount {
532    type Err = ParseError;
533
534    /// Parses a string slice where the slice includes a denomination.
535    ///
536    /// If the returned value would be zero or negative zero, then no denomination is required.
537    fn from_str(s: &str) -> Result<Self, Self::Err> {
538        let result = Self::from_str_with_denomination(s);
539
540        match result {
541            Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
542                let d = Self::from_str_in(s, Denomination::Satoshi);
543
544                if d == Ok(Self::ZERO) {
545                    Ok(Self::ZERO)
546                } else {
547                    result
548                }
549            }
550            _ => result,
551        }
552    }
553}
554
555impl TryFrom<SignedAmount> for Amount {
556    type Error = OutOfRangeError;
557
558    fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { value.to_unsigned() }
559}
560
561#[cfg(feature = "encoding")]
562encoding::encoder_newtype_exact! {
563    /// The encoder for the [`Amount`] type.
564    pub struct AmountEncoder<'e>(encoding::ArrayEncoder<8>);
565}
566
567#[cfg(feature = "encoding")]
568impl encoding::Encodable for Amount {
569    type Encoder<'e> = AmountEncoder<'e>;
570    fn encoder(&self) -> Self::Encoder<'_> {
571        AmountEncoder::new(encoding::ArrayEncoder::without_length_prefix(
572            self.to_sat().to_le_bytes(),
573        ))
574    }
575}
576
577/// The decoder for the [`Amount`] type.
578#[cfg(feature = "encoding")]
579pub struct AmountDecoder(encoding::ArrayDecoder<8>);
580
581#[cfg(feature = "encoding")]
582impl AmountDecoder {
583    /// Constructs a new [`Amount`] decoder.
584    pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
585}
586
587#[cfg(feature = "encoding")]
588impl Default for AmountDecoder {
589    fn default() -> Self { Self::new() }
590}
591
592#[cfg(feature = "encoding")]
593impl encoding::Decoder for AmountDecoder {
594    type Output = Amount;
595    type Error = AmountDecoderError;
596
597    #[inline]
598    fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
599        self.0.push_bytes(bytes).map_err(AmountDecoderError::eof)
600    }
601
602    #[inline]
603    fn end(self) -> Result<Self::Output, Self::Error> {
604        let a = u64::from_le_bytes(self.0.end().map_err(AmountDecoderError::eof)?);
605        Amount::from_sat(a).map_err(AmountDecoderError::out_of_range)
606    }
607
608    #[inline]
609    fn read_limit(&self) -> usize { self.0.read_limit() }
610}
611
612#[cfg(feature = "encoding")]
613impl encoding::Decodable for Amount {
614    type Decoder = AmountDecoder;
615    fn decoder() -> Self::Decoder { AmountDecoder(encoding::ArrayDecoder::<8>::new()) }
616}
617
618#[cfg(feature = "arbitrary")]
619impl<'a> Arbitrary<'a> for Amount {
620    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
621        let sats = u.int_in_range(Self::MIN.to_sat()..=Self::MAX.to_sat())?;
622        Ok(Self::from_sat(sats).expect("range is valid"))
623    }
624}