cast/cmd/
constructor_args.rs

1use super::{creation_code::fetch_creation_code_from_etherscan, interface::load_abi_from_file};
2use alloy_dyn_abi::DynSolType;
3use alloy_primitives::{Address, Bytes};
4use alloy_provider::Provider;
5use clap::{Parser, command};
6use eyre::{OptionExt, Result, eyre};
7use foundry_cli::{
8    opts::{EtherscanOpts, RpcOpts},
9    utils::{self, LoadConfig},
10};
11use foundry_common::abi::fetch_abi_from_etherscan;
12use foundry_config::Config;
13
14foundry_config::impl_figment_convert!(ConstructorArgsArgs, etherscan, rpc);
15
16/// CLI arguments for `cast creation-args`.
17#[derive(Parser)]
18pub struct ConstructorArgsArgs {
19    /// An Ethereum address, for which the bytecode will be fetched.
20    contract: Address,
21
22    /// Path to file containing the contract's JSON ABI. It's necessary if the target contract is
23    /// not verified on Etherscan
24    #[arg(long)]
25    abi_path: Option<String>,
26
27    #[command(flatten)]
28    etherscan: EtherscanOpts,
29
30    #[command(flatten)]
31    rpc: RpcOpts,
32}
33
34impl ConstructorArgsArgs {
35    pub async fn run(self) -> Result<()> {
36        let mut config = self.load_config()?;
37
38        let Self { contract, abi_path, etherscan: _, rpc: _ } = self;
39
40        let provider = utils::get_provider(&config)?;
41        config.chain = Some(provider.get_chain_id().await?.into());
42
43        let bytecode = fetch_creation_code_from_etherscan(contract, &config, provider).await?;
44
45        let args_arr = parse_constructor_args(bytecode, contract, &config, abi_path).await?;
46        for arg in args_arr {
47            let _ = sh_println!("{arg}");
48        }
49
50        Ok(())
51    }
52}
53
54/// Fetches the constructor arguments values and types from the creation bytecode and ABI.
55async fn parse_constructor_args(
56    bytecode: Bytes,
57    contract: Address,
58    config: &Config,
59    abi_path: Option<String>,
60) -> Result<Vec<String>> {
61    let abi = if let Some(abi_path) = abi_path {
62        load_abi_from_file(&abi_path, None)?
63    } else {
64        fetch_abi_from_etherscan(contract, config).await?
65    };
66
67    let abi = abi.into_iter().next().ok_or_eyre("No ABI found.")?;
68    let (abi, _) = abi;
69
70    let constructor = abi.constructor.ok_or_else(|| eyre!("No constructor found."))?;
71
72    if constructor.inputs.is_empty() {
73        return Err(eyre!("No constructor arguments found."));
74    }
75
76    let args_size = constructor.inputs.len() * 32;
77    let args_bytes = Bytes::from(bytecode[bytecode.len() - args_size..].to_vec());
78
79    let display_args: Vec<String> = args_bytes
80        .chunks(32)
81        .enumerate()
82        .map(|(i, arg)| {
83            format_arg(&constructor.inputs[i].ty, arg).expect("Failed to format argument.")
84        })
85        .collect();
86
87    Ok(display_args)
88}
89
90fn format_arg(ty: &str, arg: &[u8]) -> Result<String> {
91    let arg_type: DynSolType = ty.parse().expect("Invalid ABI type.");
92    let decoded = arg_type.abi_decode(arg)?;
93    let bytes = Bytes::from(arg.to_vec());
94
95    Ok(format!("{bytes} → {decoded:?}"))
96}