Skip to content

Commit

Permalink
added arg and computing state in function of some overrides (#6985)
Browse files Browse the repository at this point in the history
* added arg and computing state in function of some overrides

* review

* corrected H160 to alloy address
  • Loading branch information
loocapro authored Feb 2, 2024
1 parent 0a883bf commit 2cb8757
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 11 deletions.
6 changes: 5 additions & 1 deletion crates/anvil/core/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ pub enum EthRequest {
),

#[cfg_attr(feature = "serde", serde(rename = "eth_estimateGas"))]
EthEstimateGas(CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option<BlockId>),
EthEstimateGas(
CallRequest,
#[cfg_attr(feature = "serde", serde(default))] Option<BlockId>,
#[cfg_attr(feature = "serde", serde(default))] Option<StateOverride>,
),

#[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionByHash", with = "sequence"))]
EthGetTransactionByHash(TxHash),
Expand Down
40 changes: 32 additions & 8 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use super::{backend::mem::BlockRequest, sign::build_typed_transaction};
use super::{
backend::mem::{state, BlockRequest},
sign::build_typed_transaction,
};
use crate::{
eth::{
backend,
Expand Down Expand Up @@ -228,8 +231,8 @@ impl EthApi {
EthRequest::EthCreateAccessList(call, block) => {
self.create_access_list(call, block).await.to_rpc_result()
}
EthRequest::EthEstimateGas(call, block) => {
self.estimate_gas(call, block).await.to_rpc_result()
EthRequest::EthEstimateGas(call, block, overrides) => {
self.estimate_gas(call, block, overrides).await.to_rpc_result()
}
EthRequest::EthGetTransactionByBlockHashAndIndex(hash, index) => {
self.transaction_by_block_hash_and_index(hash, index).await.to_rpc_result()
Expand Down Expand Up @@ -860,7 +863,9 @@ impl EthApi {

if request.gas.is_none() {
// estimate if not provided
if let Ok(gas) = self.estimate_gas(request.clone().into_call_request(), None).await {
if let Ok(gas) =
self.estimate_gas(request.clone().into_call_request(), None, None).await
{
request.gas = Some(gas);
}
}
Expand All @@ -886,7 +891,9 @@ impl EthApi {

if request.gas.is_none() {
// estimate if not provided
if let Ok(gas) = self.estimate_gas(request.clone().into_call_request(), None).await {
if let Ok(gas) =
self.estimate_gas(request.clone().into_call_request(), None, None).await
{
request.gas = Some(gas);
}
}
Expand Down Expand Up @@ -1081,10 +1088,15 @@ impl EthApi {
&self,
request: CallRequest,
block_number: Option<BlockId>,
overrides: Option<StateOverride>,
) -> Result<U256> {
node_info!("eth_estimateGas");
self.do_estimate_gas(request, block_number.or_else(|| Some(BlockNumber::Pending.into())))
.await
self.do_estimate_gas(
request,
block_number.or_else(|| Some(BlockNumber::Pending.into())),
overrides,
)
.await
}

/// Get transaction by its hash.
Expand Down Expand Up @@ -2144,12 +2156,18 @@ impl EthApi {
&self,
request: CallRequest,
block_number: Option<BlockId>,
overrides: Option<StateOverride>,
) -> Result<U256> {
let block_request = self.block_request(block_number).await?;
// check if the number predates the fork, if in fork mode
if let BlockRequest::Number(number) = block_request {
if let Some(fork) = self.get_fork() {
if fork.predates_fork(number) {
if overrides.is_some() {
return Err(BlockchainError::StateOverrideError(
"not available on past forked blocks".to_string(),
));
}
return fork
.estimate_gas(&request, Some(number.into()))
.await
Expand All @@ -2159,7 +2177,13 @@ impl EthApi {
}

self.backend
.with_database_at(Some(block_request), |state, block| {
.with_database_at(Some(block_request), |mut state, block| {
if let Some(overrides) = overrides {
state = Box::new(state::apply_state_override(
overrides.into_iter().collect(),
state,
)?);
}
self.do_estimate_gas_with_state(request, state, block)
})
.await?
Expand Down
49 changes: 47 additions & 2 deletions crates/anvil/tests/it/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use crate::{
utils::{ethers_http_provider, ethers_ws_provider},
};
use alloy_primitives::U256 as rU256;
use alloy_rpc_types::BlockNumberOrTag;
use alloy_rpc_types::{
state::{AccountOverride, StateOverride},
BlockNumberOrTag,
};
use alloy_signer::Signer as AlloySigner;
use anvil::{spawn, Hardfork, NodeConfig};
use anvil_core::eth::transaction::EthTransactionRequest;
Expand Down Expand Up @@ -961,7 +964,49 @@ async fn estimates_gas_on_pending_by_default() {

let tx =
TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]);
api.estimate_gas(to_call_request_from_tx_request(tx), None).await.unwrap();
api.estimate_gas(to_call_request_from_tx_request(tx), None, None).await.unwrap();
}

#[tokio::test(flavor = "multi_thread")]
async fn test_estimate_gas() {
let (api, handle) = spawn(NodeConfig::test()).await;

let wallet = handle.dev_wallets().next().unwrap().to_ethers();
let sender = wallet.address();
let recipient = Address::random();

let tx =
TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]);
// Expect the gas estimation to fail due to insufficient funds.
let error_result =
api.estimate_gas(to_call_request_from_tx_request(tx.clone()), None, None).await;

assert!(error_result.is_err(), "Expected an error due to insufficient funds");
let error_message = error_result.unwrap_err().to_string();
assert!(
error_message.contains("Insufficient funds for gas * price + value"),
"Error message did not match expected: {}",
error_message
);

// Setup state override to simulate sufficient funds for the recipient.
let addr = alloy_primitives::Address::from_slice(recipient.as_bytes());
let account_override =
AccountOverride { balance: Some(alloy_primitives::U256::from(1e18)), ..Default::default() };
let mut state_override = StateOverride::new();
state_override.insert(addr, account_override);

// Estimate gas with state override implying sufficient funds.
let gas_estimate = api
.estimate_gas(to_call_request_from_tx_request(tx), None, Some(state_override))
.await
.expect("Failed to estimate gas with state override");

// Assert the gas estimate meets the expected minimum.
assert!(
gas_estimate >= alloy_primitives::U256::from(21000),
"Gas estimate is lower than expected minimum"
);
}

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

0 comments on commit 2cb8757

Please sign in to comment.