Skip to content

Commit 2cb8757

Browse files
authored
added arg and computing state in function of some overrides (#6985)
* added arg and computing state in function of some overrides * review * corrected H160 to alloy address
1 parent 0a883bf commit 2cb8757

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ pub enum EthRequest {
176176
),
177177

178178
#[cfg_attr(feature = "serde", serde(rename = "eth_estimateGas"))]
179-
EthEstimateGas(CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option<BlockId>),
179+
EthEstimateGas(
180+
CallRequest,
181+
#[cfg_attr(feature = "serde", serde(default))] Option<BlockId>,
182+
#[cfg_attr(feature = "serde", serde(default))] Option<StateOverride>,
183+
),
180184

181185
#[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionByHash", with = "sequence"))]
182186
EthGetTransactionByHash(TxHash),

crates/anvil/src/eth/api.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::{backend::mem::BlockRequest, sign::build_typed_transaction};
1+
use super::{
2+
backend::mem::{state, BlockRequest},
3+
sign::build_typed_transaction,
4+
};
25
use crate::{
36
eth::{
47
backend,
@@ -228,8 +231,8 @@ impl EthApi {
228231
EthRequest::EthCreateAccessList(call, block) => {
229232
self.create_access_list(call, block).await.to_rpc_result()
230233
}
231-
EthRequest::EthEstimateGas(call, block) => {
232-
self.estimate_gas(call, block).await.to_rpc_result()
234+
EthRequest::EthEstimateGas(call, block, overrides) => {
235+
self.estimate_gas(call, block, overrides).await.to_rpc_result()
233236
}
234237
EthRequest::EthGetTransactionByBlockHashAndIndex(hash, index) => {
235238
self.transaction_by_block_hash_and_index(hash, index).await.to_rpc_result()
@@ -860,7 +863,9 @@ impl EthApi {
860863

861864
if request.gas.is_none() {
862865
// estimate if not provided
863-
if let Ok(gas) = self.estimate_gas(request.clone().into_call_request(), None).await {
866+
if let Ok(gas) =
867+
self.estimate_gas(request.clone().into_call_request(), None, None).await
868+
{
864869
request.gas = Some(gas);
865870
}
866871
}
@@ -886,7 +891,9 @@ impl EthApi {
886891

887892
if request.gas.is_none() {
888893
// estimate if not provided
889-
if let Ok(gas) = self.estimate_gas(request.clone().into_call_request(), None).await {
894+
if let Ok(gas) =
895+
self.estimate_gas(request.clone().into_call_request(), None, None).await
896+
{
890897
request.gas = Some(gas);
891898
}
892899
}
@@ -1081,10 +1088,15 @@ impl EthApi {
10811088
&self,
10821089
request: CallRequest,
10831090
block_number: Option<BlockId>,
1091+
overrides: Option<StateOverride>,
10841092
) -> Result<U256> {
10851093
node_info!("eth_estimateGas");
1086-
self.do_estimate_gas(request, block_number.or_else(|| Some(BlockNumber::Pending.into())))
1087-
.await
1094+
self.do_estimate_gas(
1095+
request,
1096+
block_number.or_else(|| Some(BlockNumber::Pending.into())),
1097+
overrides,
1098+
)
1099+
.await
10881100
}
10891101

10901102
/// Get transaction by its hash.
@@ -2144,12 +2156,18 @@ impl EthApi {
21442156
&self,
21452157
request: CallRequest,
21462158
block_number: Option<BlockId>,
2159+
overrides: Option<StateOverride>,
21472160
) -> Result<U256> {
21482161
let block_request = self.block_request(block_number).await?;
21492162
// check if the number predates the fork, if in fork mode
21502163
if let BlockRequest::Number(number) = block_request {
21512164
if let Some(fork) = self.get_fork() {
21522165
if fork.predates_fork(number) {
2166+
if overrides.is_some() {
2167+
return Err(BlockchainError::StateOverrideError(
2168+
"not available on past forked blocks".to_string(),
2169+
));
2170+
}
21532171
return fork
21542172
.estimate_gas(&request, Some(number.into()))
21552173
.await
@@ -2159,7 +2177,13 @@ impl EthApi {
21592177
}
21602178

21612179
self.backend
2162-
.with_database_at(Some(block_request), |state, block| {
2180+
.with_database_at(Some(block_request), |mut state, block| {
2181+
if let Some(overrides) = overrides {
2182+
state = Box::new(state::apply_state_override(
2183+
overrides.into_iter().collect(),
2184+
state,
2185+
)?);
2186+
}
21632187
self.do_estimate_gas_with_state(request, state, block)
21642188
})
21652189
.await?

crates/anvil/tests/it/transaction.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use crate::{
33
utils::{ethers_http_provider, ethers_ws_provider},
44
};
55
use alloy_primitives::U256 as rU256;
6-
use alloy_rpc_types::BlockNumberOrTag;
6+
use alloy_rpc_types::{
7+
state::{AccountOverride, StateOverride},
8+
BlockNumberOrTag,
9+
};
710
use alloy_signer::Signer as AlloySigner;
811
use anvil::{spawn, Hardfork, NodeConfig};
912
use anvil_core::eth::transaction::EthTransactionRequest;
@@ -961,7 +964,49 @@ async fn estimates_gas_on_pending_by_default() {
961964

962965
let tx =
963966
TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]);
964-
api.estimate_gas(to_call_request_from_tx_request(tx), None).await.unwrap();
967+
api.estimate_gas(to_call_request_from_tx_request(tx), None, None).await.unwrap();
968+
}
969+
970+
#[tokio::test(flavor = "multi_thread")]
971+
async fn test_estimate_gas() {
972+
let (api, handle) = spawn(NodeConfig::test()).await;
973+
974+
let wallet = handle.dev_wallets().next().unwrap().to_ethers();
975+
let sender = wallet.address();
976+
let recipient = Address::random();
977+
978+
let tx =
979+
TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]);
980+
// Expect the gas estimation to fail due to insufficient funds.
981+
let error_result =
982+
api.estimate_gas(to_call_request_from_tx_request(tx.clone()), None, None).await;
983+
984+
assert!(error_result.is_err(), "Expected an error due to insufficient funds");
985+
let error_message = error_result.unwrap_err().to_string();
986+
assert!(
987+
error_message.contains("Insufficient funds for gas * price + value"),
988+
"Error message did not match expected: {}",
989+
error_message
990+
);
991+
992+
// Setup state override to simulate sufficient funds for the recipient.
993+
let addr = alloy_primitives::Address::from_slice(recipient.as_bytes());
994+
let account_override =
995+
AccountOverride { balance: Some(alloy_primitives::U256::from(1e18)), ..Default::default() };
996+
let mut state_override = StateOverride::new();
997+
state_override.insert(addr, account_override);
998+
999+
// Estimate gas with state override implying sufficient funds.
1000+
let gas_estimate = api
1001+
.estimate_gas(to_call_request_from_tx_request(tx), None, Some(state_override))
1002+
.await
1003+
.expect("Failed to estimate gas with state override");
1004+
1005+
// Assert the gas estimate meets the expected minimum.
1006+
assert!(
1007+
gas_estimate >= alloy_primitives::U256::from(21000),
1008+
"Gas estimate is lower than expected minimum"
1009+
);
9651010
}
9661011

9671012
#[tokio::test(flavor = "multi_thread")]

0 commit comments

Comments
 (0)