1#[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#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
39#[non_exhaustive]
40pub enum Denomination {
41 Bitcoin,
43 CentiBitcoin,
45 MilliBitcoin,
47 MicroBitcoin,
49 NanoBitcoin,
51 PicoBitcoin,
53 Bit,
55 Satoshi,
57 MilliSatoshi,
59}
60
61impl Denomination {
62 pub const BTC: Self = Denomination::Bitcoin;
64
65 pub const SAT: Self = Denomination::Satoshi;
67
68 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 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 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
117const 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 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#[derive(Debug, Clone, PartialEq, Eq)]
156#[non_exhaustive]
157pub enum ParseError {
158 Amount(ParseAmountError),
160
161 Denomination(ParseDenominationError),
163
164 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 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 ParseError::MissingDenomination(_) => None,
235 }
236 }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
241#[non_exhaustive]
242pub enum ParseAmountError {
243 OutOfRange(OutOfRangeError),
245 TooPrecise(TooPreciseError),
247 MissingDigits(MissingDigitsError),
249 InputTooLarge(InputTooLargeError),
251 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#[derive(Debug, Copy, Clone, Eq, PartialEq)]
312pub struct OutOfRangeError {
313 is_signed: bool,
314 is_greater_than_max: bool,
315}
316
317impl OutOfRangeError {
318 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 pub fn is_above_max(&self) -> bool {
330 self.is_greater_than_max
331 }
332
333 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 is_signed: true,
349 is_greater_than_max: false,
350 }
351 }
352
353 pub(crate) fn negative() -> Self {
354 Self {
355 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#[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#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
483#[non_exhaustive]
484pub enum ParseDenominationError {
485 Unknown(UnknownDenominationError),
487 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#[derive(Debug, Clone, PartialEq, Eq)]
517#[non_exhaustive]
518pub struct MissingDenominationError;
519
520#[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#[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
556fn 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
586fn 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 let precision_diff = -denom.precision();
615 if precision_diff <= 0 {
616 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; for (i, c) in s.char_indices() {
641 match c {
642 '0'..='9' => {
643 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 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 _ => {
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 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
732struct 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
787fn 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 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 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 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 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 let mut num_width = if total_decimals > 0 {
836 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 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 (true, true, _) | (true, false, fmt::Alignment::Right) => (width - num_width, 0),
857 (true, false, fmt::Alignment::Left) => (0, width - num_width),
858 (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#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
921#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
922pub struct Amount(u64);
923
924impl Amount {
925 pub const ZERO: Amount = Amount(0);
927 pub const ONE_SAT: Amount = Amount(1);
929 pub const ONE_BTC: Amount = Self::from_int_btc(1);
931 pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000);
933 pub const MIN: Amount = Amount::ZERO;
935 pub const MAX: Amount = Amount(u64::MAX);
937 pub const SIZE: usize = 8; pub const fn from_sat(satoshi: u64) -> Amount {
942 Amount(satoshi)
943 }
944
945 pub fn to_sat(self) -> u64 {
947 self.0
948 }
949
950 #[cfg(feature = "alloc")]
952 pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
953 Amount::from_float_in(btc, Denomination::Bitcoin)
954 }
955
956 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 #[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 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 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 #[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 #[cfg(feature = "alloc")]
1018 pub fn to_btc(self) -> f64 {
1019 self.to_float_in(Denomination::Bitcoin)
1020 }
1021
1022 #[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 Amount::from_str_in(&value.to_string(), denom)
1035 }
1036
1037 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 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 #[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 #[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 #[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 pub fn checked_add(self, rhs: Amount) -> Option<Amount> {
1095 self.0.checked_add(rhs.0).map(Amount)
1096 }
1097
1098 pub fn checked_sub(self, rhs: Amount) -> Option<Amount> {
1102 self.0.checked_sub(rhs.0).map(Amount)
1103 }
1104
1105 pub fn checked_mul(self, rhs: u64) -> Option<Amount> {
1109 self.0.checked_mul(rhs).map(Amount)
1110 }
1111
1112 pub fn checked_div(self, rhs: u64) -> Option<Amount> {
1118 self.0.checked_div(rhs).map(Amount)
1119 }
1120
1121 pub fn checked_rem(self, rhs: u64) -> Option<Amount> {
1125 self.0.checked_rem(rhs).map(Amount)
1126 }
1127
1128 pub fn unchecked_add(self, rhs: Amount) -> Amount {
1132 Self(self.0 + rhs.0)
1133 }
1134
1135 pub fn unchecked_sub(self, rhs: Amount) -> Amount {
1139 Self(self.0 - rhs.0)
1140 }
1141
1142 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
1164impl 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#[derive(Debug, Clone)]
1287pub struct Display {
1288 sats_abs: u64,
1290 is_negative: bool,
1292 style: DisplayStyle,
1294}
1295
1296impl Display {
1297 pub fn show_denomination(mut self) -> Self {
1299 match &mut self.style {
1300 DisplayStyle::FixedDenomination {
1301 show_denomination, ..
1302 } => *show_denomination = true,
1303 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#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1352pub struct SignedAmount(i64);
1353
1354impl SignedAmount {
1355 pub const ZERO: SignedAmount = SignedAmount(0);
1357 pub const ONE_SAT: SignedAmount = SignedAmount(1);
1359 pub const ONE_BTC: SignedAmount = SignedAmount(100_000_000);
1361 pub const MAX_MONEY: SignedAmount = SignedAmount(21_000_000 * 100_000_000);
1363 pub const MIN: SignedAmount = SignedAmount(i64::MIN);
1365 pub const MAX: SignedAmount = SignedAmount(i64::MAX);
1367
1368 pub const fn from_sat(satoshi: i64) -> SignedAmount {
1370 SignedAmount(satoshi)
1371 }
1372
1373 pub fn to_sat(self) -> i64 {
1375 self.0
1376 }
1377
1378 #[cfg(feature = "alloc")]
1380 pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
1381 SignedAmount::from_float_in(btc, Denomination::Bitcoin)
1382 }
1383
1384 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 (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 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 #[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 #[cfg(feature = "alloc")]
1426 pub fn to_btc(self) -> f64 {
1427 self.to_float_in(Denomination::Bitcoin)
1428 }
1429
1430 #[cfg(feature = "alloc")]
1436 pub fn from_float_in(
1437 value: f64,
1438 denom: Denomination,
1439 ) -> Result<SignedAmount, ParseAmountError> {
1440 SignedAmount::from_str_in(&value.to_string(), denom)
1443 }
1444
1445 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 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 #[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 #[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 #[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 pub fn abs(self) -> SignedAmount {
1501 SignedAmount(self.0.abs())
1502 }
1503
1504 pub fn unsigned_abs(self) -> Amount {
1506 Amount(self.0.unsigned_abs())
1507 }
1508
1509 pub fn signum(self) -> i64 {
1515 self.0.signum()
1516 }
1517
1518 pub fn is_positive(self) -> bool {
1521 self.0.is_positive()
1522 }
1523
1524 pub fn is_negative(self) -> bool {
1527 self.0.is_negative()
1528 }
1529
1530 pub fn checked_abs(self) -> Option<SignedAmount> {
1533 self.0.checked_abs().map(SignedAmount)
1534 }
1535
1536 pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
1539 self.0.checked_add(rhs.0).map(SignedAmount)
1540 }
1541
1542 pub fn checked_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
1545 self.0.checked_sub(rhs.0).map(SignedAmount)
1546 }
1547
1548 pub fn checked_mul(self, rhs: i64) -> Option<SignedAmount> {
1551 self.0.checked_mul(rhs).map(SignedAmount)
1552 }
1553
1554 pub fn checked_div(self, rhs: i64) -> Option<SignedAmount> {
1559 self.0.checked_div(rhs).map(SignedAmount)
1560 }
1561
1562 pub fn checked_rem(self, rhs: i64) -> Option<SignedAmount> {
1565 self.0.checked_rem(rhs).map(SignedAmount)
1566 }
1567
1568 pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount {
1572 Self(self.0 + rhs.0)
1573 }
1574
1575 pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount {
1579 Self(self.0 - rhs.0)
1580 }
1581
1582 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 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
1614impl 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
1727pub trait CheckedSum<R>: private::SumSeal<R> {
1729 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 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 #![allow(missing_docs)]
1770
1771 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 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 pub struct Token;
1809 }
1810
1811 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 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 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 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 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 #[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()); 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()); kani::assume(n1.checked_sub(n2).is_some()); 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}