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}