cast/cmd/
creation_code.rs
1use super::interface::{fetch_abi_from_etherscan, load_abi_from_file};
2use crate::SimpleCast;
3use alloy_consensus::Transaction;
4use alloy_primitives::{Address, Bytes};
5use alloy_provider::{ext::TraceApi, Provider};
6use alloy_rpc_types::trace::parity::{Action, CreateAction, CreateOutput, TraceOutput};
7use clap::{command, Parser};
8use eyre::{eyre, OptionExt, Result};
9use foundry_block_explorers::Client;
10use foundry_cli::{
11 opts::{EtherscanOpts, RpcOpts},
12 utils::{self, LoadConfig},
13};
14use foundry_common::provider::RetryProvider;
15
16#[derive(Parser)]
18pub struct CreationCodeArgs {
19 contract: Address,
21
22 #[arg(long)]
25 abi_path: Option<String>,
26
27 #[arg(long)]
29 disassemble: bool,
30
31 #[arg(long, conflicts_with = "only_args")]
33 without_args: bool,
34
35 #[arg(long)]
37 only_args: bool,
38
39 #[command(flatten)]
40 etherscan: EtherscanOpts,
41
42 #[command(flatten)]
43 rpc: RpcOpts,
44}
45
46impl CreationCodeArgs {
47 pub async fn run(self) -> Result<()> {
48 let Self { contract, mut etherscan, rpc, disassemble, without_args, only_args, abi_path } =
49 self;
50
51 let config = rpc.load_config()?;
52 let provider = utils::get_provider(&config)?;
53 let api_key = etherscan.key().unwrap_or_default();
54 let chain = provider.get_chain_id().await?;
55 etherscan.chain = Some(chain.into());
56 let client = Client::new(chain.into(), api_key)?;
57
58 let bytecode = fetch_creation_code(contract, client, provider).await?;
59
60 let bytecode = parse_code_output(
61 bytecode,
62 contract,
63 ðerscan,
64 abi_path.as_deref(),
65 without_args,
66 only_args,
67 )
68 .await?;
69
70 if disassemble {
71 let _ = sh_println!("{}", SimpleCast::disassemble(&bytecode)?);
72 } else {
73 let _ = sh_println!("{bytecode}");
74 }
75
76 Ok(())
77 }
78}
79
80pub async fn parse_code_output(
85 bytecode: Bytes,
86 contract: Address,
87 etherscan: &EtherscanOpts,
88 abi_path: Option<&str>,
89 without_args: bool,
90 only_args: bool,
91) -> Result<Bytes> {
92 if !without_args && !only_args {
93 return Ok(bytecode);
94 }
95
96 let abi = if let Some(abi_path) = abi_path {
97 load_abi_from_file(abi_path, None)?
98 } else {
99 fetch_abi_from_etherscan(contract, etherscan).await?
100 };
101
102 let abi = abi.into_iter().next().ok_or_eyre("No ABI found.")?;
103 let (abi, _) = abi;
104
105 if abi.constructor.is_none() {
106 if only_args {
107 return Err(eyre!("No constructor found."));
108 }
109 return Ok(bytecode);
110 }
111
112 let constructor = abi.constructor.unwrap();
113 if constructor.inputs.is_empty() {
114 if only_args {
115 return Err(eyre!("No constructor arguments found."));
116 }
117 return Ok(bytecode);
118 }
119
120 let args_size = constructor.inputs.len() * 32;
121
122 let bytecode = if without_args {
123 Bytes::from(bytecode[..bytecode.len() - args_size].to_vec())
124 } else if only_args {
125 Bytes::from(bytecode[bytecode.len() - args_size..].to_vec())
126 } else {
127 unreachable!();
128 };
129
130 Ok(bytecode)
131}
132
133pub async fn fetch_creation_code(
135 contract: Address,
136 client: Client,
137 provider: RetryProvider,
138) -> Result<Bytes> {
139 let creation_data = client.contract_creation_data(contract).await?;
140 let creation_tx_hash = creation_data.transaction_hash;
141 let tx_data = provider.get_transaction_by_hash(creation_tx_hash).await?;
142 let tx_data = tx_data.ok_or_eyre("Could not find creation tx data.")?;
143
144 let bytecode = if tx_data.to().is_none() {
145 tx_data.input().clone()
147 } else {
148 let mut creation_bytecode = None;
151
152 let traces = provider.trace_transaction(creation_tx_hash).await.map_err(|e| {
153 eyre!("Could not fetch traces for transaction {}: {}", creation_tx_hash, e)
154 })?;
155
156 for trace in traces {
157 if let Some(TraceOutput::Create(CreateOutput { address, .. })) = trace.trace.result {
158 if address == contract {
159 creation_bytecode = match trace.trace.action {
160 Action::Create(CreateAction { init, .. }) => Some(init),
161 _ => None,
162 };
163 }
164 }
165 }
166
167 creation_bytecode.ok_or_else(|| eyre!("Could not find contract creation trace."))?
168 };
169
170 Ok(bytecode)
171}