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