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}