satsnet_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 satsnet_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            }
111            "mSAT" | "msat" | "mSATs" | "msats" => Some(Denomination::MilliSatoshi),
112            _ => None,
113        }
114    }
115}
116
117/// These form are ambigous and could have many meanings.  For example, M could denote Mega or Milli.
118/// If any of these forms are used, an error type PossiblyConfusingDenomination is returned.
119const CONFUSING_FORMS: [&str; 9] = [
120    "Msat", "Msats", "MSAT", "MSATS", "MSat", "MSats", "MBTC", "Mbtc", "PBTC",
121];
122
123impl fmt::Display for Denomination {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        f.write_str(self.as_str())
126    }
127}
128
129impl FromStr for Denomination {
130    type Err = ParseDenominationError;
131
132    /// Convert from a str to Denomination.
133    ///
134    /// Any combination of upper and/or lower case, excluding uppercase of SI(m, u, n, p) is considered valid.
135    /// - Singular: BTC, mBTC, uBTC, nBTC, pBTC
136    /// - Plural or singular: sat, satoshi, bit, msat
137    ///
138    /// Due to ambiguity between mega and milli, pico and peta we prohibit usage of leading capital 'M', 'P'.
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        use self::ParseDenominationError::*;
141
142        if CONFUSING_FORMS.contains(&s) {
143            return Err(PossiblyConfusing(PossiblyConfusingDenominationError(
144                s.into(),
145            )));
146        };
147
148        let form = self::Denomination::forms(s);
149
150        form.ok_or_else(|| Unknown(UnknownDenominationError(s.into())))
151    }
152}
153
154/// An error during amount parsing amount with denomination.
155#[derive(Debug, Clone, PartialEq, Eq)]
156#[non_exhaustive]
157pub enum ParseError {
158    /// Invalid amount.
159    Amount(ParseAmountError),
160
161    /// Invalid denomination.
162    Denomination(ParseDenominationError),
163
164    /// The denomination was not identified.
165    MissingDenomination(MissingDenominationError),
166}
167
168internals::impl_from_infallible!(ParseError);
169
170impl From<ParseAmountError> for ParseError {
171    fn from(e: ParseAmountError) -> Self {
172        Self::Amount(e)
173    }
174}
175
176impl From<ParseDenominationError> for ParseError {
177    fn from(e: ParseDenominationError) -> Self {
178        Self::Denomination(e)
179    }
180}
181
182impl From<OutOfRangeError> for ParseError {
183    fn from(e: OutOfRangeError) -> Self {
184        Self::Amount(e.into())
185    }
186}
187
188impl From<TooPreciseError> for ParseError {
189    fn from(e: TooPreciseError) -> Self {
190        Self::Amount(e.into())
191    }
192}
193
194impl From<MissingDigitsError> for ParseError {
195    fn from(e: MissingDigitsError) -> Self {
196        Self::Amount(e.into())
197    }
198}
199
200impl From<InputTooLargeError> for ParseError {
201    fn from(e: InputTooLargeError) -> Self {
202        Self::Amount(e.into())
203    }
204}
205
206impl From<InvalidCharacterError> for ParseError {
207    fn from(e: InvalidCharacterError) -> Self {
208        Self::Amount(e.into())
209    }
210}
211
212impl fmt::Display for ParseError {
213    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214        match self {
215            ParseError::Amount(error) => write_err!(f, "invalid amount"; error),
216            ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
217            // We consider this to not be a source because it currently doesn't contain useful
218            // information
219            ParseError::MissingDenomination(_) => {
220                f.write_str("the input doesn't contain a denomination")
221            }
222        }
223    }
224}
225
226#[cfg(feature = "std")]
227impl std::error::Error for ParseError {
228    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
229        match self {
230            ParseError::Amount(error) => Some(error),
231            ParseError::Denomination(error) => Some(error),
232            // We consider this to not be a source because it currently doesn't contain useful
233            // information
234            ParseError::MissingDenomination(_) => None,
235        }
236    }
237}
238
239/// An error during amount parsing.
240#[derive(Debug, Clone, PartialEq, Eq)]
241#[non_exhaustive]
242pub enum ParseAmountError {
243    /// The amount is too big or too small.
244    OutOfRange(OutOfRangeError),
245    /// Amount has higher precision than supported by the type.
246    TooPrecise(TooPreciseError),
247    /// A digit was expected but not found.
248    MissingDigits(MissingDigitsError),
249    /// Input string was too large.
250    InputTooLarge(InputTooLargeError),
251    /// Invalid character in input.
252    InvalidCharacter(InvalidCharacterError),
253}
254
255impl From<TooPreciseError> for ParseAmountError {
256    fn from(value: TooPreciseError) -> Self {
257        Self::TooPrecise(value)
258    }
259}
260
261impl From<MissingDigitsError> for ParseAmountError {
262    fn from(value: MissingDigitsError) -> Self {
263        Self::MissingDigits(value)
264    }
265}
266
267impl From<InputTooLargeError> for ParseAmountError {
268    fn from(value: InputTooLargeError) -> Self {
269        Self::InputTooLarge(value)
270    }
271}
272
273impl From<InvalidCharacterError> for ParseAmountError {
274    fn from(value: InvalidCharacterError) -> Self {
275        Self::InvalidCharacter(value)
276    }
277}
278
279internals::impl_from_infallible!(ParseAmountError);
280
281impl fmt::Display for ParseAmountError {
282    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283        use ParseAmountError::*;
284
285        match *self {
286            OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
287            TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
288            MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
289            InputTooLarge(ref error) => write_err!(f, "the input is too large"; error),
290            InvalidCharacter(ref error) => write_err!(f, "invalid character in the input"; error),
291        }
292    }
293}
294
295#[cfg(feature = "std")]
296impl std::error::Error for ParseAmountError {
297    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
298        use ParseAmountError::*;
299
300        match *self {
301            TooPrecise(ref error) => Some(error),
302            InputTooLarge(ref error) => Some(error),
303            OutOfRange(ref error) => Some(error),
304            MissingDigits(ref error) => Some(error),
305            InvalidCharacter(ref error) => Some(error),
306        }
307    }
308}
309
310/// Returned when a parsed amount is too big or too small.
311#[derive(Debug, Copy, Clone, Eq, PartialEq)]
312pub struct OutOfRangeError {
313    is_signed: bool,
314    is_greater_than_max: bool,
315}
316
317impl OutOfRangeError {
318    /// Returns the minimum and maximum allowed values for the type that was parsed.
319    ///
320    /// This can be used to give a hint to the user which values are allowed.
321    pub fn valid_range(&self) -> (i64, u64) {
322        match self.is_signed {
323            true => (i64::MIN, i64::MAX as u64),
324            false => (0, u64::MAX),
325        }
326    }
327
328    /// Returns true if the input value was large than the maximum allowed value.
329    pub fn is_above_max(&self) -> bool {
330        self.is_greater_than_max
331    }
332
333    /// Returns true if the input value was smaller than the minimum allowed value.
334    pub fn is_below_min(&self) -> bool {
335        !self.is_greater_than_max
336    }
337
338    pub(crate) fn too_big(is_signed: bool) -> Self {
339        Self {
340            is_signed,
341            is_greater_than_max: true,
342        }
343    }
344
345    pub(crate) fn too_small() -> Self {
346        Self {
347            // implied - negative() is used for the other
348            is_signed: true,
349            is_greater_than_max: false,
350        }
351    }
352
353    pub(crate) fn negative() -> Self {
354        Self {
355            // implied - too_small() is used for the other
356            is_signed: false,
357            is_greater_than_max: false,
358        }
359    }
360}
361
362impl fmt::Display for OutOfRangeError {
363    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
364        if self.is_greater_than_max {
365            write!(f, "the amount is greater than {}", self.valid_range().1)
366        } else {
367            write!(f, "the amount is less than {}", self.valid_range().0)
368        }
369    }
370}
371
372#[cfg(feature = "std")]
373impl std::error::Error for OutOfRangeError {}
374
375impl From<OutOfRangeError> for ParseAmountError {
376    fn from(value: OutOfRangeError) -> Self {
377        ParseAmountError::OutOfRange(value)
378    }
379}
380
381/// Error returned when the input string has higher precision than satoshis.
382#[derive(Debug, Clone, Eq, PartialEq)]
383pub struct TooPreciseError {
384    position: usize,
385}
386
387impl fmt::Display for TooPreciseError {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        match self.position {
390            0 => f.write_str("the amount is less than 1 satoshi but it's not zero"),
391            pos => write!(
392                f,
393                "the digits starting from position {} represent a sub-satoshi amount",
394                pos
395            ),
396        }
397    }
398}
399
400#[cfg(feature = "std")]
401impl std::error::Error for TooPreciseError {}
402
403/// Error returned when the input string is too large.
404#[derive(Debug, Clone, Eq, PartialEq)]
405pub struct InputTooLargeError {
406    len: usize,
407}
408
409impl fmt::Display for InputTooLargeError {
410    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
411        match self.len - INPUT_STRING_LEN_LIMIT {
412            1 => write!(
413                f,
414                "the input is one character longer than the maximum allowed length ({})",
415                INPUT_STRING_LEN_LIMIT
416            ),
417            n => write!(
418                f,
419                "the input is {} characters longer than the maximum allowed length ({})",
420                n, INPUT_STRING_LEN_LIMIT
421            ),
422        }
423    }
424}
425
426#[cfg(feature = "std")]
427impl std::error::Error for InputTooLargeError {}
428
429/// Error returned when digits were expected in the input but there were none.
430///
431/// In particular, this is currently returned when the string is empty or only contains the minus sign.
432#[derive(Debug, Clone, Eq, PartialEq)]
433pub struct MissingDigitsError {
434    kind: MissingDigitsKind,
435}
436
437impl fmt::Display for MissingDigitsError {
438    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439        match self.kind {
440            MissingDigitsKind::Empty => f.write_str("the input is empty"),
441            MissingDigitsKind::OnlyMinusSign => {
442                f.write_str("there are no digits following the minus (-) sign")
443            }
444        }
445    }
446}
447
448#[cfg(feature = "std")]
449impl std::error::Error for MissingDigitsError {}
450
451#[derive(Debug, Clone, Eq, PartialEq)]
452enum MissingDigitsKind {
453    Empty,
454    OnlyMinusSign,
455}
456
457/// Returned when the input contains an invalid character.
458#[derive(Debug, Clone, PartialEq, Eq)]
459pub struct InvalidCharacterError {
460    invalid_char: char,
461    position: usize,
462}
463
464impl fmt::Display for InvalidCharacterError {
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        match self.invalid_char {
467            '.' => f.write_str("there is more than one decimal separator (dot) in the input"),
468            '-' => f.write_str("there is more than one minus sign (-) in the input"),
469            c => write!(
470                f,
471                "the character '{}' at position {} is not a valid digit",
472                c, self.position
473            ),
474        }
475    }
476}
477
478#[cfg(feature = "std")]
479impl std::error::Error for InvalidCharacterError {}
480
481/// An error during amount parsing.
482#[derive(Debug, Clone, PartialEq, Eq)]
483#[non_exhaustive]
484pub enum ParseDenominationError {
485    /// The denomination was unknown.
486    Unknown(UnknownDenominationError),
487    /// The denomination has multiple possible interpretations.
488    PossiblyConfusing(PossiblyConfusingDenominationError),
489}
490
491internals::impl_from_infallible!(ParseDenominationError);
492
493impl fmt::Display for ParseDenominationError {
494    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
495        use ParseDenominationError::*;
496
497        match *self {
498            Unknown(ref e) => write_err!(f, "denomination parse error"; e),
499            PossiblyConfusing(ref e) => write_err!(f, "denomination parse error"; e),
500        }
501    }
502}
503
504#[cfg(feature = "std")]
505impl std::error::Error for ParseDenominationError {
506    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
507        use ParseDenominationError::*;
508
509        match *self {
510            Unknown(_) | PossiblyConfusing(_) => None,
511        }
512    }
513}
514
515/// Error returned when the denomination is empty.
516#[derive(Debug, Clone, PartialEq, Eq)]
517#[non_exhaustive]
518pub struct MissingDenominationError;
519
520/// Parsing error, unknown denomination.
521#[derive(Debug, Clone, PartialEq, Eq)]
522#[non_exhaustive]
523pub struct UnknownDenominationError(InputString);
524
525impl fmt::Display for UnknownDenominationError {
526    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
527        self.0.unknown_variant("bitcoin denomination", f)
528    }
529}
530
531#[cfg(feature = "std")]
532impl std::error::Error for UnknownDenominationError {
533    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
534        None
535    }
536}
537
538/// Parsing error, possibly confusing denomination.
539#[derive(Debug, Clone, PartialEq, Eq)]
540#[non_exhaustive]
541pub struct PossiblyConfusingDenominationError(InputString);
542
543impl fmt::Display for PossiblyConfusingDenominationError {
544    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
545        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"))
546    }
547}
548
549#[cfg(feature = "std")]
550impl std::error::Error for PossiblyConfusingDenominationError {
551    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
552        None
553    }
554}
555
556/// Returns `Some(position)` if the precision is not supported.
557///
558/// The position indicates the first digit that is too precise.
559fn is_too_precise(s: &str, precision: usize) -> Option<usize> {
560    match s.find('.') {
561        Some(pos) if precision >= pos => Some(0),
562        Some(pos) => s[..pos]
563            .char_indices()
564            .rev()
565            .take(precision)
566            .find(|(_, d)| *d != '0')
567            .map(|(i, _)| i)
568            .or_else(|| {
569                s[(pos + 1)..]
570                    .char_indices()
571                    .find(|(_, d)| *d != '0')
572                    .map(|(i, _)| i + pos + 1)
573            }),
574        None if precision >= s.len() => Some(0),
575        None => s
576            .char_indices()
577            .rev()
578            .take(precision)
579            .find(|(_, d)| *d != '0')
580            .map(|(i, _)| i),
581    }
582}
583
584const INPUT_STRING_LEN_LIMIT: usize = 50;
585
586/// Parse decimal string in the given denomination into a satoshi value and a
587/// bool indicator for a negative amount.
588fn parse_signed_to_satoshi(
589    mut s: &str,
590    denom: Denomination,
591) -> Result<(bool, u64), InnerParseError> {
592    if s.is_empty() {
593        return Err(InnerParseError::MissingDigits(MissingDigitsError {
594            kind: MissingDigitsKind::Empty,
595        }));
596    }
597    if s.len() > INPUT_STRING_LEN_LIMIT {
598        return Err(InnerParseError::InputTooLarge(s.len()));
599    }
600
601    let is_negative = s.starts_with('-');
602    if is_negative {
603        if s.len() == 1 {
604            return Err(InnerParseError::MissingDigits(MissingDigitsError {
605                kind: MissingDigitsKind::OnlyMinusSign,
606            }));
607        }
608        s = &s[1..];
609    }
610
611    let max_decimals = {
612        // The difference in precision between native (satoshi)
613        // and desired denomination.
614        let precision_diff = -denom.precision();
615        if precision_diff <= 0 {
616            // If precision diff is negative, this means we are parsing
617            // into a less precise amount. That is not allowed unless
618            // there are no decimals and the last digits are zeroes as
619            // many as the difference in precision.
620            let last_n = precision_diff.unsigned_abs().into();
621            if let Some(position) = is_too_precise(s, last_n) {
622                match s.parse::<i64>() {
623                    Ok(0) => return Ok((is_negative, 0)),
624                    _ => {
625                        return Err(InnerParseError::TooPrecise(TooPreciseError {
626                            position: position + is_negative as usize,
627                        }))
628                    }
629                }
630            }
631            s = &s[0..s.find('.').unwrap_or(s.len()) - last_n];
632            0
633        } else {
634            precision_diff
635        }
636    };
637
638    let mut decimals = None;
639    let mut value: u64 = 0; // as satoshis
640    for (i, c) in s.char_indices() {
641        match c {
642            '0'..='9' => {
643                // Do `value = 10 * value + digit`, catching overflows.
644                match 10_u64.checked_mul(value) {
645                    None => return Err(InnerParseError::Overflow { is_negative }),
646                    Some(val) => match val.checked_add((c as u8 - b'0') as u64) {
647                        None => return Err(InnerParseError::Overflow { is_negative }),
648                        Some(val) => value = val,
649                    },
650                }
651                // Increment the decimal digit counter if past decimal.
652                decimals = match decimals {
653                    None => None,
654                    Some(d) if d < max_decimals => Some(d + 1),
655                    _ => {
656                        return Err(InnerParseError::TooPrecise(TooPreciseError {
657                            position: i + is_negative as usize,
658                        }))
659                    }
660                };
661            }
662            '.' => match decimals {
663                None if max_decimals <= 0 => break,
664                None => decimals = Some(0),
665                // Double decimal dot.
666                _ => {
667                    return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
668                        invalid_char: '.',
669                        position: i + is_negative as usize,
670                    }))
671                }
672            },
673            c => {
674                return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
675                    invalid_char: c,
676                    position: i + is_negative as usize,
677                }))
678            }
679        }
680    }
681
682    // Decimally shift left by `max_decimals - decimals`.
683    let scale_factor = max_decimals - decimals.unwrap_or(0);
684    for _ in 0..scale_factor {
685        value = match 10_u64.checked_mul(value) {
686            Some(v) => v,
687            None => return Err(InnerParseError::Overflow { is_negative }),
688        };
689    }
690
691    Ok((is_negative, value))
692}
693
694enum InnerParseError {
695    Overflow { is_negative: bool },
696    TooPrecise(TooPreciseError),
697    MissingDigits(MissingDigitsError),
698    InputTooLarge(usize),
699    InvalidCharacter(InvalidCharacterError),
700}
701
702internals::impl_from_infallible!(InnerParseError);
703
704impl InnerParseError {
705    fn convert(self, is_signed: bool) -> ParseAmountError {
706        match self {
707            Self::Overflow { is_negative } => OutOfRangeError {
708                is_signed,
709                is_greater_than_max: !is_negative,
710            }
711            .into(),
712            Self::TooPrecise(error) => ParseAmountError::TooPrecise(error),
713            Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
714            Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
715            Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
716        }
717    }
718}
719
720fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseError> {
721    let (i, j) = if let Some(i) = s.find(' ') {
722        (i, i + 1)
723    } else {
724        let i = s
725            .find(|c: char| c.is_alphabetic())
726            .ok_or(ParseError::MissingDenomination(MissingDenominationError))?;
727        (i, i)
728    };
729    Ok((&s[..i], s[j..].parse()?))
730}
731
732/// Options given by `fmt::Formatter`
733struct FormatOptions {
734    fill: char,
735    align: Option<fmt::Alignment>,
736    width: Option<usize>,
737    precision: Option<usize>,
738    sign_plus: bool,
739    sign_aware_zero_pad: bool,
740}
741
742impl FormatOptions {
743    fn from_formatter(f: &fmt::Formatter) -> Self {
744        FormatOptions {
745            fill: f.fill(),
746            align: f.align(),
747            width: f.width(),
748            precision: f.precision(),
749            sign_plus: f.sign_plus(),
750            sign_aware_zero_pad: f.sign_aware_zero_pad(),
751        }
752    }
753}
754
755impl Default for FormatOptions {
756    fn default() -> Self {
757        FormatOptions {
758            fill: ' ',
759            align: None,
760            width: None,
761            precision: None,
762            sign_plus: false,
763            sign_aware_zero_pad: false,
764        }
765    }
766}
767
768fn dec_width(mut num: u64) -> usize {
769    let mut width = 1;
770    loop {
771        num /= 10;
772        if num == 0 {
773            break;
774        }
775        width += 1;
776    }
777    width
778}
779
780fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
781    for _ in 0..count {
782        f.write_char(c)?;
783    }
784    Ok(())
785}
786
787/// Format the given satoshi amount in the given denomination.
788fn fmt_satoshi_in(
789    satoshi: u64,
790    negative: bool,
791    f: &mut dyn fmt::Write,
792    denom: Denomination,
793    show_denom: bool,
794    options: FormatOptions,
795) -> fmt::Result {
796    let precision = denom.precision();
797    // First we normalize the number:
798    // {num_before_decimal_point}{:0exp}{"." if nb_decimals > 0}{:0nb_decimals}{num_after_decimal_point}{:0trailing_decimal_zeros}
799    let mut num_after_decimal_point = 0;
800    let mut norm_nb_decimals = 0;
801    let mut num_before_decimal_point = satoshi;
802    let trailing_decimal_zeros;
803    let mut exp = 0;
804    match precision.cmp(&0) {
805        // We add the number of zeroes to the end
806        Ordering::Greater => {
807            if satoshi > 0 {
808                exp = precision as usize;
809            }
810            trailing_decimal_zeros = options.precision.unwrap_or(0);
811        }
812        Ordering::Less => {
813            let precision = precision.unsigned_abs();
814            let divisor = 10u64.pow(precision.into());
815            num_before_decimal_point = satoshi / divisor;
816            num_after_decimal_point = satoshi % divisor;
817            // normalize by stripping trailing zeros
818            if num_after_decimal_point == 0 {
819                norm_nb_decimals = 0;
820            } else {
821                norm_nb_decimals = usize::from(precision);
822                while num_after_decimal_point % 10 == 0 {
823                    norm_nb_decimals -= 1;
824                    num_after_decimal_point /= 10
825                }
826            }
827            // compute requested precision
828            let opt_precision = options.precision.unwrap_or(0);
829            trailing_decimal_zeros = opt_precision.saturating_sub(norm_nb_decimals);
830        }
831        Ordering::Equal => trailing_decimal_zeros = options.precision.unwrap_or(0),
832    }
833    let total_decimals = norm_nb_decimals + trailing_decimal_zeros;
834    // Compute expected width of the number
835    let mut num_width = if total_decimals > 0 {
836        // 1 for decimal point
837        1 + total_decimals
838    } else {
839        0
840    };
841    num_width += dec_width(num_before_decimal_point) + exp;
842    if options.sign_plus || negative {
843        num_width += 1;
844    }
845
846    if show_denom {
847        // + 1 for space
848        num_width += denom.as_str().len() + 1;
849    }
850
851    let width = options.width.unwrap_or(0);
852    let align = options.align.unwrap_or(fmt::Alignment::Right);
853    let (left_pad, pad_right) = match (num_width < width, options.sign_aware_zero_pad, align) {
854        (false, _, _) => (0, 0),
855        // Alignment is always right (ignored) when zero-padding
856        (true, true, _) | (true, false, fmt::Alignment::Right) => (width - num_width, 0),
857        (true, false, fmt::Alignment::Left) => (0, width - num_width),
858        // If the required padding is odd it needs to be skewed to the left
859        (true, false, fmt::Alignment::Center) => {
860            ((width - num_width) / 2, (width - num_width + 1) / 2)
861        }
862    };
863
864    if !options.sign_aware_zero_pad {
865        repeat_char(f, options.fill, left_pad)?;
866    }
867
868    if negative {
869        write!(f, "-")?;
870    } else if options.sign_plus {
871        write!(f, "+")?;
872    }
873
874    if options.sign_aware_zero_pad {
875        repeat_char(f, '0', left_pad)?;
876    }
877
878    write!(f, "{}", num_before_decimal_point)?;
879
880    repeat_char(f, '0', exp)?;
881
882    if total_decimals > 0 {
883        write!(f, ".")?;
884    }
885    if norm_nb_decimals > 0 {
886        write!(
887            f,
888            "{:0width$}",
889            num_after_decimal_point,
890            width = norm_nb_decimals
891        )?;
892    }
893    repeat_char(f, '0', trailing_decimal_zeros)?;
894
895    if show_denom {
896        write!(f, " {}", denom.as_str())?;
897    }
898
899    repeat_char(f, options.fill, pad_right)?;
900    Ok(())
901}
902
903/// Amount
904///
905/// The [Amount] type can be used to express Bitcoin amounts that support
906/// arithmetic and conversion to various denominations.
907///
908///
909/// Warning!
910///
911/// This type implements several arithmetic operations from [core::ops].
912/// To prevent errors due to overflow or underflow when using these operations,
913/// it is advised to instead use the checked arithmetic methods whose names
914/// start with `checked_`.  The operations from [core::ops] that [Amount]
915/// implements will panic when overflow or underflow occurs.  Also note that
916/// since the internal representation of amounts is unsigned, subtracting below
917/// zero is considered an underflow and will cause a panic if you're not using
918/// the checked arithmetic methods.
919///
920#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
921#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
922pub struct Amount(u64);
923
924impl Amount {
925    /// The zero amount.
926    pub const ZERO: Amount = Amount(0);
927    /// Exactly one satoshi.
928    pub const ONE_SAT: Amount = Amount(1);
929    /// Exactly one bitcoin.
930    pub const ONE_BTC: Amount = Self::from_int_btc(1);
931    /// The maximum value allowed as an amount. Useful for sanity checking.
932    pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000);
933    /// The minimum value of an amount.
934    pub const MIN: Amount = Amount::ZERO;
935    /// The maximum value of an amount.
936    pub const MAX: Amount = Amount(u64::MAX);
937    /// The number of bytes that an amount contributes to the size of a transaction.
938    pub const SIZE: usize = 8; // Serialized length of a u64.
939
940    /// Create an [Amount] with satoshi precision and the given number of satoshis.
941    pub const fn from_sat(satoshi: u64) -> Amount {
942        Amount(satoshi)
943    }
944
945    /// Gets the number of satoshis in this [`Amount`].
946    pub fn to_sat(self) -> u64 {
947        self.0
948    }
949
950    /// Convert from a value expressing bitcoins to an [Amount].
951    #[cfg(feature = "alloc")]
952    pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
953        Amount::from_float_in(btc, Denomination::Bitcoin)
954    }
955
956    /// Convert from a value expressing integer values of bitcoins to an [Amount]
957    /// in const context.
958    ///
959    /// ## Panics
960    ///
961    /// The function panics if the argument multiplied by the number of sats
962    /// per bitcoin overflows a u64 type.
963    pub const fn from_int_btc(btc: u64) -> Amount {
964        match btc.checked_mul(100_000_000) {
965            Some(amount) => Amount::from_sat(amount),
966            None => {
967                // When MSRV is 1.57+ we can use `panic!()`.
968                #[allow(unconditional_panic)]
969                #[allow(clippy::let_unit_value)]
970                #[allow(clippy::out_of_bounds_indexing)]
971                let _int_overflow_converting_btc_to_sats = [(); 0][1];
972                Amount(0)
973            }
974        }
975    }
976
977    /// Parse a decimal string as a value in the given denomination.
978    ///
979    /// Note: This only parses the value string.  If you want to parse a value
980    /// with denomination, use [FromStr].
981    pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
982        let (negative, satoshi) =
983            parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
984        if negative {
985            return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
986        }
987        Ok(Amount::from_sat(satoshi))
988    }
989
990    /// Parses amounts with denomination suffix like they are produced with
991    /// [Self::to_string_with_denomination] or with [fmt::Display].
992    /// If you want to parse only the amount without the denomination,
993    /// use [Self::from_str_in].
994    pub fn from_str_with_denomination(s: &str) -> Result<Amount, ParseError> {
995        let (amt, denom) = split_amount_and_denomination(s)?;
996        Amount::from_str_in(amt, denom).map_err(Into::into)
997    }
998
999    /// Express this [Amount] as a floating-point value in the given denomination.
1000    ///
1001    /// Please be aware of the risk of using floating-point numbers.
1002    #[cfg(feature = "alloc")]
1003    pub fn to_float_in(self, denom: Denomination) -> f64 {
1004        f64::from_str(&self.to_string_in(denom)).unwrap()
1005    }
1006
1007    /// Express this [`Amount`] as a floating-point value in Bitcoin.
1008    ///
1009    /// Please be aware of the risk of using floating-point numbers.
1010    ///
1011    /// # Examples
1012    /// ```
1013    /// # use satsnet_units::amount::{Amount, Denomination};
1014    /// let amount = Amount::from_sat(100_000);
1015    /// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin))
1016    /// ```
1017    #[cfg(feature = "alloc")]
1018    pub fn to_btc(self) -> f64 {
1019        self.to_float_in(Denomination::Bitcoin)
1020    }
1021
1022    /// Convert this [Amount] in floating-point notation with a given
1023    /// denomination.
1024    /// Can return error if the amount is too big, too precise or negative.
1025    ///
1026    /// Please be aware of the risk of using floating-point numbers.
1027    #[cfg(feature = "alloc")]
1028    pub fn from_float_in(value: f64, denom: Denomination) -> Result<Amount, ParseAmountError> {
1029        if value < 0.0 {
1030            return Err(OutOfRangeError::negative().into());
1031        }
1032        // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
1033        // Any performance-critical application should not be dealing with floats.
1034        Amount::from_str_in(&value.to_string(), denom)
1035    }
1036
1037    /// Create an object that implements [`fmt::Display`] using specified denomination.
1038    pub fn display_in(self, denomination: Denomination) -> Display {
1039        Display {
1040            sats_abs: self.to_sat(),
1041            is_negative: false,
1042            style: DisplayStyle::FixedDenomination {
1043                denomination,
1044                show_denomination: false,
1045            },
1046        }
1047    }
1048
1049    /// Create an object that implements [`fmt::Display`] dynamically selecting denomination.
1050    ///
1051    /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
1052    /// avoid confusion the denomination is always shown.
1053    pub fn display_dynamic(self) -> Display {
1054        Display {
1055            sats_abs: self.to_sat(),
1056            is_negative: false,
1057            style: DisplayStyle::DynamicDenomination,
1058        }
1059    }
1060
1061    /// Format the value of this [Amount] in the given denomination.
1062    ///
1063    /// Does not include the denomination.
1064    #[rustfmt::skip]
1065    pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
1066        fmt_satoshi_in(self.to_sat(), false, f, denom, false, FormatOptions::default())
1067    }
1068
1069    /// Get a string number of this [Amount] in the given denomination.
1070    ///
1071    /// Does not include the denomination.
1072    #[cfg(feature = "alloc")]
1073    pub fn to_string_in(self, denom: Denomination) -> String {
1074        let mut buf = String::new();
1075        self.fmt_value_in(&mut buf, denom).unwrap();
1076        buf
1077    }
1078
1079    /// Get a formatted string of this [Amount] in the given denomination,
1080    /// suffixed with the abbreviation for the denomination.
1081    #[cfg(feature = "alloc")]
1082    pub fn to_string_with_denomination(self, denom: Denomination) -> String {
1083        let mut buf = String::new();
1084        self.fmt_value_in(&mut buf, denom).unwrap();
1085        write!(buf, " {}", denom).unwrap();
1086        buf
1087    }
1088
1089    // Some arithmetic that doesn't fit in `core::ops` traits.
1090
1091    /// Checked addition.
1092    ///
1093    /// Returns [None] if overflow occurred.
1094    pub fn checked_add(self, rhs: Amount) -> Option<Amount> {
1095        self.0.checked_add(rhs.0).map(Amount)
1096    }
1097
1098    /// Checked subtraction.
1099    ///
1100    /// Returns [None] if overflow occurred.
1101    pub fn checked_sub(self, rhs: Amount) -> Option<Amount> {
1102        self.0.checked_sub(rhs.0).map(Amount)
1103    }
1104
1105    /// Checked multiplication.
1106    ///
1107    /// Returns [None] if overflow occurred.
1108    pub fn checked_mul(self, rhs: u64) -> Option<Amount> {
1109        self.0.checked_mul(rhs).map(Amount)
1110    }
1111
1112    /// Checked integer division.
1113    ///
1114    /// Be aware that integer division loses the remainder if no exact division
1115    /// can be made.
1116    /// Returns [None] if overflow occurred.
1117    pub fn checked_div(self, rhs: u64) -> Option<Amount> {
1118        self.0.checked_div(rhs).map(Amount)
1119    }
1120
1121    /// Checked remainder.
1122    ///
1123    /// Returns [None] if overflow occurred.
1124    pub fn checked_rem(self, rhs: u64) -> Option<Amount> {
1125        self.0.checked_rem(rhs).map(Amount)
1126    }
1127
1128    /// Unchecked addition.
1129    ///
1130    /// Computes `self + rhs`.  Panics in debug mode, wraps in release mode.
1131    pub fn unchecked_add(self, rhs: Amount) -> Amount {
1132        Self(self.0 + rhs.0)
1133    }
1134
1135    /// Unchecked subtraction.
1136    ///
1137    /// Computes `self - rhs`.  Panics in debug mode, wraps in release mode.
1138    pub fn unchecked_sub(self, rhs: Amount) -> Amount {
1139        Self(self.0 - rhs.0)
1140    }
1141
1142    /// Convert to a signed amount.
1143    pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
1144        if self.to_sat() > SignedAmount::MAX.to_sat() as u64 {
1145            Err(OutOfRangeError::too_big(true))
1146        } else {
1147            Ok(SignedAmount::from_sat(self.to_sat() as i64))
1148        }
1149    }
1150}
1151
1152impl default::Default for Amount {
1153    fn default() -> Self {
1154        Amount::ZERO
1155    }
1156}
1157
1158impl fmt::Debug for Amount {
1159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1160        write!(f, "{} SAT", self.to_sat())
1161    }
1162}
1163
1164// No one should depend on a binding contract for Display for this type.
1165// Just using Bitcoin denominated string.
1166impl fmt::Display for Amount {
1167    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1168        let satoshis = self.to_sat();
1169        let denomination = Denomination::Bitcoin;
1170        let mut format_options = FormatOptions::from_formatter(f);
1171
1172        if f.precision().is_none() && satoshis.rem_euclid(Amount::ONE_BTC.to_sat()) != 0 {
1173            format_options.precision = Some(8);
1174        }
1175
1176        fmt_satoshi_in(satoshis, false, f, denomination, true, format_options)
1177    }
1178}
1179
1180impl ops::Add for Amount {
1181    type Output = Amount;
1182
1183    fn add(self, rhs: Amount) -> Self::Output {
1184        self.checked_add(rhs).expect("Amount addition error")
1185    }
1186}
1187
1188impl ops::AddAssign for Amount {
1189    fn add_assign(&mut self, other: Amount) {
1190        *self = *self + other
1191    }
1192}
1193
1194impl ops::Sub for Amount {
1195    type Output = Amount;
1196
1197    fn sub(self, rhs: Amount) -> Self::Output {
1198        self.checked_sub(rhs).expect("Amount subtraction error")
1199    }
1200}
1201
1202impl ops::SubAssign for Amount {
1203    fn sub_assign(&mut self, other: Amount) {
1204        *self = *self - other
1205    }
1206}
1207
1208impl ops::Rem<u64> for Amount {
1209    type Output = Amount;
1210
1211    fn rem(self, modulus: u64) -> Self {
1212        self.checked_rem(modulus).expect("Amount remainder error")
1213    }
1214}
1215
1216impl ops::RemAssign<u64> for Amount {
1217    fn rem_assign(&mut self, modulus: u64) {
1218        *self = *self % modulus
1219    }
1220}
1221
1222impl ops::Mul<u64> for Amount {
1223    type Output = Amount;
1224
1225    fn mul(self, rhs: u64) -> Self::Output {
1226        self.checked_mul(rhs).expect("Amount multiplication error")
1227    }
1228}
1229
1230impl ops::MulAssign<u64> for Amount {
1231    fn mul_assign(&mut self, rhs: u64) {
1232        *self = *self * rhs
1233    }
1234}
1235
1236impl ops::Div<u64> for Amount {
1237    type Output = Amount;
1238
1239    fn div(self, rhs: u64) -> Self::Output {
1240        self.checked_div(rhs).expect("Amount division error")
1241    }
1242}
1243
1244impl ops::DivAssign<u64> for Amount {
1245    fn div_assign(&mut self, rhs: u64) {
1246        *self = *self / rhs
1247    }
1248}
1249
1250impl FromStr for Amount {
1251    type Err = ParseError;
1252
1253    fn from_str(s: &str) -> Result<Self, Self::Err> {
1254        Amount::from_str_with_denomination(s)
1255    }
1256}
1257
1258impl TryFrom<SignedAmount> for Amount {
1259    type Error = OutOfRangeError;
1260
1261    fn try_from(value: SignedAmount) -> Result<Self, Self::Error> {
1262        value.to_unsigned()
1263    }
1264}
1265
1266impl core::iter::Sum for Amount {
1267    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1268        let sats: u64 = iter.map(|amt| amt.0).sum();
1269        Amount::from_sat(sats)
1270    }
1271}
1272
1273/// A helper/builder that displays amount with specified settings.
1274///
1275/// This provides richer interface than `fmt::Formatter`:
1276///
1277/// * Ability to select denomination
1278/// * Show or hide denomination
1279/// * Dynamically-selected denomination - show in sats if less than 1 BTC.
1280///
1281/// However this can still be combined with `fmt::Formatter` options to precisely control zeros,
1282/// padding, alignment... The formatting works like floats from `core` but note that precision will
1283/// **never** be lossy - that means no rounding.
1284///
1285/// See [`Amount::display_in`] and [`Amount::display_dynamic`] on how to construct this.
1286#[derive(Debug, Clone)]
1287pub struct Display {
1288    /// Absolute value of satoshis to display (sign is below)
1289    sats_abs: u64,
1290    /// The sign
1291    is_negative: bool,
1292    /// How to display the value
1293    style: DisplayStyle,
1294}
1295
1296impl Display {
1297    /// Makes subsequent calls to `Display::fmt` display denomination.
1298    pub fn show_denomination(mut self) -> Self {
1299        match &mut self.style {
1300            DisplayStyle::FixedDenomination {
1301                show_denomination, ..
1302            } => *show_denomination = true,
1303            // No-op because dynamic denomination is always shown
1304            DisplayStyle::DynamicDenomination => (),
1305        }
1306        self
1307    }
1308}
1309
1310impl fmt::Display for Display {
1311    #[rustfmt::skip]
1312    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1313        let format_options = FormatOptions::from_formatter(f);
1314        match &self.style {
1315            DisplayStyle::FixedDenomination { show_denomination, denomination } => {
1316                fmt_satoshi_in(self.sats_abs, self.is_negative, f, *denomination, *show_denomination, format_options)
1317            },
1318            DisplayStyle::DynamicDenomination if self.sats_abs >= Amount::ONE_BTC.to_sat() => {
1319                fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Bitcoin, true, format_options)
1320            },
1321            DisplayStyle::DynamicDenomination => {
1322                fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Satoshi, true, format_options)
1323            },
1324        }
1325    }
1326}
1327
1328#[derive(Clone, Debug)]
1329enum DisplayStyle {
1330    FixedDenomination {
1331        denomination: Denomination,
1332        show_denomination: bool,
1333    },
1334    DynamicDenomination,
1335}
1336
1337/// SignedAmount
1338///
1339/// The [SignedAmount] type can be used to express Bitcoin amounts that support
1340/// arithmetic and conversion to various denominations.
1341///
1342///
1343/// Warning!
1344///
1345/// This type implements several arithmetic operations from [core::ops].
1346/// To prevent errors due to overflow or underflow when using these operations,
1347/// it is advised to instead use the checked arithmetic methods whose names
1348/// start with `checked_`.  The operations from [core::ops] that [Amount]
1349/// implements will panic when overflow or underflow occurs.
1350///
1351#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1352pub struct SignedAmount(i64);
1353
1354impl SignedAmount {
1355    /// The zero amount.
1356    pub const ZERO: SignedAmount = SignedAmount(0);
1357    /// Exactly one satoshi.
1358    pub const ONE_SAT: SignedAmount = SignedAmount(1);
1359    /// Exactly one bitcoin.
1360    pub const ONE_BTC: SignedAmount = SignedAmount(100_000_000);
1361    /// The maximum value allowed as an amount. Useful for sanity checking.
1362    pub const MAX_MONEY: SignedAmount = SignedAmount(21_000_000 * 100_000_000);
1363    /// The minimum value of an amount.
1364    pub const MIN: SignedAmount = SignedAmount(i64::MIN);
1365    /// The maximum value of an amount.
1366    pub const MAX: SignedAmount = SignedAmount(i64::MAX);
1367
1368    /// Create an [SignedAmount] with satoshi precision and the given number of satoshis.
1369    pub const fn from_sat(satoshi: i64) -> SignedAmount {
1370        SignedAmount(satoshi)
1371    }
1372
1373    /// Gets the number of satoshis in this [`SignedAmount`].
1374    pub fn to_sat(self) -> i64 {
1375        self.0
1376    }
1377
1378    /// Convert from a value expressing bitcoins to an [SignedAmount].
1379    #[cfg(feature = "alloc")]
1380    pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
1381        SignedAmount::from_float_in(btc, Denomination::Bitcoin)
1382    }
1383
1384    /// Parse a decimal string as a value in the given denomination.
1385    ///
1386    /// Note: This only parses the value string.  If you want to parse a value
1387    /// with denomination, use [FromStr].
1388    pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
1389        match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
1390            // (negative, amount)
1391            (false, sat) if sat > i64::MAX as u64 => {
1392                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true)))
1393            }
1394            (false, sat) => Ok(SignedAmount(sat as i64)),
1395            (true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
1396            (true, sat) if sat > i64::MIN.unsigned_abs() => {
1397                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small()))
1398            }
1399            (true, sat) => Ok(SignedAmount(-(sat as i64))),
1400        }
1401    }
1402
1403    /// Parses amounts with denomination suffix like they are produced with
1404    /// [Self::to_string_with_denomination] or with [fmt::Display].
1405    /// If you want to parse only the amount without the denomination,
1406    /// use [Self::from_str_in].
1407    pub fn from_str_with_denomination(s: &str) -> Result<SignedAmount, ParseError> {
1408        let (amt, denom) = split_amount_and_denomination(s)?;
1409        SignedAmount::from_str_in(amt, denom).map_err(Into::into)
1410    }
1411
1412    /// Express this [SignedAmount] as a floating-point value in the given denomination.
1413    ///
1414    /// Please be aware of the risk of using floating-point numbers.
1415    #[cfg(feature = "alloc")]
1416    pub fn to_float_in(self, denom: Denomination) -> f64 {
1417        f64::from_str(&self.to_string_in(denom)).unwrap()
1418    }
1419
1420    /// Express this [`SignedAmount`] as a floating-point value in Bitcoin.
1421    ///
1422    /// Equivalent to `to_float_in(Denomination::Bitcoin)`.
1423    ///
1424    /// Please be aware of the risk of using floating-point numbers.
1425    #[cfg(feature = "alloc")]
1426    pub fn to_btc(self) -> f64 {
1427        self.to_float_in(Denomination::Bitcoin)
1428    }
1429
1430    /// Convert this [SignedAmount] in floating-point notation with a given
1431    /// denomination.
1432    /// Can return error if the amount is too big, too precise or negative.
1433    ///
1434    /// Please be aware of the risk of using floating-point numbers.
1435    #[cfg(feature = "alloc")]
1436    pub fn from_float_in(
1437        value: f64,
1438        denom: Denomination,
1439    ) -> Result<SignedAmount, ParseAmountError> {
1440        // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
1441        // Any performance-critical application should not be dealing with floats.
1442        SignedAmount::from_str_in(&value.to_string(), denom)
1443    }
1444
1445    /// Create an object that implements [`fmt::Display`] using specified denomination.
1446    pub fn display_in(self, denomination: Denomination) -> Display {
1447        Display {
1448            sats_abs: self.unsigned_abs().to_sat(),
1449            is_negative: self.is_negative(),
1450            style: DisplayStyle::FixedDenomination {
1451                denomination,
1452                show_denomination: false,
1453            },
1454        }
1455    }
1456
1457    /// Create an object that implements [`fmt::Display`] dynamically selecting denomination.
1458    ///
1459    /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
1460    /// avoid confusion the denomination is always shown.
1461    pub fn display_dynamic(self) -> Display {
1462        Display {
1463            sats_abs: self.unsigned_abs().to_sat(),
1464            is_negative: self.is_negative(),
1465            style: DisplayStyle::DynamicDenomination,
1466        }
1467    }
1468
1469    /// Format the value of this [SignedAmount] in the given denomination.
1470    ///
1471    /// Does not include the denomination.
1472    #[rustfmt::skip]
1473    pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
1474        fmt_satoshi_in(self.unsigned_abs().to_sat(), self.is_negative(), f, denom, false, FormatOptions::default())
1475    }
1476
1477    /// Get a string number of this [SignedAmount] in the given denomination.
1478    ///
1479    /// Does not include the denomination.
1480    #[cfg(feature = "alloc")]
1481    pub fn to_string_in(self, denom: Denomination) -> String {
1482        let mut buf = String::new();
1483        self.fmt_value_in(&mut buf, denom).unwrap();
1484        buf
1485    }
1486
1487    /// Get a formatted string of this [SignedAmount] in the given denomination,
1488    /// suffixed with the abbreviation for the denomination.
1489    #[cfg(feature = "alloc")]
1490    pub fn to_string_with_denomination(self, denom: Denomination) -> String {
1491        let mut buf = String::new();
1492        self.fmt_value_in(&mut buf, denom).unwrap();
1493        write!(buf, " {}", denom).unwrap();
1494        buf
1495    }
1496
1497    // Some arithmetic that doesn't fit in `core::ops` traits.
1498
1499    /// Get the absolute value of this [SignedAmount].
1500    pub fn abs(self) -> SignedAmount {
1501        SignedAmount(self.0.abs())
1502    }
1503
1504    /// Get the absolute value of this [SignedAmount] returning `Amount`.
1505    pub fn unsigned_abs(self) -> Amount {
1506        Amount(self.0.unsigned_abs())
1507    }
1508
1509    /// Returns a number representing sign of this [SignedAmount].
1510    ///
1511    /// - `0` if the amount is zero
1512    /// - `1` if the amount is positive
1513    /// - `-1` if the amount is negative
1514    pub fn signum(self) -> i64 {
1515        self.0.signum()
1516    }
1517
1518    /// Returns `true` if this [SignedAmount] is positive and `false` if
1519    /// this [SignedAmount] is zero or negative.
1520    pub fn is_positive(self) -> bool {
1521        self.0.is_positive()
1522    }
1523
1524    /// Returns `true` if this [SignedAmount] is negative and `false` if
1525    /// this [SignedAmount] is zero or positive.
1526    pub fn is_negative(self) -> bool {
1527        self.0.is_negative()
1528    }
1529
1530    /// Get the absolute value of this [SignedAmount].
1531    /// Returns [None] if overflow occurred. (`self == MIN`)
1532    pub fn checked_abs(self) -> Option<SignedAmount> {
1533        self.0.checked_abs().map(SignedAmount)
1534    }
1535
1536    /// Checked addition.
1537    /// Returns [None] if overflow occurred.
1538    pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
1539        self.0.checked_add(rhs.0).map(SignedAmount)
1540    }
1541
1542    /// Checked subtraction.
1543    /// Returns [None] if overflow occurred.
1544    pub fn checked_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
1545        self.0.checked_sub(rhs.0).map(SignedAmount)
1546    }
1547
1548    /// Checked multiplication.
1549    /// Returns [None] if overflow occurred.
1550    pub fn checked_mul(self, rhs: i64) -> Option<SignedAmount> {
1551        self.0.checked_mul(rhs).map(SignedAmount)
1552    }
1553
1554    /// Checked integer division.
1555    /// Be aware that integer division loses the remainder if no exact division
1556    /// can be made.
1557    /// Returns [None] if overflow occurred.
1558    pub fn checked_div(self, rhs: i64) -> Option<SignedAmount> {
1559        self.0.checked_div(rhs).map(SignedAmount)
1560    }
1561
1562    /// Checked remainder.
1563    /// Returns [None] if overflow occurred.
1564    pub fn checked_rem(self, rhs: i64) -> Option<SignedAmount> {
1565        self.0.checked_rem(rhs).map(SignedAmount)
1566    }
1567
1568    /// Unchecked addition.
1569    ///
1570    /// Computes `self + rhs`.  Panics in debug mode, wraps in release mode.
1571    pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount {
1572        Self(self.0 + rhs.0)
1573    }
1574
1575    /// Unchecked subtraction.
1576    ///
1577    /// Computes `self - rhs`.  Panics in debug mode, wraps in release mode.
1578    pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount {
1579        Self(self.0 - rhs.0)
1580    }
1581
1582    /// Subtraction that doesn't allow negative [SignedAmount]s.
1583    /// Returns [None] if either [self], `rhs` or the result is strictly negative.
1584    pub fn positive_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
1585        if self.is_negative() || rhs.is_negative() || rhs > self {
1586            None
1587        } else {
1588            self.checked_sub(rhs)
1589        }
1590    }
1591
1592    /// Convert to an unsigned amount.
1593    pub fn to_unsigned(self) -> Result<Amount, OutOfRangeError> {
1594        if self.is_negative() {
1595            Err(OutOfRangeError::negative())
1596        } else {
1597            Ok(Amount::from_sat(self.to_sat() as u64))
1598        }
1599    }
1600}
1601
1602impl default::Default for SignedAmount {
1603    fn default() -> Self {
1604        SignedAmount::ZERO
1605    }
1606}
1607
1608impl fmt::Debug for SignedAmount {
1609    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1610        write!(f, "SignedAmount({} SAT)", self.to_sat())
1611    }
1612}
1613
1614// No one should depend on a binding contract for Display for this type.
1615// Just using Bitcoin denominated string.
1616impl fmt::Display for SignedAmount {
1617    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1618        self.fmt_value_in(f, Denomination::Bitcoin)?;
1619        write!(f, " {}", Denomination::Bitcoin)
1620    }
1621}
1622
1623impl ops::Add for SignedAmount {
1624    type Output = SignedAmount;
1625
1626    fn add(self, rhs: SignedAmount) -> Self::Output {
1627        self.checked_add(rhs).expect("SignedAmount addition error")
1628    }
1629}
1630
1631impl ops::AddAssign for SignedAmount {
1632    fn add_assign(&mut self, other: SignedAmount) {
1633        *self = *self + other
1634    }
1635}
1636
1637impl ops::Sub for SignedAmount {
1638    type Output = SignedAmount;
1639
1640    fn sub(self, rhs: SignedAmount) -> Self::Output {
1641        self.checked_sub(rhs)
1642            .expect("SignedAmount subtraction error")
1643    }
1644}
1645
1646impl ops::SubAssign for SignedAmount {
1647    fn sub_assign(&mut self, other: SignedAmount) {
1648        *self = *self - other
1649    }
1650}
1651
1652impl ops::Rem<i64> for SignedAmount {
1653    type Output = SignedAmount;
1654
1655    fn rem(self, modulus: i64) -> Self {
1656        self.checked_rem(modulus)
1657            .expect("SignedAmount remainder error")
1658    }
1659}
1660
1661impl ops::RemAssign<i64> for SignedAmount {
1662    fn rem_assign(&mut self, modulus: i64) {
1663        *self = *self % modulus
1664    }
1665}
1666
1667impl ops::Mul<i64> for SignedAmount {
1668    type Output = SignedAmount;
1669
1670    fn mul(self, rhs: i64) -> Self::Output {
1671        self.checked_mul(rhs)
1672            .expect("SignedAmount multiplication error")
1673    }
1674}
1675
1676impl ops::MulAssign<i64> for SignedAmount {
1677    fn mul_assign(&mut self, rhs: i64) {
1678        *self = *self * rhs
1679    }
1680}
1681
1682impl ops::Div<i64> for SignedAmount {
1683    type Output = SignedAmount;
1684
1685    fn div(self, rhs: i64) -> Self::Output {
1686        self.checked_div(rhs).expect("SignedAmount division error")
1687    }
1688}
1689
1690impl ops::DivAssign<i64> for SignedAmount {
1691    fn div_assign(&mut self, rhs: i64) {
1692        *self = *self / rhs
1693    }
1694}
1695
1696impl ops::Neg for SignedAmount {
1697    type Output = Self;
1698
1699    fn neg(self) -> Self::Output {
1700        Self(self.0.neg())
1701    }
1702}
1703
1704impl FromStr for SignedAmount {
1705    type Err = ParseError;
1706
1707    fn from_str(s: &str) -> Result<Self, Self::Err> {
1708        SignedAmount::from_str_with_denomination(s)
1709    }
1710}
1711
1712impl TryFrom<Amount> for SignedAmount {
1713    type Error = OutOfRangeError;
1714
1715    fn try_from(value: Amount) -> Result<Self, Self::Error> {
1716        value.to_signed()
1717    }
1718}
1719
1720impl core::iter::Sum for SignedAmount {
1721    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1722        let sats: i64 = iter.map(|amt| amt.0).sum();
1723        SignedAmount::from_sat(sats)
1724    }
1725}
1726
1727/// Calculate the sum over the iterator using checked arithmetic.
1728pub trait CheckedSum<R>: private::SumSeal<R> {
1729    /// Calculate the sum over the iterator using checked arithmetic. If an over or underflow would
1730    /// happen it returns `None`.
1731    fn checked_sum(self) -> Option<R>;
1732}
1733
1734impl<T> CheckedSum<Amount> for T
1735where
1736    T: Iterator<Item = Amount>,
1737{
1738    fn checked_sum(mut self) -> Option<Amount> {
1739        let first = Some(self.next().unwrap_or_default());
1740
1741        self.fold(first, |acc, item| acc.and_then(|acc| acc.checked_add(item)))
1742    }
1743}
1744
1745impl<T> CheckedSum<SignedAmount> for T
1746where
1747    T: Iterator<Item = SignedAmount>,
1748{
1749    fn checked_sum(mut self) -> Option<SignedAmount> {
1750        let first = Some(self.next().unwrap_or_default());
1751
1752        self.fold(first, |acc, item| acc.and_then(|acc| acc.checked_add(item)))
1753    }
1754}
1755
1756mod private {
1757    use super::{Amount, SignedAmount};
1758
1759    /// Used to seal the `CheckedSum` trait
1760    pub trait SumSeal<A> {}
1761
1762    impl<T> SumSeal<Amount> for T where T: Iterator<Item = Amount> {}
1763    impl<T> SumSeal<SignedAmount> for T where T: Iterator<Item = SignedAmount> {}
1764}
1765
1766#[cfg(feature = "serde")]
1767pub mod serde {
1768    // methods are implementation of a standardized serde-specific signature
1769    #![allow(missing_docs)]
1770
1771    //! This module adds serde serialization and deserialization support for Amounts.
1772    //! Since there is not a default way to serialize and deserialize Amounts, multiple
1773    //! ways are supported and it's up to the user to decide which serialiation to use.
1774    //! The provided modules can be used as follows:
1775    //!
1776    //! ```rust,ignore
1777    //! use serde::{Serialize, Deserialize};
1778    //! use satsnet_units::Amount;
1779    //!
1780    //! #[derive(Serialize, Deserialize)]
1781    //! pub struct HasAmount {
1782    //!     #[serde(with = "satsnet_units::amount::serde::as_btc")]
1783    //!     pub amount: Amount,
1784    //! }
1785    //! ```
1786
1787    use core::fmt;
1788
1789    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1790
1791    #[cfg(feature = "alloc")]
1792    use super::Denomination;
1793    use super::{Amount, ParseAmountError, SignedAmount};
1794
1795    /// This trait is used only to avoid code duplication and naming collisions
1796    /// of the different serde serialization crates.
1797    pub trait SerdeAmount: Copy + Sized {
1798        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1799        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error>;
1800        #[cfg(feature = "alloc")]
1801        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1802        #[cfg(feature = "alloc")]
1803        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error>;
1804    }
1805
1806    mod private {
1807        /// Controls access to the trait methods.
1808        pub struct Token;
1809    }
1810
1811    /// This trait is only for internal Amount type serialization/deserialization
1812    pub trait SerdeAmountForOpt: Copy + Sized + SerdeAmount {
1813        fn type_prefix(_: private::Token) -> &'static str;
1814        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1815        #[cfg(feature = "alloc")]
1816        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error>;
1817    }
1818
1819    struct DisplayFullError(ParseAmountError);
1820
1821    #[cfg(feature = "std")]
1822    impl fmt::Display for DisplayFullError {
1823        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1824            use std::error::Error;
1825
1826            fmt::Display::fmt(&self.0, f)?;
1827            let mut source_opt = self.0.source();
1828            while let Some(source) = source_opt {
1829                write!(f, ": {}", source)?;
1830                source_opt = source.source();
1831            }
1832            Ok(())
1833        }
1834    }
1835
1836    #[cfg(not(feature = "std"))]
1837    impl fmt::Display for DisplayFullError {
1838        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1839            fmt::Display::fmt(&self.0, f)
1840        }
1841    }
1842
1843    impl SerdeAmount for Amount {
1844        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1845            u64::serialize(&self.to_sat(), s)
1846        }
1847        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1848            Ok(Amount::from_sat(u64::deserialize(d)?))
1849        }
1850        #[cfg(feature = "alloc")]
1851        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1852            f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
1853        }
1854        #[cfg(feature = "alloc")]
1855        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1856            use serde::de::Error;
1857            Amount::from_btc(f64::deserialize(d)?)
1858                .map_err(DisplayFullError)
1859                .map_err(D::Error::custom)
1860        }
1861    }
1862
1863    impl SerdeAmountForOpt for Amount {
1864        fn type_prefix(_: private::Token) -> &'static str {
1865            "u"
1866        }
1867        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1868            s.serialize_some(&self.to_sat())
1869        }
1870        #[cfg(feature = "alloc")]
1871        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1872            s.serialize_some(&self.to_btc())
1873        }
1874    }
1875
1876    impl SerdeAmount for SignedAmount {
1877        fn ser_sat<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1878            i64::serialize(&self.to_sat(), s)
1879        }
1880        fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1881            Ok(SignedAmount::from_sat(i64::deserialize(d)?))
1882        }
1883        #[cfg(feature = "alloc")]
1884        fn ser_btc<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1885            f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
1886        }
1887        #[cfg(feature = "alloc")]
1888        fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result<Self, D::Error> {
1889            use serde::de::Error;
1890            SignedAmount::from_btc(f64::deserialize(d)?)
1891                .map_err(DisplayFullError)
1892                .map_err(D::Error::custom)
1893        }
1894    }
1895
1896    impl SerdeAmountForOpt for SignedAmount {
1897        fn type_prefix(_: private::Token) -> &'static str {
1898            "i"
1899        }
1900        fn ser_sat_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1901            s.serialize_some(&self.to_sat())
1902        }
1903        #[cfg(feature = "alloc")]
1904        fn ser_btc_opt<S: Serializer>(self, s: S, _: private::Token) -> Result<S::Ok, S::Error> {
1905            s.serialize_some(&self.to_btc())
1906        }
1907    }
1908
1909    pub mod as_sat {
1910        //! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi.
1911        //! Use with `#[serde(with = "amount::serde::as_sat")]`.
1912        //!
1913        use serde::{Deserializer, Serializer};
1914
1915        use super::private;
1916        use crate::amount::serde::SerdeAmount;
1917
1918        pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
1919            a.ser_sat(s, private::Token)
1920        }
1921
1922        pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
1923            A::des_sat(d, private::Token)
1924        }
1925
1926        pub mod opt {
1927            //! Serialize and deserialize [`Option<Amount>`](crate::Amount) as real numbers denominated in satoshi.
1928            //! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`.
1929
1930            use core::fmt;
1931            use core::marker::PhantomData;
1932
1933            use serde::{de, Deserializer, Serializer};
1934
1935            use super::private;
1936            use crate::amount::serde::SerdeAmountForOpt;
1937
1938            pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
1939                a: &Option<A>,
1940                s: S,
1941            ) -> Result<S::Ok, S::Error> {
1942                match *a {
1943                    Some(a) => a.ser_sat_opt(s, private::Token),
1944                    None => s.serialize_none(),
1945                }
1946            }
1947
1948            pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>(
1949                d: D,
1950            ) -> Result<Option<A>, D::Error> {
1951                struct VisitOptAmt<X>(PhantomData<X>);
1952
1953                impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt<X> {
1954                    type Value = Option<X>;
1955
1956                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1957                        write!(formatter, "An Option<{}64>", X::type_prefix(private::Token))
1958                    }
1959
1960                    fn visit_none<E>(self) -> Result<Self::Value, E>
1961                    where
1962                        E: de::Error,
1963                    {
1964                        Ok(None)
1965                    }
1966                    fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
1967                    where
1968                        D: Deserializer<'de>,
1969                    {
1970                        Ok(Some(X::des_sat(d, private::Token)?))
1971                    }
1972                }
1973                d.deserialize_option(VisitOptAmt::<A>(PhantomData))
1974            }
1975        }
1976    }
1977
1978    #[cfg(feature = "alloc")]
1979    pub mod as_btc {
1980        //! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC.
1981        //! Use with `#[serde(with = "amount::serde::as_btc")]`.
1982
1983        use serde::{Deserializer, Serializer};
1984
1985        use super::private;
1986        use crate::amount::serde::SerdeAmount;
1987
1988        pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
1989            a.ser_btc(s, private::Token)
1990        }
1991
1992        pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
1993            A::des_btc(d, private::Token)
1994        }
1995
1996        pub mod opt {
1997            //! Serialize and deserialize `Option<Amount>` as JSON numbers denominated in BTC.
1998            //! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`.
1999
2000            use core::fmt;
2001            use core::marker::PhantomData;
2002
2003            use serde::{de, Deserializer, Serializer};
2004
2005            use super::private;
2006            use crate::amount::serde::SerdeAmountForOpt;
2007
2008            pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
2009                a: &Option<A>,
2010                s: S,
2011            ) -> Result<S::Ok, S::Error> {
2012                match *a {
2013                    Some(a) => a.ser_btc_opt(s, private::Token),
2014                    None => s.serialize_none(),
2015                }
2016            }
2017
2018            pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>(
2019                d: D,
2020            ) -> Result<Option<A>, D::Error> {
2021                struct VisitOptAmt<X>(PhantomData<X>);
2022
2023                impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt<X> {
2024                    type Value = Option<X>;
2025
2026                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2027                        write!(formatter, "An Option<f64>")
2028                    }
2029
2030                    fn visit_none<E>(self) -> Result<Self::Value, E>
2031                    where
2032                        E: de::Error,
2033                    {
2034                        Ok(None)
2035                    }
2036                    fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
2037                    where
2038                        D: Deserializer<'de>,
2039                    {
2040                        Ok(Some(X::des_btc(d, private::Token)?))
2041                    }
2042                }
2043                d.deserialize_option(VisitOptAmt::<A>(PhantomData))
2044            }
2045        }
2046    }
2047}
2048
2049#[cfg(kani)]
2050mod verification {
2051    use std::cmp;
2052    use std::convert::TryInto;
2053
2054    use super::*;
2055
2056    // Note regarding the `unwind` parameter: this defines how many iterations
2057    // of loops kani will unwind before handing off to the SMT solver. Basically
2058    // it should be set as low as possible such that Kani still succeeds (doesn't
2059    // return "undecidable").
2060    //
2061    // There is more info here: https://model-checking.github.io/kani/tutorial-loop-unwinding.html
2062    //
2063    // Unfortunately what it means to "loop" is pretty opaque ... in this case
2064    // there appear to be loops in memcmp, which I guess comes from assert_eq!,
2065    // though I didn't see any failures until I added the to_signed() test.
2066    // Further confusing the issue, a value of 2 works fine on my system, but on
2067    // CI it fails, so we need to set it higher.
2068    #[kani::unwind(4)]
2069    #[kani::proof]
2070    fn u_amount_add_homomorphic() {
2071        let n1 = kani::any::<u64>();
2072        let n2 = kani::any::<u64>();
2073        kani::assume(n1.checked_add(n2).is_some()); // assume we don't overflow in the actual test
2074        assert_eq!(
2075            Amount::from_sat(n1) + Amount::from_sat(n2),
2076            Amount::from_sat(n1 + n2)
2077        );
2078
2079        let mut amt = Amount::from_sat(n1);
2080        amt += Amount::from_sat(n2);
2081        assert_eq!(amt, Amount::from_sat(n1 + n2));
2082
2083        let max = cmp::max(n1, n2);
2084        let min = cmp::min(n1, n2);
2085        assert_eq!(
2086            Amount::from_sat(max) - Amount::from_sat(min),
2087            Amount::from_sat(max - min)
2088        );
2089
2090        let mut amt = Amount::from_sat(max);
2091        amt -= Amount::from_sat(min);
2092        assert_eq!(amt, Amount::from_sat(max - min));
2093
2094        assert_eq!(
2095            Amount::from_sat(n1).to_signed(),
2096            if n1 <= i64::MAX as u64 {
2097                Ok(SignedAmount::from_sat(n1.try_into().unwrap()))
2098            } else {
2099                Err(OutOfRangeError::too_big(true))
2100            },
2101        );
2102    }
2103
2104    #[kani::unwind(4)]
2105    #[kani::proof]
2106    fn u_amount_add_homomorphic_checked() {
2107        let n1 = kani::any::<u64>();
2108        let n2 = kani::any::<u64>();
2109        assert_eq!(
2110            Amount::from_sat(n1).checked_add(Amount::from_sat(n2)),
2111            n1.checked_add(n2).map(Amount::from_sat),
2112        );
2113        assert_eq!(
2114            Amount::from_sat(n1).checked_sub(Amount::from_sat(n2)),
2115            n1.checked_sub(n2).map(Amount::from_sat),
2116        );
2117    }
2118
2119    #[kani::unwind(4)]
2120    #[kani::proof]
2121    fn s_amount_add_homomorphic() {
2122        let n1 = kani::any::<i64>();
2123        let n2 = kani::any::<i64>();
2124        kani::assume(n1.checked_add(n2).is_some()); // assume we don't overflow in the actual test
2125        kani::assume(n1.checked_sub(n2).is_some()); // assume we don't overflow in the actual test
2126        assert_eq!(
2127            SignedAmount::from_sat(n1) + SignedAmount::from_sat(n2),
2128            SignedAmount::from_sat(n1 + n2)
2129        );
2130        assert_eq!(
2131            SignedAmount::from_sat(n1) - SignedAmount::from_sat(n2),
2132            SignedAmount::from_sat(n1 - n2)
2133        );
2134
2135        let mut amt = SignedAmount::from_sat(n1);
2136        amt += SignedAmount::from_sat(n2);
2137        assert_eq!(amt, SignedAmount::from_sat(n1 + n2));
2138        let mut amt = SignedAmount::from_sat(n1);
2139        amt -= SignedAmount::from_sat(n2);
2140        assert_eq!(amt, SignedAmount::from_sat(n1 - n2));
2141
2142        assert_eq!(
2143            SignedAmount::from_sat(n1).to_unsigned(),
2144            if n1 >= 0 {
2145                Ok(Amount::from_sat(n1.try_into().unwrap()))
2146            } else {
2147                Err(OutOfRangeError {
2148                    is_signed: true,
2149                    is_greater_than_max: false,
2150                })
2151            },
2152        );
2153    }
2154
2155    #[kani::unwind(4)]
2156    #[kani::proof]
2157    fn s_amount_add_homomorphic_checked() {
2158        let n1 = kani::any::<i64>();
2159        let n2 = kani::any::<i64>();
2160        assert_eq!(
2161            SignedAmount::from_sat(n1).checked_add(SignedAmount::from_sat(n2)),
2162            n1.checked_add(n2).map(SignedAmount::from_sat),
2163        );
2164        assert_eq!(
2165            SignedAmount::from_sat(n1).checked_sub(SignedAmount::from_sat(n2)),
2166            n1.checked_sub(n2).map(SignedAmount::from_sat),
2167        );
2168
2169        assert_eq!(
2170            SignedAmount::from_sat(n1).positive_sub(SignedAmount::from_sat(n2)),
2171            if n1 >= 0 && n2 >= 0 && n1 >= n2 {
2172                Some(SignedAmount::from_sat(n1 - n2))
2173            } else {
2174                None
2175            },
2176        );
2177    }
2178}