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, 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 CheckedAdd for Amount {
352 type Output = Amount;
353
354 fn checked_add(self, rhs: Self) -> Option<Self::Output> {
355 Amount::checked_add(&self, rhs)
356 }
357}
358
359impl CheckedAdd for &Amount {
360 type Output = Amount;
361
362 fn checked_add(self, rhs: Self) -> Option<Self::Output> {
363 self.checked_add(*rhs)
364 }
365}
366
367impl CheckedSub for Amount {
368 type Output = Amount;
369
370 fn checked_sub(self, amount: Self) -> Option<Self::Output> {
371 self.raw
372 .checked_sub(amount.raw)
373 .map(|result| Self { raw: result })
374 }
375}
376
377impl CheckedSub for &Amount {
378 type Output = Amount;
379
380 fn checked_sub(self, amount: Self) -> Option<Self::Output> {
381 self.raw
382 .checked_sub(amount.raw)
383 .map(|result| Amount { raw: result })
384 }
385}
386
387impl Display for Amount {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 write!(f, "{}", self.raw)
390 }
391}
392
393#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
397#[derive(
398 Debug,
399 Copy,
400 Clone,
401 Hash,
402 PartialEq,
403 Eq,
404 PartialOrd,
405 Ord,
406 BorshSerialize,
407 BorshDeserialize,
408 BorshDeserializer,
409 BorshSchema,
410 Serialize,
411 Deserialize,
412)]
413#[serde(transparent)]
414pub struct Denomination(pub u8);
415
416impl From<u8> for Denomination {
417 fn from(denom: u8) -> Self {
418 Self(denom)
419 }
420}
421
422impl From<Denomination> for u8 {
423 fn from(denom: Denomination) -> Self {
424 denom.0
425 }
426}
427
428#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
430#[derive(
431 Debug,
432 Copy,
433 Clone,
434 Hash,
435 PartialEq,
436 Eq,
437 BorshSerialize,
438 BorshDeserialize,
439 BorshDeserializer,
440 BorshSchema,
441)]
442pub struct DenominatedAmount {
443 amount: Amount,
445 denom: Denomination,
447}
448
449impl DenominatedAmount {
450 pub const fn new(amount: Amount, denom: Denomination) -> Self {
452 Self { amount, denom }
453 }
454
455 pub const fn native(amount: Amount) -> Self {
457 Self {
458 amount,
459 denom: Denomination(NATIVE_MAX_DECIMAL_PLACES),
460 }
461 }
462
463 #[inline]
465 pub fn is_zero(&self) -> bool {
466 self.amount.is_zero()
467 }
468
469 pub fn to_string_precise(&self) -> String {
474 let decimals = self.denom.0 as usize;
475 let mut string = self.amount.raw.to_string();
476 if decimals == 0 {
478 return string;
479 }
480 if string.len() > decimals {
481 #[allow(clippy::arithmetic_side_effects)]
483 let idx = string.len() - decimals;
484 string.insert(idx, '.');
485 } else {
486 for _ in string.len()..decimals {
487 string.insert(0, '0');
488 }
489 string.insert(0, '.');
490 string.insert(0, '0');
491 }
492 string
493 }
494
495 pub fn canonical(self) -> Self {
499 let mut value = self.amount.raw;
500 let ten = Uint::from(10);
501 let mut denom = self.denom.0;
502 for _ in 0..self.denom.0 {
503 let (div, rem) = value.div_mod(ten);
504 if rem == Uint::zero() {
505 value = div;
506 denom = denom.checked_sub(1).unwrap_or_default();
507 }
508 }
509 Self {
510 amount: Amount { raw: value },
511 denom: denom.into(),
512 }
513 }
514
515 pub fn increase_precision(
518 self,
519 denom: Denomination,
520 ) -> Result<Self, AmountParseError> {
521 if denom.0 < self.denom.0 {
522 return Err(AmountParseError::PrecisionDecrease);
523 }
524 #[allow(clippy::arithmetic_side_effects)]
526 let denom_diff = denom.0 - self.denom.0;
527 Uint::from(10)
528 .checked_pow(Uint::from(denom_diff))
529 .and_then(|scaling| self.amount.raw.checked_mul(scaling))
530 .map(|amount| Self {
531 amount: Amount { raw: amount },
532 denom,
533 })
534 .ok_or(AmountParseError::PrecisionOverflow)
535 }
536
537 pub fn redenominate(self, new_denom: u8) -> Self {
540 Self {
541 amount: self.amount,
542 denom: new_denom.into(),
543 }
544 }
545
546 pub fn scale(
549 self,
550 denom: impl Into<u8>,
551 ) -> Result<Amount, AmountParseError> {
552 self.increase_precision(Denomination(denom.into()))
553 .map(|x| x.amount)
554 }
555
556 pub fn checked_mul(&self, rhs: DenominatedAmount) -> Option<Self> {
558 let amount = self.amount.checked_mul(rhs.amount)?;
559 let denom = self.denom.0.checked_add(rhs.denom.0)?.into();
560 Some(Self { amount, denom })
561 }
562
563 pub fn checked_sub(&self, mut rhs: DenominatedAmount) -> Option<Self> {
565 let mut lhs = *self;
566 if lhs.denom < rhs.denom {
567 lhs = lhs.increase_precision(rhs.denom).ok()?;
568 } else {
569 rhs = rhs.increase_precision(lhs.denom).ok()?;
570 }
571 let amount = lhs.amount.checked_sub(rhs.amount)?;
572 Some(Self {
573 amount,
574 denom: lhs.denom,
575 })
576 }
577
578 pub fn checked_add(&self, mut rhs: DenominatedAmount) -> Option<Self> {
580 let mut lhs = *self;
581 if lhs.denom < rhs.denom {
582 lhs = lhs.increase_precision(rhs.denom).ok()?;
583 } else {
584 rhs = rhs.increase_precision(lhs.denom).ok()?;
585 }
586 let amount = lhs.amount.checked_add(rhs.amount)?;
587 Some(Self {
588 amount,
589 denom: lhs.denom,
590 })
591 }
592
593 pub const fn amount(&self) -> Amount {
595 self.amount
596 }
597
598 pub const fn denom(&self) -> Denomination {
600 self.denom
601 }
602}
603
604impl Display for DenominatedAmount {
605 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
606 let string = self.to_string_precise();
607 let string = if self.denom.0 > 0 {
608 string.trim_end_matches(['0'])
609 } else {
610 &string
611 };
612 let string = string.trim_end_matches(['.']);
613 f.write_str(string)
614 }
615}
616
617impl FromStr for DenominatedAmount {
618 type Err = AmountParseError;
619
620 fn from_str(s: &str) -> Result<Self, Self::Err> {
621 let precision = s.find('.').map(|pos| {
622 s.len()
623 .checked_sub(pos.checked_add(1).unwrap_or(pos))
624 .unwrap_or_default()
625 });
626 let digits = s
627 .chars()
628 .filter_map(|c| {
629 if c.is_numeric() {
630 c.to_digit(10).map(Uint::from)
631 } else {
632 None
633 }
634 })
635 .rev()
636 .collect::<Vec<_>>();
637 if digits.len() != s.len() && precision.is_none()
638 || digits.len() != s.len().checked_sub(1).unwrap_or_default()
639 && precision.is_some()
640 {
641 return Err(AmountParseError::NotNumeric);
642 }
643 if digits.len() > 77 {
644 return Err(AmountParseError::ScaleTooLarge(digits.len(), 77));
645 }
646 let mut value = Uint::default();
647 let ten = Uint::from(10);
648 for (pow, digit) in digits.into_iter().enumerate() {
649 value = ten
650 .checked_pow(Uint::from(pow))
651 .and_then(|scaling| scaling.checked_mul(digit))
652 .and_then(|scaled| value.checked_add(scaled))
653 .ok_or(AmountParseError::InvalidRange)?;
654 }
655 let denom = Denomination(
656 u8::try_from(precision.unwrap_or_default())
657 .map_err(|_e| AmountParseError::PrecisionOverflow)?,
658 );
659 Ok(Self {
660 amount: Amount { raw: value },
661 denom,
662 })
663 }
664}
665
666impl PartialOrd for DenominatedAmount {
667 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
668 Some(self.cmp(other))
669 }
670}
671
672impl Ord for DenominatedAmount {
673 fn cmp(&self, other: &Self) -> Ordering {
674 if self.amount.is_zero() && other.is_zero() {
675 Ordering::Equal
676 } else if self.amount.is_zero() {
677 Ordering::Less
678 } else if other.amount.is_zero() {
679 Ordering::Greater
680 } else if self.denom < other.denom {
681 #[allow(clippy::arithmetic_side_effects)]
683 let diff = other.denom.0 - self.denom.0;
684 if diff > 77 {
685 return Ordering::Greater;
687 }
688 let (div, rem) =
689 other.amount.raw.div_mod(Uint::exp10(diff as usize));
690 let div_ceil = if rem.is_zero() {
691 div
692 } else {
693 div.checked_add(Uint::one()).unwrap_or(Uint::MAX)
694 };
695 let ord = self.amount.raw.cmp(&div_ceil);
696 if let Ordering::Equal = ord {
697 if rem.is_zero() {
698 Ordering::Equal
699 } else {
700 Ordering::Greater
701 }
702 } else {
703 ord
704 }
705 } else {
706 #[allow(clippy::arithmetic_side_effects)]
708 let diff = self.denom.0 - other.denom.0;
709 if diff > 77 {
710 return Ordering::Less;
712 }
713 let (div, rem) =
714 self.amount.raw.div_mod(Uint::exp10(diff as usize));
715 let div_ceil = if rem.is_zero() {
716 div
717 } else {
718 div.checked_add(Uint::one()).unwrap_or(Uint::MAX)
719 };
720 let ord = div_ceil.cmp(&other.amount.raw);
721 if let Ordering::Equal = ord {
722 if rem.is_zero() {
723 Ordering::Equal
724 } else {
725 Ordering::Less
726 }
727 } else {
728 ord
729 }
730 }
731 }
732}
733
734impl serde::Serialize for Amount {
735 fn serialize<S>(
736 &self,
737 serializer: S,
738 ) -> std::result::Result<S::Ok, S::Error>
739 where
740 S: serde::Serializer,
741 {
742 let amount_string = self.raw.to_string();
743 serde::Serialize::serialize(&amount_string, serializer)
744 }
745}
746
747impl<'de> serde::Deserialize<'de> for Amount {
748 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
749 where
750 D: serde::Deserializer<'de>,
751 {
752 let amount_string: String =
753 serde::Deserialize::deserialize(deserializer)?;
754 let amt = DenominatedAmount::from_str(&amount_string).unwrap();
755 Ok(amt.amount)
756 }
757}
758
759impl serde::Serialize for DenominatedAmount {
760 fn serialize<S>(
761 &self,
762 serializer: S,
763 ) -> std::result::Result<S::Ok, S::Error>
764 where
765 S: serde::Serializer,
766 {
767 let amount_string = self.to_string_precise();
768 serde::Serialize::serialize(&amount_string, serializer)
769 }
770}
771
772impl<'de> serde::Deserialize<'de> for DenominatedAmount {
773 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
774 where
775 D: serde::Deserializer<'de>,
776 {
777 use serde::de::Error;
778 let amount_string: String =
779 serde::Deserialize::deserialize(deserializer)?;
780 Self::from_str(&amount_string).map_err(D::Error::custom)
781 }
782}
783
784impl From<Amount> for DenominatedAmount {
785 fn from(amount: Amount) -> Self {
786 DenominatedAmount::new(amount, 0.into())
787 }
788}
789
790impl From<u64> for Amount {
792 fn from(val: u64) -> Amount {
793 Amount {
794 raw: Uint::from(val),
795 }
796 }
797}
798
799impl From<Amount> for U256 {
800 fn from(amt: Amount) -> Self {
801 Self(amt.raw.0)
802 }
803}
804
805impl TryFrom<Dec> for Amount {
806 type Error = arith::Error;
807
808 fn try_from(dec: Dec) -> Result<Amount, Self::Error> {
809 let _ = checked!(Dec(I256::maximum()) - dec)?;
811
812 #[allow(clippy::arithmetic_side_effects)]
814 let raw = dec.0.abs() / Uint::exp10(POS_DECIMAL_PRECISION as usize);
815 Ok(Amount { raw })
816 }
817}
818
819impl TryFrom<Amount> for u128 {
820 type Error = std::io::Error;
821
822 fn try_from(value: Amount) -> Result<Self, Self::Error> {
823 let Uint(arr) = value.raw;
824 for word in arr.iter().skip(2) {
825 if *word != 0 {
826 return Err(std::io::Error::new(
827 std::io::ErrorKind::InvalidInput,
828 "Integer overflow when casting to u128",
829 ));
830 }
831 }
832 Ok(value.raw.low_u128())
833 }
834}
835
836impl KeySeg for Amount {
837 fn parse(string: String) -> super::storage::Result<Self>
838 where
839 Self: Sized,
840 {
841 let bytes = BASE32HEX_NOPAD.decode(string.as_ref()).map_err(|err| {
842 storage::Error::ParseKeySeg(format!(
843 "Failed parsing {} with {}",
844 string, err
845 ))
846 })?;
847 Ok(Amount {
848 raw: Uint::from_big_endian(&bytes),
849 })
850 }
851
852 fn raw(&self) -> String {
853 let buf = self.raw.to_big_endian();
854 BASE32HEX_NOPAD.encode(&buf)
855 }
856
857 fn to_db_key(&self) -> DbKeySeg {
858 DbKeySeg::StringSeg(self.raw())
859 }
860}
861
862#[allow(missing_docs)]
863#[derive(Error, Debug)]
864pub enum AmountParseError {
865 #[error(
866 "Error decoding token amount, too many decimal places: {0}. Maximum \
867 {1}"
868 )]
869 ScaleTooLarge(usize, u8),
870 #[error(
871 "Error decoding token amount, the value is not within invalid range."
872 )]
873 InvalidRange,
874 #[error("Error converting amount to decimal, number too large.")]
875 ConvertToDecimal,
876 #[error(
877 "Could not convert from string, expected an unsigned 256-bit integer."
878 )]
879 FromString,
880 #[error("Could not parse string as a correctly formatted number.")]
881 NotNumeric,
882 #[error("This amount cannot handle the requested precision in 256 bits.")]
883 PrecisionOverflow,
884 #[error("More precision given in the amount than requested.")]
885 PrecisionDecrease,
886}
887
888impl From<Amount> for Change {
889 fn from(amount: Amount) -> Self {
890 amount.raw.try_into().unwrap()
891 }
892}
893
894impl From<Change> for Amount {
895 fn from(change: Change) -> Self {
896 Amount { raw: change.abs() }
897 }
898}
899
900impl From<Amount> for Uint {
901 fn from(amount: Amount) -> Self {
902 amount.raw
903 }
904}
905
906impl From<Uint> for Amount {
907 fn from(raw: Uint) -> Self {
908 Self { raw }
909 }
910}
911
912#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
915#[derive(
916 Copy,
917 Clone,
918 Debug,
919 PartialEq,
920 Eq,
921 PartialOrd,
922 Ord,
923 Hash,
924 BorshSerialize,
925 BorshDeserialize,
926 BorshDeserializer,
927 BorshSchema,
928 Serialize,
929 Deserialize,
930)]
931#[repr(u8)]
932#[allow(missing_docs)]
933#[borsh(use_discriminant = true)]
934pub enum MaspDigitPos {
935 Zero = 0,
936 One,
937 Two,
938 Three,
939}
940
941impl TryFrom<u8> for MaspDigitPos {
942 type Error = &'static str;
943
944 fn try_from(denom: u8) -> Result<Self, Self::Error> {
945 match denom {
946 0 => Ok(Self::Zero),
947 1 => Ok(Self::One),
948 2 => Ok(Self::Two),
949 3 => Ok(Self::Three),
950 _ => Err("Possible MASP denominations must be between 0 and 3"),
951 }
952 }
953}
954
955impl MaspDigitPos {
956 pub fn iter() -> impl Iterator<Item = MaspDigitPos> {
958 [
959 MaspDigitPos::Zero,
960 MaspDigitPos::One,
961 MaspDigitPos::Two,
962 MaspDigitPos::Three,
963 ]
964 .into_iter()
965 }
966
967 pub fn denominate<'a>(&self, amount: impl Into<&'a Amount>) -> u64 {
969 let amount = amount.into();
970 amount.raw.0[*self as usize]
971 }
972}
973
974impl From<Amount> for IbcAmount {
975 fn from(amount: Amount) -> Self {
976 primitive_types::U256(amount.raw.0).into()
977 }
978}
979
980impl TryFrom<IbcAmount> for Amount {
981 type Error = AmountParseError;
982
983 fn try_from(amount: IbcAmount) -> Result<Self, Self::Error> {
984 let uint = Uint(primitive_types::U256::from(amount).0);
985 Self::from_uint(uint, 0)
986 }
987}
988
989impl From<DenominatedAmount> for IbcAmount {
990 fn from(amount: DenominatedAmount) -> Self {
991 amount.amount.into()
992 }
993}
994
995#[allow(missing_docs)]
996#[derive(Error, Debug)]
997pub enum AmountError {
998 #[error("Insufficient amount")]
999 Insufficient,
1000 #[error("Amount overlofow")]
1001 Overflow,
1002}
1003
1004#[cfg(any(test, feature = "testing"))]
1005#[allow(clippy::arithmetic_side_effects)]
1007pub mod testing {
1008 use proptest::prelude::*;
1009
1010 use super::*;
1011
1012 impl std::ops::Add for Amount {
1013 type Output = Self;
1014
1015 fn add(self, rhs: Self) -> Self::Output {
1016 self.checked_add(rhs).unwrap()
1017 }
1018 }
1019
1020 impl std::ops::AddAssign for Amount {
1021 fn add_assign(&mut self, rhs: Self) {
1022 *self = self.checked_add(rhs).unwrap();
1023 }
1024 }
1025
1026 impl std::ops::Sub for Amount {
1027 type Output = Self;
1028
1029 fn sub(self, rhs: Self) -> Self::Output {
1030 self.checked_sub(rhs).unwrap()
1031 }
1032 }
1033
1034 impl std::ops::SubAssign for Amount {
1035 fn sub_assign(&mut self, rhs: Self) {
1036 *self = *self - rhs;
1037 }
1038 }
1039
1040 impl<T> std::ops::Mul<T> for Amount
1041 where
1042 T: Into<Self>,
1043 {
1044 type Output = Amount;
1045
1046 fn mul(self, rhs: T) -> Self::Output {
1047 self.checked_mul(rhs.into()).unwrap()
1048 }
1049 }
1050
1051 impl std::ops::Mul<Amount> for u64 {
1052 type Output = Amount;
1053
1054 fn mul(self, rhs: Amount) -> Self::Output {
1055 rhs * self
1056 }
1057 }
1058
1059 impl std::ops::Div<u64> for Amount {
1060 type Output = Self;
1061
1062 fn div(self, rhs: u64) -> Self::Output {
1063 Self {
1064 raw: self.raw / Uint::from(rhs),
1065 }
1066 }
1067 }
1068
1069 impl std::iter::Sum for Amount {
1070 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1071 iter.fold(Amount::zero(), |a, b| a + b)
1072 }
1073 }
1074
1075 prop_compose! {
1076 pub fn arb_denomination()(denom in 0u8..) -> Denomination {
1078 Denomination(denom)
1079 }
1080 }
1081
1082 prop_compose! {
1083 pub fn arb_denominated_amount()(
1085 amount in arb_amount(),
1086 denom in arb_denomination(),
1087 ) -> DenominatedAmount {
1088 DenominatedAmount::new(amount, denom)
1089 }
1090 }
1091
1092 pub fn arb_amount() -> impl Strategy<Value = Amount> {
1094 any::<u64>().prop_map(|val| Amount::from_uint(val, 0).unwrap())
1095 }
1096
1097 pub fn arb_amount_ceiled(max: u64) -> impl Strategy<Value = Amount> {
1099 (0..=max).prop_map(|val| Amount::from_uint(val, 0).unwrap())
1100 }
1101
1102 pub fn arb_amount_non_zero_ceiled(
1105 max: u64,
1106 ) -> impl Strategy<Value = Amount> {
1107 (1..=max).prop_map(|val| Amount::from_uint(val, 0).unwrap())
1108 }
1109}
1110
1111#[cfg(test)]
1112mod tests {
1113 use assert_matches::assert_matches;
1114
1115 use super::*;
1116
1117 #[test]
1118 fn test_token_display() {
1119 let max = Amount::from_uint(u64::MAX, 0).expect("Test failed");
1120 assert_eq!("18446744073709.551615", max.to_string_native());
1121 let max = DenominatedAmount {
1122 amount: max,
1123 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1124 };
1125 assert_eq!("18446744073709.551615", max.to_string());
1126
1127 let whole =
1128 Amount::from_uint(u64::MAX / NATIVE_SCALE * NATIVE_SCALE, 0)
1129 .expect("Test failed");
1130 assert_eq!("18446744073709.000000", whole.to_string_native());
1131 let whole = DenominatedAmount {
1132 amount: whole,
1133 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1134 };
1135 assert_eq!("18446744073709", whole.to_string());
1136
1137 let trailing_zeroes =
1138 Amount::from_uint(123000, 0).expect("Test failed");
1139 assert_eq!("0.123000", trailing_zeroes.to_string_native());
1140 let trailing_zeroes = DenominatedAmount {
1141 amount: trailing_zeroes,
1142 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1143 };
1144 assert_eq!("0.123", trailing_zeroes.to_string());
1145
1146 let zero = Amount::default();
1147 assert_eq!("0.000000", zero.to_string_native());
1148 let zero = DenominatedAmount {
1149 amount: zero,
1150 denom: NATIVE_MAX_DECIMAL_PLACES.into(),
1151 };
1152 assert_eq!("0", zero.to_string());
1153
1154 let amount = DenominatedAmount {
1155 amount: Amount::from_uint(1120, 0).expect("Test failed"),
1156 denom: 3u8.into(),
1157 };
1158 assert_eq!("1.12", amount.to_string());
1159 assert_eq!("1.120", amount.to_string_precise());
1160
1161 let amount = DenominatedAmount {
1162 amount: Amount::from_uint(1120, 0).expect("Test failed"),
1163 denom: 5u8.into(),
1164 };
1165 assert_eq!("0.0112", amount.to_string());
1166 assert_eq!("0.01120", amount.to_string_precise());
1167
1168 let amount = DenominatedAmount {
1169 amount: Amount::from_uint(200, 0).expect("Test failed"),
1170 denom: 0.into(),
1171 };
1172 assert_eq!("200", amount.to_string());
1173 assert_eq!("200", amount.to_string_precise());
1174 }
1175
1176 #[test]
1177 fn test_amount_checked_sub() {
1178 let max = Amount::native_whole(u64::MAX);
1179 let one = Amount::native_whole(1);
1180 let zero = Amount::native_whole(0);
1181
1182 assert_eq!(zero.checked_sub(zero), Some(zero));
1183 assert_eq!(zero.checked_sub(one), None);
1184 assert_eq!(zero.checked_sub(max), None);
1185
1186 assert_eq!(max.checked_sub(zero), Some(max));
1187 assert_eq!(max.checked_sub(one), Some(max - one));
1188 assert_eq!(max.checked_sub(max), Some(zero));
1189 }
1190
1191 #[test]
1192 fn test_serialization_round_trip() {
1193 let amount: Amount = serde_json::from_str(r#""1000000000""#).unwrap();
1194 assert_eq!(
1195 amount,
1196 Amount {
1197 raw: Uint::from(1000000000)
1198 }
1199 );
1200 let serialized = serde_json::to_string(&amount).unwrap();
1201 assert_eq!(serialized, r#""1000000000""#);
1202 }
1203
1204 #[test]
1205 fn test_amount_checked_add() {
1206 let max = Amount::max();
1207 let max_signed = Amount::max_signed();
1208 let one = Amount::native_whole(1);
1209 let zero = Amount::native_whole(0);
1210
1211 assert_eq!(zero.checked_add(zero), Some(zero));
1212 assert_eq!(zero.checked_signed_add(zero), Some(zero));
1213 assert_eq!(zero.checked_add(one), Some(one));
1214 assert_eq!(zero.checked_add(max - one), Some(max - one));
1215 assert_eq!(
1216 zero.checked_signed_add(max_signed - one),
1217 Some(max_signed - one)
1218 );
1219 assert_eq!(zero.checked_add(max), Some(max));
1220 assert_eq!(zero.checked_signed_add(max_signed), Some(max_signed));
1221
1222 assert_eq!(max.checked_add(zero), Some(max));
1223 assert_eq!(max.checked_signed_add(zero), None);
1224 assert_eq!(max.checked_add(one), None);
1225 assert_eq!(max.checked_add(max), None);
1226
1227 assert_eq!(max_signed.checked_add(zero), Some(max_signed));
1228 assert_eq!(max_signed.checked_add(one), Some(max_signed + one));
1229 assert_eq!(max_signed.checked_signed_add(max_signed), None);
1230 }
1231
1232 #[test]
1233 fn test_amount_from_string() {
1234 assert!(Amount::from_str("1.12", 1).is_err());
1235 assert!(Amount::from_str("0.0", 0).is_err());
1236 assert!(Amount::from_str("1.12", 80).is_err());
1237 assert!(Amount::from_str("1.12.1", 3).is_err());
1238 assert!(Amount::from_str("1.1a", 3).is_err());
1239 assert_eq!(
1240 Amount::zero(),
1241 Amount::from_str("0.0", 1).expect("Test failed")
1242 );
1243 assert_eq!(
1244 Amount::zero(),
1245 Amount::from_str(".0", 1).expect("Test failed")
1246 );
1247
1248 let amount = Amount::from_str("1.12", 3).expect("Test failed");
1249 assert_eq!(amount, Amount::from_uint(1120, 0).expect("Test failed"));
1250 let amount = Amount::from_str(".34", 3).expect("Test failed");
1251 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1252 let amount = Amount::from_str("0.34", 3).expect("Test failed");
1253 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1254 let amount = Amount::from_str("34", 1).expect("Test failed");
1255 assert_eq!(amount, Amount::from_uint(340, 0).expect("Test failed"));
1256 }
1257
1258 #[test]
1259 fn test_from_masp_denominated() {
1260 let uint = Uint([15u64, 16, 17, 18]);
1261 let original = Amount::from_uint(uint, 0).expect("Test failed");
1262 for denom in MaspDigitPos::iter() {
1263 let word = denom.denominate(&original);
1264 assert_eq!(word, denom as u64 + 15u64);
1265 let amount = Amount::from_masp_denominated(word, denom);
1266 let raw = Uint::from(amount).0;
1267 let mut expected = [0u64; 4];
1268 expected[denom as usize] = word;
1269 assert_eq!(raw, expected);
1270 }
1271 }
1272
1273 #[test]
1274 fn test_key_seg() {
1275 let original = Amount::from_uint(1234560000, 0).expect("Test failed");
1276 let key = original.raw();
1277 let amount = Amount::parse(key).expect("Test failed");
1278 assert_eq!(amount, original);
1279 }
1280
1281 #[test]
1282 fn test_amount_is_zero() {
1283 let zero = Amount::zero();
1284 assert!(zero.is_zero());
1285
1286 let non_zero = Amount::from_uint(1, 0).expect("Test failed");
1287 assert!(!non_zero.is_zero());
1288 }
1289
1290 #[test]
1291 fn test_token_amount_mul_ceil() {
1292 let one = Amount::from(1);
1293 let two = Amount::from(2);
1294 let three = Amount::from(3);
1295 let dec = Dec::from_str("0.34").unwrap();
1296 assert_eq!(one.mul_ceil(dec).unwrap(), one);
1297 assert_eq!(two.mul_ceil(dec).unwrap(), one);
1298 assert_eq!(three.mul_ceil(dec).unwrap(), two);
1299
1300 assert_matches!(one.mul_ceil(-dec), Err(_));
1301 assert_matches!(one.mul_ceil(-Dec::new(1, 12).unwrap()), Err(_));
1302 assert_matches!(
1303 Amount::native_whole(1).mul_ceil(-Dec::new(1, 12).unwrap()),
1304 Err(_)
1305 );
1306 }
1307
1308 #[test]
1309 fn test_token_amount_mul_floor() {
1310 let zero = Amount::zero();
1311 let one = Amount::from(1);
1312 let two = Amount::from(2);
1313 let three = Amount::from(3);
1314 let dec = Dec::from_str("0.34").unwrap();
1315 assert_eq!(one.mul_floor(dec).unwrap(), zero);
1316 assert_eq!(two.mul_floor(dec).unwrap(), zero);
1317 assert_eq!(three.mul_floor(dec).unwrap(), one);
1318
1319 assert_matches!(one.mul_floor(-dec), Err(_));
1320 assert_matches!(one.mul_floor(-Dec::new(1, 12).unwrap()), Err(_));
1321 assert_matches!(
1322 Amount::native_whole(1).mul_floor(-Dec::new(1, 12).unwrap()),
1323 Err(_)
1324 );
1325 }
1326
1327 #[test]
1328 fn test_denominateed_arithmetic() {
1329 let a = DenominatedAmount::new(10.into(), 3.into());
1330 let b = DenominatedAmount::new(10.into(), 2.into());
1331 let c = DenominatedAmount::new(110.into(), 3.into());
1332 let d = DenominatedAmount::new(90.into(), 3.into());
1333 let e = DenominatedAmount::new(100.into(), 5.into());
1334 let f = DenominatedAmount::new(100.into(), 3.into());
1335 let g = DenominatedAmount::new(0.into(), 3.into());
1336 assert_eq!(a.checked_add(b).unwrap(), c);
1337 assert_eq!(b.checked_sub(a).unwrap(), d);
1338 assert_eq!(a.checked_mul(b).unwrap(), e);
1339 assert!(a.checked_sub(b).is_none());
1340 assert_eq!(c.checked_sub(a).unwrap(), f);
1341 assert_eq!(c.checked_sub(c).unwrap(), g);
1342 }
1343
1344 #[test]
1345 fn test_denominated_amt_ord() {
1346 let denom_1 = DenominatedAmount {
1347 amount: Amount::from_uint(15, 0).expect("Test failed"),
1348 denom: 1.into(),
1349 };
1350 let denom_2 = DenominatedAmount {
1351 amount: Amount::from_uint(1500, 0).expect("Test failed"),
1352 denom: 3.into(),
1353 };
1354 assert_eq!(
1357 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1358 Ordering::Equal
1359 );
1360 assert_eq!(
1361 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1362 Ordering::Equal
1363 );
1364 assert_ne!(denom_1, denom_2);
1365
1366 let denom_1 = DenominatedAmount {
1367 amount: Amount::from_uint(15, 0).expect("Test failed"),
1368 denom: 1.into(),
1369 };
1370 let denom_2 = DenominatedAmount {
1371 amount: Amount::from_uint(1501, 0).expect("Test failed"),
1372 denom: 3.into(),
1373 };
1374 assert_eq!(
1375 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1376 Ordering::Less
1377 );
1378 assert_eq!(
1379 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1380 Ordering::Greater
1381 );
1382 let denom_1 = DenominatedAmount {
1383 amount: Amount::from_uint(15, 0).expect("Test failed"),
1384 denom: 1.into(),
1385 };
1386 let denom_2 = DenominatedAmount {
1387 amount: Amount::from_uint(1499, 0).expect("Test failed"),
1388 denom: 3.into(),
1389 };
1390 assert_eq!(
1391 denom_1.partial_cmp(&denom_2).expect("Test failed"),
1392 Ordering::Greater
1393 );
1394 assert_eq!(
1395 denom_2.partial_cmp(&denom_1).expect("Test failed"),
1396 Ordering::Less
1397 );
1398 }
1399
1400 #[test]
1401 fn test_token_amount_from_u128() {
1402 for val in [
1403 u128::MIN,
1404 u128::MIN + 1,
1405 u128::from(u64::MAX) - 1,
1406 u128::from(u64::MAX),
1407 u128::from(u64::MAX) + 1,
1408 u128::MAX - 1,
1409 u128::MAX,
1410 ] {
1411 let raw = Uint::from(val);
1412 let amount = Amount::from_u128(val);
1413 assert_eq!(raw, amount.raw);
1414 assert_eq!(amount.raw.as_u128(), val);
1415 }
1416 }
1417}