From e007ea01b1bc95be91d0b37e230df7f005170c4b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Dec 2022 13:44:51 +0100 Subject: [PATCH] feat(core): chain macros and impls (#1958) --- Cargo.lock | 22 ++ ethers-core/Cargo.toml | 1 + ethers-core/src/types/chain.rs | 602 ++++++++++++++------------------- 3 files changed, 272 insertions(+), 353 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a885ded..cf19ecb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1420,6 +1420,7 @@ dependencies = [ "hex", "hex-literal", "k256", + "num_enum", "once_cell", "open-fastrlp", "proc-macro2", @@ -2576,6 +2577,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num_threads" version = "0.1.3" diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml index 9dd1c927..f2c67a0e 100644 --- a/ethers-core/Cargo.toml +++ b/ethers-core/Cargo.toml @@ -42,6 +42,7 @@ cargo_metadata = { version = "0.15.2", optional = true } convert_case = { version = "0.6.0", optional = true } syn = { version = "1.0.105", optional = true } proc-macro2 = { version = "1.0.47", optional = true } +num_enum = "0.5.7" [dev-dependencies] tempfile = { version = "3.3.0", default-features = false } diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs index 70dd6a10..11417376 100644 --- a/ethers-core/src/types/chain.rs +++ b/ethers-core/src/types/chain.rs @@ -1,138 +1,160 @@ -use super::U256; +use super::{U128, U256, U512, U64}; +use num_enum::{TryFromPrimitive, TryFromPrimitiveError}; use serde::{Deserialize, Serialize, Serializer}; use std::{ convert::{TryFrom, TryInto}, fmt, - str::FromStr, time::Duration, }; -use strum::EnumVariantNames; -use thiserror::Error; - -#[derive(Debug, Clone, Error)] -#[error("Failed to parse chain: {0}")] -pub struct ParseChainError(String); +use strum::{AsRefStr, EnumString, EnumVariantNames}; // When adding a new chain: // 1. add new variant to the Chain enum; -// 2. update Display/FromStr impl; -// 3. add etherscan_keys if supported. +// 2. add extra information in the last `impl` block (explorer URLs, block time) when applicable; +// 3. (optional) add aliases: `#[strum(serialize = "main", serialize = "alias", ...)]`; +// "main" must be present and will be used in `Display`, `Serialize` and `FromStr`, +// while the aliases will be added only to `FromStr`. -/// Enum for all known chains. -#[repr(u64)] -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Deserialize, EnumVariantNames)] +/// An Ethereum EIP-155 chain. +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + AsRefStr, // also for fmt::Display and serde::Serialize + EnumVariantNames, // Self::VARIANTS + EnumString, // FromStr, TryFrom<&str> + TryFromPrimitive, // TryFrom + Deserialize, +)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "kebab-case")] +#[repr(u64)] pub enum Chain { - #[default] Mainnet = 1, Morden = 2, Ropsten = 3, Rinkeby = 4, Goerli = 5, - Optimism = 10, - Cronos = 25, - Rsk = 30, Kovan = 42, - #[strum(serialize = "bsc")] - BinanceSmartChain = 56, + Sepolia = 11155111, + + Optimism = 10, OptimismKovan = 69, - Sokol = 77, - #[strum(serialize = "bsc-testnet")] - BinanceSmartChainTestnet = 97, - Poa = 99, - #[strum(serialize = "gnosis")] - XDai = 100, - Polygon = 137, - Fantom = 250, - CronosTestnet = 338, OptimismGoerli = 420, - MoonbeamDev = 1281, - Moonbeam = 1284, - Moonriver = 1285, - Moonbase = 1287, - Dev = 1337, - FantomTestnet = 4002, - EvmosTestnet = 9000, - Evmos = 9001, - Chiado = 10200, - Oasis = 26863, - AnvilHardhat = 31337, + Arbitrum = 42161, - EmeraldTestnet = 42261, - Emerald = 42262, - AvalancheFuji = 43113, - Avalanche = 43114, - PolygonMumbai = 80001, ArbitrumTestnet = 421611, ArbitrumGoerli = 421613, - Sepolia = 11155111, - Aurora = 1313161554, - AuroraTestnet = 1313161555, + + Cronos = 25, + CronosTestnet = 338, + + Rsk = 30, + + #[strum(serialize = "bsc")] + BinanceSmartChain = 56, + #[strum(serialize = "bsc-testnet")] + BinanceSmartChainTestnet = 97, + + Poa = 99, + Sokol = 77, + + #[strum(serialize = "gnosis", serialize = "xdai", serialize = "gnosis-chain")] + XDai = 100, + + Polygon = 137, + #[strum(serialize = "mumbai", serialize = "polygon-mumbai")] + PolygonMumbai = 80001, + + Fantom = 250, + FantomTestnet = 4002, + + Moonbeam = 1284, + MoonbeamDev = 1281, + + Moonriver = 1285, + + Moonbase = 1287, + + #[strum(serialize = "dev")] + Dev = 1337, + #[strum(serialize = "anvil-hardhat", serialize = "anvil", serialize = "hardhat")] + AnvilHardhat = 31337, + + Evmos = 9001, + EvmosTestnet = 9000, + + Chiado = 10200, + + Oasis = 26863, + + Emerald = 42262, + EmeraldTestnet = 42261, + + Avalanche = 43114, + #[strum(serialize = "fuji", serialize = "avalanche-fuji")] + AvalancheFuji = 43113, + Celo = 42220, CeloAlfajores = 44787, CeloBaklava = 62320, + + Aurora = 1313161554, + AuroraTestnet = 1313161555, } // === impl Chain === -impl fmt::Display for Chain { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let chain = match self { - Chain::Mainnet => "mainnet", - Chain::Morden => "morden", - Chain::Ropsten => "ropsten", - Chain::Rinkeby => "rinkeby", - Chain::Goerli => "goerli", - Chain::Kovan => "kovan", - Chain::XDai => "gnosis", - Chain::Chiado => "chiado", - Chain::Polygon => "polygon", - Chain::PolygonMumbai => "mumbai", - Chain::Avalanche => "avalanche", - Chain::AvalancheFuji => "fuji", - Chain::Sepolia => "sepolia", - Chain::Moonbeam => "moonbeam", - Chain::Moonbase => "moonbase", - Chain::MoonbeamDev => "moonbeam-dev", - Chain::Moonriver => "moonriver", - Chain::Optimism => "optimism", - Chain::OptimismGoerli => "optimism-goerli", - Chain::OptimismKovan => "optimism-kovan", - Chain::Fantom => "fantom", - Chain::Dev => "dev", - Chain::FantomTestnet => "fantom-testnet", - Chain::BinanceSmartChain => "bsc", - Chain::BinanceSmartChainTestnet => "bsc-testnet", - Chain::Arbitrum => "arbitrum", - Chain::ArbitrumTestnet => "arbitrum-testnet", - Chain::ArbitrumGoerli => "arbitrum-goerli", - Chain::Cronos => "cronos", - Chain::CronosTestnet => "cronos-testnet", - Chain::Poa => "poa", - Chain::Sokol => "sokol", - Chain::Rsk => "rsk", - Chain::Oasis => "oasis", - Chain::Emerald => "emerald", - Chain::EmeraldTestnet => "emerald-testnet", - Chain::AnvilHardhat => "anvil-hardhat", - Chain::Evmos => "evmos", - Chain::EvmosTestnet => "evmos-testnet", - Chain::Aurora => "aurora", - Chain::AuroraTestnet => "aurora-testnet", - Chain::Celo => "celo", - Chain::CeloAlfajores => "celo-alfajores", - Chain::CeloBaklava => "celo-baklava", - }; - - f.pad(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 } } -impl From for u32 { - fn from(chain: Chain) -> Self { - chain as u32 - } +macro_rules! impl_into_numeric { + ($($ty:ty)+) => {$( + impl From 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 = TryFromPrimitiveError; + + fn try_from(value: $native) -> Result { + (value as u64).try_into() + } + } + )+ + + $( + impl TryFrom<$primitive> for Chain { + type Error = TryFromPrimitiveError; + + fn try_from(value: $primitive) -> Result { + if value.bits() > 64 { + // `TryFromPrimitiveError` only has a `number` field which has the same type + // as the `#[repr(_)]` attribute on the enum. + return Err(TryFromPrimitiveError { number: value.low_u64() }) + } + value.low_u64().try_into() + } + } + )* + }; } impl From for u64 { @@ -141,134 +163,21 @@ impl From for u64 { } } -impl From for U256 { - fn from(chain: Chain) -> Self { - u64::from(chain).into() +impl_into_numeric!(u128 U64 U128 U256 U512); + +impl TryFrom for Chain { + type Error = TryFromPrimitiveError; + + fn try_from(value: U64) -> Result { + value.low_u64().try_into() } } -impl TryFrom for Chain { - type Error = ParseChainError; +impl_try_from_numeric!(u8 u16 u32 usize; U128 U256 U512); - fn try_from(chain: u32) -> Result { - (chain as u64).try_into() - } -} - -impl TryFrom for Chain { - type Error = ParseChainError; - - fn try_from(chain: u64) -> Result { - Ok(match chain { - 1 => Chain::Mainnet, - 2 => Chain::Morden, - 3 => Chain::Ropsten, - 4 => Chain::Rinkeby, - 5 => Chain::Goerli, - 42 => Chain::Kovan, - 100 => Chain::XDai, - 10200 => Chain::Chiado, - 137 => Chain::Polygon, - 1337 => Chain::Dev, - 31337 => Chain::AnvilHardhat, - 250 => Chain::Fantom, - 4002 => Chain::FantomTestnet, - 80001 => Chain::PolygonMumbai, - 43114 => Chain::Avalanche, - 43113 => Chain::AvalancheFuji, - 11155111 => Chain::Sepolia, - 1284 => Chain::Moonbeam, - 1287 => Chain::Moonbase, - 1281 => Chain::MoonbeamDev, - 1285 => Chain::Moonriver, - 10 => Chain::Optimism, - 420 => Chain::OptimismGoerli, - 69 => Chain::OptimismKovan, - 56 => Chain::BinanceSmartChain, - 97 => Chain::BinanceSmartChainTestnet, - 42161 => Chain::Arbitrum, - 421611 => Chain::ArbitrumTestnet, - 421613 => Chain::ArbitrumGoerli, - 25 => Chain::Cronos, - 338 => Chain::CronosTestnet, - 99 => Chain::Poa, - 77 => Chain::Sokol, - 30 => Chain::Rsk, - 26863 => Chain::Oasis, - 42262 => Chain::Emerald, - 42261 => Chain::EmeraldTestnet, - 9001 => Chain::Evmos, - 9000 => Chain::EvmosTestnet, - 1313161554 => Chain::Aurora, - 1313161555 => Chain::AuroraTestnet, - 42220 => Chain::Celo, - 44787 => Chain::CeloAlfajores, - 62320 => Chain::CeloBaklava, - _ => return Err(ParseChainError(chain.to_string())), - }) - } -} - -impl TryFrom for Chain { - type Error = ParseChainError; - - fn try_from(chain: U256) -> Result { - if chain.bits() > 64 { - return Err(ParseChainError(chain.to_string())) - } - chain.as_u64().try_into() - } -} - -impl FromStr for Chain { - type Err = ParseChainError; - - fn from_str(chain: &str) -> Result { - Ok(match chain { - "mainnet" => Chain::Mainnet, - "morden" => Chain::Morden, - "ropsten" => Chain::Ropsten, - "rinkeby" => Chain::Rinkeby, - "goerli" => Chain::Goerli, - "kovan" => Chain::Kovan, - "xdai" | "gnosis" | "gnosis-chain" => Chain::XDai, - "chiado" => Chain::Chiado, - "polygon" => Chain::Polygon, - "mumbai" | "polygon-mumbai" => Chain::PolygonMumbai, - "avalanche" => Chain::Avalanche, - "fuji" | "avalanche-fuji" => Chain::AvalancheFuji, - "sepolia" => Chain::Sepolia, - "moonbeam" => Chain::Moonbeam, - "moonbase" => Chain::Moonbase, - "moonbeam-dev" => Chain::MoonbeamDev, - "moonriver" => Chain::Moonriver, - "optimism" => Chain::Optimism, - "optimism-goerli" => Chain::OptimismGoerli, - "optimism-kovan" => Chain::OptimismKovan, - "fantom" => Chain::Fantom, - "fantom-testnet" => Chain::FantomTestnet, - "dev" => Chain::Dev, - "anvil" | "hardhat" | "anvil-hardhat" => Chain::AnvilHardhat, - "bsc" => Chain::BinanceSmartChain, - "bsc-testnet" => Chain::BinanceSmartChainTestnet, - "arbitrum" => Chain::Arbitrum, - "arbitrum-testnet" => Chain::ArbitrumTestnet, - "arbitrum-goerli" => Chain::ArbitrumGoerli, - "cronos" => Chain::Cronos, - "cronos-testnet" => Chain::CronosTestnet, - "poa" => Chain::Poa, - "sokol" => Chain::Sokol, - "rsk" => Chain::Rsk, - "oasis" => Chain::Oasis, - "emerald" => Chain::Emerald, - "emerald-testnet" => Chain::EmeraldTestnet, - "aurora" => Chain::Aurora, - "aurora-testnet" => Chain::AuroraTestnet, - "celo" => Chain::Celo, - "celo-alfajores" => Chain::CeloAlfajores, - "celo-baklava" => Chain::CeloBaklava, - _ => return Err(ParseChainError(chain.to_owned())), - }) +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(self.as_ref()) } } @@ -277,163 +186,135 @@ impl Serialize for Chain { where S: Serializer, { - s.serialize_str(self.to_string().as_ref()) + 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. impl Chain { - /// The blocktime varies from chain to 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 will not return the accurate average depending on the time but is rather a - /// sensible default derived from blocktime charts like - /// - pub fn average_blocktime_hint(&self) -> Option { + /// **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). + pub const fn average_blocktime_hint(&self) -> Option { + use Chain::*; + let ms = match self { - Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli => 1_300, - Chain::Mainnet | Chain::Optimism => 13_000, - Chain::Polygon | Chain::PolygonMumbai => 2_100, - Chain::Moonbeam | Chain::Moonriver => 12_500, - Chain::BinanceSmartChain | Chain::BinanceSmartChainTestnet => 3_000, - Chain::Avalanche | Chain::AvalancheFuji => 2_000, - Chain::Fantom | Chain::FantomTestnet => 1_200, - Chain::Cronos | Chain::CronosTestnet => 5_700, - Chain::Evmos | Chain::EvmosTestnet => 1_900, - Chain::Aurora | Chain::AuroraTestnet => 1_100, - Chain::Oasis => 5_500, - Chain::Emerald => 6_000, - Chain::Dev | Chain::AnvilHardhat => 200, - Chain::Celo | Chain::CeloAlfajores | Chain::CeloBaklava => 5_000, + Arbitrum | ArbitrumTestnet | ArbitrumGoerli => 1_300, + Mainnet | Optimism => 13_000, + Polygon | PolygonMumbai => 2_100, + Moonbeam | Moonriver => 12_500, + BinanceSmartChain | BinanceSmartChainTestnet => 3_000, + Avalanche | AvalancheFuji => 2_000, + Fantom | FantomTestnet => 1_200, + Cronos | CronosTestnet => 5_700, + Evmos | EvmosTestnet => 1_900, + Aurora | AuroraTestnet => 1_100, + Oasis => 5_500, + Emerald => 6_000, + Dev | AnvilHardhat => 200, + Celo | CeloAlfajores | CeloBaklava => 5_000, // Explictly handle all network to make it easier not to forget this match when new // networks are added. - Chain::Morden | - Chain::Ropsten | - Chain::Rinkeby | - Chain::Goerli | - Chain::Kovan | - Chain::XDai | - Chain::Chiado | - Chain::Sepolia | - Chain::Moonbase | - Chain::MoonbeamDev | - Chain::OptimismGoerli | - Chain::OptimismKovan | - Chain::Poa | - Chain::Sokol | - Chain::Rsk | - Chain::EmeraldTestnet => return None, + Morden | Ropsten | Rinkeby | Goerli | Kovan | XDai | Chiado | Sepolia | Moonbase | + MoonbeamDev | OptimismGoerli | OptimismKovan | Poa | Sokol | Rsk | EmeraldTestnet => { + return None + } }; Some(Duration::from_millis(ms)) } - /// Returns the corresponding etherscan URLs + /// Returns the chain's blockchain explorer and its API (Etherscan and Etherscan-like) URLs. /// /// Returns `(API URL, BASE_URL)`, like `("https://api(-chain).etherscan.io/api", "https://etherscan.io")` - pub fn etherscan_urls(&self) -> Option<(&'static str, &'static str)> { + pub const fn etherscan_urls(&self) -> Option<(&'static str, &'static str)> { + use Chain::*; + let urls = match self { - Chain::Mainnet => ("https://api.etherscan.io/api", "https://etherscan.io"), - Chain::Ropsten => { - ("https://api-ropsten.etherscan.io/api", "https://ropsten.etherscan.io") - } - Chain::Kovan => ("https://api-kovan.etherscan.io/api", "https://kovan.etherscan.io"), - Chain::Rinkeby => { - ("https://api-rinkeby.etherscan.io/api", "https://rinkeby.etherscan.io") - } - Chain::Goerli => ("https://api-goerli.etherscan.io/api", "https://goerli.etherscan.io"), - Chain::Sepolia => { - ("https://api-sepolia.etherscan.io/api", "https://sepolia.etherscan.io") - } - Chain::Polygon => ("https://api.polygonscan.com/api", "https://polygonscan.com"), - Chain::PolygonMumbai => { + 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") } - Chain::Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"), - Chain::AvalancheFuji => { + Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"), + AvalancheFuji => { ("https://api-testnet.snowtrace.io/api", "https://testnet.snowtrace.io") } - Chain::Optimism => { + Optimism => { ("https://api-optimistic.etherscan.io/api", "https://optimistic.etherscan.io") } - Chain::OptimismGoerli => ( + OptimismGoerli => ( "https://api-goerli-optimistic.etherscan.io/api", "https://goerli-optimism.etherscan.io", ), - Chain::OptimismKovan => ( + OptimismKovan => ( "https://api-kovan-optimistic.etherscan.io/api", "https://kovan-optimistic.etherscan.io", ), - Chain::Fantom => ("https://api.ftmscan.com/api", "https://ftmscan.com"), - Chain::FantomTestnet => { - ("https://api-testnet.ftmscan.com/api", "https://testnet.ftmscan.com") - } - Chain::BinanceSmartChain => ("https://api.bscscan.com/api", "https://bscscan.com"), - Chain::BinanceSmartChainTestnet => { + 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") } - Chain::Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"), - Chain::ArbitrumTestnet => { + Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"), + ArbitrumTestnet => { ("https://api-testnet.arbiscan.io/api", "https://testnet.arbiscan.io") } - Chain::ArbitrumGoerli => ( + ArbitrumGoerli => ( "https://goerli-rollup-explorer.arbitrum.io/api", "https://goerli-rollup-explorer.arbitrum.io", ), - Chain::Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"), - Chain::CronosTestnet => { + Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"), + CronosTestnet => { ("https://api-testnet.cronoscan.com/api", "https://testnet.cronoscan.com") } - Chain::Moonbeam => { - ("https://api-moonbeam.moonscan.io/api", "https://moonbeam.moonscan.io/") - } - Chain::Moonbase => { - ("https://api-moonbase.moonscan.io/api", "https://moonbase.moonscan.io/") - } - Chain::Moonriver => { - ("https://api-moonriver.moonscan.io/api", "https://moonriver.moonscan.io") - } + 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 - Chain::XDai => { + XDai => { ("https://blockscout.com/xdai/mainnet/api", "https://blockscout.com/xdai/mainnet") } - Chain::Chiado => { + Chiado => { ("https://blockscout.chiadochain.net/api", "https://blockscout.chiadochain.net") } - Chain::Sokol => { - ("https://blockscout.com/poa/sokol/api", "https://blockscout.com/poa/sokol") - } - Chain::Poa => { - ("https://blockscout.com/poa/core/api", "https://blockscout.com/poa/core") - } - Chain::Rsk => { - ("https://blockscout.com/rsk/mainnet/api", "https://blockscout.com/rsk/mainnet") - } - Chain::Oasis => ("https://scan.oasischain.io/api", "https://scan.oasischain.io/"), - Chain::Emerald => { + 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/") } - Chain::EmeraldTestnet => ( + EmeraldTestnet => ( "https://testnet.explorer.emerald.oasis.dev/api", "https://testnet.explorer.emerald.oasis.dev/", ), - Chain::Aurora => ("https://api.aurorascan.dev/api", "https://aurorascan.dev"), - Chain::AuroraTestnet => { + Aurora => ("https://api.aurorascan.dev/api", "https://aurorascan.dev"), + AuroraTestnet => { ("https://testnet.aurorascan.dev/api", "https://testnet.aurorascan.dev") } - Chain::Evmos => ("https://evm.evmos.org/api", "https://evm.evmos.org/"), - Chain::EvmosTestnet => ("https://evm.evmos.dev/api", "https://evm.evmos.dev/"), - Chain::Celo => { - ("https://explorer.celo.org/mainnet", "https://explorer.celo.org/mainnet/api") - } - Chain::CeloAlfajores => { + 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", "https://explorer.celo.org/mainnet/api"), + CeloAlfajores => { ("https://explorer.celo.org/alfajores", "https://explorer.celo.org/alfajores/api") } - Chain::CeloBaklava => { + CeloBaklava => { ("https://explorer.celo.org/baklava", "https://explorer.celo.org/baklava/api") } - Chain::AnvilHardhat | Chain::Dev | Chain::Morden | Chain::MoonbeamDev => { + AnvilHardhat | Dev | Morden | MoonbeamDev => { // this is explicitly exhaustive so we don't forget to add new urls when adding a // new chain return None @@ -443,34 +324,49 @@ impl Chain { Some(urls) } - /// Helper function for checking if a chainid corresponds to a legacy chainid - /// without eip1559 - pub fn is_legacy(&self) -> bool { - // TODO: Add other chains which do not support EIP1559. - matches!( - self, - Chain::Optimism | - Chain::OptimismGoerli | - Chain::OptimismKovan | - Chain::Fantom | - Chain::FantomTestnet | - Chain::BinanceSmartChain | - Chain::BinanceSmartChainTestnet | - Chain::Arbitrum | - Chain::ArbitrumTestnet | - Chain::ArbitrumGoerli | - Chain::Rsk | - Chain::Oasis | - Chain::Emerald | - Chain::EmeraldTestnet | - Chain::Celo | - Chain::CeloAlfajores | - Chain::CeloBaklava, - ) + /// Returns whether the chain implements EIP-1559 (with the type 2 EIP-2718 transaction type). + 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 | + Rsk | + Oasis | + Emerald | + EmeraldTestnet | + Celo | + CeloAlfajores | + CeloBaklava => true, + + // Known EIP-1559 chains + Mainnet | Goerli | Sepolia | Polygon | PolygonMumbai | Avalanche | AvalancheFuji => { + 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 => false, + } } } -#[test] -fn test_default_chain() { - assert_eq!(serde_json::to_string(&Chain::Mainnet).unwrap(), "\"mainnet\""); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_chain() { + assert_eq!(serde_json::to_string(&Chain::default()).unwrap(), "\"mainnet\""); + } }