1use alloy::primitives::{Address, U256};
4
5use crate::{Result, asset::erc20::ERC20, provider::RpcProvider};
6
7pub mod erc20;
8pub mod identity;
9
10pub use identity::AssetIdentifier;
11
12#[derive(Debug, Clone, PartialEq)]
14pub struct Asset {
15 pub identifier: AssetIdentifier,
17 pub name: String,
19 pub symbol: String,
21 pub decimals: u8,
23}
24
25const FIAT_DECIMALS: u8 = 6;
26
27impl Asset {
28 pub async fn new(identifier: AssetIdentifier, provider: &RpcProvider) -> Result<Self> {
32 let (name, symbol, decimals) = match &identifier {
33 AssetIdentifier::ERC20 { address } => {
34 let erc20 = ERC20::new(*address, provider);
35
36 (
37 erc20.name().call().await?,
38 erc20.symbol().call().await?,
39 erc20.decimals().call().await?,
40 )
41 }
42 AssetIdentifier::Fiat { symbol } => (symbol.clone(), symbol.clone(), FIAT_DECIMALS),
43 AssetIdentifier::Native => ("Native".to_string(), "ETH".to_string(), 18),
44 };
45
46 Ok(Self {
47 identifier,
48 name,
49 symbol,
50 decimals,
51 })
52 }
53
54 pub fn nominal_amount(&self) -> U256 {
56 U256::from(10).pow(U256::from(self.decimals))
57 }
58
59 pub fn format_amount(&self, amount: U256, precision: usize) -> crate::Result<String> {
61 let amount = amount
62 .to_string()
63 .parse::<f64>()
64 .map_err(|e| crate::error::EthPricesError::InvalidAssetAmount(e.to_string()))?;
65 let amount = amount / 10_f64.powf(self.decimals as f64);
66 Ok(format!("{:.precision$}", amount))
67 }
68
69 pub fn address(&self) -> Option<Address> {
73 match &self.identifier {
74 AssetIdentifier::ERC20 { address } => Some(*address),
75 AssetIdentifier::Fiat { .. } => None,
76 AssetIdentifier::Native => None,
77 }
78 }
79}