Skip to main content

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