bitcoin_units/
amount.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bitcoin amounts.
4//!
5//! This module mainly introduces the [`Amount`] and [`SignedAmount`] types.
6//! We refer to the documentation on the types for more information.
7
8#[cfg(feature = "alloc")]
9use alloc::string::{String, ToString};
10use core::cmp::Ordering;
11use core::str::FromStr;
12use core::{default, fmt, ops};
13
14#[cfg(feature = "serde")]
15use ::serde::{Deserialize, Serialize};
16#[cfg(feature = "arbitrary")]
17use arbitrary::{Arbitrary, Unstructured};
18use internals::error::InputString;
19use internals::write_err;
20
21/// A set of denominations in which amounts can be expressed.
22///
23/// # Accepted Denominations
24///
25/// All upper or lower case, excluding SI prefix (c, m, u) which must be lower case.
26/// - Singular: BTC, cBTC, mBTC, uBTC
27/// - Plural or singular: sat, satoshi, bit
28///
29/// # Note
30///
31/// Due to ambiguity between mega and milli we prohibit usage of leading capital 'M'.  It is
32/// more important to protect users from incorrectly using a capital M to mean milli than to
33/// allow Megabitcoin which is not a realistic denomination, and Megasatoshi which is
34/// equivalent to cBTC which is allowed.
35///
36/// # Examples
37///
38/// ```
39/// # use bitcoin_units::Amount;
40///
41/// assert_eq!("1 BTC".parse::<Amount>().unwrap(), Amount::from_sat(100_000_000));
42/// assert_eq!("1 cBTC".parse::<Amount>().unwrap(), Amount::from_sat(1_000_000));
43/// assert_eq!("1 mBTC".parse::<Amount>().unwrap(), Amount::from_sat(100_000));
44/// assert_eq!("1 uBTC".parse::<Amount>().unwrap(), Amount::from_sat(100));
45/// assert_eq!("1 bit".parse::<Amount>().unwrap(), Amount::from_sat(100));
46/// assert_eq!("1 sat".parse::<Amount>().unwrap(), Amount::from_sat(1));
47/// ```
48#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
49#[non_exhaustive]
50pub enum Denomination {
51    /// BTC
52    Bitcoin,
53    /// cBTC
54    CentiBitcoin,
55    /// mBTC
56    MilliBitcoin,
57    /// uBTC
58    MicroBitcoin,
59    /// bits
60    Bit,
61    /// satoshi
62    Satoshi,
63}
64
65impl Denomination {
66    /// Convenience alias for `Denomination::Bitcoin`.
67    pub const BTC: Self = Denomination::Bitcoin;
68
69    /// Convenience alias for `Denomination::Satoshi`.
70    pub const SAT: Self = Denomination::Satoshi;
71
72    /// The number of decimal places more than a satoshi.
73    fn precision(self) -> i8 {
74        match self {
75            Denomination::Bitcoin => -8,
76            Denomination::CentiBitcoin => -6,
77            Denomination::MilliBitcoin => -5,
78            Denomination::MicroBitcoin => -2,
79            Denomination::Bit => -2,
80            Denomination::Satoshi => 0,
81        }
82    }
83
84    /// Returns a string representation of this denomination.
85    fn as_str(self) -> &'static str {
86        match self {
87            Denomination::Bitcoin => "BTC",
88            Denomination::CentiBitcoin => "cBTC",
89            Denomination::MilliBitcoin => "mBTC",
90            Denomination::MicroBitcoin => "uBTC",
91            Denomination::Bit => "bits",
92            Denomination::Satoshi => "satoshi",
93        }
94    }
95
96    /// The different `str` forms of denominations that are recognized.
97    fn forms(s: &str) -> Option<Self> {
98        match s {
99            "BTC" | "btc" => Some(Denomination::Bitcoin),
100            "cBTC" | "cbtc" => Some(Denomination::CentiBitcoin),
101            "mBTC" | "mbtc" => Some(Denomination::MilliBitcoin),
102            "uBTC" | "ubtc" => Some(Denomination::MicroBitcoin),
103            "bit" | "bits" | "BIT" | "BITS" => Some(Denomination::Bit),
104            "SATOSHI" | "satoshi" | "SATOSHIS" | "satoshis" | "SAT" | "sat" | "SATS" | "sats" =>
105                Some(Denomination::Satoshi),
106            _ => None,
107        }
108    }
109}
110
111/// These form are ambigous and could have many meanings.  For example, M could denote Mega or Milli.
112/// If any of these forms are used, an error type PossiblyConfusingDenomination is returned.
113const CONFUSING_FORMS: [&str; 6] = ["MBTC", "Mbtc", "CBTC", "Cbtc", "UBTC", "Ubtc"];
114
115impl fmt::Display for Denomination {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.as_str()) }
117}
118
119impl FromStr for Denomination {
120    type Err = ParseDenominationError;
121
122    /// Converts from a `str` to a `Denomination`.
123    ///
124    /// # Errors
125    ///
126    /// - If the denomination begins with a capital `M` a [`PossiblyConfusingDenominationError`] is
127    ///   returned.
128    ///
129    /// - If an unknown denomination is used, an [`UnknownDenominationError`] is returned.
130    fn from_str(s: &str) -> Result<Self, Self::Err> {
131        use self::ParseDenominationError::*;
132
133        if CONFUSING_FORMS.contains(&s) {
134            return Err(PossiblyConfusing(PossiblyConfusingDenominationError(s.into())));
135        };
136
137        let form = self::Denomination::forms(s);
138
139        form.ok_or_else(|| Unknown(UnknownDenominationError(s.into())))
140    }
141}
142
143/// An error during amount parsing amount with denomination.
144#[derive(Debug, Clone, PartialEq, Eq)]
145#[non_exhaustive]
146pub enum ParseError {
147    /// Invalid amount.
148    Amount(ParseAmountError),
149
150    /// Invalid denomination.
151    Denomination(ParseDenominationError),
152
153    /// The denomination was not identified.
154    MissingDenomination(MissingDenominationError),
155}
156
157internals::impl_from_infallible!(ParseError);
158
159impl From<ParseAmountError> for ParseError {
160    fn from(e: ParseAmountError) -> Self { Self::Amount(e) }
161}
162
163impl From<ParseDenominationError> for ParseError {
164    fn from(e: ParseDenominationError) -> Self { Self::Denomination(e) }
165}
166
167impl From<OutOfRangeError> for ParseError {
168    fn from(e: OutOfRangeError) -> Self { Self::Amount(e.into()) }
169}
170
171impl From<TooPreciseError> for ParseError {
172    fn from(e: TooPreciseError) -> Self { Self::Amount(e.into()) }
173}
174
175impl From<MissingDigitsError> for ParseError {
176    fn from(e: MissingDigitsError) -> Self { Self::Amount(e.into()) }
177}
178
179impl From<InputTooLargeError> for ParseError {
180    fn from(e: InputTooLargeError) -> Self { Self::Amount(e.into()) }
181}
182
183impl From<InvalidCharacterError> for ParseError {
184    fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) }
185}
186
187impl fmt::Display for ParseError {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        match self {
190            ParseError::Amount(error) => write_err!(f, "invalid amount"; error),
191            ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
192            // We consider this to not be a source because it currently doesn't contain useful
193            // information
194            ParseError::MissingDenomination(_) =>
195                f.write_str("the input doesn't contain a denomination"),
196        }
197    }
198}
199
200#[cfg(feature = "std")]
201impl std::error::Error for ParseError {
202    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
203        match self {
204            ParseError::Amount(error) => Some(error),
205            ParseError::Denomination(error) => Some(error),
206            // We consider this to not be a source because it currently doesn't contain useful
207            // information
208            ParseError::MissingDenomination(_) => None,
209        }
210    }
211}
212
213/// An error during amount parsing.
214#[derive(Debug, Clone, PartialEq, Eq)]
215#[non_exhaustive]
216pub enum ParseAmountError {
217    /// The amount is too big or too small.
218    OutOfRange(OutOfRangeError),
219    /// Amount has higher precision than supported by the type.
220    TooPrecise(TooPreciseError),
221    /// A digit was expected but not found.
222    MissingDigits(MissingDigitsError),
223    /// Input string was too large.
224    InputTooLarge(InputTooLargeError),
225    /// Invalid character in input.
226    InvalidCharacter(InvalidCharacterError),
227}
228
229impl From<TooPreciseError> for ParseAmountError {
230    fn from(value: TooPreciseError) -> Self { Self::TooPrecise(value) }
231}
232
233impl From<MissingDigitsError> for ParseAmountError {
234    fn from(value: MissingDigitsError) -> Self { Self::MissingDigits(value) }
235}
236
237impl From<InputTooLargeError> for ParseAmountError {
238    fn from(value: InputTooLargeError) -> Self { Self::InputTooLarge(value) }
239}
240
241impl From<InvalidCharacterError> for ParseAmountError {
242    fn from(value: InvalidCharacterError) -> Self { Self::InvalidCharacter(value) }
243}
244
245internals::impl_from_infallible!(ParseAmountError);
246
247impl fmt::Display for ParseAmountError {
248    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249        use ParseAmountError::*;
250
251        match *self {
252            OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
253            TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
254            MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
255            InputTooLarge(ref error) => write_err!(f, "the input is too large"; error),
256            InvalidCharacter(ref error) => write_err!(f, "invalid character in the input"; error),
257        }
258    }
259}
260
261#[cfg(feature = "std")]
262impl std::error::Error for ParseAmountError {
263    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
264        use ParseAmountError::*;
265
266        match *self {
267            TooPrecise(ref error) => Some(error),
268            InputTooLarge(ref error) => Some(error),
269            OutOfRange(ref error) => Some(error),
270            MissingDigits(ref error) => Some(error),
271            InvalidCharacter(ref error) => Some(error),
272        }
273    }
274}
275
276/// Error returned when a parsed amount is too big or too small.
277#[derive(Debug, Copy, Clone, Eq, PartialEq)]
278pub struct OutOfRangeError {
279    is_signed: bool,
280    is_greater_than_max: bool,
281}
282
283impl OutOfRangeError {
284    /// Returns the minimum and maximum allowed values for the type that was parsed.
285    ///
286    /// This can be used to give a hint to the user which values are allowed.
287    pub fn valid_range(&self) -> (i64, u64) {
288        match self.is_signed {
289            true => (i64::MIN, i64::MAX as u64),
290            false => (0, u64::MAX),
291        }
292    }
293
294    /// Returns true if the input value was large than the maximum allowed value.
295    pub fn is_above_max(&self) -> bool { self.is_greater_than_max }
296
297    /// Returns true if the input value was smaller than the minimum allowed value.
298    pub fn is_below_min(&self) -> bool { !self.is_greater_than_max }
299
300    pub(crate) fn too_big(is_signed: bool) -> Self { Self { is_signed, is_greater_than_max: true } }
301
302    pub(crate) fn too_small() -> Self {
303        Self {
304            // implied - negative() is used for the other
305            is_signed: true,
306            is_greater_than_max: false,
307        }
308    }
309
310    pub(crate) fn negative() -> Self {
311        Self {
312            // implied - too_small() is used for the other
313            is_signed: false,
314            is_greater_than_max: false,
315        }
316    }
317}
318
319impl fmt::Display for OutOfRangeError {
320    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
321        if self.is_greater_than_max {
322            write!(f, "the amount is greater than {}", self.valid_range().1)
323        } else {
324            write!(f, "the amount is less than {}", self.valid_range().0)
325        }
326    }
327}
328
329#[cfg(feature = "std")]
330impl std::error::Error for OutOfRangeError {}
331
332impl From<OutOfRangeError> for ParseAmountError {
333    fn from(value: OutOfRangeError) -> Self { ParseAmountError::OutOfRange(value) }
334}
335
336/// Error returned when the input string has higher precision than satoshis.
337#[derive(Debug, Clone, Eq, PartialEq)]
338pub struct TooPreciseError {
339    position: usize,
340}
341
342impl fmt::Display for TooPreciseError {
343    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344        match self.position {
345            0 => f.write_str("the amount is less than 1 satoshi but it's not zero"),
346            pos => write!(
347                f,
348                "the digits starting from position {} represent a sub-satoshi amount",
349                pos
350            ),
351        }
352    }
353}
354
355#[cfg(feature = "std")]
356impl std::error::Error for TooPreciseError {}
357
358/// Error returned when the input string is too large.
359#[derive(Debug, Clone, Eq, PartialEq)]
360pub struct InputTooLargeError {
361    len: usize,
362}
363
364impl fmt::Display for InputTooLargeError {
365    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366        match self.len - INPUT_STRING_LEN_LIMIT {
367            1 => write!(
368                f,
369                "the input is one character longer than the maximum allowed length ({})",
370                INPUT_STRING_LEN_LIMIT
371            ),
372            n => write!(
373                f,
374                "the input is {} characters longer than the maximum allowed length ({})",
375                n, INPUT_STRING_LEN_LIMIT
376            ),
377        }
378    }
379}
380
381#[cfg(feature = "std")]
382impl std::error::Error for InputTooLargeError {}
383
384/// Error returned when digits were expected in the input but there were none.
385///
386/// In particular, this is currently returned when the string is empty or only contains the minus sign.
387#[derive(Debug, Clone, Eq, PartialEq)]
388pub struct MissingDigitsError {
389    kind: MissingDigitsKind,
390}
391
392impl fmt::Display for MissingDigitsError {
393    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
394        match self.kind {
395            MissingDigitsKind::Empty => f.write_str("the input is empty"),
396            MissingDigitsKind::OnlyMinusSign =>
397                f.write_str("there are no digits following the minus (-) sign"),
398        }
399    }
400}
401
402#[cfg(feature = "std")]
403impl std::error::Error for MissingDigitsError {}
404
405#[derive(Debug, Clone, Eq, PartialEq)]
406enum MissingDigitsKind {
407    Empty,
408    OnlyMinusSign,
409}
410
411/// Error returned when the input contains an invalid character.
412#[derive(Debug, Clone, PartialEq, Eq)]
413pub struct InvalidCharacterError {
414    invalid_char: char,
415    position: usize,
416}
417
418impl fmt::Display for InvalidCharacterError {
419    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
420        match self.invalid_char {
421            '.' => f.write_str("there is more than one decimal separator (dot) in the input"),
422            '-' => f.write_str("there is more than one minus sign (-) in the input"),
423            c => write!(
424                f,
425                "the character '{}' at position {} is not a valid digit",
426                c, self.position
427            ),
428        }
429    }
430}
431
432#[cfg(feature = "std")]
433impl std::error::Error for InvalidCharacterError {}
434
435/// An error during amount parsing.
436#[derive(Debug, Clone, PartialEq, Eq)]
437#[non_exhaustive]
438pub enum ParseDenominationError {
439    /// The denomination was unknown.
440    Unknown(UnknownDenominationError),
441    /// The denomination has multiple possible interpretations.
442    PossiblyConfusing(PossiblyConfusingDenominationError),
443}
444
445internals::impl_from_infallible!(ParseDenominationError);
446
447impl fmt::Display for ParseDenominationError {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        use ParseDenominationError::*;
450
451        match *self {
452            Unknown(ref e) => write_err!(f, "denomination parse error"; e),
453            PossiblyConfusing(ref e) => write_err!(f, "denomination parse error"; e),
454        }
455    }
456}
457
458#[cfg(feature = "std")]
459impl std::error::Error for ParseDenominationError {
460    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
461        use ParseDenominationError::*;
462
463        match *self {
464            Unknown(_) | PossiblyConfusing(_) => None,
465        }
466    }
467}
468
469/// Error returned when the denomination is empty.
470#[derive(Debug, Clone, PartialEq, Eq)]
471#[non_exhaustive]
472pub struct MissingDenominationError;
473
474/// Error returned when parsing an unknown denomination.
475#[derive(Debug, Clone, PartialEq, Eq)]
476#[non_exhaustive]
477pub struct UnknownDenominationError(InputString);
478
479impl fmt::Display for UnknownDenominationError {
480    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481        self.0.unknown_variant("bitcoin denomination", f)
482    }
483}
484
485#[cfg(feature = "std")]
486impl std::error::Error for UnknownDenominationError {
487    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
488}
489
490/// Error returned when parsing a possibly confusing denomination.
491#[derive(Debug, Clone, PartialEq, Eq)]
492#[non_exhaustive]
493pub struct PossiblyConfusingDenominationError(InputString);
494
495impl fmt::Display for PossiblyConfusingDenominationError {
496    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497        write!(f, "{}: possibly confusing denomination - we intentionally do not support 'M' and 'P' so as to not confuse mega/milli and peta/pico", self.0.display_cannot_parse("bitcoin denomination"))
498    }
499}
500
501#[cfg(feature = "std")]
502impl std::error::Error for PossiblyConfusingDenominationError {
503    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
504}
505
506/// Returns `Some(position)` if the precision is not supported.
507///
508/// The position indicates the first digit that is too precise.
509fn is_too_precise(s: &str, precision: usize) -> Option<usize> {
510    match s.find('.') {
511        Some(pos) if precision >= pos => Some(0),
512        Some(pos) => s[..pos]
513            .char_indices()
514            .rev()
515            .take(precision)
516            .find(|(_, d)| *d != '0')
517            .map(|(i, _)| i)
518            .or_else(|| {
519                s[(pos + 1)..].char_indices().find(|(_, d)| *d != '0').map(|(i, _)| i + pos + 1)
520            }),
521        None if precision >= s.len() => Some(0),
522        None => s.char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i),
523    }
524}
525
526const INPUT_STRING_LEN_LIMIT: usize = 50;
527
528/// Parses a decimal string in the given denomination into a satoshi value and a
529/// [`bool`] indicator for a negative amount.
530fn parse_signed_to_satoshi(
531    mut s: &str,
532    denom: Denomination,
533) -> Result<(bool, u64), InnerParseError> {
534    if s.is_empty() {
535        return Err(InnerParseError::MissingDigits(MissingDigitsError {
536            kind: MissingDigitsKind::Empty,
537        }));
538    }
539    if s.len() > INPUT_STRING_LEN_LIMIT {
540        return Err(InnerParseError::InputTooLarge(s.len()));
541    }
542
543    let is_negative = s.starts_with('-');
544    if is_negative {
545        if s.len() == 1 {
546            return Err(InnerParseError::MissingDigits(MissingDigitsError {
547                kind: MissingDigitsKind::OnlyMinusSign,
548            }));
549        }
550        s = &s[1..];
551    }
552
553    let max_decimals = {
554        // The difference in precision between native (satoshi)
555        // and desired denomination.
556        let precision_diff = -denom.precision();
557        if precision_diff <= 0 {
558            // If precision diff is negative, this means we are parsing
559            // into a less precise amount. That is not allowed unless
560            // there are no decimals and the last digits are zeroes as
561            // many as the difference in precision.
562            let last_n = precision_diff.unsigned_abs().into();
563            if let Some(position) = is_too_precise(s, last_n) {
564                match s.parse::<i64>() {
565                    Ok(0) => return Ok((is_negative, 0)),
566                    _ =>
567                        return Err(InnerParseError::TooPrecise(TooPreciseError {
568                            position: position + is_negative as usize,
569                        })),
570                }
571            }
572            s = &s[0..s.find('.').unwrap_or(s.len()) - last_n];
573            0
574        } else {
575            precision_diff
576        }
577    };
578
579    let mut decimals = None;
580    let mut value: u64 = 0; // as satoshis
581    for (i, c) in s.char_indices() {
582        match c {
583            '0'..='9' => {
584                // Do `value = 10 * value + digit`, catching overflows.
585                match 10_u64.checked_mul(value) {
586                    None => return Err(InnerParseError::Overflow { is_negative }),
587                    Some(val) => match val.checked_add((c as u8 - b'0') as u64) {
588                        None => return Err(InnerParseError::Overflow { is_negative }),
589                        Some(val) => value = val,
590                    },
591                }
592                // Increment the decimal digit counter if past decimal.
593                decimals = match decimals {
594                    None => None,
595                    Some(d) if d < max_decimals => Some(d + 1),
596                    _ =>
597                        return Err(InnerParseError::TooPrecise(TooPreciseError {
598                            position: i + is_negative as usize,
599                        })),
600                };
601            }
602            '.' => match decimals {
603                None if max_decimals <= 0 => break,
604                None => decimals = Some(0),
605                // Double decimal dot.
606                _ =>
607                    return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
608                        invalid_char: '.',
609                        position: i + is_negative as usize,
610                    })),
611            },
612            c =>
613                return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
614                    invalid_char: c,
615                    position: i + is_negative as usize,
616                })),
617        }
618    }
619
620    // Decimally shift left by `max_decimals - decimals`.
621    let scale_factor = max_decimals - decimals.unwrap_or(0);
622    for _ in 0..scale_factor {
623        value = match 10_u64.checked_mul(value) {
624            Some(v) => v,
625            None => return Err(InnerParseError::Overflow { is_negative }),
626        };
627    }
628
629    Ok((is_negative, value))
630}
631
632enum InnerParseError {
633    Overflow { is_negative: bool },
634    TooPrecise(TooPreciseError),
635    MissingDigits(MissingDigitsError),
636    InputTooLarge(usize),
637    InvalidCharacter(InvalidCharacterError),
638}
639
640internals::impl_from_infallible!(InnerParseError);
641
642impl InnerParseError {
643    fn convert(self, is_signed: bool) -> ParseAmountError {
644        match self {
645            Self::Overflow { is_negative } =>
646                OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
647            Self::TooPrecise(error) => ParseAmountError::TooPrecise(error),
648            Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
649            Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
650            Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
651        }
652    }
653}
654
655fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseError> {
656    let (i, j) = if let Some(i) = s.find(' ') {
657        (i, i + 1)
658    } else {
659        let i = s
660            .find(|c: char| c.is_alphabetic())
661            .ok_or(ParseError::MissingDenomination(MissingDenominationError))?;
662        (i, i)
663    };
664    Ok((&s[..i], s[j..].parse()?))
665}
666
667/// Options given by `fmt::Formatter`
668struct FormatOptions {
669    fill: char,
670    align: Option<fmt::Alignment>,
671    width: Option<usize>,
672    precision: Option<usize>,
673    sign_plus: bool,
674    sign_aware_zero_pad: bool,
675}
676
677impl FormatOptions {
678    fn from_formatter(f: &fmt::Formatter) -> Self {
679        FormatOptions {
680            fill: f.fill(),
681            align: f.align(),
682            width: f.width(),
683            precision: f.precision(),
684            sign_plus: f.sign_plus(),
685            sign_aware_zero_pad: f.sign_aware_zero_pad(),
686        }
687    }
688}
689
690impl Default for FormatOptions {
691    fn default() -> Self {
692        FormatOptions {
693            fill: ' ',
694            align: None,
695            width: None,
696            precision: None,
697            sign_plus: false,
698            sign_aware_zero_pad: false,
699        }
700    }
701}
702
703fn dec_width(mut num: u64) -> usize {
704    let mut width = 1;
705    loop {
706        num /= 10;
707        if num == 0 {
708            break;
709        }
710        width += 1;
711    }
712    width
713}
714
715fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
716    for _ in 0..count {
717        f.write_char(c)?;
718    }
719    Ok(())
720}
721
722/// Format the given satoshi amount in the given denomination.
723fn fmt_satoshi_in(
724    mut satoshi: u64,
725    negative: bool,
726    f: &mut dyn fmt::Write,
727    denom: Denomination,
728    show_denom: bool,
729    options: FormatOptions,
730) -> fmt::Result {
731    let precision = denom.precision();
732    // First we normalize the number:
733    // {num_before_decimal_point}{:0exp}{"." if nb_decimals > 0}{:0nb_decimals}{num_after_decimal_point}{:0trailing_decimal_zeros}
734    let mut num_after_decimal_point = 0;
735    let mut norm_nb_decimals = 0;
736    let mut num_before_decimal_point = satoshi;
737    let trailing_decimal_zeros;
738    let mut exp = 0;
739    match precision.cmp(&0) {
740        // We add the number of zeroes to the end
741        Ordering::Greater => {
742            if satoshi > 0 {
743                exp = precision as usize;
744            }
745            trailing_decimal_zeros = options.precision.unwrap_or(0);
746        }
747        Ordering::Less => {
748            let precision = precision.unsigned_abs();
749            // round the number if needed
750            // rather than fiddling with chars, we just modify satoshi and let the simpler algorithm take over.
751            if let Some(format_precision) = options.precision {
752                if usize::from(precision) > format_precision {
753                    // precision is u8 so in this branch options.precision() < 255 which fits in u32
754                    let rounding_divisor =
755                        10u64.pow(u32::from(precision) - format_precision as u32);
756                    let remainder = satoshi % rounding_divisor;
757                    satoshi -= remainder;
758                    if remainder / (rounding_divisor / 10) >= 5 {
759                        satoshi += rounding_divisor;
760                    }
761                }
762            }
763            let divisor = 10u64.pow(precision.into());
764            num_before_decimal_point = satoshi / divisor;
765            num_after_decimal_point = satoshi % divisor;
766            // normalize by stripping trailing zeros
767            if num_after_decimal_point == 0 {
768                norm_nb_decimals = 0;
769            } else {
770                norm_nb_decimals = usize::from(precision);
771                while num_after_decimal_point % 10 == 0 {
772                    norm_nb_decimals -= 1;
773                    num_after_decimal_point /= 10
774                }
775            }
776            // compute requested precision
777            let opt_precision = options.precision.unwrap_or(0);
778            trailing_decimal_zeros = opt_precision.saturating_sub(norm_nb_decimals);
779        }
780        Ordering::Equal => trailing_decimal_zeros = options.precision.unwrap_or(0),
781    }
782    let total_decimals = norm_nb_decimals + trailing_decimal_zeros;
783    // Compute expected width of the number
784    let mut num_width = if total_decimals > 0 {
785        // 1 for decimal point
786        1 + total_decimals
787    } else {
788        0
789    };
790    num_width += dec_width(num_before_decimal_point) + exp;
791    if options.sign_plus || negative {
792        num_width += 1;
793    }
794
795    if show_denom {
796        // + 1 for space
797        num_width += denom.as_str().len() + 1;
798    }
799
800    let width = options.width.unwrap_or(0);
801    let align = options.align.unwrap_or(fmt::Alignment::Right);
802    let (left_pad, pad_right) = match (num_width < width, options.sign_aware_zero_pad, align) {
803        (false, _, _) => (0, 0),
804        // Alignment is always right (ignored) when zero-padding
805        (true, true, _) | (true, false, fmt::Alignment::Right) => (width - num_width, 0),
806        (true, false, fmt::Alignment::Left) => (0, width - num_width),
807        // If the required padding is odd it needs to be skewed to the left
808        (true, false, fmt::Alignment::Center) =>
809            ((width - num_width) / 2, (width - num_width + 1) / 2),
810    };
811
812    if !options.sign_aware_zero_pad {
813        repeat_char(f, options.fill, left_pad)?;
814    }
815
816    if negative {
817        write!(f, "-")?;
818    } else if options.sign_plus {
819        write!(f, "+")?;
820    }
821
822    if options.sign_aware_zero_pad {
823        repeat_char(f, '0', left_pad)?;
824    }
825
826    write!(f, "{}", num_before_decimal_point)?;
827
828    repeat_char(f, '0', exp)?;
829
830    if total_decimals > 0 {
831        write!(f, ".")?;
832    }
833    if norm_nb_decimals > 0 {
834        write!(f, "{:0width$}", num_after_decimal_point, width = norm_nb_decimals)?;
835    }
836    repeat_char(f, '0', trailing_decimal_zeros)?;
837
838    if show_denom {
839        write!(f, " {}", denom.as_str())?;
840    }
841
842    repeat_char(f, options.fill, pad_right)?;
843    Ok(())
844}
845
846/// An amount.
847///
848/// The [`Amount`] type can be used to express Bitcoin amounts that support
849/// arithmetic and conversion to various denominations.
850///
851///
852/// Warning!
853///
854/// This type implements several arithmetic operations from [`core::ops`].
855/// To prevent errors due to overflow or underflow when using these operations,
856/// it is advised to instead use the checked arithmetic methods whose names
857/// start with `checked_`.  The operations from [`core::ops`] that [`Amount`]
858/// implements will panic when overflow or underflow occurs.  Also note that
859/// since the internal representation of amounts is unsigned, subtracting below
860/// zero is considered an underflow and will cause a panic if you're not using
861/// the checked arithmetic methods.
862///
863#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
864#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
865pub struct Amount(u64);
866
867impl Amount {
868    /// The zero amount.
869    pub const ZERO: Amount = Amount(0);
870    /// Exactly one satoshi.
871    pub const ONE_SAT: Amount = Amount(1);
872    /// Exactly one bitcoin.
873    pub const ONE_BTC: Amount = Self::from_int_btc(1);
874    /// The maximum value allowed as an amount. Useful for sanity checking.
875    pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000);
876    /// The minimum value of an amount.
877    pub const MIN: Amount = Amount::ZERO;
878    /// The maximum value of an amount.
879    pub const MAX: Amount = Amount(u64::MAX);
880    /// The number of bytes that an amount contributes to the size of a transaction.
881    pub const SIZE: usize = 8; // Serialized length of a u64.
882
883    /// Creates an [`Amount`] with satoshi precision and the given number of satoshis.
884    pub const fn from_sat(satoshi: u64) -> Amount { Amount(satoshi) }
885
886    /// Gets the number of satoshis in this [`Amount`].
887    pub fn to_sat(self) -> u64 { self.0 }
888
889    /// Converts from a value expressing bitcoins to an [`Amount`].
890    #[cfg(feature = "alloc")]
891    pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
892        Amount::from_float_in(btc, Denomination::Bitcoin)
893    }
894
895    /// Converts from a value expressing integer values of bitcoins to an [`Amount`]
896    /// in const context.
897    ///
898    /// # Panics
899    ///
900    /// The function panics if the argument multiplied by the number of sats
901    /// per bitcoin overflows a u64 type.
902    pub const fn from_int_btc(btc: u64) -> Amount {
903        match btc.checked_mul(100_000_000) {
904            Some(amount) => Amount::from_sat(amount),
905            None => panic!("checked_mul overflowed"),
906        }
907    }
908
909    /// Parses a decimal string as a value in the given denomination.
910    ///
911    /// Note: This only parses the value string.  If you want to parse a value
912    /// with denomination, use [`FromStr`].
913    pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
914        let (negative, satoshi) =
915            parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
916        if negative {
917            return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
918        }
919        Ok(Amount::from_sat(satoshi))
920    }
921
922    /// Parses amounts with denomination suffix like they are produced with
923    /// [`Self::to_string_with_denomination`] or with [`fmt::Display`].
924    /// If you want to parse only the amount without the denomination,
925    /// use [`Self::from_str_in`].
926    pub fn from_str_with_denomination(s: &str) -> Result<Amount, ParseError> {
927        let (amt, denom) = split_amount_and_denomination(s)?;
928        Amount::from_str_in(amt, denom).map_err(Into::into)
929    }
930
931    /// Expresses this [`Amount`] as a floating-point value in the given denomination.
932    ///
933    /// Please be aware of the risk of using floating-point numbers.
934    #[cfg(feature = "alloc")]
935    pub fn to_float_in(self, denom: Denomination) -> f64 {
936        self.to_string_in(denom).parse::<f64>().unwrap()
937    }
938
939    /// Expresses this [`Amount`] as a floating-point value in Bitcoin.
940    ///
941    /// Please be aware of the risk of using floating-point numbers.
942    ///
943    /// # Examples
944    ///
945    /// ```
946    /// # use bitcoin_units::amount::{Amount, Denomination};
947    /// let amount = Amount::from_sat(100_000);
948    /// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin))
949    /// ```
950    #[cfg(feature = "alloc")]
951    pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
952
953    /// Converts this [`Amount`] in floating-point notation with a given
954    /// denomination.
955    ///
956    /// # Errors
957    ///
958    /// If the amount is too big, too precise or negative.
959    ///
960    /// Please be aware of the risk of using floating-point numbers.
961    #[cfg(feature = "alloc")]
962    pub fn from_float_in(value: f64, denom: Denomination) -> Result<Amount, ParseAmountError> {
963        if value < 0.0 {
964            return Err(OutOfRangeError::negative().into());
965        }
966        // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
967        // Any performance-critical application should not be dealing with floats.
968        Amount::from_str_in(&value.to_string(), denom)
969    }
970
971    /// Creates an object that implements [`fmt::Display`] using specified denomination.
972    pub fn display_in(self, denomination: Denomination) -> Display {
973        Display {
974            sats_abs: self.to_sat(),
975            is_negative: false,
976            style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
977        }
978    }
979
980    /// Creates an object that implements [`fmt::Display`] dynamically selecting denomination.
981    ///
982    /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
983    /// avoid confusion the denomination is always shown.
984    pub fn display_dynamic(self) -> Display {
985        Display {
986            sats_abs: self.to_sat(),
987            is_negative: false,
988            style: DisplayStyle::DynamicDenomination,
989        }
990    }
991
992    /// Formats the value of this [`Amount`] in the given denomination.
993    ///
994    /// Does not include the denomination.
995    #[rustfmt::skip]
996    #[deprecated(since = "TBD", note = "Use `display_in()` instead")]
997    pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
998        fmt_satoshi_in(self.to_sat(), false, f, denom, false, FormatOptions::default())
999    }
1000
1001    /// Returns a formatted string of this [`Amount`] in the given denomination.
1002    ///
1003    /// Does not include the denomination.
1004    #[cfg(feature = "alloc")]
1005    pub fn to_string_in(self, denom: Denomination) -> String { self.display_in(denom).to_string() }
1006
1007    /// Returns a formatted string of this [`Amount`] in the given denomination,
1008    /// suffixed with the abbreviation for the denomination.
1009    #[cfg(feature = "alloc")]
1010    pub fn to_string_with_denomination(self, denom: Denomination) -> String {
1011        self.display_in(denom).show_denomination().to_string()
1012    }
1013
1014    // Some arithmetic that doesn't fit in [`core::ops`] traits.
1015
1016    /// Checked addition.
1017    ///
1018    /// Returns [`None`] if overflow occurred.
1019    pub fn checked_add(self, rhs: Amount) -> Option<Amount> {
1020        self.0.checked_add(rhs.0).map(Amount)
1021    }
1022
1023    /// Checked subtraction.
1024    ///
1025    /// Returns [`None`] if overflow occurred.
1026    pub fn checked_sub(self, rhs: Amount) -> Option<Amount> {
1027        self.0.checked_sub(rhs.0).map(Amount)
1028    }
1029
1030    /// Checked multiplication.
1031    ///
1032    /// Returns [`None`] if overflow occurred.
1033    pub fn checked_mul(self, rhs: u64) -> Option<Amount> { self.0.checked_mul(rhs).map(Amount) }
1034
1035    /// Checked integer division.
1036    ///
1037    /// Be aware that integer division loses the remainder if no exact division
1038    /// can be made.
1039    /// Returns [`None`] if overflow occurred.
1040    pub fn checked_div(self, rhs: u64) -> Option<Amount> { self.0.checked_div(rhs).map(Amount) }
1041
1042    /// Checked remainder.
1043    ///
1044    /// Returns [`None`] if overflow occurred.
1045    pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) }
1046
1047    /// Unchecked addition.
1048    ///
1049    /// Computes `self + rhs`.
1050    ///
1051    /// # Panics
1052    ///
1053    /// On overflow, panics in debug mode, wraps in release mode.
1054    pub fn unchecked_add(self, rhs: Amount) -> Amount { Self(self.0 + rhs.0) }
1055
1056    /// Unchecked subtraction.
1057    ///
1058    /// Computes `self - rhs`.
1059    ///
1060    /// # Panics
1061    ///
1062    /// On overflow, panics in debug mode, wraps in release mode.
1063    pub fn unchecked_sub(self, rhs: Amount) -> Amount { Self(self.0 - rhs.0) }
1064
1065    /// Converts to a signed amount.
1066    pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
1067        if self.to_sat() > SignedAmount::MAX.to_sat() as u64 {
1068            Err(OutOfRangeError::too_big(true))
1069        } else {
1070            Ok(SignedAmount::from_sat(self.to_sat() as i64))
1071        }
1072    }
1073}
1074
1075#[cfg(feature = "arbitrary")]
1076impl<'a> Arbitrary<'a> for Amount {
1077    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
1078        let a = u64::arbitrary(u)?;
1079        Ok(Amount(a))
1080    }
1081}
1082
1083impl default::Default for Amount {
1084    fn default() -> Self { Amount::ZERO }
1085}
1086
1087impl fmt::Debug for Amount {
1088    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} SAT", self.to_sat()) }
1089}
1090
1091// No one should depend on a binding contract for Display for this type.
1092// Just using Bitcoin denominated string.
1093impl fmt::Display for Amount {
1094    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1095        fmt::Display::fmt(&self.display_in(Denomination::Bitcoin).show_denomination(), f)
1096    }
1097}
1098
1099impl ops::Add for Amount {
1100    type Output = Amount;
1101
1102    fn add(self, rhs: Amount) -> Self::Output {
1103        self.checked_add(rhs).expect("Amount addition error")
1104    }
1105}
1106
1107impl ops::AddAssign for Amount {
1108    fn add_assign(&mut self, other: Amount) { *self = *self + other }
1109}
1110
1111impl ops::Sub for Amount {
1112    type Output = Amount;
1113
1114    fn sub(self, rhs: Amount) -> Self::Output {
1115        self.checked_sub(rhs).expect("Amount subtraction error")
1116    }
1117}
1118
1119impl ops::SubAssign for Amount {
1120    fn sub_assign(&mut self, other: Amount) { *self = *self - other }
1121}
1122
1123impl ops::Rem<u64> for Amount {
1124    type Output = Amount;
1125
1126    fn rem(self, modulus: u64) -> Self {
1127        self.checked_rem(modulus).expect("Amount remainder error")
1128    }
1129}
1130
1131impl ops::RemAssign<u64> for Amount {
1132    fn rem_assign(&mut self, modulus: u64) { *self = *self % modulus }
1133}
1134
1135impl ops::Mul<u64> for Amount {
1136    type Output = Amount;
1137
1138    fn mul(self, rhs: u64) -> Self::Output {
1139        self.checked_mul(rhs).expect("Amount multiplication error")
1140    }
1141}
1142
1143impl ops::MulAssign<u64> for Amount {
1144    fn mul_assign(&mut self, rhs: u64) { *self = *self * rhs }
1145}
1146
1147impl ops::Div<u64> for Amount {
1148    type Output = Amount;
1149
1150    fn div(self, rhs: u64) -> Self::Output { self.checked_div(rhs).expect("Amount division error") }
1151}
1152
1153impl ops::DivAssign<u64> for Amount {
1154    fn div_assign(&mut self, rhs: u64) { *self = *self / rhs }
1155}
1156
1157impl FromStr for Amount {
1158    type Err = ParseError;
1159
1160    /// Parses a string slice where the slice includes a denomination.
1161    ///
1162    /// If the string slice is zero, then no denomination is required.
1163    ///
1164    /// # Returns
1165    ///
1166    /// `Ok(Amount)` if the string amount and denomination parse successfully,
1167    /// otherwise, return `Err(ParseError)`.
1168    fn from_str(s: &str) -> Result<Self, Self::Err> {
1169        let result = Amount::from_str_with_denomination(s);
1170
1171        match result {
1172            Err(ParseError::MissingDenomination(_)) => {
1173                let d = Amount::from_str_in(s, Denomination::Satoshi);
1174
1175                if d == Ok(Amount::ZERO) {
1176                    Ok(Amount::ZERO)
1177                } else {
1178                    result
1179                }
1180            }
1181            _ => result,
1182        }
1183    }
1184}
1185
1186impl TryFrom<SignedAmount> for Amount {
1187    type Error = OutOfRangeError;
1188
1189    fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { value.to_unsigned() }
1190}
1191
1192impl core::iter::Sum for Amount {
1193    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1194        let sats: u64 = iter.map(|amt| amt.0).sum();
1195        Amount::from_sat(sats)
1196    }
1197}
1198
1199/// A helper/builder that displays amount with specified settings.
1200///
1201/// This provides richer interface than [`fmt::Formatter`]:
1202///
1203/// * Ability to select denomination
1204/// * Show or hide denomination
1205/// * Dynamically-selected denomination - show in sats if less than 1 BTC.
1206///
1207/// However, this can still be combined with [`fmt::Formatter`] options to precisely control zeros,
1208/// padding, alignment... The formatting works like floats from `core` but note that precision will
1209/// **never** be lossy - that means no rounding.
1210///
1211/// Note: This implementation is currently **unstable**. The only thing that we can promise is that
1212/// unless the precision is changed, this will display an accurate, human-readable number, and the
1213/// default serialization (one with unmodified [`fmt::Formatter`] options) will round-trip with [`FromStr`]
1214///
1215/// See [`Amount::display_in`] and [`Amount::display_dynamic`] on how to construct this.
1216#[derive(Debug, Clone)]
1217pub struct Display {
1218    /// Absolute value of satoshis to display (sign is below)
1219    sats_abs: u64,
1220    /// The sign
1221    is_negative: bool,
1222    /// How to display the value
1223    style: DisplayStyle,
1224}
1225
1226impl Display {
1227    /// Makes subsequent calls to `Display::fmt` display denomination.
1228    pub fn show_denomination(mut self) -> Self {
1229        match &mut self.style {
1230            DisplayStyle::FixedDenomination { show_denomination, .. } => *show_denomination = true,
1231            // No-op because dynamic denomination is always shown
1232            DisplayStyle::DynamicDenomination => (),
1233        }
1234        self
1235    }
1236}
1237
1238impl fmt::Display for Display {
1239    #[rustfmt::skip]
1240    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1241        let format_options = FormatOptions::from_formatter(f);
1242        match &self.style {
1243            DisplayStyle::FixedDenomination { show_denomination, denomination } => {
1244                fmt_satoshi_in(self.sats_abs, self.is_negative, f, *denomination, *show_denomination, format_options)
1245            },
1246            DisplayStyle::DynamicDenomination if self.sats_abs >= Amount::ONE_BTC.to_sat() => {
1247                fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Bitcoin, true, format_options)
1248            },
1249            DisplayStyle::DynamicDenomination => {
1250                fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Satoshi, true, format_options)
1251            },
1252        }
1253    }
1254}
1255
1256#[derive(Clone, Debug)]
1257enum DisplayStyle {
1258    FixedDenomination { denomination: Denomination, show_denomination: bool },
1259    DynamicDenomination,
1260}
1261
1262/// A signed amount.
1263///
1264/// The [`SignedAmount`] type can be used to express Bitcoin amounts that support
1265/// arithmetic and conversion to various denominations.
1266///
1267///
1268/// Warning!
1269///
1270/// This type implements several arithmetic operations from [`core::ops`].
1271/// To prevent errors due to overflow or underflow when using these operations,
1272/// it is advised to instead use the checked arithmetic methods whose names
1273/// start with `checked_`.  The operations from [`core::ops`] that [`Amount`]
1274/// implements will panic when overflow or underflow occurs.
1275///
1276#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1277pub struct SignedAmount(i64);
1278
1279impl SignedAmount {
1280    /// The zero amount.
1281    pub const ZERO: SignedAmount = SignedAmount(0);
1282    /// Exactly one satoshi.
1283    pub const ONE_SAT: SignedAmount = SignedAmount(1);
1284    /// Exactly one bitcoin.
1285    pub const ONE_BTC: SignedAmount = SignedAmount(100_000_000);
1286    /// The maximum value allowed as an amount. Useful for sanity checking.
1287    pub const MAX_MONEY: SignedAmount = SignedAmount(21_000_000 * 100_000_000);
1288    /// The minimum value of an amount.
1289    pub const MIN: SignedAmount = SignedAmount(i64::MIN);
1290    /// The maximum value of an amount.
1291    pub const MAX: SignedAmount = SignedAmount(i64::MAX);
1292
1293    /// Create an [`SignedAmount`] with satoshi precision and the given number of satoshis.
1294    pub const fn from_sat(satoshi: i64) -> SignedAmount { SignedAmount(satoshi) }
1295
1296    /// Gets the number of satoshis in this [`SignedAmount`].
1297    pub fn to_sat(self) -> i64 { self.0 }
1298
1299    /// Convert from a value expressing bitcoins to an [`SignedAmount`].
1300    #[cfg(feature = "alloc")]
1301    pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
1302        SignedAmount::from_float_in(btc, Denomination::Bitcoin)
1303    }
1304
1305    /// Parse a decimal string as a value in the given denomination.
1306    ///
1307    /// Note: This only parses the value string.  If you want to parse a value
1308    /// with denomination, use [`FromStr`].
1309    pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
1310        match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
1311            // (negative, amount)
1312            (false, sat) if sat > i64::MAX as u64 =>
1313                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))),
1314            (false, sat) => Ok(SignedAmount(sat as i64)),
1315            (true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
1316            (true, sat) if sat > i64::MIN.unsigned_abs() =>
1317                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small())),
1318            (true, sat) => Ok(SignedAmount(-(sat as i64))),
1319        }
1320    }
1321
1322    /// Parses amounts with denomination suffix like they are produced with
1323    /// [`Self::to_string_with_denomination`] or with [`fmt::Display`].
1324    /// If you want to parse only the amount without the denomination,
1325    /// use [`Self::from_str_in`].
1326    pub fn from_str_with_denomination(s: &str) -> Result<SignedAmount, ParseError> {
1327        let (amt, denom) = split_amount_and_denomination(s)?;
1328        SignedAmount::from_str_in(amt, denom).map_err(Into::into)
1329    }
1330
1331    /// Express this [`SignedAmount`] as a floating-point value in the given denomination.
1332    ///
1333    /// Please be aware of the risk of using floating-point numbers.
1334    #[cfg(feature = "alloc")]
1335    pub fn to_float_in(self, denom: Denomination) -> f64 {
1336        self.to_string_in(denom).parse::<f64>().unwrap()
1337    }
1338
1339    /// Express this [`SignedAmount`] as a floating-point value in Bitcoin.
1340    ///
1341    /// Equivalent to `to_float_in(Denomination::Bitcoin)`.
1342    ///
1343    /// Please be aware of the risk of using floating-point numbers.
1344    #[cfg(feature = "alloc")]
1345    pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
1346
1347    /// Convert this [`SignedAmount`] in floating-point notation with a given
1348    /// denomination.
1349    ///
1350    /// # Errors
1351    ///
1352    /// If the amount is too big, too precise or negative.
1353    ///
1354    /// Please be aware of the risk of using floating-point numbers.
1355    #[cfg(feature = "alloc")]
1356    pub fn from_float_in(
1357        value: f64,
1358        denom: Denomination,
1359    ) -> Result<SignedAmount, ParseAmountError> {
1360        // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
1361        // Any performance-critical application should not be dealing with floats.
1362        SignedAmount::from_str_in(&value.to_string(), denom)
1363    }
1364
1365    /// Create an object that implements [`fmt::Display`] using specified denomination.
1366    pub fn display_in(self, denomination: Denomination) -> Display {
1367        Display {
1368            sats_abs: self.unsigned_abs().to_sat(),
1369            is_negative: self.is_negative(),
1370            style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
1371        }
1372    }
1373
1374    /// Create an object that implements [`fmt::Display`] dynamically selecting denomination.
1375    ///
1376    /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
1377    /// avoid confusion the denomination is always shown.
1378    pub fn display_dynamic(self) -> Display {
1379        Display {
1380            sats_abs: self.unsigned_abs().to_sat(),
1381            is_negative: self.is_negative(),
1382            style: DisplayStyle::DynamicDenomination,
1383        }
1384    }
1385
1386    /// Format the value of this [`SignedAmount`] in the given denomination.
1387    ///
1388    /// Does not include the denomination.
1389    #[rustfmt::skip]
1390    #[deprecated(since = "TBD", note = "Use `display_in()` instead")]
1391    pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
1392        fmt_satoshi_in(self.unsigned_abs().to_sat(), self.is_negative(), f, denom, false, FormatOptions::default())
1393    }
1394
1395    /// Get a string number of this [`SignedAmount`] in the given denomination.
1396    ///
1397    /// Does not include the denomination.
1398    #[cfg(feature = "alloc")]
1399    pub fn to_string_in(self, denom: Denomination) -> String { self.display_in(denom).to_string() }
1400
1401    /// Get a formatted string of this [`SignedAmount`] in the given denomination,
1402    /// suffixed with the abbreviation for the denomination.
1403    #[cfg(feature = "alloc")]
1404    pub fn to_string_with_denomination(self, denom: Denomination) -> String {
1405        self.display_in(denom).show_denomination().to_string()
1406    }
1407
1408    // Some arithmetic that doesn't fit in [`core::ops`] traits.
1409
1410    /// Get the absolute value of this [`SignedAmount`].
1411    pub fn abs(self) -> SignedAmount { SignedAmount(self.0.abs()) }
1412
1413    /// Get the absolute value of this [`SignedAmount`] returning [`Amount`].
1414    pub fn unsigned_abs(self) -> Amount { Amount(self.0.unsigned_abs()) }
1415
1416    /// Returns a number representing sign of this [`SignedAmount`].
1417    ///
1418    /// - `0` if the amount is zero
1419    /// - `1` if the amount is positive
1420    /// - `-1` if the amount is negative
1421    pub fn signum(self) -> i64 { self.0.signum() }
1422
1423    /// Checks if this [`SignedAmount`] is positive.
1424    ///
1425    /// Returns `true` if this [`SignedAmount`] is positive and `false` if
1426    /// this [`SignedAmount`] is zero or negative.
1427    pub fn is_positive(self) -> bool { self.0.is_positive() }
1428
1429    /// Checks if this [`SignedAmount`] is negative.
1430    ///
1431    /// Returns `true` if this [`SignedAmount`] is negative and `false` if
1432    /// this [`SignedAmount`] is zero or positive.
1433    pub fn is_negative(self) -> bool { self.0.is_negative() }
1434
1435    /// Returns the absolute value of this [`SignedAmount`].
1436    ///
1437    /// Consider using `unsigned_abs` which is often more practical.
1438    ///
1439    /// Returns [`None`] if overflow occurred. (`self == MIN`)
1440    pub fn checked_abs(self) -> Option<SignedAmount> { self.0.checked_abs().map(SignedAmount) }
1441
1442    /// Checked addition.
1443    ///
1444    /// Returns [`None`] if overflow occurred.
1445    pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
1446        self.0.checked_add(rhs.0).map(SignedAmount)
1447    }
1448
1449    /// Checked subtraction.
1450    ///
1451    /// Returns [`None`] if overflow occurred.
1452    pub fn checked_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
1453        self.0.checked_sub(rhs.0).map(SignedAmount)
1454    }
1455
1456    /// Checked multiplication.
1457    ///
1458    /// Returns [`None`] if overflow occurred.
1459    pub fn checked_mul(self, rhs: i64) -> Option<SignedAmount> {
1460        self.0.checked_mul(rhs).map(SignedAmount)
1461    }
1462
1463    /// Checked integer division.
1464    ///
1465    /// Be aware that integer division loses the remainder if no exact division
1466    /// can be made.
1467    ///
1468    /// Returns [`None`] if overflow occurred.
1469    pub fn checked_div(self, rhs: i64) -> Option<SignedAmount> {
1470        self.0.checked_div(rhs).map(SignedAmount)
1471    }
1472
1473    /// Checked remainder.
1474    ///
1475    /// Returns [`None`] if overflow occurred.
1476    pub fn checked_rem(self, rhs: i64) -> Option<SignedAmount> {
1477        self.0.checked_rem(rhs).map(SignedAmount)
1478    }
1479
1480    /// Unchecked addition.
1481    ///
1482    /// Computes `self + rhs`.
1483    ///
1484    /// # Panics
1485    ///
1486    /// On overflow, panics in debug mode, wraps in release mode.
1487    pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 + rhs.0) }
1488
1489    /// Unchecked subtraction.
1490    ///
1491    /// Computes `self - rhs`.
1492    ///
1493    /// # Panics
1494    ///
1495    /// On overflow, panics in debug mode, wraps in release mode.
1496    pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 - rhs.0) }
1497
1498    /// Subtraction that doesn't allow negative [`SignedAmount`]s.
1499    ///
1500    /// Returns [`None`] if either [`self`], `rhs` or the result is strictly negative.
1501    pub fn positive_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
1502        if self.is_negative() || rhs.is_negative() || rhs > self {
1503            None
1504        } else {
1505            self.checked_sub(rhs)
1506        }
1507    }
1508
1509    /// Converts to an unsigned amount.
1510    pub fn to_unsigned(self) -> Result<Amount, OutOfRangeError> {
1511        if self.is_negative() {
1512            Err(OutOfRangeError::negative())
1513        } else {
1514            Ok(Amount::from_sat(self.to_sat() as u64))
1515        }
1516    }
1517}
1518
1519impl default::Default for SignedAmount {
1520    fn default() -> Self { SignedAmount::ZERO }
1521}
1522
1523impl fmt::Debug for SignedAmount {
1524    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1525        write!(f, "SignedAmount({} SAT)", self.to_sat())
1526    }
1527}
1528
1529// No one should depend on a binding contract for Display for this type.
1530// Just using Bitcoin denominated string.
1531impl fmt::Display for SignedAmount {
1532    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1533        fmt::Display::fmt(&self.display_in(Denomination::Bitcoin).show_denomination(), f)
1534    }
1535}
1536
1537impl ops::Add for SignedAmount {
1538    type Output = SignedAmount;
1539
1540    fn add(self, rhs: SignedAmount) -> Self::Output {
1541        self.checked_add(rhs).expect("SignedAmount addition error")
1542    }
1543}
1544
1545impl ops::AddAssign for SignedAmount {
1546    fn add_assign(&mut self, other: SignedAmount) { *self = *self + other }
1547}
1548
1549impl ops::Sub for SignedAmount {
1550    type Output = SignedAmount;
1551
1552    fn sub(self, rhs: SignedAmount) -> Self::Output {
1553        self.checked_sub(rhs).expect("SignedAmount subtraction error")
1554    }
1555}
1556
1557impl ops::SubAssign for SignedAmount {
1558    fn sub_assign(&mut self, other: SignedAmount) { *self = *self - other }
1559}
1560
1561impl ops::Rem<i64> for SignedAmount {
1562    type Output = SignedAmount;
1563
1564    fn rem(self, modulus: i64) -> Self {
1565        self.checked_rem(modulus).expect("SignedAmount remainder error")
1566    }
1567}
1568
1569impl ops::RemAssign<i64> for SignedAmount {
1570    fn rem_assign(&mut self, modulus: i64) { *self = *self % modulus }
1571}
1572
1573impl ops::Mul<i64> for SignedAmount {
1574    type Output = SignedAmount;
1575
1576    fn mul(self, rhs: i64) -> Self::Output {
1577        self.checked_mul(rhs).expect("SignedAmount multiplication error")
1578    }
1579}
1580
1581impl ops::MulAssign<i64> for SignedAmount {
1582    fn mul_assign(&mut self, rhs: i64) { *self = *self * rhs }
1583}
1584
1585impl ops::Div<i64> for SignedAmount {
1586    type Output = SignedAmount;
1587
1588    fn div(self, rhs: i64) -> Self::Output {
1589        self.checked_div(rhs).expect("SignedAmount division error")
1590    }
1591}
1592
1593impl ops::DivAssign<i64> for SignedAmount {
1594    fn div_assign(&mut self, rhs: i64) { *self = *self / rhs }
1595}
1596
1597impl ops::Neg for SignedAmount {
1598    type Output = Self;
1599
1600    fn neg(self) -> Self::Output { Self(self.0.neg()) }
1601}
1602
1603impl FromStr for SignedAmount {
1604    type Err = ParseError;
1605
1606    /// Parses a string slice where the slice includes a denomination.
1607    ///
1608    /// If the string slice is zero or negative zero, then no denomination is required.
1609    ///
1610    /// # Returns
1611    ///
1612    /// `Ok(Amount)` if the string amount and denomination parse successfully,
1613    /// otherwise, return `Err(ParseError)`.
1614    fn from_str(s: &str) -> Result<Self, Self::Err> {
1615        let result = SignedAmount::from_str_with_denomination(s);
1616
1617        match result {
1618            Err(ParseError::MissingDenomination(_)) => {
1619                let d = SignedAmount::from_str_in(s, Denomination::Satoshi);
1620
1621                if d == Ok(SignedAmount::ZERO) {
1622                    Ok(SignedAmount::ZERO)
1623                } else {
1624                    result
1625                }
1626            }
1627            _ => result,
1628        }
1629    }
1630}
1631
1632impl TryFrom<Amount> for SignedAmount {
1633    type Error = OutOfRangeError;
1634
1635    fn try_from(value: Amount) -> Result<Self, Self::Error> { value.to_signed() }
1636}
1637
1638impl core::iter::Sum for SignedAmount {
1639    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1640        let sats: i64 = iter.map(|amt| amt.0).sum();
1641        SignedAmount::from_sat(sats)
1642    }
1643}
1644
1645#[cfg(feature = "arbitrary")]
1646impl<'a> Arbitrary<'a> for SignedAmount {
1647    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
1648        let s = i64::arbitrary(u)?;
1649        Ok(SignedAmount(s))
1650    }
1651}
1652
1653/// Calculates the sum over the iterator using checked arithmetic.
1654pub trait CheckedSum<R>: private::SumSeal<R> {
1655    /// Calculates the sum over the iterator using checked arithmetic. If an over or underflow would
1656    /// happen it returns [`None`].
1657    fn checked_sum(self) -> Option<R>;
1658}
1659
1660impl<T> CheckedSum<Amount> for T
1661where
1662    T: Iterator<Item = Amount>,
1663{
1664    fn checked_sum(mut self) -> Option<Amount> {
1665        let first = Some(self.next().unwrap_or_default());
1666
1667        self.fold(first, |acc, item| acc.and_then(|acc| acc.checked_add(item)))
1668    }
1669}
1670
1671impl<T> CheckedSum<SignedAmount> for T
1672where
1673    T: Iterator<Item = SignedAmount>,
1674{
1675    fn checked_sum(mut self) -> Option<SignedAmount> {
1676        let first = Some(self.next().unwrap_or_default());
1677
1678        self.fold(first, |acc, item| acc.and_then(|acc| acc.checked_add(item)))
1679    }
1680}
1681
1682mod private {
1683    use super::{Amount, SignedAmount};
1684
1685    /// Used to seal the `CheckedSum` trait
1686    pub trait SumSeal<A> {}
1687
1688    impl<T> SumSeal<Amount> for T where T: Iterator<Item = Amount> {}
1689    impl<T> SumSeal<SignedAmount> for T where T: Iterator<Item = SignedAmount> {}
1690}
1691
1692#[cfg(feature = "serde")]
1693pub mod serde {
1694    // methods are implementation of a standardized serde-specific signature
1695    #![allow(missing_docs)]
1696
1697    //! This module adds serde serialization and deserialization support for Amounts.
1698    //!
1699    //! Since there is not a default way to serialize and deserialize Amounts, multiple
1700    //! ways are supported and it's up to the user to decide which serialiation to use.
1701    //! The provided modules can be used as follows:
1702    //!
1703    //! ```rust,ignore
1704    //! use serde::{Serialize, Deserialize};
1705    //! use bitcoin_units::Amount;
1706    //!
1707    //! #[derive(Serialize, Deserialize)]
1708    //! pub struct HasAmount {
1709    //!     #[serde(with = "bitcoin_units::amount::serde::as_btc")]
1710    //!     pub amount: Amount,
1711    //! }
1712    //! ```
1713
1714    use core::fmt;
1715
1716    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1717
1718    #[cfg(feature = "alloc")]
1719    use super::Denomination;
1720    use super::{Amount, ParseAmountError, SignedAmount};
1721
1722    /// This trait is used only to avoid code duplication and naming collisions
1723    /// of the different serde serialization crates.
1724    pub trait SerdeAmount: Copy + Sized {
1725        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1726        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error>;
1727        #[cfg(feature = "alloc")]
1728        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1729        #[cfg(feature = "alloc")]
1730        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error>;
1731    }
1732
1733    mod private {
1734        /// Controls access to the trait methods.
1735        pub struct Token;
1736    }
1737
1738    /// This trait is only for internal Amount type serialization/deserialization
1739    pub trait SerdeAmountForOpt: Copy + Sized + SerdeAmount {
1740        fn type_prefix(_: private::Token) -> &'static str;
1741        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1742        #[cfg(feature = "alloc")]
1743        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1744    }
1745
1746    struct DisplayFullError(ParseAmountError);
1747
1748    #[cfg(feature = "std")]
1749    impl fmt::Display for DisplayFullError {
1750        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1751            use std::error::Error;
1752
1753            fmt::Display::fmt(&self.0, f)?;
1754            let mut source_opt = self.0.source();
1755            while let Some(source) = source_opt {
1756                write!(f, ": {}", source)?;
1757                source_opt = source.source();
1758            }
1759            Ok(())
1760        }
1761    }
1762
1763    #[cfg(not(feature = "std"))]
1764    impl fmt::Display for DisplayFullError {
1765        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
1766    }
1767
1768    impl SerdeAmount for Amount {
1769        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1770            u64::serialize(&self.to_sat(), s)
1771        }
1772        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1773            Ok(Amount::from_sat(u64::deserialize(d)?))
1774        }
1775        #[cfg(feature = "alloc")]
1776        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1777            f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
1778        }
1779        #[cfg(feature = "alloc")]
1780        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1781            use serde::de::Error;
1782            Amount::from_btc(f64::deserialize(d)?)
1783                .map_err(DisplayFullError)
1784                .map_err(D::Error::custom)
1785        }
1786    }
1787
1788    impl SerdeAmountForOpt for Amount {
1789        fn type_prefix(_: private::Token) -> &'static str { "u" }
1790        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1791            s.serialize_some(&self.to_sat())
1792        }
1793        #[cfg(feature = "alloc")]
1794        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1795            s.serialize_some(&self.to_btc())
1796        }
1797    }
1798
1799    impl SerdeAmount for SignedAmount {
1800        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1801            i64::serialize(&self.to_sat(), s)
1802        }
1803        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1804            Ok(SignedAmount::from_sat(i64::deserialize(d)?))
1805        }
1806        #[cfg(feature = "alloc")]
1807        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1808            f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
1809        }
1810        #[cfg(feature = "alloc")]
1811        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1812            use serde::de::Error;
1813            SignedAmount::from_btc(f64::deserialize(d)?)
1814                .map_err(DisplayFullError)
1815                .map_err(D::Error::custom)
1816        }
1817    }
1818
1819    impl SerdeAmountForOpt for SignedAmount {
1820        fn type_prefix(_: private::Token) -> &'static str { "i" }
1821        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1822            s.serialize_some(&self.to_sat())
1823        }
1824        #[cfg(feature = "alloc")]
1825        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1826            s.serialize_some(&self.to_btc())
1827        }
1828    }
1829
1830    pub mod as_sat {
1831        //! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi.
1832        //! Use with `#[serde(with = "amount::serde::as_sat")]`.
1833
1834        use serde::{Deserializer, Serializer};
1835
1836        use super::private;
1837        use crate::amount::serde::SerdeAmount;
1838
1839        pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
1840            a.ser_sat(s, private::Token)
1841        }
1842
1843        pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
1844            A::des_sat(d, private::Token)
1845        }
1846
1847        pub mod opt {
1848            //! Serialize and deserialize [`Option<Amount>`](crate::Amount) as real numbers denominated in satoshi.
1849            //! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`.
1850
1851            use core::fmt;
1852            use core::marker::PhantomData;
1853
1854            use serde::{de, Deserializer, Serializer};
1855
1856            use super::private;
1857            use crate::amount::serde::SerdeAmountForOpt;
1858
1859            pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
1860                a: &Option<A>,
1861                s: S,
1862            ) -> Result<S::Ok, S::Error> {
1863                match *a {
1864                    Some(a) => a.ser_sat_opt(s, private::Token),
1865                    None => s.serialize_none(),
1866                }
1867            }
1868
1869            pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>(
1870                d: D,
1871            ) -> Result<Option<A>, D::Error> {
1872                struct VisitOptAmt<X>(PhantomData<X>);
1873
1874                impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt<X> {
1875                    type Value = Option<X>;
1876
1877                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1878                        write!(formatter, "An Option<{}64>", X::type_prefix(private::Token))
1879                    }
1880
1881                    fn visit_none<E>(self) -> Result<Self::Value, E>
1882                    where
1883                        E: de::Error,
1884                    {
1885                        Ok(None)
1886                    }
1887                    fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
1888                    where
1889                        D: Deserializer<'de>,
1890                    {
1891                        Ok(Some(X::des_sat(d, private::Token)?))
1892                    }
1893                }
1894                d.deserialize_option(VisitOptAmt::<A>(PhantomData))
1895            }
1896        }
1897    }
1898
1899    #[cfg(feature = "alloc")]
1900    pub mod as_btc {
1901        //! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC.
1902        //! Use with `#[serde(with = "amount::serde::as_btc")]`.
1903
1904        use serde::{Deserializer, Serializer};
1905
1906        use super::private;
1907        use crate::amount::serde::SerdeAmount;
1908
1909        pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
1910            a.ser_btc(s, private::Token)
1911        }
1912
1913        pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
1914            A::des_btc(d, private::Token)
1915        }
1916
1917        pub mod opt {
1918            //! Serialize and deserialize `Option<Amount>` as JSON numbers denominated in BTC.
1919            //! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`.
1920
1921            use core::fmt;
1922            use core::marker::PhantomData;
1923
1924            use serde::{de, Deserializer, Serializer};
1925
1926            use super::private;
1927            use crate::amount::serde::SerdeAmountForOpt;
1928
1929            pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
1930                a: &Option<A>,
1931                s: S,
1932            ) -> Result<S::Ok, S::Error> {
1933                match *a {
1934                    Some(a) => a.ser_btc_opt(s, private::Token),
1935                    None => s.serialize_none(),
1936                }
1937            }
1938
1939            pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>(
1940                d: D,
1941            ) -> Result<Option<A>, D::Error> {
1942                struct VisitOptAmt<X>(PhantomData<X>);
1943
1944                impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt<X> {
1945                    type Value = Option<X>;
1946
1947                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1948                        write!(formatter, "An Option<f64>")
1949                    }
1950
1951                    fn visit_none<E>(self) -> Result<Self::Value, E>
1952                    where
1953                        E: de::Error,
1954                    {
1955                        Ok(None)
1956                    }
1957                    fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
1958                    where
1959                        D: Deserializer<'de>,
1960                    {
1961                        Ok(Some(X::des_btc(d, private::Token)?))
1962                    }
1963                }
1964                d.deserialize_option(VisitOptAmt::<A>(PhantomData))
1965            }
1966        }
1967    }
1968}
1969
1970#[cfg(kani)]
1971mod verification {
1972    use std::cmp;
1973
1974    use super::*;
1975
1976    // Note regarding the `unwind` parameter: this defines how many iterations
1977    // of loops kani will unwind before handing off to the SMT solver. Basically
1978    // it should be set as low as possible such that Kani still succeeds (doesn't
1979    // return "undecidable").
1980    //
1981    // There is more info here: https://model-checking.github.io/kani/tutorial-loop-unwinding.html
1982    //
1983    // Unfortunately what it means to "loop" is pretty opaque ... in this case
1984    // there appear to be loops in memcmp, which I guess comes from assert_eq!,
1985    // though I didn't see any failures until I added the to_signed() test.
1986    // Further confusing the issue, a value of 2 works fine on my system, but on
1987    // CI it fails, so we need to set it higher.
1988    #[kani::unwind(4)]
1989    #[kani::proof]
1990    fn u_amount_homomorphic() {
1991        let n1 = kani::any::<u64>();
1992        let n2 = kani::any::<u64>();
1993        kani::assume(n1.checked_add(n2).is_some()); // assume we don't overflow in the actual test
1994        assert_eq!(Amount::from_sat(n1) + Amount::from_sat(n2), Amount::from_sat(n1 + n2));
1995
1996        let mut amt = Amount::from_sat(n1);
1997        amt += Amount::from_sat(n2);
1998        assert_eq!(amt, Amount::from_sat(n1 + n2));
1999
2000        let max = cmp::max(n1, n2);
2001        let min = cmp::min(n1, n2);
2002        assert_eq!(Amount::from_sat(max) - Amount::from_sat(min), Amount::from_sat(max - min));
2003
2004        let mut amt = Amount::from_sat(max);
2005        amt -= Amount::from_sat(min);
2006        assert_eq!(amt, Amount::from_sat(max - min));
2007
2008        assert_eq!(
2009            Amount::from_sat(n1).to_signed(),
2010            if n1 <= i64::MAX as u64 {
2011                Ok(SignedAmount::from_sat(n1.try_into().unwrap()))
2012            } else {
2013                Err(OutOfRangeError::too_big(true))
2014            },
2015        );
2016    }
2017
2018    #[kani::unwind(4)]
2019    #[kani::proof]
2020    fn u_amount_homomorphic_checked() {
2021        let n1 = kani::any::<u64>();
2022        let n2 = kani::any::<u64>();
2023        assert_eq!(
2024            Amount::from_sat(n1).checked_add(Amount::from_sat(n2)),
2025            n1.checked_add(n2).map(Amount::from_sat),
2026        );
2027        assert_eq!(
2028            Amount::from_sat(n1).checked_sub(Amount::from_sat(n2)),
2029            n1.checked_sub(n2).map(Amount::from_sat),
2030        );
2031    }
2032
2033    #[kani::unwind(4)]
2034    #[kani::proof]
2035    fn s_amount_homomorphic() {
2036        let n1 = kani::any::<i64>();
2037        let n2 = kani::any::<i64>();
2038        kani::assume(n1.checked_add(n2).is_some()); // assume we don't overflow in the actual test
2039        kani::assume(n1.checked_sub(n2).is_some()); // assume we don't overflow in the actual test
2040        assert_eq!(
2041            SignedAmount::from_sat(n1) + SignedAmount::from_sat(n2),
2042            SignedAmount::from_sat(n1 + n2)
2043        );
2044        assert_eq!(
2045            SignedAmount::from_sat(n1) - SignedAmount::from_sat(n2),
2046            SignedAmount::from_sat(n1 - n2)
2047        );
2048
2049        let mut amt = SignedAmount::from_sat(n1);
2050        amt += SignedAmount::from_sat(n2);
2051        assert_eq!(amt, SignedAmount::from_sat(n1 + n2));
2052        let mut amt = SignedAmount::from_sat(n1);
2053        amt -= SignedAmount::from_sat(n2);
2054        assert_eq!(amt, SignedAmount::from_sat(n1 - n2));
2055
2056        assert_eq!(
2057            SignedAmount::from_sat(n1).to_unsigned(),
2058            if n1 >= 0 {
2059                Ok(Amount::from_sat(n1.try_into().unwrap()))
2060            } else {
2061                Err(OutOfRangeError { is_signed: false, is_greater_than_max: false })
2062            },
2063        );
2064    }
2065
2066    #[kani::unwind(4)]
2067    #[kani::proof]
2068    fn s_amount_homomorphic_checked() {
2069        let n1 = kani::any::<i64>();
2070        let n2 = kani::any::<i64>();
2071        assert_eq!(
2072            SignedAmount::from_sat(n1).checked_add(SignedAmount::from_sat(n2)),
2073            n1.checked_add(n2).map(SignedAmount::from_sat),
2074        );
2075        assert_eq!(
2076            SignedAmount::from_sat(n1).checked_sub(SignedAmount::from_sat(n2)),
2077            n1.checked_sub(n2).map(SignedAmount::from_sat),
2078        );
2079
2080        assert_eq!(
2081            SignedAmount::from_sat(n1).positive_sub(SignedAmount::from_sat(n2)),
2082            if n1 >= 0 && n2 >= 0 && n1 >= n2 {
2083                Some(SignedAmount::from_sat(n1 - n2))
2084            } else {
2085                None
2086            },
2087        );
2088    }
2089}
2090
2091#[cfg(test)]
2092mod tests {
2093    #[cfg(feature = "alloc")]
2094    use alloc::format;
2095    #[cfg(feature = "std")]
2096    use std::panic;
2097
2098    use super::*;
2099
2100    #[test]
2101    #[cfg(feature = "alloc")]
2102    fn from_str_zero() {
2103        let denoms = ["BTC", "mBTC", "uBTC", "bits", "sats"];
2104        for denom in denoms {
2105            for v in &["0", "000"] {
2106                let s = format!("{} {}", v, denom);
2107                match s.parse::<Amount>() {
2108                    Err(e) => panic!("failed to crate amount from {}: {:?}", s, e),
2109                    Ok(amount) => assert_eq!(amount, Amount::from_sat(0)),
2110                }
2111            }
2112
2113            let s = format!("-0 {}", denom);
2114            match s.parse::<Amount>() {
2115                Err(e) => assert_eq!(
2116                    e,
2117                    ParseError::Amount(ParseAmountError::OutOfRange(OutOfRangeError::negative()))
2118                ),
2119                Ok(_) => panic!("unsigned amount from {}", s),
2120            }
2121            match s.parse::<SignedAmount>() {
2122                Err(e) => panic!("failed to crate amount from {}: {:?}", s, e),
2123                Ok(amount) => assert_eq!(amount, SignedAmount::from_sat(0)),
2124            }
2125        }
2126    }
2127
2128    #[test]
2129    fn from_str_zero_without_denomination() {
2130        let _a = Amount::from_str("0").unwrap();
2131        let _a = Amount::from_str("0.0").unwrap();
2132        let _a = Amount::from_str("00.0").unwrap();
2133
2134        assert!(Amount::from_str("-0").is_err());
2135        assert!(Amount::from_str("-0.0").is_err());
2136        assert!(Amount::from_str("-00.0").is_err());
2137
2138        let _a = SignedAmount::from_str("-0").unwrap();
2139        let _a = SignedAmount::from_str("-0.0").unwrap();
2140        let _a = SignedAmount::from_str("-00.0").unwrap();
2141
2142        let _a = SignedAmount::from_str("0").unwrap();
2143        let _a = SignedAmount::from_str("0.0").unwrap();
2144        let _a = SignedAmount::from_str("00.0").unwrap();
2145    }
2146
2147    #[test]
2148    fn from_int_btc() {
2149        let amt = Amount::from_int_btc(2);
2150        assert_eq!(Amount::from_sat(200_000_000), amt);
2151    }
2152
2153    #[should_panic]
2154    #[test]
2155    fn from_int_btc_panic() { Amount::from_int_btc(u64::MAX); }
2156
2157    #[test]
2158    fn test_signed_amount_try_from_amount() {
2159        let ua_positive = Amount::from_sat(123);
2160        let sa_positive = SignedAmount::try_from(ua_positive).unwrap();
2161        assert_eq!(sa_positive, SignedAmount(123));
2162
2163        let ua_max = Amount::MAX;
2164        let result = SignedAmount::try_from(ua_max);
2165        assert_eq!(result, Err(OutOfRangeError { is_signed: true, is_greater_than_max: true }));
2166    }
2167
2168    #[test]
2169    fn test_amount_try_from_signed_amount() {
2170        let sa_positive = SignedAmount(123);
2171        let ua_positive = Amount::try_from(sa_positive).unwrap();
2172        assert_eq!(ua_positive, Amount::from_sat(123));
2173
2174        let sa_negative = SignedAmount(-123);
2175        let result = Amount::try_from(sa_negative);
2176        assert_eq!(result, Err(OutOfRangeError { is_signed: false, is_greater_than_max: false }));
2177    }
2178
2179    #[test]
2180    fn mul_div() {
2181        let sat = Amount::from_sat;
2182        let ssat = SignedAmount::from_sat;
2183
2184        assert_eq!(sat(14) * 3, sat(42));
2185        assert_eq!(sat(14) / 2, sat(7));
2186        assert_eq!(sat(14) % 3, sat(2));
2187        assert_eq!(ssat(-14) * 3, ssat(-42));
2188        assert_eq!(ssat(-14) / 2, ssat(-7));
2189        assert_eq!(ssat(-14) % 3, ssat(-2));
2190
2191        let mut b = ssat(30);
2192        b /= 3;
2193        assert_eq!(b, ssat(10));
2194        b %= 3;
2195        assert_eq!(b, ssat(1));
2196    }
2197
2198    #[cfg(feature = "std")]
2199    #[test]
2200    fn test_overflows() {
2201        // panic on overflow
2202        let result = panic::catch_unwind(|| Amount::MAX + Amount::from_sat(1));
2203        assert!(result.is_err());
2204        let result = panic::catch_unwind(|| Amount::from_sat(8446744073709551615) * 3);
2205        assert!(result.is_err());
2206    }
2207
2208    #[test]
2209    fn checked_arithmetic() {
2210        let sat = Amount::from_sat;
2211        let ssat = SignedAmount::from_sat;
2212
2213        assert_eq!(SignedAmount::MAX.checked_add(ssat(1)), None);
2214        assert_eq!(SignedAmount::MIN.checked_sub(ssat(1)), None);
2215        assert_eq!(Amount::MAX.checked_add(sat(1)), None);
2216        assert_eq!(Amount::MIN.checked_sub(sat(1)), None);
2217
2218        assert_eq!(sat(5).checked_div(2), Some(sat(2))); // integer division
2219        assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3)));
2220    }
2221
2222    #[test]
2223    #[cfg(not(debug_assertions))]
2224    fn unchecked_amount_add() {
2225        let amt = Amount::MAX.unchecked_add(Amount::ONE_SAT);
2226        assert_eq!(amt, Amount::ZERO);
2227    }
2228
2229    #[test]
2230    #[cfg(not(debug_assertions))]
2231    fn unchecked_signed_amount_add() {
2232        let signed_amt = SignedAmount::MAX.unchecked_add(SignedAmount::ONE_SAT);
2233        assert_eq!(signed_amt, SignedAmount::MIN);
2234    }
2235
2236    #[test]
2237    #[cfg(not(debug_assertions))]
2238    fn unchecked_amount_subtract() {
2239        let amt = Amount::ZERO.unchecked_sub(Amount::ONE_SAT);
2240        assert_eq!(amt, Amount::MAX);
2241    }
2242
2243    #[test]
2244    #[cfg(not(debug_assertions))]
2245    fn unchecked_signed_amount_subtract() {
2246        let signed_amt = SignedAmount::MIN.unchecked_sub(SignedAmount::ONE_SAT);
2247        assert_eq!(signed_amt, SignedAmount::MAX);
2248    }
2249
2250    #[cfg(feature = "alloc")]
2251    #[test]
2252    fn floating_point() {
2253        use super::Denomination as D;
2254        let f = Amount::from_float_in;
2255        let sf = SignedAmount::from_float_in;
2256        let sat = Amount::from_sat;
2257        let ssat = SignedAmount::from_sat;
2258
2259        assert_eq!(f(11.22, D::Bitcoin), Ok(sat(1122000000)));
2260        assert_eq!(sf(-11.22, D::MilliBitcoin), Ok(ssat(-1122000)));
2261        assert_eq!(f(11.22, D::Bit), Ok(sat(1122)));
2262        assert_eq!(f(0.0001234, D::Bitcoin), Ok(sat(12340)));
2263        assert_eq!(sf(-0.00012345, D::Bitcoin), Ok(ssat(-12345)));
2264
2265        assert_eq!(f(11.22, D::Satoshi), Err(TooPreciseError { position: 3 }.into()));
2266        assert_eq!(f(42.123456781, D::Bitcoin), Err(TooPreciseError { position: 11 }.into()));
2267        assert_eq!(sf(-184467440738.0, D::Bitcoin), Err(OutOfRangeError::too_small().into()));
2268        assert_eq!(
2269            f(18446744073709551617.0, D::Satoshi),
2270            Err(OutOfRangeError::too_big(false).into())
2271        );
2272
2273        // Amount can be grater than the max SignedAmount.
2274        assert!(f(SignedAmount::MAX.to_float_in(D::Satoshi) + 1.0, D::Satoshi).is_ok());
2275
2276        assert_eq!(
2277            f(Amount::MAX.to_float_in(D::Satoshi) + 1.0, D::Satoshi),
2278            Err(OutOfRangeError::too_big(false).into())
2279        );
2280
2281        assert_eq!(
2282            sf(SignedAmount::MAX.to_float_in(D::Satoshi) + 1.0, D::Satoshi),
2283            Err(OutOfRangeError::too_big(true).into())
2284        );
2285
2286        let btc = move |f| SignedAmount::from_btc(f).unwrap();
2287        assert_eq!(btc(2.5).to_float_in(D::Bitcoin), 2.5);
2288        assert_eq!(btc(-2.5).to_float_in(D::MilliBitcoin), -2500.0);
2289        assert_eq!(btc(2.5).to_float_in(D::Satoshi), 250000000.0);
2290
2291        let btc = move |f| Amount::from_btc(f).unwrap();
2292        assert_eq!(&btc(0.0012).to_float_in(D::Bitcoin).to_string(), "0.0012")
2293    }
2294
2295    #[test]
2296    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2297    fn parsing() {
2298        use super::ParseAmountError as E;
2299        let btc = Denomination::Bitcoin;
2300        let sat = Denomination::Satoshi;
2301        let p = Amount::from_str_in;
2302        let sp = SignedAmount::from_str_in;
2303
2304        assert_eq!(
2305            p("x", btc),
2306            Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 }))
2307        );
2308        assert_eq!(
2309            p("-", btc),
2310            Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))
2311        );
2312        assert_eq!(
2313            sp("-", btc),
2314            Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))
2315        );
2316        assert_eq!(
2317            p("-1.0x", btc),
2318            Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 4 }))
2319        );
2320        assert_eq!(
2321            p("0.0 ", btc),
2322            Err(E::from(InvalidCharacterError { invalid_char: ' ', position: 3 }))
2323        );
2324        assert_eq!(
2325            p("0.000.000", btc),
2326            Err(E::from(InvalidCharacterError { invalid_char: '.', position: 5 }))
2327        );
2328        #[cfg(feature = "alloc")]
2329        let more_than_max = format!("1{}", Amount::MAX);
2330        #[cfg(feature = "alloc")]
2331        assert_eq!(p(&more_than_max, btc), Err(OutOfRangeError::too_big(false).into()));
2332        assert_eq!(p("0.000000042", btc), Err(TooPreciseError { position: 10 }.into()));
2333        assert_eq!(p("1.0000000", sat), Ok(Amount::from_sat(1)));
2334        assert_eq!(p("1.1", sat), Err(TooPreciseError { position: 2 }.into()));
2335        assert_eq!(p("1000.1", sat), Err(TooPreciseError { position: 5 }.into()));
2336        assert_eq!(p("1001.0000000", sat), Ok(Amount::from_sat(1001)));
2337        assert_eq!(p("1000.0000001", sat), Err(TooPreciseError { position: 11 }.into()));
2338
2339        assert_eq!(p("1", btc), Ok(Amount::from_sat(1_000_000_00)));
2340        assert_eq!(sp("-.5", btc), Ok(SignedAmount::from_sat(-500_000_00)));
2341        #[cfg(feature = "alloc")]
2342        assert_eq!(sp(&i64::MIN.to_string(), sat), Ok(SignedAmount::from_sat(i64::MIN)));
2343        assert_eq!(p("1.1", btc), Ok(Amount::from_sat(1_100_000_00)));
2344        assert_eq!(p("100", sat), Ok(Amount::from_sat(100)));
2345        assert_eq!(p("55", sat), Ok(Amount::from_sat(55)));
2346        assert_eq!(p("5500000000000000000", sat), Ok(Amount::from_sat(55_000_000_000_000_000_00)));
2347        // Should this even pass?
2348        assert_eq!(p("5500000000000000000.", sat), Ok(Amount::from_sat(55_000_000_000_000_000_00)));
2349        assert_eq!(
2350            p("12345678901.12345678", btc),
2351            Ok(Amount::from_sat(12_345_678_901__123_456_78))
2352        );
2353
2354        // make sure satoshi > i64::MAX is checked.
2355        #[cfg(feature = "alloc")]
2356        {
2357            let amount = Amount::from_sat(i64::MAX as u64);
2358            assert_eq!(Amount::from_str_in(&amount.to_string_in(sat), sat), Ok(amount));
2359            assert!(
2360                SignedAmount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_err()
2361            );
2362            assert!(Amount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_ok());
2363        }
2364
2365        // exactly 50 chars.
2366        assert_eq!(
2367            p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin),
2368            Err(OutOfRangeError::too_big(false).into())
2369        );
2370        // more than 50 chars.
2371        assert_eq!(
2372            p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
2373            Err(E::InputTooLarge(InputTooLargeError { len: 51 }))
2374        );
2375    }
2376
2377    #[test]
2378    #[cfg(feature = "alloc")]
2379    fn to_string() {
2380        use super::Denomination as D;
2381
2382        assert_eq!(Amount::ONE_BTC.to_string_in(D::Bitcoin), "1");
2383        assert_eq!(format!("{:.8}", Amount::ONE_BTC.display_in(D::Bitcoin)), "1.00000000");
2384        assert_eq!(Amount::ONE_BTC.to_string_in(D::Satoshi), "100000000");
2385        assert_eq!(Amount::ONE_SAT.to_string_in(D::Bitcoin), "0.00000001");
2386        assert_eq!(SignedAmount::from_sat(-42).to_string_in(D::Bitcoin), "-0.00000042");
2387
2388        assert_eq!(Amount::ONE_BTC.to_string_with_denomination(D::Bitcoin), "1 BTC");
2389        assert_eq!(
2390            SignedAmount::ONE_BTC.to_string_with_denomination(D::Satoshi),
2391            "100000000 satoshi"
2392        );
2393        assert_eq!(Amount::ONE_SAT.to_string_with_denomination(D::Bitcoin), "0.00000001 BTC");
2394        assert_eq!(
2395            SignedAmount::from_sat(-42).to_string_with_denomination(D::Bitcoin),
2396            "-0.00000042 BTC"
2397        );
2398    }
2399
2400    // May help identify a problem sooner
2401    #[cfg(feature = "alloc")]
2402    #[test]
2403    fn test_repeat_char() {
2404        let mut buf = String::new();
2405        repeat_char(&mut buf, '0', 0).unwrap();
2406        assert_eq!(buf.len(), 0);
2407        repeat_char(&mut buf, '0', 42).unwrap();
2408        assert_eq!(buf.len(), 42);
2409        assert!(buf.chars().all(|c| c == '0'));
2410    }
2411
2412    // Creates individual test functions to make it easier to find which check failed.
2413    macro_rules! check_format_non_negative {
2414        ($denom:ident; $($test_name:ident, $val:literal, $format_string:literal, $expected:literal);* $(;)?) => {
2415            $(
2416                #[test]
2417                #[cfg(feature = "alloc")]
2418                fn $test_name() {
2419                    assert_eq!(format!($format_string, Amount::from_sat($val).display_in(Denomination::$denom)), $expected);
2420                    assert_eq!(format!($format_string, SignedAmount::from_sat($val as i64).display_in(Denomination::$denom)), $expected);
2421                }
2422            )*
2423        }
2424    }
2425
2426    macro_rules! check_format_non_negative_show_denom {
2427        ($denom:ident, $denom_suffix:literal; $($test_name:ident, $val:literal, $format_string:literal, $expected:literal);* $(;)?) => {
2428            $(
2429                #[test]
2430                #[cfg(feature = "alloc")]
2431                fn $test_name() {
2432                    assert_eq!(format!($format_string, Amount::from_sat($val).display_in(Denomination::$denom).show_denomination()), concat!($expected, $denom_suffix));
2433                    assert_eq!(format!($format_string, SignedAmount::from_sat($val as i64).display_in(Denomination::$denom).show_denomination()), concat!($expected, $denom_suffix));
2434                }
2435            )*
2436        }
2437    }
2438
2439    check_format_non_negative! {
2440        Satoshi;
2441        sat_check_fmt_non_negative_0, 0, "{}", "0";
2442        sat_check_fmt_non_negative_1, 0, "{:2}", " 0";
2443        sat_check_fmt_non_negative_2, 0, "{:02}", "00";
2444        sat_check_fmt_non_negative_3, 0, "{:.1}", "0.0";
2445        sat_check_fmt_non_negative_4, 0, "{:4.1}", " 0.0";
2446        sat_check_fmt_non_negative_5, 0, "{:04.1}", "00.0";
2447        sat_check_fmt_non_negative_6, 1, "{}", "1";
2448        sat_check_fmt_non_negative_7, 1, "{:2}", " 1";
2449        sat_check_fmt_non_negative_8, 1, "{:02}", "01";
2450        sat_check_fmt_non_negative_9, 1, "{:.1}", "1.0";
2451        sat_check_fmt_non_negative_10, 1, "{:4.1}", " 1.0";
2452        sat_check_fmt_non_negative_11, 1, "{:04.1}", "01.0";
2453        sat_check_fmt_non_negative_12, 10, "{}", "10";
2454        sat_check_fmt_non_negative_13, 10, "{:2}", "10";
2455        sat_check_fmt_non_negative_14, 10, "{:02}", "10";
2456        sat_check_fmt_non_negative_15, 10, "{:3}", " 10";
2457        sat_check_fmt_non_negative_16, 10, "{:03}", "010";
2458        sat_check_fmt_non_negative_17, 10, "{:.1}", "10.0";
2459        sat_check_fmt_non_negative_18, 10, "{:5.1}", " 10.0";
2460        sat_check_fmt_non_negative_19, 10, "{:05.1}", "010.0";
2461        sat_check_fmt_non_negative_20, 1, "{:<2}", "1 ";
2462        sat_check_fmt_non_negative_21, 1, "{:<02}", "01";
2463        sat_check_fmt_non_negative_22, 1, "{:<3.1}", "1.0";
2464        sat_check_fmt_non_negative_23, 1, "{:<4.1}", "1.0 ";
2465    }
2466
2467    check_format_non_negative_show_denom! {
2468        Satoshi, " satoshi";
2469        sat_check_fmt_non_negative_show_denom_0, 0, "{}", "0";
2470        sat_check_fmt_non_negative_show_denom_1, 0, "{:2}", "0";
2471        sat_check_fmt_non_negative_show_denom_2, 0, "{:02}", "0";
2472        sat_check_fmt_non_negative_show_denom_3, 0, "{:9}", "0";
2473        sat_check_fmt_non_negative_show_denom_4, 0, "{:09}", "0";
2474        sat_check_fmt_non_negative_show_denom_5, 0, "{:10}", " 0";
2475        sat_check_fmt_non_negative_show_denom_6, 0, "{:010}", "00";
2476        sat_check_fmt_non_negative_show_denom_7, 0, "{:.1}", "0.0";
2477        sat_check_fmt_non_negative_show_denom_8, 0, "{:11.1}", "0.0";
2478        sat_check_fmt_non_negative_show_denom_9, 0, "{:011.1}", "0.0";
2479        sat_check_fmt_non_negative_show_denom_10, 0, "{:12.1}", " 0.0";
2480        sat_check_fmt_non_negative_show_denom_11, 0, "{:012.1}", "00.0";
2481        sat_check_fmt_non_negative_show_denom_12, 1, "{}", "1";
2482        sat_check_fmt_non_negative_show_denom_13, 1, "{:10}", " 1";
2483        sat_check_fmt_non_negative_show_denom_14, 1, "{:010}", "01";
2484        sat_check_fmt_non_negative_show_denom_15, 1, "{:.1}", "1.0";
2485        sat_check_fmt_non_negative_show_denom_16, 1, "{:12.1}", " 1.0";
2486        sat_check_fmt_non_negative_show_denom_17, 1, "{:012.1}", "01.0";
2487        sat_check_fmt_non_negative_show_denom_18, 10, "{}", "10";
2488        sat_check_fmt_non_negative_show_denom_19, 10, "{:10}", "10";
2489        sat_check_fmt_non_negative_show_denom_20, 10, "{:010}", "10";
2490        sat_check_fmt_non_negative_show_denom_21, 10, "{:11}", " 10";
2491        sat_check_fmt_non_negative_show_denom_22, 10, "{:011}", "010";
2492    }
2493
2494    check_format_non_negative! {
2495        Bitcoin;
2496        btc_check_fmt_non_negative_0, 0, "{}", "0";
2497        btc_check_fmt_non_negative_1, 0, "{:2}", " 0";
2498        btc_check_fmt_non_negative_2, 0, "{:02}", "00";
2499        btc_check_fmt_non_negative_3, 0, "{:.1}", "0.0";
2500        btc_check_fmt_non_negative_4, 0, "{:4.1}", " 0.0";
2501        btc_check_fmt_non_negative_5, 0, "{:04.1}", "00.0";
2502        btc_check_fmt_non_negative_6, 1, "{}", "0.00000001";
2503        btc_check_fmt_non_negative_7, 1, "{:2}", "0.00000001";
2504        btc_check_fmt_non_negative_8, 1, "{:02}", "0.00000001";
2505        btc_check_fmt_non_negative_9, 1, "{:.1}", "0.0";
2506        btc_check_fmt_non_negative_10, 1, "{:11}", " 0.00000001";
2507        btc_check_fmt_non_negative_11, 1, "{:11.1}", "        0.0";
2508        btc_check_fmt_non_negative_12, 1, "{:011.1}", "000000000.0";
2509        btc_check_fmt_non_negative_13, 1, "{:.9}", "0.000000010";
2510        btc_check_fmt_non_negative_14, 1, "{:11.9}", "0.000000010";
2511        btc_check_fmt_non_negative_15, 1, "{:011.9}", "0.000000010";
2512        btc_check_fmt_non_negative_16, 1, "{:12.9}", " 0.000000010";
2513        btc_check_fmt_non_negative_17, 1, "{:012.9}", "00.000000010";
2514        btc_check_fmt_non_negative_18, 100_000_000, "{}", "1";
2515        btc_check_fmt_non_negative_19, 100_000_000, "{:2}", " 1";
2516        btc_check_fmt_non_negative_20, 100_000_000, "{:02}", "01";
2517        btc_check_fmt_non_negative_21, 100_000_000, "{:.1}", "1.0";
2518        btc_check_fmt_non_negative_22, 100_000_000, "{:4.1}", " 1.0";
2519        btc_check_fmt_non_negative_23, 100_000_000, "{:04.1}", "01.0";
2520        btc_check_fmt_non_negative_24, 110_000_000, "{}", "1.1";
2521        btc_check_fmt_non_negative_25, 100_000_001, "{}", "1.00000001";
2522        btc_check_fmt_non_negative_26, 100_000_001, "{:1}", "1.00000001";
2523        btc_check_fmt_non_negative_27, 100_000_001, "{:.1}", "1.0";
2524        btc_check_fmt_non_negative_28, 100_000_001, "{:10}", "1.00000001";
2525        btc_check_fmt_non_negative_29, 100_000_001, "{:11}", " 1.00000001";
2526        btc_check_fmt_non_negative_30, 100_000_001, "{:011}", "01.00000001";
2527        btc_check_fmt_non_negative_31, 100_000_001, "{:.8}", "1.00000001";
2528        btc_check_fmt_non_negative_32, 100_000_001, "{:.9}", "1.000000010";
2529        btc_check_fmt_non_negative_33, 100_000_001, "{:11.9}", "1.000000010";
2530        btc_check_fmt_non_negative_34, 100_000_001, "{:12.9}", " 1.000000010";
2531        btc_check_fmt_non_negative_35, 100_000_001, "{:012.9}", "01.000000010";
2532        btc_check_fmt_non_negative_36, 100_000_001, "{:+011.8}", "+1.00000001";
2533        btc_check_fmt_non_negative_37, 100_000_001, "{:+12.8}", " +1.00000001";
2534        btc_check_fmt_non_negative_38, 100_000_001, "{:+012.8}", "+01.00000001";
2535        btc_check_fmt_non_negative_39, 100_000_001, "{:+12.9}", "+1.000000010";
2536        btc_check_fmt_non_negative_40, 100_000_001, "{:+012.9}", "+1.000000010";
2537        btc_check_fmt_non_negative_41, 100_000_001, "{:+13.9}", " +1.000000010";
2538        btc_check_fmt_non_negative_42, 100_000_001, "{:+013.9}", "+01.000000010";
2539        btc_check_fmt_non_negative_43, 100_000_001, "{:<10}", "1.00000001";
2540        btc_check_fmt_non_negative_44, 100_000_001, "{:<11}", "1.00000001 ";
2541        btc_check_fmt_non_negative_45, 100_000_001, "{:<011}", "01.00000001";
2542        btc_check_fmt_non_negative_46, 100_000_001, "{:<11.9}", "1.000000010";
2543        btc_check_fmt_non_negative_47, 100_000_001, "{:<12.9}", "1.000000010 ";
2544        btc_check_fmt_non_negative_48, 100_000_001, "{:<12}", "1.00000001  ";
2545        btc_check_fmt_non_negative_49, 100_000_001, "{:^11}", "1.00000001 ";
2546        btc_check_fmt_non_negative_50, 100_000_001, "{:^11.9}", "1.000000010";
2547        btc_check_fmt_non_negative_51, 100_000_001, "{:^12.9}", "1.000000010 ";
2548        btc_check_fmt_non_negative_52, 100_000_001, "{:^12}", " 1.00000001 ";
2549        btc_check_fmt_non_negative_53, 100_000_001, "{:^12.9}", "1.000000010 ";
2550        btc_check_fmt_non_negative_54, 100_000_001, "{:^13.9}", " 1.000000010 ";
2551    }
2552
2553    check_format_non_negative_show_denom! {
2554        Bitcoin, " BTC";
2555        btc_check_fmt_non_negative_show_denom_0, 1, "{:14.1}", "       0.0";
2556        btc_check_fmt_non_negative_show_denom_1, 1, "{:14.8}", "0.00000001";
2557        btc_check_fmt_non_negative_show_denom_2, 1, "{:15}", " 0.00000001";
2558        btc_check_fmt_non_negative_show_denom_3, 1, "{:015}", "00.00000001";
2559        btc_check_fmt_non_negative_show_denom_4, 1, "{:.9}", "0.000000010";
2560        btc_check_fmt_non_negative_show_denom_5, 1, "{:15.9}", "0.000000010";
2561        btc_check_fmt_non_negative_show_denom_6, 1, "{:16.9}", " 0.000000010";
2562        btc_check_fmt_non_negative_show_denom_7, 1, "{:016.9}", "00.000000010";
2563    }
2564
2565    check_format_non_negative_show_denom! {
2566        Bitcoin, " BTC ";
2567        btc_check_fmt_non_negative_show_denom_align_0, 1, "{:<15}", "0.00000001";
2568        btc_check_fmt_non_negative_show_denom_align_1, 1, "{:^15}", "0.00000001";
2569        btc_check_fmt_non_negative_show_denom_align_2, 1, "{:^16}", " 0.00000001";
2570    }
2571
2572    #[test]
2573    fn test_unsigned_signed_conversion() {
2574        let sa = SignedAmount::from_sat;
2575        let ua = Amount::from_sat;
2576
2577        assert_eq!(Amount::MAX.to_signed(), Err(OutOfRangeError::too_big(true)));
2578        assert_eq!(ua(i64::MAX as u64).to_signed(), Ok(sa(i64::MAX)));
2579        assert_eq!(ua(i64::MAX as u64 + 1).to_signed(), Err(OutOfRangeError::too_big(true)));
2580
2581        assert_eq!(sa(i64::MAX).to_unsigned(), Ok(ua(i64::MAX as u64)));
2582
2583        assert_eq!(sa(i64::MAX).to_unsigned().unwrap().to_signed(), Ok(sa(i64::MAX)));
2584    }
2585
2586    #[test]
2587    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2588    fn from_str() {
2589        use ParseDenominationError::*;
2590
2591        use super::ParseAmountError as E;
2592
2593        assert_eq!(
2594            "x BTC".parse::<Amount>(),
2595            Err(InvalidCharacterError { invalid_char: 'x', position: 0 }.into())
2596        );
2597        assert_eq!(
2598            "xBTC".parse::<Amount>(),
2599            Err(Unknown(UnknownDenominationError("xBTC".into())).into()),
2600        );
2601        assert_eq!(
2602            "5 BTC BTC".parse::<Amount>(),
2603            Err(Unknown(UnknownDenominationError("BTC BTC".into())).into()),
2604        );
2605        assert_eq!(
2606            "5BTC BTC".parse::<Amount>(),
2607            Err(E::from(InvalidCharacterError { invalid_char: 'B', position: 1 }).into())
2608        );
2609        assert_eq!(
2610            "5 5 BTC".parse::<Amount>(),
2611            Err(Unknown(UnknownDenominationError("5 BTC".into())).into()),
2612        );
2613
2614        #[track_caller]
2615        fn ok_case(s: &str, expected: Amount) {
2616            assert_eq!(s.parse::<Amount>().unwrap(), expected);
2617            assert_eq!(s.replace(' ', "").parse::<Amount>().unwrap(), expected);
2618        }
2619
2620        #[track_caller]
2621        fn case(s: &str, expected: Result<Amount, impl Into<ParseError>>) {
2622            let expected = expected.map_err(Into::into);
2623            assert_eq!(s.parse::<Amount>(), expected);
2624            assert_eq!(s.replace(' ', "").parse::<Amount>(), expected);
2625        }
2626
2627        #[track_caller]
2628        fn ok_scase(s: &str, expected: SignedAmount) {
2629            assert_eq!(s.parse::<SignedAmount>().unwrap(), expected);
2630            assert_eq!(s.replace(' ', "").parse::<SignedAmount>().unwrap(), expected);
2631        }
2632
2633        #[track_caller]
2634        fn scase(s: &str, expected: Result<SignedAmount, impl Into<ParseError>>) {
2635            let expected = expected.map_err(Into::into);
2636            assert_eq!(s.parse::<SignedAmount>(), expected);
2637            assert_eq!(s.replace(' ', "").parse::<SignedAmount>(), expected);
2638        }
2639
2640        case("5 BCH", Err(Unknown(UnknownDenominationError("BCH".into()))));
2641
2642        case("-1 BTC", Err(OutOfRangeError::negative()));
2643        case("-0.0 BTC", Err(OutOfRangeError::negative()));
2644        case("0.123456789 BTC", Err(TooPreciseError { position: 10 }));
2645        scase("-0.1 satoshi", Err(TooPreciseError { position: 3 }));
2646        case("0.123456 mBTC", Err(TooPreciseError { position: 7 }));
2647        scase("-1.001 bits", Err(TooPreciseError { position: 5 }));
2648        scase("-200000000000 BTC", Err(OutOfRangeError::too_small()));
2649        case("18446744073709551616 sat", Err(OutOfRangeError::too_big(false)));
2650
2651        ok_case(".5 bits", Amount::from_sat(50));
2652        ok_scase("-.5 bits", SignedAmount::from_sat(-50));
2653        ok_case("0.00253583 BTC", Amount::from_sat(253583));
2654        ok_scase("-5 satoshi", SignedAmount::from_sat(-5));
2655        ok_case("0.10000000 BTC", Amount::from_sat(100_000_00));
2656        ok_scase("-100 bits", SignedAmount::from_sat(-10_000));
2657        #[cfg(feature = "alloc")]
2658        ok_scase(&format!("{} SAT", i64::MIN), SignedAmount::from_sat(i64::MIN));
2659    }
2660
2661    #[cfg(feature = "alloc")]
2662    #[test]
2663    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2664    fn to_from_string_in() {
2665        use super::Denomination as D;
2666        let ua_str = Amount::from_str_in;
2667        let ua_sat = Amount::from_sat;
2668        let sa_str = SignedAmount::from_str_in;
2669        let sa_sat = SignedAmount::from_sat;
2670
2671        assert_eq!("0.5", Amount::from_sat(50).to_string_in(D::Bit));
2672        assert_eq!("-0.5", SignedAmount::from_sat(-50).to_string_in(D::Bit));
2673        assert_eq!("0.00253583", Amount::from_sat(253583).to_string_in(D::Bitcoin));
2674        assert_eq!("-5", SignedAmount::from_sat(-5).to_string_in(D::Satoshi));
2675        assert_eq!("0.1", Amount::from_sat(100_000_00).to_string_in(D::Bitcoin));
2676        assert_eq!("-100", SignedAmount::from_sat(-10_000).to_string_in(D::Bit));
2677
2678        assert_eq!("0.50", format!("{:.2}", Amount::from_sat(50).display_in(D::Bit)));
2679        assert_eq!("-0.50", format!("{:.2}", SignedAmount::from_sat(-50).display_in(D::Bit)));
2680        assert_eq!(
2681            "0.10000000",
2682            format!("{:.8}", Amount::from_sat(100_000_00).display_in(D::Bitcoin))
2683        );
2684        assert_eq!("-100.00", format!("{:.2}", SignedAmount::from_sat(-10_000).display_in(D::Bit)));
2685
2686        assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
2687        assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
2688        assert_eq!(
2689            ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit),
2690            Ok(ua_sat(21_000_000))
2691        );
2692        assert_eq!(
2693            ua_str(&ua_sat(1).to_string_in(D::MicroBitcoin), D::MicroBitcoin),
2694            Ok(ua_sat(1))
2695        );
2696        assert_eq!(
2697            ua_str(&ua_sat(1_000_000_000_000).to_string_in(D::MilliBitcoin), D::MilliBitcoin),
2698            Ok(ua_sat(1_000_000_000_000))
2699        );
2700        assert!(ua_str(&ua_sat(u64::MAX).to_string_in(D::MilliBitcoin), D::MilliBitcoin).is_ok());
2701
2702        assert_eq!(
2703            sa_str(&sa_sat(-1).to_string_in(D::MicroBitcoin), D::MicroBitcoin),
2704            Ok(sa_sat(-1))
2705        );
2706
2707        assert_eq!(
2708            sa_str(&sa_sat(i64::MAX).to_string_in(D::Satoshi), D::MicroBitcoin),
2709            Err(OutOfRangeError::too_big(true).into())
2710        );
2711        // Test an overflow bug in `abs()`
2712        assert_eq!(
2713            sa_str(&sa_sat(i64::MIN).to_string_in(D::Satoshi), D::MicroBitcoin),
2714            Err(OutOfRangeError::too_small().into())
2715        );
2716    }
2717
2718    #[cfg(feature = "alloc")]
2719    #[test]
2720    fn to_string_with_denomination_from_str_roundtrip() {
2721        use ParseDenominationError::*;
2722
2723        use super::Denomination as D;
2724
2725        let amt = Amount::from_sat(42);
2726        let denom = Amount::to_string_with_denomination;
2727        assert_eq!(denom(amt, D::Bitcoin).parse::<Amount>(), Ok(amt));
2728        assert_eq!(denom(amt, D::MilliBitcoin).parse::<Amount>(), Ok(amt));
2729        assert_eq!(denom(amt, D::MicroBitcoin).parse::<Amount>(), Ok(amt));
2730        assert_eq!(denom(amt, D::Bit).parse::<Amount>(), Ok(amt));
2731        assert_eq!(denom(amt, D::Satoshi).parse::<Amount>(), Ok(amt));
2732
2733        assert_eq!(
2734            "42 satoshi BTC".parse::<Amount>(),
2735            Err(Unknown(UnknownDenominationError("satoshi BTC".into())).into()),
2736        );
2737        assert_eq!(
2738            "-42 satoshi BTC".parse::<SignedAmount>(),
2739            Err(Unknown(UnknownDenominationError("satoshi BTC".into())).into()),
2740        );
2741    }
2742
2743    #[cfg(feature = "serde")]
2744    #[test]
2745    fn serde_as_sat() {
2746        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2747        struct T {
2748            #[serde(with = "crate::amount::serde::as_sat")]
2749            pub amt: Amount,
2750            #[serde(with = "crate::amount::serde::as_sat")]
2751            pub samt: SignedAmount,
2752        }
2753
2754        serde_test::assert_tokens(
2755            &T { amt: Amount::from_sat(123456789), samt: SignedAmount::from_sat(-123456789) },
2756            &[
2757                serde_test::Token::Struct { name: "T", len: 2 },
2758                serde_test::Token::Str("amt"),
2759                serde_test::Token::U64(123456789),
2760                serde_test::Token::Str("samt"),
2761                serde_test::Token::I64(-123456789),
2762                serde_test::Token::StructEnd,
2763            ],
2764        );
2765    }
2766
2767    #[cfg(feature = "serde")]
2768    #[cfg(feature = "alloc")]
2769    #[test]
2770    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2771    fn serde_as_btc() {
2772        use serde_json;
2773
2774        #[derive(Serialize, Deserialize, PartialEq, Debug)]
2775        struct T {
2776            #[serde(with = "crate::amount::serde::as_btc")]
2777            pub amt: Amount,
2778            #[serde(with = "crate::amount::serde::as_btc")]
2779            pub samt: SignedAmount,
2780        }
2781
2782        let orig = T {
2783            amt: Amount::from_sat(21_000_000__000_000_01),
2784            samt: SignedAmount::from_sat(-21_000_000__000_000_01),
2785        };
2786
2787        let json = "{\"amt\": 21000000.00000001, \
2788                    \"samt\": -21000000.00000001}";
2789        let t: T = serde_json::from_str(json).unwrap();
2790        assert_eq!(t, orig);
2791
2792        let value: serde_json::Value = serde_json::from_str(json).unwrap();
2793        assert_eq!(t, serde_json::from_value(value).unwrap());
2794
2795        // errors
2796        let t: Result<T, serde_json::Error> =
2797            serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
2798        assert!(t
2799            .unwrap_err()
2800            .to_string()
2801            .contains(&ParseAmountError::TooPrecise(TooPreciseError { position: 16 }).to_string()));
2802        let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
2803        assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
2804    }
2805
2806    #[cfg(feature = "serde")]
2807    #[cfg(feature = "alloc")]
2808    #[test]
2809    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2810    fn serde_as_btc_opt() {
2811        use serde_json;
2812
2813        #[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
2814        struct T {
2815            #[serde(default, with = "crate::amount::serde::as_btc::opt")]
2816            pub amt: Option<Amount>,
2817            #[serde(default, with = "crate::amount::serde::as_btc::opt")]
2818            pub samt: Option<SignedAmount>,
2819        }
2820
2821        let with = T {
2822            amt: Some(Amount::from_sat(2_500_000_00)),
2823            samt: Some(SignedAmount::from_sat(-2_500_000_00)),
2824        };
2825        let without = T { amt: None, samt: None };
2826
2827        // Test Roundtripping
2828        for s in [&with, &without].iter() {
2829            let v = serde_json::to_string(s).unwrap();
2830            let w: T = serde_json::from_str(&v).unwrap();
2831            assert_eq!(w, **s);
2832        }
2833
2834        let t: T = serde_json::from_str("{\"amt\": 2.5, \"samt\": -2.5}").unwrap();
2835        assert_eq!(t, with);
2836
2837        let t: T = serde_json::from_str("{}").unwrap();
2838        assert_eq!(t, without);
2839
2840        let value_with: serde_json::Value =
2841            serde_json::from_str("{\"amt\": 2.5, \"samt\": -2.5}").unwrap();
2842        assert_eq!(with, serde_json::from_value(value_with).unwrap());
2843
2844        let value_without: serde_json::Value = serde_json::from_str("{}").unwrap();
2845        assert_eq!(without, serde_json::from_value(value_without).unwrap());
2846    }
2847
2848    #[cfg(feature = "serde")]
2849    #[cfg(feature = "alloc")]
2850    #[test]
2851    #[allow(clippy::inconsistent_digit_grouping)] // Group to show 100,000,000 sats per bitcoin.
2852    fn serde_as_sat_opt() {
2853        use serde_json;
2854
2855        #[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
2856        struct T {
2857            #[serde(default, with = "crate::amount::serde::as_sat::opt")]
2858            pub amt: Option<Amount>,
2859            #[serde(default, with = "crate::amount::serde::as_sat::opt")]
2860            pub samt: Option<SignedAmount>,
2861        }
2862
2863        let with = T {
2864            amt: Some(Amount::from_sat(2_500_000_00)),
2865            samt: Some(SignedAmount::from_sat(-2_500_000_00)),
2866        };
2867        let without = T { amt: None, samt: None };
2868
2869        // Test Roundtripping
2870        for s in [&with, &without].iter() {
2871            let v = serde_json::to_string(s).unwrap();
2872            let w: T = serde_json::from_str(&v).unwrap();
2873            assert_eq!(w, **s);
2874        }
2875
2876        let t: T = serde_json::from_str("{\"amt\": 250000000, \"samt\": -250000000}").unwrap();
2877        assert_eq!(t, with);
2878
2879        let t: T = serde_json::from_str("{}").unwrap();
2880        assert_eq!(t, without);
2881
2882        let value_with: serde_json::Value =
2883            serde_json::from_str("{\"amt\": 250000000, \"samt\": -250000000}").unwrap();
2884        assert_eq!(with, serde_json::from_value(value_with).unwrap());
2885
2886        let value_without: serde_json::Value = serde_json::from_str("{}").unwrap();
2887        assert_eq!(without, serde_json::from_value(value_without).unwrap());
2888    }
2889
2890    #[test]
2891    fn sum_amounts() {
2892        assert_eq!(Amount::from_sat(0), [].into_iter().sum::<Amount>());
2893        assert_eq!(SignedAmount::from_sat(0), [].into_iter().sum::<SignedAmount>());
2894
2895        let amounts = [Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
2896        let sum = amounts.into_iter().sum::<Amount>();
2897        assert_eq!(Amount::from_sat(1400), sum);
2898
2899        let amounts =
2900            [SignedAmount::from_sat(-42), SignedAmount::from_sat(1337), SignedAmount::from_sat(21)];
2901        let sum = amounts.into_iter().sum::<SignedAmount>();
2902        assert_eq!(SignedAmount::from_sat(1316), sum);
2903    }
2904
2905    #[test]
2906    fn checked_sum_amounts() {
2907        assert_eq!(Some(Amount::from_sat(0)), [].into_iter().checked_sum());
2908        assert_eq!(Some(SignedAmount::from_sat(0)), [].into_iter().checked_sum());
2909
2910        let amounts = [Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
2911        let sum = amounts.into_iter().checked_sum();
2912        assert_eq!(Some(Amount::from_sat(1400)), sum);
2913
2914        let amounts = [Amount::from_sat(u64::MAX), Amount::from_sat(1337), Amount::from_sat(21)];
2915        let sum = amounts.into_iter().checked_sum();
2916        assert_eq!(None, sum);
2917
2918        let amounts = [
2919            SignedAmount::from_sat(i64::MIN),
2920            SignedAmount::from_sat(-1),
2921            SignedAmount::from_sat(21),
2922        ];
2923        let sum = amounts.into_iter().checked_sum();
2924        assert_eq!(None, sum);
2925
2926        let amounts = [
2927            SignedAmount::from_sat(i64::MAX),
2928            SignedAmount::from_sat(1),
2929            SignedAmount::from_sat(21),
2930        ];
2931        let sum = amounts.into_iter().checked_sum();
2932        assert_eq!(None, sum);
2933
2934        let amounts =
2935            [SignedAmount::from_sat(42), SignedAmount::from_sat(3301), SignedAmount::from_sat(21)];
2936        let sum = amounts.into_iter().checked_sum();
2937        assert_eq!(Some(SignedAmount::from_sat(3364)), sum);
2938    }
2939
2940    #[test]
2941    fn denomination_string_acceptable_forms() {
2942        // Non-exhaustive list of valid forms.
2943        let valid = [
2944            "BTC", "btc", "mBTC", "mbtc", "uBTC", "ubtc", "SATOSHI", "satoshi", "SATOSHIS",
2945            "satoshis", "SAT", "sat", "SATS", "sats", "bit", "bits",
2946        ];
2947        for denom in valid.iter() {
2948            assert!(denom.parse::<Denomination>().is_ok());
2949        }
2950    }
2951
2952    #[test]
2953    fn disallow_confusing_forms() {
2954        let confusing = ["CBTC", "Cbtc", "MBTC", "Mbtc", "UBTC", "Ubtc"];
2955        for denom in confusing.iter() {
2956            match denom.parse::<Denomination>() {
2957                Ok(_) => panic!("from_str should error for {}", denom),
2958                Err(ParseDenominationError::PossiblyConfusing(_)) => {}
2959                Err(e) => panic!("unexpected error: {}", e),
2960            }
2961        }
2962    }
2963
2964    #[test]
2965    fn disallow_unknown_denomination() {
2966        // Non-exhaustive list of unknown forms.
2967        let unknown = ["NBTC", "ABC", "abc", "mSat", "msat"];
2968        for denom in unknown.iter() {
2969            match denom.parse::<Denomination>() {
2970                Ok(_) => panic!("from_str should error for {}", denom),
2971                Err(ParseDenominationError::Unknown(_)) => (),
2972                Err(e) => panic!("unexpected error: {}", e),
2973            }
2974        }
2975    }
2976
2977    #[test]
2978    #[cfg(feature = "alloc")]
2979    fn trailing_zeros_for_amount() {
2980        assert_eq!(format!("{}", Amount::from_sat(1000000)), "0.01 BTC");
2981        assert_eq!(format!("{}", Amount::ONE_SAT), "0.00000001 BTC");
2982        assert_eq!(format!("{}", Amount::ONE_BTC), "1 BTC");
2983        assert_eq!(format!("{}", Amount::from_sat(1)), "0.00000001 BTC");
2984        assert_eq!(format!("{}", Amount::from_sat(10)), "0.0000001 BTC");
2985        assert_eq!(format!("{:.2}", Amount::from_sat(10)), "0.00 BTC");
2986        assert_eq!(format!("{:.2}", Amount::from_sat(100)), "0.00 BTC");
2987        assert_eq!(format!("{:.2}", Amount::from_sat(1000)), "0.00 BTC");
2988        assert_eq!(format!("{:.2}", Amount::from_sat(10_000)), "0.00 BTC");
2989        assert_eq!(format!("{:.2}", Amount::from_sat(100_000)), "0.00 BTC");
2990        assert_eq!(format!("{:.2}", Amount::from_sat(1_000_000)), "0.01 BTC");
2991        assert_eq!(format!("{:.2}", Amount::from_sat(10_000_000)), "0.10 BTC");
2992        assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC");
2993        assert_eq!(format!("{:.2}", Amount::from_sat(500_000)), "0.01 BTC");
2994        assert_eq!(format!("{:.2}", Amount::from_sat(9_500_000)), "0.10 BTC");
2995        assert_eq!(format!("{:.2}", Amount::from_sat(99_500_000)), "1.00 BTC");
2996        assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC");
2997        assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC");
2998        assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 BTC");
2999        assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_010)), "4000000.0000001 BTC");
3000        assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_000)), "4000000 BTC");
3001    }
3002}