usdshe/
lib.rs

1//! # usdshe
2//!
3//! `usdshe` is a utility crate for conveniently accessing USDC (USD Coin)
4//! contract addresses on various blockchain networks.
5//!
6//! It provides a simple trait, [`Usdc`], which can be implemented for different
7//! chain identifiers to retrieve the respective USDC contract address. Currently,
8//! an implementation for [`alloy_chains::NamedChain`] is provided.
9//!
10//! ## Examples
11//!
12//! ```rust
13//! use usdshe::{Usdc, UsdcError};
14//! use alloy_chains::NamedChain;
15//! use alloy_primitives::Address;
16//! use std::str::FromStr;
17//!
18//! // Get Mainnet USDC address
19//! match NamedChain::Mainnet.usdc_address() {
20//!     Ok(address) => {
21//!         assert_eq!(
22//!             address,
23//!             Address::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap()
24//!         );
25//!         println!("Mainnet USDC Address: {}", address);
26//!     }
27//!     Err(e) => eprintln!("Error fetching Mainnet USDC: {}", e),
28//! }
29//!
30//! // Attempt to get USDC address for an unsupported chain
31//! // Assuming Kovan is not in the supported list for this example.
32//! // Replace with a NamedChain variant that is genuinely not in your match statement
33//! // if Kovan (or another example) gets added.
34//! // For instance, if `NamedChain::Gnosis` is unsupported:
35//! let some_unsupported_chain = NamedChain::Gnosis; // Example
36//! match some_unsupported_chain.usdc_address() {
37//!     Ok(address) => panic!("Should not find address for {:?}", some_unsupported_chain),
38//!     Err(UsdcError::UnsupportedChain(chain)) => {
39//!         assert_eq!(chain, some_unsupported_chain);
40//!         eprintln!("Correctly failed for unsupported chain: {:?}", chain);
41//!     }
42//!     Err(e) => panic!("Unexpected error: {}", e),
43//! }
44//! ```
45
46mod address;
47
48use alloy_chains::NamedChain;
49use alloy_primitives::Address;
50use std::str::FromStr;
51use thiserror::Error;
52
53pub use address::*;
54
55/// Represents errors that can occur when retrieving a USDC address.
56#[derive(Error, Debug)]
57pub enum UsdcError {
58    /// Indicates that a USDC address is not available or known for the specified chain.
59    #[error("USDC address not available for chain: {0:?}")]
60    UnsupportedChain(NamedChain),
61
62    /// Indicates that a known address string failed to parse into a valid [`Address`].
63    /// This should ideally not happen if the constants are well-formed.
64    #[error("Failed to parse address string '{address_str}': {source}")]
65    AddressParseError {
66        /// The address string that failed to parse.
67        address_str: String,
68        /// The underlying parsing error.
69        #[source]
70        source: alloy_primitives::hex::FromHexError,
71    },
72}
73
74/// A trait for types that can provide a USDC contract address.
75pub trait Usdc {
76    /// Returns the USDC contract address for the implementing context.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`UsdcError::UnsupportedChain`] if the address is not known for the
81    /// given context (e.g., an unsupported blockchain).
82    /// Returns [`UsdcError::AddressParseError`] if a known address string is malformed
83    /// and cannot be parsed.
84    fn usdc_address(&self) -> Result<Address, UsdcError>;
85}
86
87/// Implementation of the [`Usdc`] trait for the [`alloy_chains::NamedChain`] enum.
88///
89/// This implementation provides USDC addresses for a predefined set of chains.
90impl Usdc for NamedChain {
91    /// Retrieves the USDC address for the given `NamedChain`.
92    ///
93    /// ## Supported Chains
94    ///
95    /// Refer to the crate's README for a list of currently supported chains.
96    ///
97    /// ## Examples
98    ///
99    /// ```rust
100    /// use usdshe::{Usdc, UsdcError};
101    /// use alloy_chains::NamedChain;
102    /// use alloy_primitives::Address;
103    /// use std::str::FromStr;
104    ///
105    /// // Get Polygon USDC address
106    /// let polygon_address = NamedChain::Polygon.usdc_address().unwrap();
107    /// assert_eq!(
108    ///     polygon_address,
109    ///     // Replace with actual Polygon USDC address string from your constants
110    ///     Address::from_str("0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359").unwrap()
111    /// );
112    ///
113    /// // Handle an unsupported chain
114    /// // Assuming Gnosis is unsupported for this example.
115    /// let result = NamedChain::Gnosis.usdc_address();
116    /// match result {
117    ///     Err(UsdcError::UnsupportedChain(chain)) => {
118    ///         assert_eq!(chain, NamedChain::Gnosis);
119    ///         // Handle the error appropriately
120    ///     }
121    ///     Ok(_) => panic!("Expected an error for Gnosis, but got an address."),
122    ///     Err(e) => panic!("Unexpected error type: {}", e),
123    /// }
124    /// ```
125    ///
126    /// # Errors
127    ///
128    /// - [`UsdcError::UnsupportedChain`]: If the USDC address for the specified `NamedChain`
129    ///   is not defined in this crate.
130    /// - [`UsdcError::AddressParseError`]: If the predefined address string for a supported
131    ///   chain is malformed (this should be a bug in the crate if it occurs).
132    fn usdc_address(&self) -> Result<Address, UsdcError> {
133        use NamedChain::*;
134
135        // Note: The address strings (ARBITRUM_USDC, etc.) are expected to be
136        // valid hexadecimal strings.
137        let address_s = match self {
138            Arbitrum => Ok(ARBITRUM_USDC),
139            ArbitrumSepolia => Ok(ARBITRUM_SEPOLIA_USDC),
140            Avalanche => Ok(AVALANCHE_USDC),
141            Base => Ok(BASE_USDC),
142            BaseSepolia => Ok(BASE_SEPOLIA_USDC),
143            BinanceSmartChain => Ok(BSC_USDC),
144            Fantom => Ok(FANTOM_USDC),
145            Fraxtal => Ok(FRAXTAL_USDC),
146            Sepolia => Ok(ETHEREUM_SEPOLIA_USDC),
147            Linea => Ok(LINEA_USDC),
148            Mainnet => Ok(ETHEREUM_USDC),
149            Mantle => Ok(MANTLE_USDC),
150            Mode => Ok(MODE_USDC),
151            Optimism => Ok(OPTIMISM_USDC),
152            Polygon => Ok(POLYGON_USDC),
153            Scroll => Ok(SCROLL_USDC),
154            Sonic => Ok(SONIC_USDC),
155            Unichain => Ok(UNICHAIN_USDC),
156            ZkSync => Ok(ZKSYNC_USDC),
157            unsupported_chain => Err(UsdcError::UnsupportedChain(*unsupported_chain)),
158        }?;
159
160        Address::from_str(address_s).map_err(|e| UsdcError::AddressParseError {
161            address_str: address_s.to_string(),
162            source: e,
163        })
164    }
165}