near_token/
lib.rs

1//! A `NearToken` type to represent a value of Near.
2//!
3//! Each `Neartokens` is composed of a floating point number of tokens where each integer unit is equal to one yocto-Near.
4//! `NearToken` is implementing the common trait `FromStr`. Also, have utils function to parse from `str` into `u128`.
5//!
6//! # Examples
7//! ```
8//! use near_token::NearToken;
9//!
10//! let one_near = NearToken::from_yoctonear(10_u128.pow(24));
11//! assert_eq!(one_near, NearToken::from_near(1));
12//! assert_eq!(one_near, NearToken::from_millinear(1000));
13//! ```
14//!
15//! # Crate features
16//!
17//! * **borsh** (optional) -
18//!   When enabled allows `NearToken` to serialized and deserialized by `borsh`.
19//!
20//! * **serde** (optional) -
21//!   When enabled allows `NearToken` to serialized and deserialized by `serde`.
22//!
23//! * **schemars** (optional) -
24//!   Implements `schemars::JsonSchema` for `NearToken`.
25//!
26//! * **interactive-clap** (optional) -
27//!   Implements `interactive_clap::ToCli` for `NearToken`.
28mod error;
29
30mod utils;
31
32mod trait_impls;
33
34pub use self::error::NearTokenError;
35pub use self::utils::DecimalNumberParsingError;
36
37#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
38#[cfg_attr(
39    feature = "borsh",
40    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
41)]
42#[cfg_attr(feature = "abi", derive(borsh::BorshSchema))]
43#[repr(transparent)]
44pub struct NearToken {
45    inner: u128,
46}
47
48const ONE_NEAR: u128 = 10_u128.pow(24);
49const ONE_MILLINEAR: u128 = 10_u128.pow(21);
50
51impl NearToken {
52    /// Maximum value for NearToken (u128::MAX)
53    pub const MAX: NearToken = NearToken::from_yoctonear(u128::MAX);
54    /// Zero value for NearToken
55    pub const ZERO: NearToken = NearToken::from_yoctonear(0);
56
57    /// `from_yoctonear` is a function that takes value by a number of yocto-near.
58    /// # Examples
59    /// ```
60    /// use near_token::NearToken;
61    /// assert_eq!( NearToken::from_yoctonear(10u128.pow(21)), NearToken::from_millinear(1))
62    /// ```
63    pub const fn from_yoctonear(inner: u128) -> Self {
64        Self { inner }
65    }
66
67    /// `from_millinear` is a function that takes value by a number of mili-near and converts it to an equivalent to the yocto-near.
68    /// # Examples
69    /// ```
70    /// use near_token::NearToken;
71    /// assert_eq!(NearToken::from_millinear(1), NearToken::from_yoctonear(10u128.pow(21)))
72    /// ```
73    pub const fn from_millinear(inner: u128) -> Self {
74        Self {
75            inner: inner * ONE_MILLINEAR,
76        }
77    }
78
79    /// `from_near` is a function that takes value by a number of near and converts it to an equivalent to the yocto-near.
80    /// # Examples
81    /// ```
82    /// use near_token::NearToken;
83    /// assert_eq!(NearToken::from_near(1), NearToken::from_yoctonear(10u128.pow(24)))
84    /// ```
85    pub const fn from_near(inner: u128) -> Self {
86        Self {
87            inner: inner * ONE_NEAR,
88        }
89    }
90
91    /// `as_near` is a function that converts number of yocto-near to an equivalent to the near.
92    /// # Examples
93    /// ```
94    /// use near_token::NearToken;
95    /// assert_eq!(NearToken::from_yoctonear(10u128.pow(24)).as_near(), 1)
96    /// ```
97    pub const fn as_near(&self) -> u128 {
98        self.inner / ONE_NEAR
99    }
100
101    /// `as_millinear` is a function that converts number of yocto-near to an equivalent to the mili-near.
102    /// # Examples
103    /// ```
104    /// use near_token::NearToken;
105    /// assert_eq!(NearToken::from_yoctonear(10u128.pow(21)).as_millinear(), 1)
106    /// ```
107    pub const fn as_millinear(&self) -> u128 {
108        self.inner / ONE_MILLINEAR
109    }
110
111    /// `as_yoctonear` is a function that shows a number of yocto-near.
112    /// # Examples
113    /// ```
114    /// use near_token::NearToken;
115    /// assert_eq!(NearToken::from_yoctonear(10).as_yoctonear(), 10)
116    /// ```
117    pub const fn as_yoctonear(&self) -> u128 {
118        self.inner
119    }
120
121    /// `is_zero` is a boolian function that checks `NearToken`
122    /// if a `NearToken` inner is zero, returns true.
123    /// # Examples
124    /// ```
125    /// use near_token::NearToken;
126    /// assert_eq!(NearToken::from_yoctonear(0).is_zero(), true)
127    /// ```
128    pub const fn is_zero(&self) -> bool {
129        self.inner == 0
130    }
131
132    /// Checked integer addition. Computes self + rhs, returning None if overflow occurred.
133    ///
134    /// # Examples
135    /// ```
136    /// use near_token::NearToken;
137    /// use std::u128;
138    /// assert_eq!(NearToken::from_yoctonear(u128::MAX -2).checked_add(NearToken::from_yoctonear(2)), Some(NearToken::from_yoctonear(u128::MAX)));
139    /// assert_eq!(NearToken::from_yoctonear(u128::MAX -2).checked_add(NearToken::from_yoctonear(3)), None);
140    /// ```
141    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
142        if let Some(near) = self.as_yoctonear().checked_add(rhs.as_yoctonear()) {
143            Some(Self::from_yoctonear(near))
144        } else {
145            None
146        }
147    }
148
149    /// Checked integer subtraction. Computes self - rhs, returning None if overflow occurred.
150    ///
151    /// # Examples
152    /// ```
153    /// use near_token::NearToken;
154    /// assert_eq!(NearToken::from_yoctonear(2).checked_sub(NearToken::from_yoctonear(2)), Some(NearToken::from_yoctonear(0)));
155    /// assert_eq!(NearToken::from_yoctonear(2).checked_sub(NearToken::from_yoctonear(3)), None);
156    /// ```
157    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
158        if let Some(near) = self.as_yoctonear().checked_sub(rhs.as_yoctonear()) {
159            Some(Self::from_yoctonear(near))
160        } else {
161            None
162        }
163    }
164
165    /// Checked integer multiplication. Computes self * rhs, returning None if overflow occurred.
166    ///
167    /// # Examples
168    /// ```
169    /// use near_token::NearToken;
170    /// use std::u128;
171    /// assert_eq!(NearToken::from_yoctonear(2).checked_mul(2), Some(NearToken::from_yoctonear(4)));
172    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).checked_mul(2), None)
173    pub const fn checked_mul(self, rhs: u128) -> Option<Self> {
174        if let Some(near) = self.as_yoctonear().checked_mul(rhs) {
175            Some(Self::from_yoctonear(near))
176        } else {
177            None
178        }
179    }
180
181    /// Checked integer division. Computes self / rhs, returning None if rhs == 0.
182    ///
183    /// # Examples
184    /// ```
185    /// use near_token::NearToken;
186    /// assert_eq!(NearToken::from_yoctonear(10).checked_div(2), Some(NearToken::from_yoctonear(5)));
187    /// assert_eq!(NearToken::from_yoctonear(2).checked_div(0), None);
188    /// ```
189    pub const fn checked_div(self, rhs: u128) -> Option<Self> {
190        if let Some(near) = self.as_yoctonear().checked_div(rhs) {
191            Some(Self::from_yoctonear(near))
192        } else {
193            None
194        }
195    }
196
197    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead of overflowing.
198    ///
199    /// # Examples
200    /// ```
201    /// use near_token::NearToken;
202    /// assert_eq!(NearToken::from_yoctonear(5).saturating_add(NearToken::from_yoctonear(5)), NearToken::from_yoctonear(10));
203    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).saturating_add(NearToken::from_yoctonear(1)), NearToken::from_yoctonear(u128::MAX));
204    /// ```
205    pub const fn saturating_add(self, rhs: Self) -> Self {
206        NearToken::from_yoctonear(self.as_yoctonear().saturating_add(rhs.as_yoctonear()))
207    }
208
209    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds instead of overflowing.
210    ///
211    /// # Examples
212    /// ```
213    /// use near_token::NearToken;
214    /// assert_eq!(NearToken::from_yoctonear(5).saturating_sub(NearToken::from_yoctonear(2)), NearToken::from_yoctonear(3));
215    /// assert_eq!(NearToken::from_yoctonear(1).saturating_sub(NearToken::from_yoctonear(2)), NearToken::from_yoctonear(0));
216    /// ```
217    pub const fn saturating_sub(self, rhs: Self) -> Self {
218        NearToken::from_yoctonear(self.as_yoctonear().saturating_sub(rhs.as_yoctonear()))
219    }
220
221    /// Saturating integer multiplication. Computes self * rhs, saturating at the numeric bounds instead of overflowing.
222    ///
223    /// # Examples
224    /// ```
225    /// use near_token::NearToken;
226    /// use std::u128;
227    /// assert_eq!(NearToken::from_yoctonear(2).saturating_mul(5), NearToken::from_yoctonear(10));
228    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).saturating_mul(2), NearToken::from_yoctonear(u128::MAX));
229    /// ```
230    pub const fn saturating_mul(self, rhs: u128) -> Self {
231        NearToken::from_yoctonear(self.as_yoctonear().saturating_mul(rhs))
232    }
233
234    /// Saturating integer division. Computes self / rhs, saturating at the numeric bounds instead of overflowing.
235    ///
236    /// # Examples
237    /// ```
238    /// use near_token::NearToken;
239    /// assert_eq!(NearToken::from_yoctonear(10).saturating_div(2), NearToken::from_yoctonear(5));
240    /// assert_eq!(NearToken::from_yoctonear(10).saturating_div(0), NearToken::from_yoctonear(0))
241    /// ```
242    pub const fn saturating_div(self, rhs: u128) -> Self {
243        if rhs == 0 {
244            return NearToken::from_yoctonear(0);
245        }
246        NearToken::from_yoctonear(self.as_yoctonear().saturating_div(rhs))
247    }
248
249    /// Formats the `NearToken` and displays the amount in NEAR or yoctoNEAR depending on the value.
250    ///
251    /// # Examples
252    /// ```
253    /// use near_token::NearToken;
254    /// assert_eq!(NearToken::from_yoctonear(10_u128.pow(24)).exact_amount_display(), "1 NEAR");
255    /// assert_eq!(NearToken::from_yoctonear(15 * 10_u128.pow(23)).exact_amount_display(), "1.5 NEAR");
256    /// assert_eq!(NearToken::from_yoctonear(500).exact_amount_display(), "500 yoctoNEAR");
257    /// assert_eq!(NearToken::from_yoctonear(0).exact_amount_display(), "0 NEAR");
258    /// ```
259    pub fn exact_amount_display(&self) -> String {
260        let yoctonear = self.as_yoctonear();
261
262        if yoctonear == 0 {
263            "0 NEAR".to_string()
264        } else if yoctonear <= 1_000 {
265            format!("{} yoctoNEAR", yoctonear)
266        } else if yoctonear % ONE_NEAR == 0 {
267            format!("{} NEAR", yoctonear / ONE_NEAR)
268        } else {
269            format!(
270                "{}.{} NEAR",
271                yoctonear / ONE_NEAR,
272                format!("{:0>24}", yoctonear % ONE_NEAR).trim_end_matches('0')
273            )
274        }
275    }
276}
277
278#[cfg(test)]
279mod test {
280    use crate::NearToken;
281
282    #[test]
283    fn checked_add_tokens() {
284        let tokens = NearToken::from_yoctonear(u128::MAX - 3);
285        let any_tokens = NearToken::from_yoctonear(3);
286        let more_tokens = NearToken::from_yoctonear(4);
287        assert_eq!(
288            tokens.checked_add(any_tokens),
289            Some(NearToken::from_yoctonear(u128::MAX))
290        );
291        assert_eq!(tokens.checked_add(more_tokens), None);
292    }
293
294    #[test]
295    fn checked_sub_tokens() {
296        let tokens = NearToken::from_yoctonear(3);
297        let any_tokens = NearToken::from_yoctonear(1);
298        let more_tokens = NearToken::from_yoctonear(4);
299        assert_eq!(
300            tokens.checked_sub(any_tokens),
301            Some(NearToken::from_yoctonear(2))
302        );
303        assert_eq!(tokens.checked_sub(more_tokens), None);
304    }
305
306    #[test]
307    fn checked_mul_tokens() {
308        let tokens = NearToken::from_yoctonear(u128::MAX / 10);
309        assert_eq!(
310            tokens.checked_mul(10),
311            Some(NearToken::from_yoctonear(u128::MAX / 10 * 10))
312        );
313        assert_eq!(tokens.checked_mul(11), None);
314    }
315
316    #[test]
317    fn checked_div_tokens() {
318        let tokens = NearToken::from_yoctonear(10);
319        assert_eq!(tokens.checked_div(2), Some(NearToken::from_yoctonear(5)));
320        assert_eq!(tokens.checked_div(11), Some(NearToken::from_yoctonear(0)));
321        assert_eq!(tokens.checked_div(0), None);
322    }
323
324    #[test]
325    fn saturating_add_tokens() {
326        let tokens = NearToken::from_yoctonear(100);
327        let added_tokens = NearToken::from_yoctonear(1);
328        let another_tokens = NearToken::from_yoctonear(u128::MAX);
329        assert_eq!(
330            tokens.saturating_add(added_tokens),
331            NearToken::from_yoctonear(101)
332        );
333        assert_eq!(
334            another_tokens.saturating_add(added_tokens),
335            NearToken::from_yoctonear(u128::MAX)
336        );
337    }
338
339    #[test]
340    fn saturating_sub_tokens() {
341        let tokens = NearToken::from_yoctonear(100);
342        let rhs_tokens = NearToken::from_yoctonear(1);
343        let another_tokens = NearToken::from_yoctonear(u128::MIN);
344        assert_eq!(
345            tokens.saturating_sub(rhs_tokens),
346            NearToken::from_yoctonear(99)
347        );
348        assert_eq!(
349            another_tokens.saturating_sub(rhs_tokens),
350            NearToken::from_yoctonear(u128::MIN)
351        );
352    }
353
354    #[test]
355    fn saturating_mul_tokens() {
356        let tokens = NearToken::from_yoctonear(2);
357        let rhs = 10;
358        let another_tokens = u128::MAX;
359        assert_eq!(tokens.saturating_mul(rhs), NearToken::from_yoctonear(20));
360        assert_eq!(
361            tokens.saturating_mul(another_tokens),
362            NearToken::from_yoctonear(u128::MAX)
363        );
364    }
365
366    #[test]
367    fn saturating_div_tokens() {
368        let tokens = NearToken::from_yoctonear(10);
369        let rhs = 2;
370        let another_tokens = 20;
371        assert_eq!(tokens.saturating_div(rhs), NearToken::from_yoctonear(5));
372        assert_eq!(
373            tokens.saturating_div(another_tokens),
374            NearToken::from_yoctonear(0)
375        );
376    }
377
378    #[test]
379    fn exact_amount_display_tokens() {
380        let token = NearToken::from_yoctonear(0);
381        assert_eq!(token.exact_amount_display(), "0 NEAR");
382
383        let token = NearToken::from_yoctonear(500);
384        assert_eq!(token.exact_amount_display(), "500 yoctoNEAR");
385
386        let token = NearToken::from_yoctonear(10_u128.pow(24));
387        assert_eq!(token.exact_amount_display(), "1 NEAR");
388
389        let token = NearToken::from_yoctonear(15 * 10_u128.pow(23));
390        assert_eq!(token.exact_amount_display(), "1.5 NEAR");
391
392        let token = NearToken::from_yoctonear(1_234_567_890_123_456_789_000_000);
393        assert_eq!(token.exact_amount_display(), "1.234567890123456789 NEAR");
394    }
395}