Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cast run): try custom error decode from openchain signature database #8632

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloy_json_abi::JsonAbi;
use alloy_primitives::Address;
use eyre::{Result, WrapErr};
use foundry_common::{cli_warn, fs, TestFunctionExt};
use foundry_common::{cli_warn, fs, selectors::OpenChainClient, TestFunctionExt};
use foundry_compilers::{
artifacts::{CompactBytecode, CompactDeployedBytecode, Settings},
cache::{CacheEntry, CompilerCache},
Expand Down Expand Up @@ -379,6 +379,7 @@ pub async fn handle_traces(
Config::foundry_cache_dir(),
config.offline,
)?)
.with_openchain_client(OpenChainClient::new()?)
.build();

let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?;
Expand Down
6 changes: 5 additions & 1 deletion crates/common/src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! ABI related helper functions.

use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt};
use alloy_json_abi::{Event, Function, Param};
use alloy_json_abi::{Error, Event, Function, Param};
use alloy_primitives::{hex, Address, LogData};
use eyre::{Context, ContextCompat, Result};
use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client};
Expand Down Expand Up @@ -85,6 +85,10 @@ pub fn get_event(sig: &str) -> Result<Event> {
Event::parse(sig).wrap_err("could not parse event signature")
}

pub fn get_error(sig: &str) -> Result<Error> {
Error::parse(sig).wrap_err("could not parse error signature")
}

/// Given an event without indexed parameters and a rawlog, it tries to return the event with the
/// proper indexed parameters. Otherwise, it returns the original event.
pub fn get_indexed_event(mut event: Event, raw_log: &LogData) -> Event {
Expand Down
47 changes: 45 additions & 2 deletions crates/evm/core/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ use alloy_dyn_abi::JsonAbiExt;
use alloy_json_abi::{Error, JsonAbi};
use alloy_primitives::{hex, Log, Selector};
use alloy_sol_types::{SolCall, SolError, SolEventInterface, SolInterface, SolValue};
use foundry_common::SELECTOR_LEN;
use foundry_common::{abi::get_error, selectors::OpenChainClient, SELECTOR_LEN};
use itertools::Itertools;
use revm::interpreter::InstructionResult;
use rustc_hash::FxHashMap;
use std::sync::OnceLock;
use std::{
sync::{mpsc, OnceLock},
thread,
};
use tokio::runtime::Handle;

/// Decode a set of logs, only returning logs from DSTest logging events and Hardhat's `console.log`
pub fn decode_console_logs(logs: &[Log]) -> Vec<String> {
Expand All @@ -29,6 +33,7 @@ pub fn decode_console_log(log: &Log) -> Option<String> {
pub struct RevertDecoder {
/// The custom errors to use for decoding.
pub errors: FxHashMap<Selector, Vec<Error>>,
pub open_chain_client: Option<OpenChainClient>,
}

impl Default for &RevertDecoder {
Expand Down Expand Up @@ -183,6 +188,44 @@ impl RevertDecoder {
std::str::from_utf8(data).map_or_else(|_| trimmed_hex(data), String::from)
))
}

pub fn may_decode_using_open_chain(&self, err: &[u8]) -> Option<String> {
let (selector, data) = err.split_at(SELECTOR_LEN);
// try from https://openchain.xyz
if let Some(client) = self.open_chain_client.clone() {
if let Ok(handle) = Handle::try_current() {
let (tx, rx) = mpsc::channel();
let encoded_selector = hex::encode(selector);
thread::spawn(move || {
let result =
handle.block_on(client.decode_function_selector(&encoded_selector));
tx.send(result).unwrap();
});

let result = match rx.recv() {
Ok(Ok(sigs)) => Some(sigs),
Ok(Err(_)) | Err(_) => None,
};
if let Some(sigs) = result {
for sig in sigs {
if let Ok(error) = get_error(&sig) {
if let Ok(decoded) = error.abi_decode_input(data, true) {
return Some(format!(
"{}({})",
error.name,
decoded
.iter()
.map(foundry_common::fmt::format_token)
.format(", ")
));
}
}
}
}
}
}
None
}
}

fn trimmed_hex(s: &[u8]) -> String {
Expand Down
20 changes: 18 additions & 2 deletions crates/evm/traces/src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt
use alloy_json_abi::{Error, Event, Function, JsonAbi};
use alloy_primitives::{Address, LogData, Selector, B256};
use foundry_common::{
abi::get_indexed_event, fmt::format_token, get_contract_name, ContractsByArtifact, SELECTOR_LEN,
abi::get_indexed_event, fmt::format_token, get_contract_name, selectors::OpenChainClient,
ContractsByArtifact, SELECTOR_LEN,
};
use foundry_evm_core::{
abi::{Console, HardhatConsole, Vm, HARDHAT_CONSOLE_SELECTOR_PATCHES},
Expand Down Expand Up @@ -89,6 +90,13 @@ impl CallTraceDecoderBuilder {
self
}

/// Sets the openchain client.
#[inline]
pub fn with_openchain_client(mut self, client: OpenChainClient) -> Self {
self.decoder.revert_decoder.open_chain_client = Some(client);
self
}

/// Sets the debug identifier for the decoder.
#[inline]
pub fn with_debug_identifier(mut self, identifier: DebugTraceIdentifier) -> Self {
Expand Down Expand Up @@ -565,7 +573,15 @@ impl CallTraceDecoder {

/// The default decoded return data for a trace.
fn default_return_data(&self, trace: &CallTrace) -> Option<String> {
(!trace.success).then(|| self.revert_decoder.decode(&trace.output, Some(trace.status)))
(!trace.success).then(|| {
let err_str = self.revert_decoder.decode(&trace.output, Some(trace.status));
if err_str.contains("custom error") {
if let Some(err) = self.revert_decoder.may_decode_using_open_chain(&trace.output) {
return err;
}
}
err_str
})
}

/// Decodes an event.
Expand Down
Loading