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}