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::Pad { data, right, left: _, len } => {
206 let value = stdin::unwrap_line(data)?;
207 let out = SimpleCast::pad(&value, right, len)?;
208 print_scalar(out)?;
209 }
210 CastSubcommand::FormatBytes32String { string } => {
211 let value = stdin::unwrap_line(string)?;
212 let out = SimpleCast::format_bytes32_string(&value)?;
213 print_scalar(out)?;
214 }
215 CastSubcommand::ParseBytes32String { bytes } => {
216 let value = stdin::unwrap_line(bytes)?;
217 let out = SimpleCast::parse_bytes32_string(&value)?;
218 print_scalar(out)?;
219 }
220 CastSubcommand::ParseBytes32Address { bytes } => {
221 let value = stdin::unwrap_line(bytes)?;
222 let out = SimpleCast::parse_bytes32_address(&value)?;
223 print_scalar(out)?;
224 }
225
226 CastSubcommand::DecodeAbi { sig, calldata, input } => {
228 let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
229 print_tokens(&tokens)?;
230 }
231 CastSubcommand::AbiEncode { sig, packed, args } => {
232 let out = if packed {
233 SimpleCast::abi_encode_packed(&sig, &args)?
234 } else {
235 SimpleCast::abi_encode(&sig, &args)?
236 };
237 print_scalar(out)?;
238 }
239 CastSubcommand::AbiEncodeEvent { sig, args } => {
242 let log_data = SimpleCast::abi_encode_event(&sig, &args)?;
243 if shell::is_json() {
244 #[derive(serde::Serialize)]
245 struct EncodedEvent {
246 topics: Vec<String>,
247 data: String,
248 }
249 let encoded = EncodedEvent {
250 topics: log_data.topics().iter().map(|t| t.to_string()).collect(),
251 data: hex::encode_prefixed(&log_data.data),
252 };
253 print_json_object(encoded)?;
254 } else {
255 for (i, topic) in log_data.topics().iter().enumerate() {
256 sh_println!("[topic{}]: {}", i, topic)?;
257 }
258 if !log_data.data.is_empty() {
259 sh_println!("[data]: {}", hex::encode_prefixed(log_data.data))?;
260 }
261 }
262 }
263 CastSubcommand::DecodeCalldata { sig, calldata, file } => {
264 let raw_hex = if let Some(file_path) = file {
265 let contents = fs::read_to_string(&file_path)?;
266 contents.trim().to_string()
267 } else {
268 calldata.unwrap()
269 };
270
271 let tokens = SimpleCast::calldata_decode(&sig, &raw_hex, true)?;
272 print_tokens(&tokens)?;
273 }
274 CastSubcommand::CalldataEncode { sig, args, file } => {
275 let final_args = if let Some(file_path) = file {
276 let contents = fs::read_to_string(file_path)?;
277 contents
278 .lines()
279 .map(str::trim)
280 .filter(|line| !line.is_empty())
281 .map(String::from)
282 .collect()
283 } else {
284 args
285 };
286 let out = SimpleCast::calldata_encode(sig, &final_args)?;
287 print_scalar(out)?;
288 }
289 CastSubcommand::DecodeString { data } => {
290 let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?;
291 print_tokens(&tokens)?;
292 }
293 CastSubcommand::DecodeEvent { sig, data } => {
294 let decoded_event = if let Some(event_sig) = sig {
295 let event = get_event(event_sig.as_str())?;
296 event.decode_log_parts(core::iter::once(event.selector()), &hex::decode(data)?)?
297 } else {
298 let data = crate::strip_0x(&data);
299 let selector = data.get(..64).unwrap_or_default();
300 let selector = selector.parse()?;
301 let identified_event =
302 SignaturesIdentifier::new(false)?.identify_event(selector).await;
303 if let Some(event) = identified_event {
304 let _ = sh_println!("{}", event.signature());
305 let data = data.get(64..).unwrap_or_default();
306 get_event(event.signature().as_str())?
307 .decode_log_parts(core::iter::once(selector), &hex::decode(data)?)?
308 } else {
309 eyre::bail!("No matching event signature found for selector `{selector}`");
310 }
311 };
312 print_tokens(&decoded_event.body)?;
313 }
314 CastSubcommand::DecodeError { sig, data } => {
315 let error = if let Some(err_sig) = sig {
316 get_error(err_sig.as_str())?
317 } else {
318 let data = crate::strip_0x(&data);
319 let selector = data.get(..8).unwrap_or_default();
320 let identified_error =
321 SignaturesIdentifier::new(false)?.identify_error(selector.parse()?).await;
322 if let Some(error) = identified_error {
323 let _ = sh_println!("{}", error.signature());
324 error
325 } else {
326 eyre::bail!("No matching error signature found for selector `{selector}`");
327 }
328 };
329 let decoded_error = error.decode_error(&hex::decode(data)?)?;
330 print_tokens(&decoded_error.body)?;
331 }
332 CastSubcommand::Interface(cmd) => cmd.run().await?,
333 CastSubcommand::CreationCode(cmd) => cmd.run().await?,
334 CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?,
335 CastSubcommand::Artifact(cmd) => cmd.run().await?,
336 CastSubcommand::Bind(cmd) => cmd.run().await?,
337 CastSubcommand::B2EPayload(cmd) => cmd.run().await?,
338 CastSubcommand::PrettyCalldata { calldata, offline } => {
339 let calldata = stdin::unwrap_line(calldata)?;
340 let out = pretty_calldata(&calldata, offline).await?.to_string();
341 print_scalar(out)?;
342 }
343 CastSubcommand::Sig { sig, optimize } => {
345 let sig = stdin::unwrap_line(sig)?;
346 match optimize {
347 Some(opt) => {
348 sh_status!("Starting to optimize signature...")?;
349 let start_time = Instant::now();
350 let (selector, signature) = SimpleCast::get_selector(&sig, opt)?;
351 sh_status!("Successfully generated in {:?}", start_time.elapsed())?;
352 sh_println!("Selector: {selector}")?;
353 sh_println!("Optimized signature: {signature}")?;
354 }
355 None => {
356 let out = SimpleCast::get_selector(&sig, 0)?.0;
357 print_scalar(out)?;
358 }
359 }
360 }
361
362 CastSubcommand::AccessList(cmd) => cmd.run().await?,
364 CastSubcommand::Age { block, rpc } => {
365 let config = rpc.load_config()?;
366 let provider = utils::get_provider(&config)?;
367 let out = format!(
368 "{} UTC",
369 Cast::new(provider).age(block.unwrap_or(BlockId::Number(Latest))).await?
370 );
371 print_scalar(out)?;
372 }
373 CastSubcommand::Balance { block, who, ether, rpc, erc20, overrides } => {
374 if erc20.is_none() && !overrides.is_empty() {
375 eyre::bail!("call overrides require `--erc20` when using `cast balance`");
376 }
377 let config = rpc.load_config()?;
378 let provider = utils::get_provider(&config)?;
379 let account_addr = who.resolve(&provider).await?;
380
381 match erc20 {
382 Some(token) => {
383 let token = IERC20::new(token, &provider);
384 let balance_call =
385 token.balanceOf(account_addr).block(block.unwrap_or_default());
386 let call = balance_call.call();
387 let balance = overrides.apply(call)?.await?;
388
389 sh_warn!("--erc20 flag is deprecated, use `cast erc20 balance` instead")?;
390 print_scalar(format_uint_exp(balance))?;
391 }
392 None => {
393 let value = Cast::new(&provider).balance(account_addr, block).await?;
394 let out = if ether {
395 SimpleCast::from_wei(&value.to_string(), "eth")?
396 } else {
397 value.to_string()
398 };
399 print_scalar(out)?;
400 }
401 }
402 }
403 CastSubcommand::BaseFee { block, rpc } => {
404 let config = rpc.load_config()?;
405 let provider = utils::get_provider(&config)?;
406 let out = Cast::new(provider)
407 .base_fee(block.unwrap_or(BlockId::Number(Latest)))
408 .await?
409 .to_string();
410 print_scalar(out)?;
411 }
412 CastSubcommand::Block { block, full, fields, raw, rpc, network } => {
413 let config = rpc.load_config()?;
414 let is_raw_block = raw || fields.contains(&"raw".into());
416 let output = if is_raw_block {
417 match network {
418 #[cfg(feature = "optimism")]
419 Some(NetworkVariant::Optimism) => {
420 let provider =
421 ProviderBuilder::<Optimism>::from_config(&config)?.build()?;
422
423 Cast::new(&provider)
424 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
425 .await?
426 }
427 Some(NetworkVariant::Tempo) => {
428 let provider =
429 ProviderBuilder::<TempoNetwork>::from_config(&config)?.build()?;
430 Cast::new(&provider)
431 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
432 .await?
433 }
434 _ => {
436 let provider =
437 ProviderBuilder::<Ethereum>::from_config(&config)?.build()?;
438 Cast::new(&provider)
439 .block_raw(block.unwrap_or(BlockId::Number(Latest)), full)
440 .await?
441 }
442 }
443 } else {
444 let provider = utils::get_provider(&config)?;
445 Cast::new(provider)
446 .block(block.unwrap_or(BlockId::Number(Latest)), full, fields)
447 .await?
448 };
449 print_json_value_or_scalar(output)?;
450 }
451 CastSubcommand::BlockNumber { rpc, block } => {
452 let config = rpc.load_config()?;
453 let provider = utils::get_provider(&config)?;
454 let number = match block {
455 Some(id) => {
456 provider
457 .get_block(id)
458 .await?
459 .ok_or_else(|| eyre::eyre!("block {id:?} not found"))?
460 .header
461 .number
462 }
463 None => Cast::new(provider).block_number().await?,
464 };
465 print_scalar(number)?;
466 }
467 CastSubcommand::Chain { rpc } => {
468 let config = rpc.load_config()?;
469 let provider = utils::get_provider(&config)?;
470 let out = Cast::new(provider).chain().await?.to_string();
471 print_scalar(out)?;
472 }
473 CastSubcommand::ChainId { rpc } => {
474 let config = rpc.load_config()?;
475 let provider = utils::get_provider(&config)?;
476 let out = Cast::new(provider).chain_id().await?.to_string();
477 print_scalar(out)?;
478 }
479 CastSubcommand::Client { rpc } => {
480 let config = rpc.load_config()?;
481 let provider = utils::get_provider(&config)?;
482 let out = provider.get_client_version().await?;
483 print_scalar(out)?;
484 }
485 CastSubcommand::Code { block, who, disassemble, rpc } => {
486 let config = rpc.load_config()?;
487 let provider = utils::get_provider(&config)?;
488 let who = who.resolve(&provider).await?;
489 let out = Cast::new(provider).code(who, block, disassemble).await?;
490 print_scalar(out)?;
491 }
492 CastSubcommand::Codesize { block, who, rpc } => {
493 let config = rpc.load_config()?;
494 let provider = utils::get_provider(&config)?;
495 let who = who.resolve(&provider).await?;
496 let out = Cast::new(provider).codesize(who, block).await?;
497 print_scalar(out)?;
498 }
499 CastSubcommand::ComputeAddress { address, nonce, salt, init_code, init_code_hash, rpc } => {
500 let address = stdin::unwrap_line(address)?;
501 let computed = {
502 if let Some(init_code_hash) = init_code_hash {
504 address.create2(salt.unwrap_or(B256::ZERO), init_code_hash)
505 } else if let Some(init_code) = init_code {
506 address.create2(salt.unwrap_or(B256::ZERO), keccak256(hex::decode(init_code)?))
507 } else {
508 let config = rpc.load_config()?;
510 let provider = utils::get_provider(&config)?;
511 Cast::new(provider).compute_address(address, nonce).await?
512 }
513 };
514 let addr = computed.to_checksum(None);
515 print_scalar(addr)?;
516 }
517 CastSubcommand::Disassemble { bytecode } => {
518 let bytecode = stdin::unwrap_line(bytecode)?;
519 let out = SimpleCast::disassemble(&hex::decode(bytecode)?)?;
520 print_scalar(out)?;
521 }
522 CastSubcommand::Selectors { bytecode, resolve } => {
523 let bytecode = stdin::unwrap_line(bytecode)?;
524 let functions = SimpleCast::extract_functions(&bytecode)?;
525
526 let resolve_results: Vec<String> = if resolve {
527 let selectors = functions
528 .iter()
529 .map(|&(selector, ..)| SelectorKind::Function(selector))
530 .collect::<Vec<_>>();
531 let ds = decode_selectors(&selectors).await?;
532 ds.into_iter().map(|v| v.join("|")).collect()
533 } else {
534 vec![]
535 };
536
537 if shell::is_json() {
538 #[derive(serde::Serialize)]
539 struct SelectorInfo {
540 selector: String,
541 arguments: String,
542 state_mutability: String,
543 #[serde(skip_serializing_if = "Option::is_none")]
544 resolved: Option<String>,
545 }
546 let infos: Vec<SelectorInfo> = functions
547 .into_iter()
548 .enumerate()
549 .map(|(pos, (selector, arguments, state_mutability))| SelectorInfo {
550 selector: selector.to_string(),
551 arguments,
552 state_mutability: state_mutability.to_string(),
553 resolved: resolve_results.get(pos).cloned(),
554 })
555 .collect();
556 print_json_object(infos)?;
557 } else {
558 let max_args_len = functions.iter().map(|r| r.1.len()).max().unwrap_or(0);
559 let max_mutability_len = functions.iter().map(|r| r.2.len()).max().unwrap_or(0);
560 for (pos, (selector, arguments, state_mutability)) in
561 functions.into_iter().enumerate()
562 {
563 if resolve {
564 let resolved = &resolve_results[pos];
565 sh_println!(
566 "{selector}\t{arguments:max_args_len$}\t{state_mutability:max_mutability_len$}\t{resolved}"
567 )?
568 } else {
569 sh_println!("{selector}\t{arguments:max_args_len$}\t{state_mutability}")?
570 }
571 }
572 }
573 }
574 CastSubcommand::FindBlock(cmd) => cmd.run().await?,
575 CastSubcommand::GasPrice { rpc } => {
576 let config = rpc.load_config()?;
577 let provider = utils::get_provider(&config)?;
578 let out = Cast::new(provider).gas_price().await?.to_string();
579 print_scalar(out)?;
580 }
581 CastSubcommand::Index { key_type, key, slot_number } => {
582 let out = SimpleCast::index(&key_type, &key, &slot_number)?;
583 print_scalar(out)?;
584 }
585 CastSubcommand::IndexErc7201 { id, formula_id } => {
586 eyre::ensure!(formula_id == "erc7201", "unsupported formula ID: {formula_id}");
587 let id = stdin::unwrap_line(id)?;
588 let out = foundry_common::erc7201(&id).to_string();
589 print_scalar(out)?;
590 }
591 CastSubcommand::Implementation { block, beacon, who, rpc } => {
592 let config = rpc.load_config()?;
593 let provider = utils::get_provider(&config)?;
594 let who = who.resolve(&provider).await?;
595 let out = Cast::new(provider).implementation(who, beacon, block).await?;
596 print_scalar(out)?;
597 }
598 CastSubcommand::Admin { block, who, rpc } => {
599 let config = rpc.load_config()?;
600 let provider = utils::get_provider(&config)?;
601 let who = who.resolve(&provider).await?;
602 let out = Cast::new(provider).admin(who, block).await?;
603 print_scalar(out)?;
604 }
605 CastSubcommand::Nonce { block, who, rpc } => {
606 let config = rpc.load_config()?;
607 let provider = utils::get_provider(&config)?;
608 let who = who.resolve(&provider).await?;
609 let out = Cast::new(provider).nonce(who, block).await?;
610 print_scalar(out)?;
611 }
612 CastSubcommand::Codehash { block, who, slots, rpc } => {
613 let config = rpc.load_config()?;
614 let provider = utils::get_provider(&config)?;
615 let who = who.resolve(&provider).await?;
616 let out = Cast::new(provider).codehash(who, slots, block).await?;
617 print_scalar(out)?;
618 }
619 CastSubcommand::StorageRoot { block, who, slots, rpc } => {
620 let config = rpc.load_config()?;
621 let provider = utils::get_provider(&config)?;
622 let who = who.resolve(&provider).await?;
623 let out = Cast::new(provider).storage_root(who, slots, block).await?;
624 print_scalar(out)?;
625 }
626 CastSubcommand::ChannelId {
627 payer,
628 payee,
629 token,
630 salt,
631 operator,
632 authorized_signer,
633 expiring_nonce_hash,
634 reserve,
635 block,
636 rpc,
637 } => {
638 let config = rpc.load_config()?;
639 let provider = utils::get_provider(&config)?;
640 let payer = payer.resolve(&provider).await?;
641 let payee = payee.resolve(&provider).await?;
642 let token = token.resolve(&provider).await?;
643 let operator = match operator {
644 Some(operator) => operator.resolve(&provider).await?,
645 None => Address::ZERO,
646 };
647 let authorized_signer = match authorized_signer {
648 Some(authorized_signer) => authorized_signer.resolve(&provider).await?,
649 None => Address::ZERO,
650 };
651 let reserve = match reserve {
652 Some(reserve) => reserve.resolve(&provider).await?,
653 None => TIP20_CHANNEL_RESERVE_ADDRESS,
654 };
655
656 let channel_id = ITIP20ChannelReserve::new(reserve, &provider)
657 .computeChannelId(
658 payer,
659 payee,
660 operator,
661 token,
662 salt,
663 authorized_signer,
664 expiring_nonce_hash,
665 )
666 .block(block.unwrap_or_default())
667 .call()
668 .await?;
669 print_scalar(format!("{channel_id:#x}"))?;
670 }
671 CastSubcommand::Proof { address, slots, rpc, block } => {
672 let config = rpc.load_config()?;
673 let provider = utils::get_provider(&config)?;
674 let address = address.resolve(&provider).await?;
675 let value = provider
676 .get_proof(address, slots.into_iter().collect())
677 .block_id(block.unwrap_or_default())
678 .await?;
679 print_json_object(value)?;
680 }
681 CastSubcommand::Rpc(cmd) => cmd.run().await?,
682 CastSubcommand::Storage(cmd) => cmd.run().await?,
683
684 CastSubcommand::Call(cmd) => cmd.run().await?,
686 CastSubcommand::Estimate(cmd) => cmd.run().await?,
687 CastSubcommand::MakeTx(cmd) => cmd.run().await?,
688 CastSubcommand::PublishTx { raw_tx, cast_async, rpc } => {
689 let config = rpc.load_config()?;
690 let provider = utils::get_provider(&config)?;
691 let cast = Cast::new(&provider);
692 let pending_tx = cast.publish(raw_tx).await?;
693 let tx_hash = pending_tx.inner().tx_hash();
694
695 if cast_async {
696 print_scalar(format!("{tx_hash:#x}"))?;
697 } else {
698 let receipt = pending_tx.get_receipt().await?;
699 print_json_object(receipt)?;
700 }
701 }
702 CastSubcommand::Receipt { tx_hash, field, cast_async, confirmations, rpc } => {
703 let config = rpc.load_config()?;
704 let provider = utils::get_provider(&config)?;
705 sh_println!(
707 "{}",
708 CastTxSender::new(provider)
709 .receipt(tx_hash, field, confirmations, None, cast_async)
710 .await?
711 )?
712 }
713 CastSubcommand::Run(cmd) => cmd.run().await?,
714 CastSubcommand::SendTx(cmd) => cmd.run().await?,
715 CastSubcommand::BatchMakeTx(cmd) => cmd.run().await?,
716 CastSubcommand::BatchSend(cmd) => cmd.run().await?,
717 CastSubcommand::Classify { raw_tx } => {
718 let raw_tx = stdin::unwrap_line(raw_tx)?;
719 print_json_value_or_scalar(classify_raw_transaction_output(&raw_tx)?)?
720 }
721 CastSubcommand::Tx { tx_hash, from, nonce, field, raw, lane, rpc, to_request, network } => {
722 let config = rpc.load_config()?;
723 let is_raw = raw || field.as_ref().is_some_and(|f| f == "raw");
725 let output = match network {
726 #[cfg(feature = "optimism")]
727 Some(NetworkVariant::Optimism) => {
728 let provider = ProviderBuilder::<Optimism>::from_config(&config)?.build()?;
729
730 Cast::new(&provider)
731 .transaction(tx_hash, from, nonce, field, is_raw, to_request, lane)
732 .await?
733 }
734 Some(NetworkVariant::Tempo) => {
735 let provider =
736 ProviderBuilder::<TempoNetwork>::from_config(&config)?.build()?;
737 Cast::new(&provider)
738 .transaction(tx_hash, from, nonce, field, is_raw, to_request, lane)
739 .await?
740 }
741 _ => {
743 let provider = utils::get_provider(&config)?;
744 Cast::new(&provider)
745 .transaction(tx_hash, from, nonce, field, is_raw, to_request, lane)
746 .await?
747 }
748 };
749 print_json_value_or_scalar(output)?;
750 }
751
752 CastSubcommand::FourByte { selector } => {
754 let selector = stdin::unwrap_line(selector)?;
755 let sigs = decode_function_selector(selector).await?;
756 if sigs.is_empty() {
757 eyre::bail!("No matching function signatures found for selector `{selector}`");
758 }
759 print_list(&sigs)?;
760 }
761
762 CastSubcommand::FourByteCalldata { calldata } => {
765 let calldata = stdin::unwrap_line(calldata)?;
766
767 if calldata.len() == 10 {
768 let sigs = decode_function_selector(calldata.parse()?).await?;
769 if sigs.is_empty() {
770 eyre::bail!("No matching function signatures found for calldata `{calldata}`");
771 }
772 for sig in sigs {
773 sh_println!("{sig}")?
774 }
775 return Ok(());
776 }
777
778 let sigs = decode_calldata(&calldata).await?;
779 sigs.iter().enumerate().for_each(|(i, sig)| {
780 let _ = sh_println!("{}) \"{sig}\"", i + 1);
781 });
782
783 let sig = match sigs.len() {
784 0 => {
785 eyre::bail!("No signatures found");
786 }
787 1 => sigs.first().unwrap(),
788 _ => {
789 let i: usize = prompt!("Select a function signature by number: ")?;
790 sigs.get(i - 1).ok_or_else(|| eyre::eyre!("Invalid signature index"))?
791 }
792 };
793
794 let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?;
795 print_tokens(&tokens)?;
796 }
797
798 CastSubcommand::FourByteEvent { topic } => {
799 let topic = stdin::unwrap_line(topic)?;
800 let sigs = decode_event_topic(topic).await?;
801 if sigs.is_empty() {
802 eyre::bail!("No matching event signatures found for topic `{topic}`");
803 }
804 print_list(&sigs)?;
805 }
806 CastSubcommand::UploadSignature { signatures } => {
809 let signatures = stdin::unwrap_vec(signatures)?;
810 let ParsedSignatures { signatures, abis } = parse_signatures(signatures);
811 if !abis.is_empty() {
812 import_selectors(SelectorImportData::Abi(abis)).await?.describe();
813 }
814 if !signatures.is_empty() {
815 import_selectors(SelectorImportData::Raw(signatures)).await?.describe();
816 }
817 }
818
819 CastSubcommand::Namehash { name } => {
821 let name = stdin::unwrap_line(name)?;
822 let out = namehash(&name).to_string();
823 print_scalar(out)?;
824 }
825 CastSubcommand::LookupAddress { who, rpc, verify } => {
826 let config = rpc.load_config()?;
827 let provider = utils::get_provider(&config)?;
828
829 let who = stdin::unwrap_line(who)?;
830 let name = provider.lookup_address(&who).await?;
831 if verify {
832 let address = provider.resolve_name(&name).await?;
833 eyre::ensure!(
834 address == who,
835 "Reverse lookup verification failed: got `{address}`, expected `{who}`"
836 );
837 }
838 print_scalar(name)?;
839 }
840 CastSubcommand::ResolveName { who, rpc, verify } => {
841 let config = rpc.load_config()?;
842 let provider = utils::get_provider(&config)?;
843
844 let who = stdin::unwrap_line(who)?;
845 let address = provider
846 .resolve_name(&who)
847 .await
848 .wrap_err(format!("Failed to resolve ENS name: {who}"))?;
849 if verify {
850 let name = provider.lookup_address(&address).await?;
851 eyre::ensure!(
852 name == who,
853 "Forward lookup verification failed: got `{name}`, expected `{who}`"
854 );
855 }
856 print_scalar(address.to_string())?;
857 }
858
859 CastSubcommand::Keccak { data } => {
861 let bytes = match data {
862 Some(data) => data.into_bytes(),
863 None => stdin::read_bytes(false)?,
864 };
865 let out = match String::from_utf8(bytes) {
866 Ok(s) => SimpleCast::keccak(&s)?,
867 Err(e) => format!("0x{}", hex::encode(keccak256(e.as_bytes()))),
868 };
869 print_scalar(out)?;
870 }
871 CastSubcommand::HashMessage { message } => {
872 let message = stdin::unwrap(message, false)?;
873 let out = eip191_hash_message(message).to_string();
874 print_scalar(out)?;
875 }
876 CastSubcommand::SigEvent { event_string } => {
877 let event_string = stdin::unwrap_line(event_string)?;
878 let parsed_event = get_event(&event_string)?;
879 print_scalar(format!("{:?}", parsed_event.selector()))?;
880 }
881 CastSubcommand::LeftShift { value, bits, base_in, base_out } => {
882 let out = SimpleCast::left_shift(&value, &bits, base_in.as_deref(), &base_out)?;
883 print_scalar(out)?;
884 }
885 CastSubcommand::RightShift { value, bits, base_in, base_out } => {
886 let out = SimpleCast::right_shift(&value, &bits, base_in.as_deref(), &base_out)?;
887 print_scalar(out)?;
888 }
889 CastSubcommand::Source {
891 address,
892 directory,
893 explorer_api_url,
894 explorer_url,
895 etherscan,
896 flatten,
897 } => {
898 let config = etherscan.load_config()?;
899 let chain = config.chain.unwrap_or_default();
900 let api_key = config.get_etherscan_api_key(Some(chain));
901 match (directory, flatten) {
902 (Some(dir), false) => {
903 SimpleCast::expand_etherscan_source_to_directory(
904 chain,
905 address,
906 api_key,
907 dir,
908 explorer_api_url,
909 explorer_url,
910 )
911 .await?
912 }
913 (None, false) => sh_println!(
914 "{}",
915 SimpleCast::etherscan_source(
916 chain,
917 address,
918 api_key,
919 explorer_api_url,
920 explorer_url
921 )
922 .await?
923 )?,
924 (dir, true) => {
925 SimpleCast::etherscan_source_flatten(
926 chain,
927 address,
928 api_key,
929 dir,
930 explorer_api_url,
931 explorer_url,
932 )
933 .await?;
934 }
935 }
936 }
937 CastSubcommand::Create2(cmd) => {
938 cmd.run()?;
939 }
940 CastSubcommand::Wallet { command } => command.run().await?,
941 CastSubcommand::Completions { shell } => {
942 generate(shell, &mut CastArgs::command(), "cast", &mut std::io::stdout())
943 }
944 CastSubcommand::Logs(cmd) => cmd.run().await?,
945 CastSubcommand::DecodeTransaction { tx, network } => {
946 let tx = stdin::unwrap_line(tx)?;
947 let decoded_tx = match network {
948 #[cfg(feature = "optimism")]
949 Some(NetworkVariant::Optimism) => {
950 SimpleCast::decode_raw_transaction::<Optimism>(&tx)?
951 }
952 Some(NetworkVariant::Tempo) => {
953 SimpleCast::decode_raw_transaction::<TempoNetwork>(&tx)?
954 }
955 Some(NetworkVariant::Ethereum) => {
956 SimpleCast::decode_raw_transaction::<Ethereum>(&tx)?
957 }
958 None => SimpleCast::decode_raw_transaction::<FoundryNetwork>(&tx)?,
962 };
963 print_json_object(decoded_tx)?;
964 }
965 CastSubcommand::RecoverAuthority { auth } => {
966 let auth: SignedAuthorization = serde_json::from_str(&auth)?;
967 let out = auth.recover_authority()?.to_string();
968 print_scalar(out)?;
969 }
970 CastSubcommand::TxPool { command } => command.run().await?,
971 CastSubcommand::Erc20Token { command } => command.run().await?,
972 CastSubcommand::Tip20Token { command } => command.run().await?,
973 CastSubcommand::ReceivePolicy { command } => command.run().await?,
974 CastSubcommand::Tip403 { command } => command.run().await?,
975 CastSubcommand::StorageCredits { command } => command.run().await?,
976 CastSubcommand::Keychain { command } => command.run().await?,
977 CastSubcommand::KeyAuthorization { command } => command.run().await?,
978 CastSubcommand::Tempo { command } => command.run().await?,
979 CastSubcommand::VirtualAddress { command } => command.run().await?,
980 #[cfg(feature = "optimism")]
981 CastSubcommand::DAEstimate(cmd) => {
982 cmd.run().await?;
983 }
984 CastSubcommand::Trace(cmd) => cmd.run().await?,
985 };
986
987 Ok(())
988}
989
990pub(crate) fn classify_raw_transaction_output(raw_tx: &str) -> Result<String> {
991 let raw_tx = hex::decode(raw_tx)?;
992 let mut data = raw_tx.as_slice();
993 FoundryTxEnvelope::decode_2718(&mut data).wrap_err("failed to decode raw transaction")?;
994 format_lane_classification(&classify_payment_lane(&raw_tx))
995}
996
997pub(crate) fn format_lane_classification(
998 classification: &PaymentLaneClassification,
999) -> Result<String> {
1000 if shell::is_json() {
1001 Ok(serde_json::to_string_pretty(classification)?)
1002 } else {
1003 Ok(serde_json::to_string(classification)?)
1004 }
1005}