cast/cmd/
artifact.rs

1use super::{
2    creation_code::{fetch_creation_code_from_etherscan, parse_code_output},
3    interface::{fetch_abi_from_etherscan, load_abi_from_file},
4};
5use alloy_primitives::Address;
6use alloy_provider::Provider;
7use clap::{command, Parser};
8use eyre::Result;
9use foundry_cli::{
10    opts::{EtherscanOpts, RpcOpts},
11    utils::{self, LoadConfig},
12};
13use foundry_common::fs;
14use serde_json::json;
15use std::path::PathBuf;
16
17/// CLI arguments for `cast artifact`.
18#[derive(Parser)]
19pub struct ArtifactArgs {
20    /// An Ethereum address, for which the artifact will be produced.
21    contract: Address,
22
23    /// Path to file containing the contract's JSON ABI. It's necessary if the target contract is
24    /// not verified on Etherscan.
25    #[arg(long)]
26    abi_path: Option<String>,
27
28    /// The path to the output file.
29    ///
30    /// If not specified, the artifact will be output to stdout.
31    #[arg(
32        short,
33        long,
34        value_hint = clap::ValueHint::FilePath,
35        value_name = "PATH",
36    )]
37    output: Option<PathBuf>,
38
39    #[command(flatten)]
40    etherscan: EtherscanOpts,
41
42    #[command(flatten)]
43    rpc: RpcOpts,
44}
45
46impl ArtifactArgs {
47    pub async fn run(self) -> Result<()> {
48        let Self { contract, mut etherscan, rpc, output: output_location, abi_path } = self;
49
50        let config = rpc.load_config()?;
51        let provider = utils::get_provider(&config)?;
52        let chain = provider.get_chain_id().await?;
53        etherscan.chain = Some(chain.into());
54
55        let abi = if let Some(ref abi_path) = abi_path {
56            load_abi_from_file(abi_path, None)?
57        } else {
58            fetch_abi_from_etherscan(contract, &etherscan).await?
59        };
60
61        let (abi, _) = abi.first().ok_or_else(|| eyre::eyre!("No ABI found"))?;
62
63        let bytecode = fetch_creation_code_from_etherscan(contract, &etherscan, provider).await?;
64        let bytecode =
65            parse_code_output(bytecode, contract, &etherscan, abi_path.as_deref(), true, false)
66                .await?;
67
68        let artifact = json!({
69            "abi": abi,
70            "bytecode": {
71              "object": bytecode
72            }
73        });
74
75        let artifact = serde_json::to_string_pretty(&artifact)?;
76
77        if let Some(loc) = output_location {
78            if let Some(parent) = loc.parent() {
79                fs::create_dir_all(parent)?;
80            }
81            fs::write(&loc, artifact)?;
82            sh_println!("Saved artifact at {}", loc.display())?;
83        } else {
84            sh_println!("{artifact}")?;
85        }
86
87        Ok(())
88    }
89}