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}