foundry_cli/utils/
abi.rs

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_block_explorers::EtherscanApiVersion;
8use foundry_common::abi::{
9    encode_function_args, encode_function_args_raw, get_func, get_func_etherscan,
10};
11use futures::future::join_all;
12
13async fn resolve_name_args<P: Provider<AnyNetwork>>(args: &[String], provider: &P) -> Vec<String> {
14    join_all(args.iter().map(|arg| async {
15        if arg.contains('.') {
16            let addr = NameOrAddress::Name(arg.to_string()).resolve(provider).await;
17            match addr {
18                Ok(addr) => addr.to_string(),
19                Err(_) => arg.to_string(),
20            }
21        } else {
22            arg.to_string()
23        }
24    }))
25    .await
26}
27
28pub async fn parse_function_args<P: Provider<AnyNetwork>>(
29    sig: &str,
30    args: Vec<String>,
31    to: Option<Address>,
32    chain: Chain,
33    provider: &P,
34    etherscan_api_key: Option<&str>,
35    etherscan_api_version: EtherscanApiVersion,
36) -> Result<(Vec<u8>, Option<Function>)> {
37    if sig.trim().is_empty() {
38        eyre::bail!("Function signature or calldata must be provided.")
39    }
40
41    let args = resolve_name_args(&args, provider).await;
42
43    if let Ok(data) = hex::decode(sig) {
44        return Ok((data, None));
45    }
46
47    let func = if sig.contains('(') {
48        // a regular function signature with parentheses
49        get_func(sig)?
50    } else {
51        let etherscan_api_key = etherscan_api_key.ok_or_eyre(
52            "If you wish to fetch function data from Etherscan, please provide an Etherscan API key.",
53        )?;
54        let to = to.ok_or_eyre("A 'to' address must be provided to fetch function data.")?;
55        get_func_etherscan(sig, to, &args, chain, etherscan_api_key, etherscan_api_version).await?
56    };
57
58    if to.is_none() {
59        // if this is a CREATE call we must exclude the (constructor) function selector: https://github.com/foundry-rs/foundry/issues/10947
60        Ok((encode_function_args_raw(&func, &args)?, Some(func)))
61    } else {
62        Ok((encode_function_args(&func, &args)?, Some(func)))
63    }
64}