Skip to content

Commit 38184bf

Browse files
committed
fix(anvil): coerce decoder hardfork to executed network family
A cross-namespace hardfork override on a tempo or monad node (e.g. --hardfork prague with an inferred fork network) left the trace decoder without a network hardfork while execution still coerced via the lossy From conversions, dropping monad metadata and widening the tempo precompile set. The decoder now derives its hardfork through the same coercion execution applies.
1 parent e1d41a7 commit 38184bf

4 files changed

Lines changed: 50 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
anvil: patch
3+
---
4+
5+
Fixed anvil trace decoding to follow the executed network hardfork when a cross-namespace hardfork override is configured.

crates/anvil/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,7 @@ impl NodeConfig {
14221422
.unwrap_or_else(|| self.get_hardfork());
14231423
let mut decoder_builder = CallTraceDecoderBuilder::new()
14241424
.with_networks(self.networks)
1425-
.with_hardfork(Some(active_hardfork));
1425+
.with_hardfork(Some(self.networks.executed_hardfork(active_hardfork)));
14261426
if self.print_traces {
14271427
// if traces should get printed we configure the decoder with the signatures cache
14281428
if let Ok(identifier) = SignaturesIdentifier::new(false) {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,7 @@ impl<N: Network> Backend<N> {
14901490

14911491
/// Returns a trace decoder configured for the currently resolved hardfork.
14921492
fn call_trace_decoder(&self) -> Arc<CallTraceDecoder> {
1493-
let hardfork = Some(self.hardfork());
1493+
let hardfork = Some(self.networks.executed_hardfork(self.hardfork()));
14941494
let decoder = self.call_trace_decoder.read();
14951495
if decoder.hardfork() == hardfork {
14961496
return Arc::clone(&decoder);
@@ -9883,6 +9883,20 @@ mod tests {
98839883
assert_eq!(outcome.block_number, original_best_number + 1);
98849884
}
98859885

9886+
#[tokio::test]
9887+
async fn trace_decoder_follows_executed_hardfork_for_cross_namespace_override() {
9888+
let (api, _) = spawn(
9889+
NodeConfig::test_tempo()
9890+
.with_hardfork(Some(FoundryHardfork::Ethereum(EthereumHardfork::Prague))),
9891+
)
9892+
.await;
9893+
9894+
let decoder = api.backend.call_trace_decoder();
9895+
assert_eq!(decoder.hardfork(), Some(FoundryHardfork::Tempo(api.backend.tempo_hardfork())));
9896+
// The refresh compares the same coerced value, so repeated calls stay stable.
9897+
assert!(Arc::ptr_eq(&decoder, &api.backend.call_trace_decoder()));
9898+
}
9899+
98869900
#[cfg(feature = "monad")]
98879901
#[tokio::test]
98889902
async fn monad_trace_decoder_follows_resolved_hardfork() {

crates/evm/networks/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,23 @@ impl NetworkConfigs {
461461
false
462462
}
463463

464+
/// Coerces `hardfork` into this network's family.
465+
///
466+
/// Execution and fee rules apply the same lossy `From` conversions, so a cross-namespace
467+
/// override such as `--hardfork prague` on a Tempo node runs as a Tempo hardfork. Callers that
468+
/// need to describe what actually executed, like trace decoding, go through here rather than
469+
/// carrying the configured value.
470+
pub fn executed_hardfork(&self, hardfork: FoundryHardfork) -> FoundryHardfork {
471+
if self.is_tempo() {
472+
return TempoHardfork::from(hardfork).into();
473+
}
474+
#[cfg(feature = "monad")]
475+
if self.is_monad() {
476+
return MonadHardfork::from(hardfork).into();
477+
}
478+
hardfork
479+
}
480+
464481
/// Returns additional cheatcode contract addresses for the active network.
465482
pub const fn extra_cheatcode_addresses(&self) -> &'static [Address] {
466483
#[cfg(feature = "monad")]
@@ -1789,4 +1806,16 @@ mod tests {
17891806
assert!(cfg_optimism.is_optimism());
17901807
}
17911808
}
1809+
1810+
#[test]
1811+
fn executed_hardfork_follows_the_network_family() {
1812+
// A cross-namespace override runs as the configured network's hardfork, so the value used
1813+
// to describe execution has to be coerced the same way.
1814+
let prague = FoundryHardfork::Ethereum(EthereumHardfork::Prague);
1815+
assert_eq!(NetworkConfigs::default().executed_hardfork(prague), prague);
1816+
assert_eq!(
1817+
NetworkConfigs::with_tempo().executed_hardfork(prague),
1818+
FoundryHardfork::Tempo(TempoHardfork::from(prague))
1819+
);
1820+
}
17921821
}

0 commit comments

Comments
 (0)