use alloy_primitives::{address, Address};
use core::fmt;
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Chain {
#[cfg_attr(feature = "clap", value(name = "ethereum"))]
Ethereum,
#[cfg_attr(feature = "clap", value(name = "arbitrum"))]
Arbitrum,
#[cfg_attr(feature = "clap", value(name = "optimism"))]
Optimism,
#[cfg_attr(feature = "clap", value(name = "polygon"))]
Polygon,
#[cfg_attr(feature = "clap", value(name = "base"))]
Base,
#[cfg_attr(feature = "clap", value(name = "bsc"))]
Bsc,
#[cfg_attr(feature = "clap", value(name = "sonic"))]
Sonic,
#[cfg_attr(feature = "clap", value(name = "avalanche"))]
Avalanche,
#[cfg_attr(feature = "clap", value(name = "celo"))]
Celo,
#[cfg_attr(feature = "clap", value(name = "hyperevm"))]
HyperEvm,
}
impl Chain {
pub const ALL: &'static [Self] = &[
Self::Ethereum,
Self::Arbitrum,
Self::Optimism,
Self::Polygon,
Self::Base,
Self::Bsc,
Self::Sonic,
Self::Avalanche,
Self::Celo,
Self::HyperEvm,
];
pub const fn id(self) -> u64 {
match self {
Self::Ethereum => 1,
Self::Optimism => 10,
Self::Bsc => 56,
Self::Polygon => 137,
Self::Sonic => 146,
Self::HyperEvm => 999,
Self::Base => 8_453,
Self::Arbitrum => 42_161,
Self::Celo => 42_220,
Self::Avalanche => 43_114,
}
}
pub const fn name(self) -> &'static str {
match self {
Self::Ethereum => "ethereum",
Self::Arbitrum => "arbitrum",
Self::Optimism => "optimism",
Self::Polygon => "polygon",
Self::Base => "base",
Self::Bsc => "bsc",
Self::Sonic => "sonic",
Self::Avalanche => "avalanche",
Self::Celo => "celo",
Self::HyperEvm => "hyperevm",
}
}
pub const fn wrapped_native(self) -> Option<Address> {
match self {
Self::Ethereum => Some(address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")),
Self::Arbitrum => Some(address!("82aF49447D8a07e3bd95BD0d56f35241523fBab1")),
Self::Optimism => Some(address!("4200000000000000000000000000000000000006")),
Self::Polygon => Some(address!("0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270")),
Self::Base => Some(address!("4200000000000000000000000000000000000006")),
Self::Bsc => Some(address!("bb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c")),
Self::Sonic => Some(address!("039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38")),
Self::Avalanche => Some(address!("B31f66AA3C1e785363F0875A1B74E27b85FD66c7")),
Self::Celo => None,
Self::HyperEvm => Some(address!("5555555555555555555555555555555555555555")),
}
}
}
impl TryFrom<u64> for Chain {
type Error = u64;
fn try_from(id: u64) -> Result<Self, u64> {
Ok(match id {
1 => Self::Ethereum,
10 => Self::Optimism,
56 => Self::Bsc,
137 => Self::Polygon,
146 => Self::Sonic,
999 => Self::HyperEvm,
8_453 => Self::Base,
42_161 => Self::Arbitrum,
42_220 => Self::Celo,
43_114 => Self::Avalanche,
other => return Err(other),
})
}
}
impl fmt::Display for Chain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn id_round_trip() {
for &chain in Chain::ALL {
let id = chain.id();
let back = Chain::try_from(id).expect("known chain id");
assert_eq!(back, chain, "round-trip failed for {chain}");
}
}
#[test]
fn avalanche_chain_id_is_43114() {
assert_eq!(Chain::Avalanche.id(), 43_114);
assert_eq!(Chain::try_from(43_114).unwrap(), Chain::Avalanche);
}
#[test]
fn sonic_chain_id_is_146() {
assert_eq!(Chain::Sonic.id(), 146);
assert_eq!(Chain::try_from(146).unwrap(), Chain::Sonic);
}
#[test]
fn hyperevm_chain_id_is_999() {
assert_eq!(Chain::HyperEvm.id(), 999);
assert_eq!(Chain::try_from(999).unwrap(), Chain::HyperEvm);
}
#[test]
fn try_from_unknown_returns_id() {
let unknown = 999_999u64;
assert_eq!(Chain::try_from(unknown), Err(unknown));
}
#[test]
fn name_is_lowercase() {
assert_eq!(Chain::Ethereum.name(), "ethereum");
assert_eq!(Chain::Bsc.name(), "bsc");
assert_eq!(Chain::Arbitrum.name(), "arbitrum");
}
#[test]
fn display_matches_name() {
assert_eq!(format!("{}", Chain::Ethereum), "ethereum");
assert_eq!(format!("{}", Chain::Celo), "celo");
}
#[test]
fn wrapped_native_round_trip() {
for chain in Chain::ALL {
let _ = chain.wrapped_native(); }
}
#[test]
fn wrapped_native_celo_is_none() {
assert_eq!(Chain::Celo.wrapped_native(), None);
}
#[test]
fn wrapped_native_hyperevm_is_whype() {
assert_eq!(
Chain::HyperEvm.wrapped_native(),
Some(address!("5555555555555555555555555555555555555555"))
);
}
#[test]
fn wrapped_native_distinct_per_evm_chain() {
assert_ne!(Chain::Ethereum.wrapped_native(), Chain::Polygon.wrapped_native(),);
assert_ne!(Chain::Ethereum.wrapped_native(), Chain::Avalanche.wrapped_native(),);
assert_ne!(Chain::Polygon.wrapped_native(), Chain::Bsc.wrapped_native(),);
}
}