mod error;
mod utils;
mod trait_impls;
pub use self::error::NearTokenError;
pub use self::utils::DecimalNumberParsingError;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshDeserialize, borsh::BorshSerialize)
)]
#[cfg_attr(feature = "abi", derive(borsh::BorshSchema))]
#[repr(transparent)]
pub struct NearToken {
inner: u128,
}
const ONE_NEAR: u128 = 10_u128.pow(24);
const ONE_MILLINEAR: u128 = 10_u128.pow(21);
const ONE_MICRONEAR: u128 = 10_u128.pow(18);
impl NearToken {
pub const MAX: NearToken = NearToken::from_yoctonear(u128::MAX);
pub const ZERO: NearToken = NearToken::from_yoctonear(0);
pub const fn from_yoctonear(inner: u128) -> Self {
Self { inner }
}
pub const fn from_micronear(inner: u128) -> Self {
Self {
inner: inner * ONE_MICRONEAR,
}
}
pub const fn from_millinear(inner: u128) -> Self {
Self {
inner: inner * ONE_MILLINEAR,
}
}
pub const fn from_near(inner: u128) -> Self {
Self {
inner: inner * ONE_NEAR,
}
}
pub const fn as_near(&self) -> u128 {
self.inner / ONE_NEAR
}
pub const fn as_micronear(&self) -> u128 {
self.inner / ONE_MICRONEAR
}
pub const fn as_millinear(&self) -> u128 {
self.inner / ONE_MILLINEAR
}
pub const fn as_yoctonear(&self) -> u128 {
self.inner
}
pub const fn is_zero(&self) -> bool {
self.inner == 0
}
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
if let Some(near) = self.as_yoctonear().checked_add(rhs.as_yoctonear()) {
Some(Self::from_yoctonear(near))
} else {
None
}
}
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
if let Some(near) = self.as_yoctonear().checked_sub(rhs.as_yoctonear()) {
Some(Self::from_yoctonear(near))
} else {
None
}
}
pub const fn checked_mul(self, rhs: u128) -> Option<Self> {
if let Some(near) = self.as_yoctonear().checked_mul(rhs) {
Some(Self::from_yoctonear(near))
} else {
None
}
}
pub const fn checked_div(self, rhs: u128) -> Option<Self> {
if let Some(near) = self.as_yoctonear().checked_div(rhs) {
Some(Self::from_yoctonear(near))
} else {
None
}
}
pub const fn saturating_add(self, rhs: Self) -> Self {
NearToken::from_yoctonear(self.as_yoctonear().saturating_add(rhs.as_yoctonear()))
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
NearToken::from_yoctonear(self.as_yoctonear().saturating_sub(rhs.as_yoctonear()))
}
pub const fn saturating_mul(self, rhs: u128) -> Self {
NearToken::from_yoctonear(self.as_yoctonear().saturating_mul(rhs))
}
pub const fn saturating_div(self, rhs: u128) -> Self {
if rhs == 0 {
return NearToken::from_yoctonear(0);
}
NearToken::from_yoctonear(self.as_yoctonear().saturating_div(rhs))
}
pub fn exact_amount_display(&self) -> String {
let yoctonear = self.as_yoctonear();
if yoctonear == 0 {
"0 NEAR".to_string()
} else if yoctonear <= 1_000 {
format!("{} yoctoNEAR", yoctonear)
} else if yoctonear % ONE_NEAR == 0 {
format!("{} NEAR", yoctonear / ONE_NEAR)
} else {
format!(
"{}.{} NEAR",
yoctonear / ONE_NEAR,
format!("{:0>24}", yoctonear % ONE_NEAR).trim_end_matches('0')
)
}
}
}
#[cfg(test)]
mod test {
use crate::NearToken;
#[test]
fn checked_add_tokens() {
let tokens = NearToken::from_yoctonear(u128::MAX - 3);
let any_tokens = NearToken::from_yoctonear(3);
let more_tokens = NearToken::from_yoctonear(4);
assert_eq!(
tokens.checked_add(any_tokens),
Some(NearToken::from_yoctonear(u128::MAX))
);
assert_eq!(tokens.checked_add(more_tokens), None);
}
#[test]
fn checked_sub_tokens() {
let tokens = NearToken::from_yoctonear(3);
let any_tokens = NearToken::from_yoctonear(1);
let more_tokens = NearToken::from_yoctonear(4);
assert_eq!(
tokens.checked_sub(any_tokens),
Some(NearToken::from_yoctonear(2))
);
assert_eq!(tokens.checked_sub(more_tokens), None);
}
#[test]
fn checked_mul_tokens() {
let tokens = NearToken::from_yoctonear(u128::MAX / 10);
assert_eq!(
tokens.checked_mul(10),
Some(NearToken::from_yoctonear(u128::MAX / 10 * 10))
);
assert_eq!(tokens.checked_mul(11), None);
}
#[test]
fn checked_div_tokens() {
let tokens = NearToken::from_yoctonear(10);
assert_eq!(tokens.checked_div(2), Some(NearToken::from_yoctonear(5)));
assert_eq!(tokens.checked_div(11), Some(NearToken::from_yoctonear(0)));
assert_eq!(tokens.checked_div(0), None);
}
#[test]
fn saturating_add_tokens() {
let tokens = NearToken::from_yoctonear(100);
let added_tokens = NearToken::from_yoctonear(1);
let another_tokens = NearToken::from_yoctonear(u128::MAX);
assert_eq!(
tokens.saturating_add(added_tokens),
NearToken::from_yoctonear(101)
);
assert_eq!(
another_tokens.saturating_add(added_tokens),
NearToken::from_yoctonear(u128::MAX)
);
}
#[test]
fn saturating_sub_tokens() {
let tokens = NearToken::from_yoctonear(100);
let rhs_tokens = NearToken::from_yoctonear(1);
let another_tokens = NearToken::from_yoctonear(u128::MIN);
assert_eq!(
tokens.saturating_sub(rhs_tokens),
NearToken::from_yoctonear(99)
);
assert_eq!(
another_tokens.saturating_sub(rhs_tokens),
NearToken::from_yoctonear(u128::MIN)
);
}
#[test]
fn saturating_mul_tokens() {
let tokens = NearToken::from_yoctonear(2);
let rhs = 10;
let another_tokens = u128::MAX;
assert_eq!(tokens.saturating_mul(rhs), NearToken::from_yoctonear(20));
assert_eq!(
tokens.saturating_mul(another_tokens),
NearToken::from_yoctonear(u128::MAX)
);
}
#[test]
fn saturating_div_tokens() {
let tokens = NearToken::from_yoctonear(10);
let rhs = 2;
let another_tokens = 20;
assert_eq!(tokens.saturating_div(rhs), NearToken::from_yoctonear(5));
assert_eq!(
tokens.saturating_div(another_tokens),
NearToken::from_yoctonear(0)
);
}
#[test]
fn exact_amount_display_tokens() {
let token = NearToken::from_yoctonear(0);
assert_eq!(token.exact_amount_display(), "0 NEAR");
let token = NearToken::from_yoctonear(500);
assert_eq!(token.exact_amount_display(), "500 yoctoNEAR");
let token = NearToken::from_yoctonear(10_u128.pow(24));
assert_eq!(token.exact_amount_display(), "1 NEAR");
let token = NearToken::from_yoctonear(15 * 10_u128.pow(23));
assert_eq!(token.exact_amount_display(), "1.5 NEAR");
let token = NearToken::from_yoctonear(1_234_567_890_123_456_789_000_000);
assert_eq!(token.exact_amount_display(), "1.234567890123456789 NEAR");
}
}