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