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}