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