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 tempo::classify_payment_lane,
42};
43use foundry_config::Chain;
44use foundry_evm::core::bytecode::InstIter;
45use foundry_primitives::FoundryTxEnvelope;
46use futures::{FutureExt, StreamExt, TryStreamExt, future::Either};
47#[cfg(feature = "optimism")]
48use op_alloy_consensus as _;
49
50use rayon::prelude::*;
51use serde::Serialize;
52use std::{
53 borrow::Cow,
54 fmt::Write,
55 io,
56 marker::PhantomData,
57 path::PathBuf,
58 str::FromStr,
59 sync::atomic::{AtomicBool, Ordering},
60};
61use tokio::signal::ctrl_c;
62
63pub use foundry_evm::*;
64
65pub mod args;
66pub mod cmd;
67pub mod opts;
68pub mod tempo;
69
70pub mod base;
71pub mod call_spec;
72pub(crate) mod debug;
73pub mod errors;
74mod rlp_converter;
75pub mod rpc_trace;
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, true))
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 FoundryTxEnvelope::decode_2718(&mut encoded.as_slice())
1224 .wrap_err("failed to decode transaction for lane classification")?;
1225 crate::args::format_lane_classification(&classify_payment_lane(&encoded))?
1226 } else if let Some(ref field) = field {
1227 if let Some(value) = get_pretty_tx_attr::<N>(&tx, field.as_str()) {
1228 value
1229 } else {
1230 let tx_json = serde_json::to_value(&tx)?;
1231 let value = tx_json
1232 .get(field)
1233 .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.clone()))?;
1234
1235 match value {
1236 serde_json::Value::String(value) => value.clone(),
1237 value => value.to_string(),
1238 }
1239 }
1240 } else if shell::is_json() {
1241 // to_value first to sort json object keys
1242 serde_json::to_value(&tx)?.to_string()
1243 } else if to_request {
1244 serde_json::to_string_pretty(&Into::<N::TransactionRequest>::into(tx))?
1245 } else {
1246 tx.pretty()
1247 })
1248 }
1249}
1250
1251pub struct SimpleCast;
1252
1253impl SimpleCast {
1254 /// Returns the maximum value of the given integer type
1255 ///
1256 /// # Example
1257 ///
1258 /// ```
1259 /// use alloy_primitives::{I256, U256};
1260 /// use cast::SimpleCast;
1261 ///
1262 /// assert_eq!(SimpleCast::max_int("uint256")?, U256::MAX.to_string());
1263 /// assert_eq!(SimpleCast::max_int("int256")?, I256::MAX.to_string());
1264 /// assert_eq!(SimpleCast::max_int("int32")?, i32::MAX.to_string());
1265 /// # Ok::<(), eyre::Report>(())
1266 /// ```
1267 pub fn max_int(s: &str) -> Result<String> {
1268 Self::max_min_int::<true>(s)
1269 }
1270
1271 /// Returns the maximum value of the given integer type
1272 ///
1273 /// # Example
1274 ///
1275 /// ```
1276 /// use alloy_primitives::{I256, U256};
1277 /// use cast::SimpleCast;
1278 ///
1279 /// assert_eq!(SimpleCast::min_int("uint256")?, "0");
1280 /// assert_eq!(SimpleCast::min_int("int256")?, I256::MIN.to_string());
1281 /// assert_eq!(SimpleCast::min_int("int32")?, i32::MIN.to_string());
1282 /// # Ok::<(), eyre::Report>(())
1283 /// ```
1284 pub fn min_int(s: &str) -> Result<String> {
1285 Self::max_min_int::<false>(s)
1286 }
1287
1288 fn max_min_int<const MAX: bool>(s: &str) -> Result<String> {
1289 let ty = DynSolType::parse(s).wrap_err("Invalid type, expected `(u)int<bit size>`")?;
1290 match ty {
1291 DynSolType::Int(n) => {
1292 let mask = U256::from(1).wrapping_shl(n - 1);
1293 let max = (U256::MAX & mask).saturating_sub(U256::from(1));
1294 if MAX {
1295 Ok(max.to_string())
1296 } else {
1297 let min = I256::from_raw(max).wrapping_neg() + I256::MINUS_ONE;
1298 Ok(min.to_string())
1299 }
1300 }
1301 DynSolType::Uint(n) => {
1302 if MAX {
1303 let mut max = U256::MAX;
1304 if n < 256 {
1305 max &= U256::from(1).wrapping_shl(n).wrapping_sub(U256::from(1));
1306 }
1307 Ok(max.to_string())
1308 } else {
1309 Ok("0".to_string())
1310 }
1311 }
1312 _ => Err(eyre::eyre!("Type is not int/uint: {s}")),
1313 }
1314 }
1315
1316 /// Converts UTF-8 text input to hex
1317 ///
1318 /// # Example
1319 ///
1320 /// ```
1321 /// use cast::SimpleCast as Cast;
1322 ///
1323 /// assert_eq!(Cast::from_utf8("yo"), "0x796f");
1324 /// assert_eq!(Cast::from_utf8("Hello, World!"), "0x48656c6c6f2c20576f726c6421");
1325 /// assert_eq!(Cast::from_utf8("TurboDappTools"), "0x547572626f44617070546f6f6c73");
1326 /// # Ok::<_, eyre::Report>(())
1327 /// ```
1328 pub fn from_utf8(s: &str) -> String {
1329 hex::encode_prefixed(s)
1330 }
1331
1332 /// Converts hex input to UTF-8 text
1333 ///
1334 /// # Example
1335 ///
1336 /// ```
1337 /// use cast::SimpleCast as Cast;
1338 ///
1339 /// assert_eq!(Cast::to_utf8("0x796f")?, "yo");
1340 /// assert_eq!(Cast::to_utf8("0x48656c6c6f2c20576f726c6421")?, "Hello, World!");
1341 /// assert_eq!(Cast::to_utf8("0x547572626f44617070546f6f6c73")?, "TurboDappTools");
1342 /// assert_eq!(Cast::to_utf8("0xe4bda0e5a5bd")?, "ä½ å¥½");
1343 /// # Ok::<_, eyre::Report>(())
1344 /// ```
1345 pub fn to_utf8(s: &str) -> Result<String> {
1346 let bytes = hex::decode(s)?;
1347 Ok(String::from_utf8_lossy(bytes.as_ref()).to_string())
1348 }
1349
1350 /// Converts hex data into text data
1351 ///
1352 /// # Example
1353 ///
1354 /// ```
1355 /// use cast::SimpleCast as Cast;
1356 ///
1357 /// assert_eq!(Cast::to_ascii("0x796f")?, "yo");
1358 /// assert_eq!(Cast::to_ascii("48656c6c6f2c20576f726c6421")?, "Hello, World!");
1359 /// assert_eq!(Cast::to_ascii("0x547572626f44617070546f6f6c73")?, "TurboDappTools");
1360 /// # Ok::<_, eyre::Report>(())
1361 /// ```
1362 pub fn to_ascii(hex: &str) -> Result<String> {
1363 let bytes = hex::decode(hex)?;
1364 if !bytes.iter().all(u8::is_ascii) {
1365 return Err(eyre::eyre!("Invalid ASCII bytes"));
1366 }
1367 Ok(String::from_utf8(bytes).unwrap())
1368 }
1369
1370 /// Converts fixed point number into specified number of decimals
1371 /// ```
1372 /// use alloy_primitives::U256;
1373 /// use cast::SimpleCast as Cast;
1374 ///
1375 /// assert_eq!(Cast::from_fixed_point("10", "0")?, "10");
1376 /// assert_eq!(Cast::from_fixed_point("1.0", "1")?, "10");
1377 /// assert_eq!(Cast::from_fixed_point("0.10", "2")?, "10");
1378 /// assert_eq!(Cast::from_fixed_point("0.010", "3")?, "10");
1379 /// # Ok::<_, eyre::Report>(())
1380 /// ```
1381 pub fn from_fixed_point(value: &str, decimals: &str) -> Result<String> {
1382 let units: Unit = Unit::from_str(decimals)?;
1383 let n = ParseUnits::parse_units(value, units)?;
1384 Ok(n.to_string())
1385 }
1386
1387 /// Converts integers with specified decimals into fixed point numbers
1388 ///
1389 /// # Example
1390 ///
1391 /// ```
1392 /// use alloy_primitives::U256;
1393 /// use cast::SimpleCast as Cast;
1394 ///
1395 /// assert_eq!(Cast::to_fixed_point("10", "0")?, "10.");
1396 /// assert_eq!(Cast::to_fixed_point("10", "1")?, "1.0");
1397 /// assert_eq!(Cast::to_fixed_point("10", "2")?, "0.10");
1398 /// assert_eq!(Cast::to_fixed_point("10", "3")?, "0.010");
1399 ///
1400 /// assert_eq!(Cast::to_fixed_point("-10", "0")?, "-10.");
1401 /// assert_eq!(Cast::to_fixed_point("-10", "1")?, "-1.0");
1402 /// assert_eq!(Cast::to_fixed_point("-10", "2")?, "-0.10");
1403 /// assert_eq!(Cast::to_fixed_point("-10", "3")?, "-0.010");
1404 /// # Ok::<_, eyre::Report>(())
1405 /// ```
1406 pub fn to_fixed_point(value: &str, decimals: &str) -> Result<String> {
1407 let (sign, mut value, value_len) = {
1408 let number = NumberWithBase::parse_int(value, None)?;
1409 let sign = if number.is_nonnegative() { "" } else { "-" };
1410 let value = format!("{number:#}");
1411 let value_stripped = value.strip_prefix('-').unwrap_or(&value).to_string();
1412 let value_len = value_stripped.len();
1413 (sign, value_stripped, value_len)
1414 };
1415 let decimals = NumberWithBase::parse_uint(decimals, None)?.number().to::<usize>();
1416
1417 let value = if decimals >= value_len {
1418 // Add "0." and pad with 0s
1419 format!("0.{value:0>decimals$}")
1420 } else {
1421 // Insert decimal at -idx (i.e 1 => decimal idx = -1)
1422 value.insert(value_len - decimals, '.');
1423 value
1424 };
1425
1426 Ok(format!("{sign}{value}"))
1427 }
1428
1429 /// Concatencates hex strings
1430 ///
1431 /// # Example
1432 ///
1433 /// ```
1434 /// use cast::SimpleCast as Cast;
1435 ///
1436 /// assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001");
1437 /// assert_eq!(Cast::concat_hex(["1", "2"]), "0x12");
1438 /// # Ok::<_, eyre::Report>(())
1439 /// ```
1440 pub fn concat_hex<T: AsRef<str>>(values: impl IntoIterator<Item = T>) -> String {
1441 let mut out = String::new();
1442 for s in values {
1443 let s = s.as_ref();
1444 out.push_str(strip_0x(s))
1445 }
1446 format!("0x{out}")
1447 }
1448
1449 /// Converts a number into uint256 hex string with 0x prefix
1450 ///
1451 /// # Example
1452 ///
1453 /// ```
1454 /// use cast::SimpleCast as Cast;
1455 ///
1456 /// assert_eq!(
1457 /// Cast::to_uint256("100")?,
1458 /// "0x0000000000000000000000000000000000000000000000000000000000000064"
1459 /// );
1460 /// assert_eq!(
1461 /// Cast::to_uint256("192038293923")?,
1462 /// "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"
1463 /// );
1464 /// assert_eq!(
1465 /// Cast::to_uint256(
1466 /// "115792089237316195423570985008687907853269984665640564039457584007913129639935"
1467 /// )?,
1468 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1469 /// );
1470 /// # Ok::<_, eyre::Report>(())
1471 /// ```
1472 pub fn to_uint256(value: &str) -> Result<String> {
1473 let n = NumberWithBase::parse_uint(value, None)?;
1474 Ok(format!("{n:#066x}"))
1475 }
1476
1477 /// Converts a number into int256 hex string with 0x prefix
1478 ///
1479 /// # Example
1480 ///
1481 /// ```
1482 /// use cast::SimpleCast as Cast;
1483 ///
1484 /// assert_eq!(
1485 /// Cast::to_int256("0")?,
1486 /// "0x0000000000000000000000000000000000000000000000000000000000000000"
1487 /// );
1488 /// assert_eq!(
1489 /// Cast::to_int256("100")?,
1490 /// "0x0000000000000000000000000000000000000000000000000000000000000064"
1491 /// );
1492 /// assert_eq!(
1493 /// Cast::to_int256("-100")?,
1494 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c"
1495 /// );
1496 /// assert_eq!(
1497 /// Cast::to_int256("192038293923")?,
1498 /// "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"
1499 /// );
1500 /// assert_eq!(
1501 /// Cast::to_int256("-192038293923")?,
1502 /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffd349a02e5d"
1503 /// );
1504 /// assert_eq!(
1505 /// Cast::to_int256(
1506 /// "57896044618658097711785492504343953926634992332820282019728792003956564819967"
1507 /// )?,
1508 /// "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1509 /// );
1510 /// assert_eq!(
1511 /// Cast::to_int256(
1512 /// "-57896044618658097711785492504343953926634992332820282019728792003956564819968"
1513 /// )?,
1514 /// "0x8000000000000000000000000000000000000000000000000000000000000000"
1515 /// );
1516 /// # Ok::<_, eyre::Report>(())
1517 /// ```
1518 pub fn to_int256(value: &str) -> Result<String> {
1519 let n = NumberWithBase::parse_int(value, None)?;
1520 Ok(format!("{n:#066x}"))
1521 }
1522
1523 /// Converts an eth amount into a specified unit
1524 ///
1525 /// # Example
1526 ///
1527 /// ```
1528 /// use cast::SimpleCast as Cast;
1529 ///
1530 /// assert_eq!(Cast::to_unit("1 wei", "wei")?, "1");
1531 /// assert_eq!(Cast::to_unit("1", "wei")?, "1");
1532 /// assert_eq!(Cast::to_unit("1ether", "wei")?, "1000000000000000000");
1533 /// # Ok::<_, eyre::Report>(())
1534 /// ```
1535 pub fn to_unit(value: &str, unit: &str) -> Result<String> {
1536 let value = DynSolType::coerce_str(&DynSolType::Uint(256), value)?
1537 .as_uint()
1538 .wrap_err("Could not convert to uint")?
1539 .0;
1540 let unit = unit.parse().wrap_err("could not parse units")?;
1541 Ok(Self::format_unit_as_string(value, unit))
1542 }
1543
1544 /// Convert a number into a uint with arbitrary decimals.
1545 ///
1546 /// # Example
1547 ///
1548 /// ```
1549 /// use cast::SimpleCast as Cast;
1550 ///
1551 /// # fn main() -> eyre::Result<()> {
1552 /// assert_eq!(Cast::parse_units("1.0", 6)?, "1000000"); // USDC (6 decimals)
1553 /// assert_eq!(Cast::parse_units("2.5", 6)?, "2500000");
1554 /// assert_eq!(Cast::parse_units("1.0", 12)?, "1000000000000"); // 12 decimals
1555 /// assert_eq!(Cast::parse_units("1.23", 3)?, "1230"); // 3 decimals
1556 ///
1557 /// # Ok(())
1558 /// # }
1559 /// ```
1560 pub fn parse_units(value: &str, unit: u8) -> Result<String> {
1561 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
1562
1563 Ok(ParseUnits::parse_units(value, unit)?.to_string())
1564 }
1565
1566 /// Format a number from smallest unit to decimal with arbitrary decimals.
1567 ///
1568 /// # Example
1569 ///
1570 /// ```
1571 /// use cast::SimpleCast as Cast;
1572 ///
1573 /// # fn main() -> eyre::Result<()> {
1574 /// assert_eq!(Cast::format_units("1000000", 6)?, "1"); // USDC (6 decimals)
1575 /// assert_eq!(Cast::format_units("2500000", 6)?, "2.500000");
1576 /// assert_eq!(Cast::format_units("1000000000000", 12)?, "1"); // 12 decimals
1577 /// assert_eq!(Cast::format_units("1230", 3)?, "1.230"); // 3 decimals
1578 ///
1579 /// # Ok(())
1580 /// # }
1581 /// ```
1582 pub fn format_units(value: &str, unit: u8) -> Result<String> {
1583 let value = NumberWithBase::parse_int(value, None)?.number();
1584 let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
1585 Ok(Self::format_unit_as_string(value, unit))
1586 }
1587
1588 // Helper function to format units as a string
1589 fn format_unit_as_string(value: U256, unit: Unit) -> String {
1590 let mut formatted = ParseUnits::U256(value).format_units(unit);
1591 // Trim empty fractional part.
1592 if let Some(dot) = formatted.find('.') {
1593 let fractional = &formatted[dot + 1..];
1594 if fractional.chars().all(|c: char| c == '0') {
1595 formatted = formatted[..dot].to_string();
1596 }
1597 }
1598 formatted
1599 }
1600
1601 /// Converts wei into an eth amount
1602 ///
1603 /// # Example
1604 ///
1605 /// ```
1606 /// use cast::SimpleCast as Cast;
1607 ///
1608 /// assert_eq!(Cast::from_wei("1", "gwei")?, "0.000000001");
1609 /// assert_eq!(Cast::from_wei("12340000005", "gwei")?, "12.340000005");
1610 /// assert_eq!(Cast::from_wei("10", "ether")?, "0.000000000000000010");
1611 /// assert_eq!(Cast::from_wei("100", "eth")?, "0.000000000000000100");
1612 /// assert_eq!(Cast::from_wei("17", "ether")?, "0.000000000000000017");
1613 /// # Ok::<_, eyre::Report>(())
1614 /// ```
1615 pub fn from_wei(value: &str, unit: &str) -> Result<String> {
1616 let value = NumberWithBase::parse_int(value, None)?.number();
1617 Ok(ParseUnits::U256(value).format_units(unit.parse()?))
1618 }
1619
1620 /// Converts an eth amount into wei
1621 ///
1622 /// # Example
1623 ///
1624 /// ```
1625 /// use cast::SimpleCast as Cast;
1626 ///
1627 /// assert_eq!(Cast::to_wei("100", "gwei")?, "100000000000");
1628 /// assert_eq!(Cast::to_wei("100", "eth")?, "100000000000000000000");
1629 /// assert_eq!(Cast::to_wei("1000", "ether")?, "1000000000000000000000");
1630 /// # Ok::<_, eyre::Report>(())
1631 /// ```
1632 pub fn to_wei(value: &str, unit: &str) -> Result<String> {
1633 let unit = unit.parse().wrap_err("could not parse units")?;
1634 Ok(ParseUnits::parse_units(value, unit)?.to_string())
1635 }
1636
1637 // Decodes RLP encoded data with validation for canonical integer representation
1638 ///
1639 /// # Examples
1640 /// ```
1641 /// use cast::SimpleCast as Cast;
1642 ///
1643 /// assert_eq!(Cast::from_rlp("0xc0", false).unwrap(), "[]");
1644 /// assert_eq!(Cast::from_rlp("0x0f", false).unwrap(), "\"0x0f\"");
1645 /// assert_eq!(Cast::from_rlp("0x33", false).unwrap(), "\"0x33\"");
1646 /// assert_eq!(Cast::from_rlp("0xc161", false).unwrap(), "[\"0x61\"]");
1647 /// assert_eq!(Cast::from_rlp("820002", true).is_err(), true);
1648 /// assert_eq!(Cast::from_rlp("820002", false).unwrap(), "\"0x0002\"");
1649 /// assert_eq!(Cast::from_rlp("00", true).is_err(), true);
1650 /// assert_eq!(Cast::from_rlp("00", false).unwrap(), "\"0x00\"");
1651 /// # Ok::<_, eyre::Report>(())
1652 /// ```
1653 pub fn from_rlp(value: impl AsRef<str>, as_int: bool) -> Result<String> {
1654 let bytes = hex::decode(value.as_ref()).wrap_err("Could not decode hex")?;
1655
1656 if as_int {
1657 return Ok(U256::decode(&mut &bytes[..])?.to_string());
1658 }
1659
1660 let item = Item::decode(&mut &bytes[..]).wrap_err("Could not decode rlp")?;
1661
1662 Ok(item.to_string())
1663 }
1664
1665 /// Encodes hex data or list of hex data to hexadecimal rlp
1666 ///
1667 /// # Example
1668 ///
1669 /// ```
1670 /// use cast::SimpleCast as Cast;
1671 ///
1672 /// assert_eq!(Cast::to_rlp("[]").unwrap(), "0xc0".to_string());
1673 /// assert_eq!(Cast::to_rlp("0x22").unwrap(), "0x22".to_string());
1674 /// assert_eq!(Cast::to_rlp("[\"0x61\"]",).unwrap(), "0xc161".to_string());
1675 /// assert_eq!(Cast::to_rlp("[\"0xf1\", \"f2\"]").unwrap(), "0xc481f181f2".to_string());
1676 /// # Ok::<_, eyre::Report>(())
1677 /// ```
1678 pub fn to_rlp(value: &str) -> Result<String> {
1679 let val = serde_json::from_str(value)
1680 .unwrap_or_else(|_| serde_json::Value::String(value.to_string()));
1681 let item = Item::value_to_item(&val)?;
1682 Ok(format!("0x{}", hex::encode(alloy_rlp::encode(item))))
1683 }
1684
1685 /// Converts a number of one base to another
1686 ///
1687 /// # Example
1688 ///
1689 /// ```
1690 /// use alloy_primitives::I256;
1691 /// use cast::SimpleCast as Cast;
1692 ///
1693 /// assert_eq!(Cast::to_base("100", Some("10"), "16")?, "0x64");
1694 /// assert_eq!(Cast::to_base("100", Some("10"), "oct")?, "0o144");
1695 /// assert_eq!(Cast::to_base("100", Some("10"), "binary")?, "0b1100100");
1696 ///
1697 /// assert_eq!(Cast::to_base("0xffffffffffffffff", None, "10")?, u64::MAX.to_string());
1698 /// assert_eq!(
1699 /// Cast::to_base("0xffffffffffffffffffffffffffffffff", None, "dec")?,
1700 /// u128::MAX.to_string()
1701 /// );
1702 /// // U256::MAX overflows as internally it is being parsed as I256
1703 /// assert_eq!(
1704 /// Cast::to_base(
1705 /// "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1706 /// None,
1707 /// "decimal"
1708 /// )?,
1709 /// I256::MAX.to_string()
1710 /// );
1711 /// # Ok::<_, eyre::Report>(())
1712 /// ```
1713 pub fn to_base(value: &str, base_in: Option<&str>, base_out: &str) -> Result<String> {
1714 let base_in = Base::unwrap_or_detect(base_in, value)?;
1715 let base_out: Base = base_out.parse()?;
1716 if base_in == base_out {
1717 return Ok(value.to_string());
1718 }
1719
1720 let mut n = NumberWithBase::parse_int(value, Some(&base_in.to_string()))?;
1721 n.set_base(base_out);
1722
1723 // Use Debug fmt
1724 Ok(format!("{n:#?}"))
1725 }
1726
1727 /// Converts hexdata into bytes32 value
1728 ///
1729 /// # Example
1730 ///
1731 /// ```
1732 /// use cast::SimpleCast as Cast;
1733 ///
1734 /// let bytes = Cast::to_bytes32("1234")?;
1735 /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000");
1736 ///
1737 /// let bytes = Cast::to_bytes32("0x1234")?;
1738 /// assert_eq!(bytes, "0x1234000000000000000000000000000000000000000000000000000000000000");
1739 ///
1740 /// let err = Cast::to_bytes32("0x123400000000000000000000000000000000000000000000000000000000000011").unwrap_err();
1741 /// assert_eq!(err.to_string(), "string >32 bytes");
1742 /// # Ok::<_, eyre::Report>(())
1743 pub fn to_bytes32(s: &str) -> Result<String> {
1744 let s = strip_0x(s);
1745 if s.len() > 64 {
1746 eyre::bail!("string >32 bytes");
1747 }
1748
1749 let padded = format!("{s:0<64}");
1750 Ok(padded.parse::<B256>()?.to_string())
1751 }
1752
1753 /// Encodes string into bytes32 value
1754 pub fn format_bytes32_string(s: &str) -> Result<String> {
1755 let str_bytes: &[u8] = s.as_bytes();
1756 eyre::ensure!(str_bytes.len() <= 32, "bytes32 strings must not exceed 32 bytes in length");
1757
1758 let mut bytes32: [u8; 32] = [0u8; 32];
1759 bytes32[..str_bytes.len()].copy_from_slice(str_bytes);
1760 Ok(hex::encode_prefixed(bytes32))
1761 }
1762
1763 /// Pads hex data to a specified length
1764 ///
1765 /// # Example
1766 ///
1767 /// ```
1768 /// use cast::SimpleCast as Cast;
1769 ///
1770 /// let padded = Cast::pad("abcd", true, 20)?;
1771 /// assert_eq!(padded, "0xabcd000000000000000000000000000000000000");
1772 ///
1773 /// let padded = Cast::pad("abcd", false, 20)?;
1774 /// assert_eq!(padded, "0x000000000000000000000000000000000000abcd");
1775 ///
1776 /// let padded = Cast::pad("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", true, 32)?;
1777 /// assert_eq!(padded, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2000000000000000000000000");
1778 ///
1779 /// let padded = Cast::pad("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", false, 32)?;
1780 /// assert_eq!(padded, "0x000000000000000000000000C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
1781 ///
1782 /// let err = Cast::pad("1234", false, 1).unwrap_err();
1783 /// assert_eq!(err.to_string(), "input length exceeds target length");
1784 ///
1785 /// let err = Cast::pad("foobar", false, 32).unwrap_err();
1786 /// assert_eq!(err.to_string(), "input is not a valid hex");
1787 ///
1788 /// # Ok::<_, eyre::Report>(())
1789 /// ```
1790 pub fn pad(s: &str, right: bool, len: usize) -> Result<String> {
1791 let s = strip_0x(s);
1792 let hex_len = len * 2;
1793
1794 // Validate input
1795 if s.len() > hex_len {
1796 eyre::bail!("input length exceeds target length");
1797 }
1798 if !s.chars().all(|c| c.is_ascii_hexdigit()) {
1799 eyre::bail!("input is not a valid hex");
1800 }
1801
1802 Ok(if right { format!("0x{s:0<hex_len$}") } else { format!("0x{s:0>hex_len$}") })
1803 }
1804
1805 /// Decodes string from bytes32 value
1806 pub fn parse_bytes32_string(s: &str) -> Result<String> {
1807 let bytes = hex::decode(s)?;
1808 eyre::ensure!(bytes.len() == 32, "expected 32 byte hex-string");
1809 let len = bytes.iter().take_while(|x| **x != 0).count();
1810 Ok(std::str::from_utf8(&bytes[..len])?.into())
1811 }
1812
1813 /// Decodes checksummed address from bytes32 value
1814 pub fn parse_bytes32_address(s: &str) -> Result<String> {
1815 let s = strip_0x(s);
1816 if s.len() != 64 {
1817 eyre::bail!("expected 64 byte hex-string, got {s}");
1818 }
1819
1820 let s = if let Some(stripped) = s.strip_prefix("000000000000000000000000") {
1821 stripped
1822 } else {
1823 return Err(eyre::eyre!("Not convertible to address, there are non-zero bytes"));
1824 };
1825
1826 let lowercase_address_string = format!("0x{s}");
1827 let lowercase_address = Address::from_str(&lowercase_address_string)?;
1828
1829 Ok(lowercase_address.to_checksum(None))
1830 }
1831
1832 /// Decodes abi-encoded hex input or output
1833 ///
1834 /// When `input=true`, `calldata` string MUST not be prefixed with function selector
1835 ///
1836 /// # Example
1837 ///
1838 /// ```
1839 /// use cast::SimpleCast as Cast;
1840 /// use alloy_primitives::hex;
1841 ///
1842 /// // Passing `input = false` will decode the data as the output type.
1843 /// // The input data types and the full function sig are ignored, i.e.
1844 /// // you could also pass `balanceOf()(uint256)` and it'd still work.
1845 /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
1846 /// let sig = "balanceOf(address, uint256)(uint256)";
1847 /// let decoded = Cast::abi_decode(sig, data, false)?[0].as_uint().unwrap().0.to_string();
1848 /// assert_eq!(decoded, "1");
1849 ///
1850 /// // Passing `input = true` will decode the data with the input function signature.
1851 /// // We exclude the "prefixed" function selector from the data field (the first 4 bytes).
1852 /// let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
1853 /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
1854 /// let decoded = Cast::abi_decode(sig, data, true)?;
1855 /// let decoded = [
1856 /// decoded[0].as_address().unwrap().to_string().to_lowercase(),
1857 /// decoded[1].as_address().unwrap().to_string().to_lowercase(),
1858 /// decoded[2].as_uint().unwrap().0.to_string(),
1859 /// decoded[3].as_uint().unwrap().0.to_string(),
1860 /// hex::encode(decoded[4].as_bytes().unwrap())
1861 /// ]
1862 /// .into_iter()
1863 /// .collect::<Vec<_>>();
1864 ///
1865 /// assert_eq!(
1866 /// decoded,
1867 /// vec!["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "42", "1", ""]
1868 /// );
1869 /// # Ok::<_, eyre::Report>(())
1870 /// ```
1871 pub fn abi_decode(sig: &str, calldata: &str, input: bool) -> Result<Vec<DynSolValue>> {
1872 foundry_common::abi::abi_decode_calldata(sig, calldata, input, false)
1873 }
1874
1875 /// Decodes calldata-encoded hex input or output
1876 ///
1877 /// Similar to `abi_decode`, but `calldata` string MUST be prefixed with function selector
1878 ///
1879 /// # Example
1880 ///
1881 /// ```
1882 /// use cast::SimpleCast as Cast;
1883 /// use alloy_primitives::hex;
1884 ///
1885 /// // Passing `input = false` will decode the data as the output type.
1886 /// // The input data types and the full function sig are ignored, i.e.
1887 /// // you could also pass `balanceOf()(uint256)` and it'd still work.
1888 /// let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
1889 /// let sig = "balanceOf(address, uint256)(uint256)";
1890 /// let decoded = Cast::calldata_decode(sig, data, false)?[0].as_uint().unwrap().0.to_string();
1891 /// assert_eq!(decoded, "1");
1892 ///
1893 /// // Passing `input = true` will decode the data with the input function signature.
1894 /// let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
1895 /// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
1896 /// let decoded = Cast::calldata_decode(sig, data, true)?;
1897 /// let decoded = [
1898 /// decoded[0].as_address().unwrap().to_string().to_lowercase(),
1899 /// decoded[1].as_address().unwrap().to_string().to_lowercase(),
1900 /// decoded[2].as_uint().unwrap().0.to_string(),
1901 /// decoded[3].as_uint().unwrap().0.to_string(),
1902 /// hex::encode(decoded[4].as_bytes().unwrap()),
1903 /// ]
1904 /// .into_iter()
1905 /// .collect::<Vec<_>>();
1906 /// assert_eq!(
1907 /// decoded,
1908 /// vec!["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "42", "1", ""]
1909 /// );
1910 /// # Ok::<_, eyre::Report>(())
1911 /// ```
1912 pub fn calldata_decode(sig: &str, calldata: &str, input: bool) -> Result<Vec<DynSolValue>> {
1913 foundry_common::abi::abi_decode_calldata(sig, calldata, input, true)
1914 }
1915
1916 /// Performs ABI encoding based off of the function signature. Does not include
1917 /// the function selector in the result.
1918 ///
1919 /// # Example
1920 ///
1921 /// ```
1922 /// use cast::SimpleCast as Cast;
1923 ///
1924 /// assert_eq!(
1925 /// "0x0000000000000000000000000000000000000000000000000000000000000001",
1926 /// Cast::abi_encode("f(uint a)", &["1"]).unwrap().as_str()
1927 /// );
1928 /// assert_eq!(
1929 /// "0x0000000000000000000000000000000000000000000000000000000000000001",
1930 /// Cast::abi_encode("constructor(uint a)", &["1"]).unwrap().as_str()
1931 /// );
1932 /// # Ok::<_, eyre::Report>(())
1933 /// ```
1934 pub fn abi_encode(sig: &str, args: &[impl AsRef<str>]) -> Result<String> {
1935 let func = get_func(sig)?;
1936 match encode_function_args(&func, args) {
1937 Ok(res) => Ok(hex::encode_prefixed(&res[4..])),
1938 Err(e) => {
1939 eyre::bail!("Could not ABI encode the function and arguments: {e}");
1940 }
1941 }
1942 }
1943
1944 /// Performs packed ABI encoding based off of the function signature or tuple.
1945 ///
1946 /// # Examplez
1947 ///
1948 /// ```
1949 /// use cast::SimpleCast as Cast;
1950 ///
1951 /// assert_eq!(
1952 /// "0x0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000012c00000000000000c8",
1953 /// Cast::abi_encode_packed("(uint128[] a, uint64 b)", &["[100, 300]", "200"]).unwrap().as_str()
1954 /// );
1955 ///
1956 /// assert_eq!(
1957 /// "0x8dbd1b711dc621e1404633da156fcc779e1c6f3e68656c6c6f20776f726c64",
1958 /// Cast::abi_encode_packed("foo(address a, string b)", &["0x8dbd1b711dc621e1404633da156fcc779e1c6f3e", "hello world"]).unwrap().as_str()
1959 /// );
1960 /// # Ok::<_, eyre::Report>(())
1961 /// ```
1962 pub fn abi_encode_packed(sig: &str, args: &[impl AsRef<str>]) -> Result<String> {
1963 // If the signature is a tuple, we need to prefix it to make it a function
1964 let sig =
1965 if sig.trim_start().starts_with('(') { format!("foo{sig}") } else { sig.to_string() };
1966
1967 let func = get_func(sig.as_str())?;
1968 let encoded = match encode_function_args_packed(&func, args) {
1969 Ok(res) => hex::encode(res),
1970 Err(e) => {
1971 eyre::bail!("Could not ABI encode the function and arguments: {e}");
1972 }
1973 };
1974 Ok(format!("0x{encoded}"))
1975 }
1976
1977 /// Performs ABI encoding of an event to produce the topics and data.
1978 ///
1979 /// # Example
1980 ///
1981 /// ```
1982 /// use alloy_primitives::hex;
1983 /// use cast::SimpleCast as Cast;
1984 ///
1985 /// let log_data = Cast::abi_encode_event(
1986 /// "Transfer(address indexed from, address indexed to, uint256 value)",
1987 /// &[
1988 /// "0x1234567890123456789012345678901234567890",
1989 /// "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
1990 /// "1000",
1991 /// ],
1992 /// )
1993 /// .unwrap();
1994 ///
1995 /// // topic0 is the event selector
1996 /// assert_eq!(log_data.topics().len(), 3);
1997 /// assert_eq!(
1998 /// log_data.topics()[0].to_string(),
1999 /// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
2000 /// );
2001 /// assert_eq!(
2002 /// log_data.topics()[1].to_string(),
2003 /// "0x0000000000000000000000001234567890123456789012345678901234567890"
2004 /// );
2005 /// assert_eq!(
2006 /// log_data.topics()[2].to_string(),
2007 /// "0x000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd"
2008 /// );
2009 /// assert_eq!(
2010 /// hex::encode_prefixed(log_data.data),
2011 /// "0x00000000000000000000000000000000000000000000000000000000000003e8"
2012 /// );
2013 /// # Ok::<_, eyre::Report>(())
2014 /// ```
2015 pub fn abi_encode_event(sig: &str, args: &[impl AsRef<str>]) -> Result<LogData> {
2016 let event = get_event(sig)?;
2017 let tokens = std::iter::zip(&event.inputs, args)
2018 .map(|(input, arg)| coerce_value(&input.ty, arg.as_ref()))
2019 .collect::<Result<Vec<_>>>()?;
2020
2021 let mut topics = vec![event.selector()];
2022 let mut data_tokens: Vec<u8> = Vec::new();
2023
2024 for (input, token) in event.inputs.iter().zip(tokens) {
2025 if input.indexed {
2026 let ty = DynSolType::parse(&input.ty)?;
2027 if matches!(
2028 ty,
2029 DynSolType::String
2030 | DynSolType::Bytes
2031 | DynSolType::Array(_)
2032 | DynSolType::Tuple(_)
2033 ) {
2034 // For dynamic types, hash the encoded value
2035 let encoded = token.abi_encode();
2036 let hash = keccak256(encoded);
2037 topics.push(hash);
2038 } else {
2039 // For fixed-size types, encode directly to 32 bytes
2040 let mut encoded = [0u8; 32];
2041 let token_encoded = token.abi_encode();
2042 if token_encoded.len() <= 32 {
2043 let start = 32 - token_encoded.len();
2044 encoded[start..].copy_from_slice(&token_encoded);
2045 }
2046 topics.push(B256::from(encoded));
2047 }
2048 } else {
2049 // Non-indexed parameters go into data
2050 data_tokens.extend_from_slice(&token.abi_encode());
2051 }
2052 }
2053
2054 Ok(LogData::new_unchecked(topics, data_tokens.into()))
2055 }
2056
2057 /// Performs ABI encoding to produce the hexadecimal calldata with the given arguments.
2058 ///
2059 /// # Example
2060 ///
2061 /// ```
2062 /// use cast::SimpleCast as Cast;
2063 ///
2064 /// assert_eq!(
2065 /// "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001",
2066 /// Cast::calldata_encode("f(uint256 a)", &["1"]).unwrap().as_str()
2067 /// );
2068 /// # Ok::<_, eyre::Report>(())
2069 /// ```
2070 pub fn calldata_encode(sig: impl AsRef<str>, args: &[impl AsRef<str>]) -> Result<String> {
2071 let func = get_func(sig.as_ref())?;
2072 let calldata = encode_function_args(&func, args)?;
2073 Ok(hex::encode_prefixed(calldata))
2074 }
2075
2076 /// Returns the slot number for a given mapping key and slot.
2077 ///
2078 /// Given `mapping(k => v) m`, for a key `k` the slot number of its associated `v` is
2079 /// `keccak256(concat(h(k), p))`, where `h` is the padding function for `k`'s type, and `p`
2080 /// is slot number of the mapping `m`.
2081 ///
2082 /// See [the Solidity documentation](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays)
2083 /// for more details.
2084 ///
2085 /// # Example
2086 ///
2087 /// ```
2088 /// # use cast::SimpleCast as Cast;
2089 ///
2090 /// // Value types.
2091 /// assert_eq!(
2092 /// Cast::index("address", "0xD0074F4E6490ae3f888d1d4f7E3E43326bD3f0f5", "2").unwrap().as_str(),
2093 /// "0x9525a448a9000053a4d151336329d6563b7e80b24f8e628e95527f218e8ab5fb"
2094 /// );
2095 /// assert_eq!(
2096 /// Cast::index("uint256", "42", "6").unwrap().as_str(),
2097 /// "0xfc808b0f31a1e6b9cf25ff6289feae9b51017b392cc8e25620a94a38dcdafcc1"
2098 /// );
2099 ///
2100 /// // Strings and byte arrays.
2101 /// assert_eq!(
2102 /// Cast::index("string", "hello", "1").unwrap().as_str(),
2103 /// "0x8404bb4d805e9ca2bd5dd5c43a107e935c8ec393caa7851b353b3192cd5379ae"
2104 /// );
2105 /// # Ok::<_, eyre::Report>(())
2106 /// ```
2107 pub fn index(key_type: &str, key: &str, slot_number: &str) -> Result<String> {
2108 let mut hasher = Keccak256::new();
2109
2110 let k_ty = DynSolType::parse(key_type).wrap_err("Could not parse type")?;
2111 let k = k_ty.coerce_str(key).wrap_err("Could not parse value")?;
2112 match k_ty {
2113 // For value types, `h` pads the value to 32 bytes in the same way as when storing the
2114 // value in memory.
2115 DynSolType::Bool
2116 | DynSolType::Int(_)
2117 | DynSolType::Uint(_)
2118 | DynSolType::FixedBytes(_)
2119 | DynSolType::Address
2120 | DynSolType::Function => hasher.update(k.as_word().unwrap()),
2121
2122 // For strings and byte arrays, `h(k)` is just the unpadded data.
2123 DynSolType::String | DynSolType::Bytes => hasher.update(k.as_packed_seq().unwrap()),
2124
2125 DynSolType::Array(..)
2126 | DynSolType::FixedArray(..)
2127 | DynSolType::Tuple(..)
2128 | DynSolType::CustomStruct { .. } => {
2129 eyre::bail!("Type `{k_ty}` is not supported as a mapping key");
2130 }
2131 }
2132
2133 let p = DynSolType::Uint(256)
2134 .coerce_str(slot_number)
2135 .wrap_err("Could not parse slot number")?;
2136 let p = p.as_word().unwrap();
2137 hasher.update(p);
2138
2139 let location = hasher.finalize();
2140 Ok(location.to_string())
2141 }
2142
2143 /// Keccak-256 hashes arbitrary data
2144 ///
2145 /// # Example
2146 ///
2147 /// ```
2148 /// use cast::SimpleCast as Cast;
2149 ///
2150 /// assert_eq!(
2151 /// Cast::keccak("foo")?,
2152 /// "0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d"
2153 /// );
2154 /// assert_eq!(
2155 /// Cast::keccak("123abc")?,
2156 /// "0xb1f1c74a1ba56f07a892ea1110a39349d40f66ca01d245e704621033cb7046a4"
2157 /// );
2158 /// assert_eq!(
2159 /// Cast::keccak("0x12")?,
2160 /// "0x5fa2358263196dbbf23d1ca7a509451f7a2f64c15837bfbb81298b1e3e24e4fa"
2161 /// );
2162 /// assert_eq!(
2163 /// Cast::keccak("12")?,
2164 /// "0x7f8b6b088b6d74c2852fc86c796dca07b44eed6fb3daf5e6b59f7c364db14528"
2165 /// );
2166 /// # Ok::<_, eyre::Report>(())
2167 /// ```
2168 pub fn keccak(data: &str) -> Result<String> {
2169 // Hex-decode if data starts with 0x.
2170 let hash = if data.starts_with("0x") {
2171 keccak256(hex::decode(data.trim_end())?)
2172 } else {
2173 keccak256(data)
2174 };
2175 Ok(hash.to_string())
2176 }
2177
2178 /// Performs the left shift operation (<<) on a number
2179 ///
2180 /// # Example
2181 ///
2182 /// ```
2183 /// use cast::SimpleCast as Cast;
2184 ///
2185 /// assert_eq!(Cast::left_shift("16", "10", Some("10"), "hex")?, "0x4000");
2186 /// assert_eq!(Cast::left_shift("255", "16", Some("dec"), "hex")?, "0xff0000");
2187 /// assert_eq!(Cast::left_shift("0xff", "16", None, "hex")?, "0xff0000");
2188 /// # Ok::<_, eyre::Report>(())
2189 /// ```
2190 pub fn left_shift(
2191 value: &str,
2192 bits: &str,
2193 base_in: Option<&str>,
2194 base_out: &str,
2195 ) -> Result<String> {
2196 let base_out: Base = base_out.parse()?;
2197 let value = NumberWithBase::parse_uint(value, base_in)?;
2198 let bits = NumberWithBase::parse_uint(bits, None)?;
2199
2200 let res = value.number() << bits.number();
2201
2202 Ok(res.to_base(base_out, true)?)
2203 }
2204
2205 /// Performs the right shift operation (>>) on a number
2206 ///
2207 /// # Example
2208 ///
2209 /// ```
2210 /// use cast::SimpleCast as Cast;
2211 ///
2212 /// assert_eq!(Cast::right_shift("0x4000", "10", None, "dec")?, "16");
2213 /// assert_eq!(Cast::right_shift("16711680", "16", Some("10"), "hex")?, "0xff");
2214 /// assert_eq!(Cast::right_shift("0xff0000", "16", None, "hex")?, "0xff");
2215 /// # Ok::<(), eyre::Report>(())
2216 /// ```
2217 pub fn right_shift(
2218 value: &str,
2219 bits: &str,
2220 base_in: Option<&str>,
2221 base_out: &str,
2222 ) -> Result<String> {
2223 let base_out: Base = base_out.parse()?;
2224 let value = NumberWithBase::parse_uint(value, base_in)?;
2225 let bits = NumberWithBase::parse_uint(bits, None)?;
2226
2227 let res = value.number().wrapping_shr(bits.number().saturating_to());
2228
2229 Ok(res.to_base(base_out, true)?)
2230 }
2231
2232 /// Fetches source code of verified contracts from etherscan.
2233 ///
2234 /// # Example
2235 ///
2236 /// ```
2237 /// # use cast::SimpleCast as Cast;
2238 /// # use foundry_config::NamedChain;
2239 /// # async fn foo() -> eyre::Result<()> {
2240 /// assert_eq!(
2241 /// "/*
2242 /// - Bytecode Verification performed was compared on second iteration -
2243 /// This file is part of the DAO.....",
2244 /// Cast::etherscan_source(
2245 /// NamedChain::Mainnet.into(),
2246 /// "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(),
2247 /// Some("<etherscan_api_key>".to_string()),
2248 /// None,
2249 /// None
2250 /// )
2251 /// .await
2252 /// .unwrap()
2253 /// .as_str()
2254 /// );
2255 /// # Ok(())
2256 /// # }
2257 /// ```
2258 pub async fn etherscan_source(
2259 chain: Chain,
2260 contract_address: String,
2261 etherscan_api_key: Option<String>,
2262 explorer_api_url: Option<String>,
2263 explorer_url: Option<String>,
2264 ) -> Result<String> {
2265 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2266 let metadata = client.contract_source_code(contract_address.parse()?).await?;
2267 Ok(metadata.source_code())
2268 }
2269
2270 /// Fetches the source code of verified contracts from etherscan and expands the resulting
2271 /// files to a directory for easy perusal.
2272 ///
2273 /// # Example
2274 ///
2275 /// ```
2276 /// # use cast::SimpleCast as Cast;
2277 /// # use foundry_config::NamedChain;
2278 /// # use std::path::PathBuf;
2279 /// # async fn expand() -> eyre::Result<()> {
2280 /// Cast::expand_etherscan_source_to_directory(
2281 /// NamedChain::Mainnet.into(),
2282 /// "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".to_string(),
2283 /// Some("<etherscan_api_key>".to_string()),
2284 /// PathBuf::from("output_dir"),
2285 /// None,
2286 /// None,
2287 /// )
2288 /// .await?;
2289 /// # Ok(())
2290 /// # }
2291 /// ```
2292 pub async fn expand_etherscan_source_to_directory(
2293 chain: Chain,
2294 contract_address: String,
2295 etherscan_api_key: Option<String>,
2296 output_directory: PathBuf,
2297 explorer_api_url: Option<String>,
2298 explorer_url: Option<String>,
2299 ) -> eyre::Result<()> {
2300 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2301 let meta = client.contract_source_code(contract_address.parse()?).await?;
2302 let source_tree = meta.source_tree();
2303 source_tree.write_to(&output_directory)?;
2304 Ok(())
2305 }
2306
2307 /// Fetches the source code of verified contracts from etherscan, flattens it and writes it to
2308 /// the given path or stdout.
2309 pub async fn etherscan_source_flatten(
2310 chain: Chain,
2311 contract_address: String,
2312 etherscan_api_key: Option<String>,
2313 output_path: Option<PathBuf>,
2314 explorer_api_url: Option<String>,
2315 explorer_url: Option<String>,
2316 ) -> Result<()> {
2317 let client = explorer_client(chain, etherscan_api_key, explorer_api_url, explorer_url)?;
2318 let metadata = client.contract_source_code(contract_address.parse()?).await?;
2319 let Some(metadata) = metadata.items.first() else {
2320 eyre::bail!("Empty contract source code");
2321 };
2322
2323 let tmp = tempfile::tempdir()?;
2324 let project = etherscan_project(metadata, tmp.path())?;
2325 let target_path = project.find_contract_path(&metadata.contract_name)?;
2326
2327 let flattened = flatten(project, &target_path)?;
2328
2329 if let Some(path) = output_path {
2330 fs::create_dir_all(path.parent().unwrap())?;
2331 fs::write(&path, flattened)?;
2332 sh_status!("Flattened file written at {}", path.display())?
2333 } else {
2334 sh_println!("{flattened}")?
2335 }
2336
2337 Ok(())
2338 }
2339
2340 /// Disassembles hex encoded bytecode into individual / human readable opcodes
2341 ///
2342 /// # Example
2343 ///
2344 /// ```
2345 /// use alloy_primitives::hex;
2346 /// use cast::SimpleCast as Cast;
2347 ///
2348 /// # async fn foo() -> eyre::Result<()> {
2349 /// let bytecode = "0x608060405260043610603f57600035";
2350 /// let opcodes = Cast::disassemble(&hex::decode(bytecode)?)?;
2351 /// println!("{}", opcodes);
2352 /// # Ok(())
2353 /// # }
2354 /// ```
2355 pub fn disassemble(code: &[u8]) -> Result<String> {
2356 let mut output = String::new();
2357 for (pc, inst) in InstIter::new(code).with_pc() {
2358 writeln!(output, "{pc:08x}: {inst}")?;
2359 }
2360 Ok(output)
2361 }
2362
2363 /// Gets the selector for a given function signature
2364 /// Optimizes if the `optimize` parameter is set to a number of leading zeroes
2365 ///
2366 /// # Example
2367 ///
2368 /// ```
2369 /// use cast::SimpleCast as Cast;
2370 ///
2371 /// assert_eq!(Cast::get_selector("foo(address,uint256)", 0)?.0, String::from("0xbd0d639f"));
2372 /// # Ok::<(), eyre::Error>(())
2373 /// ```
2374 pub fn get_selector(signature: &str, optimize: usize) -> Result<(String, String)> {
2375 if optimize > 4 {
2376 eyre::bail!("number of leading zeroes must not be greater than 4");
2377 }
2378 if optimize == 0 {
2379 let selector = get_func(signature)?.selector();
2380 return Ok((selector.to_string(), String::from(signature)));
2381 }
2382 let Some((name, params)) = signature.split_once('(') else {
2383 eyre::bail!("invalid function signature");
2384 };
2385
2386 let num_threads = rayon::current_num_threads();
2387 let found = AtomicBool::new(false);
2388
2389 let result: Option<(u32, String, String)> =
2390 (0..num_threads).into_par_iter().find_map_any(|i| {
2391 let nonce_start = i as u32;
2392 let nonce_step = num_threads as u32;
2393
2394 let mut nonce = nonce_start;
2395 while nonce < u32::MAX && !found.load(Ordering::Relaxed) {
2396 let input = format!("{name}{nonce}({params}");
2397 let hash = keccak256(input.as_bytes());
2398 let selector = &hash[..4];
2399
2400 if selector.iter().take_while(|&&byte| byte == 0).count() == optimize {
2401 found.store(true, Ordering::Relaxed);
2402 return Some((nonce, hex::encode_prefixed(selector), input));
2403 }
2404
2405 nonce += nonce_step;
2406 }
2407 None
2408 });
2409
2410 match result {
2411 Some((_nonce, selector, signature)) => Ok((selector, signature)),
2412 None => {
2413 eyre::bail!("No selector found");
2414 }
2415 }
2416 }
2417
2418 /// Extracts function selectors, arguments and state mutability from bytecode
2419 ///
2420 /// # Example
2421 ///
2422 /// ```
2423 /// use alloy_primitives::fixed_bytes;
2424 /// use cast::SimpleCast as Cast;
2425 ///
2426 /// let bytecode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80632125b65b14602a575b5f80fd5b603a6035366004603c565b505050565b005b5f805f60608486031215604d575f80fd5b833563ffffffff81168114605f575f80fd5b925060208401356001600160a01b03811681146079575f80fd5b915060408401356001600160e01b03811681146093575f80fd5b80915050925092509256";
2427 /// let functions = Cast::extract_functions(bytecode)?;
2428 /// assert_eq!(functions, vec![(fixed_bytes!("0x2125b65b"), "uint32,address,uint224".to_string(), "pure")]);
2429 /// # Ok::<(), eyre::Report>(())
2430 /// ```
2431 pub fn extract_functions(bytecode: &str) -> Result<Vec<(Selector, String, &str)>> {
2432 let code = hex::decode(bytecode)?;
2433 let info = evmole::contract_info(
2434 evmole::ContractInfoArgs::new(&code)
2435 .with_selectors()
2436 .with_arguments()
2437 .with_state_mutability(),
2438 );
2439 Ok(info
2440 .functions
2441 .expect("functions extraction was requested")
2442 .into_iter()
2443 .map(|f| {
2444 (
2445 f.selector.into(),
2446 f.arguments
2447 .expect("arguments extraction was requested")
2448 .into_iter()
2449 .map(|t| t.sol_type_name().to_string())
2450 .collect::<Vec<String>>()
2451 .join(","),
2452 f.state_mutability
2453 .expect("state_mutability extraction was requested")
2454 .as_json_str(),
2455 )
2456 })
2457 .collect())
2458 }
2459
2460 /// Decodes a raw EIP2718 transaction payload
2461 /// Returns details about the typed transaction and ECSDA signature components
2462 ///
2463 /// # Example
2464 ///
2465 /// ```
2466 /// use alloy_network::Ethereum;
2467 /// use cast::SimpleCast as Cast;
2468 ///
2469 /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8";
2470 /// let tx_envelope = Cast::decode_raw_transaction::<Ethereum>(&tx)?;
2471 /// # Ok::<(), eyre::Report>(())
2472 pub fn decode_raw_transaction<N: Network<TxEnvelope: SignerRecoverable + Serialize>>(
2473 tx: &str,
2474 ) -> Result<String> {
2475 let tx_hex = hex::decode(tx)?;
2476 let tx: N::TxEnvelope = Decodable2718::decode_2718(&mut tx_hex.as_slice())?;
2477 if let Ok(signer) = tx.recover_signer() {
2478 Ok(serde_json::to_string_pretty(&Recovered::new_unchecked(tx, signer))?)
2479 } else {
2480 Ok(serde_json::to_string_pretty(&tx)?)
2481 }
2482 }
2483}
2484
2485pub(crate) fn strip_0x(s: &str) -> &str {
2486 s.strip_prefix("0x").unwrap_or(s)
2487}
2488
2489fn explorer_client(
2490 chain: Chain,
2491 api_key: Option<String>,
2492 api_url: Option<String>,
2493 explorer_url: Option<String>,
2494) -> Result<Client> {
2495 let mut builder = Client::builder();
2496
2497 let deduced = chain.etherscan_urls();
2498
2499 let explorer_url = explorer_url
2500 .or(deduced.map(|d| d.1.to_string()))
2501 .ok_or_eyre("Please provide the explorer browser URL using `--explorer-url`")?;
2502 builder = builder.with_url(explorer_url)?;
2503
2504 let api_url = api_url
2505 .or(deduced.map(|d| d.0.to_string()))
2506 .ok_or_eyre("Please provide the explorer API URL using `--explorer-api-url`")?;
2507 builder = builder.with_api_url(api_url)?;
2508
2509 if let Some(api_key) = api_key {
2510 builder = builder.with_api_key(api_key);
2511 }
2512
2513 builder.build().map_err(Into::into)
2514}
2515
2516/// Tests for the `eth_getLogs` chunking/bisection helpers, kept in a separate module so they can
2517/// use the provider-based [`Cast`] (the `tests` module aliases `Cast` to `SimpleCast`).
2518#[cfg(test)]
2519mod logs_bisecting {
2520 use super::Cast;
2521 use alloy_json_rpc::{RequestPacket, ResponsePacket, SerializedRequest};
2522 use alloy_network::AnyNetwork;
2523 use alloy_provider::ProviderBuilder;
2524 use alloy_rpc_client::RpcClient;
2525 use alloy_rpc_types::{Filter, Log};
2526 use alloy_transport::{
2527 TransportError, TransportFut,
2528 mock::{Asserter, MockTransport},
2529 };
2530 use std::{
2531 sync::{Arc, Mutex},
2532 task::{Context, Poll},
2533 };
2534 use tower::Service;
2535
2536 fn log_at(block: u64) -> Log {
2537 Log { block_number: Some(block), ..Default::default() }
2538 }
2539
2540 /// Mock transport that records the `eth_getLogs` `[fromBlock, toBlock]` ranges it is asked for
2541 /// while delegating the actual responses to a FIFO [`Asserter`].
2542 #[derive(Clone)]
2543 struct RecordingTransport {
2544 inner: MockTransport,
2545 ranges: Arc<Mutex<Vec<(String, String)>>>,
2546 }
2547
2548 impl RecordingTransport {
2549 fn new(asserter: Asserter) -> Self {
2550 Self { inner: MockTransport::new(asserter), ranges: Arc::new(Mutex::new(Vec::new())) }
2551 }
2552
2553 fn record(&self, req: &SerializedRequest) {
2554 if req.method() != "eth_getLogs" {
2555 return;
2556 }
2557 let Some(params) = req.params() else { return };
2558 let Ok(value) = serde_json::from_str::<serde_json::Value>(params.get()) else { return };
2559 let Some(filter) = value.get(0) else { return };
2560 let field =
2561 |name| filter.get(name).and_then(|v| v.as_str()).unwrap_or_default().to_string();
2562 self.ranges.lock().unwrap().push((field("fromBlock"), field("toBlock")));
2563 }
2564 }
2565
2566 impl Service<RequestPacket> for RecordingTransport {
2567 type Response = ResponsePacket;
2568 type Error = TransportError;
2569 type Future = TransportFut<'static>;
2570
2571 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2572 self.inner.poll_ready(cx)
2573 }
2574
2575 fn call(&mut self, req: RequestPacket) -> Self::Future {
2576 match &req {
2577 RequestPacket::Single(req) => self.record(req),
2578 RequestPacket::Batch(reqs) => reqs.iter().for_each(|req| self.record(req)),
2579 }
2580 self.inner.call(req)
2581 }
2582 }
2583
2584 // A range-limit failure splits depth-first into [0,1]/[2,3] and aggregates in range order.
2585 #[tokio::test]
2586 async fn bisects_failed_range_and_aggregates_in_order() {
2587 let asserter = Asserter::new();
2588 asserter.push_failure_msg("query returned more than 10000 results");
2589 asserter.push_success(&vec![log_at(0)]);
2590 asserter.push_success(&vec![log_at(2)]);
2591
2592 let transport = RecordingTransport::new(asserter);
2593 let ranges = transport.ranges.clone();
2594 let provider = ProviderBuilder::<_, _, AnyNetwork>::default()
2595 .connect_client(RpcClient::new(transport, true));
2596
2597 let logs = Cast::get_logs_bisecting(&provider, &Filter::new(), 0, 3).await.unwrap();
2598 let blocks: Vec<_> = logs.iter().map(|l| l.block_number).collect();
2599 assert_eq!(blocks, vec![Some(0), Some(2)]);
2600
2601 // The original range fails, then bisection requests exactly the two halves in order.
2602 let ranges = ranges.lock().unwrap();
2603 assert_eq!(
2604 *ranges,
2605 vec![
2606 ("0x0".to_string(), "0x3".to_string()),
2607 ("0x0".to_string(), "0x1".to_string()),
2608 ("0x2".to_string(), "0x3".to_string()),
2609 ]
2610 );
2611 }
2612
2613 // A single-block failure can't be split, so the error is surfaced.
2614 #[tokio::test]
2615 async fn surfaces_single_block_failure() {
2616 let asserter = Asserter::new();
2617 asserter.push_failure_msg("query returned more than 10000 results");
2618
2619 let provider =
2620 ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(asserter);
2621
2622 let err = Cast::get_logs_bisecting(&provider, &Filter::new(), 5, 5).await.unwrap_err();
2623 assert!(err.to_string().contains("more than 10000 results"), "got: {err}");
2624 }
2625
2626 // A non-range error fails after one request instead of bisecting.
2627 #[tokio::test]
2628 async fn does_not_bisect_non_range_errors() {
2629 let asserter = Asserter::new();
2630 asserter.push_failure_msg("unauthorized: invalid api key");
2631
2632 let provider =
2633 ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(asserter);
2634
2635 let err = Cast::get_logs_bisecting(&provider, &Filter::new(), 0, 3).await.unwrap_err();
2636 assert!(err.to_string().contains("unauthorized"), "got: {err}");
2637 }
2638}
2639
2640#[cfg(test)]
2641mod tests {
2642 use super::{DynSolValue, SimpleCast as Cast, serialize_value_as_json};
2643 use alloy_primitives::hex;
2644
2645 #[test]
2646 fn simple_selector() {
2647 assert_eq!("0xc2985578", Cast::get_selector("foo()", 0).unwrap().0.as_str())
2648 }
2649
2650 #[test]
2651 fn selector_with_arg() {
2652 assert_eq!("0xbd0d639f", Cast::get_selector("foo(address,uint256)", 0).unwrap().0.as_str())
2653 }
2654
2655 #[test]
2656 fn calldata_uint() {
2657 assert_eq!(
2658 "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001",
2659 Cast::calldata_encode("f(uint256 a)", &["1"]).unwrap().as_str()
2660 );
2661 }
2662
2663 // <https://github.com/foundry-rs/foundry/issues/2681>
2664 #[test]
2665 fn calldata_array() {
2666 assert_eq!(
2667 "0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
2668 Cast::calldata_encode("propose(string[])", &["[\"\"]"]).unwrap().as_str()
2669 );
2670 }
2671
2672 #[test]
2673 fn calldata_bool() {
2674 assert_eq!(
2675 "0x6fae94120000000000000000000000000000000000000000000000000000000000000000",
2676 Cast::calldata_encode("bar(bool)", &["false"]).unwrap().as_str()
2677 );
2678 }
2679
2680 #[test]
2681 fn abi_decode() {
2682 let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
2683 let sig = "balanceOf(address, uint256)(uint256)";
2684 assert_eq!(
2685 "1",
2686 Cast::abi_decode(sig, data, false).unwrap()[0].as_uint().unwrap().0.to_string()
2687 );
2688
2689 let data = "0x0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
2690 let sig = "safeTransferFrom(address,address,uint256,uint256,bytes)";
2691 let decoded = Cast::abi_decode(sig, data, true).unwrap();
2692 let decoded = [
2693 decoded[0]
2694 .as_address()
2695 .unwrap()
2696 .to_string()
2697 .strip_prefix("0x")
2698 .unwrap()
2699 .to_owned()
2700 .to_lowercase(),
2701 decoded[1]
2702 .as_address()
2703 .unwrap()
2704 .to_string()
2705 .strip_prefix("0x")
2706 .unwrap()
2707 .to_owned()
2708 .to_lowercase(),
2709 decoded[2].as_uint().unwrap().0.to_string(),
2710 decoded[3].as_uint().unwrap().0.to_string(),
2711 hex::encode(decoded[4].as_bytes().unwrap()),
2712 ]
2713 .to_vec();
2714 assert_eq!(
2715 decoded,
2716 vec![
2717 "8dbd1b711dc621e1404633da156fcc779e1c6f3e",
2718 "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc",
2719 "42",
2720 "1",
2721 ""
2722 ]
2723 );
2724 }
2725
2726 #[test]
2727 fn calldata_decode() {
2728 let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
2729 let sig = "balanceOf(address, uint256)(uint256)";
2730 let decoded =
2731 Cast::calldata_decode(sig, data, false).unwrap()[0].as_uint().unwrap().0.to_string();
2732 assert_eq!(decoded, "1");
2733
2734 // Passing `input = true` will decode the data with the input function signature.
2735 // We exclude the "prefixed" function selector from the data field (the first 4 bytes).
2736 let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
2737 let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
2738 let decoded = Cast::calldata_decode(sig, data, true).unwrap();
2739 let decoded = [
2740 decoded[0].as_address().unwrap().to_string().to_lowercase(),
2741 decoded[1].as_address().unwrap().to_string().to_lowercase(),
2742 decoded[2].as_uint().unwrap().0.to_string(),
2743 decoded[3].as_uint().unwrap().0.to_string(),
2744 hex::encode(decoded[4].as_bytes().unwrap()),
2745 ]
2746 .into_iter()
2747 .collect::<Vec<_>>();
2748 assert_eq!(
2749 decoded,
2750 vec![
2751 "0x8dbd1b711dc621e1404633da156fcc779e1c6f3e",
2752 "0xd9f3c9cc99548bf3b44a43e0a2d07399eb918adc",
2753 "42",
2754 "1",
2755 ""
2756 ]
2757 );
2758 }
2759
2760 #[test]
2761 fn calldata_decode_nested_json() {
2762 let calldata = "0xdb5b0ed700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006772bf190000000000000000000000000000000000000000000000000000000000020716000000000000000000000000af9d27ffe4d51ed54ac8eec78f2785d7e11e5ab100000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000404366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb73188600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5";
2763 let sig = "sequenceBatchesValidium((bytes32,bytes32,uint64,bytes32)[],uint64,uint64,address,bytes)";
2764 let decoded = Cast::calldata_decode(sig, calldata, true).unwrap();
2765 let json_value = serialize_value_as_json(DynSolValue::Array(decoded), None, true).unwrap();
2766 let expected = serde_json::json!([
2767 [
2768 [
2769 "0x04366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27",
2770 "0x0000000000000000000000000000000000000000000000000000000000000000",
2771 0,
2772 "0x0000000000000000000000000000000000000000000000000000000000000000"
2773 ],
2774 [
2775 "0x093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed74",
2776 "0x0000000000000000000000000000000000000000000000000000000000000000",
2777 0,
2778 "0x0000000000000000000000000000000000000000000000000000000000000000"
2779 ],
2780 [
2781 "0x60f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf7",
2782 "0x0000000000000000000000000000000000000000000000000000000000000000",
2783 0,
2784 "0x0000000000000000000000000000000000000000000000000000000000000000"
2785 ],
2786 [
2787 "0x93dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb731886",
2788 "0x0000000000000000000000000000000000000000000000000000000000000000",
2789 0,
2790 "0x0000000000000000000000000000000000000000000000000000000000000000"
2791 ]
2792 ],
2793 1735573273,
2794 132886,
2795 "0xAF9d27ffe4d51eD54AC8eEc78f2785D7E11E5ab1",
2796 "0x334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5"
2797 ]);
2798 assert_eq!(json_value, expected);
2799 }
2800
2801 #[test]
2802 fn concat_hex() {
2803 assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001");
2804 assert_eq!(Cast::concat_hex(["1", "2"]), "0x12");
2805 }
2806
2807 #[test]
2808 fn from_rlp() {
2809 let rlp = "0xf8b1a02b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca8080a02838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f90380a0e46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd8754980a01d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5808080a0236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff08080808080";
2810 let item = Cast::from_rlp(rlp, false).unwrap();
2811 assert_eq!(
2812 item,
2813 r#"["0x2b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca","0x","0x","0x2838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f903","0x","0xe46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd87549","0x","0x1d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5","0x","0x","0x","0x236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff0","0x","0x","0x","0x","0x"]"#
2814 )
2815 }
2816
2817 #[test]
2818 fn disassemble_incomplete_sequence() {
2819 let incomplete = &hex!("60"); // PUSH1
2820 let disassembled = Cast::disassemble(incomplete).unwrap();
2821 assert_eq!(disassembled, "00000000: PUSH1\n");
2822
2823 let complete = &hex!("6000"); // PUSH1 0x00
2824 let disassembled = Cast::disassemble(complete).unwrap();
2825 assert_eq!(disassembled, "00000000: PUSH1 0x00\n");
2826
2827 let incomplete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 31 bytes
2828 let disassembled = Cast::disassemble(incomplete).unwrap();
2829 assert_eq!(disassembled, "00000000: PUSH32\n");
2830
2831 let complete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 32 bytes
2832 let disassembled = Cast::disassemble(complete).unwrap();
2833 assert_eq!(
2834 disassembled,
2835 "00000000: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n"
2836 );
2837 }
2838}