1use alloy_chains::Chain;
2use alloy_ens::NameOrAddress;
3use alloy_json_abi::Function;
4use alloy_primitives::{Address, hex};
5use alloy_provider::{Network, Provider};
6use eyre::{OptionExt, Result};
7use foundry_common::abi::{
8 encode_function_args, encode_function_args_raw, get_func, get_func_etherscan,
9};
10use futures::future::join_all;
11
12pub async fn parse_function_args<N: Network, P: Provider<N>>(
13 sig: &str,
14 args: Vec<String>,
15 to: Option<Address>,
16 chain: Chain,
17 provider: &P,
18 etherscan_api_key: Option<&str>,
19 etherscan_api_url: Option<&str>,
20) -> Result<(Vec<u8>, Option<Function>)> {
21 if sig.trim().is_empty() {
22 eyre::bail!("Function signature or calldata must be provided.");
23 }
24
25 let args = resolve_name_args(&args, provider).await;
26
27 if let Ok(data) = hex::decode(sig) {
29 return Ok((data, None));
30 } else if sig.starts_with("0x") || sig.starts_with("0X") {
31 let e = hex::decode(sig).unwrap_err();
32 eyre::bail!("Invalid hex calldata '{}': {e}", sig);
33 }
34
35 let func = if sig.contains('(') {
36 get_func(sig)?
38 } else {
39 info!(
40 "function signature does not contain parentheses, fetching function data from Etherscan"
41 );
42 let etherscan_api_key = etherscan_api_key.ok_or_eyre(
43 "Function signature does not contain parentheses. If you wish to fetch function data from Etherscan, please provide an API key.",
44 )?;
45 let to = to.ok_or_eyre("A 'to' address must be provided to fetch function data.")?;
46 get_func_etherscan(sig, to, &args, chain, etherscan_api_key, etherscan_api_url).await?
47 };
48
49 if to.is_none() {
50 Ok((encode_function_args_raw(&func, &args)?, Some(func)))
52 } else {
53 Ok((encode_function_args(&func, &args)?, Some(func)))
54 }
55}
56
57async fn resolve_name_args<N: Network, P: Provider<N>>(
58 args: &[String],
59 provider: &P,
60) -> Vec<String> {
61 join_all(args.iter().map(|arg| async {
62 if arg.contains('.') {
63 let addr = NameOrAddress::Name(arg.clone()).resolve(provider).await;
64 match addr {
65 Ok(addr) => addr.to_string(),
66 Err(_) => arg.clone(),
67 }
68 } else {
69 arg.clone()
70 }
71 }))
72 .await
73}