Skip to content

Commit 8567c2a

Browse files
authored
chore: extract fetch_abi_from_etherscan and use &Config (#11426)
1 parent 22bb635 commit 8567c2a

File tree

6 files changed

+55
-48
lines changed

6 files changed

+55
-48
lines changed

crates/cast/src/cmd/artifact.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
22
creation_code::{fetch_creation_code_from_etherscan, parse_code_output},
3-
interface::{fetch_abi_from_etherscan, load_abi_from_file},
3+
interface::load_abi_from_file,
44
};
55
use alloy_primitives::Address;
66
use alloy_provider::Provider;
@@ -10,10 +10,12 @@ use foundry_cli::{
1010
opts::{EtherscanOpts, RpcOpts},
1111
utils::{self, LoadConfig},
1212
};
13-
use foundry_common::fs;
13+
use foundry_common::{abi::fetch_abi_from_etherscan, fs};
1414
use serde_json::json;
1515
use std::path::PathBuf;
1616

17+
foundry_config::impl_figment_convert!(ArtifactArgs, etherscan, rpc);
18+
1719
/// CLI arguments for `cast artifact`.
1820
#[derive(Parser)]
1921
pub struct ArtifactArgs {
@@ -45,24 +47,25 @@ pub struct ArtifactArgs {
4547

4648
impl ArtifactArgs {
4749
pub async fn run(self) -> Result<()> {
48-
let Self { contract, mut etherscan, rpc, output: output_location, abi_path } = self;
50+
let mut config = self.load_config()?;
51+
52+
let Self { contract, output: output_location, abi_path, etherscan: _, rpc: _ } = self;
4953

50-
let config = rpc.load_config()?;
5154
let provider = utils::get_provider(&config)?;
5255
let chain = provider.get_chain_id().await?;
53-
etherscan.chain = Some(chain.into());
56+
config.chain = Some(chain.into());
5457

5558
let abi = if let Some(ref abi_path) = abi_path {
5659
load_abi_from_file(abi_path, None)?
5760
} else {
58-
fetch_abi_from_etherscan(contract, &etherscan).await?
61+
fetch_abi_from_etherscan(contract, &config).await?
5962
};
6063

6164
let (abi, _) = abi.first().ok_or_else(|| eyre::eyre!("No ABI found"))?;
6265

63-
let bytecode = fetch_creation_code_from_etherscan(contract, &etherscan, provider).await?;
66+
let bytecode = fetch_creation_code_from_etherscan(contract, &config, provider).await?;
6467
let bytecode =
65-
parse_code_output(bytecode, contract, &etherscan, abi_path.as_deref(), true, false)
68+
parse_code_output(bytecode, contract, &config, abi_path.as_deref(), true, false)
6669
.await?;
6770

6871
let artifact = json!({

crates/cast/src/cmd/constructor_args.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use super::{
2-
creation_code::fetch_creation_code_from_etherscan,
3-
interface::{fetch_abi_from_etherscan, load_abi_from_file},
4-
};
1+
use super::{creation_code::fetch_creation_code_from_etherscan, interface::load_abi_from_file};
52
use alloy_dyn_abi::DynSolType;
63
use alloy_primitives::{Address, Bytes};
74
use alloy_provider::Provider;
@@ -11,6 +8,10 @@ use foundry_cli::{
118
opts::{EtherscanOpts, RpcOpts},
129
utils::{self, LoadConfig},
1310
};
11+
use foundry_common::abi::fetch_abi_from_etherscan;
12+
use foundry_config::Config;
13+
14+
foundry_config::impl_figment_convert!(ConstructorArgsArgs, etherscan, rpc);
1415

1516
/// CLI arguments for `cast creation-args`.
1617
#[derive(Parser)]
@@ -32,16 +33,16 @@ pub struct ConstructorArgsArgs {
3233

3334
impl ConstructorArgsArgs {
3435
pub async fn run(self) -> Result<()> {
35-
let Self { contract, mut etherscan, rpc, abi_path } = self;
36+
let mut config = self.load_config()?;
37+
38+
let Self { contract, abi_path, etherscan: _, rpc: _ } = self;
3639

37-
let config = rpc.load_config()?;
3840
let provider = utils::get_provider(&config)?;
39-
let chain = provider.get_chain_id().await?;
40-
etherscan.chain = Some(chain.into());
41+
config.chain = Some(provider.get_chain_id().await?.into());
4142

42-
let bytecode = fetch_creation_code_from_etherscan(contract, &etherscan, provider).await?;
43+
let bytecode = fetch_creation_code_from_etherscan(contract, &config, provider).await?;
4344

44-
let args_arr = parse_constructor_args(bytecode, contract, &etherscan, abi_path).await?;
45+
let args_arr = parse_constructor_args(bytecode, contract, &config, abi_path).await?;
4546
for arg in args_arr {
4647
let _ = sh_println!("{arg}");
4748
}
@@ -54,13 +55,13 @@ impl ConstructorArgsArgs {
5455
async fn parse_constructor_args(
5556
bytecode: Bytes,
5657
contract: Address,
57-
etherscan: &EtherscanOpts,
58+
config: &Config,
5859
abi_path: Option<String>,
5960
) -> Result<Vec<String>> {
6061
let abi = if let Some(abi_path) = abi_path {
6162
load_abi_from_file(&abi_path, None)?
6263
} else {
63-
fetch_abi_from_etherscan(contract, etherscan).await?
64+
fetch_abi_from_etherscan(contract, config).await?
6465
};
6566

6667
let abi = abi.into_iter().next().ok_or_eyre("No ABI found.")?;

crates/cast/src/cmd/creation_code.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::interface::{fetch_abi_from_etherscan, load_abi_from_file};
1+
use super::interface::load_abi_from_file;
22
use crate::SimpleCast;
33
use alloy_consensus::Transaction;
44
use alloy_primitives::{Address, Bytes};
@@ -11,7 +11,10 @@ use foundry_cli::{
1111
opts::{EtherscanOpts, RpcOpts},
1212
utils::{self, LoadConfig},
1313
};
14-
use foundry_common::provider::RetryProvider;
14+
use foundry_common::{abi::fetch_abi_from_etherscan, provider::RetryProvider};
15+
use foundry_config::Config;
16+
17+
foundry_config::impl_figment_convert!(CreationCodeArgs, etherscan, rpc);
1518

1619
/// CLI arguments for `cast creation-code`.
1720
#[derive(Parser)]
@@ -45,20 +48,21 @@ pub struct CreationCodeArgs {
4548

4649
impl CreationCodeArgs {
4750
pub async fn run(self) -> Result<()> {
48-
let Self { contract, mut etherscan, rpc, disassemble, without_args, only_args, abi_path } =
51+
let mut config = self.load_config()?;
52+
53+
let Self { contract, disassemble, without_args, only_args, abi_path, etherscan: _, rpc: _ } =
4954
self;
5055

51-
let config = rpc.load_config()?;
5256
let provider = utils::get_provider(&config)?;
5357
let chain = provider.get_chain_id().await?;
54-
etherscan.chain = Some(chain.into());
58+
config.chain = Some(chain.into());
5559

56-
let bytecode = fetch_creation_code_from_etherscan(contract, &etherscan, provider).await?;
60+
let bytecode = fetch_creation_code_from_etherscan(contract, &config, provider).await?;
5761

5862
let bytecode = parse_code_output(
5963
bytecode,
6064
contract,
61-
&etherscan,
65+
&config,
6266
abi_path.as_deref(),
6367
without_args,
6468
only_args,
@@ -82,7 +86,7 @@ impl CreationCodeArgs {
8286
pub async fn parse_code_output(
8387
bytecode: Bytes,
8488
contract: Address,
85-
etherscan: &EtherscanOpts,
89+
config: &Config,
8690
abi_path: Option<&str>,
8791
without_args: bool,
8892
only_args: bool,
@@ -94,7 +98,7 @@ pub async fn parse_code_output(
9498
let abi = if let Some(abi_path) = abi_path {
9599
load_abi_from_file(abi_path, None)?
96100
} else {
97-
fetch_abi_from_etherscan(contract, etherscan).await?
101+
fetch_abi_from_etherscan(contract, config).await?
98102
};
99103

100104
let abi = abi.into_iter().next().ok_or_eyre("No ABI found.")?;
@@ -131,10 +135,9 @@ pub async fn parse_code_output(
131135
/// Fetches the creation code of a contract from Etherscan and RPC.
132136
pub async fn fetch_creation_code_from_etherscan(
133137
contract: Address,
134-
etherscan: &EtherscanOpts,
138+
config: &Config,
135139
provider: RetryProvider,
136140
) -> Result<Bytes> {
137-
let config = etherscan.load_config()?;
138141
let chain = config.chain.unwrap_or_default();
139142
let api_version = config.get_etherscan_api_version(Some(chain));
140143
let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default();

crates/cast/src/cmd/interface.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use alloy_json_abi::{ContractObject, JsonAbi};
22
use alloy_primitives::Address;
33
use clap::Parser;
44
use eyre::{Context, Result};
5-
use foundry_block_explorers::Client;
65
use foundry_cli::{opts::EtherscanOpts, utils::LoadConfig};
76
use foundry_common::{
87
ContractsByArtifact,
8+
abi::fetch_abi_from_etherscan,
99
compile::{PathOrContractInfo, ProjectCompiler},
1010
find_target_path, fs, shell,
1111
};
@@ -65,7 +65,7 @@ impl InterfaceArgs {
6565
load_abi_from_file(&contract, name)?
6666
} else {
6767
match Address::from_str(&contract) {
68-
Ok(address) => fetch_abi_from_etherscan(address, &etherscan).await?,
68+
Ok(address) => fetch_abi_from_etherscan(address, &etherscan.load_config()?).await?,
6969
Err(_) => load_abi_from_artifact(&contract)?,
7070
}
7171
};
@@ -137,20 +137,6 @@ fn load_abi_from_artifact(path_or_contract: &str) -> Result<Vec<(JsonAbi, String
137137
Ok(vec![(abi.clone(), contract.name().unwrap_or(name).to_string())])
138138
}
139139

140-
/// Fetches the ABI of a contract from Etherscan.
141-
pub async fn fetch_abi_from_etherscan(
142-
address: Address,
143-
etherscan: &EtherscanOpts,
144-
) -> Result<Vec<(JsonAbi, String)>> {
145-
let config = etherscan.load_config()?;
146-
let chain = config.chain.unwrap_or_default();
147-
let api_version = config.get_etherscan_api_version(Some(chain));
148-
let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
149-
let client = Client::new_with_api_version(chain, api_key, api_version)?;
150-
let source = client.contract_source_code(address).await?;
151-
source.items.into_iter().map(|item| Ok((item.abi()?, item.contract_name))).collect()
152-
}
153-
154140
/// Converts a vector of tuples containing the ABI and contract name into a vector of
155141
/// `InterfaceSource` objects.
156142
fn get_interfaces(abis: Vec<(JsonAbi, String)>) -> Result<Vec<InterfaceSource>> {

crates/common/src/abi.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! ABI related helper functions.
22
33
use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt};
4-
use alloy_json_abi::{Error, Event, Function, Param};
4+
use alloy_json_abi::{Error, Event, Function, JsonAbi, Param};
55
use alloy_primitives::{Address, LogData, hex};
66
use eyre::{Context, ContextCompat, Result};
77
use foundry_block_explorers::{
@@ -137,6 +137,19 @@ pub fn get_indexed_event(mut event: Event, raw_log: &LogData) -> Event {
137137
event
138138
}
139139

140+
/// Fetches the ABI of a contract from Etherscan.
141+
pub async fn fetch_abi_from_etherscan(
142+
address: Address,
143+
config: &foundry_config::Config,
144+
) -> Result<Vec<(JsonAbi, String)>> {
145+
let chain = config.chain.unwrap_or_default();
146+
let api_version = config.get_etherscan_api_version(Some(chain));
147+
let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default();
148+
let client = Client::new_with_api_version(chain, api_key, api_version)?;
149+
let source = client.contract_source_code(address).await?;
150+
source.items.into_iter().map(|item| Ok((item.abi()?, item.contract_name))).collect()
151+
}
152+
140153
/// Given a function name, address, and args, tries to parse it as a `Function` by fetching the
141154
/// abi from etherscan. If the address is a proxy, fetches the ABI of the implementation contract.
142155
pub async fn get_func_etherscan(

crates/common/src/contracts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ pub fn find_matching_contract_artifact(
566566
Ok(artifact.clone())
567567
}
568568
}
569+
569570
#[cfg(test)]
570571
mod tests {
571572
use super::*;

0 commit comments

Comments
 (0)