Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
30 changes: 12 additions & 18 deletions crates/cast/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ use foundry_common::{
selectors::{
decode_calldata, decode_event_topic, decode_function_selector, decode_selectors,
import_selectors, parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData,
SelectorType,
SelectorKind,
},
shell, stdin,
};
use foundry_config::Config;
use std::time::Instant;

/// Run the `cast` command-line interface.
Expand Down Expand Up @@ -209,11 +208,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
let data = data.strip_prefix("0x").unwrap_or(data.as_str());
let selector = data.get(..64).unwrap_or_default();
let identified_event =
SignaturesIdentifier::new(Config::foundry_cache_dir(), false)?
.write()
.await
.identify_event(&hex::decode(selector)?)
.await;
SignaturesIdentifier::new(false)?.identify_event(selector.parse()?).await;
if let Some(event) = identified_event {
let _ = sh_println!("{}", event.signature());
let data = data.get(64..).unwrap_or_default();
Expand All @@ -235,11 +230,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
let data = data.strip_prefix("0x").unwrap_or(data.as_str());
let selector = data.get(..8).unwrap_or_default();
let identified_error =
SignaturesIdentifier::new(Config::foundry_cache_dir(), false)?
.write()
.await
.identify_error(&hex::decode(selector)?)
.await;
SignaturesIdentifier::new(false)?.identify_error(selector.parse()?).await;
if let Some(error) = identified_error {
let _ = sh_println!("{}", error.signature());
error
Expand Down Expand Up @@ -385,9 +376,12 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
let max_mutability_len = functions.iter().map(|r| r.2.len()).max().unwrap_or(0);

let resolve_results = if resolve {
let selectors_it = functions.iter().map(|r| &r.0);
let ds = decode_selectors(SelectorType::Function, selectors_it).await?;
ds.into_iter().map(|v| v.unwrap_or_default().join("|")).collect()
let selectors = functions
.iter()
.map(|&(selector, ..)| SelectorKind::Function(selector))
.collect::<Vec<_>>();
let ds = decode_selectors(&selectors).await?;
ds.into_iter().map(|v| v.join("|")).collect()
} else {
vec![]
};
Expand Down Expand Up @@ -501,7 +495,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
// 4Byte
CastSubcommand::FourByte { selector } => {
let selector = stdin::unwrap_line(selector)?;
let sigs = decode_function_selector(&selector).await?;
let sigs = decode_function_selector(selector).await?;
if sigs.is_empty() {
eyre::bail!("No matching function signatures found for selector `{selector}`");
}
Expand All @@ -514,7 +508,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
let calldata = stdin::unwrap_line(calldata)?;

if calldata.len() == 10 {
let sigs = decode_function_selector(&calldata).await?;
let sigs = decode_function_selector(calldata.parse()?).await?;
if sigs.is_empty() {
eyre::bail!("No matching function signatures found for calldata `{calldata}`");
}
Expand Down Expand Up @@ -544,7 +538,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {

CastSubcommand::FourByteEvent { topic } => {
let topic = stdin::unwrap_line(topic)?;
let sigs = decode_event_topic(&topic).await?;
let sigs = decode_event_topic(topic).await?;
if sigs.is_empty() {
eyre::bail!("No matching event signatures found for topic `{topic}`");
}
Expand Down
29 changes: 12 additions & 17 deletions crates/cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use alloy_network::AnyNetwork;
use alloy_primitives::{
hex,
utils::{keccak256, ParseUnits, Unit},
Address, Keccak256, TxHash, TxKind, B256, I256, U256,
Address, Keccak256, Selector, TxHash, TxKind, B256, I256, U256,
};
use alloy_provider::{
network::eip2718::{Decodable2718, Encodable2718},
Expand Down Expand Up @@ -367,22 +367,16 @@ impl<P: Provider<AnyNetwork>> Cast<P> {
}

async fn block_field_as_num<B: Into<BlockId>>(&self, block: B, field: String) -> Result<U256> {
let block = block.into();
let block_field = Self::block(
Self::block(
self,
block,
block.into(),
false,
// Select only select field
Some(field),
)
.await?;

let ret = if block_field.starts_with("0x") {
U256::from_str_radix(strip_0x(&block_field), 16).expect("Unable to convert hex to U256")
} else {
U256::from_str_radix(&block_field, 10).expect("Unable to convert decimal to U256")
};
Ok(ret)
.await?
.parse()
.map_err(Into::into)
}

pub async fn base_fee<B: Into<BlockId>>(&self, block: B) -> Result<U256> {
Expand Down Expand Up @@ -2132,15 +2126,16 @@ impl SimpleCast {
/// # Example
///
/// ```
/// use alloy_primitives::fixed_bytes;
/// use cast::SimpleCast as Cast;
///
/// let bytecode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80632125b65b14602a575b5f80fd5b603a6035366004603c565b505050565b005b5f805f60608486031215604d575f80fd5b833563ffffffff81168114605f575f80fd5b925060208401356001600160a01b03811681146079575f80fd5b915060408401356001600160e01b03811681146093575f80fd5b80915050925092509256";
/// let functions = Cast::extract_functions(bytecode)?;
/// assert_eq!(functions, vec![("0x2125b65b".to_string(), "uint32,address,uint224".to_string(), "pure")]);
/// assert_eq!(functions, vec![(fixed_bytes!("0x2125b65b"), "uint32,address,uint224".to_string(), "pure")]);
/// # Ok::<(), eyre::Report>(())
/// ```
pub fn extract_functions(bytecode: &str) -> Result<Vec<(String, String, &str)>> {
let code = hex::decode(strip_0x(bytecode))?;
pub fn extract_functions(bytecode: &str) -> Result<Vec<(Selector, String, &str)>> {
let code = hex::decode(bytecode)?;
let info = evmole::contract_info(
evmole::ContractInfoArgs::new(&code)
.with_selectors()
Expand All @@ -2153,7 +2148,7 @@ impl SimpleCast {
.into_iter()
.map(|f| {
(
hex::encode_prefixed(f.selector),
f.selector.into(),
f.arguments
.expect("arguments extraction was requested")
.into_iter()
Expand All @@ -2180,7 +2175,7 @@ impl SimpleCast {
/// let tx_envelope = Cast::decode_raw_transaction(&tx)?;
/// # Ok::<(), eyre::Report>(())
pub fn decode_raw_transaction(tx: &str) -> Result<TxEnvelope> {
let tx_hex = hex::decode(strip_0x(tx))?;
let tx_hex = hex::decode(tx)?;
let tx = TxEnvelope::decode_2718(&mut tx_hex.as_slice())?;
Ok(tx)
}
Expand Down
6 changes: 3 additions & 3 deletions crates/cast/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::cmd::{
mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs,
txpool::TxPoolSubcommands, wallet::WalletSubcommands,
};
use alloy_primitives::{Address, B256, U256};
use alloy_primitives::{Address, Selector, B256, U256};
use alloy_rpc_types::BlockId;
use clap::{Parser, Subcommand, ValueHint};
use eyre::Result;
Expand Down Expand Up @@ -646,7 +646,7 @@ pub enum CastSubcommand {
#[command(name = "4byte", visible_aliases = &["4", "4b"])]
FourByte {
/// The function selector.
selector: Option<String>,
selector: Option<Selector>,
},

/// Decode ABI-encoded calldata using <https://openchain.xyz>.
Expand All @@ -661,7 +661,7 @@ pub enum CastSubcommand {
FourByteEvent {
/// Topic 0
#[arg(value_name = "TOPIC_0")]
topic: Option<String>,
topic: Option<B256>,
},

/// Upload the given signatures to <https://openchain.xyz>.
Expand Down
25 changes: 10 additions & 15 deletions crates/cast/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,23 +459,18 @@ where

/// Helper function that tries to decode custom error name and inputs from error payload data.
async fn decode_execution_revert(data: &RawValue) -> Result<Option<String>> {
if let Some(err_data) = serde_json::from_str::<String>(data.get())?.strip_prefix("0x") {
let Some(selector) = err_data.get(..8) else { return Ok(None) };

if let Some(known_error) = SignaturesIdentifier::new(Config::foundry_cache_dir(), false)?
.write()
.await
.identify_error(&hex::decode(selector)?)
.await
{
let mut decoded_error = known_error.name.clone();
if !known_error.inputs.is_empty() {
if let Ok(error) = known_error.decode_error(&hex::decode(err_data)?) {
write!(decoded_error, "({})", format_tokens(&error.body).format(", "))?;
}
let err_data = serde_json::from_str::<Bytes>(data.get())?;
let Some(selector) = err_data.get(..4) else { return Ok(None) };
if let Some(known_error) =
SignaturesIdentifier::new(false)?.identify_error(selector.try_into().unwrap()).await
{
let mut decoded_error = known_error.name.clone();
if !known_error.inputs.is_empty() {
if let Ok(error) = known_error.decode_error(&err_data) {
write!(decoded_error, "({})", format_tokens(&error.body).format(", "))?;
}
return Ok(Some(decoded_error))
}
return Ok(Some(decoded_error))
}
Ok(None)
}
2 changes: 0 additions & 2 deletions crates/cast/tests/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2179,11 +2179,9 @@ contract CounterInExternalLibScript is Script {
.tx_hash();

// Cache project selectors.
cmd.forge_fuse().set_current_dir(prj.root());
cmd.forge_fuse().args(["selectors", "cache"]).assert_success();

// Assert cast with local artifacts can decode external lib signature.
cmd.cast_fuse().set_current_dir(prj.root());
cmd.cast_fuse()
.args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()])
.assert_success()
Expand Down
4 changes: 3 additions & 1 deletion crates/cast/tests/cli/selectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ transfer(address,uint256)

casttest!(fourbyte_invalid, |_prj, cmd| {
cmd.args(["4byte", "0xa9059c"]).assert_failure().stderr_eq(str![[r#"
Error: Invalid selector 0xa9059c: expected 10 characters (including 0x prefix).
error: invalid value '0xa9059c' for '[SELECTOR]': invalid string length

For more information, try '--help'.

"#]]);
});
Expand Down
9 changes: 4 additions & 5 deletions crates/chisel/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
use alloy_json_abi::{InternalType, JsonAbi};
use alloy_primitives::{hex, Address};
use forge_fmt::FormatterConfig;
use foundry_config::{Config, RpcEndpointUrl};
use foundry_config::RpcEndpointUrl;
use foundry_evm::{
decode::decode_console_logs,
traces::{
Expand Down Expand Up @@ -925,9 +925,8 @@ impl ChiselDispatcher {
) -> eyre::Result<CallTraceDecoder> {
let mut decoder = CallTraceDecoderBuilder::new()
.with_labels(result.labeled_addresses.clone())
.with_signature_identifier(SignaturesIdentifier::new(
Config::foundry_cache_dir(),
session_config.foundry_config.offline,
.with_signature_identifier(SignaturesIdentifier::from_config(
&session_config.foundry_config,
)?)
.build();

Expand Down Expand Up @@ -965,7 +964,7 @@ impl ChiselDispatcher {
for (kind, trace) in &mut result.traces {
// Display all Setup + Execution traces.
if matches!(kind, TraceKind::Setup | TraceKind::Execution) {
decode_trace_arena(trace, decoder).await?;
decode_trace_arena(trace, decoder).await;
sh_println!("{}", render_trace_arena(trace))?;
}
}
Expand Down
53 changes: 20 additions & 33 deletions crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use alloy_json_abi::JsonAbi;
use alloy_primitives::Address;
use eyre::{Result, WrapErr};
use foundry_common::{compile::ProjectCompiler, fs, shell, ContractsByArtifact, TestFunctionExt};
use foundry_common::{
compile::ProjectCompiler, fs, selectors::SelectorKind, shell, ContractsByArtifact,
TestFunctionExt,
};
use foundry_compilers::{
artifacts::{CompactBytecode, Settings},
cache::{CacheEntry, CompilerCache},
Expand All @@ -16,7 +19,7 @@ use foundry_evm::{
traces::{
debug::{ContractSources, DebugTraceIdentifier},
decode_trace_arena,
identifier::{CachedSignatures, SignaturesIdentifier, TraceIdentifiers},
identifier::{SignaturesCache, SignaturesIdentifier, TraceIdentifiers},
render_trace_arena_inner, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces,
},
};
Expand Down Expand Up @@ -364,10 +367,7 @@ pub async fn handle_traces(

let mut builder = CallTraceDecoderBuilder::new()
.with_labels(labels.chain(config_labels))
.with_signature_identifier(SignaturesIdentifier::new(
Config::foundry_cache_dir(),
config.offline,
)?);
.with_signature_identifier(SignaturesIdentifier::from_config(config)?);
let mut identifier = TraceIdentifiers::new().with_etherscan(config, chain)?;
if let Some(contracts) = &known_contracts {
builder = builder.with_known_contracts(contracts);
Expand Down Expand Up @@ -416,7 +416,7 @@ pub async fn print_traces(
}

for (_, arena) in traces {
decode_trace_arena(arena, decoder).await?;
decode_trace_arena(arena, decoder).await;
sh_println!("{}", render_trace_arena_inner(arena, verbose, state_changes))?;
}

Expand All @@ -437,34 +437,21 @@ pub async fn print_traces(

/// Traverse the artifacts in the project to generate local signatures and merge them into the cache
/// file.
pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf) -> Result<()> {
let path = cache_path.join("signatures");
let mut cached_signatures = CachedSignatures::load(cache_path);
output.artifacts().for_each(|(_, artifact)| {
pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_dir: &Path) -> Result<()> {
let path = cache_dir.join("signatures");
let mut signatures = SignaturesCache::load(&path);
for (_, artifact) in output.artifacts() {
if let Some(abi) = &artifact.abi {
for func in abi.functions() {
cached_signatures.functions.insert(func.selector().to_string(), func.signature());
}
for event in abi.events() {
cached_signatures
.events
.insert(event.selector().to_string(), event.full_signature());
}
for error in abi.errors() {
cached_signatures.errors.insert(error.selector().to_string(), error.signature());
}
// External libraries doesn't have functions included in abi, but `methodIdentifiers`.
if let Some(method_identifiers) = &artifact.method_identifiers {
method_identifiers.iter().for_each(|(signature, selector)| {
cached_signatures
.functions
.entry(format!("0x{selector}"))
.or_insert(signature.to_string());
});
}
signatures.extend_from_abi(abi);
}
});

fs::write_json_file(&path, &cached_signatures)?;
// External libraries don't have functions included in the ABI, but `methodIdentifiers`.
if let Some(method_identifiers) = &artifact.method_identifiers {
signatures.extend(method_identifiers.iter().filter_map(|(signature, selector)| {
Some((SelectorKind::Function(selector.parse().ok()?), signature.clone()))
}));
}
}
signatures.save(&path);
Ok(())
}
6 changes: 3 additions & 3 deletions crates/common/src/provider/runtime_transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl RuntimeTransport {
/// Connects the underlying transport, depending on the URL scheme.
pub async fn connect(&self) -> Result<InnerTransport, RuntimeTransportError> {
match self.url.scheme() {
"http" | "https" => self.connect_http().await,
"http" | "https" => self.connect_http(),
"ws" | "wss" => self.connect_ws().await,
"file" => self.connect_ipc().await,
_ => Err(RuntimeTransportError::BadScheme(self.url.scheme().to_string())),
Expand Down Expand Up @@ -190,7 +190,7 @@ impl RuntimeTransport {
}

/// Connects to an HTTP [alloy_transport_http::Http] transport.
async fn connect_http(&self) -> Result<InnerTransport, RuntimeTransportError> {
fn connect_http(&self) -> Result<InnerTransport, RuntimeTransportError> {
let client = self.reqwest_client()?;
Ok(InnerTransport::Http(Http::with_client(client, self.url.clone())))
}
Expand Down Expand Up @@ -351,7 +351,7 @@ mod tests {
let transport = RuntimeTransportBuilder::new(url.clone())
.with_headers(vec!["User-Agent: test-agent".to_string()])
.build();
let inner = transport.connect_http().await.unwrap();
let inner = transport.connect_http().unwrap();

match inner {
InnerTransport::Http(http) => {
Expand Down
Loading