Skip to content

Commit bf72787

Browse files
fix(anvil): forward trace_replayBlockTransactions params correctly (foundry-rs#15813)
1 parent 1a5b52f commit bf72787

2 files changed

Lines changed: 58 additions & 3 deletions

File tree

crates/anvil/src/eth/backend/fork.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,12 @@ impl<N: Network> ClientFork<N> {
397397
number: u64,
398398
trace_types: HashSet<TraceType>,
399399
) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
400-
// Forward to upstream provider for historical blocks
401-
let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
402-
self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
400+
// Forward to upstream provider for historical blocks. Use the typed trace API so the block
401+
// and trace types are serialized in the format upstream providers expect.
402+
self.provider()
403+
.trace_replay_block_transactions(BlockId::number(number))
404+
.trace_types(trace_types)
405+
.await
403406
}
404407

405408
pub async fn trace_replay_transaction(

crates/anvil/tests/it/fork.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use alloy_rpc_types::{
1818
anvil::Forking,
1919
request::{TransactionInput, TransactionRequest},
2020
state::EvmOverrides,
21+
trace::parity::{Action, TraceResultsWithTransactionHash, TraceType},
2122
};
2223
use alloy_serde::WithOtherFields;
2324
use alloy_signer_local::PrivateKeySigner;
@@ -228,6 +229,57 @@ async fn test_fork_debug_account_info_at() {
228229
assert_eq!(by_tag_after.unwrap().balance, amount);
229230
}
230231

232+
// Pre-fork `trace_replayBlockTransactions` must be forwarded to the upstream with the trace types
233+
// serialized as their camelCase JSON names (`trace`, `stateDiff`), not their Rust `Debug`
234+
// representation, otherwise the upstream rejects the request.
235+
#[tokio::test(flavor = "multi_thread")]
236+
async fn test_fork_trace_replay_block_transactions_forwards_trace_types() {
237+
// Use a local anvil node as the upstream so the request path is fully exercised in-process.
238+
let (_origin_api, origin_handle) = spawn(NodeConfig::test()).await;
239+
let origin_provider = origin_handle.http_provider();
240+
241+
let account = origin_handle.dev_wallets().next().unwrap().address();
242+
let to = Address::random();
243+
let amount = U256::from(1_000u64);
244+
245+
// Mine two blocks on the upstream; the first strictly predates the fork head.
246+
let mut first_block = None;
247+
for _ in 0..2 {
248+
let tx = TransactionRequest::default().from(account).to(to).value(amount);
249+
let tx = WithOtherFields::new(tx);
250+
let receipt =
251+
origin_provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
252+
first_block.get_or_insert(receipt.block_number.unwrap());
253+
}
254+
let pre_fork_block = first_block.unwrap();
255+
256+
// Fork from the upstream head so `pre_fork_block` is delegated upstream.
257+
let (_fork_api, fork_handle) =
258+
spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await;
259+
let fork_provider = fork_handle.http_provider();
260+
261+
let results: Vec<TraceResultsWithTransactionHash> = fork_provider
262+
.client()
263+
.request(
264+
"trace_replayBlockTransactions",
265+
(pre_fork_block, vec![TraceType::Trace, TraceType::StateDiff]),
266+
)
267+
.await
268+
.unwrap();
269+
270+
assert_eq!(results.len(), 1);
271+
let full_trace = &results[0].full_trace;
272+
match &full_trace.trace[0].action {
273+
Action::Call(call) => {
274+
assert_eq!(call.from, account);
275+
assert_eq!(call.to, to);
276+
}
277+
other => panic!("expected Call action, got {other:?}"),
278+
}
279+
// `StateDiff` was also requested, so it must be honored, not just `Trace`.
280+
assert!(full_trace.state_diff.is_some());
281+
}
282+
231283
#[tokio::test(flavor = "multi_thread")]
232284
async fn test_spawn_fork() {
233285
let (api, _handle) = spawn(fork_config()).await;

0 commit comments

Comments
 (0)