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