1use crate::{
2 base::{Base, NumberWithBase},
3 cmd::{erc20::IERC20, rpc_provider},
4 opts::{
5 AbiSubcommand, Cast as CastArgs, CastSubcommand, ConversionSubcommand, MiscSubcommand,
6 QuerySubcommand, ToBaseArgs, TransactionSubcommand,
7 },
8 traces::identifier::SignaturesIdentifier,
9 tx::CastTxSender,
10};
11use alloy_consensus::{
12 Typed2718,
13 transaction::{Recovered, SignerRecoverable},
14};
15use alloy_dyn_abi::{DynSolType, DynSolValue, ErrorExt, EventExt, Specifier};
16use alloy_eips::{Encodable2718, eip7702::SignedAuthorization};
17use alloy_ens::{NameOrAddress, ProviderEnsExt, namehash};
18use alloy_network::{BlockResponse, Ethereum, Network, eip2718::Decodable2718};
19use alloy_primitives::{
20 Address, B256, Bytes, I256, Keccak256, LogData, TxHash, U64, U256, b256, eip191_hash_message,
21 hex, keccak256,
22 utils::{ParseUnits, Unit},
23};
24use alloy_provider::Provider;
25use alloy_rlp::{decode_exact, encode};
26use alloy_rpc_types::BlockId;
27use clap::{CommandFactory, Parser};
28use clap_complete::generate;
29use eyre::{ContextCompat, OptionExt, Result, WrapErr};
30use foundry_block_explorers::Client;
31use foundry_cli::{
32 json::{print_json_object, print_json_value_or_scalar, print_list, print_scalar, print_tokens},
33 opts::RpcOpts,
34 utils::{self, LoadConfig},
35};
36use foundry_common::{
37 abi::{
38 abi_decode_calldata, abi_decode_event_data, encode_function_args,
39 encode_function_args_packed, get_error, get_event, get_func,
40 },
41 fmt::{UIfmt, UIfmtSignatureExt, format_uint_exp, get_pretty_block_attr, get_pretty_tx_attr},
42 fs,
43 provider::{ProviderBuilder, RetryProvider},
44 selectors::{
45 ParsedSignatures, SelectorImportData, SelectorKind, decode_calldata, decode_event_topic,
46 decode_function_selector, decode_selectors, import_selectors, parse_signatures,
47 pretty_calldata,
48 },
49 shell, stdin,
50 tempo::classify_payment_lane,
51};
52use foundry_config::Chain;
53use foundry_evm_networks::NetworkVariant;
54use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope};
55use rayon::prelude::*;
56use serde::Serialize;
57use std::{
58 str::FromStr,
59 sync::atomic::{AtomicBool, Ordering},
60 time::Instant,
61};
62use tempo_alloy::TempoNetwork;
63use tempo_contracts::precompiles::{ITIP20ChannelReserve, TIP20_CHANNEL_RESERVE_ADDRESS};
64
65#[cfg(feature = "base")]
66use base_common_network::Base as BaseNetwork;
67
68#[cfg(feature = "optimism")]
69use op_alloy_network::Optimism;
70
71macro_rules! with_network_provider {
77 ($network:expr, $config:expr, $default:expr, |$provider:ident $(, $network_type:ident)?| $body:expr) => {
78 with_network_provider!($network, $config, |$provider $(, $network_type)?| $body, _ => {
79 $(type $network_type = Ethereum;)?
80 let $provider = $default;
81 $body
82 })
83 };
84 ($network:expr, $config:expr, |$provider:ident $(, $network_type:ident)?| $body:expr, _ => $default:expr) => {
85 match $network {
86 #[cfg(feature = "base")]
87 Some(NetworkVariant::Base) => {
88 $(type $network_type = BaseNetwork;)?
89 let $provider = ProviderBuilder::<BaseNetwork>::from_config($config)?.build()?;
90 $body
91 }
92 #[cfg(feature = "optimism")]
93 Some(NetworkVariant::Optimism) => {
94 $(type $network_type = Optimism;)?
95 let $provider = ProviderBuilder::<Optimism>::from_config($config)?.build()?;
96 $body
97 }
98 Some(NetworkVariant::Tempo) => {
99 $(type $network_type = TempoNetwork;)?
100 let $provider = ProviderBuilder::<TempoNetwork>::from_config($config)?.build()?;
101 $body
102 }
103 _ => $default,
104 }
105 };
106}
107
108pub fn run() -> Result<()> {
110 foundry_cli::opts::GlobalArgs::check_markdown_help::<CastArgs>();
111
112 let warnings = setup()?;
113
114 let args = CastArgs::parse();
115 args.global.init()?;
116 for warning in warnings {
117 let _ = foundry_common::sh_warn!("{warning}");
118 }
119 args.global.tokio_runtime().block_on(run_command(args))
120}
121
122pub fn setup() -> Result<Vec<String>> {
124 let warnings = utils::common_setup();
125 utils::subscriber();
126
127 Ok(warnings)
128}
129
130#[allow(clippy::large_stack_frames)]
132pub async fn run_command(args: CastArgs) -> Result<()> {
133 match args.cmd {
134 CastSubcommand::Conversion(
136 ConversionSubcommand::MaxInt { r#type } | ConversionSubcommand::MaxUint { r#type },
137 ) => {
138 print_scalar(int_bound(&r#type, true)?)?;
139 }
140 CastSubcommand::Conversion(ConversionSubcommand::MinInt { r#type }) => {
141 print_scalar(int_bound(&r#type, false)?)?
142 }
143 CastSubcommand::Conversion(ConversionSubcommand::AddressZero) => {
144 print_scalar(format!("{:?}", Address::ZERO))?
145 }
146 CastSubcommand::Conversion(ConversionSubcommand::HashZero) => {
147 print_scalar(format!("{:?}", B256::ZERO))?
148 }
149
150 CastSubcommand::Conversion(ConversionSubcommand::FromUtf8 { text }) => {
152 print_scalar(hex::encode_prefixed(stdin::unwrap(text, false)?))?;
153 }
154 CastSubcommand::Conversion(ConversionSubcommand::ToAscii { hexdata }) => {
155 let bytes = hex::decode(stdin::unwrap(hexdata, false)?.trim())?;
156 eyre::ensure!(bytes.iter().all(u8::is_ascii), "Invalid ASCII bytes");
157 print_scalar(String::from_utf8(bytes).unwrap())?;
158 }
159 CastSubcommand::Conversion(ConversionSubcommand::ToUtf8 { hexdata }) => {
160 let bytes = hex::decode(stdin::unwrap(hexdata, false)?)?;
161 print_scalar(String::from_utf8_lossy(&bytes).into_owned())?;
162 }
163 CastSubcommand::Conversion(ConversionSubcommand::FromFixedPoint { value, decimals }) => {
164 let (value, decimals) = stdin::unwrap2(value, decimals)?;
165 print_scalar(ParseUnits::parse_units(&value, Unit::from_str(&decimals)?)?.to_string())?;
166 }
167 CastSubcommand::Conversion(ConversionSubcommand::ToFixedPoint { value, decimals }) => {
168 let (value, decimals) = stdin::unwrap2(value, decimals)?;
169
170 let number = NumberWithBase::parse_int(&value, None)?;
171 let sign = if number.is_nonnegative() { "" } else { "-" };
172 let mut value = number.to_string().trim_start_matches('-').to_string();
173 let value_len = value.len();
174 let decimals_num = NumberWithBase::parse_uint(&decimals, None)?.number();
175 let decimals: usize = decimals_num
176 .try_into()
177 .ok()
178 .filter(|&d: &usize| d <= u16::MAX as usize)
179 .ok_or_else(|| eyre::eyre!("decimals out of range: {decimals_num}"))?;
180
181 if decimals >= value_len {
182 value = format!("0.{value:0>decimals$}");
183 } else {
184 value.insert(value_len - decimals, '.');
185 }
186 print_scalar(format!("{sign}{value}"))?;
187 }
188 CastSubcommand::Conversion(ConversionSubcommand::ConcatHex { data }) => {
189 let input;
190 let values = if data.is_empty() {
191 input = stdin::read(true)?;
192 itertools::Either::Left(input.split_whitespace())
193 } else {
194 itertools::Either::Right(data.iter().map(String::as_str))
195 };
196 let out = values.map(strip_0x).collect::<String>();
197 print_scalar(format!("0x{out}"))?;
198 }
199 CastSubcommand::Conversion(ConversionSubcommand::FromBin) => {
200 print_scalar(hex::encode_prefixed(stdin::read_bytes(false)?))?;
201 }
202 CastSubcommand::Conversion(ConversionSubcommand::ToHexdata { input }) => {
203 let value = stdin::unwrap_line(input)?;
204 let output = match value {
205 s if s.starts_with('@') => hex::encode(std::env::var(&s[1..])?),
206 s if s.starts_with('/') => hex::encode(fs::read(s)?),
207 s => s.split(':').map(|s| s.trim_start_matches("0x").to_lowercase()).collect(),
208 };
209 print_scalar(format!("0x{output}"))?;
210 }
211 CastSubcommand::Conversion(ConversionSubcommand::ToCheckSumAddress {
212 address,
213 chain_id,
214 }) => {
215 print_scalar(stdin::unwrap_line(address)?.to_checksum(chain_id))?;
216 }
217 CastSubcommand::Conversion(ConversionSubcommand::ToUint256 { value }) => {
218 let n = NumberWithBase::parse_uint(&stdin::unwrap_line(value)?, None)?;
219 print_scalar(format!("{n:#066x}"))?;
220 }
221 CastSubcommand::Conversion(ConversionSubcommand::ToInt256 { value }) => {
222 let n = NumberWithBase::parse_int(&stdin::unwrap_line(value)?, None)?;
223 print_scalar(format!("{n:#066x}"))?;
224 }
225 CastSubcommand::Conversion(ConversionSubcommand::ToUnit { value, unit }) => {
226 let value = stdin::unwrap_line(value)?;
227 let value = DynSolType::coerce_str(&DynSolType::Uint(256), &value)?
228 .as_uint()
229 .wrap_err("Could not convert to uint")?
230 .0;
231 let unit = unit.parse().wrap_err("could not parse units")?;
232 print_scalar(format_unit_as_string(ParseUnits::U256(value), unit))?;
233 }
234 CastSubcommand::Conversion(ConversionSubcommand::ParseUnits { value, unit }) => {
235 let value = stdin::unwrap_line(value)?;
236 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
237
238 print_scalar(ParseUnits::parse_units(&value, unit)?.to_string())?;
239 }
240 CastSubcommand::Conversion(ConversionSubcommand::FormatUnits { value, unit }) => {
241 print_scalar(format_units(&stdin::unwrap_line(value)?, unit)?)?;
242 }
243 CastSubcommand::Conversion(ConversionSubcommand::FromWei { value, unit }) => {
244 print_scalar(
245 signed_parse_units(&NumberWithBase::parse_int(&stdin::unwrap_line(value)?, None)?)?
246 .format_units(unit.parse()?),
247 )?;
248 }
249 CastSubcommand::Conversion(ConversionSubcommand::ToWei { value, unit }) => {
250 let value = stdin::unwrap_line(value)?;
251 let unit = unit.parse().wrap_err("could not parse units")?;
252 print_scalar(ParseUnits::parse_units(&value, unit)?.to_string())?;
253 }
254 CastSubcommand::Conversion(ConversionSubcommand::FromRlp { value, as_int }) => {
255 let bytes = hex::decode(stdin::unwrap_line(value)?).wrap_err("Could not decode hex")?;
256 let value = if as_int {
257 decode_exact::<U256>(&bytes)?.to_string()
258 } else {
259 decode_exact::<crate::rlp_converter::Item>(&bytes)
260 .wrap_err("Could not decode rlp")?
261 .to_string()
262 };
263 print_scalar(value)?;
264 }
265 CastSubcommand::Conversion(ConversionSubcommand::ToRlp { value }) => {
266 let value = stdin::unwrap_line(value)?;
267 let val =
268 serde_json::from_str(&value).unwrap_or_else(|_| serde_json::Value::String(value));
269 let item = crate::rlp_converter::Item::value_to_item(&val)?;
270 print_scalar(hex::encode_prefixed(encode(item)))?;
271 }
272 CastSubcommand::Conversion(ConversionSubcommand::ToHex(ToBaseArgs { value, base_in })) => {
273 let value = stdin::unwrap_line(value)?;
274 print_scalar(to_base(&value, base_in.as_deref(), "hex")?)?;
275 }
276 CastSubcommand::Conversion(ConversionSubcommand::ToDec(ToBaseArgs { value, base_in })) => {
277 let value = stdin::unwrap_line(value)?;
278 print_scalar(to_base(&value, base_in.as_deref(), "dec")?)?;
279 }
280 CastSubcommand::Conversion(ConversionSubcommand::ToBase {
281 base: ToBaseArgs { value, base_in },
282 base_out,
283 }) => {
284 let (value, base_out) = stdin::unwrap2(value, base_out)?;
285 print_scalar(to_base(&value, base_in.as_deref(), &base_out)?)?;
286 }
287 CastSubcommand::Conversion(ConversionSubcommand::ToBytes32 { bytes }) => {
288 let s = stdin::unwrap_line(bytes)?;
289 let s = strip_0x(&s);
290 if s.len() > 64 {
291 eyre::bail!("string >32 bytes");
292 }
293
294 let padded = format!("{s:0<64}");
295 print_scalar(padded.parse::<B256>()?.to_string())?;
296 }
297 CastSubcommand::Conversion(ConversionSubcommand::ToBytesMemory { data }) => {
298 let data = stdin::unwrap_line(data)?;
299 const WORD: usize = 32;
300
301 let data = hex::decode(data).wrap_err("Could not decode hex")?;
302 let padded_len = data.len().next_multiple_of(WORD);
303 let mut out = Vec::with_capacity(WORD + padded_len);
304 out.extend_from_slice(&U256::from(data.len()).to_be_bytes::<WORD>());
305 out.extend_from_slice(&data);
306 out.resize(WORD + padded_len, 0);
307 print_scalar(hex::encode_prefixed(out))?;
308 }
309 CastSubcommand::Conversion(ConversionSubcommand::Pad { data, right, left: _, len }) => {
310 let s = stdin::unwrap_line(data)?;
311 let s = strip_0x(&s);
312 let hex_len = len
313 .checked_mul(2)
314 .filter(|&h| h <= u16::MAX as usize)
315 .ok_or_else(|| eyre::eyre!("len out of range: {len}"))?;
316
317 if s.len() > hex_len {
319 eyre::bail!("input length exceeds target length");
320 }
321 if !s.chars().all(|c| c.is_ascii_hexdigit()) {
322 eyre::bail!("input is not a valid hex");
323 }
324
325 print_scalar(if right {
326 format!("0x{s:0<hex_len$}")
327 } else {
328 format!("0x{s:0>hex_len$}")
329 })?;
330 }
331 CastSubcommand::Conversion(ConversionSubcommand::FormatBytes32String { string }) => {
332 let s = stdin::unwrap_line(string)?;
333 let str_bytes: &[u8] = s.as_bytes();
334 eyre::ensure!(
335 str_bytes.len() <= 32,
336 "bytes32 strings must not exceed 32 bytes in length"
337 );
338
339 let mut bytes32: [u8; 32] = [0u8; 32];
340 bytes32[..str_bytes.len()].copy_from_slice(str_bytes);
341 print_scalar(hex::encode_prefixed(bytes32))?;
342 }
343 CastSubcommand::Conversion(ConversionSubcommand::ParseBytes32String { bytes }) => {
344 let s = stdin::unwrap_line(bytes)?;
345 let bytes = hex::decode(s)?;
346 eyre::ensure!(bytes.len() == 32, "expected 32 byte hex-string");
347 let len = bytes.iter().take_while(|x| **x != 0).count();
348 print_scalar(std::str::from_utf8(&bytes[..len])?)?;
349 }
350 CastSubcommand::Conversion(ConversionSubcommand::ParseBytes32Address { bytes }) => {
351 let s = stdin::unwrap_line(bytes)?;
352 let s = strip_0x(&s);
353 if s.len() != 64 {
354 eyre::bail!("expected 64 byte hex-string, got {s}");
355 }
356 let Some(s) = s.strip_prefix("000000000000000000000000") else {
357 eyre::bail!("Not convertible to address, there are non-zero bytes");
358 };
359 print_scalar(Address::from_str(s)?.to_checksum(None))?;
360 }
361
362 CastSubcommand::Abi(AbiSubcommand::DecodeAbi { sig, calldata, input }) => {
364 print_tokens(&abi_decode_calldata(&sig, &calldata, input, false)?)?;
365 }
366 CastSubcommand::Abi(AbiSubcommand::AbiEncode { sig, packed, args }) => {
367 let out = if packed {
368 let sig = if sig.trim_start().starts_with('(') { format!("foo{sig}") } else { sig };
370
371 let func = get_func(&sig)?;
372 let encoded = encode_function_args_packed(&func, &args).map_err(|e| {
373 eyre::eyre!("Could not ABI encode the function and arguments: {e}")
374 })?;
375 hex::encode_prefixed(encoded)
376 } else {
377 let func = get_func(&sig)?;
378 let encoded = encode_function_args(&func, &args).map_err(|e| {
379 eyre::eyre!("Could not ABI encode the function and arguments: {e}")
380 })?;
381 hex::encode_prefixed(&encoded[4..])
382 };
383 print_scalar(out)?;
384 }
385 CastSubcommand::Abi(AbiSubcommand::AbiEncodeEvent { sig, args }) => {
386 let event = get_event(&sig)?;
387 if event.inputs.len() != args.len() {
388 eyre::bail!(
389 "encode length mismatch: expected {} types, got {}",
390 event.inputs.len(),
391 args.len(),
392 );
393 }
394
395 let types = event
396 .inputs
397 .iter()
398 .map(Specifier::<DynSolType>::resolve)
399 .collect::<Result<Vec<_>, _>>()?;
400 let tokens = std::iter::zip(&types, &args)
401 .map(|(ty, arg)| Ok(DynSolType::coerce_str(ty, arg.as_ref())?))
402 .collect::<Result<Vec<_>>>()?;
403
404 let mut topics = if event.anonymous { vec![] } else { vec![event.selector()] };
405 let mut data_tokens = Vec::new();
407 for (input, token) in event.inputs.iter().zip(tokens) {
408 if input.indexed {
409 topics.push(encode_event_topic(&token));
410 } else {
411 data_tokens.push(token);
412 }
413 }
414
415 let data = DynSolValue::Tuple(data_tokens).abi_encode_params();
416 let log_data = LogData::new_unchecked(topics, data.into());
417 if shell::is_json() {
418 #[derive(serde::Serialize)]
419 struct EncodedEvent {
420 topics: Vec<String>,
421 data: String,
422 }
423 print_json_object(EncodedEvent {
424 topics: log_data.topics().iter().map(|t| t.to_string()).collect(),
425 data: hex::encode_prefixed(&log_data.data),
426 })?;
427 } else {
428 for (i, topic) in log_data.topics().iter().enumerate() {
429 sh_println!("[topic{i}]: {topic}")?;
430 }
431 if !log_data.data.is_empty() {
432 sh_println!("[data]: {}", hex::encode_prefixed(log_data.data))?;
433 }
434 }
435 }
436 CastSubcommand::Abi(AbiSubcommand::DecodeCalldata { sig, calldata, file }) => {
437 let raw_hex = match file {
438 Some(file_path) => fs::read_to_string(&file_path)?.trim().to_string(),
439 None => calldata.unwrap(),
440 };
441 print_tokens(&abi_decode_calldata(&sig, &raw_hex, true, true)?)?;
442 }
443 CastSubcommand::Abi(AbiSubcommand::CalldataEncode { sig, args, file }) => {
444 let args = match file {
445 Some(file_path) => fs::read_to_string(file_path)?
446 .lines()
447 .map(str::trim)
448 .filter(|line| !line.is_empty())
449 .map(String::from)
450 .collect(),
451 None => args,
452 };
453 print_scalar(hex::encode_prefixed(encode_function_args(&get_func(&sig)?, &args)?))?;
454 }
455 CastSubcommand::Abi(AbiSubcommand::DecodeString { data }) => {
456 print_tokens(&abi_decode_calldata("Any(string)", &data, true, true)?)?;
457 }
458 CastSubcommand::Abi(AbiSubcommand::DecodeEvent { sig, data, topics }) => {
459 let decoded_values = if let Some(event_sig) = sig {
460 let event = get_event(&event_sig)?;
461 let data = hex::decode(data)?;
462 if topics.is_empty() {
463 abi_decode_event_data(&event, &data)?
464 } else {
465 let decoded = event.decode_log_parts(topics, &data)?;
466 let mut indexed = decoded.indexed.into_iter();
467 let mut body = decoded.body.into_iter();
468 event
469 .inputs
470 .iter()
471 .map(|input| {
472 if input.indexed { indexed.next() } else { body.next() }
473 .expect("decoded event has a value for each input")
474 })
475 .collect()
476 }
477 } else {
478 let data = strip_0x(&data);
479 let selector: B256 = data.get(..64).unwrap_or_default().parse()?;
480 let Some(event) = SignaturesIdentifier::new(false)?.identify_event(selector).await
481 else {
482 eyre::bail!("No matching event signature found for selector `{selector}`");
483 };
484 let _ = sh_println!("{}", event.signature());
485 let data = data.get(64..).unwrap_or_default();
486 get_event(&event.signature())?
487 .decode_log_parts(core::iter::once(selector), &hex::decode(data)?)?
488 .body
489 };
490 print_tokens(&decoded_values)?;
491 }
492 CastSubcommand::Abi(AbiSubcommand::DecodeError { sig, data }) => {
493 let error = if let Some(err_sig) = sig {
494 get_error(&err_sig)?
495 } else {
496 let data = strip_0x(&data);
497 let selector = data.get(..8).unwrap_or_default();
498 let Some(error) =
499 SignaturesIdentifier::new(false)?.identify_error(selector.parse()?).await
500 else {
501 eyre::bail!("No matching error signature found for selector `{selector}`");
502 };
503 let _ = sh_println!("{}", error.signature());
504 error
505 };
506 print_tokens(&error.decode_error(&hex::decode(data)?)?.body)?;
507 }
508 CastSubcommand::Abi(AbiSubcommand::Interface(cmd)) => cmd.run().await?,
509 CastSubcommand::Abi(AbiSubcommand::CreationCode(cmd)) => cmd.run().await?,
510 CastSubcommand::Abi(AbiSubcommand::ConstructorArgs(cmd)) => cmd.run().await?,
511 CastSubcommand::Abi(AbiSubcommand::Artifact(cmd)) => cmd.run().await?,
512 CastSubcommand::Abi(AbiSubcommand::Bind(cmd)) => cmd.run().await?,
513 CastSubcommand::Abi(AbiSubcommand::B2EPayload(cmd)) => cmd.run().await?,
514 CastSubcommand::Abi(AbiSubcommand::PrettyCalldata { calldata, offline }) => {
515 let calldata = stdin::unwrap_line(calldata)?;
516 print_scalar(pretty_calldata(&calldata, offline).await?.to_string())?;
517 }
518 CastSubcommand::Abi(AbiSubcommand::Sig { sig, optimize }) => {
520 let sig = stdin::unwrap_line(sig)?;
521 match optimize {
522 Some(opt) => {
523 sh_status!("Starting to optimize signature...")?;
524 let start_time = Instant::now();
525 let (selector, signature) = get_selector(&sig, opt)?;
526 sh_status!("Successfully generated in {:?}", start_time.elapsed())?;
527 sh_println!("Selector: {selector}")?;
528 sh_println!("Optimized signature: {signature}")?;
529 }
530 None => print_scalar(get_selector(&sig, 0)?.0)?,
531 }
532 }
533
534 CastSubcommand::Query(QuerySubcommand::AccessList(cmd)) => cmd.run().await?,
536 CastSubcommand::Query(QuerySubcommand::Age { block, rpc }) => {
537 let timestamp = rpc_provider(&rpc)?
538 .get_block(block.unwrap_or_default())
539 .await?
540 .ok_or_eyre("block not found")?
541 .header
542 .timestamp;
543 let age = i64::try_from(timestamp)
544 .ok()
545 .and_then(|timestamp| chrono::DateTime::from_timestamp(timestamp, 0))
546 .ok_or_eyre("invalid timestamp")?
547 .format("%a %b %e %H:%M:%S %Y");
548 print_scalar(format!("{age} UTC"))?;
549 }
550 CastSubcommand::Query(QuerySubcommand::Balance {
551 block,
552 who,
553 ether,
554 rpc,
555 erc20,
556 overrides,
557 }) => {
558 if erc20.is_none() && !overrides.is_empty() {
559 eyre::bail!("call overrides require `--erc20` when using `cast balance`");
560 }
561 let (provider, account_addr) = rpc_provider_and_address(&rpc, who).await?;
562
563 match erc20 {
564 Some(token) => {
565 let token = IERC20::new(token, &provider);
566 let balance_call =
567 token.balanceOf(account_addr).block(block.unwrap_or_default());
568 let balance = overrides.apply(balance_call.call())?.await?;
569
570 sh_warn!("--erc20 flag is deprecated, use `cast erc20 balance` instead")?;
571 print_scalar(format_uint_exp(balance))?;
572 }
573 None => {
574 let value = provider
575 .get_balance(account_addr)
576 .block_id(block.unwrap_or_default())
577 .await?;
578 let out = if ether {
579 ParseUnits::U256(value).format_units(Unit::ETHER)
580 } else {
581 value.to_string()
582 };
583 print_scalar(out)?;
584 }
585 }
586 }
587 CastSubcommand::Query(QuerySubcommand::BaseFee { block, rpc }) => {
588 let fee = rpc_provider(&rpc)?
589 .get_block(block.unwrap_or_default())
590 .await?
591 .ok_or_eyre("block not found")?
592 .header
593 .base_fee_per_gas
594 .ok_or_eyre("base fee not found")?;
595 print_scalar(fee.to_string())?;
596 }
597 CastSubcommand::Query(QuerySubcommand::Block {
598 block,
599 full,
600 fields,
601 raw,
602 rpc,
603 network,
604 }) => {
605 let config = rpc.load_config()?;
606 #[cfg(feature = "base")]
607 let network = if network.is_none() && (raw || fields.iter().any(|f| f == "raw")) {
608 crate::cmd::resolve_transaction_network(&config, false)
609 .await?
610 .is_base()
611 .then_some(NetworkVariant::Base)
612 } else {
613 network
614 };
615 let block = block.unwrap_or_default();
616 let output = if raw || fields.contains(&"raw".into()) {
618 with_network_provider!(
619 network,
620 &config,
621 ProviderBuilder::<Ethereum>::from_config(&config)?.build()?,
622 |provider, N| {
623 let block_id = block;
624 let block = provider
625 .get_block(block_id)
626 .kind(full.into())
627 .await?
628 .ok_or_else(|| eyre::eyre!("block {:?} not found", block_id))?;
629 hex::encode_prefixed(alloy_rlp::encode(
630 AsRef::<<N as Network>::Header>::as_ref(block.header()),
631 ))
632 }
633 )
634 } else {
635 let provider = utils::get_provider(&config)?;
636 if fields.contains(&"transactions".into()) && !full {
637 eyre::bail!("use --full to view transactions");
638 }
639
640 let block = provider
641 .get_block(block)
642 .kind(full.into())
643 .await?
644 .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?;
645
646 if !fields.is_empty() {
647 let mut result = String::new();
648 for field in fields {
649 result.push_str(
650 &get_pretty_block_attr::<alloy_network::AnyNetwork>(&block, &field)
651 .unwrap_or_else(|| format!("{field} is not a valid block field")),
652 );
653
654 result.push('\n');
655 }
656 result.trim_end().to_string()
657 } else if shell::is_json() {
658 serde_json::to_value(&block).unwrap().to_string()
659 } else {
660 block.pretty()
661 }
662 };
663 print_json_value_or_scalar(output)?;
664 }
665 CastSubcommand::Query(QuerySubcommand::BlockNumber { rpc, block }) => {
666 let provider = rpc_provider(&rpc)?;
667 let number = match block {
668 Some(id) => {
669 provider
670 .get_block(id)
671 .await?
672 .ok_or_else(|| eyre::eyre!("block {id:?} not found"))?
673 .header
674 .number
675 }
676 None => provider.get_block_number().await?,
677 };
678 print_scalar(number)?;
679 }
680 CastSubcommand::Query(QuerySubcommand::Bal(cmd)) => cmd.run().await?,
681 CastSubcommand::Query(QuerySubcommand::Chain { rpc }) => {
682 let provider = rpc_provider(&rpc)?;
683 const GENESIS_CHAINS: &[(&str, &str)] = &[
684 ("0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9", "kovan"),
685 ("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", "ropsten"),
686 (
687 "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b",
688 "optimism-mainnet",
689 ),
690 (
691 "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1",
692 "optimism-goerli",
693 ),
694 (
695 "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5",
696 "optimism-kovan",
697 ),
698 ("0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa", "fraxtal"),
699 (
700 "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489",
701 "fraxtal-testnet",
702 ),
703 (
704 "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442",
705 "arbitrum-mainnet",
706 ),
707 ("0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303", "morden"),
708 ("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", "rinkeby"),
709 ("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a", "goerli"),
710 ("0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4", "kotti"),
711 (
712 "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b",
713 "polygon-pos",
714 ),
715 (
716 "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697",
717 "polygon-pos-amoy-testnet",
718 ),
719 (
720 "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741",
721 "polygon-zkevm",
722 ),
723 (
724 "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9",
725 "polygon-zkevm-cardona-testnet",
726 ),
727 ("0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756", "gnosis"),
728 ("0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a", "chiado"),
729 ("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34", "bsctest"),
730 ("0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b", "bsc"),
731 ("0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee", "ink"),
732 (
733 "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834",
734 "ink-sepolia",
735 ),
736 ];
737
738 let genesis_hash = provider
739 .get_block_by_number(0.into())
740 .await?
741 .ok_or_eyre("block not found")?
742 .header
743 .hash
744 .to_string();
745 let chain = match genesis_hash.as_str() {
746 "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => {
748 match provider
749 .get_block_by_number(1920000.into())
750 .await?
751 .ok_or_eyre("block not found")?
752 .header
753 .hash
754 .to_string()
755 .as_str()
756 {
757 "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => {
758 "etclive"
759 }
760 _ => "ethlive",
761 }
762 }
763 "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => {
765 match provider
766 .get_block_by_number(1.into())
767 .await?
768 .ok_or_eyre("block not found")?
769 .header
770 .hash
771 .to_string()
772 .as_str()
773 {
774 "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => {
775 "avalanche-fuji"
776 }
777 _ => "avalanche",
778 }
779 }
780 hash => GENESIS_CHAINS
781 .iter()
782 .find(|(genesis, _)| *genesis == hash)
783 .map_or("unknown", |(_, chain)| chain),
784 };
785 print_scalar(chain)?;
786 }
787 CastSubcommand::Query(QuerySubcommand::ChainId { rpc }) => {
788 print_scalar(rpc_provider(&rpc)?.get_chain_id().await?.to_string())?;
789 }
790 CastSubcommand::Query(QuerySubcommand::Client { rpc }) => {
791 print_scalar(rpc_provider(&rpc)?.get_client_version().await?)?;
792 }
793 CastSubcommand::Query(QuerySubcommand::Code { block, who, disassemble, rpc }) => {
794 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
795 let code = provider.get_code_at(who).block_id(block.unwrap_or_default()).await?;
796 print_scalar(if disassemble {
797 crate::cmd::disassemble(&code)?
798 } else {
799 code.to_string()
800 })?;
801 }
802 CastSubcommand::Query(QuerySubcommand::Codesize { block, who, rpc }) => {
803 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
804 print_scalar(
805 provider
806 .get_code_at(who)
807 .block_id(block.unwrap_or_default())
808 .await?
809 .len()
810 .to_string(),
811 )?;
812 }
813 CastSubcommand::Query(QuerySubcommand::ComputeAddress {
814 address,
815 nonce,
816 salt,
817 init_code,
818 init_code_hash,
819 rpc,
820 }) => {
821 let address = stdin::unwrap_line(address)?;
822 let salt = salt.unwrap_or(B256::ZERO);
823 let computed = if let Some(init_code_hash) = init_code_hash {
824 address.create2(salt, init_code_hash)
825 } else if let Some(init_code) = init_code {
826 address.create2(salt, keccak256(hex::decode(init_code)?))
827 } else {
828 let nonce = match nonce {
830 Some(nonce) => nonce,
831 None => rpc_provider(&rpc)?.get_transaction_count(address).await?,
832 };
833 address.create(nonce)
834 };
835 print_scalar(computed.to_checksum(None))?;
836 }
837 CastSubcommand::Query(QuerySubcommand::Disassemble { bytecode }) => {
838 let bytecode = stdin::unwrap_line(bytecode)?;
839 print_scalar(crate::cmd::disassemble(&hex::decode(bytecode)?)?)?;
840 }
841 CastSubcommand::Query(QuerySubcommand::Selectors { bytecode, resolve }) => {
842 let bytecode = stdin::unwrap_line(bytecode)?;
843 let code = hex::decode(&bytecode)?;
844 let info = evmole::contract_info(
845 evmole::ContractInfoArgs::new(&code)
846 .with_selectors()
847 .with_arguments()
848 .with_state_mutability(),
849 );
850 let functions = info
851 .functions
852 .expect("functions extraction was requested")
853 .into_iter()
854 .filter(|f| f.dispatch == evmole::SelectorDispatch::Abi)
855 .map(|f| {
856 let arguments = f
857 .arguments
858 .expect("arguments extraction was requested")
859 .iter()
860 .map(|t| t.sol_type_name())
861 .collect::<Vec<_>>()
862 .join(",");
863 let mutability =
864 f.state_mutability.expect("state_mutability extraction was requested");
865 (
866 alloy_primitives::Selector::from(f.selector),
867 arguments,
868 mutability.as_json_str(),
869 )
870 })
871 .collect::<Vec<_>>();
872
873 let resolve_results: Vec<String> = if resolve {
874 let selectors = functions
875 .iter()
876 .map(|&(selector, ..)| SelectorKind::Function(selector))
877 .collect::<Vec<_>>();
878 let ds = decode_selectors(&selectors).await?;
879 ds.into_iter().map(|v| v.join("|")).collect()
880 } else {
881 vec![]
882 };
883
884 if shell::is_json() {
885 #[derive(serde::Serialize)]
886 struct SelectorInfo {
887 selector: String,
888 arguments: String,
889 state_mutability: String,
890 #[serde(skip_serializing_if = "Option::is_none")]
891 resolved: Option<String>,
892 }
893 let infos = functions
894 .into_iter()
895 .enumerate()
896 .map(|(pos, (selector, arguments, state_mutability))| SelectorInfo {
897 selector: selector.to_string(),
898 arguments,
899 state_mutability: state_mutability.to_string(),
900 resolved: resolve_results.get(pos).cloned(),
901 })
902 .collect::<Vec<_>>();
903 print_json_object(infos)?;
904 } else {
905 let max_args_len = functions.iter().map(|r| r.1.len()).max().unwrap_or(0);
906 let max_mutability_len = functions.iter().map(|r| r.2.len()).max().unwrap_or(0);
907 for (pos, (selector, arguments, state_mutability)) in
908 functions.into_iter().enumerate()
909 {
910 if resolve {
911 let resolved = &resolve_results[pos];
912 sh_println!(
913 "{selector}\t{arguments:max_args_len$}\t{state_mutability:max_mutability_len$}\t{resolved}"
914 )?
915 } else {
916 sh_println!("{selector}\t{arguments:max_args_len$}\t{state_mutability}")?
917 }
918 }
919 }
920 }
921 CastSubcommand::Query(QuerySubcommand::FindBlock(cmd)) => cmd.run().await?,
922 CastSubcommand::Query(QuerySubcommand::GasPrice { rpc }) => {
923 print_scalar(rpc_provider(&rpc)?.get_gas_price().await?.to_string())?;
924 }
925 CastSubcommand::Query(QuerySubcommand::Index { key_type, key, slot_number }) => {
926 let mut hasher = Keccak256::new();
927
928 let k_ty = DynSolType::parse(&key_type).wrap_err("Could not parse type")?;
929 let k = k_ty.coerce_str(&key).wrap_err("Could not parse value")?;
930 match k_ty {
931 DynSolType::Bool
934 | DynSolType::Int(_)
935 | DynSolType::Uint(_)
936 | DynSolType::FixedBytes(_)
937 | DynSolType::Address
938 | DynSolType::Function => hasher.update(k.as_word().unwrap()),
939
940 DynSolType::String | DynSolType::Bytes => hasher.update(k.as_packed_seq().unwrap()),
942
943 DynSolType::Array(..)
944 | DynSolType::FixedArray(..)
945 | DynSolType::Tuple(..)
946 | DynSolType::CustomStruct { .. } => {
947 eyre::bail!("Type `{k_ty}` is not supported as a mapping key");
948 }
949 }
950
951 let p = DynSolType::Uint(256)
952 .coerce_str(&slot_number)
953 .wrap_err("Could not parse slot number")?;
954 let p = p.as_word().unwrap();
955 hasher.update(p);
956
957 let location = hasher.finalize();
958 print_scalar(location.to_string())?;
959 }
960 CastSubcommand::Query(QuerySubcommand::IndexErc7201 { id, formula_id }) => {
961 eyre::ensure!(formula_id == "erc7201", "unsupported formula ID: {formula_id}");
962 let id = stdin::unwrap_line(id)?;
963 print_scalar(foundry_common::erc7201(&id).to_string())?;
964 }
965 CastSubcommand::Query(QuerySubcommand::Implementation { block, beacon, who, rpc }) => {
966 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
967 const BEACON_SLOT: B256 =
969 b256!("0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50");
970 const IMPLEMENTATION_SLOT: B256 =
972 b256!("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
973
974 let slot = if beacon { BEACON_SLOT } else { IMPLEMENTATION_SLOT };
975 print_scalar(address_at_slot(&provider, who, slot, block).await?)?;
976 }
977 CastSubcommand::Query(QuerySubcommand::Admin { block, who, rpc }) => {
978 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
979 const ADMIN_SLOT: B256 =
981 b256!("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103");
982 print_scalar(address_at_slot(&provider, who, ADMIN_SLOT, block).await?)?;
983 }
984 CastSubcommand::Query(QuerySubcommand::Nonce { block, who, rpc }) => {
985 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
986 print_scalar(
987 provider.get_transaction_count(who).block_id(block.unwrap_or_default()).await?,
988 )?;
989 }
990 CastSubcommand::Query(QuerySubcommand::Codehash { block, who, slots, rpc }) => {
991 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
992 print_scalar(
993 provider
994 .get_proof(who, slots)
995 .block_id(block.unwrap_or_default())
996 .await?
997 .code_hash
998 .to_string(),
999 )?;
1000 }
1001 CastSubcommand::Query(QuerySubcommand::StorageRoot { block, who, slots, rpc }) => {
1002 let (provider, who) = rpc_provider_and_address(&rpc, who).await?;
1003 print_scalar(
1004 provider
1005 .get_proof(who, slots)
1006 .block_id(block.unwrap_or_default())
1007 .await?
1008 .storage_hash
1009 .to_string(),
1010 )?;
1011 }
1012 CastSubcommand::Query(QuerySubcommand::ChannelId {
1013 payer,
1014 payee,
1015 token,
1016 salt,
1017 operator,
1018 authorized_signer,
1019 expiring_nonce_hash,
1020 reserve,
1021 block,
1022 rpc,
1023 }) => {
1024 let provider = rpc_provider(&rpc)?;
1025 let payer = payer.resolve(&provider).await?;
1026 let payee = payee.resolve(&provider).await?;
1027 let token = token.resolve(&provider).await?;
1028 let operator = resolve_or(operator, Address::ZERO, &provider).await?;
1029 let authorized_signer = resolve_or(authorized_signer, Address::ZERO, &provider).await?;
1030 let reserve = resolve_or(reserve, TIP20_CHANNEL_RESERVE_ADDRESS, &provider).await?;
1031
1032 let channel_id = ITIP20ChannelReserve::new(reserve, &provider)
1033 .computeChannelId(
1034 payer,
1035 payee,
1036 operator,
1037 token,
1038 salt,
1039 authorized_signer,
1040 expiring_nonce_hash,
1041 )
1042 .block(block.unwrap_or_default())
1043 .call()
1044 .await?;
1045 print_scalar(format!("{channel_id:#x}"))?;
1046 }
1047 CastSubcommand::Query(QuerySubcommand::Proof { address, slots, rpc, block }) => {
1048 let (provider, address) = rpc_provider_and_address(&rpc, address).await?;
1049 let value =
1050 provider.get_proof(address, slots).block_id(block.unwrap_or_default()).await?;
1051 print_json_object(value)?;
1052 }
1053 CastSubcommand::Query(QuerySubcommand::Rpc(cmd)) => cmd.run().await?,
1054 CastSubcommand::Query(QuerySubcommand::Storage(cmd)) => cmd.run().await?,
1055
1056 CastSubcommand::Transaction(TransactionSubcommand::Call(cmd)) => cmd.run().await?,
1058 CastSubcommand::Transaction(TransactionSubcommand::Estimate(cmd)) => cmd.run().await?,
1059 CastSubcommand::Transaction(TransactionSubcommand::MakeTx(cmd)) => cmd.run().await?,
1060 CastSubcommand::Transaction(TransactionSubcommand::PublishTx {
1061 raw_tx,
1062 cast_async,
1063 rpc,
1064 }) => {
1065 let provider = rpc_provider(&rpc)?;
1066 let raw_tx = hex::decode(strip_0x(&raw_tx))?;
1067 let pending_tx = provider.send_raw_transaction(&raw_tx).await?;
1068 if cast_async {
1069 print_scalar(format!("{:#x}", pending_tx.inner().tx_hash()))?;
1070 } else {
1071 print_json_object(pending_tx.get_receipt().await?)?;
1072 }
1073 }
1074 CastSubcommand::Transaction(TransactionSubcommand::Receipt {
1075 tx_hash,
1076 field,
1077 cast_async,
1078 confirmations,
1079 rpc,
1080 }) => {
1081 sh_println!(
1083 "{}",
1084 CastTxSender::new(rpc_provider(&rpc)?)
1085 .receipt(tx_hash, field, confirmations, None, cast_async)
1086 .await?
1087 )?
1088 }
1089 CastSubcommand::Transaction(TransactionSubcommand::Run(cmd)) => cmd.run().await?,
1090 CastSubcommand::Transaction(TransactionSubcommand::SendTx(cmd)) => cmd.run().await?,
1091 CastSubcommand::Transaction(TransactionSubcommand::BatchMakeTx(cmd)) => cmd.run().await?,
1092 CastSubcommand::Transaction(TransactionSubcommand::BatchSend(cmd)) => cmd.run().await?,
1093 CastSubcommand::Transaction(TransactionSubcommand::Classify { raw_tx }) => {
1094 let raw_tx = hex::decode(stdin::unwrap_line(raw_tx)?)?;
1095 let out = format_lane_classification(&raw_tx, "failed to decode raw transaction")?;
1096 print_json_value_or_scalar(out)?
1097 }
1098 CastSubcommand::Transaction(TransactionSubcommand::Tx {
1099 tx_hash,
1100 from,
1101 nonce,
1102 field,
1103 raw,
1104 lane,
1105 rpc,
1106 to_request,
1107 network,
1108 }) => {
1109 let config = rpc.load_config()?;
1110 #[cfg(feature = "base")]
1111 let network = match network {
1112 Some(network) => Some(network),
1113 None => crate::cmd::resolve_transaction_network(&config, false)
1114 .await?
1115 .is_base()
1116 .then_some(NetworkVariant::Base),
1117 };
1118 let is_raw = raw || field.as_deref() == Some("raw");
1120 let output = if is_raw || lane {
1121 let encoded: Bytes = with_network_provider!(
1122 network,
1123 &config,
1124 |provider| {
1125 let tx =
1126 transaction_response(&provider, tx_hash, from, nonce).await?;
1127 tx.as_ref().encoded_2718().into()
1128 },
1129 _ => {
1130 let provider = utils::get_provider(&config)?;
1131 let tx =
1132 transaction_response(&provider, tx_hash, from, nonce).await?;
1133 FoundryTxEnvelope::encode_rpc_2718(&tx).wrap_err_with(|| {
1134 format!("Cannot EIP-2718 encode transaction type 0x{:x}", tx.ty())
1135 })?
1136 }
1137 );
1138 if lane {
1139 format_lane_classification(
1140 &encoded,
1141 "failed to decode transaction for lane classification",
1142 )?
1143 } else {
1144 hex::encode_prefixed(encoded)
1145 }
1146 } else {
1147 with_network_provider!(
1148 network,
1149 &config,
1150 utils::get_provider(&config)?,
1151 |provider| {
1152 let tx = transaction_response(&provider, tx_hash, from, nonce).await?;
1153 format_transaction(&provider, tx, field, to_request)?
1154 }
1155 )
1156 };
1157 print_json_value_or_scalar(output)?;
1158 }
1159
1160 CastSubcommand::Abi(AbiSubcommand::FourByte { selector }) => {
1162 let selector = stdin::unwrap_line(selector)?;
1163 let sigs = decode_function_selector(selector).await?;
1164 if sigs.is_empty() {
1165 eyre::bail!("No matching function signatures found for selector `{selector}`");
1166 }
1167 print_list(&sigs)?;
1168 }
1169
1170 CastSubcommand::Abi(AbiSubcommand::FourByteCalldata { calldata }) => {
1173 let calldata = stdin::unwrap_line(calldata)?;
1174
1175 if calldata.len() == 10 {
1176 let sigs = decode_function_selector(calldata.parse()?).await?;
1177 if sigs.is_empty() {
1178 eyre::bail!("No matching function signatures found for calldata `{calldata}`");
1179 }
1180 for sig in sigs {
1181 sh_println!("{sig}")?
1182 }
1183 return Ok(());
1184 }
1185
1186 let sigs = decode_calldata(&calldata).await?;
1187 for (i, sig) in sigs.iter().enumerate() {
1188 let _ = sh_println!("{}) \"{sig}\"", i + 1);
1189 }
1190
1191 let sig = match sigs.len() {
1192 0 => eyre::bail!("No signatures found"),
1193 1 => &sigs[0],
1194 _ => {
1195 let i: usize = prompt!("Select a function signature by number: ")?;
1196 sigs.get(i - 1).ok_or_else(|| eyre::eyre!("Invalid signature index"))?
1197 }
1198 };
1199
1200 print_tokens(&abi_decode_calldata(sig, &calldata, true, true)?)?;
1201 }
1202
1203 CastSubcommand::Abi(AbiSubcommand::FourByteEvent { topic }) => {
1204 let topic = stdin::unwrap_line(topic)?;
1205 let sigs = decode_event_topic(topic).await?;
1206 if sigs.is_empty() {
1207 eyre::bail!("No matching event signatures found for topic `{topic}`");
1208 }
1209 print_list(&sigs)?;
1210 }
1211 CastSubcommand::Abi(AbiSubcommand::UploadSignature { signatures }) => {
1214 let signatures = stdin::unwrap_vec(signatures)?;
1215 let ParsedSignatures { signatures, abis } = parse_signatures(signatures);
1216 if !abis.is_empty() {
1217 import_selectors(SelectorImportData::Abi(abis)).await?.describe();
1218 }
1219 if !signatures.is_empty() {
1220 import_selectors(SelectorImportData::Raw(signatures)).await?.describe();
1221 }
1222 }
1223
1224 CastSubcommand::Query(QuerySubcommand::Namehash { name }) => {
1226 print_scalar(namehash(&stdin::unwrap_line(name)?).to_string())?;
1227 }
1228 CastSubcommand::Query(QuerySubcommand::LookupAddress { who, rpc, verify }) => {
1229 let provider = rpc_provider(&rpc)?;
1230 let who = stdin::unwrap_line(who)?;
1231 let name = provider.lookup_address(&who).await?;
1232 if verify {
1233 let address = provider.resolve_name(&name).await?;
1234 eyre::ensure!(
1235 address == who,
1236 "Reverse lookup verification failed: got `{address}`, expected `{who}`"
1237 );
1238 }
1239 print_scalar(name)?;
1240 }
1241 CastSubcommand::Query(QuerySubcommand::ResolveName { who, rpc, verify }) => {
1242 let provider = rpc_provider(&rpc)?;
1243 let who = stdin::unwrap_line(who)?;
1244 let address = provider
1245 .resolve_name(&who)
1246 .await
1247 .wrap_err(format!("Failed to resolve ENS name: {who}"))?;
1248 if verify {
1249 let name = provider.lookup_address(&address).await?;
1250 eyre::ensure!(
1251 name == who,
1252 "Forward lookup verification failed: got `{name}`, expected `{who}`"
1253 );
1254 }
1255 print_scalar(address.to_string())?;
1256 }
1257
1258 CastSubcommand::Misc(MiscSubcommand::Keccak { data }) => {
1260 let bytes = match data {
1261 Some(data) => data.into_bytes(),
1262 None => stdin::read_bytes(false)?,
1263 };
1264 let out = match String::from_utf8(bytes) {
1265 Ok(s) => {
1266 if s.starts_with("0x") {
1268 keccak256(hex::decode(s.trim_end())?)
1269 } else {
1270 keccak256(s)
1271 }
1272 .to_string()
1273 }
1274 Err(e) => hex::encode_prefixed(keccak256(e.as_bytes())),
1275 };
1276 print_scalar(out)?;
1277 }
1278 CastSubcommand::Misc(MiscSubcommand::HashMessage { message }) => {
1279 print_scalar(eip191_hash_message(stdin::unwrap(message, false)?).to_string())?;
1280 }
1281 CastSubcommand::Misc(MiscSubcommand::SigEvent { event_string }) => {
1282 let event = get_event(&stdin::unwrap_line(event_string)?)?;
1283 print_scalar(format!("{:?}", event.selector()))?;
1284 }
1285 CastSubcommand::Misc(MiscSubcommand::LeftShift { value, bits, base_in, base_out }) => {
1286 print_scalar(shift(&value, &bits, base_in.as_deref(), &base_out, |value, bits| {
1287 value << bits
1288 })?)?;
1289 }
1290 CastSubcommand::Misc(MiscSubcommand::RightShift { value, bits, base_in, base_out }) => {
1291 print_scalar(shift(&value, &bits, base_in.as_deref(), &base_out, |value, bits| {
1292 value.wrapping_shr(bits.saturating_to())
1293 })?)?;
1294 }
1295 CastSubcommand::Misc(MiscSubcommand::Source {
1296 address,
1297 directory,
1298 explorer_api_url,
1299 explorer_url,
1300 etherscan,
1301 flatten,
1302 }) => {
1303 let config = etherscan.load_config()?;
1304 let chain = config.chain.unwrap_or_default();
1305 let api_key = config.get_etherscan_api_key(Some(chain));
1306 let client = explorer_client(chain, api_key, explorer_api_url, explorer_url)?;
1307 let metadata = client.contract_source_code(address.parse()?).await?;
1308 match (directory, flatten) {
1309 (Some(dir), false) => {
1310 metadata.source_tree().write_to(&dir)?;
1311 }
1312 (None, false) => sh_println!("{}", metadata.source_code())?,
1313 (dir, true) => {
1314 let Some(metadata) = metadata.items.first() else {
1315 eyre::bail!("Empty contract source code");
1316 };
1317
1318 let tmp = tempfile::tempdir()?;
1319 let project = foundry_common::compile::etherscan_project(metadata, tmp.path())?;
1320 let target_path = project.find_contract_path(&metadata.contract_name)?;
1321
1322 let flattened = foundry_common::flatten(project, &target_path)?;
1323
1324 if let Some(path) = dir {
1325 fs::create_dir_all(path.parent().unwrap())?;
1326 fs::write(&path, flattened)?;
1327 sh_status!("Flattened file written at {}", path.display())?
1328 } else {
1329 sh_println!("{flattened}")?
1330 }
1331 }
1332 }
1333 }
1334 CastSubcommand::Misc(MiscSubcommand::Create2(cmd)) => cmd.execute()?,
1335 CastSubcommand::Misc(MiscSubcommand::Wallet { command }) => command.run().await?,
1336 CastSubcommand::Misc(MiscSubcommand::Safe { command }) => command.run().await?,
1337 CastSubcommand::Misc(MiscSubcommand::Completions { shell }) => {
1338 generate(shell, &mut CastArgs::command(), "cast", &mut std::io::stdout())
1339 }
1340 CastSubcommand::Misc(MiscSubcommand::Logs(cmd)) => cmd.run().await?,
1341 CastSubcommand::Misc(MiscSubcommand::Events(cmd)) => cmd.run().await?,
1342 CastSubcommand::Misc(MiscSubcommand::DecodeTransaction { tx, network }) => {
1343 let tx = stdin::unwrap_line(tx)?;
1344 let decoded_tx = match network {
1345 #[cfg(feature = "optimism")]
1346 Some(NetworkVariant::Optimism) => decode_raw_transaction::<Optimism>(&tx)?,
1347 Some(NetworkVariant::Tempo) => decode_raw_transaction::<TempoNetwork>(&tx)?,
1348 #[cfg(feature = "base")]
1349 Some(NetworkVariant::Base) => decode_raw_transaction::<BaseNetwork>(&tx)?,
1350 Some(NetworkVariant::Ethereum) => decode_raw_transaction::<Ethereum>(&tx)?,
1351 #[cfg(feature = "monad")]
1352 Some(NetworkVariant::Monad) => decode_raw_transaction::<Ethereum>(&tx)?,
1353 None => decode_raw_transaction::<FoundryNetwork>(&tx)?,
1357 };
1358 print_json_object(decoded_tx)?;
1359 }
1360 CastSubcommand::Misc(MiscSubcommand::RecoverAuthority { auth }) => {
1361 let auth: SignedAuthorization = serde_json::from_str(&auth)?;
1362 print_scalar(auth.recover_authority()?.to_string())?;
1363 }
1364 CastSubcommand::Misc(MiscSubcommand::TxPool { command }) => command.run().await?,
1365 CastSubcommand::Misc(MiscSubcommand::Erc20Token { command }) => command.run().await?,
1366 CastSubcommand::Misc(MiscSubcommand::Erc4626 { command }) => command.run().await?,
1367 CastSubcommand::Misc(MiscSubcommand::Tip20Token { command }) => command.run().await?,
1368 CastSubcommand::Misc(MiscSubcommand::ReceivePolicy { command }) => command.run().await?,
1369 CastSubcommand::Misc(MiscSubcommand::Tip403 { command }) => command.run().await?,
1370 CastSubcommand::Misc(MiscSubcommand::StorageCredits { command }) => command.run().await?,
1371 CastSubcommand::Misc(MiscSubcommand::Keychain { command }) => command.run().await?,
1372 CastSubcommand::Misc(MiscSubcommand::KeyAuthorization { command }) => command.run().await?,
1373 CastSubcommand::Misc(MiscSubcommand::Tempo(args)) => args.run().await?,
1374 CastSubcommand::Misc(MiscSubcommand::VirtualAddress { command }) => command.run().await?,
1375 #[cfg(any(feature = "base", feature = "optimism"))]
1376 CastSubcommand::Misc(MiscSubcommand::DAEstimate(cmd)) => cmd.run().await?,
1377 CastSubcommand::Misc(MiscSubcommand::Trace(cmd)) => cmd.run().await?,
1378 };
1379
1380 Ok(())
1381}
1382
1383async fn rpc_provider_and_address(
1385 rpc: &RpcOpts,
1386 who: NameOrAddress,
1387) -> Result<(RetryProvider, Address)> {
1388 let provider = rpc_provider(rpc)?;
1389 let who = who.resolve(&provider).await?;
1390 Ok((provider, who))
1391}
1392
1393async fn resolve_or(
1395 who: Option<NameOrAddress>,
1396 default: Address,
1397 provider: &RetryProvider,
1398) -> Result<Address> {
1399 Ok(match who {
1400 Some(who) => who.resolve(provider).await?,
1401 None => default,
1402 })
1403}
1404
1405fn format_lane_classification(encoded: &[u8], decode_context: &'static str) -> Result<String> {
1408 FoundryTxEnvelope::decode_2718(&mut &encoded[..]).wrap_err(decode_context)?;
1409 let classification = classify_payment_lane(encoded);
1410 if shell::is_json() {
1411 Ok(serde_json::to_string_pretty(&classification)?)
1412 } else {
1413 Ok(serde_json::to_string(&classification)?)
1414 }
1415}
1416
1417async fn address_at_slot<N: alloy_network::Network>(
1418 provider: &impl Provider<N>,
1419 who: Address,
1420 slot: B256,
1421 block: Option<BlockId>,
1422) -> Result<String> {
1423 let value =
1424 provider.get_storage_at(who, slot.into()).block_id(block.unwrap_or_default()).await?;
1425 Ok(format!("{:?}", Address::from_word(value.into())))
1426}
1427
1428async fn transaction_response<N: Network>(
1429 provider: &impl Provider<N>,
1430 tx_hash: Option<String>,
1431 from: Option<NameOrAddress>,
1432 nonce: Option<u64>,
1433) -> Result<N::TransactionResponse> {
1434 if let Some(tx_hash) = tx_hash {
1435 let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?;
1436 provider
1437 .get_transaction_by_hash(tx_hash)
1438 .await?
1439 .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))
1440 } else if let Some(from) = from {
1441 let nonce = U64::from(nonce.unwrap_or_default());
1442 let from = from.resolve(provider.root()).await?;
1443 provider
1444 .raw_request::<_, Option<N::TransactionResponse>>(
1445 "eth_getTransactionBySenderAndNonce".into(),
1446 (from, nonce),
1447 )
1448 .await?
1449 .ok_or_else(|| {
1450 eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::<u64>())
1451 })
1452 } else {
1453 eyre::bail!("tx hash or from address is required")
1454 }
1455}
1456
1457fn format_transaction<N: Network>(
1458 _provider: &impl Provider<N>,
1459 tx: N::TransactionResponse,
1460 field: Option<String>,
1461 to_request: bool,
1462) -> Result<String>
1463where
1464 N::TransactionResponse: UIfmt,
1465 N::TxEnvelope: UIfmtSignatureExt,
1466{
1467 Ok(if let Some(field) = field {
1468 if let Some(value) = get_pretty_tx_attr::<N>(&tx, &field) {
1469 value
1470 } else {
1471 let tx_json = serde_json::to_value(&tx)?;
1472 let value =
1473 tx_json.get(&field).ok_or_else(|| eyre::eyre!("invalid tx field: {field}"))?;
1474 match value {
1475 serde_json::Value::String(value) => value.clone(),
1476 value => value.to_string(),
1477 }
1478 }
1479 } else if shell::is_json() {
1480 serde_json::to_value(&tx)?.to_string()
1482 } else if to_request {
1483 serde_json::to_string_pretty(&Into::<N::TransactionRequest>::into(tx))?
1484 } else {
1485 tx.pretty()
1486 })
1487}
1488
1489fn int_bound(s: &str, max: bool) -> Result<String> {
1490 let ty = DynSolType::parse(s).wrap_err("Invalid type, expected `(u)int<bit size>`")?;
1491 match ty {
1492 DynSolType::Int(n) => {
1493 let max_value = (U256::MAX & U256::ONE.wrapping_shl(n - 1)) - U256::ONE;
1494 if max {
1495 Ok(max_value.to_string())
1496 } else {
1497 Ok((I256::from_raw(max_value).wrapping_neg() + I256::MINUS_ONE).to_string())
1498 }
1499 }
1500 DynSolType::Uint(n) if max => {
1501 let mut max_value = U256::MAX;
1502 if n < 256 {
1503 max_value &= U256::ONE.wrapping_shl(n).wrapping_sub(U256::ONE);
1504 }
1505 Ok(max_value.to_string())
1506 }
1507 DynSolType::Uint(_) => Ok("0".to_string()),
1508 _ => Err(eyre::eyre!("Type is not int/uint: {s}")),
1509 }
1510}
1511
1512fn signed_parse_units(value: &NumberWithBase) -> Result<ParseUnits> {
1520 if value.is_nonnegative() {
1521 return Ok(ParseUnits::U256(value.number()));
1522 }
1523 let signed = I256::from_raw(value.number());
1524 if !signed.is_negative() {
1525 eyre::bail!("value out of range for a signed 256-bit integer");
1526 }
1527 Ok(ParseUnits::I256(signed))
1528}
1529
1530fn format_unit_as_string(value: ParseUnits, unit: Unit) -> String {
1531 let mut formatted = value.format_units(unit);
1532 if let Some(dot) = formatted.find('.') {
1534 let fractional = &formatted[dot + 1..];
1535 if fractional.chars().all(|c: char| c == '0') {
1536 formatted = formatted[..dot].to_string();
1537 }
1538 }
1539 formatted
1540}
1541
1542pub(super) fn format_units(value: &str, unit: u8) -> Result<String> {
1543 let value = NumberWithBase::parse_int(value, None)?;
1544 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
1545 let parsed = signed_parse_units(&value)?;
1546 Ok(format_unit_as_string(parsed, unit))
1547}
1548
1549fn to_base(value: &str, base_in: Option<&str>, base_out: &str) -> Result<String> {
1550 let base_in = Base::unwrap_or_detect(base_in, value)?;
1551 let base_out = base_out.parse()?;
1552 let n = NumberWithBase::parse_int_in(value, base_in)?.with_base(base_out);
1553 if base_in == base_out {
1554 return Ok(value.to_string());
1555 }
1556 Ok(format!("{n:#?}"))
1557}
1558
1559fn shift(
1562 value: &str,
1563 bits: &str,
1564 base_in: Option<&str>,
1565 base_out: &str,
1566 shift: impl FnOnce(U256, U256) -> U256,
1567) -> Result<String> {
1568 let base_out = base_out.parse()?;
1569 let value = NumberWithBase::parse_uint(value, base_in)?.number();
1570 let bits = NumberWithBase::parse_uint(bits, None)?.number();
1571 Ok(format!("{:#?}", NumberWithBase::from(shift(value, bits)).with_base(base_out)))
1572}
1573
1574fn explorer_client(
1575 chain: Chain,
1576 api_key: Option<String>,
1577 api_url: Option<String>,
1578 explorer_url: Option<String>,
1579) -> Result<Client> {
1580 let mut builder = Client::builder();
1581
1582 let deduced = chain.etherscan_urls();
1583
1584 let explorer_url = explorer_url
1585 .or(deduced.map(|d| d.1.to_string()))
1586 .ok_or_eyre("Please provide the explorer browser URL using `--explorer-url`")?;
1587 builder = builder.with_url(explorer_url)?;
1588
1589 let api_url = api_url
1590 .or(deduced.map(|d| d.0.to_string()))
1591 .ok_or_eyre("Please provide the explorer API URL using `--explorer-api-url`")?;
1592 builder = builder.with_api_url(api_url)?;
1593
1594 if let Some(api_key) = api_key {
1595 builder = builder.with_api_key(api_key);
1596 }
1597
1598 builder.build().map_err(Into::into)
1599}
1600
1601fn decode_raw_transaction<N: Network<TxEnvelope: SignerRecoverable + Serialize>>(
1602 tx: &str,
1603) -> Result<serde_json::Value> {
1604 let tx_hex = hex::decode(tx)?;
1605 let tx: N::TxEnvelope = Decodable2718::decode_2718(&mut tx_hex.as_slice())?;
1606 if let Ok(signer) = tx.recover_signer() {
1607 Ok(serde_json::to_value(Recovered::new_unchecked(tx, signer))?)
1608 } else {
1609 Ok(serde_json::to_value(tx)?)
1610 }
1611}
1612
1613fn get_selector(signature: &str, optimize: usize) -> Result<(String, String)> {
1614 if optimize > 4 {
1615 eyre::bail!("number of leading zeroes must not be greater than 4");
1616 }
1617 if optimize == 0 {
1618 let selector = get_func(signature)?.selector();
1619 return Ok((selector.to_string(), String::from(signature)));
1620 }
1621 let Some((name, params)) = signature.split_once('(') else {
1622 eyre::bail!("invalid function signature");
1623 };
1624
1625 let num_threads = rayon::current_num_threads();
1626 let found = AtomicBool::new(false);
1627
1628 (0..num_threads as u32)
1630 .into_par_iter()
1631 .find_map_any(|mut nonce| {
1632 while nonce < u32::MAX && !found.load(Ordering::Relaxed) {
1633 let input = format!("{name}{nonce}({params}");
1634 let selector = &keccak256(input.as_bytes())[..4];
1635 if selector.iter().take_while(|&&byte| byte == 0).count() == optimize {
1636 found.store(true, Ordering::Relaxed);
1637 return Some((hex::encode_prefixed(selector), input));
1638 }
1639 nonce += num_threads as u32;
1640 }
1641 None
1642 })
1643 .ok_or_eyre("No selector found")
1644}
1645
1646fn strip_0x(s: &str) -> &str {
1647 s.strip_prefix("0x").unwrap_or(s)
1648}
1649
1650pub(super) fn encode_event_topic(value: &DynSolValue) -> B256 {
1659 if let Some(word) = value.as_word() {
1660 return word;
1661 }
1662 if let Some(bytes) = value.as_packed_seq() {
1664 return keccak256(bytes);
1665 }
1666 let mut preimage = Vec::new();
1667 encode_event_topic_preimage(value, &mut preimage);
1668 keccak256(preimage)
1669}
1670
1671fn encode_event_topic_preimage(value: &DynSolValue, out: &mut Vec<u8>) {
1675 if let Some(word) = value.as_word() {
1676 out.extend_from_slice(word.as_slice());
1677 } else if let Some(bytes) = value.as_packed_seq() {
1678 let pad = bytes.len().next_multiple_of(32) - bytes.len();
1679 out.extend_from_slice(bytes);
1680 out.resize(out.len() + pad, 0);
1681 } else if let Some(values) = value.as_fixed_seq().or_else(|| value.as_array()) {
1682 for value in values {
1683 encode_event_topic_preimage(value, out);
1684 }
1685 }
1686}
1687
1688#[cfg(test)]
1689mod tests {
1690 use super::*;
1691 use alloy_sol_types::{EventTopic, sol_data};
1692
1693 #[test]
1696 fn encode_event_topic_matches_static_encoding() {
1697 let uint = |n: u64| DynSolValue::Uint(U256::from(n), 256);
1698 let string = |s: &str| DynSolValue::String(s.into());
1699 let topic = |v: &DynSolValue| super::encode_event_topic(v);
1700
1701 let long = "abcdefghijklmnopqrstuvwxyz0123456789abcd";
1702 for s in ["", "hello", long] {
1703 assert_eq!(
1704 topic(&string(s)),
1705 <sol_data::String as EventTopic>::encode_topic(&s.to_string()).0,
1706 "string {s:?}"
1707 );
1708 }
1709
1710 let bytes = hex::decode("deadbeef").unwrap();
1711 assert_eq!(
1712 topic(&DynSolValue::Bytes(bytes.clone())),
1713 <sol_data::Bytes as EventTopic>::encode_topic(&Bytes::from(bytes)).0,
1714 );
1715
1716 let addr = Address::repeat_byte(0x42);
1717 assert_eq!(
1718 topic(&DynSolValue::Address(addr)),
1719 <sol_data::Address as EventTopic>::encode_topic(&addr).0,
1720 );
1721
1722 assert_eq!(
1723 topic(&DynSolValue::Array(vec![uint(1), uint(2)])),
1724 <sol_data::Array<sol_data::Uint<256>> as EventTopic>::encode_topic(&vec![
1725 U256::ONE,
1726 U256::from(2)
1727 ])
1728 .0,
1729 );
1730
1731 assert_eq!(
1732 topic(&DynSolValue::FixedArray(vec![uint(7), uint(9)])),
1733 <sol_data::FixedArray<sol_data::Uint<256>, 2> as EventTopic>::encode_topic(&[
1734 U256::from(7),
1735 U256::from(9)
1736 ])
1737 .0,
1738 );
1739
1740 assert_eq!(
1741 topic(&DynSolValue::Array(vec![string("alpha"), string(long)])),
1742 <sol_data::Array<sol_data::String> as EventTopic>::encode_topic(&vec![
1743 "alpha".to_string(),
1744 long.to_string()
1745 ])
1746 .0,
1747 );
1748
1749 assert_eq!(
1750 topic(&DynSolValue::Tuple(vec![uint(7), string("hello")])),
1751 <(sol_data::Uint<256>, sol_data::String) as EventTopic>::encode_topic(&(
1752 U256::from(7),
1753 "hello".to_string()
1754 ))
1755 .0,
1756 );
1757
1758 assert_eq!(
1759 topic(&DynSolValue::Array(vec![
1760 DynSolValue::Array(vec![uint(1)]),
1761 DynSolValue::Array(vec![uint(2), uint(3)]),
1762 ])),
1763 <sol_data::Array<sol_data::Array<sol_data::Uint<256>>> as EventTopic>::encode_topic(
1764 &vec![vec![U256::ONE], vec![U256::from(2), U256::from(3)]]
1765 )
1766 .0,
1767 );
1768 }
1769}