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