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