Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 81 additions & 8 deletions cli/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ mod term;
mod utils;

use cast::{Cast, SimpleCast, TxBuilder};
use foundry_config::Config;
use comfy_table::Table;
use foundry_config::{find_project_root_path, Config};
use utils::get_http_provider;
mod opts;
use crate::{cmd::Cmd, utils::consume_config_rpc_url};
Expand All @@ -17,8 +18,9 @@ use clap_complete::generate;
use ethers::{
abi::HumanReadableParser,
core::types::{BlockId, BlockNumber::Latest, H256},
prelude::artifacts::output_selection::ContractOutputSelection,
providers::{Middleware, Provider},
types::{Address, NameOrAddress, U256},
types::{Address, NameOrAddress, TxHash, U256},
};
use eyre::WrapErr;
use foundry_common::fs;
Expand All @@ -38,8 +40,9 @@ use rustc_hex::ToHex;
use std::{
convert::TryFrom,
io::{self, Read, Write},
path::Path,
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};

#[tokio::main]
Expand Down Expand Up @@ -620,12 +623,82 @@ async fn main() -> eyre::Result<()> {
}
println!("{name}");
}
Subcommands::Storage { address, slot, rpc_url, block } => {
Subcommands::Storage { address, slot, rpc_url, block, contract } => {
let rpc_url = consume_config_rpc_url(rpc_url);

let provider = Provider::try_from(rpc_url)?;
let value = provider.get_storage_at(address, slot, block).await?;
println!("{:?}", value);
let provider = Provider::try_from(&rpc_url)?;
if let Some(slot) = slot {
let value = provider.get_storage_at(address, slot, block).await?;
println!("{:?}", value);
} else if let Some(contract) = contract {
let root: PathBuf = if contract.path.is_none() {
find_project_root_path().unwrap()
} else {
contract.path.as_ref().unwrap().into()
};
let figment = Config::figment_with_root(root);
let mut config = Config::from_provider(figment).sanitized();
if !config.extra_output.contains(&ContractOutputSelection::StorageLayout) {
config.extra_output.push(ContractOutputSelection::StorageLayout);
}
let project = config.project()?;
let output = project.compile()?;
let artifact = output.find_contract(&contract);
let local_bytecode = artifact.unwrap().bytecode.clone();
if let Some(local_bytecode) = local_bytecode {
let local_bytecode = format!("{}", local_bytecode.object.as_bytes().unwrap());
let provider = Arc::new(provider);
let code = Cast::new(provider.clone()).code(address.clone(), block).await?;
if code.eq(&local_bytecode) {
if let Some(storage_layout) = &artifact.unwrap().storage_layout {
let mut table = Table::new();
let mut header =
vec!["Name", "Type", "Slot", "Offset", "Bytes", "Contract"];
let (provider, contract_addr) = if let (
rpc_url,
NameOrAddress::Address(addr),
) = (&rpc_url, address)
{
header.push("Value");
(Some(get_http_provider(rpc_url, true)), addr)
} else {
(None, Address::default())
};

table.set_header(header);
for slot in &storage_layout.storage {
let storage_type = storage_layout.types.get(&slot.storage_type);
let mut row = vec![
slot.label.clone(),
storage_type
.as_ref()
.map_or("?".to_string(), |t| t.label.clone()),
slot.slot.clone(),
slot.offset.to_string(),
storage_type
.as_ref()
.map_or("?".to_string(), |t| t.number_of_bytes.clone()),
slot.contract.clone(),
];
if let Some(ref provider) = provider {
let location =
TxHash::from_low_u64_be(slot.slot.parse::<u64>()?);
let value = provider
.get_storage_at(contract_addr, location, block)
.await?;
row.push(format!("{:?}", value));
}
table.add_row(row);
}
println!("{table}");
}
} else {
println!("code mismatch");
// TODO: fetch source from etherscan(see https://github.com/foundry-rs/foundry/pull/1413), compile and get storage value
}
}
} else {
// TODO: fetch source from etherscan(see https://github.com/foundry-rs/foundry/pull/1413), compile and get storage value,
}
}
Subcommands::Proof { address, slots, rpc_url, block } => {
let rpc_url = consume_config_rpc_url(rpc_url);
Expand Down
16 changes: 14 additions & 2 deletions cli/src/opts/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use crate::{
utils::{parse_ether_value, parse_u256},
};
use clap::{Parser, Subcommand, ValueHint};
use ethers::types::{Address, BlockId, BlockNumber, NameOrAddress, H256, U256};
use ethers::{
prelude::info::ContractInfo,
types::{Address, BlockId, BlockNumber, NameOrAddress, H256, U256},
};
use std::{path::PathBuf, str::FromStr};

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -664,7 +667,16 @@ Tries to decode the calldata using https://sig.eth.samczsun.com unless --offline
#[clap(help = "The contract address.", parse(try_from_str = parse_name_or_address), value_name = "ADDRESS")]
address: NameOrAddress,
#[clap(help = "The storage slot number (hex or decimal)", parse(try_from_str = parse_slot), value_name = "SLOT")]
slot: H256,
slot: Option<H256>,

#[clap(
short,
long,
help = "The identifier of the contract to inspect in the form `(<path>:)?<contractname>`.",
value_name = "CONTRACT"
)]
contract: Option<ContractInfo>,

#[clap(short, long, env = "ETH_RPC_URL", value_name = "URL")]
rpc_url: Option<String>,
#[clap(
Expand Down