cast/lib.rs
1//! Cast is a Swiss Army knife for interacting with Ethereum applications from the command line.
2
3#![cfg_attr(not(test), warn(unused_crate_dependencies))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6#[macro_use]
7extern crate foundry_common;
8#[macro_use]
9extern crate tracing;
10use alloy_consensus::Header;
11use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt};
12use alloy_ens::NameOrAddress;
13use alloy_json_abi::Function;
14use alloy_network::{AnyNetwork, AnyRpcTransaction};
15use alloy_primitives::{
16 Address, B256, I256, Keccak256, LogData, Selector, TxHash, TxKind, U64, U256, hex,
17 utils::{ParseUnits, Unit, keccak256},
18};
19use alloy_provider::{
20 PendingTransactionBuilder, Provider,
21 network::eip2718::{Decodable2718, Encodable2718},
22};
23use alloy_rlp::Decodable;
24use alloy_rpc_types::{
25 BlockId, BlockNumberOrTag, BlockOverrides, Filter, FilterBlockOption, Log, TransactionRequest,
26 state::StateOverride,
27};
28use alloy_serde::WithOtherFields;
29use base::{Base, NumberWithBase, ToBase};
30use chrono::DateTime;
31use eyre::{Context, ContextCompat, OptionExt, Result};
32use foundry_block_explorers::Client;
33use foundry_common::{
34 abi::{coerce_value, encode_function_args, encode_function_args_packed, get_event, get_func},
35 compile::etherscan_project,
36 flatten,
37 fmt::*,
38 fs, shell,
39};
40use foundry_config::Chain;
41use foundry_evm::core::bytecode::InstIter;
42use foundry_primitives::FoundryTxEnvelope;
43use futures::{FutureExt, StreamExt, future::Either};
44
45use rayon::prelude::*;
46use std::{
47 borrow::Cow,
48 fmt::Write,
49 io,
50 path::PathBuf,
51 str::FromStr,
52 sync::atomic::{AtomicBool, Ordering},
53};
54use tokio::signal::ctrl_c;
55
56pub use foundry_evm::*;
57
58pub mod args;
59pub mod cmd;
60pub mod opts;
61
62pub mod base;
63pub(crate) mod debug;
64pub mod errors;
65mod rlp_converter;
66pub mod tx;
67
68use rlp_converter::Item;
69
70// TODO: CastContract with common contract initializers? Same for CastProviders?
71
72pub struct Cast<P> {
73 provider: P,
74}
75
76impl<P: Provider<AnyNetwork> + Clone + Unpin> Cast<P> {
77 /// Creates a new Cast instance from the provided client
78 ///
79 /// # Example
80 ///
81 /// ```
82 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
83 /// use cast::Cast;
84 ///
85 /// # async fn foo() -> eyre::Result<()> {
86 /// let provider =
87 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
88 /// let cast = Cast::new(provider);
89 /// # Ok(())
90 /// # }
91 /// ```
92 pub fn new(provider: P) -> Self {
93 Self { provider }
94 }
95
96 /// Makes a read-only call to the specified address
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// use alloy_primitives::{Address, U256, Bytes};
102 /// use alloy_rpc_types::{TransactionRequest, BlockOverrides, state::{StateOverride, AccountOverride}};
103 /// use alloy_serde::WithOtherFields;
104 /// use cast::Cast;
105 /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork};
106 /// use std::{str::FromStr, collections::HashMap};
107 /// use alloy_rpc_types::state::StateOverridesBuilder;
108 /// use alloy_sol_types::{sol, SolCall};
109 ///
110 /// sol!(
111 /// function greeting(uint256 i) public returns (string);
112 /// );
113 ///
114 /// # async fn foo() -> eyre::Result<()> {
115 /// let alloy_provider = ProviderBuilder::<_,_, AnyNetwork>::default().connect("http://localhost:8545").await?;;
116 /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?;
117 /// let greeting = greetingCall { i: U256::from(5) }.abi_encode();
118 /// let bytes = Bytes::from_iter(greeting.iter());
119 /// let tx = TransactionRequest::default().to(to).input(bytes.into());
120 /// let tx = WithOtherFields::new(tx);
121 ///
122 /// // Create state overrides
123 /// let mut state_override = StateOverride::default();
124 /// let mut account_override = AccountOverride::default();
125 /// account_override.balance = Some(U256::from(1000));
126 /// state_override.insert(to, account_override);
127 /// let state_override_object = StateOverridesBuilder::default().build();
128 /// let block_override_object = BlockOverrides::default();
129 ///
130 /// let cast = Cast::new(alloy_provider);
131 /// let data = cast.call(&tx, None, None, Some(state_override_object), Some(block_override_object)).await?;
132 /// println!("{}", data);
133 /// # Ok(())
134 /// # }
135 /// ```
136 pub async fn call(
137 &self,
138 req: &WithOtherFields<TransactionRequest>,
139 func: Option<&Function>,
140 block: Option<BlockId>,
141 state_override: Option<StateOverride>,
142 block_override: Option<BlockOverrides>,
143 ) -> Result<String> {
144 let mut call = self
145 .provider
146 .call(req.clone())
147 .block(block.unwrap_or_default())
148 .with_block_overrides_opt(block_override);
149 if let Some(state_override) = state_override {
150 call = call.overrides(state_override)
151 }
152
153 let res = call.await?;
154 let mut decoded = vec![];
155
156 if let Some(func) = func {
157 // decode args into tokens
158 decoded = match func.abi_decode_output(res.as_ref()) {
159 Ok(decoded) => decoded,
160 Err(err) => {
161 // ensure the address is a contract
162 if res.is_empty() {
163 // check that the recipient is a contract that can be called
164 if let Some(TxKind::Call(addr)) = req.to {
165 if let Ok(code) = self
166 .provider
167 .get_code_at(addr)
168 .block_id(block.unwrap_or_default())
169 .await
170 && code.is_empty()
171 {
172 eyre::bail!("contract {addr:?} does not have any code")
173 }
174 } else if Some(TxKind::Create) == req.to {
175 eyre::bail!("tx req is a contract deployment");
176 } else {
177 eyre::bail!("recipient is None");
178 }
179 }
180 return Err(err).wrap_err(
181 "could not decode output; did you specify the wrong function return data type?"
182 );
183 }
184 };
185 }
186
187 // handle case when return type is not specified
188 Ok(if decoded.is_empty() {
189 res.to_string()
190 } else if shell::is_json() {
191 let tokens = decoded
192 .into_iter()
193 .map(|value| serialize_value_as_json(value, None))
194 .collect::<eyre::Result<Vec<_>>>()?;
195 serde_json::to_string_pretty(&tokens).unwrap()
196 } else {
197 // seth compatible user-friendly return type conversions
198 decoded.iter().map(format_token).collect::<Vec<_>>().join("\n")
199 })
200 }
201
202 /// Generates an access list for the specified transaction
203 ///
204 /// # Example
205 ///
206 /// ```
207 /// use cast::{Cast};
208 /// use alloy_primitives::{Address, U256, Bytes};
209 /// use alloy_rpc_types::{TransactionRequest};
210 /// use alloy_serde::WithOtherFields;
211 /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork};
212 /// use std::str::FromStr;
213 /// use alloy_sol_types::{sol, SolCall};
214 ///
215 /// sol!(
216 /// function greeting(uint256 i) public returns (string);
217 /// );
218 ///
219 /// # async fn foo() -> eyre::Result<()> {
220 /// let provider = ProviderBuilder::<_,_, AnyNetwork>::default().connect("http://localhost:8545").await?;;
221 /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?;
222 /// let greeting = greetingCall { i: U256::from(5) }.abi_encode();
223 /// let bytes = Bytes::from_iter(greeting.iter());
224 /// let tx = TransactionRequest::default().to(to).input(bytes.into());
225 /// let tx = WithOtherFields::new(tx);
226 /// let cast = Cast::new(&provider);
227 /// let access_list = cast.access_list(&tx, None).await?;
228 /// println!("{}", access_list);
229 /// # Ok(())
230 /// # }
231 /// ```
232 pub async fn access_list(
233 &self,
234 req: &WithOtherFields<TransactionRequest>,
235 block: Option<BlockId>,
236 ) -> Result<String> {
237 let access_list =
238 self.provider.create_access_list(req).block_id(block.unwrap_or_default()).await?;
239 let res = if shell::is_json() {
240 serde_json::to_string(&access_list)?
241 } else {
242 let mut s =
243 vec![format!("gas used: {}", access_list.gas_used), "access list:".to_string()];
244 for al in access_list.access_list.0 {
245 s.push(format!("- address: {}", &al.address.to_checksum(None)));
246 if !al.storage_keys.is_empty() {
247 s.push(" keys:".to_string());
248 for key in al.storage_keys {
249 s.push(format!(" {key:?}"));
250 }
251 }
252 }
253 s.join("\n")
254 };
255
256 Ok(res)
257 }
258
259 pub async fn balance(&self, who: Address, block: Option<BlockId>) -> Result<U256> {
260 Ok(self.provider.get_balance(who).block_id(block.unwrap_or_default()).await?)
261 }
262
263 /// Publishes a raw transaction to the network
264 ///
265 /// # Example
266 ///
267 /// ```
268 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
269 /// use cast::Cast;
270 ///
271 /// # async fn foo() -> eyre::Result<()> {
272 /// let provider =
273 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
274 /// let cast = Cast::new(provider);
275 /// let res = cast.publish("0x1234".to_string()).await?;
276 /// println!("{:?}", res);
277 /// # Ok(())
278 /// # }
279 /// ```
280 pub async fn publish(&self, raw_tx: String) -> Result<PendingTransactionBuilder<AnyNetwork>> {
281 let tx = hex::decode(strip_0x(&raw_tx))?;
282 let res = self.provider.send_raw_transaction(&tx).await?;
283
284 Ok(res)
285 }
286
287 /// # Example
288 ///
289 /// ```
290 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
291 /// use cast::Cast;
292 ///
293 /// # async fn foo() -> eyre::Result<()> {
294 /// let provider =
295 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
296 /// let cast = Cast::new(provider);
297 /// let block = cast.block(5, true, vec![], false).await?;
298 /// println!("{}", block);
299 /// # Ok(())
300 /// # }
301 /// ```
302 pub async fn block<B: Into<BlockId>>(
303 &self,
304 block: B,
305 full: bool,
306 fields: Vec<String>,
307 raw: bool,
308 ) -> Result<String> {
309 let block = block.into();
310 if fields.contains(&"transactions".into()) && !full {
311 eyre::bail!("use --full to view transactions")
312 }
313
314 let block = self
315 .provider
316 .get_block(block)
317 .kind(full.into())
318 .await?
319 .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?;
320
321 Ok(if raw {
322 let header: Header = block.into_inner().header.inner.try_into_header()?;
323 format!("0x{}", hex::encode(alloy_rlp::encode(&header)))
324 } else if !fields.is_empty() {
325 let mut result = String::new();
326 for field in fields {
327 result.push_str(
328 &get_pretty_block_attr(&block, &field)
329 .unwrap_or_else(|| format!("{field} is not a valid block field")),
330 );
331
332 result.push('\n');
333 }
334 result.trim_end().to_string()
335 } else if shell::is_json() {
336 serde_json::to_value(&block).unwrap().to_string()
337 } else {
338 block.pretty()
339 })
340 }
341
342 async fn block_field_as_num<B: Into<BlockId>>(&self, block: B, field: String) -> Result<U256> {
343 Self::block(
344 self,
345 block.into(),
346 false,
347 // Select only select field
348 vec![field],
349 false,
350 )
351 .await?
352 .parse()
353 .map_err(Into::into)
354 }
355
356 pub async fn base_fee<B: Into<BlockId>>(&self, block: B) -> Result<U256> {
357 Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await
358 }
359
360 pub async fn age<B: Into<BlockId>>(&self, block: B) -> Result<String> {
361 let timestamp_str =
362 Self::block_field_as_num(self, block, String::from("timestamp")).await?.to_string();
363 let datetime = DateTime::from_timestamp(timestamp_str.parse::<i64>().unwrap(), 0).unwrap();
364 Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string())
365 }
366
367 pub async fn timestamp<B: Into<BlockId>>(&self, block: B) -> Result<U256> {
368 Self::block_field_as_num(self, block, "timestamp".to_string()).await
369 }
370
371 pub async fn chain(&self) -> Result<&str> {
372 let genesis_hash = Self::block(
373 self,
374 0,
375 false,
376 // Select only block hash
377 vec![String::from("hash")],
378 false,
379 )
380 .await?;
381
382 Ok(match &genesis_hash[..] {
383 "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => {
384 match &(Self::block(self, 1920000, false, vec![String::from("hash")], false)
385 .await?)[..]
386 {
387 "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => {
388 "etclive"
389 }
390 _ => "ethlive",
391 }
392 }
393 "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan",
394 "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten",
395 "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => {
396 "optimism-mainnet"
397 }
398 "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => {
399 "optimism-goerli"
400 }
401 "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => {
402 "optimism-kovan"
403 }
404 "0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa" => "fraxtal",
405 "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489" => {
406 "fraxtal-testnet"
407 }
408 "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => {
409 "arbitrum-mainnet"
410 }
411 "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden",
412 "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby",
413 "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli",
414 "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti",
415 "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon-pos",
416 "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697" => {
417 "polygon-pos-amoy-testnet"
418 }
419 "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741" => "polygon-zkevm",
420 "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9" => {
421 "polygon-zkevm-cardona-testnet"
422 }
423 "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis",
424 "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado",
425 "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest",
426 "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc",
427 "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => {
428 match &(Self::block(self, 1, false, vec![String::from("hash")], false).await?)[..] {
429 "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => {
430 "avalanche-fuji"
431 }
432 _ => "avalanche",
433 }
434 }
435 "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee" => "ink",
436 "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834" => "ink-sepolia",
437 _ => "unknown",
438 })
439 }
440
441 pub async fn chain_id(&self) -> Result<u64> {
442 Ok(self.provider.get_chain_id().await?)
443 }
444
445 pub async fn block_number(&self) -> Result<u64> {
446 Ok(self.provider.get_block_number().await?)
447 }
448
449 pub async fn gas_price(&self) -> Result<u128> {
450 Ok(self.provider.get_gas_price().await?)
451 }
452
453 /// # Example
454 ///
455 /// ```
456 /// use alloy_primitives::Address;
457 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
458 /// use cast::Cast;
459 /// use std::str::FromStr;
460 ///
461 /// # async fn foo() -> eyre::Result<()> {
462 /// let provider =
463 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
464 /// let cast = Cast::new(provider);
465 /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
466 /// let nonce = cast.nonce(addr, None).await?;
467 /// println!("{}", nonce);
468 /// # Ok(())
469 /// # }
470 /// ```
471 pub async fn nonce(&self, who: Address, block: Option<BlockId>) -> Result<u64> {
472 Ok(self.provider.get_transaction_count(who).block_id(block.unwrap_or_default()).await?)
473 }
474
475 /// #Example
476 ///
477 /// ```
478 /// use alloy_primitives::{Address, FixedBytes};
479 /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider};
480 /// use cast::Cast;
481 /// use std::str::FromStr;
482 ///
483 /// # async fn foo() -> eyre::Result<()> {
484 /// let provider =
485 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
486 /// let cast = Cast::new(provider);
487 /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
488 /// let slots = vec![FixedBytes::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")?];
489 /// let codehash = cast.codehash(addr, slots, None).await?;
490 /// println!("{}", codehash);
491 /// # Ok(())
492 /// # }
493 pub async fn codehash(
494 &self,
495 who: Address,
496 slots: Vec<B256>,
497 block: Option<BlockId>,
498 ) -> Result<String> {
499 Ok(self
500 .provider
501 .get_proof(who, slots)
502 .block_id(block.unwrap_or_default())
503 .await?
504 .code_hash
505 .to_string())
506 }
507
508 /// #Example
509 ///
510 /// ```
511 /// use alloy_primitives::{Address, FixedBytes};
512 /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider};
513 /// use cast::Cast;
514 /// use std::str::FromStr;
515 ///
516 /// # async fn foo() -> eyre::Result<()> {
517 /// let provider =
518 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
519 /// let cast = Cast::new(provider);
520 /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
521 /// let slots = vec![FixedBytes::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")?];
522 /// let storage_root = cast.storage_root(addr, slots, None).await?;
523 /// println!("{}", storage_root);
524 /// # Ok(())
525 /// # }
526 pub async fn storage_root(
527 &self,
528 who: Address,
529 slots: Vec<B256>,
530 block: Option<BlockId>,
531 ) -> Result<String> {
532 Ok(self
533 .provider
534 .get_proof(who, slots)
535 .block_id(block.unwrap_or_default())
536 .await?
537 .storage_hash
538 .to_string())
539 }
540
541 /// # Example
542 ///
543 /// ```
544 /// use alloy_primitives::Address;
545 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
546 /// use cast::Cast;
547 /// use std::str::FromStr;
548 ///
549 /// # async fn foo() -> eyre::Result<()> {
550 /// let provider =
551 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
552 /// let cast = Cast::new(provider);
553 /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
554 /// let implementation = cast.implementation(addr, false, None).await?;
555 /// println!("{}", implementation);
556 /// # Ok(())
557 /// # }
558 /// ```
559 pub async fn implementation(
560 &self,
561 who: Address,
562 is_beacon: bool,
563 block: Option<BlockId>,
564 ) -> Result<String> {
565 let slot = match is_beacon {
566 true => {
567 // Use the beacon slot : bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)
568 B256::from_str(
569 "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
570 )?
571 }
572 false => {
573 // Use the implementation slot :
574 // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
575 B256::from_str(
576 "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
577 )?
578 }
579 };
580
581 let value = self
582 .provider
583 .get_storage_at(who, slot.into())
584 .block_id(block.unwrap_or_default())
585 .await?;
586 let addr = Address::from_word(value.into());
587 Ok(format!("{addr:?}"))
588 }
589
590 /// # Example
591 ///
592 /// ```
593 /// use alloy_primitives::Address;
594 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
595 /// use cast::Cast;
596 /// use std::str::FromStr;
597 ///
598 /// # async fn foo() -> eyre::Result<()> {
599 /// let provider =
600 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
601 /// let cast = Cast::new(provider);
602 /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
603 /// let admin = cast.admin(addr, None).await?;
604 /// println!("{}", admin);
605 /// # Ok(())
606 /// # }
607 /// ```
608 pub async fn admin(&self, who: Address, block: Option<BlockId>) -> Result<String> {
609 let slot =
610 B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?;
611 let value = self
612 .provider
613 .get_storage_at(who, slot.into())
614 .block_id(block.unwrap_or_default())
615 .await?;
616 let addr = Address::from_word(value.into());
617 Ok(format!("{addr:?}"))
618 }
619
620 /// # Example
621 ///
622 /// ```
623 /// use alloy_primitives::{Address, U256};
624 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
625 /// use cast::Cast;
626 /// use std::str::FromStr;
627 ///
628 /// # async fn foo() -> eyre::Result<()> {
629 /// let provider =
630 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
631 /// let cast = Cast::new(provider);
632 /// let addr = Address::from_str("7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
633 /// let computed_address = cast.compute_address(addr, None).await?;
634 /// println!("Computed address for address {addr}: {computed_address}");
635 /// # Ok(())
636 /// # }
637 /// ```
638 pub async fn compute_address(&self, address: Address, nonce: Option<u64>) -> Result<Address> {
639 let unpacked = if let Some(n) = nonce { n } else { self.nonce(address, None).await? };
640 Ok(address.create(unpacked))
641 }
642
643 /// # Example
644 ///
645 /// ```
646 /// use alloy_primitives::Address;
647 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
648 /// use cast::Cast;
649 /// use std::str::FromStr;
650 ///
651 /// # async fn foo() -> eyre::Result<()> {
652 /// let provider =
653 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
654 /// let cast = Cast::new(provider);
655 /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?;
656 /// let code = cast.code(addr, None, false).await?;
657 /// println!("{}", code);
658 /// # Ok(())
659 /// # }
660 /// ```
661 pub async fn code(
662 &self,
663 who: Address,
664 block: Option<BlockId>,
665 disassemble: bool,
666 ) -> Result<String> {
667 if disassemble {
668 let code =
669 self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec();
670 SimpleCast::disassemble(&code)
671 } else {
672 Ok(format!(
673 "{}",
674 self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?
675 ))
676 }
677 }
678
679 /// Example
680 ///
681 /// ```
682 /// use alloy_primitives::Address;
683 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
684 /// use cast::Cast;
685 /// use std::str::FromStr;
686 ///
687 /// # async fn foo() -> eyre::Result<()> {
688 /// let provider =
689 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
690 /// let cast = Cast::new(provider);
691 /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?;
692 /// let codesize = cast.codesize(addr, None).await?;
693 /// println!("{}", codesize);
694 /// # Ok(())
695 /// # }
696 /// ```
697 pub async fn codesize(&self, who: Address, block: Option<BlockId>) -> Result<String> {
698 let code =
699 self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec();
700 Ok(code.len().to_string())
701 }
702
703 /// # Example
704 ///
705 /// ```
706 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
707 /// use cast::Cast;
708 ///
709 /// # async fn foo() -> eyre::Result<()> {
710 /// let provider =
711 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
712 /// let cast = Cast::new(provider);
713 /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc";
714 /// let tx = cast.transaction(Some(tx_hash.to_string()), None, None, None, false, false).await?;
715 /// println!("{}", tx);
716 /// # Ok(())
717 /// # }
718 /// ```
719 pub async fn transaction(
720 &self,
721 tx_hash: Option<String>,
722 from: Option<NameOrAddress>,
723 nonce: Option<u64>,
724 field: Option<String>,
725 raw: bool,
726 to_request: bool,
727 ) -> Result<String> {
728 let tx = if let Some(tx_hash) = tx_hash {
729 let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?;
730 self.provider
731 .get_transaction_by_hash(tx_hash)
732 .await?
733 .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?
734 } else if let Some(from) = from {
735 // If nonce is not provided, uses 0.
736 let nonce = U64::from(nonce.unwrap_or_default());
737 let from = from.resolve(self.provider.root()).await?;
738
739 self.provider
740 .raw_request::<_, Option<AnyRpcTransaction>>(
741 "eth_getTransactionBySenderAndNonce".into(),
742 (from, nonce),
743 )
744 .await?
745 .ok_or_else(|| {
746 eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::<u64>())
747 })?
748 } else {
749 eyre::bail!("tx hash or from address is required")
750 };
751
752 Ok(if raw {
753 // convert to FoundryTxEnvelope to support all foundry tx types (including opstack
754 // deposit transactions)
755 let foundry_tx = FoundryTxEnvelope::try_from(tx)?;
756 let encoded = foundry_tx.encoded_2718();
757 format!("0x{}", hex::encode(encoded))
758 } else if let Some(ref field) = field {
759 get_pretty_tx_attr(&tx.inner, field.as_str())
760 .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))?
761 } else if shell::is_json() {
762 // to_value first to sort json object keys
763 serde_json::to_value(&tx)?.to_string()
764 } else if to_request {
765 serde_json::to_string_pretty(&TransactionRequest::from_recovered_transaction(
766 tx.into(),
767 ))?
768 } else {
769 tx.pretty()
770 })
771 }
772
773 /// Perform a raw JSON-RPC request
774 ///
775 /// # Example
776 ///
777 /// ```
778 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
779 /// use cast::Cast;
780 ///
781 /// # async fn foo() -> eyre::Result<()> {
782 /// let provider =
783 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
784 /// let cast = Cast::new(provider);
785 /// let result = cast
786 /// .rpc("eth_getBalance", &["0xc94770007dda54cF92009BFF0dE90c06F603a09f", "latest"])
787 /// .await?;
788 /// println!("{}", result);
789 /// # Ok(())
790 /// # }
791 /// ```
792 pub async fn rpc<V>(&self, method: &str, params: V) -> Result<String>
793 where
794 V: alloy_json_rpc::RpcSend,
795 {
796 let res = self
797 .provider
798 .raw_request::<V, serde_json::Value>(Cow::Owned(method.to_string()), params)
799 .await?;
800 Ok(serde_json::to_string(&res)?)
801 }
802
803 /// Returns the slot
804 ///
805 /// # Example
806 ///
807 /// ```
808 /// use alloy_primitives::{Address, B256};
809 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
810 /// use cast::Cast;
811 /// use std::str::FromStr;
812 ///
813 /// # async fn foo() -> eyre::Result<()> {
814 /// let provider =
815 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
816 /// let cast = Cast::new(provider);
817 /// let addr = Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?;
818 /// let slot = B256::ZERO;
819 /// let storage = cast.storage(addr, slot, None).await?;
820 /// println!("{}", storage);
821 /// # Ok(())
822 /// # }
823 /// ```
824 pub async fn storage(
825 &self,
826 from: Address,
827 slot: B256,
828 block: Option<BlockId>,
829 ) -> Result<String> {
830 Ok(format!(
831 "{:?}",
832 B256::from(
833 self.provider
834 .get_storage_at(from, slot.into())
835 .block_id(block.unwrap_or_default())
836 .await?
837 )
838 ))
839 }
840
841 pub async fn filter_logs(&self, filter: Filter) -> Result<String> {
842 let logs = self.provider.get_logs(&filter).await?;
843 Self::format_logs(logs)
844 }
845
846 /// Retrieves logs using chunked requests to handle large block ranges.
847 ///
848 /// Automatically divides large block ranges into smaller chunks to avoid provider limits
849 /// and processes them with controlled concurrency to prevent rate limiting.
850 pub async fn filter_logs_chunked(&self, filter: Filter, chunk_size: u64) -> Result<String> {
851 let logs = self.get_logs_chunked(&filter, chunk_size).await?;
852 Self::format_logs(logs)
853 }
854
855 fn format_logs(logs: Vec<Log>) -> Result<String> {
856 let res = if shell::is_json() {
857 serde_json::to_string(&logs)?
858 } else {
859 let mut s = vec![];
860 for log in logs {
861 let pretty = log
862 .pretty()
863 .replacen('\n', "- ", 1) // Remove empty first line
864 .replace('\n', "\n "); // Indent
865 s.push(pretty);
866 }
867 s.join("\n")
868 };
869 Ok(res)
870 }
871
872 fn extract_block_range(filter: &Filter) -> (Option<u64>, Option<u64>) {
873 let FilterBlockOption::Range { from_block, to_block } = &filter.block_option else {
874 return (None, None);
875 };
876
877 (from_block.and_then(|b| b.as_number()), to_block.and_then(|b| b.as_number()))
878 }
879
880 /// Retrieves logs with automatic chunking fallback.
881 ///
882 /// First tries to fetch logs for the entire range. If that fails,
883 /// falls back to concurrent chunked requests with rate limiting.
884 async fn get_logs_chunked(&self, filter: &Filter, chunk_size: u64) -> Result<Vec<Log>>
885 where
886 P: Clone + Unpin,
887 {
888 // Try the full range first
889 if let Ok(logs) = self.provider.get_logs(filter).await {
890 return Ok(logs);
891 }
892
893 // Fallback: use concurrent chunked approach
894 self.get_logs_chunked_concurrent(filter, chunk_size).await
895 }
896
897 /// Retrieves logs using concurrent chunked requests with rate limiting.
898 ///
899 /// Divides the block range into chunks and processes them with a maximum of
900 /// 5 concurrent requests. Falls back to single-block queries if chunks fail.
901 async fn get_logs_chunked_concurrent(
902 &self,
903 filter: &Filter,
904 chunk_size: u64,
905 ) -> Result<Vec<Log>>
906 where
907 P: Clone + Unpin,
908 {
909 let (from_block, to_block) = Self::extract_block_range(filter);
910 let (Some(from), Some(to)) = (from_block, to_block) else {
911 return self.provider.get_logs(filter).await.map_err(Into::into);
912 };
913
914 if from >= to {
915 return Ok(vec![]);
916 }
917
918 // Create chunk ranges using iterator
919 let chunk_ranges: Vec<(u64, u64)> = (from..to)
920 .step_by(chunk_size as usize)
921 .map(|chunk_start| (chunk_start, (chunk_start + chunk_size).min(to)))
922 .collect();
923
924 // Process chunks with controlled concurrency using buffered stream
925 let mut all_results: Vec<(u64, Vec<Log>)> = futures::stream::iter(chunk_ranges)
926 .map(|(start_block, chunk_end)| {
927 let chunk_filter = filter.clone().from_block(start_block).to_block(chunk_end - 1);
928 let provider = self.provider.clone();
929
930 async move {
931 // Try direct chunk request with simplified fallback
932 match provider.get_logs(&chunk_filter).await {
933 Ok(logs) => (start_block, logs),
934 Err(_) => {
935 // Simple fallback: try individual blocks in this chunk
936 let mut fallback_logs = Vec::new();
937 for single_block in start_block..chunk_end {
938 let single_filter = chunk_filter
939 .clone()
940 .from_block(single_block)
941 .to_block(single_block);
942 if let Ok(logs) = provider.get_logs(&single_filter).await {
943 fallback_logs.extend(logs);
944 }
945 }
946 (start_block, fallback_logs)
947 }
948 }
949 }
950 })
951 .buffered(5) // Limit to 5 concurrent requests to avoid rate limits
952 .collect()
953 .await;
954
955 // Sort once at the end by block number and flatten
956 all_results.sort_by_key(|(block_num, _)| *block_num);
957
958 let mut all_logs = Vec::new();
959 for (_, logs) in all_results {
960 all_logs.extend(logs);
961 }
962
963 Ok(all_logs)
964 }
965
966 /// Converts a block identifier into a block number.
967 ///
968 /// If the block identifier is a block number, then this function returns the block number. If
969 /// the block identifier is a block hash, then this function returns the block number of
970 /// that block hash. If the block identifier is `None`, then this function returns `None`.
971 ///
972 /// # Example
973 ///
974 /// ```
975 /// use alloy_primitives::fixed_bytes;
976 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
977 /// use alloy_rpc_types::{BlockId, BlockNumberOrTag};
978 /// use cast::Cast;
979 /// use std::{convert::TryFrom, str::FromStr};
980 ///
981 /// # async fn foo() -> eyre::Result<()> {
982 /// let provider =
983 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?;
984 /// let cast = Cast::new(provider);
985 ///
986 /// let block_number = cast.convert_block_number(Some(BlockId::number(5))).await?;
987 /// assert_eq!(block_number, Some(BlockNumberOrTag::Number(5)));
988 ///
989 /// let block_number = cast
990 /// .convert_block_number(Some(BlockId::hash(fixed_bytes!(
991 /// "0000000000000000000000000000000000000000000000000000000000001234"
992 /// ))))
993 /// .await?;
994 /// assert_eq!(block_number, Some(BlockNumberOrTag::Number(4660)));
995 ///
996 /// let block_number = cast.convert_block_number(None).await?;
997 /// assert_eq!(block_number, None);
998 /// # Ok(())
999 /// # }
1000 /// ```
1001 pub async fn convert_block_number(
1002 &self,
1003 block: Option<BlockId>,
1004 ) -> Result<Option<BlockNumberOrTag>, eyre::Error> {
1005 match block {
1006 Some(block) => match block {
1007 BlockId::Number(block_number) => Ok(Some(block_number)),
1008 BlockId::Hash(hash) => {
1009 let block = self.provider.get_block_by_hash(hash.block_hash).await?;
1010 Ok(block.map(|block| block.header.number).map(BlockNumberOrTag::from))
1011 }
1012 },
1013 None => Ok(None),
1014 }
1015 }
1016
1017 /// Sets up a subscription to the given filter and writes the logs to the given output.
1018 ///
1019 /// # Example
1020 ///
1021 /// ```
1022 /// use alloy_primitives::Address;
1023 /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork};
1024 /// use alloy_rpc_types::Filter;
1025 /// use alloy_transport::BoxTransport;
1026 /// use cast::Cast;
1027 /// use std::{io, str::FromStr};
1028 ///
1029 /// # async fn foo() -> eyre::Result<()> {
1030 /// let provider =
1031 /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("wss://localhost:8545").await?;
1032 /// let cast = Cast::new(provider);
1033 ///
1034 /// let filter =
1035 /// Filter::new().address(Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?);
1036 /// let mut output = io::stdout();
1037 /// cast.subscribe(filter, &mut output).await?;
1038 /// # Ok(())
1039 /// # }
1040 /// ```
1041 pub async fn subscribe(&self, filter: Filter, output: &mut dyn io::Write) -> Result<()> {
1042 // Initialize the subscription stream for logs
1043 let mut subscription = self.provider.subscribe_logs(&filter).await?.into_stream();
1044
1045 // Check if a to_block is specified, if so, subscribe to blocks
1046 let mut block_subscription = if filter.get_to_block().is_some() {
1047 Some(self.provider.subscribe_blocks().await?.into_stream())
1048 } else {
1049 None
1050 };
1051
1052 let format_json = shell::is_json();
1053 let to_block_number = filter.get_to_block();
1054
1055 // If output should be JSON, start with an opening bracket
1056 if format_json {
1057 write!(output, "[")?;
1058 }
1059
1060 let mut first = true;
1061
1062 loop {
1063 tokio::select! {
1064 // If block subscription is present, listen to it to avoid blocking indefinitely past the desired to_block
1065 block = if let Some(bs) = &mut block_subscription {
1066 Either::Left(bs.next().fuse())
1067 } else {
1068 Either::Right(futures::future::pending())
1069 } => {
1070 if let (Some(block), Some(to_block)) = (block, to_block_number)
1071 && block.number > to_block {
1072 break;
1073 }
1074 },
1075 // Process incoming log
1076 log = subscription.next() => {
1077 if format_json {
1078 if !first {
1079 write!(output, ",")?;
1080 }
1081 first = false;
1082 let log_str = serde_json::to_string(&log).unwrap();
1083 write!(output, "{log_str}")?;
1084 } else {
1085 let log_str = log.pretty()
1086 .replacen('\n', "- ", 1) // Remove empty first line
1087 .replace('\n', "\n "); // Indent
1088 writeln!(output, "{log_str}")?;
1089 }
1090 },
1091 // Break on cancel signal, to allow for closing JSON bracket
1092 _ = ctrl_c() => {
1093 break;
1094 },
1095 else => break,
1096 }
1097 }
1098
1099 // If output was JSON, end with a closing bracket
1100 if format_json {
1101 write!(output, "]")?;
1102 }
1103
1104 Ok(())
1105 }
1106}
1107
1108pub struct SimpleCast;
1109
1110impl SimpleCast {
1111 /// Returns the maximum value of the given integer type
1112 ///
1113 /// # Example
1114 ///
1115 /// ```
1116 /// use alloy_primitives::{I256, U256};
1117 /// use cast::SimpleCast;
1118 ///
1119 /// assert_eq!(SimpleCast::max_int("uint256")?, U256::MAX.to_string());
1120 /// assert_eq!(SimpleCast::max_int("int256")?, I256::MAX.to_string());
1121 /// assert_eq!(SimpleCast::max_int("int32")?, i32::MAX.to_string());
1122 /// # Ok::<(), eyre::Report>(())
1123 /// ```
1124 pub fn max_int(s: &str) -> Result<String> {
1125 Self::max_min_int::<true>(s)
1126 }
1127
1128 /// Returns the maximum value of the given integer type
1129 ///
1130 /// # Example
1131 ///
1132 /// ```
1133 /// use alloy_primitives::{I256, U256};
1134 /// use cast::SimpleCast;
1135 ///
1136 /// assert_eq!(SimpleCast::min_int("uint256")?, "0");
1137 /// assert_eq!(SimpleCast::min_int("int256")?, I256::MIN.to_string());
1138 /// assert_eq!(SimpleCast::min_int("int32")?, i32::MIN.to_string());
1139 /// # Ok::<(), eyre::Report>(())
1140 /// ```
1141 pub fn min_int(s: &str) -> Result<String> {
1142 Self::max_min_int::<false>(s)
1143 }
1144
1145 fn max_min_int<const MAX: bool>(s: &str) -> Result<String> {
1146 let ty = DynSolType::parse(s).wrap_err("Invalid type, expected `(u)int<bit size>`")?;
1147 match ty {
1148 DynSolType::Int(n) => {
1149 let mask = U256::from(1).wrapping_shl(n - 1);
1150 let max = (U256::MAX & mask).saturating_sub(U256::from(1));
1151 if MAX {
1152 Ok(max.to_string())
1153 } else {
1154 let min = I256::from_raw(max).wrapping_neg() + I256::MINUS_ONE;
1155 Ok(min.to_string())
1156 }
1157 }
1158 DynSolType::Uint(n) => {
1159 if MAX {
1160 let mut max = U256::MAX;
1161 if n < 255 {
1162 max &= U256::from(1).wrapping_shl(n).wrapping_sub(U256::from(1));
1163 }
1164 Ok(max.to_string())
1165 } else {
1166 Ok("0".to_string())
1167 }
1168 }
1169 _ => Err(eyre::eyre!("Type is not int/uint: {s}")),
1170 }
1171 }
1172
1173 /// Converts UTF-8 text input to hex
1174 ///
1175 /// # Example
1176 ///
1177 /// ```
1178 /// use cast::SimpleCast as Cast;
1179 ///
1180 /// assert_eq!(Cast::from_utf8("yo"), "0x796f");
1181 /// assert_eq!(Cast::from_utf8("Hello, World!"), "0x48656c6c6f2c20576f726c6421");
1182 /// assert_eq!(Cast::from_utf8("TurboDappTools"), "0x547572626f44617070546f6f6c73");
1183 /// # Ok::<_, eyre::Report>(())
1184 /// ```
1185 pub fn from_utf8(s: &str) -> String {
1186 hex::encode_prefixed(s)
1187 }
1188
1189 /// Converts hex input to UTF-8 text
1190 ///
1191 /// # Example
1192 ///
1193 /// ```
1194 /// use cast::SimpleCast as Cast;
1195 ///
1196 /// assert_eq!(Cast::to_utf8("0x796f")?, "yo");
1197 /// assert_eq!(Cast::to_utf8("0x48656c6c6f2c20576f726c6421")?, "Hello, World!");
1198 /// assert_eq!(Cast::to_utf8("0x547572626f44617070546f6f6c73")?, "TurboDappTools");
1199 /// assert_eq!(Cast::to_utf8("0xe4bda0e5a5bd")?, "ä½ å¥½");
1200 /// # Ok::<_, eyre::Report>(())
1201 /// ```
1202 pub fn to_utf8(s: &str) -> Result<String> {
1203 let bytes = hex::decode(s)?;
1204 Ok(String::from_utf8_lossy(bytes.as_ref()).to_string())
1205 }
1206
1207 /// Converts hex data into text data
1208 ///
1209 /// # Example
1210 ///
1211 /// ```
1212 /// use cast::SimpleCast as Cast;
1213 ///
1214 /// assert_eq!(Cast::to_ascii("0x796f")?, "yo");
1215 /// assert_eq!(Cast::to_ascii("48656c6c6f2c20576f726c6421")?, "Hello, World!");
1216 /// assert_eq!(Cast::to_ascii("0x547572626f44617070546f6f6c73")?, "TurboDappTools");
1217 /// # Ok::<_, eyre::Report>(())
1218 /// ```
1219 pub fn to_ascii(hex: &str) -> Result<String> {
1220 let bytes = hex::decode(hex)?;
1221 if !bytes.iter().all(u8::is_ascii) {
1222 return Err(eyre::eyre!("Invalid ASCII bytes"));
1223 }
1224 Ok(String::from_utf8(bytes).unwrap())
1225 }
1226
1227 /// Converts fixed point number into specified number of decimals
1228 /// ```
1229 /// use alloy_primitives::U256;
1230 /// use cast::SimpleCast as Cast;
1231 ///
1232 /// assert_eq!(Cast::from_fixed_point("10", "0")?, "10");
1233 /// assert_eq!(Cast::from_fixed_point("1.0", "1")?, "10");
1234 /// assert_eq!(Cast::from_fixed_point("0.10", "2")?, "10");
1235 /// assert_eq!(Cast::from_fixed_point("0.010", "3")?, "10");
1236 /// # Ok::<_, eyre::Report>(())
1237 /// ```
1238 pub fn from_fixed_point(value: &str, decimals: &str) -> Result<String> {
1239 let units: Unit = Unit::from_str(decimals)?;
1240 let n = ParseUnits::parse_units(value, units)?;
1241 Ok(n.to_string())
1242 }
1243
1244 /// Converts integers with specified decimals into fixed point numbers
1245 ///
1246 /// # Example
1247 ///
1248 /// ```
1249 /// use alloy_primitives::U256;
1250 /// use cast::SimpleCast as Cast;
1251 ///
1252 /// assert_eq!(Cast::to_fixed_point("10", "0")?, "10.");
1253 /// assert_eq!(Cast::to_fixed_point("10", "1")?, "1.0");
1254 /// assert_eq!(Cast::to_fixed_point("10", "2")?, "0.10");
1255 /// assert_eq!(Cast::to_fixed_point("10", "3")?, "0.010");
1256 ///
1257 /// assert_eq!(Cast::to_fixed_point("-10", "0")?, "-10.");
1258 /// assert_eq!(Cast::to_fixed_point("-10", "1")?, "-1.0");
1259 /// assert_eq!(Cast::to_fixed_point("-10", "2")?, "-0.10");
1260 /// assert_eq!(Cast::to_fixed_point("-10", "3")?, "-0.010");
1261 /// # Ok::<_, eyre::Report>(())
1262 /// ```
1263 pub fn to_fixed_point(value: &str, decimals: &str) -> Result<String> {
1264 let (sign, mut value, value_len) = {
1265 let number = NumberWithBase::parse_int(value, None)?;
1266 let sign = if number.is_nonnegative() { "" } else { "-" };
1267 let value = format!("{number:#}");
1268 let value_stripped = value.strip_prefix('-').unwrap_or(&value).to_string();
1269 let value_len = value_stripped.len();
1270 (sign, value_stripped, value_len)
1271 };
1272 let decimals = NumberWithBase::parse_uint(decimals, None)?.number().to::<usize>();
1273
1274 let value = if decimals >= value_len {
1275 // Add "0." and pad with 0s
1276 format!("0.{value:0>decimals$}")
1277 } else {
1278 // Insert decimal at -idx (i.e 1 => decimal idx = -1)
1279 value.insert(value_len - decimals, '.');
1280 value
1281 };
1282
1283 Ok(format!("{sign}{value}"))
1284 }
1285
1286 /// Concatencates hex strings
1287 ///
1288 /// # Example
1289 ///
1290 /// ```
1291 /// use cast::SimpleCast as Cast;
1292 ///
1293 /// assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001");
1294 /// assert_eq!(Cast::concat_hex(["1", "2"]), "0x12");
1295 /// # Ok::<_, eyre::Report>(())
1296 /// ```
1297 pub fn concat_hex<T: AsRef<str>>(values: impl IntoIterator<Item = T>) -> String {
1298 let mut out = String::new();
1299 for s in values {
1300 let s = s.as_ref();
1301 out.push_str(strip_0x(s))
1302 }
1303 format!("0x{out}")
1304 }
1305
1306 /// Converts a number into uint256 hex string with 0x prefix
1307 ///
1308 /// # Example
1309 ///
1310 /// ```
1311 /// use cast::SimpleCast as Cast;
1312 ///
1313 /// assert_eq!(
1314 /// Cast::to_uint256("100")?,
1315 /// "0x0000000000000000000000000000000000000000000000000000000000000064"
1316 /// );
1317 /// assert_eq!(
1318 /// Cast::to_uint256("192038293923")?,
1319 /// "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"
1320 /// );
1321 /// assert_eq!(
1322 /// Cast::to_uint256(
1323 /// "115792089237316195423570985008687907853269984665640564039457584007913129639935"
1324 /// )?,
1325 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1326 /// );
1327 /// # Ok::<_, eyre::Report>(())
1328 /// ```
1329 pub fn to_uint256(value: &str) -> Result<String> {
1330 let n = NumberWithBase::parse_uint(value, None)?;
1331 Ok(format!("{n:#066x}"))
1332 }
1333
1334 /// Converts a number into int256 hex string with 0x prefix
1335 ///
1336 /// # Example
1337 ///
1338 /// ```
1339 /// use cast::SimpleCast as Cast;
1340 ///
1341 /// assert_eq!(
1342 /// Cast::to_int256("0")?,
1343 /// "0x0000000000000000000000000000000000000000000000000000000000000000"
1344 /// );
1345 /// assert_eq!(
1346 /// Cast::to_int256("100")?,
1347 /// "0x0000000000000000000000000000000000000000000000000000000000000064"
1348 /// );
1349 /// assert_eq!(
1350 /// Cast::to_int256("-100")?,
1351 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c"
1352 /// );
1353 /// assert_eq!(
1354 /// Cast::to_int256("192038293923")?,
1355 /// "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"
1356 /// );
1357 /// assert_eq!(
1358 /// Cast::to_int256("-192038293923")?,
1359 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffd349a02e5d"
1360 /// );
1361 /// assert_eq!(
1362 /// Cast::to_int256(
1363 /// "57896044618658097711785492504343953926634992332820282019728792003956564819967"
1364 /// )?,
1365 /// "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1366 /// );
1367 /// assert_eq!(
1368 /// Cast::to_int256(
1369 /// "-57896044618658097711785492504343953926634992332820282019728792003956564819968"
1370 /// )?,
1371 /// "0x8000000000000000000000000000000000000000000000000000000000000000"
1372 /// );
1373 /// # Ok::<_, eyre::Report>(())
1374 /// ```
1375 pub fn to_int256(value: &str) -> Result<String> {
1376 let n = NumberWithBase::parse_int(value, None)?;
1377 Ok(format!("{n:#066x}"))
1378 }
1379
1380 /// Converts an eth amount into a specified unit
1381 ///
1382 /// # Example
1383 ///
1384 /// ```
1385 /// use cast::SimpleCast as Cast;
1386 ///
1387 /// assert_eq!(Cast::to_unit("1 wei", "wei")?, "1");
1388 /// assert_eq!(Cast::to_unit("1", "wei")?, "1");
1389 /// assert_eq!(Cast::to_unit("1ether", "wei")?, "1000000000000000000");
1390 /// # Ok::<_, eyre::Report>(())
1391 /// ```
1392 pub fn to_unit(value: &str, unit: &str) -> Result<String> {
1393 let value = DynSolType::coerce_str(&DynSolType::Uint(256), value)?
1394 .as_uint()
1395 .wrap_err("Could not convert to uint")?
1396 .0;
1397 let unit = unit.parse().wrap_err("could not parse units")?;
1398 Ok(Self::format_unit_as_string(value, unit))
1399 }
1400
1401 /// Convert a number into a uint with arbitrary decimals.
1402 ///
1403 /// # Example
1404 ///
1405 /// ```
1406 /// use cast::SimpleCast as Cast;
1407 ///
1408 /// # fn main() -> eyre::Result<()> {
1409 /// assert_eq!(Cast::parse_units("1.0", 6)?, "1000000"); // USDC (6 decimals)
1410 /// assert_eq!(Cast::parse_units("2.5", 6)?, "2500000");
1411 /// assert_eq!(Cast::parse_units("1.0", 12)?, "1000000000000"); // 12 decimals
1412 /// assert_eq!(Cast::parse_units("1.23", 3)?, "1230"); // 3 decimals
1413 ///
1414 /// # Ok(())
1415 /// # }
1416 /// ```
1417 pub fn parse_units(value: &str, unit: u8) -> Result<String> {
1418 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
1419
1420 Ok(ParseUnits::parse_units(value, unit)?.to_string())
1421 }
1422
1423 /// Format a number from smallest unit to decimal with arbitrary decimals.
1424 ///
1425 /// # Example
1426 ///
1427 /// ```
1428 /// use cast::SimpleCast as Cast;
1429 ///
1430 /// # fn main() -> eyre::Result<()> {
1431 /// assert_eq!(Cast::format_units("1000000", 6)?, "1"); // USDC (6 decimals)
1432 /// assert_eq!(Cast::format_units("2500000", 6)?, "2.500000");
1433 /// assert_eq!(Cast::format_units("1000000000000", 12)?, "1"); // 12 decimals
1434 /// assert_eq!(Cast::format_units("1230", 3)?, "1.230"); // 3 decimals
1435 ///
1436 /// # Ok(())
1437 /// # }
1438 /// ```
1439 pub fn format_units(value: &str, unit: u8) -> Result<String> {
1440 let value = NumberWithBase::parse_int(value, None)?.number();
1441 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
1442 Ok(Self::format_unit_as_string(value, unit))
1443 }
1444
1445 // Helper function to format units as a string
1446 fn format_unit_as_string(value: U256, unit: Unit) -> String {
1447 let mut formatted = ParseUnits::U256(value).format_units(unit);
1448 // Trim empty fractional part.
1449 if let Some(dot) = formatted.find('.') {
1450 let fractional = &formatted[dot + 1..];
1451 if fractional.chars().all(|c: char| c == '0') {
1452 formatted = formatted[..dot].to_string();
1453 }
1454 }
1455 formatted
1456 }
1457
1458 /// Converts wei into an eth amount
1459 ///
1460 /// # Example
1461 ///
1462 /// ```
1463 /// use cast::SimpleCast as Cast;
1464 ///
1465 /// assert_eq!(Cast::from_wei("1", "gwei")?, "0.000000001");
1466 /// assert_eq!(Cast::from_wei("12340000005", "gwei")?, "12.340000005");
1467 /// assert_eq!(Cast::from_wei("10", "ether")?, "0.000000000000000010");
1468 /// assert_eq!(Cast::from_wei("100", "eth")?, "0.000000000000000100");
1469 /// assert_eq!(Cast::from_wei("17", "ether")?, "0.000000000000000017");
1470 /// # Ok::<_, eyre::Report>(())
1471 /// ```
1472 pub fn from_wei(value: &str, unit: &str) -> Result<String> {
1473 let value = NumberWithBase::parse_int(value, None)?.number();
1474 Ok(ParseUnits::U256(value).format_units(unit.parse()?))
1475 }
1476
1477 /// Converts an eth amount into wei
1478 ///
1479 /// # Example
1480 ///
1481 /// ```
1482 /// use cast::SimpleCast as Cast;
1483 ///
1484 /// assert_eq!(Cast::to_wei("100", "gwei")?, "100000000000");
1485 /// assert_eq!(Cast::to_wei("100", "eth")?, "100000000000000000000");
1486 /// assert_eq!(Cast::to_wei("1000", "ether")?, "1000000000000000000000");
1487 /// # Ok::<_, eyre::Report>(())
1488 /// ```
1489 pub fn to_wei(value: &str, unit: &str) -> Result<String> {
1490 let unit = unit.parse().wrap_err("could not parse units")?;
1491 Ok(ParseUnits::parse_units(value, unit)?.to_string())
1492 }
1493
1494 // Decodes RLP encoded data with validation for canonical integer representation
1495 ///
1496 /// # Examples
1497 /// ```
1498 /// use cast::SimpleCast as Cast;
1499 ///
1500 /// assert_eq!(Cast::from_rlp("0xc0", false).unwrap(), "[]");
1501 /// assert_eq!(Cast::from_rlp("0x0f", false).unwrap(), "\"0x0f\"");
1502 /// assert_eq!(Cast::from_rlp("0x33", false).unwrap(), "\"0x33\"");
1503 /// assert_eq!(Cast::from_rlp("0xc161", false).unwrap(), "[\"0x61\"]");
1504 /// assert_eq!(Cast::from_rlp("820002", true).is_err(), true);
1505 /// assert_eq!(Cast::from_rlp("820002", false).unwrap(), "\"0x0002\"");
1506 /// assert_eq!(Cast::from_rlp("00", true).is_err(), true);
1507 /// assert_eq!(Cast::from_rlp("00", false).unwrap(), "\"0x00\"");
1508 /// # Ok::<_, eyre::Report>(())
1509 /// ```
1510 pub fn from_rlp(value: impl AsRef<str>, as_int: bool) -> Result<String> {
1511 let bytes = hex::decode(value.as_ref()).wrap_err("Could not decode hex")?;
1512
1513 if as_int {
1514 return Ok(U256::decode(&mut &bytes[..])?.to_string());
1515 }
1516
1517 let item = Item::decode(&mut &bytes[..]).wrap_err("Could not decode rlp")?;
1518
1519 Ok(item.to_string())
1520 }
1521
1522 /// Encodes hex data or list of hex data to hexadecimal rlp
1523 ///
1524 /// # Example
1525 ///
1526 /// ```
1527 /// use cast::SimpleCast as Cast;
1528 ///
1529 /// assert_eq!(Cast::to_rlp("[]").unwrap(), "0xc0".to_string());
1530 /// assert_eq!(Cast::to_rlp("0x22").unwrap(), "0x22".to_string());
1531 /// assert_eq!(Cast::to_rlp("[\"0x61\"]",).unwrap(), "0xc161".to_string());
1532 /// assert_eq!(Cast::to_rlp("[\"0xf1\", \"f2\"]").unwrap(), "0xc481f181f2".to_string());
1533 /// # Ok::<_, eyre::Report>(())
1534 /// ```
1535 pub fn to_rlp(value: &str) -> Result<String> {
1536 let val = serde_json::from_str(value)
1537 .unwrap_or_else(|_| serde_json::Value::String(value.to_string()));
1538 let item = Item::value_to_item(&val)?;
1539 Ok(format!("0x{}", hex::encode(alloy_rlp::encode(item))))
1540 }
1541
1542 /// Converts a number of one base to another
1543 ///
1544 /// # Example
1545 ///
1546 /// ```
1547 /// use alloy_primitives::I256;
1548 /// use cast::SimpleCast as Cast;
1549 ///
1550 /// assert_eq!(Cast::to_base("100", Some("10"), "16")?, "0x64");
1551 /// assert_eq!(Cast::to_base("100", Some("10"), "oct")?, "0o144");
1552 /// assert_eq!(Cast::to_base("100", Some("10"), "binary")?, "0b1100100");
1553 ///
1554 /// assert_eq!(Cast::to_base("0xffffffffffffffff", None, "10")?, u64::MAX.to_string());
1555 /// assert_eq!(
1556 /// Cast::to_base("0xffffffffffffffffffffffffffffffff", None, "dec")?,
1557 /// u128::MAX.to_string()
1558 /// );
1559 /// // U256::MAX overflows as internally it is being parsed as I256
1560 /// assert_eq!(
1561 /// Cast::to_base(
1562 /// "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1563 /// None,
1564 /// "decimal"
1565 /// )?,
1566 /// I256::MAX.to_string()
1567 /// );
1568 /// # Ok::<_, eyre::Report>(())
1569 /// ```
1570 pub fn to_base(value: &str, base_in: Option<&str>, base_out: &str) -> Result<String> {
1571 let base_in = Base::unwrap_or_detect(base_in, value)?;
1572 let base_out: Base = base_out.parse()?;
1573 if base_in == base_out {
1574 return Ok(value.to_string());
1575 }
1576
1577 let mut n = NumberWithBase::parse_int(value, Some(&base_in.to_string()))?;
1578 n.set_base(base_out);
1579
1580 // Use Debug fmt
1581 Ok(format!("{n:#?}"))
1582 }
1583
1584 /// Converts hexdata into bytes32 value
1585 ///
1586 /// # Example
1587 ///
1588 /// ```
1589 /// use cast::SimpleCast as Cast;
1590 ///
1591 /// let bytes = Cast::to_bytes32("1234")?;
1592 /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000");
1593 ///
1594 /// let bytes = Cast::to_bytes32("0x1234")?;
1595 /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000");
1596 ///
1597 /// let err = Cast::to_bytes32("0x123400000000000000000000000000000000000000000000000000000000000011").unwrap_err();
1598 /// assert_eq!(err.to_string(), "string >32 bytes");
1599 /// # Ok::<_, eyre::Report>(())
1600 pub fn to_bytes32(s: &str) -> Result<String> {
1601 let s = strip_0x(s);
1602 if s.len() > 64 {
1603 eyre::bail!("string >32 bytes");
1604 }
1605
1606 let padded = format!("{s:0<64}");
1607 Ok(padded.parse::<B256>()?.to_string())
1608 }
1609
1610 /// Encodes string into bytes32 value
1611 pub fn format_bytes32_string(s: &str) -> Result<String> {
1612 let str_bytes: &[u8] = s.as_bytes();
1613 eyre::ensure!(str_bytes.len() <= 32, "bytes32 strings must not exceed 32 bytes in length");
1614
1615 let mut bytes32: [u8; 32] = [0u8; 32];
1616 bytes32[..str_bytes.len()].copy_from_slice(str_bytes);
1617 Ok(hex::encode_prefixed(bytes32))
1618 }
1619
1620 /// Pads hex data to a specified length
1621 ///
1622 /// # Example
1623 ///
1624 /// ```
1625 /// use cast::SimpleCast as Cast;
1626 ///
1627 /// let padded = Cast::pad("abcd", true, 20)?;
1628 /// assert_eq!(padded, "0xabcd000000000000000000000000000000000000");
1629 ///
1630 /// let padded = Cast::pad("abcd", false, 20)?;
1631 /// assert_eq!(padded, "0x000000000000000000000000000000000000abcd");
1632 ///
1633 /// let padded = Cast::pad("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", true, 32)?;
1634 /// assert_eq!(padded, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2000000000000000000000000");
1635 ///
1636 /// let padded = Cast::pad("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", false, 32)?;
1637 /// assert_eq!(padded, "0x000000000000000000000000C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
1638 ///
1639 /// let err = Cast::pad("1234", false, 1).unwrap_err();
1640 /// assert_eq!(err.to_string(), "input length exceeds target length");
1641 ///
1642 /// let err = Cast::pad("foobar", false, 32).unwrap_err();
1643 /// assert_eq!(err.to_string(), "input is not a valid hex");
1644 ///
1645 /// # Ok::<_, eyre::Report>(())
1646 /// ```
1647 pub fn pad(s: &str, right: bool, len: usize) -> Result<String> {
1648 let s = strip_0x(s);
1649 let hex_len = len * 2;
1650
1651 // Validate input
1652 if s.len() > hex_len {
1653 eyre::bail!("input length exceeds target length");
1654 }
1655 if !s.chars().all(|c| c.is_ascii_hexdigit()) {
1656 eyre::bail!("input is not a valid hex");
1657 }
1658
1659 Ok(if right { format!("0x{s:0<hex_len$}") } else { format!("0x{s:0>hex_len$}") })
1660 }
1661
1662 /// Decodes string from bytes32 value
1663 pub fn parse_bytes32_string(s: &str) -> Result<String> {
1664 let bytes = hex::decode(s)?;
1665 eyre::ensure!(bytes.len() == 32, "expected 32 byte hex-string");
1666 let len = bytes.iter().take_while(|x| **x != 0).count();
1667 Ok(std::str::from_utf8(&bytes[..len])?.into())
1668 }
1669
1670 /// Decodes checksummed address from bytes32 value
1671 pub fn parse_bytes32_address(s: &str) -> Result<String> {
1672 let s = strip_0x(s);
1673 if s.len() != 64 {
1674 eyre::bail!("expected 64 byte hex-string, got {s}");
1675 }
1676
1677 let s = if let Some(stripped) = s.strip_prefix("000000000000000000000000") {
1678 stripped
1679 } else {
1680 return Err(eyre::eyre!("Not convertible to address, there are non-zero bytes"));
1681 };
1682
1683 let lowercase_address_string = format!("0x{s}");
1684 let lowercase_address = Address::from_str(&lowercase_address_string)?;
1685
1686 Ok(lowercase_address.to_checksum(None))
1687 }
1688
1689 /// Decodes abi-encoded hex input or output
1690 ///
1691 /// When `input=true`, `calldata` string MUST not be prefixed with function selector
1692 ///
1693 /// # Example
1694 ///
1695 /// ```
1696 /// use cast::SimpleCast as Cast;
1697 /// use alloy_primitives::hex;
1698 ///
1699 /// // Passing `input = false` will decode the data as the output type.
1700 /// // The input data types and the full function sig are ignored, i.e.
1701 /// // you could also pass `balanceOf()(uint256)` and it'd still work.
1702 /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
1703 /// let sig = "balanceOf(address, uint256)(uint256)";
1704 /// let decoded = Cast::abi_decode(sig, data, false)?[0].as_uint().unwrap().0.to_string();
1705 /// assert_eq!(decoded, "1");
1706 ///
1707 /// // Passing `input = true` will decode the data with the input function signature.
1708 /// // We exclude the "prefixed" function selector from the data field (the first 4 bytes).
1709 /// let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
1710 /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
1711 /// let decoded = Cast::abi_decode(sig, data, true)?;
1712 /// let decoded = [
1713 /// decoded[0].as_address().unwrap().to_string().to_lowercase(),
1714 /// decoded[1].as_address().unwrap().to_string().to_lowercase(),
1715 /// decoded[2].as_uint().unwrap().0.to_string(),
1716 /// decoded[3].as_uint().unwrap().0.to_string(),
1717 /// hex::encode(decoded[4].as_bytes().unwrap())
1718 /// ]
1719 /// .into_iter()
1720 /// .collect::<Vec<_>>();
1721 ///
1722 /// assert_eq!(
1723 /// decoded,
1724 /// vec!["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "42", "1", ""]
1725 /// );
1726 /// # Ok::<_, eyre::Report>(())
1727 /// ```
1728 pub fn abi_decode(sig: &str, calldata: &str, input: bool) -> Result<Vec<DynSolValue>> {
1729 foundry_common::abi::abi_decode_calldata(sig, calldata, input, false)
1730 }
1731
1732 /// Decodes calldata-encoded hex input or output
1733 ///
1734 /// Similar to `abi_decode`, but `calldata` string MUST be prefixed with function selector
1735 ///
1736 /// # Example
1737 ///
1738 /// ```
1739 /// use cast::SimpleCast as Cast;
1740 /// use alloy_primitives::hex;
1741 ///
1742 /// // Passing `input = false` will decode the data as the output type.
1743 /// // The input data types and the full function sig are ignored, i.e.
1744 /// // you could also pass `balanceOf()(uint256)` and it'd still work.
1745 /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
1746 /// let sig = "balanceOf(address, uint256)(uint256)";
1747 /// let decoded = Cast::calldata_decode(sig, data, false)?[0].as_uint().unwrap().0.to_string();
1748 /// assert_eq!(decoded, "1");
1749 ///
1750 /// // Passing `input = true` will decode the data with the input function signature.
1751 /// let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
1752 /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
1753 /// let decoded = Cast::calldata_decode(sig, data, true)?;
1754 /// let decoded = [
1755 /// decoded[0].as_address().unwrap().to_string().to_lowercase(),
1756 /// decoded[1].as_address().unwrap().to_string().to_lowercase(),
1757 /// decoded[2].as_uint().unwrap().0.to_string(),
1758 /// decoded[3].as_uint().unwrap().0.to_string(),
1759 /// hex::encode(decoded[4].as_bytes().unwrap()),
1760 /// ]
1761 /// .into_iter()
1762 /// .collect::<Vec<_>>();
1763 /// assert_eq!(
1764 /// decoded,
1765 /// vec!["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "42", "1", ""]
1766 /// );
1767 /// # Ok::<_, eyre::Report>(())
1768 /// ```
1769 pub fn calldata_decode(sig: &str, calldata: &str, input: bool) -> Result<Vec<DynSolValue>> {
1770 foundry_common::abi::abi_decode_calldata(sig, calldata, input, true)
1771 }
1772
1773 /// Performs ABI encoding based off of the function signature. Does not include
1774 /// the function selector in the result.
1775 ///
1776 /// # Example
1777 ///
1778 /// ```
1779 /// use cast::SimpleCast as Cast;
1780 ///
1781 /// assert_eq!(
1782 /// "0x0000000000000000000000000000000000000000000000000000000000000001",
1783 /// Cast::abi_encode("f(uint a)", &["1"]).unwrap().as_str()
1784 /// );
1785 /// assert_eq!(
1786 /// "0x0000000000000000000000000000000000000000000000000000000000000001",
1787 /// Cast::abi_encode("constructor(uint a)", &["1"]).unwrap().as_str()
1788 /// );
1789 /// # Ok::<_, eyre::Report>(())
1790 /// ```
1791 pub fn abi_encode(sig: &str, args: &[impl AsRef<str>]) -> Result<String> {
1792 let func = get_func(sig)?;
1793 match encode_function_args(&func, args) {
1794 Ok(res) => Ok(hex::encode_prefixed(&res[4..])),
1795 Err(e) => eyre::bail!("Could not ABI encode the function and arguments: {e}"),
1796 }
1797 }
1798
1799 /// Performs packed ABI encoding based off of the function signature or tuple.
1800 ///
1801 /// # Examplez
1802 ///
1803 /// ```
1804 /// use cast::SimpleCast as Cast;
1805 ///
1806 /// assert_eq!(
1807 /// "0x0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000012c00000000000000c8",
1808 /// Cast::abi_encode_packed("(uint128[] a, uint64 b)", &["[100, 300]", "200"]).unwrap().as_str()
1809 /// );
1810 ///
1811 /// assert_eq!(
1812 /// "0x8dbd1b711dc621e1404633da156fcc779e1c6f3e68656c6c6f20776f726c64",
1813 /// Cast::abi_encode_packed("foo(address a, string b)", &["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "hello world"]).unwrap().as_str()
1814 /// );
1815 /// # Ok::<_, eyre::Report>(())
1816 /// ```
1817 pub fn abi_encode_packed(sig: &str, args: &[impl AsRef<str>]) -> Result<String> {
1818 // If the signature is a tuple, we need to prefix it to make it a function
1819 let sig =
1820 if sig.trim_start().starts_with('(') { format!("foo{sig}") } else { sig.to_string() };
1821
1822 let func = get_func(sig.as_str())?;
1823 let encoded = match encode_function_args_packed(&func, args) {
1824 Ok(res) => hex::encode(res),
1825 Err(e) => eyre::bail!("Could not ABI encode the function and arguments: {e}"),
1826 };
1827 Ok(format!("0x{encoded}"))
1828 }
1829
1830 /// Performs ABI encoding of an event to produce the topics and data.
1831 ///
1832 /// # Example
1833 ///
1834 /// ```
1835 /// use alloy_primitives::hex;
1836 /// use cast::SimpleCast as Cast;
1837 ///
1838 /// let log_data = Cast::abi_encode_event(
1839 /// "Transfer(address indexed from, address indexed to, uint256 value)",
1840 /// &[
1841 /// "0x1234567890123456789012345678901234567890",
1842 /// "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
1843 /// "1000",
1844 /// ],
1845 /// )
1846 /// .unwrap();
1847 ///
1848 /// // topic0 is the event selector
1849 /// assert_eq!(log_data.topics().len(), 3);
1850 /// assert_eq!(
1851 /// log_data.topics()[0].to_string(),
1852 /// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
1853 /// );
1854 /// assert_eq!(
1855 /// log_data.topics()[1].to_string(),
1856 /// "0x0000000000000000000000001234567890123456789012345678901234567890"
1857 /// );
1858 /// assert_eq!(
1859 /// log_data.topics()[2].to_string(),
1860 /// "0x000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd"
1861 /// );
1862 /// assert_eq!(
1863 /// hex::encode_prefixed(log_data.data),
1864 /// "0x00000000000000000000000000000000000000000000000000000000000003e8"
1865 /// );
1866 /// # Ok::<_, eyre::Report>(())
1867 /// ```
1868 pub fn abi_encode_event(sig: &str, args: &[impl AsRef<str>]) -> Result<LogData> {
1869 let event = get_event(sig)?;
1870 let tokens = std::iter::zip(&event.inputs, args)
1871 .map(|(input, arg)| coerce_value(&input.ty, arg.as_ref()))
1872 .collect::<Result<Vec<_>>>()?;
1873
1874 let mut topics = vec![event.selector()];
1875 let mut data_tokens: Vec<u8> = Vec::new();
1876
1877 for (input, token) in event.inputs.iter().zip(tokens.into_iter()) {
1878 if input.indexed {
1879 let ty = DynSolType::parse(&input.ty)?;
1880 if matches!(
1881 ty,
1882 DynSolType::String
1883 | DynSolType::Bytes
1884 | DynSolType::Array(_)
1885 | DynSolType::Tuple(_)
1886 ) {
1887 // For dynamic types, hash the encoded value
1888 let encoded = token.abi_encode();
1889 let hash = keccak256(encoded);
1890 topics.push(hash);
1891 } else {
1892 // For fixed-size types, encode directly to 32 bytes
1893 let mut encoded = [0u8; 32];
1894 let token_encoded = token.abi_encode();
1895 if token_encoded.len() <= 32 {
1896 let start = 32 - token_encoded.len();
1897 encoded[start..].copy_from_slice(&token_encoded);
1898 }
1899 topics.push(B256::from(encoded));
1900 }
1901 } else {
1902 // Non-indexed parameters go into data
1903 data_tokens.extend_from_slice(&token.abi_encode());
1904 }
1905 }
1906
1907 Ok(LogData::new_unchecked(topics, data_tokens.into()))
1908 }
1909
1910 /// Performs ABI encoding to produce the hexadecimal calldata with the given arguments.
1911 ///
1912 /// # Example
1913 ///
1914 /// ```
1915 /// use cast::SimpleCast as Cast;
1916 ///
1917 /// assert_eq!(
1918 /// "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001",
1919 /// Cast::calldata_encode("f(uint256 a)", &["1"]).unwrap().as_str()
1920 /// );
1921 /// # Ok::<_, eyre::Report>(())
1922 /// ```
1923 pub fn calldata_encode(sig: impl AsRef<str>, args: &[impl AsRef<str>]) -> Result<String> {
1924 let func = get_func(sig.as_ref())?;
1925 let calldata = encode_function_args(&func, args)?;
1926 Ok(hex::encode_prefixed(calldata))
1927 }
1928
1929 /// Returns the slot number for a given mapping key and slot.
1930 ///
1931 /// Given `mapping(k => v) m`, for a key `k` the slot number of its associated `v` is
1932 /// `keccak256(concat(h(k), p))`, where `h` is the padding function for `k`'s type, and `p`
1933 /// is slot number of the mapping `m`.
1934 ///
1935 /// See [the Solidity documentation](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays)
1936 /// for more details.
1937 ///
1938 /// # Example
1939 ///
1940 /// ```
1941 /// # use cast::SimpleCast as Cast;
1942 ///
1943 /// // Value types.
1944 /// assert_eq!(
1945 /// Cast::index("address", "0xD0074F4E6490ae3f888d1d4f7E3E43326bD3f0f5", "2").unwrap().as_str(),
1946 /// "0x9525a448a9000053a4d151336329d6563b7e80b24f8e628e95527f218e8ab5fb"
1947 /// );
1948 /// assert_eq!(
1949 /// Cast::index("uint256", "42", "6").unwrap().as_str(),
1950 /// "0xfc808b0f31a1e6b9cf25ff6289feae9b51017b392cc8e25620a94a38dcdafcc1"
1951 /// );
1952 ///
1953 /// // Strings and byte arrays.
1954 /// assert_eq!(
1955 /// Cast::index("string", "hello", "1").unwrap().as_str(),
1956 /// "0x8404bb4d805e9ca2bd5dd5c43a107e935c8ec393caa7851b353b3192cd5379ae"
1957 /// );
1958 /// # Ok::<_, eyre::Report>(())
1959 /// ```
1960 pub fn index(key_type: &str, key: &str, slot_number: &str) -> Result<String> {
1961 let mut hasher = Keccak256::new();
1962
1963 let k_ty = DynSolType::parse(key_type).wrap_err("Could not parse type")?;
1964 let k = k_ty.coerce_str(key).wrap_err("Could not parse value")?;
1965 match k_ty {
1966 // For value types, `h` pads the value to 32 bytes in the same way as when storing the
1967 // value in memory.
1968 DynSolType::Bool
1969 | DynSolType::Int(_)
1970 | DynSolType::Uint(_)
1971 | DynSolType::FixedBytes(_)
1972 | DynSolType::Address
1973 | DynSolType::Function => hasher.update(k.as_word().unwrap()),
1974
1975 // For strings and byte arrays, `h(k)` is just the unpadded data.
1976 DynSolType::String | DynSolType::Bytes => hasher.update(k.as_packed_seq().unwrap()),
1977
1978 DynSolType::Array(..)
1979 | DynSolType::FixedArray(..)
1980 | DynSolType::Tuple(..)
1981 | DynSolType::CustomStruct { .. } => {
1982 eyre::bail!("Type `{k_ty}` is not supported as a mapping key")
1983 }
1984 }
1985
1986 let p = DynSolType::Uint(256)
1987 .coerce_str(slot_number)
1988 .wrap_err("Could not parse slot number")?;
1989 let p = p.as_word().unwrap();
1990 hasher.update(p);
1991
1992 let location = hasher.finalize();
1993 Ok(location.to_string())
1994 }
1995
1996 /// Keccak-256 hashes arbitrary data
1997 ///
1998 /// # Example
1999 ///
2000 /// ```
2001 /// use cast::SimpleCast as Cast;
2002 ///
2003 /// assert_eq!(
2004 /// Cast::keccak("foo")?,
2005 /// "0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d"
2006 /// );
2007 /// assert_eq!(
2008 /// Cast::keccak("123abc")?,
2009 /// "0xb1f1c74a1ba56f07a892ea1110a39349d40f66ca01d245e704621033cb7046a4"
2010 /// );
2011 /// assert_eq!(
2012 /// Cast::keccak("0x12")?,
2013 /// "0x5fa2358263196dbbf23d1ca7a509451f7a2f64c15837bfbb81298b1e3e24e4fa"
2014 /// );
2015 /// assert_eq!(
2016 /// Cast::keccak("12")?,
2017 /// "0x7f8b6b088b6d74c2852fc86c796dca07b44eed6fb3daf5e6b59f7c364db14528"
2018 /// );
2019 /// # Ok::<_, eyre::Report>(())
2020 /// ```
2021 pub fn keccak(data: &str) -> Result<String> {
2022 // Hex-decode if data starts with 0x.
2023 let hash = if data.starts_with("0x") {
2024 keccak256(hex::decode(data.trim_end())?)
2025 } else {
2026 keccak256(data)
2027 };
2028 Ok(hash.to_string())
2029 }
2030
2031 /// Performs the left shift operation (<<) on a number
2032 ///
2033 /// # Example
2034 ///
2035 /// ```
2036 /// use cast::SimpleCast as Cast;
2037 ///
2038 /// assert_eq!(Cast::left_shift("16", "10", Some("10"), "hex")?, "0x4000");
2039 /// assert_eq!(Cast::left_shift("255", "16", Some("dec"), "hex")?, "0xff0000");
2040 /// assert_eq!(Cast::left_shift("0xff", "16", None, "hex")?, "0xff0000");
2041 /// # Ok::<_, eyre::Report>(())
2042 /// ```
2043 pub fn left_shift(
2044 value: &str,
2045 bits: &str,
2046 base_in: Option<&str>,
2047 base_out: &str,
2048 ) -> Result<String> {
2049 let base_out: Base = base_out.parse()?;
2050 let value = NumberWithBase::parse_uint(value, base_in)?;
2051 let bits = NumberWithBase::parse_uint(bits, None)?;
2052
2053 let res = value.number() << bits.number();
2054
2055 Ok(res.to_base(base_out, true)?)
2056 }
2057
2058 /// Performs the right shift operation (>>) on a number
2059 ///
2060 /// # Example
2061 ///
2062 /// ```
2063 /// use cast::SimpleCast as Cast;
2064 ///
2065 /// assert_eq!(Cast::right_shift("0x4000", "10", None, "dec")?, "16");
2066 /// assert_eq!(Cast::right_shift("16711680", "16", Some("10"), "hex")?, "0xff");
2067 /// assert_eq!(Cast::right_shift("0xff0000", "16", None, "hex")?, "0xff");
2068 /// # Ok::<(), eyre::Report>(())
2069 /// ```
2070 pub fn right_shift(
2071 value: &str,
2072 bits: &str,
2073 base_in: Option<&str>,
2074 base_out: &str,
2075 ) -> Result<String> {
2076 let base_out: Base = base_out.parse()?;
2077 let value = NumberWithBase::parse_uint(value, base_in)?;
2078 let bits = NumberWithBase::parse_uint(bits, None)?;
2079
2080 let res = value.number().wrapping_shr(bits.number().saturating_to());
2081
2082 Ok(res.to_base(base_out, true)?)
2083 }
2084
2085 /// Fetches source code of verified contracts from etherscan.
2086 ///
2087 /// # Example
2088 ///
2089 /// ```
2090 /// # use cast::SimpleCast as Cast;
2091 /// # use foundry_config::NamedChain;
2092 /// # async fn foo() -> eyre::Result<()> {
2093 /// assert_eq!(
2094 /// "/*
2095 /// - Bytecode Verification performed was compared on second iteration -
2096 /// This file is part of the DAO.....",
2097 /// Cast::etherscan_source(
2098 /// NamedChain::Mainnet.into(),
2099 /// "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(),
2100 /// Some("<etherscan_api_key>".to_string()),
2101 /// None,
2102 /// None
2103 /// )
2104 /// .await
2105 /// .unwrap()
2106 /// .as_str()
2107 /// );
2108 /// # Ok(())
2109 /// # }
2110 /// ```
2111 pub async fn etherscan_source(
2112 chain: Chain,
2113 contract_address: String,
2114 etherscan_api_key: Option<String>,
2115 explorer_api_url: Option<String>,
2116 explorer_url: Option<String>,
2117 ) -> Result<String> {
2118 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2119 let metadata = client.contract_source_code(contract_address.parse()?).await?;
2120 Ok(metadata.source_code())
2121 }
2122
2123 /// Fetches the source code of verified contracts from etherscan and expands the resulting
2124 /// files to a directory for easy perusal.
2125 ///
2126 /// # Example
2127 ///
2128 /// ```
2129 /// # use cast::SimpleCast as Cast;
2130 /// # use foundry_config::NamedChain;
2131 /// # use std::path::PathBuf;
2132 /// # async fn expand() -> eyre::Result<()> {
2133 /// Cast::expand_etherscan_source_to_directory(
2134 /// NamedChain::Mainnet.into(),
2135 /// "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(),
2136 /// Some("<etherscan_api_key>".to_string()),
2137 /// PathBuf::from("output_dir"),
2138 /// None,
2139 /// None,
2140 /// )
2141 /// .await?;
2142 /// # Ok(())
2143 /// # }
2144 /// ```
2145 pub async fn expand_etherscan_source_to_directory(
2146 chain: Chain,
2147 contract_address: String,
2148 etherscan_api_key: Option<String>,
2149 output_directory: PathBuf,
2150 explorer_api_url: Option<String>,
2151 explorer_url: Option<String>,
2152 ) -> eyre::Result<()> {
2153 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2154 let meta = client.contract_source_code(contract_address.parse()?).await?;
2155 let source_tree = meta.source_tree();
2156 source_tree.write_to(&output_directory)?;
2157 Ok(())
2158 }
2159
2160 /// Fetches the source code of verified contracts from etherscan, flattens it and writes it to
2161 /// the given path or stdout.
2162 pub async fn etherscan_source_flatten(
2163 chain: Chain,
2164 contract_address: String,
2165 etherscan_api_key: Option<String>,
2166 output_path: Option<PathBuf>,
2167 explorer_api_url: Option<String>,
2168 explorer_url: Option<String>,
2169 ) -> Result<()> {
2170 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2171 let metadata = client.contract_source_code(contract_address.parse()?).await?;
2172 let Some(metadata) = metadata.items.first() else {
2173 eyre::bail!("Empty contract source code")
2174 };
2175
2176 let tmp = tempfile::tempdir()?;
2177 let project = etherscan_project(metadata, tmp.path())?;
2178 let target_path = project.find_contract_path(&metadata.contract_name)?;
2179
2180 let flattened = flatten(project, &target_path)?;
2181
2182 if let Some(path) = output_path {
2183 fs::create_dir_all(path.parent().unwrap())?;
2184 fs::write(&path, flattened)?;
2185 sh_println!("Flattened file written at {}", path.display())?
2186 } else {
2187 sh_println!("{flattened}")?
2188 }
2189
2190 Ok(())
2191 }
2192
2193 /// Disassembles hex encoded bytecode into individual / human readable opcodes
2194 ///
2195 /// # Example
2196 ///
2197 /// ```
2198 /// use alloy_primitives::hex;
2199 /// use cast::SimpleCast as Cast;
2200 ///
2201 /// # async fn foo() -> eyre::Result<()> {
2202 /// let bytecode = "0x608060405260043610603f57600035";
2203 /// let opcodes = Cast::disassemble(&hex::decode(bytecode)?)?;
2204 /// println!("{}", opcodes);
2205 /// # Ok(())
2206 /// # }
2207 /// ```
2208 pub fn disassemble(code: &[u8]) -> Result<String> {
2209 let mut output = String::new();
2210 for (pc, inst) in InstIter::new(code).with_pc() {
2211 writeln!(output, "{pc:08x}: {inst}")?;
2212 }
2213 Ok(output)
2214 }
2215
2216 /// Gets the selector for a given function signature
2217 /// Optimizes if the `optimize` parameter is set to a number of leading zeroes
2218 ///
2219 /// # Example
2220 ///
2221 /// ```
2222 /// use cast::SimpleCast as Cast;
2223 ///
2224 /// assert_eq!(Cast::get_selector("foo(address,uint256)", 0)?.0, String::from("0xbd0d639f"));
2225 /// # Ok::<(), eyre::Error>(())
2226 /// ```
2227 pub fn get_selector(signature: &str, optimize: usize) -> Result<(String, String)> {
2228 if optimize > 4 {
2229 eyre::bail!("number of leading zeroes must not be greater than 4");
2230 }
2231 if optimize == 0 {
2232 let selector = get_func(signature)?.selector();
2233 return Ok((selector.to_string(), String::from(signature)));
2234 }
2235 let Some((name, params)) = signature.split_once('(') else {
2236 eyre::bail!("invalid function signature");
2237 };
2238
2239 let num_threads = rayon::current_num_threads();
2240 let found = AtomicBool::new(false);
2241
2242 let result: Option<(u32, String, String)> =
2243 (0..num_threads).into_par_iter().find_map_any(|i| {
2244 let nonce_start = i as u32;
2245 let nonce_step = num_threads as u32;
2246
2247 let mut nonce = nonce_start;
2248 while nonce < u32::MAX && !found.load(Ordering::Relaxed) {
2249 let input = format!("{name}{nonce}({params}");
2250 let hash = keccak256(input.as_bytes());
2251 let selector = &hash[..4];
2252
2253 if selector.iter().take_while(|&&byte| byte == 0).count() == optimize {
2254 found.store(true, Ordering::Relaxed);
2255 return Some((nonce, hex::encode_prefixed(selector), input));
2256 }
2257
2258 nonce += nonce_step;
2259 }
2260 None
2261 });
2262
2263 match result {
2264 Some((_nonce, selector, signature)) => Ok((selector, signature)),
2265 None => eyre::bail!("No selector found"),
2266 }
2267 }
2268
2269 /// Extracts function selectors, arguments and state mutability from bytecode
2270 ///
2271 /// # Example
2272 ///
2273 /// ```
2274 /// use alloy_primitives::fixed_bytes;
2275 /// use cast::SimpleCast as Cast;
2276 ///
2277 /// let bytecode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80632125b65b14602a575b5f80fd5b603a6035366004603c565b505050565b005b5f805f60608486031215604d575f80fd5b833563ffffffff81168114605f575f80fd5b925060208401356001600160a01b03811681146079575f80fd5b915060408401356001600160e01b03811681146093575f80fd5b80915050925092509256";
2278 /// let functions = Cast::extract_functions(bytecode)?;
2279 /// assert_eq!(functions, vec![(fixed_bytes!("0x2125b65b"), "uint32,address,uint224".to_string(), "pure")]);
2280 /// # Ok::<(), eyre::Report>(())
2281 /// ```
2282 pub fn extract_functions(bytecode: &str) -> Result<Vec<(Selector, String, &str)>> {
2283 let code = hex::decode(bytecode)?;
2284 let info = evmole::contract_info(
2285 evmole::ContractInfoArgs::new(&code)
2286 .with_selectors()
2287 .with_arguments()
2288 .with_state_mutability(),
2289 );
2290 Ok(info
2291 .functions
2292 .expect("functions extraction was requested")
2293 .into_iter()
2294 .map(|f| {
2295 (
2296 f.selector.into(),
2297 f.arguments
2298 .expect("arguments extraction was requested")
2299 .into_iter()
2300 .map(|t| t.sol_type_name().to_string())
2301 .collect::<Vec<String>>()
2302 .join(","),
2303 f.state_mutability
2304 .expect("state_mutability extraction was requested")
2305 .as_json_str(),
2306 )
2307 })
2308 .collect())
2309 }
2310
2311 /// Decodes a raw EIP2718 transaction payload
2312 /// Returns details about the typed transaction and ECSDA signature components
2313 ///
2314 /// # Example
2315 ///
2316 /// ```
2317 /// use cast::SimpleCast as Cast;
2318 ///
2319 /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8";
2320 /// let tx_envelope = Cast::decode_raw_transaction(&tx)?;
2321 /// # Ok::<(), eyre::Report>(())
2322 pub fn decode_raw_transaction(tx: &str) -> Result<FoundryTxEnvelope> {
2323 let tx_hex = hex::decode(tx)?;
2324 let tx = Decodable2718::decode_2718(&mut tx_hex.as_slice())?;
2325 Ok(tx)
2326 }
2327}
2328
2329pub(crate) fn strip_0x(s: &str) -> &str {
2330 s.strip_prefix("0x").unwrap_or(s)
2331}
2332
2333fn explorer_client(
2334 chain: Chain,
2335 api_key: Option<String>,
2336 api_url: Option<String>,
2337 explorer_url: Option<String>,
2338) -> Result<Client> {
2339 let mut builder = Client::builder().chain(chain)?;
2340
2341 let deduced = chain.etherscan_urls();
2342
2343 let explorer_url = explorer_url
2344 .or(deduced.map(|d| d.1.to_string()))
2345 .ok_or_eyre("Please provide the explorer browser URL using `--explorer-url`")?;
2346 builder = builder.with_url(explorer_url)?;
2347
2348 let api_url = api_url
2349 .or(deduced.map(|d| d.0.to_string()))
2350 .ok_or_eyre("Please provide the explorer API URL using `--explorer-api-url`")?;
2351 builder = builder.with_api_url(api_url)?;
2352
2353 if let Some(api_key) = api_key {
2354 builder = builder.with_api_key(api_key);
2355 }
2356
2357 builder.build().map_err(Into::into)
2358}
2359
2360#[cfg(test)]
2361mod tests {
2362 use super::{DynSolValue, SimpleCast as Cast, serialize_value_as_json};
2363 use alloy_primitives::hex;
2364
2365 #[test]
2366 fn simple_selector() {
2367 assert_eq!("0xc2985578", Cast::get_selector("foo()", 0).unwrap().0.as_str())
2368 }
2369
2370 #[test]
2371 fn selector_with_arg() {
2372 assert_eq!("0xbd0d639f", Cast::get_selector("foo(address,uint256)", 0).unwrap().0.as_str())
2373 }
2374
2375 #[test]
2376 fn calldata_uint() {
2377 assert_eq!(
2378 "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001",
2379 Cast::calldata_encode("f(uint256 a)", &["1"]).unwrap().as_str()
2380 );
2381 }
2382
2383 // <https://github.com/foundry-rs/foundry/issues/2681>
2384 #[test]
2385 fn calldata_array() {
2386 assert_eq!(
2387 "0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
2388 Cast::calldata_encode("propose(string[])", &["[\"\"]"]).unwrap().as_str()
2389 );
2390 }
2391
2392 #[test]
2393 fn calldata_bool() {
2394 assert_eq!(
2395 "0x6fae94120000000000000000000000000000000000000000000000000000000000000000",
2396 Cast::calldata_encode("bar(bool)", &["false"]).unwrap().as_str()
2397 );
2398 }
2399
2400 #[test]
2401 fn abi_decode() {
2402 let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
2403 let sig = "balanceOf(address, uint256)(uint256)";
2404 assert_eq!(
2405 "1",
2406 Cast::abi_decode(sig, data, false).unwrap()[0].as_uint().unwrap().0.to_string()
2407 );
2408
2409 let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
2410 let sig = "safeTransferFrom(address,address,uint256,uint256,bytes)";
2411 let decoded = Cast::abi_decode(sig, data, true).unwrap();
2412 let decoded = [
2413 decoded[0]
2414 .as_address()
2415 .unwrap()
2416 .to_string()
2417 .strip_prefix("0x")
2418 .unwrap()
2419 .to_owned()
2420 .to_lowercase(),
2421 decoded[1]
2422 .as_address()
2423 .unwrap()
2424 .to_string()
2425 .strip_prefix("0x")
2426 .unwrap()
2427 .to_owned()
2428 .to_lowercase(),
2429 decoded[2].as_uint().unwrap().0.to_string(),
2430 decoded[3].as_uint().unwrap().0.to_string(),
2431 hex::encode(decoded[4].as_bytes().unwrap()),
2432 ]
2433 .to_vec();
2434 assert_eq!(
2435 decoded,
2436 vec![
2437 "8dbd1b711dc621e1404633da156fcc779e1c6f3e",
2438 "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc",
2439 "42",
2440 "1",
2441 ""
2442 ]
2443 );
2444 }
2445
2446 #[test]
2447 fn calldata_decode() {
2448 let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
2449 let sig = "balanceOf(address, uint256)(uint256)";
2450 let decoded =
2451 Cast::calldata_decode(sig, data, false).unwrap()[0].as_uint().unwrap().0.to_string();
2452 assert_eq!(decoded, "1");
2453
2454 // Passing `input = true` will decode the data with the input function signature.
2455 // We exclude the "prefixed" function selector from the data field (the first 4 bytes).
2456 let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
2457 let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
2458 let decoded = Cast::calldata_decode(sig, data, true).unwrap();
2459 let decoded = [
2460 decoded[0].as_address().unwrap().to_string().to_lowercase(),
2461 decoded[1].as_address().unwrap().to_string().to_lowercase(),
2462 decoded[2].as_uint().unwrap().0.to_string(),
2463 decoded[3].as_uint().unwrap().0.to_string(),
2464 hex::encode(decoded[4].as_bytes().unwrap()),
2465 ]
2466 .into_iter()
2467 .collect::<Vec<_>>();
2468 assert_eq!(
2469 decoded,
2470 vec![
2471 "0x8dbd1b711dc621e1404633da156fcc779e1c6f3e",
2472 "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc",
2473 "42",
2474 "1",
2475 ""
2476 ]
2477 );
2478 }
2479
2480 #[test]
2481 fn calldata_decode_nested_json() {
2482 let calldata = "0xdb5b0ed700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006772bf190000000000000000000000000000000000000000000000000000000000020716000000000000000000000000af9d27ffe4d51ed54ac8eec78f2785d7e11e5ab100000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000404366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb73188600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5";
2483 let sig = "sequenceBatchesValidium((bytes32,bytes32,uint64,bytes32)[],uint64,uint64,address,bytes)";
2484 let decoded = Cast::calldata_decode(sig, calldata, true).unwrap();
2485 let json_value = serialize_value_as_json(DynSolValue::Array(decoded), None).unwrap();
2486 let expected = serde_json::json!([
2487 [
2488 [
2489 "0x04366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27",
2490 "0x0000000000000000000000000000000000000000000000000000000000000000",
2491 0,
2492 "0x0000000000000000000000000000000000000000000000000000000000000000"
2493 ],
2494 [
2495 "0x093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed74",
2496 "0x0000000000000000000000000000000000000000000000000000000000000000",
2497 0,
2498 "0x0000000000000000000000000000000000000000000000000000000000000000"
2499 ],
2500 [
2501 "0x60f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf7",
2502 "0x0000000000000000000000000000000000000000000000000000000000000000",
2503 0,
2504 "0x0000000000000000000000000000000000000000000000000000000000000000"
2505 ],
2506 [
2507 "0x93dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb731886",
2508 "0x0000000000000000000000000000000000000000000000000000000000000000",
2509 0,
2510 "0x0000000000000000000000000000000000000000000000000000000000000000"
2511 ]
2512 ],
2513 1735573273,
2514 132886,
2515 "0xAF9d27ffe4d51eD54AC8eEc78f2785D7E11E5ab1",
2516 "0x334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5"
2517 ]);
2518 assert_eq!(json_value, expected);
2519 }
2520
2521 #[test]
2522 fn concat_hex() {
2523 assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001");
2524 assert_eq!(Cast::concat_hex(["1", "2"]), "0x12");
2525 }
2526
2527 #[test]
2528 fn from_rlp() {
2529 let rlp = "0xf8b1a02b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca8080a02838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f90380a0e46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd8754980a01d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5808080a0236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff08080808080";
2530 let item = Cast::from_rlp(rlp, false).unwrap();
2531 assert_eq!(
2532 item,
2533 r#"["0x2b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca","0x","0x","0x2838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f903","0x","0xe46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd87549","0x","0x1d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5","0x","0x","0x","0x236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff0","0x","0x","0x","0x","0x"]"#
2534 )
2535 }
2536
2537 #[test]
2538 fn disassemble_incomplete_sequence() {
2539 let incomplete = &hex!("60"); // PUSH1
2540 let disassembled = Cast::disassemble(incomplete).unwrap();
2541 assert_eq!(disassembled, "00000000: PUSH1\n");
2542
2543 let complete = &hex!("6000"); // PUSH1 0x00
2544 let disassembled = Cast::disassemble(complete).unwrap();
2545 assert_eq!(disassembled, "00000000: PUSH1 0x00\n");
2546
2547 let incomplete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 31 bytes
2548 let disassembled = Cast::disassemble(incomplete).unwrap();
2549 assert_eq!(disassembled, "00000000: PUSH32\n");
2550
2551 let complete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 32 bytes
2552 let disassembled = Cast::disassemble(complete).unwrap();
2553 assert_eq!(
2554 disassembled,
2555 "00000000: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n"
2556 );
2557 }
2558}