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