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