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