use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TokenDecimals(u8);
impl TokenDecimals {
pub const MAX_REASONABLE: u8 = 18;
pub const STANDARD: Self = Self(18);
pub const USDC: Self = Self(6);
pub const WBTC: Self = Self(8);
pub const DAI: Self = Self(18);
pub const fn new(decimals: u8) -> Self {
Self(decimals)
}
pub const fn as_u8(&self) -> u8 {
self.0
}
pub const fn is_reasonable(&self) -> bool {
self.0 <= Self::MAX_REASONABLE
}
pub fn divisor(&self) -> f64 {
10_f64.powi(self.0 as i32)
}
}
impl From<u8> for TokenDecimals {
fn from(value: u8) -> Self {
Self(value)
}
}
impl std::fmt::Display for TokenDecimals {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} decimals", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_decimals_constants() {
assert_eq!(TokenDecimals::STANDARD.as_u8(), 18);
assert_eq!(TokenDecimals::USDC.as_u8(), 6);
assert_eq!(TokenDecimals::WBTC.as_u8(), 8);
assert_eq!(TokenDecimals::DAI.as_u8(), 18);
}
#[test]
fn test_token_decimals_reasonable() {
assert!(TokenDecimals::new(0).is_reasonable());
assert!(TokenDecimals::new(18).is_reasonable());
assert!(!TokenDecimals::new(19).is_reasonable());
assert!(!TokenDecimals::new(255).is_reasonable());
}
#[test]
fn test_token_decimals_divisor() {
assert_eq!(TokenDecimals::USDC.divisor(), 1_000_000.0);
assert_eq!(TokenDecimals::WBTC.divisor(), 100_000_000.0);
assert_eq!(
TokenDecimals::STANDARD.divisor(),
1_000_000_000_000_000_000.0
);
}
#[test]
fn test_display_formatting() {
let decimals = TokenDecimals::STANDARD;
assert_eq!(format!("{}", decimals), "18 decimals");
}
#[test]
fn test_serialization() {
let decimals = TokenDecimals::STANDARD;
let json = serde_json::to_string(&decimals).unwrap();
let deserialized: TokenDecimals = serde_json::from_str(&json).unwrap();
assert_eq!(decimals, deserialized);
}
#[test]
fn test_conversions() {
let u8_val: u8 = 18;
let decimals: TokenDecimals = u8_val.into();
let back: u8 = decimals.as_u8();
assert_eq!(u8_val, back);
}
}