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