use alloy_chains::NamedChain;
use alloy_primitives::{Address, U256};
use bigdecimal::BigDecimal;
use std::str::FromStr;
use crate::config::constants::stablecoins::BSC_BINANCE_PEG_USDC;
use crate::errors::RetrievalError;
use super::decimal_precision::DecimalPrecision;
pub fn get_token_decimal_precision(chain: NamedChain, token_address: Address) -> DecimalPrecision {
if token_address == Address::ZERO {
return DecimalPrecision::NativeToken;
}
if matches!(chain, NamedChain::BinanceSmartChain) && token_address == BSC_BINANCE_PEG_USDC {
DecimalPrecision::BinancePegUsdc } else {
DecimalPrecision::Usdc }
}
pub fn u256_to_bigdecimal(
value: U256,
precision: DecimalPrecision,
) -> Result<BigDecimal, RetrievalError> {
let divisor = match precision {
DecimalPrecision::Usdc => U256::from(1_000_000u64), DecimalPrecision::BinancePegUsdc | DecimalPrecision::NativeToken => {
U256::from(1_000_000_000_000_000_000u128) }
DecimalPrecision::Custom(decimals) => U256::from(10u64).pow(U256::from(decimals)),
};
let whole = value / divisor;
let fractional = value % divisor;
let whole_decimal = BigDecimal::from_str(&whole.to_string())
.map_err(|_| RetrievalError::bigdecimal_conversion_failed(whole))?;
let fractional_decimal = BigDecimal::from_str(&fractional.to_string())
.map_err(|_| RetrievalError::bigdecimal_conversion_failed(fractional))?;
let divisor_decimal = BigDecimal::from_str(&divisor.to_string())
.map_err(|_| RetrievalError::bigdecimal_conversion_failed(divisor))?;
Ok(whole_decimal + (fractional_decimal / divisor_decimal))
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_chains::NamedChain;
use alloy_primitives::{address, U256};
#[test]
fn get_token_decimal_precision_for_native_token() {
let precision = get_token_decimal_precision(NamedChain::Arbitrum, Address::ZERO);
assert_eq!(precision, DecimalPrecision::NativeToken);
}
#[test]
fn get_token_decimal_precision_for_bsc_binance_peg_usdc() {
let bsc_binance_peg_usdc = address!("8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d");
let precision =
get_token_decimal_precision(NamedChain::BinanceSmartChain, bsc_binance_peg_usdc);
assert_eq!(precision, DecimalPrecision::BinancePegUsdc);
}
#[test]
fn get_token_decimal_precision_for_standard_usdc_on_arbitrum() {
let arbitrum_usdc = address!("af88d065e77c8cC2239327C5EDb3A432268e5831");
let precision = get_token_decimal_precision(NamedChain::Arbitrum, arbitrum_usdc);
assert_eq!(precision, DecimalPrecision::Usdc);
}
#[test]
fn get_token_decimal_precision_for_standard_usdc_on_base() {
let base_usdc = address!("833589fCD6eDb6E08f4c7C32D4f71b54bdA02913");
let precision = get_token_decimal_precision(NamedChain::Base, base_usdc);
assert_eq!(precision, DecimalPrecision::Usdc);
}
#[test]
fn get_token_decimal_precision_for_non_usdc_on_bsc() {
let other_token = address!("1111111111111111111111111111111111111111");
let precision = get_token_decimal_precision(NamedChain::BinanceSmartChain, other_token);
assert_eq!(precision, DecimalPrecision::Usdc); }
#[test]
fn u256_to_bigdecimal_with_usdc_precision() {
let value = U256::from(1_000_000u64); let result = u256_to_bigdecimal(value, DecimalPrecision::Usdc).unwrap();
let expected = BigDecimal::from_str("1.0").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_native_token_precision() {
let value = U256::from(1_000_000_000_000_000_000u128); let result = u256_to_bigdecimal(value, DecimalPrecision::NativeToken).unwrap();
let expected = BigDecimal::from_str("1.0").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_bsc_binance_peg_usdc_precision() {
let value = U256::from(1_500_000_000_000_000_000u128); let result = u256_to_bigdecimal(value, DecimalPrecision::BinancePegUsdc).unwrap();
let expected = BigDecimal::from_str("1.5").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_fractional_usdc() {
let value = U256::from(123_456u64); let result = u256_to_bigdecimal(value, DecimalPrecision::Usdc).unwrap();
let expected = BigDecimal::from_str("0.123456").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_zero() {
let value = U256::ZERO;
let result = u256_to_bigdecimal(value, DecimalPrecision::Usdc).unwrap();
let expected = BigDecimal::from_str("0").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_large_value() {
let value = U256::from(1_000_000_000_000_000_000_000u128); let result = u256_to_bigdecimal(value, DecimalPrecision::NativeToken).unwrap();
let expected = BigDecimal::from_str("1000.0").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_preserves_precision() {
let value = U256::from(123_456_789_012_345_678u128); let result = u256_to_bigdecimal(value, DecimalPrecision::NativeToken).unwrap();
let expected = BigDecimal::from_str("0.123456789012345678").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_custom_8_decimals() {
let value = U256::from(100_000_000u64); let result = u256_to_bigdecimal(value, DecimalPrecision::Custom(8)).unwrap();
let expected = BigDecimal::from_str("1.0").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_custom_12_decimals() {
let value = U256::from(1_500_000_000_000u64); let result = u256_to_bigdecimal(value, DecimalPrecision::Custom(12)).unwrap();
let expected = BigDecimal::from_str("1.5").unwrap();
assert_eq!(result, expected);
}
#[test]
fn u256_to_bigdecimal_with_custom_zero_decimals() {
let value = U256::from(42u64);
let result = u256_to_bigdecimal(value, DecimalPrecision::Custom(0)).unwrap();
let expected = BigDecimal::from_str("42").unwrap();
assert_eq!(result, expected);
}
#[test]
fn decimal_precision_custom_returns_correct_decimals() {
assert_eq!(DecimalPrecision::Custom(8).decimals(), 8);
assert_eq!(DecimalPrecision::Custom(12).decimals(), 12);
assert_eq!(DecimalPrecision::Custom(0).decimals(), 0);
}
}