1use crate::{
2 Cast, SimpleCast,
3 cmd::erc20::IERC20,
4 opts::{Cast as CastArgs, CastSubcommand, ToBaseArgs},
5 traces::identifier::SignaturesIdentifier,
6 tx::CastTxSender,
7};
8use alloy_dyn_abi::{DynSolValue, ErrorExt, EventExt};
9use alloy_eips::eip7702::SignedAuthorization;
10use alloy_ens::{ProviderEnsExt, namehash};
11use alloy_network::Ethereum;
12use alloy_primitives::{Address, B256, eip191_hash_message, hex, keccak256};
13use alloy_provider::Provider;
14use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest};
15use clap::{CommandFactory, Parser};
16use clap_complete::generate;
17use eyre::{Result, WrapErr};
18use foundry_cli::utils::{self, LoadConfig};
19use foundry_common::{
20 abi::{get_error, get_event},
21 fmt::{format_tokens, format_uint_exp, serialize_value_as_json},
22 fs,
23 provider::ProviderBuilder,
24 selectors::{
25 ParsedSignatures, SelectorImportData, SelectorKind, decode_calldata, decode_event_topic,
26 decode_function_selector, decode_selectors, import_selectors, parse_signatures,
27 pretty_calldata,
28 },
29 shell, stdin,
30};
31use foundry_evm_networks::NetworkVariant;
32#[cfg(feature = "optimism")]
33use op_alloy_network::Optimism;
34use std::time::Instant;
35use tempo_alloy::TempoNetwork;
36
37pub fn run() -> Result<()> {
39 foundry_cli::opts::GlobalArgs::check_introspect::<CastArgs>();
42 foundry_cli::opts::GlobalArgs::check_markdown_help::<CastArgs>();
43
44 setup()?;
45
46 let args = CastArgs::parse();
47 args.global.init()?;
48 args.global.tokio_runtime().block_on(run_command(args))
49}
50
51pub fn setup() -> Result<()> {
53 utils::common_setup();
54 utils::subscriber();
55
56 Ok(())
57}
58
59#[allow(clippy::large_stack_frames)]
61pub async fn run_command(args: CastArgs) -> Result<()> {
62 match args.cmd {
63 CastSubcommand::MaxInt { r#type } => {
65 sh_println!("{}", SimpleCast::max_int(&r#type)?)?;
66 }
67 CastSubcommand::MinInt { r#type } => {
68 sh_println!("{}", SimpleCast::min_int(&r#type)?)?;
69 }
70 CastSubcommand::MaxUint { r#type } => {
71 sh_println!("{}", SimpleCast::max_int(&r#type)?)?;
72 }
73 CastSubcommand::AddressZero => {
74 sh_println!("{:?}", Address::ZERO)?;
75 }
76 CastSubcommand::HashZero => {
77 sh_println!("{:?}", B256::ZERO)?;
78 }
79
80 CastSubcommand::FromUtf8 { text } => {
82 let value = stdin::unwrap(text, false)?;
83 sh_println!("{}", SimpleCast::from_utf8(&value))?
84 }
85 CastSubcommand::ToAscii { hexdata } => {
86 let value = stdin::unwrap(hexdata, false)?;
87 sh_println!("{}", SimpleCast::to_ascii(value.trim())?)?
88 }
89 CastSubcommand::ToUtf8 { hexdata } => {
90 let value = stdin::unwrap(hexdata, false)?;
91 sh_println!("{}", SimpleCast::to_utf8(&value)?)?
92 }
93 CastSubcommand::FromFixedPoint { value, decimals } => {
94 let (value, decimals) = stdin::unwrap2(value, decimals)?;
95 sh_println!("{}", SimpleCast::from_fixed_point(&value, &decimals)?)?
96 }
97 CastSubcommand::ToFixedPoint { value, decimals } => {
98 let (value, decimals) = stdin::unwrap2(value, decimals)?;
99 sh_println!("{}", SimpleCast::to_fixed_point(&value, &decimals)?)?
100 }
101 CastSubcommand::ConcatHex { data } => {
102 if data.is_empty() {
103 let s = stdin::read(true)?;
104 sh_println!("{}", SimpleCast::concat_hex(s.split_whitespace()))?
105 } else {
106 sh_println!("{}", SimpleCast::concat_hex(data))?
107 }
108 }
109 CastSubcommand::FromBin => {
110 let hex = stdin::read_bytes(false)?;
111 sh_println!("{}", hex::encode_prefixed(hex))?
112 }
113 CastSubcommand::ToHexdata { input } => {
114 let value = stdin::unwrap_line(input)?;
115 let output = match value {
116 s if s.starts_with('@') => hex::encode(std::env::var(&s[1..])?),
117 s if s.starts_with('/') => hex::encode(fs::read(s)?),
118 s => s.split(':').map(|s| s.trim_start_matches("0x").to_lowercase()).collect(),
119 };
120 sh_println!("0x{output}")?
121 }
122 CastSubcommand::ToCheckSumAddress { address, chain_id } => {
123 let value = stdin::unwrap_line(address)?;
124 sh_println!("{}", value.to_checksum(chain_id))?
125 }
126 CastSubcommand::ToUint256 { value } => {
127 let value = stdin::unwrap_line(value)?;
128 sh_println!("{}", SimpleCast::to_uint256(&value)?)?
129 }
130 CastSubcommand::ToInt256 { value } => {
131 let value = stdin::unwrap_line(value)?;
132 sh_println!("{}", SimpleCast::to_int256(&value)?)?
133 }
134 CastSubcommand::ToUnit { value, unit } => {
135 let value = stdin::unwrap_line(value)?;
136 sh_println!("{}", SimpleCast::to_unit(&value, &unit)?)?
137 }
138 CastSubcommand::ParseUnits { value, unit } => {
139 let value = stdin::unwrap_line(value)?;
140 sh_println!("{}", SimpleCast::parse_units(&value, unit)?)?;
141 }
142 CastSubcommand::FormatUnits { value, unit } => {
143 let value = stdin::unwrap_line(value)?;
144 sh_println!("{}", SimpleCast::format_units(&value, unit)?)?;
145 }
146 CastSubcommand::FromWei { value, unit } => {
147 let value = stdin::unwrap_line(value)?;
148 sh_println!("{}", SimpleCast::from_wei(&value, &unit)?)?
149 }
150 CastSubcommand::ToWei { value, unit } => {
151 let value = stdin::unwrap_line(value)?;
152 sh_println!("{}", SimpleCast::to_wei(&value, &unit)?)?
153 }
154 CastSubcommand::FromRlp { value, as_int } => {
155 let value = stdin::unwrap_line(value)?;
156 sh_println!("{}", SimpleCast::from_rlp(value, as_int)?)?
157 }
158 CastSubcommand::ToRlp { value } => {
159 let value = stdin::unwrap_line(value)?;
160 sh_println!("{}", SimpleCast::to_rlp(&value)?)?
161 }
162 CastSubcommand::ToHex(ToBaseArgs { value, base_in }) => {
163 let value = stdin::unwrap_line(value)?;
164 sh_println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), "hex")?)?
165 }
166 CastSubcommand::ToDec(ToBaseArgs { value, base_in }) => {
167 let value = stdin::unwrap_line(value)?;
168 sh_println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), "dec")?)?
169 }
170 CastSubcommand::ToBase { base: ToBaseArgs { value, base_in }, base_out } => {
171 let (value, base_out) = stdin::unwrap2(value, base_out)?;
172 sh_println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), &base_out)?)?
173 }
174 CastSubcommand::ToBytes32 { bytes } => {
175 let value = stdin::unwrap_line(bytes)?;
176 sh_println!("{}", SimpleCast::to_bytes32(&value)?)?
177 }
178 CastSubcommand::Pad { data, right, left: _, len } => {
179 let value = stdin::unwrap_line(data)?;
180 sh_println!("{}", SimpleCast::pad(&value, right, len)?)?
181 }
182 CastSubcommand::FormatBytes32String { string } => {
183 let value = stdin::unwrap_line(string)?;
184 sh_println!("{}", SimpleCast::format_bytes32_string(&value)?)?
185 }
186 CastSubcommand::ParseBytes32String { bytes } => {
187 let value = stdin::unwrap_line(bytes)?;
188 sh_println!("{}", SimpleCast::parse_bytes32_string(&value)?)?
189 }
190 CastSubcommand::ParseBytes32Address { bytes } => {
191 let value = stdin::unwrap_line(bytes)?;
192 sh_println!("{}", SimpleCast::parse_bytes32_address(&value)?)?
193 }
194
195 CastSubcommand::DecodeAbi { sig, calldata, input } => {
197 let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
198 print_tokens(&tokens);
199 }
200 CastSubcommand::AbiEncode { sig, packed, args } => {
201 if packed {
202 sh_println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?)?
203 } else {
204 sh_println!("{}", SimpleCast::abi_encode(&sig, &args)?)?
205 }
206 }
207 CastSubcommand::AbiEncodeEvent { sig, args } => {
208 let log_data = SimpleCast::abi_encode_event(&sig, &args)?;
209 for (i, topic) in log_data.topics().iter().enumerate() {
210 sh_println!("[topic{}]: {}", i, topic)?;
211 }
212 if !log_data.data.is_empty() {
213 sh_println!("[data]: {}", hex::encode_prefixed(log_data.data))?;
214 }
215 }
216 CastSubcommand::DecodeCalldata { sig, calldata, file } => {
217 let raw_hex = if let Some(file_path) = file {
218 let contents = fs::read_to_string(&file_path)?;
219 contents.trim().to_string()
220 } else {
221 calldata.unwrap()
222 };
223
224 let tokens = SimpleCast::calldata_decode(&sig, &raw_hex, true)?;
225 print_tokens(&tokens);
226 }
227 CastSubcommand::CalldataEncode { sig, args, file } => {
228 let final_args = if let Some(file_path) = file {
229 let contents = fs::read_to_string(file_path)?;
230 contents
231 .lines()
232 .map(str::trim)
233 .filter(|line| !line.is_empty())
234 .map(String::from)
235 .collect()
236 } else {
237 args
238 };
239 sh_println!("{}", SimpleCast::calldata_encode(sig, &final_args)?)?;
240 }
241 CastSubcommand::DecodeString { data } => {
242 let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?;
243 print_tokens(&tokens);
244 }
245 CastSubcommand::DecodeEvent { sig, data } => {
246 let decoded_event = if let Some(event_sig) = sig {
247 let event = get_event(event_sig.as_str())?;
248 event.decode_log_parts(core::iter::once(event.selector()), &hex::decode(data)?)?
249 } else {
250 let data = crate::strip_0x(&data);
251 let selector = data.get(..64).unwrap_or_default();
252 let selector = selector.parse()?;
253 let identified_event =
254 SignaturesIdentifier::new(false)?.identify_event(selector).await;
255 if let Some(event) = identified_event {
256 let _ = sh_println!("{}", event.signature());
257 let data = data.get(64..).unwrap_or_default();
258 get_event(event.signature().as_str())?
259 .decode_log_parts(core::iter::once(selector), &hex::decode(data)?)?
260 } else {
261 eyre::bail!("No matching event signature found for selector `{selector}`")
262 }
263 };
264 print_tokens(&decoded_event.body);
265 }
266 CastSubcommand::DecodeError { sig, data } => {
267 let error = if let Some(err_sig) = sig {
268 get_error(err_sig.as_str())?
269 } else {
270 let data = crate::strip_0x(&data);
271 let selector = data.get(..8).unwrap_or_default();
272 let identified_error =
273 SignaturesIdentifier::new(false)?.identify_error(selector.parse()?).await;
274 if let Some(error) = identified_error {
275 let _ = sh_println!("{}", error.signature());
276 error
277 } else {
278 eyre::bail!("No matching error signature found for selector `{selector}`")
279 }
280 };
281 let decoded_error = error.decode_error(&hex::decode(data)?)?;
282 print_tokens(&decoded_error.body);
283 }
284 CastSubcommand::Interface(cmd) => cmd.run().await?,
285 CastSubcommand::CreationCode(cmd) => cmd.run().await?,
286 CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?,
287 CastSubcommand::Artifact(cmd) => cmd.run().await?,
288 CastSubcommand::Bind(cmd) => cmd.run().await?,
289 CastSubcommand::B2EPayload(cmd) => cmd.run().await?,
290 CastSubcommand::PrettyCalldata { calldata, offline } => {
291 let calldata = stdin::unwrap_line(calldata)?;
292 sh_println!("{}", pretty_calldata(&calldata, offline).await?)?;
293 }
294 CastSubcommand::Sig { sig, optimize } => {
295 let sig = stdin::unwrap_line(sig)?;
296 match optimize {
297 Some(opt) => {
298 sh_println!("Starting to optimize signature...")?;
299 let start_time = Instant::now();
300 let (selector, signature) = SimpleCast::get_selector(&sig, opt)?;
301 sh_println!("Successfully generated in {:?}", start_time.elapsed())?;
302 sh_println!("Selector: {selector}")?;
303 sh_println!("Optimized signature: {signature}")?;
304 }
305 None => sh_println!("{}", SimpleCast::get_selector(&sig, 0)?.0)?,
306 }
307 }
308
309 CastSubcommand::AccessList(cmd) => cmd.run().await?,
311 CastSubcommand::Age { block, rpc } => {
312 let config = rpc.load_config()?;
313 let provider = utils::get_provider(&config)?;
314 sh_println!(
315 "{} UTC",
316 Cast::new(provider).age(block.unwrap_or(BlockId::Number(Latest))).await?
317 )?
318 }
319 CastSubcommand::Balance { block, who, ether, rpc, erc20 } => {
320 let config = rpc.load_config()?;
321 let provider = utils::get_provider(&config)?;
322 let account_addr = who.resolve(&provider).await?;
323
324 match erc20 {
325 Some(token) => {
326 let balance = IERC20::new(token, &provider)
327 .balanceOf(account_addr)
328 .block(block.unwrap_or_default())
329 .call()
330 .await?;
331
332 sh_warn!("--erc20 flag is deprecated, use `cast erc20 balance` instead")?;
333 sh_println!("{}", format_uint_exp(balance))?
334 }
335 None => {
336 let value = Cast::new(&provider).balance(account_addr, block).await?;
337 if ether {
338 sh_println!("{}", SimpleCast::from_wei(&value.to_string(), "eth")?)?
339 } else {
340 sh_println!("{value}")?
341 }
342 }
343 }
344 }
345 CastSubcommand::BaseFee { block, rpc } => {
346 let config = rpc.load_config()?;
347 let provider = utils::get_provider(&config)?;
348 sh_println!(
349 "{}",
350 Cast::new(provider).base_fee(block.unwrap_or(BlockId::Number(Latest))).await?
351 )?
352 }
353 CastSubcommand::Block { block, full, fields, raw, rpc, network } => {
354 let config = rpc.load_config()?;
355 let output = if raw || fields.contains(&"raw".into()) {
357 match network {
358 #[cfg(feature = "optimism")]
359 Some(NetworkVariant::Optimism) => {
360 let provider =
361 ProviderBuilder::<Optimism>::from_config(&config)?.build()?;
362
363 Cast::new(&provider)
364 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
365 .await?
366 }
367 Some(NetworkVariant::Tempo) => {
368 let provider =
369 ProviderBuilder::<TempoNetwork>::from_config(&config)?.build()?;
370 Cast::new(&provider)
371 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
372 .await?
373 }
374 _ => {
376 let provider =
377 ProviderBuilder::<Ethereum>::from_config(&config)?.build()?;
378 Cast::new(&provider)
379 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
380 .await?
381 }
382 }
383 } else {
384 let provider = utils::get_provider(&config)?;
385 Cast::new(provider)
386 .block(block.unwrap_or(BlockId::Number(Latest)), full, fields)
387 .await?
388 };
389 sh_println!("{}", output)?
390 }
391 CastSubcommand::BlockNumber { rpc, block } => {
392 let config = rpc.load_config()?;
393 let provider = utils::get_provider(&config)?;
394 let number = match block {
395 Some(id) => {
396 provider
397 .get_block(id)
398 .await?
399 .ok_or_else(|| eyre::eyre!("block {id:?} not found"))?
400 .header
401 .number
402 }
403 None => Cast::new(provider).block_number().await?,
404 };
405 sh_println!("{number}")?
406 }
407 CastSubcommand::Chain { rpc } => {
408 let config = rpc.load_config()?;
409 let provider = utils::get_provider(&config)?;
410 sh_println!("{}", Cast::new(provider).chain().await?)?
411 }
412 CastSubcommand::ChainId { rpc } => {
413 let config = rpc.load_config()?;
414 let provider = utils::get_provider(&config)?;
415 sh_println!("{}", Cast::new(provider).chain_id().await?)?
416 }
417 CastSubcommand::Client { rpc } => {
418 let config = rpc.load_config()?;
419 let provider = utils::get_provider(&config)?;
420 sh_println!("{}", provider.get_client_version().await?)?
421 }
422 CastSubcommand::Code { block, who, disassemble, rpc } => {
423 let config = rpc.load_config()?;
424 let provider = utils::get_provider(&config)?;
425 let who = who.resolve(&provider).await?;
426 sh_println!("{}", Cast::new(provider).code(who, block, disassemble).await?)?
427 }
428 CastSubcommand::Codesize { block, who, rpc } => {
429 let config = rpc.load_config()?;
430 let provider = utils::get_provider(&config)?;
431 let who = who.resolve(&provider).await?;
432 sh_println!("{}", Cast::new(provider).codesize(who, block).await?)?
433 }
434 CastSubcommand::ComputeAddress { address, nonce, salt, init_code, init_code_hash, rpc } => {
435 let address = stdin::unwrap_line(address)?;
436 let computed = {
437 if let Some(init_code_hash) = init_code_hash {
439 address.create2(salt.unwrap_or(B256::ZERO), init_code_hash)
440 } else if let Some(init_code) = init_code {
441 address.create2(salt.unwrap_or(B256::ZERO), keccak256(hex::decode(init_code)?))
442 } else {
443 let config = rpc.load_config()?;
445 let provider = utils::get_provider(&config)?;
446 Cast::new(provider).compute_address(address, nonce).await?
447 }
448 };
449 sh_println!("Computed Address: {}", computed.to_checksum(None))?
450 }
451 CastSubcommand::Disassemble { bytecode } => {
452 let bytecode = stdin::unwrap_line(bytecode)?;
453 sh_println!("{}", SimpleCast::disassemble(&hex::decode(bytecode)?)?)?
454 }
455 CastSubcommand::Selectors { bytecode, resolve } => {
456 let bytecode = stdin::unwrap_line(bytecode)?;
457 let functions = SimpleCast::extract_functions(&bytecode)?;
458 let max_args_len = functions.iter().map(|r| r.1.len()).max().unwrap_or(0);
459 let max_mutability_len = functions.iter().map(|r| r.2.len()).max().unwrap_or(0);
460
461 let resolve_results = if resolve {
462 let selectors = functions
463 .iter()
464 .map(|&(selector, ..)| SelectorKind::Function(selector))
465 .collect::<Vec<_>>();
466 let ds = decode_selectors(&selectors).await?;
467 ds.into_iter().map(|v| v.join("|")).collect()
468 } else {
469 vec![]
470 };
471 for (pos, (selector, arguments, state_mutability)) in functions.into_iter().enumerate()
472 {
473 if resolve {
474 let resolved = &resolve_results[pos];
475 sh_println!(
476 "{selector}\t{arguments:max_args_len$}\t{state_mutability:max_mutability_len$}\t{resolved}"
477 )?
478 } else {
479 sh_println!("{selector}\t{arguments:max_args_len$}\t{state_mutability}")?
480 }
481 }
482 }
483 CastSubcommand::FindBlock(cmd) => cmd.run().await?,
484 CastSubcommand::GasPrice { rpc } => {
485 let config = rpc.load_config()?;
486 let provider = utils::get_provider(&config)?;
487 sh_println!("{}", Cast::new(provider).gas_price().await?)?;
488 }
489 CastSubcommand::Index { key_type, key, slot_number } => {
490 sh_println!("{}", SimpleCast::index(&key_type, &key, &slot_number)?)?;
491 }
492 CastSubcommand::IndexErc7201 { id, formula_id } => {
493 eyre::ensure!(formula_id == "erc7201", "unsupported formula ID: {formula_id}");
494 let id = stdin::unwrap_line(id)?;
495 sh_println!("{}", foundry_common::erc7201(&id))?;
496 }
497 CastSubcommand::Implementation { block, beacon, who, rpc } => {
498 let config = rpc.load_config()?;
499 let provider = utils::get_provider(&config)?;
500 let who = who.resolve(&provider).await?;
501 sh_println!("{}", Cast::new(provider).implementation(who, beacon, block).await?)?;
502 }
503 CastSubcommand::Admin { block, who, rpc } => {
504 let config = rpc.load_config()?;
505 let provider = utils::get_provider(&config)?;
506 let who = who.resolve(&provider).await?;
507 sh_println!("{}", Cast::new(provider).admin(who, block).await?)?;
508 }
509 CastSubcommand::Nonce { block, who, rpc } => {
510 let config = rpc.load_config()?;
511 let provider = utils::get_provider(&config)?;
512 let who = who.resolve(&provider).await?;
513 sh_println!("{}", Cast::new(provider).nonce(who, block).await?)?;
514 }
515 CastSubcommand::Codehash { block, who, slots, rpc } => {
516 let config = rpc.load_config()?;
517 let provider = utils::get_provider(&config)?;
518 let who = who.resolve(&provider).await?;
519 sh_println!("{}", Cast::new(provider).codehash(who, slots, block).await?)?;
520 }
521 CastSubcommand::StorageRoot { block, who, slots, rpc } => {
522 let config = rpc.load_config()?;
523 let provider = utils::get_provider(&config)?;
524 let who = who.resolve(&provider).await?;
525 sh_println!("{}", Cast::new(provider).storage_root(who, slots, block).await?)?;
526 }
527 CastSubcommand::Proof { address, slots, rpc, block } => {
528 let config = rpc.load_config()?;
529 let provider = utils::get_provider(&config)?;
530 let address = address.resolve(&provider).await?;
531 let value = provider
532 .get_proof(address, slots.into_iter().collect())
533 .block_id(block.unwrap_or_default())
534 .await?;
535 sh_println!("{}", serde_json::to_string(&value)?)?;
536 }
537 CastSubcommand::Rpc(cmd) => cmd.run().await?,
538 CastSubcommand::Storage(cmd) => cmd.run().await?,
539
540 CastSubcommand::Call(cmd) => cmd.run().await?,
542 CastSubcommand::Estimate(cmd) => cmd.run().await?,
543 CastSubcommand::MakeTx(cmd) => cmd.run().await?,
544 CastSubcommand::PublishTx { raw_tx, cast_async, rpc } => {
545 let config = rpc.load_config()?;
546 let provider = utils::get_provider(&config)?;
547 let cast = Cast::new(&provider);
548 let pending_tx = cast.publish(raw_tx).await?;
549 let tx_hash = pending_tx.inner().tx_hash();
550
551 if cast_async {
552 sh_println!("{tx_hash:#x}")?;
553 } else {
554 let receipt = pending_tx.get_receipt().await?;
555 sh_println!("{}", serde_json::json!(receipt))?;
556 }
557 }
558 CastSubcommand::Receipt { tx_hash, field, cast_async, confirmations, rpc } => {
559 let config = rpc.load_config()?;
560 let provider = utils::get_provider(&config)?;
561 sh_println!(
562 "{}",
563 CastTxSender::new(provider)
564 .receipt(tx_hash, field, confirmations, None, cast_async)
565 .await?
566 )?
567 }
568 CastSubcommand::Run(cmd) => cmd.run().await?,
569 CastSubcommand::SendTx(cmd) => cmd.run().await?,
570 CastSubcommand::BatchMakeTx(cmd) => cmd.run().await?,
571 CastSubcommand::BatchSend(cmd) => cmd.run().await?,
572 CastSubcommand::Tx { tx_hash, from, nonce, field, raw, rpc, to_request, network } => {
573 let config = rpc.load_config()?;
574 let is_raw = raw || field.as_ref().is_some_and(|f| f == "raw");
576 let output = match network {
577 #[cfg(feature = "optimism")]
578 Some(NetworkVariant::Optimism) => {
579 let provider = ProviderBuilder::<Optimism>::from_config(&config)?.build()?;
580
581 Cast::new(&provider)
582 .transaction(tx_hash, from, nonce, field, is_raw, to_request)
583 .await?
584 }
585 Some(NetworkVariant::Tempo) => {
586 let provider =
587 ProviderBuilder::<TempoNetwork>::from_config(&config)?.build()?;
588 Cast::new(&provider)
589 .transaction(tx_hash, from, nonce, field, is_raw, to_request)
590 .await?
591 }
592 _ => {
594 let provider = utils::get_provider(&config)?;
595 Cast::new(&provider)
596 .transaction(tx_hash, from, nonce, field, is_raw, to_request)
597 .await?
598 }
599 };
600 sh_println!("{}", output)?
601 }
602
603 CastSubcommand::FourByte { selector } => {
605 let selector = stdin::unwrap_line(selector)?;
606 let sigs = decode_function_selector(selector).await?;
607 if sigs.is_empty() {
608 eyre::bail!("No matching function signatures found for selector `{selector}`");
609 }
610 for sig in sigs {
611 sh_println!("{sig}")?
612 }
613 }
614
615 CastSubcommand::FourByteCalldata { calldata } => {
616 let calldata = stdin::unwrap_line(calldata)?;
617
618 if calldata.len() == 10 {
619 let sigs = decode_function_selector(calldata.parse()?).await?;
620 if sigs.is_empty() {
621 eyre::bail!("No matching function signatures found for calldata `{calldata}`");
622 }
623 for sig in sigs {
624 sh_println!("{sig}")?
625 }
626 return Ok(());
627 }
628
629 let sigs = decode_calldata(&calldata).await?;
630 sigs.iter().enumerate().for_each(|(i, sig)| {
631 let _ = sh_println!("{}) \"{sig}\"", i + 1);
632 });
633
634 let sig = match sigs.len() {
635 0 => eyre::bail!("No signatures found"),
636 1 => sigs.first().unwrap(),
637 _ => {
638 let i: usize = prompt!("Select a function signature by number: ")?;
639 sigs.get(i - 1).ok_or_else(|| eyre::eyre!("Invalid signature index"))?
640 }
641 };
642
643 let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?;
644 print_tokens(&tokens);
645 }
646
647 CastSubcommand::FourByteEvent { topic } => {
648 let topic = stdin::unwrap_line(topic)?;
649 let sigs = decode_event_topic(topic).await?;
650 if sigs.is_empty() {
651 eyre::bail!("No matching event signatures found for topic `{topic}`");
652 }
653 for sig in sigs {
654 sh_println!("{sig}")?
655 }
656 }
657 CastSubcommand::UploadSignature { signatures } => {
658 let signatures = stdin::unwrap_vec(signatures)?;
659 let ParsedSignatures { signatures, abis } = parse_signatures(signatures);
660 if !abis.is_empty() {
661 import_selectors(SelectorImportData::Abi(abis)).await?.describe();
662 }
663 if !signatures.is_empty() {
664 import_selectors(SelectorImportData::Raw(signatures)).await?.describe();
665 }
666 }
667
668 CastSubcommand::Namehash { name } => {
670 let name = stdin::unwrap_line(name)?;
671 sh_println!("{}", namehash(&name))?
672 }
673 CastSubcommand::LookupAddress { who, rpc, verify } => {
674 let config = rpc.load_config()?;
675 let provider = utils::get_provider(&config)?;
676
677 let who = stdin::unwrap_line(who)?;
678 let name = provider.lookup_address(&who).await?;
679 if verify {
680 let address = provider.resolve_name(&name).await?;
681 eyre::ensure!(
682 address == who,
683 "Reverse lookup verification failed: got `{address}`, expected `{who}`"
684 );
685 }
686 sh_println!("{name}")?
687 }
688 CastSubcommand::ResolveName { who, rpc, verify } => {
689 let config = rpc.load_config()?;
690 let provider = utils::get_provider(&config)?;
691
692 let who = stdin::unwrap_line(who)?;
693 let address = provider
694 .resolve_name(&who)
695 .await
696 .wrap_err(format!("Failed to resolve ENS name: {who}"))?;
697 if verify {
698 let name = provider.lookup_address(&address).await?;
699 eyre::ensure!(
700 name == who,
701 "Forward lookup verification failed: got `{name}`, expected `{who}`"
702 );
703 }
704 sh_println!("{address}")?
705 }
706
707 CastSubcommand::Keccak { data } => {
709 let bytes = match data {
710 Some(data) => data.into_bytes(),
711 None => stdin::read_bytes(false)?,
712 };
713 match String::from_utf8(bytes) {
714 Ok(s) => {
715 let s = SimpleCast::keccak(&s)?;
716 sh_println!("{s}")?
717 }
718 Err(e) => {
719 let hash = keccak256(e.as_bytes());
720 let s = hex::encode(hash);
721 sh_println!("0x{s}")?
722 }
723 };
724 }
725 CastSubcommand::HashMessage { message } => {
726 let message = stdin::unwrap(message, false)?;
727 sh_println!("{}", eip191_hash_message(message))?
728 }
729 CastSubcommand::SigEvent { event_string } => {
730 let event_string = stdin::unwrap_line(event_string)?;
731 let parsed_event = get_event(&event_string)?;
732 sh_println!("{:?}", parsed_event.selector())?
733 }
734 CastSubcommand::LeftShift { value, bits, base_in, base_out } => sh_println!(
735 "{}",
736 SimpleCast::left_shift(&value, &bits, base_in.as_deref(), &base_out)?
737 )?,
738 CastSubcommand::RightShift { value, bits, base_in, base_out } => sh_println!(
739 "{}",
740 SimpleCast::right_shift(&value, &bits, base_in.as_deref(), &base_out)?
741 )?,
742 CastSubcommand::Source {
743 address,
744 directory,
745 explorer_api_url,
746 explorer_url,
747 etherscan,
748 flatten,
749 } => {
750 let config = etherscan.load_config()?;
751 let chain = config.chain.unwrap_or_default();
752 let api_key = config.get_etherscan_api_key(Some(chain));
753 match (directory, flatten) {
754 (Some(dir), false) => {
755 SimpleCast::expand_etherscan_source_to_directory(
756 chain,
757 address,
758 api_key,
759 dir,
760 explorer_api_url,
761 explorer_url,
762 )
763 .await?
764 }
765 (None, false) => sh_println!(
766 "{}",
767 SimpleCast::etherscan_source(
768 chain,
769 address,
770 api_key,
771 explorer_api_url,
772 explorer_url
773 )
774 .await?
775 )?,
776 (dir, true) => {
777 SimpleCast::etherscan_source_flatten(
778 chain,
779 address,
780 api_key,
781 dir,
782 explorer_api_url,
783 explorer_url,
784 )
785 .await?;
786 }
787 }
788 }
789 CastSubcommand::Create2(cmd) => {
790 cmd.run()?;
791 }
792 CastSubcommand::Wallet { command } => command.run().await?,
793 CastSubcommand::Completions { shell } => {
794 generate(shell, &mut CastArgs::command(), "cast", &mut std::io::stdout())
795 }
796 CastSubcommand::Logs(cmd) => cmd.run().await?,
797 CastSubcommand::DecodeTransaction { tx, network } => {
798 let tx = stdin::unwrap_line(tx)?;
799 let decoded_tx = match network {
800 #[cfg(feature = "optimism")]
801 Some(NetworkVariant::Optimism) => {
802 SimpleCast::decode_raw_transaction::<Optimism>(&tx)?
803 }
804 Some(NetworkVariant::Tempo) => {
805 SimpleCast::decode_raw_transaction::<TempoNetwork>(&tx)?
806 }
807 _ => SimpleCast::decode_raw_transaction::<Ethereum>(&tx)?,
808 };
809 sh_println!("{}", serde_json::to_string_pretty(&decoded_tx)?)?;
810 }
811 CastSubcommand::RecoverAuthority { auth } => {
812 let auth: SignedAuthorization = serde_json::from_str(&auth)?;
813 sh_println!("{}", auth.recover_authority()?)?;
814 }
815 CastSubcommand::TxPool { command } => command.run().await?,
816 CastSubcommand::Erc20Token { command } => command.run().await?,
817 CastSubcommand::Tip20Token { command } => command.run().await?,
818 CastSubcommand::Keychain { command } => command.run().await?,
819 CastSubcommand::Tempo { command } => command.run().await?,
820 CastSubcommand::VirtualAddress { command } => command.run().await?,
821 #[cfg(feature = "optimism")]
822 CastSubcommand::DAEstimate(cmd) => {
823 cmd.run().await?;
824 }
825 CastSubcommand::Trace(cmd) => cmd.run().await?,
826 };
827
828 fn print_tokens(tokens: &[DynSolValue]) {
833 if shell::is_json() {
834 let tokens: Vec<serde_json::Value> = tokens
835 .iter()
836 .cloned()
837 .map(|t| serialize_value_as_json(t, None))
838 .collect::<Result<Vec<_>>>()
839 .unwrap();
840 let _ = sh_println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
841 } else {
842 let tokens = format_tokens(tokens);
843 tokens.for_each(|t| {
844 let _ = sh_println!("{t}");
845 });
846 }
847 }
848
849 Ok(())
850}
851
852#[cfg(test)]
853mod tests {
854 use super::*;
855 use foundry_cli::introspect::{
856 CommandRegistry, INTROSPECT_SCHEMA_ID, IntrospectDocument, build_document,
857 duplicate_command_ids, render_introspect_document,
858 };
859
860 #[test]
869 fn introspect_command_ids_are_unique() {
870 let dups = std::thread::Builder::new()
871 .stack_size(16 * 1024 * 1024)
872 .spawn(|| {
873 let cmd = <CastArgs as clap::CommandFactory>::command();
874 let doc = build_document(&cmd, &CommandRegistry::EMPTY);
875 duplicate_command_ids(&doc)
876 })
877 .expect("spawn worker thread")
878 .join()
879 .expect("worker thread join");
880 assert!(dups.is_empty(), "duplicate cast command_ids: {dups:?}");
881 }
882
883 #[test]
887 fn introspect_document_is_valid_json() {
888 let json = std::thread::Builder::new()
889 .stack_size(16 * 1024 * 1024)
890 .spawn(|| {
891 let cmd = <CastArgs as clap::CommandFactory>::command();
892 render_introspect_document(&cmd, &CommandRegistry::EMPTY)
893 })
894 .expect("spawn worker thread")
895 .join()
896 .expect("worker thread join");
897 let doc: IntrospectDocument = serde_json::from_str(&json).expect("valid JSON");
898 assert_eq!(doc.schema_id, INTROSPECT_SCHEMA_ID);
899 assert_eq!(doc.binary.name, "cast");
900 }
901}