Skip to content

Commit b68c7fc

Browse files
authored
Merge branch 'foundry-rs:master' into 5-bug-release-workflow-failed
2 parents 15758c0 + ac81a53 commit b68c7fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3060
-1989
lines changed

Cargo.lock

Lines changed: 322 additions & 282 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ alloy-transport = { version = "0.6.4", default-features = false }
205205
alloy-transport-http = { version = "0.6.4", default-features = false }
206206
alloy-transport-ipc = { version = "0.6.4", default-features = false }
207207
alloy-transport-ws = { version = "0.6.4", default-features = false }
208+
alloy-node-bindings = { version = "0.6.4", default-features = false }
208209

209210
## alloy-core
210211
alloy-dyn-abi = "0.8.11"

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3013,11 +3013,8 @@ pub fn prove_storage(storage: &HashMap<U256, U256>, keys: &[B256]) -> Vec<Vec<By
30133013
}
30143014

30153015
pub fn is_arbitrum(chain_id: u64) -> bool {
3016-
matches!(
3017-
NamedChain::try_from(chain_id),
3018-
Ok(NamedChain::Arbitrum |
3019-
NamedChain::ArbitrumTestnet |
3020-
NamedChain::ArbitrumGoerli |
3021-
NamedChain::ArbitrumNova)
3022-
)
3016+
if let Ok(chain) = NamedChain::try_from(chain_id) {
3017+
return chain.is_arbitrum()
3018+
}
3019+
false
30233020
}

crates/anvil/src/eth/otterscan/api.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn mentions_address(trace: LocalizedTransactionTrace, address: Address) -> O
4646

4747
/// Converts the list of traces for a transaction into the expected Otterscan format.
4848
///
49-
/// Follows format specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_tracetransaction) spec.
49+
/// Follows format specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_tracetransaction) spec.
5050
pub fn batch_build_ots_traces(traces: Vec<LocalizedTransactionTrace>) -> Vec<TraceEntry> {
5151
traces
5252
.into_iter()
@@ -350,7 +350,7 @@ impl EthApi {
350350
/// their `gas_used`. This would be extremely inefficient in a real blockchain RPC, but we can
351351
/// get away with that in this context.
352352
///
353-
/// The [original spec](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails)
353+
/// The [original spec](https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_getblockdetails)
354354
/// also mentions we can hardcode `transactions` and `logsBloom` to an empty array to save
355355
/// bandwidth, because fields weren't intended to be used in the Otterscan UI at this point.
356356
///
@@ -402,7 +402,7 @@ impl EthApi {
402402
/// Fetches all receipts for the blocks's transactions, as required by the
403403
/// [`ots_getBlockTransactions`] endpoint spec, and returns the final response object.
404404
///
405-
/// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails
405+
/// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/main/docs/custom-jsonrpc.md#ots_getblockdetails
406406
pub async fn build_ots_block_tx(
407407
&self,
408408
mut block: AnyRpcBlock,

crates/cast/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ tikv-jemallocator = { workspace = true, optional = true }
9494
[dev-dependencies]
9595
anvil.workspace = true
9696
foundry-test-utils.workspace = true
97+
alloy-node-bindings.workspace = true
9798

9899
async-trait.workspace = true
99100
divan.workspace = true

crates/cast/bin/args.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,8 @@ pub enum CastSubcommand {
512512
///
513513
/// Similar to `abi-decode --input`, but function selector MUST be prefixed in `calldata`
514514
/// string
515-
#[command(visible_aliases = &["--calldata-decode", "cdd"])]
516-
CalldataDecode {
515+
#[command(visible_aliases = &["calldata-decode", "--calldata-decode", "cdd"])]
516+
DecodeCalldata {
517517
/// The function signature in the format `<name>(<in-types>)(<out-types>)`.
518518
sig: String,
519519

@@ -524,19 +524,39 @@ pub enum CastSubcommand {
524524
/// Decode ABI-encoded string.
525525
///
526526
/// Similar to `calldata-decode --input`, but the function argument is a `string`
527-
#[command(visible_aliases = &["--string-decode", "sd"])]
528-
StringDecode {
527+
#[command(visible_aliases = &["string-decode", "--string-decode", "sd"])]
528+
DecodeString {
529529
/// The ABI-encoded string.
530530
data: String,
531531
},
532532

533+
/// Decode event data.
534+
#[command(visible_aliases = &["event-decode", "--event-decode", "ed"])]
535+
DecodeEvent {
536+
/// The event signature. If none provided then tries to decode from local cache or `https://api.openchain.xyz`.
537+
#[arg(long, visible_alias = "event-sig")]
538+
sig: Option<String>,
539+
/// The event data to decode.
540+
data: String,
541+
},
542+
543+
/// Decode custom error data.
544+
#[command(visible_aliases = &["error-decode", "--error-decode", "erd"])]
545+
DecodeError {
546+
/// The error signature. If none provided then tries to decode from local cache or `https://api.openchain.xyz`.
547+
#[arg(long, visible_alias = "error-sig")]
548+
sig: Option<String>,
549+
/// The error data to decode.
550+
data: String,
551+
},
552+
533553
/// Decode ABI-encoded input or output data.
534554
///
535555
/// Defaults to decoding output data. To decode input data pass --input.
536556
///
537557
/// When passing `--input`, function selector must NOT be prefixed in `calldata` string
538-
#[command(name = "abi-decode", visible_aliases = &["ad", "--abi-decode"])]
539-
AbiDecode {
558+
#[command(name = "decode-abi", visible_aliases = &["abi-decode", "--abi-decode", "ad"])]
559+
DecodeAbi {
540560
/// The function signature in the format `<name>(<in-types>)(<out-types>)`.
541561
sig: String,
542562

crates/cast/bin/cmd/call.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use foundry_cli::{
88
opts::{EthereumOpts, TransactionOpts},
99
utils::{self, handle_traces, parse_ether_value, TraceResult},
1010
};
11-
use foundry_common::ens::NameOrAddress;
11+
use foundry_common::{ens::NameOrAddress, shell};
1212
use foundry_compilers::artifacts::EvmVersion;
1313
use foundry_config::{
1414
figment::{
@@ -18,7 +18,11 @@ use foundry_config::{
1818
},
1919
Config,
2020
};
21-
use foundry_evm::{executors::TracingExecutor, opts::EvmOpts};
21+
use foundry_evm::{
22+
executors::TracingExecutor,
23+
opts::EvmOpts,
24+
traces::{InternalTraceMode, TraceMode},
25+
};
2226
use std::str::FromStr;
2327

2428
/// CLI arguments for `cast call`.
@@ -175,15 +179,30 @@ impl CallArgs {
175179
config.fork_block_number = Some(block_number);
176180
}
177181

182+
let create2_deployer = evm_opts.create2_deployer;
178183
let (mut env, fork, chain, alphanet) =
179184
TracingExecutor::get_fork_material(&config, evm_opts).await?;
180185

181186
// modify settings that usually set in eth_call
182187
env.cfg.disable_block_gas_limit = true;
183188
env.block.gas_limit = U256::MAX;
184189

185-
let mut executor =
186-
TracingExecutor::new(env, fork, evm_version, debug, decode_internal, alphanet);
190+
let trace_mode = TraceMode::Call
191+
.with_debug(debug)
192+
.with_decode_internal(if decode_internal {
193+
InternalTraceMode::Full
194+
} else {
195+
InternalTraceMode::None
196+
})
197+
.with_state_changes(shell::verbosity() > 4);
198+
let mut executor = TracingExecutor::new(
199+
env,
200+
fork,
201+
evm_version,
202+
trace_mode,
203+
alphanet,
204+
create2_deployer,
205+
);
187206

188207
let value = tx.value.unwrap_or_default();
189208
let input = tx.inner.input.into_input().unwrap_or_default();

crates/cast/bin/cmd/run.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use foundry_cli::{
1010
opts::{EtherscanOpts, RpcOpts},
1111
utils::{handle_traces, init_progress, TraceResult},
1212
};
13-
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
13+
use foundry_common::{is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE};
1414
use foundry_compilers::artifacts::EvmVersion;
1515
use foundry_config::{
1616
figment::{
@@ -23,6 +23,7 @@ use foundry_config::{
2323
use foundry_evm::{
2424
executors::{EvmError, TracingExecutor},
2525
opts::EvmOpts,
26+
traces::{InternalTraceMode, TraceMode},
2627
utils::configure_tx_env,
2728
};
2829

@@ -107,11 +108,9 @@ impl RunArgs {
107108
let compute_units_per_second =
108109
if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second };
109110

110-
let provider = foundry_common::provider::ProviderBuilder::new(
111-
&config.get_rpc_url_or_localhost_http()?,
112-
)
113-
.compute_units_per_second_opt(compute_units_per_second)
114-
.build()?;
111+
let provider = foundry_cli::utils::get_provider_builder(&config)?
112+
.compute_units_per_second_opt(compute_units_per_second)
113+
.build()?;
115114

116115
let tx_hash = self.tx_hash.parse().wrap_err("invalid tx hash")?;
117116
let tx = provider
@@ -138,6 +137,7 @@ impl RunArgs {
138137
// we need to fork off the parent block
139138
config.fork_block_number = Some(tx_block_number - 1);
140139

140+
let create2_deployer = evm_opts.create2_deployer;
141141
let (mut env, fork, chain, alphanet) =
142142
TracingExecutor::get_fork_material(&config, evm_opts).await?;
143143

@@ -163,20 +163,30 @@ impl RunArgs {
163163
}
164164
}
165165

166+
let trace_mode = TraceMode::Call
167+
.with_debug(self.debug)
168+
.with_decode_internal(if self.decode_internal {
169+
InternalTraceMode::Full
170+
} else {
171+
InternalTraceMode::None
172+
})
173+
.with_state_changes(shell::verbosity() > 4);
166174
let mut executor = TracingExecutor::new(
167175
env.clone(),
168176
fork,
169177
evm_version,
170-
self.debug,
171-
self.decode_internal,
178+
trace_mode,
172179
alphanet,
180+
create2_deployer,
173181
);
174182
let mut env =
175183
EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id());
176184

177185
// Set the state to the moment right before the transaction
178186
if !self.quick {
179-
sh_println!("Executing previous transactions from the block.")?;
187+
if !shell::is_json() {
188+
sh_println!("Executing previous transactions from the block.")?;
189+
}
180190

181191
if let Some(block) = block {
182192
let pb = init_progress(block.transactions.len() as u64, "tx");

crates/cast/bin/main.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[macro_use]
22
extern crate tracing;
33

4-
use alloy_dyn_abi::DynSolValue;
4+
use alloy_dyn_abi::{DynSolValue, ErrorExt, EventExt};
55
use alloy_primitives::{eip191_hash_message, hex, keccak256, Address, B256};
66
use alloy_provider::Provider;
77
use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest};
@@ -11,7 +11,7 @@ use clap_complete::generate;
1111
use eyre::Result;
1212
use foundry_cli::{handler, utils};
1313
use foundry_common::{
14-
abi::get_event,
14+
abi::{get_error, get_event},
1515
ens::{namehash, ProviderEnsExt},
1616
fmt::{format_tokens, format_tokens_raw, format_uint_exp},
1717
fs,
@@ -30,6 +30,7 @@ pub mod cmd;
3030
pub mod tx;
3131

3232
use args::{Cast as CastArgs, CastSubcommand, ToBaseArgs};
33+
use cast::traces::identifier::SignaturesIdentifier;
3334

3435
#[macro_use]
3536
extern crate foundry_common;
@@ -189,7 +190,7 @@ async fn main_args(args: CastArgs) -> Result<()> {
189190
}
190191

191192
// ABI encoding & decoding
192-
CastSubcommand::AbiDecode { sig, calldata, input } => {
193+
CastSubcommand::DecodeAbi { sig, calldata, input } => {
193194
let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
194195
print_tokens(&tokens);
195196
}
@@ -200,17 +201,65 @@ async fn main_args(args: CastArgs) -> Result<()> {
200201
sh_println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?)?
201202
}
202203
}
203-
CastSubcommand::CalldataDecode { sig, calldata } => {
204+
CastSubcommand::DecodeCalldata { sig, calldata } => {
204205
let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?;
205206
print_tokens(&tokens);
206207
}
207208
CastSubcommand::CalldataEncode { sig, args } => {
208209
sh_println!("{}", SimpleCast::calldata_encode(sig, &args)?)?;
209210
}
210-
CastSubcommand::StringDecode { data } => {
211+
CastSubcommand::DecodeString { data } => {
211212
let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?;
212213
print_tokens(&tokens);
213214
}
215+
CastSubcommand::DecodeEvent { sig, data } => {
216+
let decoded_event = if let Some(event_sig) = sig {
217+
get_event(event_sig.as_str())?.decode_log_parts(None, &hex::decode(data)?, false)?
218+
} else {
219+
let data = data.strip_prefix("0x").unwrap_or(data.as_str());
220+
let selector = data.get(..64).unwrap_or_default();
221+
let identified_event =
222+
SignaturesIdentifier::new(Config::foundry_cache_dir(), false)?
223+
.write()
224+
.await
225+
.identify_event(&hex::decode(selector)?)
226+
.await;
227+
if let Some(event) = identified_event {
228+
let _ = sh_println!("{}", event.signature());
229+
let data = data.get(64..).unwrap_or_default();
230+
get_event(event.signature().as_str())?.decode_log_parts(
231+
None,
232+
&hex::decode(data)?,
233+
false,
234+
)?
235+
} else {
236+
eyre::bail!("No matching event signature found for selector `{selector}`")
237+
}
238+
};
239+
print_tokens(&decoded_event.body);
240+
}
241+
CastSubcommand::DecodeError { sig, data } => {
242+
let error = if let Some(err_sig) = sig {
243+
get_error(err_sig.as_str())?
244+
} else {
245+
let data = data.strip_prefix("0x").unwrap_or(data.as_str());
246+
let selector = data.get(..8).unwrap_or_default();
247+
let identified_error =
248+
SignaturesIdentifier::new(Config::foundry_cache_dir(), false)?
249+
.write()
250+
.await
251+
.identify_error(&hex::decode(selector)?)
252+
.await;
253+
if let Some(error) = identified_error {
254+
let _ = sh_println!("{}", error.signature());
255+
error
256+
} else {
257+
eyre::bail!("No matching error signature found for selector `{selector}`")
258+
}
259+
};
260+
let decoded_error = error.decode_error(&hex::decode(data)?)?;
261+
print_tokens(&decoded_error.body);
262+
}
214263
CastSubcommand::Interface(cmd) => cmd.run().await?,
215264
CastSubcommand::CreationCode(cmd) => cmd.run().await?,
216265
CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?,

crates/cast/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ impl SimpleCast {
20172017
pub fn disassemble(code: &[u8]) -> Result<String> {
20182018
let mut output = String::new();
20192019

2020-
for step in decode_instructions(code) {
2020+
for step in decode_instructions(code)? {
20212021
write!(output, "{:08x}: ", step.pc)?;
20222022

20232023
if let Some(op) = step.op {
@@ -2290,4 +2290,23 @@ mod tests {
22902290
r#"["0x2b5df5f0757397573e8ff34a8b987b21680357de1f6c8d10273aa528a851eaca","0x","0x","0x2838ac1d2d2721ba883169179b48480b2ba4f43d70fcf806956746bd9e83f903","0x","0xe46fff283b0ab96a32a7cc375cecc3ed7b6303a43d64e0a12eceb0bc6bd87549","0x","0x1d818c1c414c665a9c9a0e0c0ef1ef87cacb380b8c1f6223cb2a68a4b2d023f5","0x","0x","0x","0x236e8f61ecde6abfebc6c529441f782f62469d8a2cc47b7aace2c136bd3b1ff0","0x","0x","0x","0x","0x"]"#
22912291
)
22922292
}
2293+
2294+
#[test]
2295+
fn disassemble_incomplete_sequence() {
2296+
let incomplete = &hex!("60"); // PUSH1
2297+
let disassembled = Cast::disassemble(incomplete);
2298+
assert!(disassembled.is_err());
2299+
2300+
let complete = &hex!("6000"); // PUSH1 0x00
2301+
let disassembled = Cast::disassemble(complete);
2302+
assert!(disassembled.is_ok());
2303+
2304+
let incomplete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 31 bytes
2305+
let disassembled = Cast::disassemble(incomplete);
2306+
assert!(disassembled.is_err());
2307+
2308+
let complete = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // PUSH32 with 32 bytes
2309+
let disassembled = Cast::disassemble(complete);
2310+
assert!(disassembled.is_ok());
2311+
}
22932312
}

0 commit comments

Comments
 (0)