Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat: add etherscan urls function #1582

Merged
merged 1 commit into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions ethers-core/src/types/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ use thiserror::Error;
#[error("Failed to parse chain: {0}")]
pub struct ParseChainError(String);

/// Enum for all known chains
///
/// When adding a new chain:
/// 1. add new variant
/// 2. update Display/FromStr impl
/// 3. add etherscan_keys if supported
#[repr(u64)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, EnumVariantNames)]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -92,6 +98,100 @@ impl Chain {

Some(Duration::from_millis(ms))
}

/// Returns the corresponding etherscan 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)> {
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 => {
("https://api-testnet.polygonscan.com/api", "https://mumbai.polygonscan.com")
}
Chain::Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"),
Chain::AvalancheFuji => {
("https://api-testnet.snowtrace.io/api", "https://testnet.snowtrace.io")
}
Chain::Optimism => {
("https://api-optimistic.etherscan.io/api", "https://optimistic.etherscan.io")
}
Chain::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 => {
("https://api-testnet.bscscan.com/api", "https://testnet.bscscan.com")
}
Chain::Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"),
Chain::ArbitrumTestnet => {
("https://api-testnet.arbiscan.io/api", "https://testnet.arbiscan.io")
}
Chain::Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"),
Chain::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")
}
// blockscout API is etherscan compatible
Chain::XDai => {
("https://blockscout.com/xdai/mainnet/api", "https://blockscout.com/xdai/mainnet")
}
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 => {
("https://explorer.emerald.oasis.dev/api", "https://explorer.emerald.oasis.dev/")
}
Chain::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 => {
("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::AnvilHardhat | Chain::Dev | Chain::Morden | Chain::MoonbeamDev => {
// this is explicitly exhaustive so we don't forget to add new urls when adding a
// new chain
return None
}
};

Some(urls)
}
}

impl fmt::Display for Chain {
Expand Down
88 changes: 4 additions & 84 deletions ethers-etherscan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,90 +231,10 @@ impl ClientBuilder {
) -> (reqwest::Result<Url>, reqwest::Result<Url>) {
(api.into_url(), url.into_url())
}

let (etherscan_api_url, etherscan_url) = match chain {
Chain::Mainnet => urls("https://api.etherscan.io/api", "https://etherscan.io"),
Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli | Chain::Sepolia => {
let chain_name = chain.to_string().to_lowercase();
urls(
format!("https://api-{}.etherscan.io/api", chain_name),
format!("https://{}.etherscan.io", chain_name),
)
}
Chain::Polygon => urls("https://api.polygonscan.com/api", "https://polygonscan.com"),
Chain::PolygonMumbai => {
urls("https://api-testnet.polygonscan.com/api", "https://mumbai.polygonscan.com")
}
Chain::Avalanche => urls("https://api.snowtrace.io/api", "https://snowtrace.io"),
Chain::AvalancheFuji => {
urls("https://api-testnet.snowtrace.io/api", "https://testnet.snowtrace.io")
}
Chain::Optimism => {
urls("https://api-optimistic.etherscan.io/api", "https://optimistic.etherscan.io")
}
Chain::OptimismKovan => urls(
"https://api-kovan-optimistic.etherscan.io/api",
"https://kovan-optimistic.etherscan.io",
),
Chain::Fantom => urls("https://api.ftmscan.com/api", "https://ftmscan.com"),
Chain::FantomTestnet => {
urls("https://api-testnet.ftmscan.com/api", "https://testnet.ftmscan.com")
}
Chain::BinanceSmartChain => urls("https://api.bscscan.com/api", "https://bscscan.com"),
Chain::BinanceSmartChainTestnet => {
urls("https://api-testnet.bscscan.com/api", "https://testnet.bscscan.com")
}
Chain::Arbitrum => urls("https://api.arbiscan.io/api", "https://arbiscan.io"),
Chain::ArbitrumTestnet => {
urls("https://api-testnet.arbiscan.io/api", "https://testnet.arbiscan.io")
}
Chain::Cronos => urls("https://api.cronoscan.com/api", "https://cronoscan.com"),
Chain::CronosTestnet => {
urls("https://api-testnet.cronoscan.com/api", "https://testnet.cronoscan.com")
}
Chain::Moonbeam => {
urls("https://api-moonbeam.moonscan.io/api", "https://moonbeam.moonscan.io/")
}
Chain::Moonbase => {
urls("https://api-moonbase.moonscan.io/api", "https://moonbase.moonscan.io/")
}
Chain::Moonriver => {
urls("https://api-moonriver.moonscan.io/api", "https://moonriver.moonscan.io")
}
// blockscout API is etherscan compatible
Chain::XDai => urls(
"https://blockscout.com/xdai/mainnet/api",
"https://blockscout.com/xdai/mainnet",
),
Chain::Sokol => {
urls("https://blockscout.com/poa/sokol/api", "https://blockscout.com/poa/sokol")
}
Chain::Poa => {
urls("https://blockscout.com/poa/core/api", "https://blockscout.com/poa/core")
}
Chain::Rsk => {
urls("https://blockscout.com/rsk/mainnet/api", "https://blockscout.com/rsk/mainnet")
}
Chain::Oasis => urls("https://scan.oasischain.io/api", "https://scan.oasischain.io/"),
Chain::Emerald => urls(
"https://explorer.emerald.oasis.dev/api",
"https://explorer.emerald.oasis.dev/",
),
Chain::EmeraldTestnet => urls(
"https://testnet.explorer.emerald.oasis.dev/api",
"https://testnet.explorer.emerald.oasis.dev/",
),
Chain::Aurora => urls("https://api.aurorascan.dev/api", "https://aurorascan.dev"),
Chain::AuroraTestnet => {
urls("https://testnet.aurorascan.dev/api", "https://testnet.aurorascan.dev")
}
Chain::AnvilHardhat | Chain::Dev => {
return Err(EtherscanError::LocalNetworksNotSupported)
}
Chain::Evmos => urls("https://evm.evmos.org/api", "https://evm.evmos.org/"),
Chain::EvmosTestnet => urls("https://evm.evmos.dev/api", "https://evm.evmos.dev/"),
chain => return Err(EtherscanError::ChainNotSupported(chain)),
};
let (etherscan_api_url, etherscan_url) = chain
.etherscan_urls()
.map(|(api, base)| urls(api, base))
.ok_or(EtherscanError::ChainNotSupported(chain))?;
self.with_api_url(etherscan_api_url?)?.with_url(etherscan_url?)
}

Expand Down