Skip to content

Commit 180f897

Browse files
committed
feat(anvil): add block context overrides for eth_call and eth_estimateGas
This commit implements block context overrides for eth_call and eth_estimateGas RPC methods, allowing users to specify block parameters like timestamp, number, difficulty, etc. for call execution context. Key changes: - BlockOverrides added to EthRequest enum variants for eth_call and eth_estimateGas - Block overrides handling implemented in `anvil::eth::backend::mem::state` as `apply_block_overrides()` function (ported from reth) - Add new error type EvmOverrideError to group state and block override related errors (due to fork mode) - State override handling refactored: - Rename `apply_cached_db_state_override()` to `apply_state_overrides()` - Modify to take mutable `CacheDB` reference instead of creating new instance - Consistent pattern for `apply_state_overrides()` and `apply_block_overrides()`
1 parent 9e5f4b9 commit 180f897

File tree

7 files changed

+135
-96
lines changed

7 files changed

+135
-96
lines changed

crates/anvil/core/src/eth/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use alloy_rpc_types::{
1010
filter::TraceFilter,
1111
geth::{GethDebugTracingCallOptions, GethDebugTracingOptions},
1212
},
13-
BlockId, BlockNumberOrTag as BlockNumber, Filter, Index,
13+
BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides, Filter, Index,
1414
};
1515
use alloy_serde::WithOtherFields;
1616
use foundry_common::serde_helpers::{
@@ -151,6 +151,7 @@ pub enum EthRequest {
151151
WithOtherFields<TransactionRequest>,
152152
#[serde(default)] Option<BlockId>,
153153
#[serde(default)] Option<StateOverride>,
154+
#[serde(default)] Option<Box<BlockOverrides>>,
154155
),
155156

156157
#[serde(rename = "eth_simulateV1")]
@@ -164,6 +165,7 @@ pub enum EthRequest {
164165
WithOtherFields<TransactionRequest>,
165166
#[serde(default)] Option<BlockId>,
166167
#[serde(default)] Option<StateOverride>,
168+
#[serde(default)] Option<Box<BlockOverrides>>,
167169
),
168170

169171
#[serde(rename = "eth_getTransactionByHash", with = "sequence")]

crates/anvil/src/eth/api.rs

+41-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use super::{
2-
backend::mem::{state, BlockRequest, State},
2+
backend::{
3+
db::MaybeFullDatabase,
4+
mem::{state, BlockRequest, State},
5+
},
36
sign::build_typed_transaction,
47
};
58
use crate::{
@@ -54,7 +57,7 @@ use alloy_rpc_types::{
5457
},
5558
request::TransactionRequest,
5659
simulate::{SimulatePayload, SimulatedBlock},
57-
state::StateOverride,
60+
state::EvmOverrides,
5861
trace::{
5962
filter::TraceFilter,
6063
geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace},
@@ -84,7 +87,7 @@ use foundry_evm::{
8487
backend::DatabaseError,
8588
decode::RevertDecoder,
8689
revm::{
87-
db::DatabaseRef,
90+
db::{CacheDB, DatabaseRef},
8891
interpreter::{return_ok, return_revert, InstructionResult},
8992
primitives::BlockEnv,
9093
},
@@ -247,18 +250,20 @@ impl EthApi {
247250
EthRequest::EthSendRawTransaction(tx) => {
248251
self.send_raw_transaction(tx).await.to_rpc_result()
249252
}
250-
EthRequest::EthCall(call, block, overrides) => {
251-
self.call(call, block, overrides).await.to_rpc_result()
252-
}
253+
EthRequest::EthCall(call, block, state_override, block_overrides) => self
254+
.call(call, block, EvmOverrides::new(state_override, block_overrides))
255+
.await
256+
.to_rpc_result(),
253257
EthRequest::EthSimulateV1(simulation, block) => {
254258
self.simulate_v1(simulation, block).await.to_rpc_result()
255259
}
256260
EthRequest::EthCreateAccessList(call, block) => {
257261
self.create_access_list(call, block).await.to_rpc_result()
258262
}
259-
EthRequest::EthEstimateGas(call, block, overrides) => {
260-
self.estimate_gas(call, block, overrides).await.to_rpc_result()
261-
}
263+
EthRequest::EthEstimateGas(call, block, state_override, block_overrides) => self
264+
.estimate_gas(call, block, EvmOverrides::new(state_override, block_overrides))
265+
.await
266+
.to_rpc_result(),
262267
EthRequest::EthGetRawTransactionByHash(hash) => {
263268
self.raw_transaction(hash).await.to_rpc_result()
264269
}
@@ -969,7 +974,8 @@ impl EthApi {
969974

970975
if request.gas.is_none() {
971976
// estimate if not provided
972-
if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await {
977+
if let Ok(gas) = self.estimate_gas(request.clone(), None, EvmOverrides::default()).await
978+
{
973979
request.gas = Some(gas.to());
974980
}
975981
}
@@ -996,7 +1002,8 @@ impl EthApi {
9961002

9971003
if request.gas.is_none() {
9981004
// estimate if not provided
999-
if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await {
1005+
if let Ok(gas) = self.estimate_gas(request.clone(), None, EvmOverrides::default()).await
1006+
{
10001007
request.gas = Some(gas.to());
10011008
}
10021009
}
@@ -1070,16 +1077,16 @@ impl EthApi {
10701077
&self,
10711078
request: WithOtherFields<TransactionRequest>,
10721079
block_number: Option<BlockId>,
1073-
overrides: Option<StateOverride>,
1080+
overrides: EvmOverrides,
10741081
) -> Result<Bytes> {
10751082
node_info!("eth_call");
10761083
let block_request = self.block_request(block_number).await?;
10771084
// check if the number predates the fork, if in fork mode
10781085
if let BlockRequest::Number(number) = block_request {
10791086
if let Some(fork) = self.get_fork() {
10801087
if fork.predates_fork(number) {
1081-
if overrides.is_some() {
1082-
return Err(BlockchainError::StateOverrideError(
1088+
if overrides.has_state() || overrides.has_block() {
1089+
return Err(BlockchainError::EvmOverrideError(
10831090
"not available on past forked blocks".to_string(),
10841091
));
10851092
}
@@ -1201,7 +1208,7 @@ impl EthApi {
12011208
&self,
12021209
request: WithOtherFields<TransactionRequest>,
12031210
block_number: Option<BlockId>,
1204-
overrides: Option<StateOverride>,
1211+
overrides: EvmOverrides,
12051212
) -> Result<U256> {
12061213
node_info!("eth_estimateGas");
12071214
self.do_estimate_gas(
@@ -2082,7 +2089,9 @@ impl EthApi {
20822089

20832090
// Estimate gas
20842091
if tx_req.gas.is_none() {
2085-
if let Ok(gas) = self.estimate_gas(tx_req.clone(), None, None).await {
2092+
if let Ok(gas) =
2093+
self.estimate_gas(tx_req.clone(), None, EvmOverrides::default()).await
2094+
{
20862095
tx_req.gas = Some(gas.to());
20872096
}
20882097
}
@@ -2550,7 +2559,8 @@ impl EthApi {
25502559

25512560
request.from = Some(from);
25522561

2553-
let gas_limit_fut = self.estimate_gas(request.clone(), Some(BlockId::latest()), None);
2562+
let gas_limit_fut =
2563+
self.estimate_gas(request.clone(), Some(BlockId::latest()), EvmOverrides::default());
25542564

25552565
let fees_fut = self.fee_history(
25562566
U256::from(EIP1559_FEE_ESTIMATION_PAST_BLOCKS),
@@ -2651,15 +2661,15 @@ impl EthApi {
26512661
&self,
26522662
request: WithOtherFields<TransactionRequest>,
26532663
block_number: Option<BlockId>,
2654-
overrides: Option<StateOverride>,
2664+
overrides: EvmOverrides,
26552665
) -> Result<u128> {
26562666
let block_request = self.block_request(block_number).await?;
26572667
// check if the number predates the fork, if in fork mode
26582668
if let BlockRequest::Number(number) = block_request {
26592669
if let Some(fork) = self.get_fork() {
26602670
if fork.predates_fork(number) {
2661-
if overrides.is_some() {
2662-
return Err(BlockchainError::StateOverrideError(
2671+
if overrides.has_state() || overrides.has_block() {
2672+
return Err(BlockchainError::EvmOverrideError(
26632673
"not available on past forked blocks".to_string(),
26642674
));
26652675
}
@@ -2672,14 +2682,18 @@ impl EthApi {
26722682
// <https://github.com/foundry-rs/foundry/issues/6036>
26732683
self.on_blocking_task(|this| async move {
26742684
this.backend
2675-
.with_database_at(Some(block_request), |mut state, block| {
2676-
if let Some(overrides) = overrides {
2677-
state = Box::new(state::apply_state_override(
2678-
overrides.into_iter().collect(),
2679-
state,
2680-
)?);
2685+
.with_database_at(Some(block_request), |state, mut block| {
2686+
let mut cache_db = CacheDB::new(state);
2687+
if let Some(state_overrides) = overrides.state {
2688+
state::apply_state_overrides(
2689+
state_overrides.into_iter().collect(),
2690+
&mut cache_db,
2691+
)?;
2692+
}
2693+
if let Some(block_overrides) = overrides.block {
2694+
state::apply_block_overrides(*block_overrides, &mut cache_db, &mut block);
26812695
}
2682-
this.do_estimate_gas_with_state(request, &state, block)
2696+
this.do_estimate_gas_with_state(request, cache_db.as_dyn(), block)
26832697
})
26842698
.await?
26852699
})

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

+27-45
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use alloy_rpc_types::{
5252
request::TransactionRequest,
5353
serde_helpers::JsonStorageKey,
5454
simulate::{SimBlock, SimCallResult, SimulatePayload, SimulatedBlock},
55-
state::StateOverride,
55+
state::EvmOverrides,
5656
trace::{
5757
filter::TraceFilter,
5858
geth::{
@@ -1359,16 +1359,19 @@ impl Backend {
13591359
request: WithOtherFields<TransactionRequest>,
13601360
fee_details: FeeDetails,
13611361
block_request: Option<BlockRequest>,
1362-
overrides: Option<StateOverride>,
1362+
overrides: EvmOverrides,
13631363
) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1364-
self.with_database_at(block_request, |state, block| {
1364+
self.with_database_at(block_request, |state, mut block| {
13651365
let block_number = block.number.to::<u64>();
1366-
let (exit, out, gas, state) = match overrides {
1367-
None => self.call_with_state(state.as_dyn(), request, fee_details, block),
1368-
Some(overrides) => {
1369-
let state = state::apply_state_override(overrides.into_iter().collect(), state)?;
1370-
self.call_with_state(state.as_dyn(), request, fee_details, block)
1371-
},
1366+
let (exit, out, gas, state) = {
1367+
let mut cache_db = CacheDB::new(state);
1368+
if let Some(state_overrides) = overrides.state {
1369+
state::apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
1370+
}
1371+
if let Some(block_overrides) = overrides.block {
1372+
state::apply_block_overrides(*block_overrides, &mut cache_db, &mut block);
1373+
}
1374+
self.call_with_state(cache_db.as_dyn(), request, fee_details, block)
13721375
}?;
13731376
trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
13741377
Ok((exit, out, gas, state))
@@ -1512,34 +1515,12 @@ impl Backend {
15121515
let mut log_index = 0;
15131516
let mut gas_used = 0;
15141517
let mut transactions = Vec::with_capacity(calls.len());
1515-
// apply state overrides before executing the transactions
1518+
// apply state and block overrides before executing the transactions
15161519
if let Some(state_overrides) = state_overrides {
1517-
state::apply_cached_db_state_override(state_overrides, &mut cache_db)?;
1520+
state::apply_state_overrides(state_overrides, &mut cache_db)?;
15181521
}
1519-
1520-
// apply block overrides
1521-
if let Some(overrides) = block_overrides {
1522-
if let Some(number) = overrides.number {
1523-
block_env.number = number.saturating_to();
1524-
}
1525-
if let Some(difficulty) = overrides.difficulty {
1526-
block_env.difficulty = difficulty;
1527-
}
1528-
if let Some(time) = overrides.time {
1529-
block_env.timestamp = U256::from(time);
1530-
}
1531-
if let Some(gas_limit) = overrides.gas_limit {
1532-
block_env.gas_limit = U256::from(gas_limit);
1533-
}
1534-
if let Some(coinbase) = overrides.coinbase {
1535-
block_env.coinbase = coinbase;
1536-
}
1537-
if let Some(random) = overrides.random {
1538-
block_env.prevrandao = Some(random);
1539-
}
1540-
if let Some(base_fee) = overrides.base_fee {
1541-
block_env.basefee = base_fee.saturating_to();
1542-
}
1522+
if let Some(block_overrides) = block_overrides {
1523+
state::apply_block_overrides(block_overrides, &mut cache_db, &mut block_env);
15431524
}
15441525

15451526
// execute all calls in that block
@@ -1740,19 +1721,20 @@ impl Backend {
17401721
block_request: Option<BlockRequest>,
17411722
opts: GethDebugTracingCallOptions,
17421723
) -> Result<GethTrace, BlockchainError> {
1743-
let GethDebugTracingCallOptions { tracing_options, block_overrides: _, state_overrides } =
1724+
let GethDebugTracingCallOptions { tracing_options, block_overrides, state_overrides } =
17441725
opts;
17451726
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
17461727

1747-
self.with_database_at(block_request, |state, block| {
1728+
self.with_database_at(block_request, |state, mut block| {
17481729
let block_number = block.number;
17491730

1750-
let state = if let Some(overrides) = state_overrides {
1751-
Box::new(state::apply_state_override(overrides, state)?)
1752-
as Box<dyn MaybeFullDatabase>
1753-
} else {
1754-
state
1755-
};
1731+
let mut cache_db = CacheDB::new(state);
1732+
if let Some(state_overrides) = state_overrides {
1733+
state::apply_state_overrides(state_overrides, &mut cache_db)?;
1734+
}
1735+
if let Some(block_overrides) = block_overrides {
1736+
state::apply_block_overrides(block_overrides, &mut cache_db, &mut block);
1737+
}
17561738

17571739
if let Some(tracer) = tracer {
17581740
return match tracer {
@@ -1768,7 +1750,7 @@ impl Backend {
17681750

17691751
let env = self.build_call_env(request, fee_details, block);
17701752
let mut evm = self.new_evm_with_inspector_ref(
1771-
state.as_dyn(),
1753+
cache_db.as_dyn(),
17721754
env,
17731755
&mut inspector,
17741756
);
@@ -1803,7 +1785,7 @@ impl Backend {
18031785
.with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
18041786

18051787
let env = self.build_call_env(request, fee_details, block);
1806-
let mut evm = self.new_evm_with_inspector_ref(state.as_dyn(), env, &mut inspector);
1788+
let mut evm = self.new_evm_with_inspector_ref(cache_db.as_dyn(), env, &mut inspector);
18071789
let ResultAndState { result, state: _ } = evm.transact()?;
18081790

18091791
let (exit_reason, gas_used, out) = match result {

0 commit comments

Comments
 (0)