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