1use std::cmp::Ordering;
2use std::fmt;
3use std::iter::Sum;
4use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6use num_bigint::BigInt;
7use num_integer::Integer;
8use num_traits::{Signed, Zero};
9use serde::{Deserialize, Serialize, Serializer};
10
11use crate::bigint::bigint_ser;
12
13#[derive(Clone, PartialEq, Eq, Hash)]
19pub struct TokenAmount {
20 atto: BigInt,
21}
22
23impl TokenAmount {
26 pub const DECIMALS: usize = 18;
28
29 pub const PRECISION: u64 = 10u64.pow(Self::DECIMALS as u32);
31
32 pub fn from_atto(atto: impl Into<BigInt>) -> Self {
34 Self { atto: atto.into() }
35 }
36
37 pub fn from_nano(nano: impl Into<BigInt>) -> Self {
39 const NANO_PRECISION: u64 = 10u64.pow((TokenAmount::DECIMALS as u32) - 9);
40 Self {
41 atto: nano.into() * NANO_PRECISION,
42 }
43 }
44
45 pub fn from_whole(tokens: impl Into<BigInt>) -> Self {
47 Self::from_atto(tokens.into() * Self::PRECISION)
48 }
49
50 pub fn atto(&self) -> &BigInt {
52 &self.atto
53 }
54
55 pub fn is_zero(&self) -> bool {
56 self.atto.is_zero()
57 }
58
59 pub fn is_positive(&self) -> bool {
60 self.atto.is_positive()
61 }
62
63 pub fn is_negative(&self) -> bool {
64 self.atto.is_negative()
65 }
66}
67
68impl Zero for TokenAmount {
69 #[inline]
70 fn zero() -> Self {
71 Self {
72 atto: BigInt::zero(),
73 }
74 }
75
76 #[inline]
77 fn is_zero(&self) -> bool {
78 self.atto.is_zero()
79 }
80}
81
82impl PartialOrd for TokenAmount {
83 #[inline]
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
85 Some(self.atto.cmp(&other.atto))
86 }
87}
88
89impl Ord for TokenAmount {
90 #[inline]
91 fn cmp(&self, other: &Self) -> Ordering {
92 self.atto.cmp(&other.atto)
93 }
94}
95
96impl Default for TokenAmount {
97 #[inline]
98 fn default() -> TokenAmount {
99 TokenAmount::zero()
100 }
101}
102
103impl fmt::Debug for TokenAmount {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write!(f, "TokenAmount({})", self)
106 }
107}
108
109impl fmt::Display for TokenAmount {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 let (q, r) = self.atto.div_rem(&BigInt::from(Self::PRECISION));
116 let before_decimal = q.abs().to_str_radix(10);
117 let after_decimal = if r.is_zero() {
118 "0".to_string()
119 } else {
120 let fraction_str = r.abs().to_str_radix(10);
121 let render = "0".repeat(Self::DECIMALS - fraction_str.len()) + fraction_str.as_str();
122 render.trim_end_matches('0').to_string()
123 };
124
125 let after_decimal = if let Some(precision) = f.precision() {
127 let len = after_decimal.len();
128 if len < precision {
129 after_decimal + "0".repeat(precision - len).as_str()
130 } else {
131 after_decimal[0..precision].to_string()
132 }
133 } else {
134 after_decimal
135 };
136
137 let complete_without_sign = before_decimal + "." + after_decimal.as_str();
139 f.pad_integral(!self.atto().is_negative(), "", &complete_without_sign)
141 }
142}
143
144impl Neg for TokenAmount {
145 type Output = TokenAmount;
146
147 #[inline]
148 fn neg(self) -> TokenAmount {
149 TokenAmount { atto: -self.atto }
150 }
151}
152
153impl Neg for &TokenAmount {
154 type Output = TokenAmount;
155
156 #[inline]
157 fn neg(self) -> TokenAmount {
158 TokenAmount {
159 atto: (&self.atto).neg(),
160 }
161 }
162}
163
164macro_rules! impl_add {
167 ($(impl<$($a:lifetime),*> Add<$Other:ty> for $Self:ty;)*) => {$(
168 impl<$($a),*> Add<$Other> for $Self {
169 type Output = TokenAmount;
170
171 #[inline]
172 fn add(self, other: $Other) -> TokenAmount {
173 let TokenAmount { atto: x, .. } = self;
175 let TokenAmount { atto: y, .. } = other;
176 TokenAmount {atto: x + y}
177 }
178 }
179 )*}
180}
181impl_add! {
182 impl<> Add<TokenAmount> for TokenAmount;
183 impl<'b> Add<&'b TokenAmount> for TokenAmount;
184 impl<'a> Add<TokenAmount> for &'a TokenAmount;
185 impl<'a, 'b> Add<&'b TokenAmount> for &'a TokenAmount;
186}
187
188impl AddAssign<TokenAmount> for TokenAmount {
189 #[inline]
190 fn add_assign(&mut self, other: TokenAmount) {
191 self.atto += &other.atto;
192 }
193}
194
195impl AddAssign<&TokenAmount> for TokenAmount {
196 #[inline]
197 fn add_assign(&mut self, other: &TokenAmount) {
198 self.atto += &other.atto;
199 }
200}
201
202macro_rules! impl_sub {
204 ($(impl<$($a:lifetime),*> Sub<$Other:ty> for $Self:ty;)*) => {$(
205 impl<$($a),*> Sub<$Other> for $Self {
206 type Output = TokenAmount;
207
208 #[inline]
209 fn sub(self, other: $Other) -> TokenAmount {
210 let TokenAmount { atto: x, .. } = self;
212 let TokenAmount { atto: y, .. } = other;
213 TokenAmount {atto: x - y}
214 }
215 }
216 )*}
217}
218impl_sub! {
219 impl<> Sub<TokenAmount> for TokenAmount;
220 impl<'b> Sub<&'b TokenAmount> for TokenAmount;
221 impl<'a> Sub<TokenAmount> for &'a TokenAmount;
222 impl<'a, 'b> Sub<&'b TokenAmount> for &'a TokenAmount;
223}
224
225impl SubAssign<TokenAmount> for TokenAmount {
226 #[inline]
227 fn sub_assign(&mut self, other: TokenAmount) {
228 self.atto -= &other.atto;
229 }
230}
231
232impl SubAssign<&TokenAmount> for TokenAmount {
233 #[inline]
234 fn sub_assign(&mut self, other: &TokenAmount) {
235 self.atto -= &other.atto;
236 }
237}
238
239impl<T> Mul<T> for TokenAmount
240where
241 BigInt: Mul<T, Output = BigInt>,
242{
243 type Output = TokenAmount;
244
245 fn mul(self, rhs: T) -> Self::Output {
246 TokenAmount {
247 atto: self.atto * rhs,
248 }
249 }
250}
251
252impl<'a, T> Mul<T> for &'a TokenAmount
253where
254 &'a BigInt: Mul<T, Output = BigInt>,
255{
256 type Output = TokenAmount;
257
258 fn mul(self, rhs: T) -> Self::Output {
259 TokenAmount {
260 atto: &self.atto * rhs,
261 }
262 }
263}
264
265macro_rules! impl_mul {
266 ($(impl<$($a:lifetime),*> Mul<$Other:ty> for $Self:ty;)*) => {$(
267 impl<$($a),*> Mul<$Other> for $Self {
268 type Output = TokenAmount;
269
270 #[inline]
271 fn mul(self, other: $Other) -> TokenAmount {
272 other * self
273 }
274 }
275 )*}
276}
277
278macro_rules! impl_muls {
279 ($($t:ty,)*) => {$(
280 impl_mul! {
281 impl<> Mul<TokenAmount> for $t;
282 impl<'b> Mul<&'b TokenAmount> for $t;
283 impl<'a> Mul<TokenAmount> for &'a $t;
284 impl<'a, 'b> Mul<&'b TokenAmount> for &'a $t;
285 }
286 )*};
287}
288
289impl_muls! {
290 u8, u16, u32, u64, u128,
291 i8, i16, i32, i64, i128,
292 BigInt,
293}
294
295impl<T> MulAssign<T> for TokenAmount
296where
297 BigInt: MulAssign<T>,
298{
299 #[inline]
300 fn mul_assign(&mut self, other: T) {
301 self.atto *= other;
302 }
303}
304
305impl TokenAmount {
308 #[inline]
309 pub fn div_rem(&self, other: impl Into<BigInt>) -> (TokenAmount, TokenAmount) {
310 let (q, r) = self.atto.div_rem(&other.into());
311 (TokenAmount { atto: q }, TokenAmount { atto: r })
312 }
313
314 #[inline]
315 pub fn div_ceil(&self, other: impl Into<BigInt>) -> TokenAmount {
316 TokenAmount {
317 atto: self.atto.div_ceil(&other.into()),
318 }
319 }
320
321 #[inline]
322 pub fn div_floor(&self, other: impl Into<BigInt>) -> TokenAmount {
323 TokenAmount {
324 atto: self.atto.div_floor(&other.into()),
325 }
326 }
327}
328
329impl Sum for TokenAmount {
330 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
331 Self::from_atto(iter.map(|t| t.atto).sum::<BigInt>())
332 }
333}
334
335impl<'a> Sum<&'a TokenAmount> for TokenAmount {
336 fn sum<I: Iterator<Item = &'a TokenAmount>>(iter: I) -> Self {
337 Self::from_atto(iter.map(|t| &t.atto).sum::<BigInt>())
338 }
339}
340
341impl Serialize for TokenAmount {
344 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
345 where
346 S: Serializer,
347 {
348 bigint_ser::serialize(&self.atto, serializer)
349 }
350}
351
352impl<'de> Deserialize<'de> for TokenAmount {
353 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
354 where
355 D: serde::Deserializer<'de>,
356 {
357 bigint_ser::deserialize(deserializer).map(|v| TokenAmount { atto: v })
358 }
359}
360
361#[cfg(test)]
362mod test {
363 use num_bigint::BigInt;
364 use num_traits::Zero;
365
366 use crate::TokenAmount;
367
368 fn whole(x: impl Into<BigInt>) -> TokenAmount {
369 TokenAmount::from_whole(x)
370 }
371
372 fn atto(x: impl Into<BigInt>) -> TokenAmount {
373 TokenAmount::from_atto(x.into())
374 }
375
376 #[test]
377 fn display_basic() {
378 fn basic(expected: &str, t: TokenAmount) {
379 assert_eq!(expected, format!("{}", t));
380 }
381
382 basic("0.0", TokenAmount::zero());
383 basic("0.000000000000000001", atto(1));
384 basic("0.000000000000001", atto(1000));
385 basic("0.1234", atto(123_400_000_000_000_000_u64));
386 basic("0.10101", atto(101_010_000_000_000_000_u64));
387 basic("1.0", whole(1));
388 basic("1.0", atto(1_000_000_000_000_000_000_u128));
389 basic("1.1", atto(1_100_000_000_000_000_000_u128));
390 basic("1.000000000000000001", atto(1_000_000_000_000_000_001_u128));
391 basic(
392 "1234.000000000123456789",
393 whole(1234) + atto(123_456_789_u64),
394 );
395 }
396
397 #[test]
398 fn display_precision() {
399 assert_eq!("0.0", format!("{:.1}", TokenAmount::zero()));
400 assert_eq!("0.000", format!("{:.3}", TokenAmount::zero()));
401 assert_eq!("0.000", format!("{:.3}", atto(1))); assert_eq!(
403 "0.123",
404 format!("{:.3}", atto(123_456_789_000_000_000_u64)) );
406 assert_eq!(
407 "0.123456789000",
408 format!("{:.12}", atto(123_456_789_000_000_000_u64))
409 );
410 }
411
412 #[test]
413 fn display_padding() {
414 assert_eq!("0.0", format!("{:01}", TokenAmount::zero()));
415 assert_eq!("0.0", format!("{:03}", TokenAmount::zero()));
416 assert_eq!("000.0", format!("{:05}", TokenAmount::zero()));
417 assert_eq!(
418 "0.123",
419 format!("{:01.3}", atto(123_456_789_000_000_000_u64))
420 );
421 assert_eq!(
422 "00.123",
423 format!("{:06.3}", atto(123_456_789_000_000_000_u64))
424 );
425 }
426
427 #[test]
428 fn display_negative() {
429 assert_eq!("-0.000001", format!("{:01}", -TokenAmount::from_nano(1000)));
430 }
431
432 #[test]
433 fn ops() {
434 assert_eq!(atto(15), atto(10) + atto(5));
436 assert_eq!(atto(3), atto(10) - atto(7));
437 assert_eq!(atto(12), atto(3) * 4);
438 let (q, r) = atto(14).div_rem(4);
439 assert_eq!((atto(3), atto(2)), (q, r));
440
441 let mut a = atto(1);
442 a += atto(2);
443 assert_eq!(atto(3), a);
444 a *= 2;
445 assert_eq!(atto(6), a);
446 a -= atto(2);
447 assert_eq!(atto(4), a);
448 }
449
450 #[test]
451 fn nano_fil() {
452 assert_eq!(
453 TokenAmount::from_nano(1),
454 TokenAmount::from_whole(1).div_floor(10u64.pow(9))
455 )
456 }
457
458 #[test]
459 fn test_mul() {
460 let a = atto(2) * 3;
461 let b = 3 * atto(2);
462 assert_eq!(a, atto(6));
463 assert_eq!(a, b);
464 }
465
466 #[test]
467 fn test_sum() {
468 assert_eq!(
469 [1, 2, 3, 4].into_iter().map(atto).sum::<TokenAmount>(),
470 atto(10)
471 );
472 }
473}