use crate::{
helpers::Ctx, ErroredState, EvmErrored, EvmNeedsTx, EvmReady, EvmTransacted, TransactedState,
Trevm,
};
use revm::{
context::result::{EVMError, ExecutionResult},
interpreter::gas::calculate_initial_tx_gas_for_tx,
Database, InspectEvm, Inspector,
};
impl<Db, Insp> EvmReady<Db, Insp>
where
Db: Database,
Insp: Inspector<Ctx<Db>>,
{
pub fn clear_tx(self) -> EvmNeedsTx<Db, Insp> {
unsafe { core::mem::transmute(self) }
}
pub fn run(mut self) -> Result<EvmTransacted<Db, Insp>, EvmErrored<Db, Insp>> {
let result = self.inner.inspect_tx(self.tx().clone());
let Self { inner, .. } = self;
match result {
Ok(result) => Ok(Trevm { inner, state: TransactedState { result } }),
Err(error) => Err(EvmErrored { inner, state: ErroredState { error } }),
}
}
#[cfg(feature = "call")]
pub fn call(self) -> Result<(ExecutionResult, EvmNeedsTx<Db, Insp>), EvmErrored<Db, Insp>> {
let mut output = std::mem::MaybeUninit::uninit();
let gas_limit = self.tx().gas_limit;
let this =
self.try_with_call_filler(&crate::fillers::CallFiller { gas_limit }, |this| {
let t = this.run()?;
let (o, t) = t.take_result();
output.write(o);
Ok(t)
})?;
Ok((unsafe { output.assume_init() }, this))
}
pub fn calculate_initial_gas(&self) -> u64 {
calculate_initial_tx_gas_for_tx(self.tx(), self.spec_id()).initial_gas
}
#[cfg(feature = "estimate_gas")]
fn estimate_gas_simple_transfer(
&mut self,
) -> Result<Option<u64>, EVMError<<Db as Database>::Error>> {
use alloy::consensus::constants::KECCAK_EMPTY;
use tracing::trace;
if !self.is_transfer() {
return Ok(None);
}
let Some(acc) = self.callee_account()? else { return Ok(None) };
if acc.code_hash != KECCAK_EMPTY {
return Ok(None);
}
let initial = self.calculate_initial_gas();
trace!(initial, "using initial gas for simple transfer");
Ok(Some(initial))
}
#[cfg(feature = "estimate_gas")]
fn run_estimate(
self,
filler: &crate::fillers::GasEstimationFiller,
) -> Result<(crate::EstimationResult, Self), EvmErrored<Db, Insp>> {
use tracing::trace;
let mut estimation = std::mem::MaybeUninit::uninit();
let this = self.try_with_estimate_gas_filler(filler, |this| match this.run() {
Ok(trevm) => {
let (e, t) = trevm.take_estimation();
estimation.write(e);
Ok(t)
}
Err(err) => Err(err),
})?;
Ok((unsafe { estimation.assume_init() }, this))
.inspect(|(est, _)| trace!(?est, "gas estimation result",))
}
#[cfg(feature = "estimate_gas")]
pub fn estimate_gas(mut self) -> Result<(crate::EstimationResult, Self), EvmErrored<Db, Insp>> {
use tracing::{debug, enabled, trace};
if let Some(est) = trevm_try!(self.estimate_gas_simple_transfer(), self) {
return Ok((crate::EstimationResult::basic_transfer_success(est), self));
}
let initial_limit = self.gas_limit();
let mut search_range =
crate::est::SearchRange::new(crate::MIN_TRANSACTION_GAS, initial_limit);
let span = tracing::debug_span!(
"Trevm::estimate_gas",
start_min = search_range.min(),
start_max = search_range.max(),
);
if enabled!(tracing::Level::TRACE) {
span.record("tx", format!("{:?}", &self.tx()));
span.record("block", format!("{:?}", &self.block()));
} else {
span.record("tx", "omitted. Use TRACE for details");
}
let _e = span.enter();
trevm_try!(self.cap_tx_gas(), self);
search_range.maybe_lower_max(self.gas_limit());
search_range.maybe_raise_min(self.calculate_initial_gas());
debug!(gas_limit = self.gas_limit(), "running optimistic estimate");
let (mut estimate, mut trevm) = self.run_estimate(&search_range.max().into())?;
if estimate.is_failure() {
debug!(%estimate, "optimistic estimate failed");
return Ok((estimate, trevm));
}
trace!(%estimate, "optimistic estimate succeeded");
let mut best = estimate.clone();
let mut gas_used = estimate.gas_used();
let gas_refunded = estimate.gas_refunded().expect("checked is_failure");
search_range.maybe_raise_min(gas_used - 1);
let mut needle = (gas_used + gas_refunded + revm::interpreter::gas::CALL_STIPEND) * 64 / 63;
if search_range.contains(needle) {
estimate_and_adjust!(best, estimate, trevm, needle, search_range);
gas_used = estimate.gas_used();
}
needle = std::cmp::min(gas_used * 3, search_range.midpoint());
while search_range.size() > 1 && search_range.ratio() > 0.015 {
estimate_and_adjust!(best, estimate, trevm, needle, search_range);
needle = search_range.midpoint();
}
Ok((best, trevm))
}
}