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 AssetIdentifier::Custom(id) => (id.clone(), id.clone(), 0),
45 };
46
47 Ok(Self {
48 identifier,
49 name,
50 symbol,
51 decimals,
52 })
53 }
54
55 pub fn nominal_amount(&self) -> U256 {
57 U256::from(10).pow(U256::from(self.decimals))
58 }
59
60 pub fn format_amount(&self, amount: U256, precision: usize) -> crate::Result<String> {
62 let amount = amount
63 .to_string()
64 .parse::<f64>()
65 .map_err(|e| crate::error::EthPricesError::InvalidAssetAmount(e.to_string()))?;
66 let amount = amount / 10_f64.powf(self.decimals as f64);
67 Ok(format!("{:.precision$}", amount))
68 }
69
70 pub fn address(&self) -> Option<Address> {
74 match &self.identifier {
75 AssetIdentifier::ERC20 { address } => Some(*address),
76 AssetIdentifier::Fiat { .. } => None,
77 AssetIdentifier::Native => None,
78 AssetIdentifier::Custom(_) => None,
79 }
80 }
81}