618 lines
20 KiB
Rust
618 lines
20 KiB
Rust
use super::{U128, U256, U512, U64};
|
|
use serde::{Deserialize, Serialize, Serializer};
|
|
use std::{
|
|
convert::{TryFrom, TryInto},
|
|
fmt,
|
|
time::Duration,
|
|
};
|
|
use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames};
|
|
|
|
// compatibility re-export
|
|
#[doc(hidden)]
|
|
pub use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
|
|
#[doc(hidden)]
|
|
pub type ParseChainError = TryFromPrimitiveError<Chain>;
|
|
|
|
// When adding a new chain:
|
|
// 1. add new variant to the Chain enum;
|
|
// 2. add extra information in the last `impl` block (explorer URLs, block time) when applicable;
|
|
// 3. (optional) add aliases:
|
|
// - Strum (in kebab-case): `#[strum(to_string = "<main>", serialize = "<aliasX>", ...)]`
|
|
// `to_string = "<main>"` must be present and will be used in `Display`, `Serialize`
|
|
// and `FromStr`, while `serialize = "<aliasX>"` will be appended to `FromStr`.
|
|
// More info: <https://docs.rs/strum/latest/strum/additional_attributes/index.html#attributes-on-variants>
|
|
// - Serde (in snake_case): `#[serde(alias = "<aliasX>", ...)]`
|
|
// Aliases are appended to the `Deserialize` implementation.
|
|
// More info: <https://serde.rs/variant-attrs.html>
|
|
// - Add a test at the bottom of the file
|
|
|
|
// We don't derive Serialize because it is manually implemented using AsRef<str> and it would
|
|
// break a lot of things since Serialize is `kebab-case` vs Deserialize `snake_case`.
|
|
// This means that the Chain type is not "round-trippable", because the Serialize and Deserialize
|
|
// implementations do not use the same case style.
|
|
|
|
/// An Ethereum EIP-155 chain.
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
PartialEq,
|
|
Eq,
|
|
PartialOrd,
|
|
Ord,
|
|
Hash,
|
|
AsRefStr, // AsRef<str>, fmt::Display and serde::Serialize
|
|
EnumVariantNames, // Chain::VARIANTS
|
|
EnumString, // FromStr, TryFrom<&str>
|
|
EnumIter, // Chain::iter
|
|
EnumCount, // Chain::COUNT
|
|
TryFromPrimitive, // TryFrom<u64>
|
|
Deserialize,
|
|
)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[strum(serialize_all = "kebab-case")]
|
|
#[repr(u64)]
|
|
pub enum Chain {
|
|
#[strum(serialize = "ethlive", serialize = "mainnet")]
|
|
Mainnet = 1,
|
|
Morden = 2,
|
|
Ropsten = 3,
|
|
Rinkeby = 4,
|
|
Goerli = 5,
|
|
Kovan = 42,
|
|
Sepolia = 11155111,
|
|
|
|
Optimism = 10,
|
|
OptimismKovan = 69,
|
|
OptimismGoerli = 420,
|
|
|
|
Arbitrum = 42161,
|
|
ArbitrumTestnet = 421611,
|
|
ArbitrumGoerli = 421613,
|
|
ArbitrumNova = 42170,
|
|
|
|
Cronos = 25,
|
|
CronosTestnet = 338,
|
|
|
|
Rsk = 30,
|
|
|
|
#[strum(to_string = "bsc", serialize = "binance-smart-chain")]
|
|
#[serde(alias = "bsc")]
|
|
BinanceSmartChain = 56,
|
|
#[strum(to_string = "bsc-testnet", serialize = "binance-smart-chain-testnet")]
|
|
#[serde(alias = "bsc_testnet")]
|
|
BinanceSmartChainTestnet = 97,
|
|
|
|
Poa = 99,
|
|
Sokol = 77,
|
|
|
|
#[strum(to_string = "xdai", serialize = "gnosis", serialize = "gnosis-chain")]
|
|
#[serde(alias = "xdai", alias = "gnosis", alias = "gnosis_chain")]
|
|
XDai = 100,
|
|
|
|
Polygon = 137,
|
|
#[strum(to_string = "mumbai", serialize = "polygon-mumbai")]
|
|
#[serde(alias = "mumbai")]
|
|
PolygonMumbai = 80001,
|
|
|
|
Fantom = 250,
|
|
FantomTestnet = 4002,
|
|
|
|
Moonbeam = 1284,
|
|
MoonbeamDev = 1281,
|
|
|
|
Moonriver = 1285,
|
|
|
|
Moonbase = 1287,
|
|
|
|
Dev = 1337,
|
|
#[strum(to_string = "anvil-hardhat", serialize = "anvil", serialize = "hardhat")]
|
|
#[serde(alias = "anvil", alias = "hardhat")]
|
|
AnvilHardhat = 31337,
|
|
|
|
Evmos = 9001,
|
|
EvmosTestnet = 9000,
|
|
|
|
Chiado = 10200,
|
|
|
|
Oasis = 26863,
|
|
|
|
Emerald = 42262,
|
|
EmeraldTestnet = 42261,
|
|
|
|
FilecoinMainnet = 314,
|
|
FilecoinHyperspaceTestnet = 3141,
|
|
|
|
Avalanche = 43114,
|
|
#[strum(to_string = "fuji", serialize = "avalanche-fuji")]
|
|
#[serde(alias = "fuji")]
|
|
AvalancheFuji = 43113,
|
|
|
|
Celo = 42220,
|
|
CeloAlfajores = 44787,
|
|
CeloBaklava = 62320,
|
|
|
|
Aurora = 1313161554,
|
|
AuroraTestnet = 1313161555,
|
|
|
|
Canto = 7700,
|
|
CantoTestnet = 740,
|
|
|
|
Boba = 288,
|
|
}
|
|
|
|
// === impl Chain ===
|
|
|
|
// This must be implemented manually so we avoid a conflict with `TryFromPrimitive` where it treats
|
|
// the `#[default]` attribute as its own `#[num_enum(default)]`
|
|
impl Default for Chain {
|
|
fn default() -> Self {
|
|
Self::Mainnet
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_into_numeric {
|
|
($($ty:ty)+) => {$(
|
|
impl From<Chain> for $ty {
|
|
fn from(chain: Chain) -> Self {
|
|
u64::from(chain).into()
|
|
}
|
|
}
|
|
)+};
|
|
}
|
|
|
|
macro_rules! impl_try_from_numeric {
|
|
($($native:ty)+ ; $($primitive:ty)*) => {
|
|
$(
|
|
impl TryFrom<$native> for Chain {
|
|
type Error = ParseChainError;
|
|
|
|
fn try_from(value: $native) -> Result<Self, Self::Error> {
|
|
(value as u64).try_into()
|
|
}
|
|
}
|
|
)+
|
|
|
|
$(
|
|
impl TryFrom<$primitive> for Chain {
|
|
type Error = ParseChainError;
|
|
|
|
fn try_from(value: $primitive) -> Result<Self, Self::Error> {
|
|
if value.bits() > 64 {
|
|
// `TryFromPrimitiveError` only has a `number` field which has the same type
|
|
// as the `#[repr(_)]` attribute on the enum.
|
|
return Err(ParseChainError { number: value.low_u64() })
|
|
}
|
|
value.low_u64().try_into()
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl From<Chain> for u64 {
|
|
fn from(chain: Chain) -> Self {
|
|
chain as u64
|
|
}
|
|
}
|
|
|
|
impl_into_numeric!(u128 U64 U128 U256 U512);
|
|
|
|
impl TryFrom<U64> for Chain {
|
|
type Error = ParseChainError;
|
|
|
|
fn try_from(value: U64) -> Result<Self, Self::Error> {
|
|
value.low_u64().try_into()
|
|
}
|
|
}
|
|
|
|
impl_try_from_numeric!(u8 u16 u32 usize; U128 U256 U512);
|
|
|
|
impl fmt::Display for Chain {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.pad(self.as_ref())
|
|
}
|
|
}
|
|
|
|
impl Serialize for Chain {
|
|
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
s.serialize_str(self.as_ref())
|
|
}
|
|
}
|
|
|
|
// NB: all utility functions *should* be explicitly exhaustive (not use `_` matcher) so we don't
|
|
// forget to update them when adding a new `Chain` variant.
|
|
#[allow(clippy::match_like_matches_macro)]
|
|
impl Chain {
|
|
/// Returns the chain's average blocktime, if applicable.
|
|
///
|
|
/// It can be beneficial to know the average blocktime to adjust the polling of an HTTP provider
|
|
/// for example.
|
|
///
|
|
/// **Note:** this is not an accurate average, but is rather a sensible default derived from
|
|
/// blocktime charts such as [Etherscan's](https://etherscan.com/chart/blocktime)
|
|
/// or [Polygonscan's](https://polygonscan.com/chart/blocktime).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use ethers_core::types::Chain;
|
|
/// use std::time::Duration;
|
|
///
|
|
/// assert_eq!(
|
|
/// Chain::Mainnet.average_blocktime_hint(),
|
|
/// Some(Duration::from_millis(12_000)),
|
|
/// );
|
|
/// assert_eq!(Chain::Optimism.average_blocktime_hint(), None);
|
|
/// ```
|
|
pub const fn average_blocktime_hint(&self) -> Option<Duration> {
|
|
use Chain::*;
|
|
|
|
let ms = match self {
|
|
Mainnet => 12_000,
|
|
Arbitrum | ArbitrumTestnet | ArbitrumGoerli | ArbitrumNova => 1_300,
|
|
Polygon | PolygonMumbai => 2_100,
|
|
Moonbeam | Moonriver => 12_500,
|
|
BinanceSmartChain | BinanceSmartChainTestnet => 3_000,
|
|
Avalanche | AvalancheFuji => 2_000,
|
|
Fantom | FantomTestnet => 1_200,
|
|
Cronos | CronosTestnet | Canto | CantoTestnet => 5_700,
|
|
Evmos | EvmosTestnet => 1_900,
|
|
Aurora | AuroraTestnet => 1_100,
|
|
Oasis => 5_500,
|
|
Emerald => 6_000,
|
|
Dev | AnvilHardhat => 200,
|
|
Celo | CeloAlfajores | CeloBaklava => 5_000,
|
|
FilecoinHyperspaceTestnet | FilecoinMainnet => 30_000,
|
|
|
|
// Explicitly exhaustive. See NB above.
|
|
Morden | Ropsten | Rinkeby | Goerli | Kovan | XDai | Chiado | Sepolia | Moonbase |
|
|
MoonbeamDev | Optimism | OptimismGoerli | OptimismKovan | Poa | Sokol | Rsk |
|
|
EmeraldTestnet | Boba => return None,
|
|
};
|
|
|
|
Some(Duration::from_millis(ms))
|
|
}
|
|
|
|
/// Returns whether the chain implements EIP-1559 (with the type 2 EIP-2718 transaction type).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use ethers_core::types::Chain;
|
|
///
|
|
/// assert!(!Chain::Mainnet.is_legacy());
|
|
/// assert!(Chain::Celo.is_legacy());
|
|
/// ```
|
|
pub const fn is_legacy(&self) -> bool {
|
|
use Chain::*;
|
|
|
|
match self {
|
|
// Known legacy chains / non EIP-1559 compliant
|
|
Optimism |
|
|
OptimismGoerli |
|
|
OptimismKovan |
|
|
Fantom |
|
|
FantomTestnet |
|
|
BinanceSmartChain |
|
|
BinanceSmartChainTestnet |
|
|
Arbitrum |
|
|
ArbitrumTestnet |
|
|
ArbitrumGoerli |
|
|
ArbitrumNova |
|
|
Rsk |
|
|
Oasis |
|
|
Emerald |
|
|
EmeraldTestnet |
|
|
Celo |
|
|
CeloAlfajores |
|
|
CeloBaklava |
|
|
Boba => true,
|
|
|
|
// Known EIP-1559 chains
|
|
Mainnet |
|
|
Goerli |
|
|
Sepolia |
|
|
Polygon |
|
|
PolygonMumbai |
|
|
Avalanche |
|
|
AvalancheFuji |
|
|
FilecoinHyperspaceTestnet => false,
|
|
|
|
// Unknown / not applicable, default to false for backwards compatibility
|
|
Dev | AnvilHardhat | Morden | Ropsten | Rinkeby | Cronos | CronosTestnet | Kovan |
|
|
Sokol | Poa | XDai | Moonbeam | MoonbeamDev | Moonriver | Moonbase | Evmos |
|
|
EvmosTestnet | Chiado | Aurora | AuroraTestnet | Canto | CantoTestnet |
|
|
FilecoinMainnet => false,
|
|
}
|
|
}
|
|
|
|
/// Returns the chain's blockchain explorer and its API (Etherscan and Etherscan-like) URLs.
|
|
///
|
|
/// Returns `(API_URL, BASE_URL)`
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use ethers_core::types::Chain;
|
|
///
|
|
/// assert_eq!(
|
|
/// Chain::Mainnet.etherscan_urls(),
|
|
/// Some(("https://api.etherscan.io/api", "https://etherscan.io"))
|
|
/// );
|
|
/// assert_eq!(
|
|
/// Chain::Avalanche.etherscan_urls(),
|
|
/// Some(("https://api.snowtrace.io/api", "https://snowtrace.io"))
|
|
/// );
|
|
/// assert_eq!(Chain::AnvilHardhat.etherscan_urls(), None);
|
|
/// ```
|
|
pub const fn etherscan_urls(&self) -> Option<(&'static str, &'static str)> {
|
|
use Chain::*;
|
|
|
|
let urls = match self {
|
|
Mainnet => ("https://api.etherscan.io/api", "https://etherscan.io"),
|
|
Ropsten => ("https://api-ropsten.etherscan.io/api", "https://ropsten.etherscan.io"),
|
|
Kovan => ("https://api-kovan.etherscan.io/api", "https://kovan.etherscan.io"),
|
|
Rinkeby => ("https://api-rinkeby.etherscan.io/api", "https://rinkeby.etherscan.io"),
|
|
Goerli => ("https://api-goerli.etherscan.io/api", "https://goerli.etherscan.io"),
|
|
Sepolia => ("https://api-sepolia.etherscan.io/api", "https://sepolia.etherscan.io"),
|
|
|
|
Polygon => ("https://api.polygonscan.com/api", "https://polygonscan.com"),
|
|
PolygonMumbai => {
|
|
("https://api-testnet.polygonscan.com/api", "https://mumbai.polygonscan.com")
|
|
}
|
|
|
|
Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"),
|
|
AvalancheFuji => {
|
|
("https://api-testnet.snowtrace.io/api", "https://testnet.snowtrace.io")
|
|
}
|
|
|
|
Optimism => {
|
|
("https://api-optimistic.etherscan.io/api", "https://optimistic.etherscan.io")
|
|
}
|
|
OptimismGoerli => (
|
|
"https://api-goerli-optimistic.etherscan.io/api",
|
|
"https://goerli-optimism.etherscan.io",
|
|
),
|
|
OptimismKovan => (
|
|
"https://api-kovan-optimistic.etherscan.io/api",
|
|
"https://kovan-optimistic.etherscan.io",
|
|
),
|
|
|
|
Fantom => ("https://api.ftmscan.com/api", "https://ftmscan.com"),
|
|
FantomTestnet => ("https://api-testnet.ftmscan.com/api", "https://testnet.ftmscan.com"),
|
|
|
|
BinanceSmartChain => ("https://api.bscscan.com/api", "https://bscscan.com"),
|
|
BinanceSmartChainTestnet => {
|
|
("https://api-testnet.bscscan.com/api", "https://testnet.bscscan.com")
|
|
}
|
|
|
|
Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"),
|
|
ArbitrumTestnet => {
|
|
("https://api-testnet.arbiscan.io/api", "https://testnet.arbiscan.io")
|
|
}
|
|
ArbitrumGoerli => ("https://api-goerli.arbiscan.io/api", "https://goerli.arbiscan.io"),
|
|
ArbitrumNova => ("https://api-nova.arbiscan.io/api", "https://nova.arbiscan.io/"),
|
|
|
|
Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"),
|
|
CronosTestnet => {
|
|
("https://api-testnet.cronoscan.com/api", "https://testnet.cronoscan.com")
|
|
}
|
|
|
|
Moonbeam => ("https://api-moonbeam.moonscan.io/api", "https://moonbeam.moonscan.io/"),
|
|
Moonbase => ("https://api-moonbase.moonscan.io/api", "https://moonbase.moonscan.io/"),
|
|
Moonriver => ("https://api-moonriver.moonscan.io/api", "https://moonriver.moonscan.io"),
|
|
|
|
// blockscout API is etherscan compatible
|
|
XDai => {
|
|
("https://blockscout.com/xdai/mainnet/api", "https://blockscout.com/xdai/mainnet")
|
|
}
|
|
|
|
Chiado => {
|
|
("https://blockscout.chiadochain.net/api", "https://blockscout.chiadochain.net")
|
|
}
|
|
|
|
FilecoinHyperspaceTestnet => {
|
|
("https://api.hyperspace.node.glif.io/rpc/v1", "https://hyperspace.filfox.info")
|
|
}
|
|
|
|
Sokol => ("https://blockscout.com/poa/sokol/api", "https://blockscout.com/poa/sokol"),
|
|
|
|
Poa => ("https://blockscout.com/poa/core/api", "https://blockscout.com/poa/core"),
|
|
|
|
Rsk => ("https://blockscout.com/rsk/mainnet/api", "https://blockscout.com/rsk/mainnet"),
|
|
|
|
Oasis => ("https://scan.oasischain.io/api", "https://scan.oasischain.io/"),
|
|
|
|
Emerald => {
|
|
("https://explorer.emerald.oasis.dev/api", "https://explorer.emerald.oasis.dev/")
|
|
}
|
|
EmeraldTestnet => (
|
|
"https://testnet.explorer.emerald.oasis.dev/api",
|
|
"https://testnet.explorer.emerald.oasis.dev/",
|
|
),
|
|
|
|
Aurora => ("https://api.aurorascan.dev/api", "https://aurorascan.dev"),
|
|
AuroraTestnet => {
|
|
("https://testnet.aurorascan.dev/api", "https://testnet.aurorascan.dev")
|
|
}
|
|
|
|
Evmos => ("https://evm.evmos.org/api", "https://evm.evmos.org/"),
|
|
EvmosTestnet => ("https://evm.evmos.dev/api", "https://evm.evmos.dev/"),
|
|
|
|
Celo => ("https://explorer.celo.org/mainnet/api", "https://explorer.celo.org/mainnet"),
|
|
CeloAlfajores => {
|
|
("https://explorer.celo.org/alfajores/api", "https://explorer.celo.org/alfajores")
|
|
}
|
|
CeloBaklava => {
|
|
("https://explorer.celo.org/baklava/api", "https://explorer.celo.org/baklava")
|
|
}
|
|
|
|
Canto => ("https://evm.explorer.canto.io/api", "https://evm.explorer.canto.io/"),
|
|
CantoTestnet => (
|
|
"https://testnet-explorer.canto.neobase.one/api",
|
|
"https://testnet-explorer.canto.neobase.one/",
|
|
),
|
|
|
|
Boba => ("https://api.bobascan.com/api", "https://bobascan.com"),
|
|
|
|
AnvilHardhat | Dev | Morden | MoonbeamDev | FilecoinMainnet => {
|
|
// this is explicitly exhaustive so we don't forget to add new urls when adding a
|
|
// new chain
|
|
return None
|
|
}
|
|
};
|
|
|
|
Some(urls)
|
|
}
|
|
|
|
/// Returns the chain's blockchain explorer's API key environment variable's default name.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use ethers_core::types::Chain;
|
|
///
|
|
/// assert_eq!(Chain::Mainnet.etherscan_api_key_name(), Some("ETHERSCAN_API_KEY"));
|
|
/// assert_eq!(Chain::AnvilHardhat.etherscan_api_key_name(), None);
|
|
/// ```
|
|
pub const fn etherscan_api_key_name(&self) -> Option<&'static str> {
|
|
use Chain::*;
|
|
|
|
let api_key_name = match self {
|
|
Mainnet |
|
|
Morden |
|
|
Ropsten |
|
|
Kovan |
|
|
Rinkeby |
|
|
Goerli |
|
|
Optimism |
|
|
OptimismGoerli |
|
|
OptimismKovan |
|
|
BinanceSmartChain |
|
|
BinanceSmartChainTestnet |
|
|
Arbitrum |
|
|
ArbitrumTestnet |
|
|
ArbitrumGoerli |
|
|
ArbitrumNova |
|
|
Cronos |
|
|
CronosTestnet |
|
|
Aurora |
|
|
AuroraTestnet |
|
|
Celo |
|
|
CeloAlfajores |
|
|
CeloBaklava => "ETHERSCAN_API_KEY",
|
|
|
|
Avalanche | AvalancheFuji => "SNOWTRACE_API_KEY",
|
|
|
|
Polygon | PolygonMumbai => "POLYGONSCAN_API_KEY",
|
|
|
|
Fantom | FantomTestnet => "FTMSCAN_API_KEY",
|
|
|
|
Moonbeam | Moonbase | MoonbeamDev | Moonriver => "MOONSCAN_API_KEY",
|
|
|
|
Canto | CantoTestnet => "BLOCKSCOUT_API_KEY",
|
|
|
|
Boba => "BOBASCAN_API_KEY",
|
|
|
|
// Explicitly exhaustive. See NB above.
|
|
XDai |
|
|
Chiado |
|
|
Sepolia |
|
|
Rsk |
|
|
Sokol |
|
|
Poa |
|
|
Oasis |
|
|
Emerald |
|
|
EmeraldTestnet |
|
|
Evmos |
|
|
EvmosTestnet |
|
|
AnvilHardhat |
|
|
Dev |
|
|
FilecoinMainnet |
|
|
FilecoinHyperspaceTestnet => return None,
|
|
};
|
|
|
|
Some(api_key_name)
|
|
}
|
|
|
|
/// Returns the chain's blockchain explorer's API key, from the environment variable with the
|
|
/// name specified in [`etherscan_api_key_name`](Chain::etherscan_api_key_name).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use ethers_core::types::Chain;
|
|
///
|
|
/// let chain = Chain::Mainnet;
|
|
/// std::env::set_var(chain.etherscan_api_key_name().unwrap(), "KEY");
|
|
/// assert_eq!(chain.etherscan_api_key().as_deref(), Some("KEY"));
|
|
/// ```
|
|
pub fn etherscan_api_key(&self) -> Option<String> {
|
|
self.etherscan_api_key_name().and_then(|name| std::env::var(name).ok())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use strum::IntoEnumIterator;
|
|
|
|
#[test]
|
|
fn default() {
|
|
assert_eq!(serde_json::to_string(&Chain::default()).unwrap(), "\"mainnet\"");
|
|
}
|
|
|
|
#[test]
|
|
fn enum_iter() {
|
|
assert_eq!(Chain::COUNT, Chain::iter().size_hint().0);
|
|
}
|
|
|
|
#[test]
|
|
fn roundtrip_string() {
|
|
for chain in Chain::iter() {
|
|
let chain_string = chain.to_string();
|
|
assert_eq!(chain_string, format!("{chain}"));
|
|
assert_eq!(chain_string.as_str(), chain.as_ref());
|
|
assert_eq!(serde_json::to_string(&chain).unwrap(), format!("\"{chain_string}\""));
|
|
|
|
assert_eq!(chain_string.parse::<Chain>().unwrap(), chain);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn roundtrip_serde() {
|
|
for chain in Chain::iter() {
|
|
let chain_string = serde_json::to_string(&chain).unwrap();
|
|
let chain_string = chain_string.replace('-', "_");
|
|
assert_eq!(serde_json::from_str::<'_, Chain>(&chain_string).unwrap(), chain);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn aliases() {
|
|
use Chain::*;
|
|
|
|
// kebab-case
|
|
const ALIASES: &[(Chain, &[&str])] = &[
|
|
(BinanceSmartChain, &["bsc", "binance-smart-chain"]),
|
|
(BinanceSmartChainTestnet, &["bsc-testnet", "binance-smart-chain-testnet"]),
|
|
(XDai, &["xdai", "gnosis", "gnosis-chain"]),
|
|
(PolygonMumbai, &["mumbai"]),
|
|
(AnvilHardhat, &["anvil", "hardhat"]),
|
|
(AvalancheFuji, &["fuji"]),
|
|
];
|
|
|
|
for &(chain, aliases) in ALIASES {
|
|
for &alias in aliases {
|
|
assert_eq!(alias.parse::<Chain>().unwrap(), chain);
|
|
let s = alias.to_string().replace('-', "_");
|
|
assert_eq!(serde_json::from_str::<Chain>(&format!("\"{s}\"")).unwrap(), chain);
|
|
}
|
|
}
|
|
}
|
|
}
|