diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 7ab5ddd511f9..4b26c848ef66 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -10,7 +10,7 @@ use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{handle_traces, init_progress, TraceResult}, }; -use foundry_common::{is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE}; +use foundry_common::{is_impersonated_tx, is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ figment::{ @@ -212,6 +212,12 @@ impl RunArgs { configure_tx_env(&mut env, &tx.inner); + if is_impersonated_tx(&tx.inner.inner) { + // If the transaction is impersonated, we need to set the caller to the from + // address Ref: https://github.com/foundry-rs/foundry/issues/9541 + env.tx.caller = tx.from; + } + if let Some(to) = Transaction::to(tx) { trace!(tx=?tx.tx_hash(),?to, "executing previous call transaction"); executor.transact_with_env(env.clone()).wrap_err_with(|| { @@ -251,6 +257,12 @@ impl RunArgs { configure_tx_env(&mut env, &tx.inner); + if is_impersonated_tx(&tx.inner.inner) { + // If the transaction is impersonated, we need to set the caller to the from address + // Ref: https://github.com/foundry-rs/foundry/issues/9541 + env.tx.caller = tx.from; + } + if let Some(to) = Transaction::to(&tx) { trace!(tx=?tx.tx_hash(), to=?to, "executing call transaction"); TraceResult::try_from(executor.transact_with_env(env))? diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index f2ee99009547..df9842722474 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,9 +1,10 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; -use alloy_network::TransactionResponse; -use alloy_primitives::{b256, B256}; -use alloy_rpc_types::{BlockNumberOrTag, Index}; +use alloy_network::{TransactionBuilder, TransactionResponse}; +use alloy_primitives::{address, b256, Bytes, B256}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::{BlockNumberOrTag, Index, TransactionRequest}; use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ casttest, file, forgetest, forgetest_async, @@ -1995,3 +1996,37 @@ forgetest_async!(cast_call_custom_chain_id, |_prj, cmd| { ]) .assert_success(); }); + +// https://github.com/foundry-rs/foundry/issues/9541 +forgetest_async!(cast_run_impersonated_tx, |_prj, cmd| { + let (_api, handle) = anvil::spawn( + NodeConfig::test() + .with_auto_impersonate(true) + .with_eth_rpc_url(Some("https://sepolia.base.org")), + ) + .await; + + let http_endpoint = handle.http_endpoint(); + + let provider = ProviderBuilder::new().on_http(http_endpoint.parse().unwrap()); + + // send impersonated tx + let tx = TransactionRequest::default() + .with_from(address!("041563c07028Fc89106788185763Fc73028e8511")) + .with_to(address!("F38aA5909D89F5d98fCeA857e708F6a6033f6CF8")) + .with_input( + Bytes::from_str( + "0x60fe47b1000000000000000000000000000000000000000000000000000000000000000c", + ) + .unwrap(), + ); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert!(receipt.status()); + + // run impersonated tx + cmd.cast_fuse() + .args(["run", &receipt.transaction_hash.to_string(), "--rpc-url", &http_endpoint]) + .assert_success(); +}); diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index 4ff3eb8d70fb..c67131585ba2 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -1,6 +1,8 @@ //! Commonly used constants. -use alloy_primitives::{address, Address}; +use alloy_consensus::Typed2718; +use alloy_network::AnyTxEnvelope; +use alloy_primitives::{address, Address, PrimitiveSignature, B256}; use std::time::Duration; /// The dev chain-id, inherited from hardhat @@ -53,6 +55,25 @@ pub fn is_known_system_sender(sender: Address) -> bool { [ARBITRUM_SENDER, OPTIMISM_SYSTEM_ADDRESS].contains(&sender) } +pub fn is_impersonated_tx(tx: &AnyTxEnvelope) -> bool { + if let AnyTxEnvelope::Ethereum(tx) = tx { + return is_impersonated_sig(tx.signature(), tx.ty()); + } + false +} + +pub fn is_impersonated_sig(sig: &PrimitiveSignature, ty: u8) -> bool { + let impersonated_sig = PrimitiveSignature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + false, + ); + if ty != SYSTEM_TRANSACTION_TYPE && sig == &impersonated_sig { + return true; + } + false +} + #[cfg(test)] mod tests { use super::*;