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#[derive(Parser)]
19pub struct ArtifactArgs {
20 contract: Address,
22
23 #[arg(long)]
26 abi_path: Option<String>,
27
28 #[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, ðerscan).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, ðerscan, provider).await?;
64 let bytecode =
65 parse_code_output(bytecode, contract, ðerscan, 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}