1use std::cmp::Ordering;
4use std::fmt::Display;
5use std::str::FromStr;
6
7use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
8use data_encoding::BASE32HEX_NOPAD;
9use ethabi::ethereum_types::U256;
10use ibc::apps::transfer::types::Amount as IbcAmount;
11use namada_macros::BorshDeserializer;
12#[cfg(feature = "migrations")]
13use namada_migrations::*;
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16
17use crate::arith::{self, CheckedAdd, CheckedSub, One, Zero, checked};
18use crate::dec::{Dec, POS_DECIMAL_PRECISION};
19use crate::storage;
20use crate::storage::{DbKeySeg, KeySeg};
21use crate::uint::{self, I256, Uint};
22
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[derive(
27 Clone,
28 Copy,
29 Default,
30 BorshSerialize,
31 BorshDeserialize,
32 BorshDeserializer,
33 BorshSchema,
34 PartialEq,
35 Eq,
36 PartialOrd,
37 Ord,
38 Debug,
39 Hash,
40)]
41pub struct Amount {
42 raw: Uint,
43}
44
45pub const NATIVE_MAX_DECIMAL_PLACES: u8 = 6;
49
50pub const NATIVE_SCALE: u64 = 1_000_000;
54
55pub type Change = I256;
57
58impl Amount {
59 pub fn iter_words(self) -> impl Iterator<Item = u64> {
61 self.raw.0.into_iter()
62 }
63
64 pub const fn from_u64(x: u64) -> Self {
66 Self {
67 raw: Uint::from_u64(x),
68 }
69 }
70
71 pub const fn from_u128(value: u128) -> Self {
73 let mut ret = [0; 4];
74 #[allow(clippy::cast_possible_truncation)]
75 {
76 ret[0] = value as u64;
77 }
78 ret[1] = (value >> 64) as u64;
79 Self { raw: Uint(ret) }
80 }
81
82 pub fn change(&self) -> Change {
84 self.raw.try_into().unwrap()
85 }
86
87 pub fn spend(&mut self, amount: &Amount) -> Result<(), AmountError> {
89 self.raw = self
90 .raw
91 .checked_sub(amount.raw)
92 .ok_or(AmountError::Insufficient)?;
93 Ok(())
94 }
95
96 pub fn can_spend(&self, amount: &Amount) -> bool {
98 self.raw >= amount.raw
99 }
100
101 pub fn receive(&mut self, amount: &Amount) -> Result<(), AmountError> {
103 self.raw = self
104 .raw
105 .checked_add(amount.raw)
106 .ok_or(AmountError::Overflow)?;
107 Ok(())
108 }
109
110 pub fn native_whole(amount: u64) -> Self {
112 let raw = Uint::from(amount)
113 .checked_mul(Uint::from(NATIVE_SCALE))
114 .expect("u64 cannot overflow token amount");
115 Self { raw }
116 }
117
118 pub fn raw_amount(&self) -> Uint {
120 self.raw
121 }
122
123 pub fn max() -> Self {
125 Self {
126 raw: uint::MAX_VALUE,
127 }
128 }
129
130 pub fn max_signed() -> Self {
132 Self {
133 raw: uint::MAX_SIGNED_VALUE,
134 }
135 }
136
137 pub fn zero() -> Self {
139 Self::default()
140 }
141
142 pub fn is_zero(&self) -> bool {
144 self.raw == Uint::from(0)
145 }
146
147 pub fn is_positive(&self) -> bool {
149 !self.is_zero()
150 }
151
152 #[must_use]
155 pub fn checked_add(&self, amount: Amount) -> Option<Self> {
156 self.raw.checked_add(amount.raw).and_then(|result| {
157 if result <= uint::MAX_VALUE {
158 Some(Self { raw: result })
159 } else {
160 None
161 }
162 })
163 }
164
165 #[must_use]
168 pub fn checked_signed_add(&self, amount: Amount) -> Option<Self> {
169 self.raw.checked_add(amount.raw).and_then(|result| {
170 if result <= uint::MAX_SIGNED_VALUE {
171 Some(Self { raw: result })
172 } else {
173 None
174 }
175 })
176 }
177
178 #[must_use]
180 pub fn checked_sub(&self, amount: Amount) -> Option<Self> {
181 self.raw
182 .checked_sub(amount.raw)
183 .map(|result| Self { raw: result })
184 }
185
186 pub fn from_change(change: Change) -> Self {
188 Self { raw: change.abs() }
189 }
190
191 #[must_use]
193 pub fn checked_div(&self, amount: Amount) -> Option<Self> {
194 self.raw
195 .checked_div(amount.raw)
196 .map(|result| Self { raw: result })
197 }
198
199 #[must_use]
201 pub fn checked_mul<T>(&self, amount: T) -> Option<Self>
202 where
203 T: Into<Self>,
204 {
205 self.raw
206 .checked_mul(amount.into().raw)
207 .map(|result| Self { raw: result })
208 }
209
210 pub fn from_str(
212 string: impl AsRef<str>,
213 denom: impl Into<u8>,
214 ) -> Result<Amount, AmountParseError> {
215 DenominatedAmount::from_str(string.as_ref())?.scale(denom)
216 }
217
218 pub fn from_uint(
221 uint: impl Into<Uint>,
222 denom: impl Into<u8>,
223 ) -> Result<Self, AmountParseError> {
224 let denom = denom.into();
225 let uint = uint.into();
226 if denom == 0 {
227 return Ok(uint.into());
228 }
229 match Uint::from(10)
230 .checked_pow(Uint::from(denom))
231 .and_then(|scaling| scaling.checked_mul(uint))
232 {
233 Some(amount) => Ok(Self { raw: amount }),
234 None => Err(AmountParseError::ConvertToDecimal),
235 }
236 }
237
238 pub fn from_masp_denominated(val: u64, denom: MaspDigitPos) -> Self {
241 let mut raw = [0u64; 4];
242 raw[denom as usize] = val;
243 Self { raw: Uint(raw) }
244 }
245
246 pub fn from_masp_denominated_i128(
249 val: i128,
250 denom: MaspDigitPos,
251 ) -> Option<Self> {
252 #[allow(clippy::cast_sign_loss)]
253 #[allow(clippy::cast_possible_truncation)]
254 let lo = val as u64;
255 #[allow(clippy::cast_sign_loss)]
256 let hi = (val >> 64) as u64;
257 let lo_pos = denom as usize;
258 let hi_pos = lo_pos.checked_add(1)?;
259 let mut raw = [0u64; 4];
260 raw[lo_pos] = lo;
261 if hi != 0 && hi_pos >= 4 {
262 return None;
263 } else if hi != 0 {
264 raw[hi_pos] = hi;
265 }
266 Some(Self { raw: Uint(raw) })
267 }
268
269 pub fn to_string_native(&self) -> String {
271 DenominatedAmount {
272 amount: *self,
273 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
274 }
275 .to_string_precise()
276 }
277
278 #[inline]
280 pub const fn native_denominated(self) -> DenominatedAmount {
281 DenominatedAmount::native(self)
282 }
283
284 pub fn from_string_precise(string: &str) -> Result<Self, AmountParseError> {
287 DenominatedAmount::from_str(string).map(|den| den.amount)
288 }
289
290 pub fn mul_ceil(&self, dec: Dec) -> Result<Self, arith::Error> {
293 let _ = checked!(Dec(I256::maximum()) - dec)?;
295
296 let tot = checked!(self.raw * dec.abs())?;
297 let denom = Uint::from(10u64.pow(u32::from(POS_DECIMAL_PRECISION)));
298 let floor_div = checked!(tot / denom)?;
299 let rem = checked!(tot % denom)?;
300 let raw = if !rem.is_zero() {
302 checked!(floor_div + Uint::one())?
303 } else {
304 floor_div
305 };
306 Ok(Self { raw })
307 }
308
309 pub fn mul_floor(&self, dec: Dec) -> Result<Self, arith::Error> {
312 let _ = checked!(Dec(I256::maximum()) - dec)?;
314
315 let raw = checked!(
316 (Uint::from(*self) * dec.0.abs())
317 / Uint::from(10u64.pow(u32::from(POS_DECIMAL_PRECISION)))
318 )?;
319 Ok(Self { raw })
320 }
321
322 pub fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Option<Self> {
324 iter.try_fold(Amount::zero(), |acc, amt| acc.checked_add(amt))
325 }
326
327 pub fn checked_div_u64(self, rhs: u64) -> Option<Self> {
329 if rhs == 0 {
330 return None;
331 }
332 let raw = self.raw.checked_div(Uint::from(rhs))?;
333 Some(Self { raw })
334 }
335
336 pub fn u128_eucl_div_rem(
339 mut self,
340 (a, b): (u128, u128),
341 ) -> Option<(Amount, Amount)> {
342 let a = Uint::from(a);
343 let b = Uint::from(b);
344 let raw = (self.raw.checked_div(b))?.checked_mul(a)?;
345 let amt = Amount { raw };
346 self.raw = self.raw.checked_rem(b)?;
347 Some((amt, self))
348 }
349}
350
351impl Zero for Amount {
352 fn zero() -> Self {
353 Self { raw: uint::ZERO }
354 }
355
356 fn is_zero(&self) -> bool {
357 self.raw == uint::ZERO
358 }
359}
360
361impl One for Amount {
362 fn one() -> Self {
363 Self { raw: uint::ONE }
364 }
365
366 fn is_one(&self) -> bool {
367 self.raw == uint::ONE
368 }
369}
370
371impl CheckedAdd for Amount {
372 type Output = Amount;
373
374 fn checked_add(self, rhs: Self) -> Option<Self::Output> {
375 Amount::checked_add(&self, rhs)
376 }
377}
378
379impl CheckedAdd for &Amount {
380 type Output = Amount;
381
382 fn checked_add(self, rhs: Self) -> Option<Self::Output> {
383 self.checked_add(*rhs)
384 }
385}
386
387impl CheckedSub for Amount {
388 type Output = Amount;
389
390 fn checked_sub(self, amount: Self) -> Option<Self::Output> {
391 self.raw
392 .checked_sub(amount.raw)
393 .map(|result| Self { raw: result })
394 }
395}
396
397impl CheckedSub for &Amount {
398 type Output = Amount;
399
400 fn checked_sub(self, amount: Self) -> Option<Self::Output> {
401 self.raw
402 .checked_sub(amount.raw)
403 .map(|result| Amount { raw: result })
404 }
405}
406
407impl Display for Amount {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 write!(f, "{}", self.raw)
410 }
411}
412
413#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
417#[derive(
418 Debug,
419 Copy,
420 Clone,
421 Hash,
422 PartialEq,
423 Eq,
424 PartialOrd,
425 Ord,
426 BorshSerialize,
427 BorshDeserialize,
428 BorshDeserializer,
429 BorshSchema,
430 Serialize,
431 Deserialize,
432)]
433#[serde(transparent)]
434pub struct Denomination(pub u8);
435
436impl From<u8> for Denomination {
437 fn from(denom: u8) -> Self {
438 Self(denom)
439 }
440}
441
442impl From<Denomination> for u8 {
443 fn from(denom: Denomination) -> Self {
444 denom.0
445 }
446}
447
448#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
450#[derive(
451 Debug,
452 Copy,
453 Clone,
454 Hash,
455 PartialEq,
456 Eq,
457 BorshSerialize,
458 BorshDeserialize,
459 BorshDeserializer,
460 BorshSchema,
461)]
462pub struct DenominatedAmount {
463 amount: Amount,
465 denom: Denomination,
467}
468
469impl DenominatedAmount {
470 pub const fn new(amount: Amount, denom: Denomination) -> Self {
472 Self { amount, denom }
473 }
474
475 pub const fn native(amount: Amount) -> Self {
477 Self {
478 amount,
479 denom: Denomination(NATIVE_MAX_DECIMAL_PLACES),
480 }
481 }
482
483 #[inline]
485 pub fn is_zero(&self) -> bool {
486 self.amount.is_zero()
487 }
488
489 pub fn to_string_precise(&self) -> String {
494 let decimals = self.denom.0 as usize;
495 let mut string = self.amount.raw.to_string();
496 if decimals == 0 {
498 return string;
499 }
500 if string.len() > decimals {
501 #[allow(clippy::arithmetic_side_effects)]
503 let idx = string.len() - decimals;
504 string.insert(idx, '.');
505 } else {
506 for _ in string.len()..decimals {
507 string.insert(0, '0');
508 }
509 string.insert(0, '.');
510 string.insert(0, '0');
511 }
512 string
513 }
514
515 pub fn canonical(self) -> Self {
519 let mut value = self.amount.raw;
520 let ten = Uint::from(10);
521 let mut denom = self.denom.0;
522 for _ in 0..self.denom.0 {
523 let (div, rem) = value.div_mod(ten);
524 if rem == Uint::zero() {
525 value = div;
526 denom = denom.checked_sub(1).unwrap_or_default();
527 }
528 }
529 Self {
530 amount: Amount { raw: value },
531 denom: denom.into(),
532 }
533 }
534
535 pub fn increase_precision(
538 self,
539 denom: Denomination,
540 ) -> Result<Self, AmountParseError> {
541 if denom.0 < self.denom.0 {
542 return Err(AmountParseError::PrecisionDecrease);
543 }
544 #[allow(clippy::arithmetic_side_effects)]
546 let denom_diff = denom.0 - self.denom.0;
547 Uint::from(10)
548 .checked_pow(Uint::from(denom_diff))
549 .and_then(|scaling| self.amount.raw.checked_mul(scaling))
550 .map(|amount| Self {
551 amount: Amount { raw: amount },
552 denom,
553 })
554 .ok_or(AmountParseError::PrecisionOverflow)
555 }
556
557 pub fn approximate(
560 self,
561 denom: Denomination,
562 ) -> Result<(Self, Self), AmountParseError> {
563 if denom.0 < self.denom.0 {
564 let amount = self.amount.raw_amount();
566 #[allow(clippy::arithmetic_side_effects)]
567 let (quot, rem) = Uint::from(10)
568 .checked_pow(Uint::from(self.denom.0 - denom.0))
569 .and_then(|scaling| {
570 amount.checked_div(scaling).zip(amount.checked_rem(scaling))
571 })
572 .ok_or(AmountParseError::PrecisionOverflow)?;
573 let approx = Self {
574 amount: quot.into(),
575 denom,
576 };
577 let error = Self {
578 amount: rem.into(),
579 denom: self.denom,
580 };
581 Ok((approx, error))
582 } else {
583 let error = Self {
585 amount: 0.into(),
586 denom: self.denom,
587 };
588 self.increase_precision(denom).map(|x| (x, error))
589 }
590 }
591
592 pub fn redenominate(self, new_denom: u8) -> Self {
595 Self {
596 amount: self.amount,
597 denom: new_denom.into(),
598 }
599 }
600
601 pub fn scale(
604 self,
605 denom: impl Into<u8>,
606 ) -> Result<Amount, AmountParseError> {
607 self.increase_precision(Denomination(denom.into()))
608 .map(|x| x.amount)
609 }
610
611 pub fn checked_mul(&self, rhs: DenominatedAmount) -> Option<Self> {
613 let amount = self.amount.checked_mul(rhs.amount)?;
614 let denom = self.denom.0.checked_add(rhs.denom.0)?.into();
615 Some(Self { amount, denom })
616 }
617
618 pub fn checked_sub(&self, mut rhs: DenominatedAmount) -> Option<Self> {
620 let mut lhs = *self;
621 if lhs.denom < rhs.denom {
622 lhs = lhs.increase_precision(rhs.denom).ok()?;
623 } else {
624 rhs = rhs.increase_precision(lhs.denom).ok()?;
625 }
626 let amount = lhs.amount.checked_sub(rhs.amount)?;
627 Some(Self {
628 amount,
629 denom: lhs.denom,
630 })
631 }
632
633 pub fn checked_add(&self, mut rhs: DenominatedAmount) -> Option<Self> {
635 let mut lhs = *self;
636 if lhs.denom < rhs.denom {
637 lhs = lhs.increase_precision(rhs.denom).ok()?;
638 } else {
639 rhs = rhs.increase_precision(lhs.denom).ok()?;
640 }
641 let amount = lhs.amount.checked_add(rhs.amount)?;
642 Some(Self {
643 amount,
644 denom: lhs.denom,
645 })
646 }
647
648 pub fn checked_div_precision(
651 &self,
652 rhs: DenominatedAmount,
653 denom: Denomination,
654 ) -> Option<Self> {
655 #[allow(clippy::arithmetic_side_effects)]
656 let pow = i16::from(rhs.denom.0) + i16::from(denom.0)
657 - i16::from(self.denom.0);
658 if pow < 0 {
659 return None;
660 }
661 let amount = Uint::from(10).checked_pow(Uint::from(pow)).and_then(
662 |scaling| {
663 scaling.checked_mul_div(
664 self.amount.raw_amount(),
665 rhs.amount.raw_amount(),
666 )
667 },
668 )?;
669 Some(Self {
670 amount: amount.0.into(),
671 denom,
672 })
673 }
674
675 pub fn checked_div(&self, rhs: DenominatedAmount) -> Option<Self> {
677 self.checked_div_precision(rhs, self.denom)
678 }
679
680 pub const fn amount(&self) -> Amount {
682 self.amount
683 }
684
685 pub const fn denom(&self) -> Denomination {
687 self.denom
688 }
689}
690
691impl Display for DenominatedAmount {
692 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693 let string = self.to_string_precise();
694 let string = if self.denom.0 > 0 {
695 string.trim_end_matches(['0'])
696 } else {
697 &string
698 };
699 let string = string.trim_end_matches(['.']);
700 f.write_str(string)
701 }
702}
703
704impl FromStr for DenominatedAmount {
705 type Err = AmountParseError;
706
707 fn from_str(s: &str) -> Result<Self, Self::Err> {
708 let precision = s.find('.').map(|pos| {
709 s.len()
710 .checked_sub(pos.checked_add(1).unwrap_or(pos))
711 .unwrap_or_default()
712 });
713 let digits = s
714 .chars()
715 .filter_map(|c| {
716 if c.is_numeric() {
717 c.to_digit(10).map(Uint::from)
718 } else {
719 None
720 }
721 })
722 .rev()
723 .collect::<Vec<_>>();
724 if digits.len() != s.len() && precision.is_none()
725 || digits.len() != s.len().checked_sub(1).unwrap_or_default()
726 && precision.is_some()
727 {
728 return Err(AmountParseError::NotNumeric);
729 }
730 if digits.len() > 77 {
731 return Err(AmountParseError::ScaleTooLarge(digits.len(), 77));
732 }
733 let mut value = Uint::default();
734 let ten = Uint::from(10);
735 for (pow, digit) in digits.into_iter().enumerate() {
736 value = ten
737 .checked_pow(Uint::from(pow))
738 .and_then(|scaling| scaling.checked_mul(digit))
739 .and_then(|scaled| value.checked_add(scaled))
740 .ok_or(AmountParseError::InvalidRange)?;
741 }
742 let denom = Denomination(
743 u8::try_from(precision.unwrap_or_default())
744 .map_err(|_e| AmountParseError::PrecisionOverflow)?,
745 );
746 Ok(Self {
747 amount: Amount { raw: value },
748 denom,
749 })
750 }
751}
752
753impl PartialOrd for DenominatedAmount {
754 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
755 Some(self.cmp(other))
756 }
757}
758
759impl Ord for DenominatedAmount {
760 fn cmp(&self, other: &Self) -> Ordering {
761 if self.amount.is_zero() && other.is_zero() {
762 Ordering::Equal
763 } else if self.amount.is_zero() {
764 Ordering::Less
765 } else if other.amount.is_zero() {
766 Ordering::Greater
767 } else if self.denom < other.denom {
768 #[allow(clippy::arithmetic_side_effects)]
770 let diff = other.denom.0 - self.denom.0;
771 if diff > 77 {
772 return Ordering::Greater;
774 }
775 let (div, rem) =
776 other.amount.raw.div_mod(Uint::exp10(diff as usize));
777 let div_ceil = if rem.is_zero() {
778 div
779 } else {
780 div.checked_add(Uint::one()).unwrap_or(Uint::MAX)
781 };
782 let ord = self.amount.raw.cmp(&div_ceil);
783 if let Ordering::Equal = ord {
784 if rem.is_zero() {
785 Ordering::Equal
786 } else {
787 Ordering::Greater
788 }
789 } else {
790 ord
791 }
792 } else {
793 #[allow(clippy::arithmetic_side_effects)]
795 let diff = self.denom.0 - other.denom.0;
796 if diff > 77 {
797 return Ordering::Less;
799 }
800 let (div, rem) =
801 self.amount.raw.div_mod(Uint::exp10(diff as usize));
802 let div_ceil = if rem.is_zero() {
803 div
804 } else {
805 div.checked_add(Uint::one()).unwrap_or(Uint::MAX)
806 };
807 let ord = div_ceil.cmp(&other.amount.raw);
808 if let Ordering::Equal = ord {
809 if rem.is_zero() {
810 Ordering::Equal
811 } else {
812 Ordering::Less
813 }
814 } else {
815 ord
816 }
817 }
818 }
819}
820
821impl serde::Serialize for Amount {
822 fn serialize<S>(
823 &self,
824 serializer: S,
825 ) -> std::result::Result<S::Ok, S::Error>
826 where
827 S: serde::Serializer,
828 {
829 let amount_string = self.raw.to_string();
830 serde::Serialize::serialize(&amount_string, serializer)
831 }
832}
833
834impl<'de> serde::Deserialize<'de> for Amount {
835 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
836 where
837 D: serde::Deserializer<'de>,
838 {
839 let amount_string: String =
840 serde::Deserialize::deserialize(deserializer)?;
841 let amt = DenominatedAmount::from_str(&amount_string).unwrap();
842 Ok(amt.amount)
843 }
844}
845
846impl serde::Serialize for DenominatedAmount {
847 fn serialize<S>(
848 &self,
849 serializer: S,
850 ) -> std::result::Result<S::Ok, S::Error>
851 where
852 S: serde::Serializer,
853 {
854 let amount_string = self.to_string_precise();
855 serde::Serialize::serialize(&amount_string, serializer)
856 }
857}
858
859impl<'de> serde::Deserialize<'de> for DenominatedAmount {
860 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
861 where
862 D: serde::Deserializer<'de>,
863 {
864 use serde::de::Error;
865 let amount_string: String =
866 serde::Deserialize::deserialize(deserializer)?;
867 Self::from_str(&amount_string).map_err(D::Error::custom)
868 }
869}
870
871impl From<Amount> for DenominatedAmount {
872 fn from(amount: Amount) -> Self {
873 DenominatedAmount::new(amount, 0.into())
874 }
875}
876
877impl From<u64> for Amount {
879 fn from(val: u64) -> Amount {
880 Amount {
881 raw: Uint::from(val),
882 }
883 }
884}
885
886impl From<Amount> for U256 {
887 fn from(amt: Amount) -> Self {
888 Self(amt.raw.0)
889 }
890}
891
892impl TryFrom<Dec> for Amount {
893 type Error = arith::Error;
894
895 fn try_from(dec: Dec) -> Result<Amount, Self::Error> {
896 let _ = checked!(Dec(I256::maximum()) - dec)?;
898
899 #[allow(clippy::arithmetic_side_effects)]
901 let raw = dec.0.abs() / Uint::exp10(POS_DECIMAL_PRECISION as usize);
902 Ok(Amount { raw })
903 }
904}
905
906impl TryFrom<Amount> for u128 {
907 type Error = std::io::Error;
908
909 fn try_from(value: Amount) -> Result<Self, Self::Error> {
910 let Uint(arr) = value.raw;
911 for word in arr.iter().skip(2) {
912 if *word != 0 {
913 return Err(std::io::Error::new(
914 std::io::ErrorKind::InvalidInput,
915 "Integer overflow when casting to u128",
916 ));
917 }
918 }
919 Ok(value.raw.low_u128())
920 }
921}
922
923impl KeySeg for Amount {
924 fn parse(string: String) -> super::storage::Result<Self>
925 where
926 Self: Sized,
927 {
928 let bytes = BASE32HEX_NOPAD.decode(string.as_ref()).map_err(|err| {
929 storage::Error::ParseKeySeg(format!(
930 "Failed parsing {} with {}",
931 string, err
932 ))
933 })?;
934 Ok(Amount {
935 raw: Uint::from_big_endian(&bytes),
936 })
937 }
938
939 fn raw(&self) -> String {
940 let buf = self.raw.to_big_endian();
941 BASE32HEX_NOPAD.encode(&buf)
942 }
943
944 fn to_db_key(&self) -> DbKeySeg {
945 DbKeySeg::StringSeg(self.raw())
946 }
947}
948
949#[allow(missing_docs)]
950#[derive(Error, Debug)]
951pub enum AmountParseError {
952 #[error(
953 "Error decoding token amount, too many decimal places: {0}. Maximum \
954 {1}"
955 )]
956 ScaleTooLarge(usize, u8),
957 #[error(
958 "Error decoding token amount, the value is not within invalid range."
959 )]
960 InvalidRange,
961 #[error("Error converting amount to decimal, number too large.")]
962 ConvertToDecimal,
963 #[error(
964 "Could not convert from string, expected an unsigned 256-bit integer."
965 )]
966 FromString,
967 #[error("Could not parse string as a correctly formatted number.")]
968 NotNumeric,
969 #[error("This amount cannot handle the requested precision in 256 bits.")]
970 PrecisionOverflow,
971 #[error("More precision given in the amount than requested.")]
972 PrecisionDecrease,
973}
974
975impl From<Amount> for Change {
976 fn from(amount: Amount) -> Self {
977 amount.raw.try_into().unwrap()
978 }
979}
980
981impl From<Change> for Amount {
982 fn from(change: Change) -> Self {
983 Amount { raw: change.abs() }
984 }
985}
986
987impl From<Amount> for Uint {
988 fn from(amount: Amount) -> Self {
989 amount.raw
990 }
991}
992
993impl From<Uint> for Amount {
994 fn from(raw: Uint) -> Self {
995 Self { raw }
996 }
997}
998
999#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1002#[derive(
1003 Copy,
1004 Clone,
1005 Debug,
1006 PartialEq,
1007 Eq,
1008 PartialOrd,
1009 Ord,
1010 Hash,
1011 BorshSerialize,
1012 BorshDeserialize,
1013 BorshDeserializer,
1014 BorshSchema,
1015 Serialize,
1016 Deserialize,
1017)]
1018#[repr(u8)]
1019#[allow(missing_docs)]
1020#[borsh(use_discriminant = true)]
1021pub enum MaspDigitPos {
1022 Zero = 0,
1023 One,
1024 Two,
1025 Three,
1026}
1027
1028impl TryFrom<u8> for MaspDigitPos {
1029 type Error = &'static str;
1030
1031 fn try_from(denom: u8) -> Result<Self, Self::Error> {
1032 match denom {
1033 0 => Ok(Self::Zero),
1034 1 => Ok(Self::One),
1035 2 => Ok(Self::Two),
1036 3 => Ok(Self::Three),
1037 _ => Err("Possible MASP denominations must be between 0 and 3"),
1038 }
1039 }
1040}
1041
1042impl MaspDigitPos {
1043 pub fn iter() -> impl Iterator<Item = MaspDigitPos> {
1045 [
1046 MaspDigitPos::Zero,
1047 MaspDigitPos::One,
1048 MaspDigitPos::Two,
1049 MaspDigitPos::Three,
1050 ]
1051 .into_iter()
1052 }
1053
1054 pub fn denominate<'a>(&self, amount: impl Into<&'a Amount>) -> u64 {
1056 let amount = amount.into();
1057 amount.raw.0[*self as usize]
1058 }
1059}
1060
1061impl From<Amount> for IbcAmount {
1062 fn from(amount: Amount) -> Self {
1063 primitive_types::U256(amount.raw.0).into()
1064 }
1065}
1066
1067impl TryFrom<IbcAmount> for Amount {
1068 type Error = AmountParseError;
1069
1070 fn try_from(amount: IbcAmount) -> Result<Self, Self::Error> {
1071 let uint = Uint(primitive_types::U256::from(amount).0);
1072 Self::from_uint(uint, 0)
1073 }
1074}
1075
1076impl From<DenominatedAmount> for IbcAmount {
1077 fn from(amount: DenominatedAmount) -> Self {
1078 amount.amount.into()
1079 }
1080}
1081
1082#[allow(missing_docs)]
1083#[derive(Error, Debug)]
1084pub enum AmountError {
1085 #[error("Insufficient amount")]
1086 Insufficient,
1087 #[error("Amount overlofow")]
1088 Overflow,
1089}
1090
1091#[cfg(any(test, feature = "testing"))]
1092#[allow(clippy::arithmetic_side_effects)]
1094pub mod testing {
1095 use proptest::prelude::*;
1096
1097 use super::*;
1098
1099 impl std::ops::Add for Amount {
1100 type Output = Self;
1101
1102 fn add(self, rhs: Self) -> Self::Output {
1103 self.checked_add(rhs).unwrap()
1104 }
1105 }
1106
1107 impl std::ops::AddAssign for Amount {
1108 fn add_assign(&mut self, rhs: Self) {
1109 *self = self.checked_add(rhs).unwrap();
1110 }
1111 }
1112
1113 impl std::ops::Sub for Amount {
1114 type Output = Self;
1115
1116 fn sub(self, rhs: Self) -> Self::Output {
1117 self.checked_sub(rhs).unwrap()
1118 }
1119 }
1120
1121 impl std::ops::SubAssign for Amount {
1122 fn sub_assign(&mut self, rhs: Self) {
1123 *self = *self - rhs;
1124 }
1125 }
1126
1127 impl<T> std::ops::Mul<T> for Amount
1128 where
1129 T: Into<Self>,
1130 {
1131 type Output = Amount;
1132
1133 fn mul(self, rhs: T) -> Self::Output {
1134 self.checked_mul(rhs.into()).unwrap()
1135 }
1136 }
1137
1138 impl std::ops::Mul<Amount> for u64 {
1139 type Output = Amount;
1140
1141 fn mul(self, rhs: Amount) -> Self::Output {
1142 rhs * self
1143 }
1144 }
1145
1146 impl std::ops::Div<u64> for Amount {
1147 type Output = Self;
1148
1149 fn div(self, rhs: u64) -> Self::Output {
1150 Self {
1151 raw: self.raw / Uint::from(rhs),
1152 }
1153 }
1154 }
1155
1156 impl std::iter::Sum for Amount {
1157 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1158 iter.fold(Amount::zero(), |a, b| a + b)
1159 }
1160 }
1161
1162 prop_compose! {
1163 pub fn arb_denomination()(denom in 0u8..) -> Denomination {
1165 Denomination(denom)
1166 }
1167 }
1168
1169 prop_compose! {
1170 pub fn arb_denominated_amount()(
1172 amount in arb_amount(),
1173 denom in arb_denomination(),
1174 ) -> DenominatedAmount {
1175 DenominatedAmount::new(amount, denom)
1176 }
1177 }
1178
1179 pub fn arb_amount() -> impl Strategy<Value = Amount> {
1181 any::<u64>().prop_map(|val| Amount::from_uint(val, 0).unwrap())
1182 }
1183
1184 pub fn arb_amount_ceiled(max: u64) -> impl Strategy<Value = Amount> {
1186 (0..=max).prop_map(|val| Amount::from_uint(val, 0).unwrap())
1187 }
1188
1189 pub fn arb_amount_non_zero_ceiled(
1192 max: u64,
1193 ) -> impl Strategy<Value = Amount> {
1194 (1..=max).prop_map(|val| Amount::from_uint(val, 0).unwrap())
1195 }
1196}
1197
1198#[cfg(test)]
1199mod tests {
1200 use assert_matches::assert_matches;
1201
1202 use super::*;
1203
1204 #[test]
1205 fn test_token_display() {
1206 let max = Amount::from_uint(u64::MAX, 0).expect("Test failed");
1207 assert_eq!("18446744073709.551615", max.to_string_native());
1208 let max = DenominatedAmount {
1209 amount: max,
1210 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1211 };
1212 assert_eq!("18446744073709.551615", max.to_string());
1213
1214 let whole =
1215 Amount::from_uint(u64::MAX / NATIVE_SCALE * NATIVE_SCALE, 0)
1216 .expect("Test failed");
1217 assert_eq!("18446744073709.000000", whole.to_string_native());
1218 let whole = DenominatedAmount {
1219 amount: whole,
1220 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1221 };
1222 assert_eq!("18446744073709", whole.to_string());
1223
1224 let trailing_zeroes =
1225 Amount::from_uint(123000, 0).expect("Test failed");
1226 assert_eq!("0.123000", trailing_zeroes.to_string_native());
1227 let trailing_zeroes = DenominatedAmount {
1228 amount: trailing_zeroes,
1229 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1230 };
1231 assert_eq!("0.123", trailing_zeroes.to_string());
1232
1233 let zero = Amount::default();
1234 assert_eq!("0.000000", zero.to_string_native());
1235 let zero = DenominatedAmount {
1236 amount: zero,
1237 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1238 };
1239 assert_eq!("0", zero.to_string());
1240
1241 let amount = DenominatedAmount {
1242 amount: Amount::from_uint(1120, 0).expect("Test failed"),
1243 denom: 3u8.into(),
1244 };
1245 assert_eq!("1.12", amount.to_string());
1246 assert_eq!("1.120", amount.to_string_precise());
1247
1248 let amount = DenominatedAmount {
1249 amount: Amount::from_uint(1120, 0).expect("Test failed"),
1250 denom: 5u8.into(),
1251 };
1252 assert_eq!("0.0112", amount.to_string());
1253 assert_eq!("0.01120", amount.to_string_precise());
1254
1255 let amount = DenominatedAmount {
1256 amount: Amount::from_uint(200, 0).expect("Test failed"),
1257 denom: 0.into(),
1258 };
1259 assert_eq!("200", amount.to_string());
1260 assert_eq!("200", amount.to_string_precise());
1261 }
1262
1263 #[test]
1264 fn test_amount_checked_sub() {
1265 let max = Amount::native_whole(u64::MAX);
1266 let one = Amount::native_whole(1);
1267 let zero = Amount::native_whole(0);
1268
1269 assert_eq!(zero.checked_sub(zero), Some(zero));
1270 assert_eq!(zero.checked_sub(one), None);
1271 assert_eq!(zero.checked_sub(max), None);
1272
1273 assert_eq!(max.checked_sub(zero), Some(max));
1274 assert_eq!(max.checked_sub(one), Some(max - one));
1275 assert_eq!(max.checked_sub(max), Some(zero));
1276 }
1277
1278 #[test]
1279 fn test_serialization_round_trip() {
1280 let amount: Amount = serde_json::from_str(r#""1000000000""#).unwrap();
1281 assert_eq!(
1282 amount,
1283 Amount {
1284 raw: Uint::from(1000000000)
1285 }
1286 );
1287 let serialized = serde_json::to_string(&amount).unwrap();
1288 assert_eq!(serialized, r#""1000000000""#);
1289 }
1290
1291 #[test]
1292 fn test_amount_checked_add() {
1293 let max = Amount::max();
1294 let max_signed = Amount::max_signed();
1295 let one = Amount::native_whole(1);
1296 let zero = Amount::native_whole(0);
1297
1298 assert_eq!(zero.checked_add(zero), Some(zero));
1299 assert_eq!(zero.checked_signed_add(zero), Some(zero));
1300 assert_eq!(zero.checked_add(one), Some(one));
1301 assert_eq!(zero.checked_add(max - one), Some(max - one));
1302 assert_eq!(
1303 zero.checked_signed_add(max_signed - one),
1304 Some(max_signed - one)
1305 );
1306 assert_eq!(zero.checked_add(max), Some(max));
1307 assert_eq!(zero.checked_signed_add(max_signed), Some(max_signed));
1308
1309 assert_eq!(max.checked_add(zero), Some(max));
1310 assert_eq!(max.checked_signed_add(zero), None);
1311 assert_eq!(max.checked_add(one), None);
1312 assert_eq!(max.checked_add(max), None);
1313
1314 assert_eq!(max_signed.checked_add(zero), Some(max_signed));
1315 assert_eq!(max_signed.checked_add(one), Some(max_signed + one));
1316 assert_eq!(max_signed.checked_signed_add(max_signed), None);
1317 }
1318
1319 #[test]
1320 fn test_amount_from_string() {
1321 assert!(Amount::from_str("1.12", 1).is_err());
1322 assert!(Amount::from_str("0.0", 0).is_err());
1323 assert!(Amount::from_str("1.12", 80).is_err());
1324 assert!(Amount::from_str("1.12.1", 3).is_err());
1325 assert!(Amount::from_str("1.1a", 3).is_err());
1326 assert_eq!(
1327 Amount::zero(),
1328 Amount::from_str("0.0", 1).expect("Test failed")
1329 );
1330 assert_eq!(
1331 Amount::zero(),
1332 Amount::from_str(".0", 1).expect("Test failed")
1333 );
1334
1335 let amount = Amount::from_str("1.12", 3).expect("Test failed");
1336 assert_eq!(amount, Amount::from_uint(1120, 0).expect("Test failed"));
1337 let amount = Amount::from_str(".34", 3).expect("Test failed");
1338 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1339 let amount = Amount::from_str("0.34", 3).expect("Test failed");
1340 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1341 let amount = Amount::from_str("34", 1).expect("Test failed");
1342 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1343 }
1344
1345 #[test]
1346 fn test_from_masp_denominated() {
1347 let uint = Uint([15u64, 16, 17, 18]);
1348 let original = Amount::from_uint(uint, 0).expect("Test failed");
1349 for denom in MaspDigitPos::iter() {
1350 let word = denom.denominate(&original);
1351 assert_eq!(word, denom as u64 + 15u64);
1352 let amount = Amount::from_masp_denominated(word, denom);
1353 let raw = Uint::from(amount).0;
1354 let mut expected = [0u64; 4];
1355 expected[denom as usize] = word;
1356 assert_eq!(raw, expected);
1357 }
1358 }
1359
1360 #[test]
1361 fn test_key_seg() {
1362 let original = Amount::from_uint(1234560000, 0).expect("Test failed");
1363 let key = original.raw();
1364 let amount = Amount::parse(key).expect("Test failed");
1365 assert_eq!(amount, original);
1366 }
1367
1368 #[test]
1369 fn test_amount_is_zero() {
1370 let zero = Amount::zero();
1371 assert!(zero.is_zero());
1372
1373 let non_zero = Amount::from_uint(1, 0).expect("Test failed");
1374 assert!(!non_zero.is_zero());
1375 }
1376
1377 #[test]
1378 fn test_token_amount_mul_ceil() {
1379 let one = Amount::from(1);
1380 let two = Amount::from(2);
1381 let three = Amount::from(3);
1382 let dec = Dec::from_str("0.34").unwrap();
1383 assert_eq!(one.mul_ceil(dec).unwrap(), one);
1384 assert_eq!(two.mul_ceil(dec).unwrap(), one);
1385 assert_eq!(three.mul_ceil(dec).unwrap(), two);
1386
1387 assert_matches!(one.mul_ceil(-dec), Err(_));
1388 assert_matches!(one.mul_ceil(-Dec::new(1, 12).unwrap()), Err(_));
1389 assert_matches!(
1390 Amount::native_whole(1).mul_ceil(-Dec::new(1, 12).unwrap()),
1391 Err(_)
1392 );
1393 }
1394
1395 #[test]
1396 fn test_token_amount_mul_floor() {
1397 let zero = Amount::zero();
1398 let one = Amount::from(1);
1399 let two = Amount::from(2);
1400 let three = Amount::from(3);
1401 let dec = Dec::from_str("0.34").unwrap();
1402 assert_eq!(one.mul_floor(dec).unwrap(), zero);
1403 assert_eq!(two.mul_floor(dec).unwrap(), zero);
1404 assert_eq!(three.mul_floor(dec).unwrap(), one);
1405
1406 assert_matches!(one.mul_floor(-dec), Err(_));
1407 assert_matches!(one.mul_floor(-Dec::new(1, 12).unwrap()), Err(_));
1408 assert_matches!(
1409 Amount::native_whole(1).mul_floor(-Dec::new(1, 12).unwrap()),
1410 Err(_)
1411 );
1412 }
1413
1414 #[test]
1415 fn test_denominateed_arithmetic() {
1416 let a = DenominatedAmount::new(10.into(), 3.into());
1417 let b = DenominatedAmount::new(10.into(), 2.into());
1418 let c = DenominatedAmount::new(110.into(), 3.into());
1419 let d = DenominatedAmount::new(90.into(), 3.into());
1420 let e = DenominatedAmount::new(100.into(), 5.into());
1421 let f = DenominatedAmount::new(100.into(), 3.into());
1422 let g = DenominatedAmount::new(0.into(), 3.into());
1423 assert_eq!(a.checked_add(b).unwrap(), c);
1424 assert_eq!(b.checked_sub(a).unwrap(), d);
1425 assert_eq!(a.checked_mul(b).unwrap(), e);
1426 assert!(a.checked_sub(b).is_none());
1427 assert_eq!(c.checked_sub(a).unwrap(), f);
1428 assert_eq!(c.checked_sub(c).unwrap(), g);
1429 }
1430
1431 #[test]
1432 fn test_denominated_amt_ord() {
1433 let denom_1 = DenominatedAmount {
1434 amount: Amount::from_uint(15, 0).expect("Test failed"),
1435 denom: 1.into(),
1436 };
1437 let denom_2 = DenominatedAmount {
1438 amount: Amount::from_uint(1500, 0).expect("Test failed"),
1439 denom: 3.into(),
1440 };
1441 assert_eq!(
1444 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1445 Ordering::Equal
1446 );
1447 assert_eq!(
1448 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1449 Ordering::Equal
1450 );
1451 assert_ne!(denom_1, denom_2);
1452
1453 let denom_1 = DenominatedAmount {
1454 amount: Amount::from_uint(15, 0).expect("Test failed"),
1455 denom: 1.into(),
1456 };
1457 let denom_2 = DenominatedAmount {
1458 amount: Amount::from_uint(1501, 0).expect("Test failed"),
1459 denom: 3.into(),
1460 };
1461 assert_eq!(
1462 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1463 Ordering::Less
1464 );
1465 assert_eq!(
1466 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1467 Ordering::Greater
1468 );
1469 let denom_1 = DenominatedAmount {
1470 amount: Amount::from_uint(15, 0).expect("Test failed"),
1471 denom: 1.into(),
1472 };
1473 let denom_2 = DenominatedAmount {
1474 amount: Amount::from_uint(1499, 0).expect("Test failed"),
1475 denom: 3.into(),
1476 };
1477 assert_eq!(
1478 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1479 Ordering::Greater
1480 );
1481 assert_eq!(
1482 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1483 Ordering::Less
1484 );
1485 }
1486
1487 #[test]
1488 fn test_token_amount_from_u128() {
1489 for val in [
1490 u128::MIN,
1491 u128::MIN + 1,
1492 u128::from(u64::MAX) - 1,
1493 u128::from(u64::MAX),
1494 u128::from(u64::MAX) + 1,
1495 u128::MAX - 1,
1496 u128::MAX,
1497 ] {
1498 let raw = Uint::from(val);
1499 let amount = Amount::from_u128(val);
1500 assert_eq!(raw, amount.raw);
1501 assert_eq!(amount.raw.as_u128(), val);
1502 }
1503 }
1504}