Skip to content
Open
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
5 changes: 4 additions & 1 deletion crates/cast/src/cmd/batch_mktx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ impl BatchMakeTxArgs {

// Get chain for parsing function args
let chain = utils::get_chain(config.chain, &provider).await?;
let etherscan_api_key = config.get_etherscan_api_key(Some(chain));
let etherscan_config = config.get_etherscan_config_with_chain(Some(chain)).ok().flatten();
let etherscan_api_key = etherscan_config.as_ref().map(|c| c.key.clone());
let etherscan_api_url = etherscan_config.map(|c| c.api_url);

// Build Vec<Call> from specs
let mut tempo_calls = Vec::with_capacity(call_specs.len());
Expand All @@ -88,6 +90,7 @@ impl BatchMakeTxArgs {
chain,
&provider,
etherscan_api_key.as_deref(),
etherscan_api_url.as_deref(),
)
.await
.map_err(|e| eyre!("Failed to encode call {}: {}", i + 1, e))?;
Expand Down
5 changes: 4 additions & 1 deletion crates/cast/src/cmd/batch_send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ impl BatchSendArgs {

// Get chain for parsing function args
let chain = utils::get_chain(config.chain, &provider).await?;
let etherscan_api_key = config.get_etherscan_api_key(Some(chain));
let etherscan_config = config.get_etherscan_config_with_chain(Some(chain)).ok().flatten();
let etherscan_api_key = etherscan_config.as_ref().map(|c| c.key.clone());
let etherscan_api_url = etherscan_config.map(|c| c.api_url);

// Build Vec<Call> from specs
let mut tempo_calls = Vec::with_capacity(call_specs.len());
Expand All @@ -89,6 +91,7 @@ impl BatchSendArgs {
chain,
&provider,
etherscan_api_key.as_deref(),
etherscan_api_url.as_deref(),
)
.await
.map_err(|e| eyre!("Failed to encode call {}: {}", i + 1, e))?;
Expand Down
7 changes: 4 additions & 3 deletions crates/cast/src/cmd/creation_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use alloy_provider::{Provider, RootProvider, ext::TraceApi};
use alloy_rpc_types::trace::parity::{Action, CreateAction, CreateOutput, TraceOutput};
use clap::Parser;
use eyre::{OptionExt, Result, eyre};
use foundry_block_explorers::Client;
use foundry_cli::{
opts::{EtherscanOpts, RpcOpts},
utils::{self, LoadConfig, fetch_abi_from_etherscan},
Expand Down Expand Up @@ -139,8 +138,10 @@ pub async fn fetch_creation_code_from_etherscan(
provider: RootProvider<AnyNetwork>,
) -> Result<Bytes> {
let chain = config.chain.unwrap_or_default();
let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
let client = Client::new(chain, api_key)?;
let client = config
.get_etherscan_config_with_chain(Some(chain))?
.ok_or_else(|| eyre!("No Etherscan API key configured for chain {chain}"))?
.into_client()?;
let creation_data = client.contract_creation_data(contract).await?;
let creation_tx_hash = creation_data.transaction_hash;
let tx_data = provider.get_transaction_by_hash(creation_tx_hash).await?;
Expand Down
15 changes: 10 additions & 5 deletions crates/cast/src/cmd/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use alloy_rpc_types::BlockId;
use clap::Parser;
use comfy_table::{Cell, Table, modifiers::UTF8_ROUND_CORNERS, presets::ASCII_MARKDOWN};
use eyre::Result;
use foundry_block_explorers::Client;
use foundry_cli::{
opts::{BuildOpts, EtherscanOpts, RpcOpts},
utils,
Expand Down Expand Up @@ -141,10 +140,16 @@ impl StorageArgs {
}

let chain = utils::get_chain(config.chain, &provider).await?;
let api_key = config.get_etherscan_api_key(Some(chain)).or_else(|| self.etherscan.key()).ok_or_else(|| {
eyre::eyre!("You must provide an Etherscan API key if you're fetching a remote contract's storage.")
})?;
let client = Client::new(chain, api_key)?;
let etherscan_api_key = self.etherscan.key();
let client = match config.get_etherscan_config_with_chain(Some(chain))? {
Some(etherscan_config) => etherscan_config.into_client()?,
None => {
let api_key = etherscan_api_key.ok_or_else(|| {
eyre::eyre!("You must provide an Etherscan API key if you're fetching a remote contract's storage.")
})?;
foundry_block_explorers::Client::new(chain, api_key)?
}
};
let source = if let Some(proxy) = self.proxy {
find_source(client, proxy.resolve(&provider).await?).await?
} else {
Expand Down
9 changes: 8 additions & 1 deletion crates/cast/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ pub struct CastTxBuilder<N: Network, P, S> {
auth: Vec<CliAuthorizationList>,
chain: Chain,
etherscan_api_key: Option<String>,
etherscan_api_url: Option<String>,
access_list: Option<Option<AccessList>>,
state: S,
}
Expand All @@ -359,7 +360,9 @@ where
let mut tx = N::TransactionRequest::default();

let chain = utils::get_chain(config.chain, &provider).await?;
let etherscan_api_key = config.get_etherscan_api_key(Some(chain));
let etherscan_config = config.get_etherscan_config_with_chain(Some(chain)).ok().flatten();
let etherscan_api_key = etherscan_config.as_ref().map(|c| c.key.clone());
let etherscan_api_url = etherscan_config.map(|c| c.api_url);
// mark it as legacy if requested or the chain is legacy and no 7702 is provided.
let legacy = tx_opts.legacy || (chain.is_legacy() && tx_opts.auth.is_empty());

Expand All @@ -375,6 +378,7 @@ where
fill: true,
chain,
etherscan_api_key,
etherscan_api_url,
auth: tx_opts.auth,
access_list: tx_opts.access_list,
state: InitState,
Expand All @@ -393,6 +397,7 @@ where
fill: self.fill,
chain: self.chain,
etherscan_api_key: self.etherscan_api_key,
etherscan_api_url: self.etherscan_api_url,
auth: self.auth,
access_list: self.access_list,
state: ToState { to },
Expand Down Expand Up @@ -421,6 +426,7 @@ where
self.chain,
&self.provider,
self.etherscan_api_key.as_deref(),
self.etherscan_api_url.as_deref(),
)
.await?
} else {
Expand Down Expand Up @@ -454,6 +460,7 @@ where
fill: self.fill,
chain: self.chain,
etherscan_api_key: self.etherscan_api_key,
etherscan_api_url: self.etherscan_api_url,
auth: self.auth,
access_list: self.access_list,
state: InputState { kind: self.state.to.into(), input, func },
Expand Down
1 change: 0 additions & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ repository.workspace = true
workspace = true

[dependencies]
foundry-block-explorers.workspace = true
foundry-cli-markdown.workspace = true
foundry-common.workspace = true
foundry-config.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion crates/cli/src/utils/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub async fn parse_function_args<N: Network, P: Provider<N>>(
chain: Chain,
provider: &P,
etherscan_api_key: Option<&str>,
etherscan_api_url: Option<&str>,
) -> Result<(Vec<u8>, Option<Function>)> {
if sig.trim().is_empty() {
eyre::bail!("Function signature or calldata must be provided.")
Expand All @@ -60,7 +61,7 @@ pub async fn parse_function_args<N: Network, P: Provider<N>>(
"Function signature does not contain parentheses. If you wish to fetch function data from Etherscan, please provide an API key.",
)?;
let to = to.ok_or_eyre("A 'to' address must be provided to fetch function data.")?;
get_func_etherscan(sig, to, &args, chain, etherscan_api_key).await?
get_func_etherscan(sig, to, &args, chain, etherscan_api_key, etherscan_api_url).await?
};

if to.is_none() {
Expand Down
6 changes: 4 additions & 2 deletions crates/cli/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,10 @@ pub async fn fetch_abi_from_etherscan(
config: &foundry_config::Config,
) -> Result<Vec<(JsonAbi, String)>> {
let chain = config.chain.unwrap_or_default();
let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
let client = foundry_block_explorers::Client::new(chain, api_key)?;
let client = config
.get_etherscan_config_with_chain(Some(chain))?
.ok_or_else(|| eyre::eyre!("No Etherscan API key configured for chain {chain}"))?
.into_client()?;
let source = client.contract_source_code(address).await?;
source.items.into_iter().map(|item| Ok((item.abi()?, item.contract_name))).collect()
}
Expand Down
7 changes: 6 additions & 1 deletion crates/common/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,13 @@ pub async fn get_func_etherscan(
args: &[String],
chain: Chain,
etherscan_api_key: &str,
etherscan_api_url: Option<&str>,
) -> Result<Function> {
let client = Client::new(chain, etherscan_api_key)?;
let client = if let Some(api_url) = etherscan_api_url {
Client::builder().with_api_key(etherscan_api_key).with_api_url(api_url)?.build()?
} else {
Client::new(chain, etherscan_api_key)?
};
let source = find_source(client, contract).await?;
let metadata = source.items.first().wrap_err("etherscan returned empty metadata")?;

Expand Down
19 changes: 12 additions & 7 deletions crates/forge/src/cmd/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,12 @@ impl CloneArgs {
// step 1. get the metadata from client based on source type
let (meta, explorer_name, sourcify_client) = match source {
SourceExplorer::Etherscan => {
let etherscan_api_key =
config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
let client = Client::new(chain, etherscan_api_key.clone())?;
let client = config
.get_etherscan_config_with_chain(Some(chain))?
.ok_or_else(|| {
eyre::eyre!("No Etherscan API key configured for chain {chain}")
})?
.into_client()?;
sh_println!("Downloading the source code of {address} from Etherscan...")?;
let meta = Self::collect_metadata_from_client(address, &client).await?;
(meta, "Etherscan", None)
Expand Down Expand Up @@ -177,13 +180,15 @@ impl CloneArgs {

match source {
SourceExplorer::Etherscan => {
let etherscan_api_key =
config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
let client = Client::new(chain, etherscan_api_key.clone())?;
if etherscan_api_key.is_empty() {
let etherscan_config =
config.get_etherscan_config_with_chain(Some(chain))?.ok_or_else(|| {
eyre::eyre!("No Etherscan API key configured for chain {chain}")
})?;
if etherscan_config.key.is_empty() {
sh_warn!("Waiting for 5 seconds to avoid rate limit...")?;
tokio::time::sleep(Duration::from_secs(5)).await;
}
let client = etherscan_config.into_client()?;
Self::collect_compilation_metadata(&meta, chain, address, &root, &client).await?;
}
SourceExplorer::Sourcify => {
Expand Down
Loading