Skip to content

Commit

Permalink
feat(cast run): add --etherscan-api-key to resolve contract names (
Browse files Browse the repository at this point in the history
#9295)

* fix(cast run): decode traces for non mainnet

* Add test

* Changes after review: use EtherscanOpts, remove short -e from evm_version
Simplify test to avoid rate limiting.
  • Loading branch information
grandizzy authored Nov 11, 2024
1 parent b7fe62e commit 8c01706
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 9 deletions.
11 changes: 9 additions & 2 deletions crates/cast/bin/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cast::revm::primitives::EnvWithHandlerCfg;
use clap::Parser;
use eyre::{Result, WrapErr};
use foundry_cli::{
opts::RpcOpts,
opts::{EtherscanOpts, RpcOpts},
utils::{handle_traces, init_progress, TraceResult},
};
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
Expand Down Expand Up @@ -58,13 +58,16 @@ pub struct RunArgs {
#[arg(long, short)]
label: Vec<String>,

#[command(flatten)]
etherscan: EtherscanOpts,

#[command(flatten)]
rpc: RpcOpts,

/// The EVM version to use.
///
/// Overrides the version specified in the config.
#[arg(long, short)]
#[arg(long)]
evm_version: Option<EvmVersion>,

/// Sets the number of assumed available compute units per second for this provider
Expand Down Expand Up @@ -269,6 +272,10 @@ impl figment::Provider for RunArgs {
map.insert("alphanet".into(), self.alphanet.into());
}

if let Some(api_key) = &self.etherscan.key {
map.insert("etherscan_api_key".into(), api_key.as_str().into());
}

if let Some(evm_version) = self.evm_version {
map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
}
Expand Down
29 changes: 27 additions & 2 deletions crates/cast/tests/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use anvil::{EthereumHardfork, NodeConfig};
use foundry_test_utils::{
casttest, file,
rpc::{
next_http_rpc_endpoint, next_mainnet_etherscan_api_key, next_rpc_endpoint,
next_ws_rpc_endpoint,
next_etherscan_api_key, next_http_rpc_endpoint, next_mainnet_etherscan_api_key,
next_rpc_endpoint, next_ws_rpc_endpoint,
},
str,
util::OutputExt,
Expand Down Expand Up @@ -1503,3 +1503,28 @@ casttest!(fetch_constructor_args_from_etherscan, |_prj, cmd| {
"#]]);
});

// <https://github.com/foundry-rs/foundry/issues/3473>
casttest!(test_non_mainnet_traces, |prj, cmd| {
prj.clear();
cmd.args([
"run",
"0xa003e419e2d7502269eb5eda56947b580120e00abfd5b5460d08f8af44a0c24f",
"--rpc-url",
next_rpc_endpoint(NamedChain::Optimism).as_str(),
"--etherscan-api-key",
next_etherscan_api_key(NamedChain::Optimism).as_str(),
])
.assert_success()
.stdout_eq(str![[r#"
Executing previous transactions from the block.
Traces:
[33841] FiatTokenProxy::fallback(0x111111125421cA6dc452d289314280a0f8842A65, 164054805 [1.64e8])
├─ [26673] FiatTokenV2_2::approve(0x111111125421cA6dc452d289314280a0f8842A65, 164054805 [1.64e8]) [delegatecall]
│ ├─ emit Approval(owner: 0x9a95Af47C51562acfb2107F44d7967DF253197df, spender: 0x111111125421cA6dc452d289314280a0f8842A65, value: 164054805 [1.64e8])
│ └─ ← [Return] true
└─ ← [Return] true
...
"#]]);
});
12 changes: 8 additions & 4 deletions crates/evm/core/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,6 @@ impl EvmOpts {
/// Returns the chain ID from the RPC, if any.
pub async fn get_remote_chain_id(&self) -> Option<Chain> {
if let Some(ref url) = self.fork_url {
if url.contains("mainnet") {
trace!(?url, "auto detected mainnet chain");
return Some(Chain::mainnet());
}
trace!(?url, "retrieving chain via eth_chainId");
let provider = ProviderBuilder::new(url.as_str())
.compute_units_per_second(self.get_compute_units_per_second())
Expand All @@ -206,6 +202,14 @@ impl EvmOpts {
if let Ok(id) = provider.get_chain_id().await {
return Some(Chain::from(id));
}

// Provider URLs could be of the format `{CHAIN_IDENTIFIER}-mainnet`
// (e.g. Alchemy `opt-mainnet`, `arb-mainnet`), fallback to this method only
// if we're not able to retrieve chain id from `RetryProvider`.
if url.contains("mainnet") {
trace!(?url, "auto detected mainnet chain");
return Some(Chain::mainnet());
}
}

None
Expand Down
16 changes: 15 additions & 1 deletion crates/test-utils/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! RPC API keys utilities.
use foundry_config::NamedChain;
use foundry_config::{NamedChain, NamedChain::Optimism};
use rand::seq::SliceRandom;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Expand Down Expand Up @@ -75,6 +75,10 @@ static ETHERSCAN_MAINNET_KEYS: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
keys
});

// List of etherscan keys for Optimism.
static ETHERSCAN_OPTIMISM_KEYS: LazyLock<Vec<&'static str>> =
LazyLock::new(|| vec!["JQNGFHINKS1W7Y5FRXU4SPBYF43J3NYK46"]);

/// Returns the next index to use.
fn next() -> usize {
static NEXT_INDEX: AtomicUsize = AtomicUsize::new(0);
Expand Down Expand Up @@ -127,6 +131,16 @@ pub fn next_mainnet_etherscan_api_key() -> String {
ETHERSCAN_MAINNET_KEYS[idx].to_string()
}

/// Returns the next etherscan api key for given chain.
pub fn next_etherscan_api_key(chain: NamedChain) -> String {
let keys = match chain {
Optimism => &ETHERSCAN_OPTIMISM_KEYS,
_ => &ETHERSCAN_MAINNET_KEYS,
};
let idx = next() % keys.len();
keys[idx].to_string()
}

fn next_url(is_ws: bool, chain: NamedChain) -> String {
use NamedChain::*;

Expand Down

0 comments on commit 8c01706

Please sign in to comment.