Skip to main content

eth_prices/token/
identity.rs

1use std::fmt::Display;
2
3use alloy::primitives::Address;
4use serde::{Deserialize, Deserializer};
5
6/// A lightweight token identifier used by quoters and config.
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum TokenIdentifier {
9    /// An ERC-20 token identified by contract address.
10    ERC20 { address: Address },
11    /// A fiat endpoint identified by symbol, e.g. "fiat:usd".
12    Fiat { symbol: String },
13    /// The native currency of a chain, e.g. "eth" on Ethereum.
14    Native,
15}
16
17impl From<Address> for TokenIdentifier {
18    fn from(address: Address) -> Self {
19        TokenIdentifier::ERC20 { address }
20    }
21}
22
23impl TryFrom<String> for TokenIdentifier {
24    type Error = crate::error::EthPricesError;
25
26    /// Parses an identifier from strings such as `0x...`, `fiat:usd`, or `native`.
27    fn try_from(input: String) -> Result<Self, Self::Error> {
28        if input == "native" {
29            Ok(TokenIdentifier::Native)
30        } else if input.starts_with("fiat:") {
31            let symbol = input
32                .split("fiat:")
33                .nth(1)
34                .ok_or(crate::error::EthPricesError::InvalidFiatSymbol)?
35                .to_string();
36
37            Ok(TokenIdentifier::Fiat { symbol })
38        } else if input.starts_with("0x") {
39            let address = input
40                .parse::<Address>()
41                .map_err(|e| crate::error::EthPricesError::InvalidAddress(e.to_string()))?;
42
43            Ok(TokenIdentifier::ERC20 { address })
44        } else {
45            Err(crate::error::EthPricesError::TokenNotFound(input))
46        }
47    }
48}
49
50impl Display for TokenIdentifier {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        match self {
53            TokenIdentifier::ERC20 { address } => write!(f, "{}", address),
54            TokenIdentifier::Fiat { symbol } => write!(f, "fiat:{}", symbol),
55            TokenIdentifier::Native => write!(f, "native"),
56        }
57    }
58}
59
60impl<'de> Deserialize<'de> for TokenIdentifier {
61    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62    where
63        D: Deserializer<'de>,
64    {
65        let s = String::deserialize(deserializer)?;
66        TokenIdentifier::try_from(s).map_err(serde::de::Error::custom)
67    }
68}