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