use std::fmt;
use alloy_primitives::U256;
use derive_more::{Add, AddAssign, Div, Mul, Sub, Sum};
use serde::{Deserialize, Serialize};
use super::{Token, TokenError};
#[derive(
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Hash,
Add,
AddAssign,
Div,
Mul,
Sub,
Sum,
)]
#[mul(forward)]
#[div(forward)]
pub struct Usdt(U256);
impl Usdt {
pub const ZERO: Usdt = Usdt(U256::ZERO);
pub fn from_decimal(value: f64) -> Result<Self, TokenError> {
let micro_usdt = (value * 1_000_000.0).round(); if micro_usdt.is_nan() || micro_usdt.is_infinite() {
return Err(TokenError::InvalidAmount);
}
let amount = U256::from(micro_usdt.abs() as u128);
Ok(Self(amount))
}
pub fn is_whole(&self) -> bool {
self.0 % U256::from(1_000_000) == U256::ZERO
}
pub fn to<T>(&self) -> T
where
T: TryFrom<U256> + std::fmt::Debug,
<T as TryFrom<alloy_primitives::Uint<256, 4>>>::Error: std::fmt::Debug,
{
T::try_from(self.0).expect("failed to build decimal from Usdt")
}
pub fn from<T>(val: T) -> Self
where
U256: TryFrom<T>,
<T as TryInto<U256>>::Error: std::fmt::Debug,
{
Self(U256::try_from(val).expect("failed to build Usdt from value"))
}
pub fn checked_add(self, other: Self) -> Option<Self> {
self.0.checked_add(other.0).map(Self)
}
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.0.checked_sub(other.0).map(Self)
}
pub fn to_decimal(&self) -> f64 {
todo!()
}
pub fn to_decimal_string(&self) -> String {
let divisor = U256::from(10).pow(U256::from(Self::decimals()));
let integer = self.0 / divisor;
let fraction = self.0 % divisor;
let mut s = format!("{integer}.{fraction:06}");
while s.ends_with('0') {
s.pop();
}
if s.ends_with('.') {
s.pop();
}
s
}
}
impl From<U256> for Usdt {
fn from(value: U256) -> Self {
Usdt(value)
}
}
impl From<Usdt> for U256 {
fn from(value: Usdt) -> Self {
value.0
}
}
impl Token for Usdt {
fn symbol() -> &'static str {
"USDT"
}
fn decimals() -> u32 {
6
}
}
impl fmt::Display for Usdt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.to_decimal_string(), Self::symbol())
}
}
impl fmt::Debug for Usdt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Usdt")
.field(&self.to_decimal_string())
.finish()
}
}
#[macro_export]
macro_rules! usdt {
($val:literal USDT) => {{
$crate::contracts::token::usdt::Usdt::from_decimal($val)
.expect("Invalid USDT amount")
}};
($val:literal MICRO_USDT) => {
$crate::contracts::token::usdt::Usdt::from($val)
};
}