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