Skip to content

Commit c5592d5

Browse files
committed
Simplify Etherscan provider option, default v2
1 parent 08f6305 commit c5592d5

File tree

9 files changed

+84
-43
lines changed

9 files changed

+84
-43
lines changed

crates/cli/src/opts/rpc.rs

+10
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ pub struct EtherscanOpts {
114114
#[serde(rename = "etherscan_api_key", skip_serializing_if = "Option::is_none")]
115115
pub key: Option<String>,
116116

117+
/// The Etherscan API version.
118+
#[arg(short, long = "etherscan-api-version", env = "ETHERSCAN_API_VERSION")]
119+
#[serde(rename = "etherscan_api_version", skip_serializing_if = "Option::is_none")]
120+
pub api_version: Option<String>,
121+
117122
/// The chain name or EIP-155 chain ID.
118123
#[arg(
119124
short,
@@ -154,6 +159,11 @@ impl EtherscanOpts {
154159
if let Some(key) = self.key() {
155160
dict.insert("etherscan_api_key".into(), key.into());
156161
}
162+
163+
if let Some(api_version) = &self.api_version {
164+
dict.insert("etherscan_api_version".into(), api_version.to_string().into());
165+
}
166+
157167
if let Some(chain) = self.chain {
158168
if let ChainKind::Id(id) = chain.kind() {
159169
dict.insert("chain_id".into(), (*id).into());

crates/config/src/etherscan.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub enum EtherscanConfigError {
5858

5959
#[error("At least one of `url` or `chain` must be present{0}")]
6060
MissingUrlOrChain(String),
61+
62+
#[error("Invalid Etherscan API version {0}")]
63+
InvalidApiVersion(String),
6164
}
6265

6366
/// Container type for Etherscan API keys and URLs.
@@ -194,7 +197,7 @@ impl EtherscanConfig {
194197
) -> Result<ResolvedEtherscanConfig, EtherscanConfigError> {
195198
let Self { chain, mut url, key, api_version } = self;
196199

197-
let api_version_string = api_version.map(|v| v.to_string());
200+
let api_version = api_version.unwrap_or(EtherscanApiVersion::V2);
198201

199202
if let Some(url) = &mut url {
200203
*url = interpolate(url)?;
@@ -225,12 +228,12 @@ impl EtherscanConfig {
225228
match (chain, url) {
226229
(Some(chain), Some(api_url)) => Ok(ResolvedEtherscanConfig {
227230
api_url,
228-
api_version: api_version_string,
231+
api_version,
229232
browser_url: chain.etherscan_urls().map(|(_, url)| url.to_string()),
230233
key,
231234
chain: Some(chain),
232235
}),
233-
(Some(chain), None) => ResolvedEtherscanConfig::create(key, chain, api_version_string)
236+
(Some(chain), None) => ResolvedEtherscanConfig::create(key, chain, api_version)
234237
.ok_or_else(|| {
235238
let msg = alias.map(|a| format!(" `{a}`")).unwrap_or_default();
236239
EtherscanConfigError::UnknownChain(msg, chain)
@@ -240,7 +243,7 @@ impl EtherscanConfig {
240243
browser_url: None,
241244
key,
242245
chain: None,
243-
api_version: api_version_string,
246+
api_version,
244247
}),
245248
(None, None) => {
246249
let msg = alias
@@ -264,8 +267,8 @@ pub struct ResolvedEtherscanConfig {
264267
/// The resolved API key.
265268
pub key: String,
266269
/// Etherscan API Version.
267-
#[serde(default, skip_serializing_if = "Option::is_none")]
268-
pub api_version: Option<String>,
270+
#[serde(default)]
271+
pub api_version: EtherscanApiVersion,
269272
/// The chain name or EIP-155 chain ID.
270273
#[serde(default, skip_serializing_if = "Option::is_none")]
271274
pub chain: Option<Chain>,
@@ -276,13 +279,13 @@ impl ResolvedEtherscanConfig {
276279
pub fn create(
277280
api_key: impl Into<String>,
278281
chain: impl Into<Chain>,
279-
api_version: Option<impl Into<String>>,
282+
api_version: EtherscanApiVersion,
280283
) -> Option<Self> {
281284
let chain = chain.into();
282285
let (api_url, browser_url) = chain.etherscan_urls()?;
283286
Some(Self {
284287
api_url: api_url.to_string(),
285-
api_version: api_version.map(|v| v.into()),
288+
api_version,
286289
browser_url: Some(browser_url.to_string()),
287290
key: api_key.into(),
288291
chain: Some(chain),
@@ -330,14 +333,13 @@ impl ResolvedEtherscanConfig {
330333
}
331334

332335
let api_url = into_url(&api_url)?;
333-
let parsed_api_version = api_version.map(EtherscanApiVersion::try_from).transpose()?;
334336
let client = reqwest::Client::builder()
335337
.user_agent(ETHERSCAN_USER_AGENT)
336338
.tls_built_in_root_certs(api_url.scheme() == "https")
337339
.build()?;
338340
foundry_block_explorers::Client::builder()
339341
.with_client(client)
340-
.with_api_version(parsed_api_version.unwrap_or_default())
342+
.with_api_version(api_version)
341343
.with_api_key(api_key)
342344
.with_api_url(api_url)?
343345
// the browser url is not used/required by the client so we can simply set the
@@ -452,7 +454,7 @@ mod tests {
452454
let mut resolved = configs.resolved();
453455
let config = resolved.remove("mainnet").unwrap().unwrap();
454456
// None version = None
455-
assert_eq!(config.api_version, None);
457+
assert_eq!(config.api_version, EtherscanApiVersion::V2);
456458
let client = config.into_client().unwrap();
457459
assert_eq!(*client.etherscan_api_version(), EtherscanApiVersion::V2);
458460
}
@@ -472,7 +474,7 @@ mod tests {
472474

473475
let mut resolved = configs.resolved();
474476
let config = resolved.remove("mainnet").unwrap().unwrap();
475-
assert_eq!(config.api_version, Some("v1".to_string()));
477+
assert_eq!(config.api_version, EtherscanApiVersion::V1);
476478
let client = config.into_client().unwrap();
477479
assert_eq!(*client.etherscan_api_version(), EtherscanApiVersion::V1);
478480
}

crates/config/src/lib.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub mod fix;
9696
// reexport so cli types can implement `figment::Provider` to easily merge compiler arguments
9797
pub use alloy_chains::{Chain, NamedChain};
9898
pub use figment;
99+
use foundry_block_explorers::EtherscanApiVersion;
99100

100101
pub mod providers;
101102
pub use providers::Remappings;
@@ -120,6 +121,7 @@ mod bind_json;
120121
use bind_json::BindJsonConfig;
121122

122123
mod compilation;
124+
use crate::etherscan::EtherscanConfigError::InvalidApiVersion;
123125
pub use compilation::{CompilationRestrictions, SettingsOverrides};
124126

125127
/// Foundry configuration
@@ -281,6 +283,8 @@ pub struct Config {
281283
pub eth_rpc_headers: Option<Vec<String>>,
282284
/// etherscan API key, or alias for an `EtherscanConfig` in `etherscan` table
283285
pub etherscan_api_key: Option<String>,
286+
/// etherscan API version
287+
pub etherscan_api_version: Option<String>,
284288
/// Multiple etherscan api configs and their aliases
285289
#[serde(default, skip_serializing_if = "EtherscanConfigs::is_empty")]
286290
pub etherscan: EtherscanConfigs,
@@ -1406,7 +1410,14 @@ impl Config {
14061410
// etherscan fallback via API key
14071411
if let Some(key) = self.etherscan_api_key.as_ref() {
14081412
let chain = chain.or(self.chain).unwrap_or_default();
1409-
return Ok(ResolvedEtherscanConfig::create(key, chain, None::<String>));
1413+
1414+
let api_version = match self.etherscan_api_version.as_ref() {
1415+
Some(api_version) => EtherscanApiVersion::try_from(api_version.to_string())
1416+
.map_err(|_| InvalidApiVersion(api_version.to_string()))?,
1417+
None => EtherscanApiVersion::V2,
1418+
};
1419+
1420+
return Ok(ResolvedEtherscanConfig::create(key, chain, api_version));
14101421
}
14111422

14121423
Ok(None)
@@ -2369,6 +2380,7 @@ impl Default for Config {
23692380
eth_rpc_timeout: None,
23702381
eth_rpc_headers: None,
23712382
etherscan_api_key: None,
2383+
etherscan_api_version: None,
23722384
verbosity: 0,
23732385
remappings: vec![],
23742386
auto_detect_remappings: true,
@@ -3081,7 +3093,7 @@ mod tests {
30813093
api_url: mainnet_urls.0.to_string(),
30823094
chain: Some(NamedChain::Mainnet.into()),
30833095
browser_url: Some(mainnet_urls.1.to_string()),
3084-
api_version: None,
3096+
api_version: EtherscanApiVersion::V2,
30853097
key: "FX42Z3BBJJEWXWGYV2X1CIPRSCN".to_string(),
30863098
}
30873099
),
@@ -3091,7 +3103,7 @@ mod tests {
30913103
api_url: mb_urls.0.to_string(),
30923104
chain: Some(Moonbeam.into()),
30933105
browser_url: Some(mb_urls.1.to_string()),
3094-
api_version: None,
3106+
api_version: EtherscanApiVersion::V2,
30953107
key: "123456789".to_string(),
30963108
}
30973109
),
@@ -3136,7 +3148,7 @@ mod tests {
31363148
api_url: mainnet_urls.0.to_string(),
31373149
chain: Some(NamedChain::Mainnet.into()),
31383150
browser_url: Some(mainnet_urls.1.to_string()),
3139-
api_version: Some("v2".to_string()),
3151+
api_version: EtherscanApiVersion::V2,
31403152
key: "FX42Z3BBJJEWXWGYV2X1CIPRSCN".to_string(),
31413153
}
31423154
),
@@ -3146,7 +3158,7 @@ mod tests {
31463158
api_url: mb_urls.0.to_string(),
31473159
chain: Some(Moonbeam.into()),
31483160
browser_url: Some(mb_urls.1.to_string()),
3149-
api_version: Some("v1".to_string()),
3161+
api_version: EtherscanApiVersion::V1,
31503162
key: "123456789".to_string(),
31513163
}
31523164
),

crates/forge/src/cmd/create.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ impl CreateArgs {
228228
num_of_optimizations: None,
229229
etherscan: EtherscanOpts {
230230
key: self.eth.etherscan.key.clone(),
231+
api_version: self.eth.etherscan.api_version.clone(),
231232
chain: Some(chain.into()),
232233
},
233234
rpc: Default::default(),
@@ -416,7 +417,11 @@ impl CreateArgs {
416417
constructor_args,
417418
constructor_args_path: None,
418419
num_of_optimizations,
419-
etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) },
420+
etherscan: EtherscanOpts {
421+
key: self.eth.etherscan.key(),
422+
api_version: self.eth.etherscan.api_version.clone(),
423+
chain: Some(chain.into()),
424+
},
420425
rpc: Default::default(),
421426
flatten: false,
422427
force: false,

crates/forge/tests/cli/config.rs

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ forgetest!(can_extract_config_values, |prj, cmd| {
119119
eth_rpc_timeout: None,
120120
eth_rpc_headers: None,
121121
etherscan_api_key: None,
122+
etherscan_api_version: None,
122123
etherscan: Default::default(),
123124
verbosity: 4,
124125
remappings: vec![Remapping::from_str("forge-std/=lib/forge-std/").unwrap().into()],

crates/verify/src/bytecode.rs

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ impl VerifyBytecodeArgs {
141141
&self.verifier.verifier,
142142
self.verifier.verifier_url.as_deref(),
143143
self.etherscan.key().as_deref(),
144+
self.verifier.verifier_api_version.as_deref(),
144145
&config,
145146
)?;
146147

crates/verify/src/etherscan/mod.rs

+28-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use foundry_block_explorers::{
1212
errors::EtherscanError,
1313
utils::lookup_compiler_version,
1414
verify::{CodeFormat, VerifyContract},
15-
Client,
15+
Client, EtherscanApiVersion,
1616
};
1717
use foundry_cli::utils::{get_provider, read_constructor_args_file, LoadConfig};
1818
use foundry_common::{abi::encode_function_args, retry::RetryError};
@@ -155,6 +155,7 @@ impl VerificationProvider for EtherscanVerificationProvider {
155155
&args.verifier.verifier,
156156
args.verifier.verifier_url.as_deref(),
157157
args.etherscan.key().as_deref(),
158+
args.verifier.verifier_api_version.as_deref(),
158159
&config,
159160
)?;
160161
args.retry
@@ -224,6 +225,7 @@ impl EtherscanVerificationProvider {
224225
&args.verifier.verifier,
225226
args.verifier.verifier_url.as_deref(),
226227
args.etherscan.key().as_deref(),
228+
args.verifier.verifier_api_version.as_deref(),
227229
&config,
228230
)?;
229231
let verify_args = self.create_verify_request(args, context).await?;
@@ -256,13 +258,28 @@ impl EtherscanVerificationProvider {
256258
verifier_type: &VerificationProviderType,
257259
verifier_url: Option<&str>,
258260
etherscan_key: Option<&str>,
261+
verifier_api_version: Option<&str>,
259262
config: &Config,
260263
) -> Result<Client> {
261264
let etherscan_config = config.get_etherscan_config_with_chain(Some(chain))?;
262265

266+
let api_version = match verifier_api_version {
267+
Some(api_version) => EtherscanApiVersion::try_from(api_version.to_string())?,
268+
None => {
269+
if verifier_type.is_etherscan() {
270+
etherscan_config
271+
.as_ref()
272+
.map(|c| c.api_version)
273+
.unwrap_or(EtherscanApiVersion::V2)
274+
} else {
275+
EtherscanApiVersion::V1
276+
}
277+
}
278+
};
279+
263280
let etherscan_api_url = verifier_url
264281
.or_else(|| {
265-
if verifier_type.is_etherscan_v2() {
282+
if api_version == EtherscanApiVersion::V2 {
266283
None
267284
} else {
268285
etherscan_config.as_ref().map(|c| c.api_url.as_str())
@@ -277,13 +294,8 @@ impl EtherscanVerificationProvider {
277294
.or_else(|| chain.etherscan_urls().map(|(_, url)| url));
278295
let etherscan_key =
279296
etherscan_key.or_else(|| etherscan_config.as_ref().map(|c| c.key.as_str()));
280-
let etherscan_api_version = if verifier_type.is_etherscan_v2() {
281-
foundry_block_explorers::EtherscanApiVersion::V2
282-
} else {
283-
foundry_block_explorers::EtherscanApiVersion::V1
284-
};
285297

286-
let mut builder = Client::builder().with_api_version(etherscan_api_version);
298+
let mut builder = Client::builder().with_api_version(api_version);
287299

288300
builder = if let Some(api_url) = api_url {
289301
// we don't want any trailing slashes because this can cause cloudflare issues: <https://github.com/foundry-rs/foundry/pull/6079>
@@ -407,6 +419,7 @@ impl EtherscanVerificationProvider {
407419
&args.verifier.verifier,
408420
args.verifier.verifier_url.as_deref(),
409421
args.etherscan.key.as_deref(),
422+
args.verifier.verifier_api_version.as_deref(),
410423
&context.config,
411424
)?;
412425

@@ -476,7 +489,6 @@ async fn ensure_solc_build_metadata(version: Version) -> Result<Version> {
476489
mod tests {
477490
use super::*;
478491
use clap::Parser;
479-
use foundry_block_explorers::EtherscanApiVersion;
480492
use foundry_common::fs;
481493
use foundry_test_utils::{forgetest_async, str};
482494
use tempfile::tempdir;
@@ -515,6 +527,7 @@ mod tests {
515527
&args.verifier.verifier,
516528
args.verifier.verifier_url.as_deref(),
517529
args.etherscan.key().as_deref(),
530+
args.verifier.verifier_api_version.as_deref(),
518531
&config,
519532
)
520533
.unwrap();
@@ -543,6 +556,7 @@ mod tests {
543556
&args.verifier.verifier,
544557
args.verifier.verifier_url.as_deref(),
545558
args.etherscan.key().as_deref(),
559+
args.verifier.verifier_api_version.as_deref(),
546560
&config,
547561
)
548562
.unwrap();
@@ -570,7 +584,7 @@ mod tests {
570584
"0xd8509bee9c9bf012282ad33aba0d87241baf5064",
571585
"src/Counter.sol:Counter",
572586
"--verifier",
573-
"etherscan-v2",
587+
"etherscan",
574588
"--chain",
575589
"mumbai",
576590
"--root",
@@ -587,6 +601,7 @@ mod tests {
587601
&args.verifier.verifier,
588602
args.verifier.verifier_url.as_deref(),
589603
args.etherscan.key().as_deref(),
604+
args.verifier.verifier_api_version.as_deref(),
590605
&config,
591606
)
592607
.unwrap();
@@ -599,7 +614,7 @@ mod tests {
599614
"0xd8509bee9c9bf012282ad33aba0d87241baf5064",
600615
"src/Counter.sol:Counter",
601616
"--verifier",
602-
"etherscan-v2",
617+
"etherscan",
603618
"--chain",
604619
"mumbai",
605620
"--verifier-url",
@@ -610,7 +625,7 @@ mod tests {
610625

611626
let config = args.load_config().unwrap();
612627

613-
assert_eq!(args.verifier.verifier, VerificationProviderType::EtherscanV2);
628+
assert_eq!(args.verifier.verifier, VerificationProviderType::Etherscan);
614629

615630
let etherscan = EtherscanVerificationProvider::default();
616631
let client = etherscan
@@ -619,6 +634,7 @@ mod tests {
619634
&args.verifier.verifier,
620635
args.verifier.verifier_url.as_deref(),
621636
args.etherscan.key().as_deref(),
637+
args.verifier.verifier_api_version.as_deref(),
622638
&config,
623639
)
624640
.unwrap();

0 commit comments

Comments
 (0)