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);
50const ONE_MICRONEAR: u128 = 10_u128.pow(18);
51
52impl NearToken {
53    /// Maximum value for NearToken (u128::MAX)
54    pub const MAX: NearToken = NearToken::from_yoctonear(u128::MAX);
55    /// Zero value for NearToken
56    pub const ZERO: NearToken = NearToken::from_yoctonear(0);
57
58    /// `from_yoctonear` is a function that takes value by a number of yocto-near.
59    /// # Examples
60    /// ```
61    /// use near_token::NearToken;
62    /// assert_eq!( NearToken::from_yoctonear(10u128.pow(21)), NearToken::from_millinear(1))
63    /// ```
64    pub const fn from_yoctonear(inner: u128) -> Self {
65        Self { inner }
66    }
67
68    /// `from_micronear` is a function that takes value by a number of micro-near and converts it to an equivalent to the yocto-near.
69    /// # Examples
70    /// ```
71    /// use near_token::NearToken;
72    /// assert_eq!(NearToken::from_micronear(1), NearToken::from_yoctonear(10u128.pow(18)))
73    /// ```
74    pub const fn from_micronear(inner: u128) -> Self {
75        Self {
76            inner: inner * ONE_MICRONEAR,
77        }
78    }
79
80    /// `from_millinear` is a function that takes value by a number of milli-near and converts it to an equivalent to the yocto-near.
81    /// # Examples
82    /// ```
83    /// use near_token::NearToken;
84    /// assert_eq!(NearToken::from_millinear(1), NearToken::from_yoctonear(10u128.pow(21)))
85    /// ```
86    pub const fn from_millinear(inner: u128) -> Self {
87        Self {
88            inner: inner * ONE_MILLINEAR,
89        }
90    }
91
92    /// `from_near` is a function that takes value by a number of near and converts it to an equivalent to the yocto-near.
93    /// # Examples
94    /// ```
95    /// use near_token::NearToken;
96    /// assert_eq!(NearToken::from_near(1), NearToken::from_yoctonear(10u128.pow(24)))
97    /// ```
98    pub const fn from_near(inner: u128) -> Self {
99        Self {
100            inner: inner * ONE_NEAR,
101        }
102    }
103
104    /// `as_near` is a function that converts number of yocto-near to an equivalent to the near.
105    /// # Examples
106    /// ```
107    /// use near_token::NearToken;
108    /// assert_eq!(NearToken::from_yoctonear(10u128.pow(24)).as_near(), 1)
109    /// ```
110    pub const fn as_near(&self) -> u128 {
111        self.inner / ONE_NEAR
112    }
113
114    /// `as_micronear` is a function that converts number of yocto-near to an equivalent to the micro-near.
115    /// # Examples
116    /// ```
117    /// use near_token::NearToken;
118    /// assert_eq!(NearToken::from_yoctonear(10u128.pow(18)).as_micronear(), 1)
119    /// ```
120    pub const fn as_micronear(&self) -> u128 {
121        self.inner / ONE_MICRONEAR
122    }
123
124    /// `as_millinear` is a function that converts number of yocto-near to an equivalent to the milli-near.
125    /// # Examples
126    /// ```
127    /// use near_token::NearToken;
128    /// assert_eq!(NearToken::from_yoctonear(10u128.pow(21)).as_millinear(), 1)
129    /// ```
130    pub const fn as_millinear(&self) -> u128 {
131        self.inner / ONE_MILLINEAR
132    }
133
134    /// `as_yoctonear` is a function that shows a number of yocto-near.
135    /// # Examples
136    /// ```
137    /// use near_token::NearToken;
138    /// assert_eq!(NearToken::from_yoctonear(10).as_yoctonear(), 10)
139    /// ```
140    pub const fn as_yoctonear(&self) -> u128 {
141        self.inner
142    }
143
144    /// `is_zero` is a boolian function that checks `NearToken`
145    /// if a `NearToken` inner is zero, returns true.
146    /// # Examples
147    /// ```
148    /// use near_token::NearToken;
149    /// assert_eq!(NearToken::from_yoctonear(0).is_zero(), true)
150    /// ```
151    pub const fn is_zero(&self) -> bool {
152        self.inner == 0
153    }
154
155    /// Checked integer addition. Computes self + rhs, returning None if overflow occurred.
156    ///
157    /// # Examples
158    /// ```
159    /// use near_token::NearToken;
160    /// use std::u128;
161    /// assert_eq!(NearToken::from_yoctonear(u128::MAX -2).checked_add(NearToken::from_yoctonear(2)), Some(NearToken::from_yoctonear(u128::MAX)));
162    /// assert_eq!(NearToken::from_yoctonear(u128::MAX -2).checked_add(NearToken::from_yoctonear(3)), None);
163    /// ```
164    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
165        if let Some(near) = self.as_yoctonear().checked_add(rhs.as_yoctonear()) {
166            Some(Self::from_yoctonear(near))
167        } else {
168            None
169        }
170    }
171
172    /// Checked integer subtraction. Computes self - rhs, returning None if overflow occurred.
173    ///
174    /// # Examples
175    /// ```
176    /// use near_token::NearToken;
177    /// assert_eq!(NearToken::from_yoctonear(2).checked_sub(NearToken::from_yoctonear(2)), Some(NearToken::from_yoctonear(0)));
178    /// assert_eq!(NearToken::from_yoctonear(2).checked_sub(NearToken::from_yoctonear(3)), None);
179    /// ```
180    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
181        if let Some(near) = self.as_yoctonear().checked_sub(rhs.as_yoctonear()) {
182            Some(Self::from_yoctonear(near))
183        } else {
184            None
185        }
186    }
187
188    /// Checked integer multiplication. Computes self * rhs, returning None if overflow occurred.
189    ///
190    /// # Examples
191    /// ```
192    /// use near_token::NearToken;
193    /// use std::u128;
194    /// assert_eq!(NearToken::from_yoctonear(2).checked_mul(2), Some(NearToken::from_yoctonear(4)));
195    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).checked_mul(2), None)
196    pub const fn checked_mul(self, rhs: u128) -> Option<Self> {
197        if let Some(near) = self.as_yoctonear().checked_mul(rhs) {
198            Some(Self::from_yoctonear(near))
199        } else {
200            None
201        }
202    }
203
204    /// Checked integer division. Computes self / rhs, returning None if rhs == 0.
205    ///
206    /// # Examples
207    /// ```
208    /// use near_token::NearToken;
209    /// assert_eq!(NearToken::from_yoctonear(10).checked_div(2), Some(NearToken::from_yoctonear(5)));
210    /// assert_eq!(NearToken::from_yoctonear(2).checked_div(0), None);
211    /// ```
212    pub const fn checked_div(self, rhs: u128) -> Option<Self> {
213        if let Some(near) = self.as_yoctonear().checked_div(rhs) {
214            Some(Self::from_yoctonear(near))
215        } else {
216            None
217        }
218    }
219
220    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead of overflowing.
221    ///
222    /// # Examples
223    /// ```
224    /// use near_token::NearToken;
225    /// assert_eq!(NearToken::from_yoctonear(5).saturating_add(NearToken::from_yoctonear(5)), NearToken::from_yoctonear(10));
226    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).saturating_add(NearToken::from_yoctonear(1)), NearToken::from_yoctonear(u128::MAX));
227    /// ```
228    pub const fn saturating_add(self, rhs: Self) -> Self {
229        NearToken::from_yoctonear(self.as_yoctonear().saturating_add(rhs.as_yoctonear()))
230    }
231
232    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds instead of overflowing.
233    ///
234    /// # Examples
235    /// ```
236    /// use near_token::NearToken;
237    /// assert_eq!(NearToken::from_yoctonear(5).saturating_sub(NearToken::from_yoctonear(2)), NearToken::from_yoctonear(3));
238    /// assert_eq!(NearToken::from_yoctonear(1).saturating_sub(NearToken::from_yoctonear(2)), NearToken::from_yoctonear(0));
239    /// ```
240    pub const fn saturating_sub(self, rhs: Self) -> Self {
241        NearToken::from_yoctonear(self.as_yoctonear().saturating_sub(rhs.as_yoctonear()))
242    }
243
244    /// Saturating integer multiplication. Computes self * rhs, saturating at the numeric bounds instead of overflowing.
245    ///
246    /// # Examples
247    /// ```
248    /// use near_token::NearToken;
249    /// use std::u128;
250    /// assert_eq!(NearToken::from_yoctonear(2).saturating_mul(5), NearToken::from_yoctonear(10));
251    /// assert_eq!(NearToken::from_yoctonear(u128::MAX).saturating_mul(2), NearToken::from_yoctonear(u128::MAX));
252    /// ```
253    pub const fn saturating_mul(self, rhs: u128) -> Self {
254        NearToken::from_yoctonear(self.as_yoctonear().saturating_mul(rhs))
255    }
256
257    /// Saturating integer division. Computes self / rhs, saturating at the numeric bounds instead of overflowing.
258    ///
259    /// # Examples
260    /// ```
261    /// use near_token::NearToken;
262    /// assert_eq!(NearToken::from_yoctonear(10).saturating_div(2), NearToken::from_yoctonear(5));
263    /// assert_eq!(NearToken::from_yoctonear(10).saturating_div(0), NearToken::from_yoctonear(0))
264    /// ```
265    pub const fn saturating_div(self, rhs: u128) -> Self {
266        if rhs == 0 {
267            return NearToken::from_yoctonear(0);
268        }
269        NearToken::from_yoctonear(self.as_yoctonear().saturating_div(rhs))
270    }
271
272    /// Formats the `NearToken` and displays the amount in NEAR or yoctoNEAR depending on the value.
273    ///
274    /// # Examples
275    /// ```
276    /// use near_token::NearToken;
277    /// assert_eq!(NearToken::from_yoctonear(10_u128.pow(24)).exact_amount_display(), "1 NEAR");
278    /// assert_eq!(NearToken::from_yoctonear(15 * 10_u128.pow(23)).exact_amount_display(), "1.5 NEAR");
279    /// assert_eq!(NearToken::from_yoctonear(500).exact_amount_display(), "500 yoctoNEAR");
280    /// assert_eq!(NearToken::from_yoctonear(0).exact_amount_display(), "0 NEAR");
281    /// ```
282    pub fn exact_amount_display(&self) -> String {
283        let yoctonear = self.as_yoctonear();
284
285        if yoctonear == 0 {
286            "0 NEAR".to_string()
287        } else if yoctonear <= 1_000 {
288            format!("{} yoctoNEAR", yoctonear)
289        } else if yoctonear % ONE_NEAR == 0 {
290            format!("{} NEAR", yoctonear / ONE_NEAR)
291        } else {
292            format!(
293                "{}.{} NEAR",
294                yoctonear / ONE_NEAR,
295                format!("{:0>24}", yoctonear % ONE_NEAR).trim_end_matches('0')
296            )
297        }
298    }
299}
300
301#[cfg(test)]
302mod test {
303    use crate::NearToken;
304
305    #[test]
306    fn checked_add_tokens() {
307        let tokens = NearToken::from_yoctonear(u128::MAX - 3);
308        let any_tokens = NearToken::from_yoctonear(3);
309        let more_tokens = NearToken::from_yoctonear(4);
310        assert_eq!(
311            tokens.checked_add(any_tokens),
312            Some(NearToken::from_yoctonear(u128::MAX))
313        );
314        assert_eq!(tokens.checked_add(more_tokens), None);
315    }
316
317    #[test]
318    fn checked_sub_tokens() {
319        let tokens = NearToken::from_yoctonear(3);
320        let any_tokens = NearToken::from_yoctonear(1);
321        let more_tokens = NearToken::from_yoctonear(4);
322        assert_eq!(
323            tokens.checked_sub(any_tokens),
324            Some(NearToken::from_yoctonear(2))
325        );
326        assert_eq!(tokens.checked_sub(more_tokens), None);
327    }
328
329    #[test]
330    fn checked_mul_tokens() {
331        let tokens = NearToken::from_yoctonear(u128::MAX / 10);
332        assert_eq!(
333            tokens.checked_mul(10),
334            Some(NearToken::from_yoctonear(u128::MAX / 10 * 10))
335        );
336        assert_eq!(tokens.checked_mul(11), None);
337    }
338
339    #[test]
340    fn checked_div_tokens() {
341        let tokens = NearToken::from_yoctonear(10);
342        assert_eq!(tokens.checked_div(2), Some(NearToken::from_yoctonear(5)));
343        assert_eq!(tokens.checked_div(11), Some(NearToken::from_yoctonear(0)));
344        assert_eq!(tokens.checked_div(0), None);
345    }
346
347    #[test]
348    fn saturating_add_tokens() {
349        let tokens = NearToken::from_yoctonear(100);
350        let added_tokens = NearToken::from_yoctonear(1);
351        let another_tokens = NearToken::from_yoctonear(u128::MAX);
352        assert_eq!(
353            tokens.saturating_add(added_tokens),
354            NearToken::from_yoctonear(101)
355        );
356        assert_eq!(
357            another_tokens.saturating_add(added_tokens),
358            NearToken::from_yoctonear(u128::MAX)
359        );
360    }
361
362    #[test]
363    fn saturating_sub_tokens() {
364        let tokens = NearToken::from_yoctonear(100);
365        let rhs_tokens = NearToken::from_yoctonear(1);
366        let another_tokens = NearToken::from_yoctonear(u128::MIN);
367        assert_eq!(
368            tokens.saturating_sub(rhs_tokens),
369            NearToken::from_yoctonear(99)
370        );
371        assert_eq!(
372            another_tokens.saturating_sub(rhs_tokens),
373            NearToken::from_yoctonear(u128::MIN)
374        );
375    }
376
377    #[test]
378    fn saturating_mul_tokens() {
379        let tokens = NearToken::from_yoctonear(2);
380        let rhs = 10;
381        let another_tokens = u128::MAX;
382        assert_eq!(tokens.saturating_mul(rhs), NearToken::from_yoctonear(20));
383        assert_eq!(
384            tokens.saturating_mul(another_tokens),
385            NearToken::from_yoctonear(u128::MAX)
386        );
387    }
388
389    #[test]
390    fn saturating_div_tokens() {
391        let tokens = NearToken::from_yoctonear(10);
392        let rhs = 2;
393        let another_tokens = 20;
394        assert_eq!(tokens.saturating_div(rhs), NearToken::from_yoctonear(5));
395        assert_eq!(
396            tokens.saturating_div(another_tokens),
397            NearToken::from_yoctonear(0)
398        );
399    }
400
401    #[test]
402    fn exact_amount_display_tokens() {
403        let token = NearToken::from_yoctonear(0);
404        assert_eq!(token.exact_amount_display(), "0 NEAR");
405
406        let token = NearToken::from_yoctonear(500);
407        assert_eq!(token.exact_amount_display(), "500 yoctoNEAR");
408
409        let token = NearToken::from_yoctonear(10_u128.pow(24));
410        assert_eq!(token.exact_amount_display(), "1 NEAR");
411
412        let token = NearToken::from_yoctonear(15 * 10_u128.pow(23));
413        assert_eq!(token.exact_amount_display(), "1.5 NEAR");
414
415        let token = NearToken::from_yoctonear(1_234_567_890_123_456_789_000_000);
416        assert_eq!(token.exact_amount_display(), "1.234567890123456789 NEAR");
417    }
418}