use crate::{
driver::DriveBlockResult,
est::{EstimationResult, SearchRange},
fillers::GasEstimationFiller,
unwrap_or_trevm_err, Block, BlockDriver, BundleDriver, Cfg, ChainDriver, DriveBundleResult,
DriveChainResult, ErroredState, EvmErrored, EvmExtUnchecked, EvmNeedsBlock, EvmNeedsCfg,
EvmNeedsTx, EvmReady, EvmTransacted, HasBlock, HasCfg, HasTx, NeedsCfg, NeedsTx, Ready,
TransactedState, Tx, MIN_TRANSACTION_GAS,
};
use alloy::{
primitives::{Address, Bytes, U256},
rpc::types::{state::StateOverride, BlockOverrides},
};
use core::convert::Infallible;
use revm::{
db::{states::bundle_state::BundleRetention, BundleState, State},
interpreter::gas::{calculate_initial_tx_gas, CALL_STIPEND},
primitives::{
AccountInfo, AuthorizationList, BlockEnv, Bytecode, EVMError, Env, EvmState,
ExecutionResult, InvalidTransaction, ResultAndState, SpecId, TxEnv, TxKind, KECCAK_EMPTY,
},
Database, DatabaseCommit, DatabaseRef, Evm,
};
use std::{fmt, mem::MaybeUninit};
pub struct Trevm<'a, Ext, Db: Database + DatabaseCommit, TrevmState> {
pub(crate) inner: Box<Evm<'a, Ext, Db>>,
pub(crate) state: TrevmState,
}
impl<Ext, Db: Database + DatabaseCommit, TrevmState> fmt::Debug for Trevm<'_, Ext, Db, TrevmState> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Trevm").finish_non_exhaustive()
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> AsRef<Evm<'a, Ext, Db>>
for Trevm<'a, Ext, Db, TrevmState>
{
fn as_ref(&self) -> &Evm<'a, Ext, Db> {
&self.inner
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> From<Evm<'a, Ext, Db>> for EvmNeedsCfg<'a, Ext, Db> {
fn from(inner: Evm<'a, Ext, Db>) -> Self {
Self { inner: Box::new(inner), state: NeedsCfg::new() }
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, TrevmState> {
pub fn inner(&self) -> &Evm<'a, Ext, Db> {
self.as_ref()
}
pub fn inner_mut_unchecked(&mut self) -> &mut Evm<'a, Ext, Db> {
&mut self.inner
}
pub fn into_inner(self) -> Box<Evm<'a, Ext, Db>> {
self.inner
}
pub fn env_unchecked(&self) -> &Env {
&self.inner().context.evm.inner.env
}
pub fn env_mut_unchecked(&mut self) -> &mut Env {
&mut self.inner_mut_unchecked().context.evm.inner.env
}
pub fn spec_id(&self) -> SpecId {
self.inner.spec_id()
}
pub fn set_spec_id(&mut self, spec_id: SpecId) {
self.inner.modify_spec_id(spec_id)
}
pub fn with_spec_id<F, NewState>(
mut self,
spec_id: SpecId,
f: F,
) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let old = self.spec_id();
self.set_spec_id(spec_id);
let mut this = f(self);
this.set_spec_id(old);
this
}
pub fn errored<E>(self, error: E) -> EvmErrored<'a, Ext, Db, E> {
EvmErrored { inner: self.inner, state: ErroredState { error } }
}
pub fn try_read_account(&mut self, address: Address) -> Result<Option<AccountInfo>, Db::Error> {
self.inner.db_mut().basic(address)
}
pub fn try_read_nonce(&mut self, address: Address) -> Result<u64, Db::Error> {
self.try_read_account(address).map(|a| a.map(|a| a.nonce).unwrap_or_default())
}
pub fn try_read_balance(&mut self, address: Address) -> Result<U256, Db::Error> {
self.try_read_account(address).map(|a| a.map(|a| a.balance).unwrap_or_default())
}
pub fn try_read_storage(&mut self, address: Address, slot: U256) -> Result<U256, Db::Error> {
self.inner.db_mut().storage(address, slot)
}
pub fn try_read_code(&mut self, address: Address) -> Result<Option<Bytecode>, Db::Error> {
let acct_info = self.try_read_account(address)?;
match acct_info {
Some(acct) => Ok(Some(self.inner.db_mut().code_by_hash(acct.code_hash)?)),
None => Ok(None),
}
}
pub fn apply_state_overrides(
mut self,
overrides: &StateOverride,
) -> Result<Self, EVMError<Db::Error>> {
for (address, account_override) in overrides {
if let Some(balance) = account_override.balance {
self.inner.set_balance(*address, balance).map_err(EVMError::Database)?;
}
if let Some(nonce) = account_override.nonce {
self.inner.set_nonce(*address, nonce).map_err(EVMError::Database)?;
}
if let Some(code) = account_override.code.as_ref() {
self.inner
.set_bytecode(
*address,
Bytecode::new_raw_checked(code.clone())
.map_err(|_| EVMError::Custom("Invalid bytecode".to_string()))?,
)
.map_err(EVMError::Database)?;
}
if let Some(state) = account_override.state.as_ref() {
for (slot, value) in state {
self.inner
.set_storage(
*address,
U256::from_be_bytes((*slot).into()),
U256::from_be_bytes((*value).into()),
)
.map_err(EVMError::Database)?;
}
}
}
Ok(self)
}
pub fn maybe_apply_state_overrides(
self,
overrides: Option<&StateOverride>,
) -> Result<Self, EVMError<Db::Error>> {
if let Some(overrides) = overrides {
self.apply_state_overrides(overrides)
} else {
Ok(self)
}
}
}
impl<Ext, Db: Database + DatabaseCommit + DatabaseRef, TrevmState> Trevm<'_, Ext, Db, TrevmState> {
pub fn try_read_account_ref(
&self,
address: Address,
) -> Result<Option<AccountInfo>, <Db as DatabaseRef>::Error> {
self.inner.db().basic_ref(address)
}
pub fn try_read_nonce_ref(&self, address: Address) -> Result<u64, <Db as DatabaseRef>::Error> {
self.try_read_account_ref(address).map(|a| a.map(|a| a.nonce).unwrap_or_default())
}
pub fn try_read_balance_ref(
&self,
address: Address,
) -> Result<U256, <Db as DatabaseRef>::Error> {
self.try_read_account_ref(address).map(|a| a.map(|a| a.balance).unwrap_or_default())
}
pub fn try_read_storage_ref(
&self,
address: Address,
slot: U256,
) -> Result<U256, <Db as DatabaseRef>::Error> {
self.inner.db().storage_ref(address, slot)
}
pub fn try_read_code_ref(
&self,
address: Address,
) -> Result<Option<Bytecode>, <Db as DatabaseRef>::Error> {
let acct_info = self.try_read_account_ref(address)?;
match acct_info {
Some(acct) => Ok(Some(self.inner.db().code_by_hash_ref(acct.code_hash)?)),
None => Ok(None),
}
}
}
impl<Ext, Db: Database<Error = Infallible> + DatabaseCommit, TrevmState>
Trevm<'_, Ext, Db, TrevmState>
{
pub fn read_account(&mut self, address: Address) -> Option<AccountInfo> {
self.inner.db_mut().basic(address).expect("infallible")
}
pub fn read_nonce(&mut self, address: Address) -> u64 {
self.read_account(address).map(|a: AccountInfo| a.nonce).unwrap_or_default()
}
pub fn read_balance(&mut self, address: Address) -> U256 {
self.read_account(address).map(|a: AccountInfo| a.balance).unwrap_or_default()
}
pub fn read_storage(&mut self, address: Address, slot: U256) -> U256 {
self.inner.db_mut().storage(address, slot).expect("infallible")
}
pub fn read_code(&mut self, address: Address) -> Option<Bytecode> {
let acct_info = self.read_account(address)?;
Some(self.inner.db_mut().code_by_hash(acct_info.code_hash).expect("infallible"))
}
}
impl<
Ext,
Db: Database<Error = Infallible> + DatabaseRef<Error = Infallible> + DatabaseCommit,
TrevmState,
> Trevm<'_, Ext, Db, TrevmState>
{
pub fn read_account_ref(&self, address: Address) -> Option<AccountInfo> {
self.inner.db().basic_ref(address).expect("infallible")
}
pub fn read_nonce_ref(&self, address: Address) -> u64 {
self.read_account_ref(address).map(|a: AccountInfo| a.nonce).unwrap_or_default()
}
pub fn read_balance_ref(&self, address: Address) -> U256 {
self.read_account_ref(address).map(|a: AccountInfo| a.balance).unwrap_or_default()
}
pub fn read_storage_ref(&self, address: Address, slot: U256) -> U256 {
self.inner.db().storage_ref(address, slot).expect("infallible")
}
pub fn read_code_ref(&self, address: Address) -> Option<Bytecode> {
let acct_info = self.read_account_ref(address)?;
Some(self.inner.db().code_by_hash_ref(acct_info.code_hash).expect("infallible"))
}
}
impl<Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'_, Ext, Db, TrevmState> {
pub fn commit_unchecked(&mut self, state: EvmState) {
self.inner.db_mut().commit(state);
}
pub fn try_modify_account_unchecked<F: FnOnce(&mut AccountInfo)>(
&mut self,
address: Address,
f: F,
) -> Result<AccountInfo, Db::Error> {
self.inner.modify_account(address, f)
}
pub fn try_set_nonce_unchecked(
&mut self,
address: Address,
nonce: u64,
) -> Result<u64, Db::Error> {
self.inner.set_nonce(address, nonce)
}
pub fn try_increment_nonce_unchecked(&mut self, address: Address) -> Result<u64, Db::Error> {
self.inner.increment_nonce(address)
}
pub fn try_decrement_nonce_unchecked(&mut self, address: Address) -> Result<u64, Db::Error> {
self.inner.decrement_nonce(address)
}
pub fn try_set_storage_unchecked(
&mut self,
address: Address,
slot: U256,
value: U256,
) -> Result<U256, Db::Error> {
self.inner.set_storage(address, slot, value)
}
pub fn try_set_bytecode_unchecked(
&mut self,
address: Address,
bytecode: Bytecode,
) -> Result<Option<Bytecode>, Db::Error> {
self.inner.set_bytecode(address, bytecode)
}
pub fn try_increase_balance_unchecked(
&mut self,
address: Address,
amount: U256,
) -> Result<U256, Db::Error> {
self.inner.increase_balance(address, amount)
}
pub fn try_decrease_balance_unchecked(
&mut self,
address: Address,
amount: U256,
) -> Result<U256, Db::Error> {
self.inner.decrease_balance(address, amount)
}
pub fn try_set_balance_unchecked(
&mut self,
address: Address,
amount: U256,
) -> Result<U256, Db::Error> {
self.inner.set_balance(address, amount)
}
}
impl<Ext, Db: Database<Error = Infallible> + DatabaseCommit, TrevmState>
Trevm<'_, Ext, Db, TrevmState>
{
pub fn modify_account_unchecked(
&mut self,
address: Address,
f: impl FnOnce(&mut AccountInfo),
) -> AccountInfo {
self.try_modify_account_unchecked(address, f).expect("infallible")
}
pub fn set_nonce_unchecked(&mut self, address: Address, nonce: u64) -> u64 {
self.try_set_nonce_unchecked(address, nonce).expect("infallible")
}
pub fn increment_nonce_unchecked(&mut self, address: Address) -> u64 {
self.try_increment_nonce_unchecked(address).expect("infallible")
}
pub fn decrement_nonce_unchecked(&mut self, address: Address) -> u64 {
self.try_decrement_nonce_unchecked(address).expect("infallible")
}
pub fn set_storage_unchecked(&mut self, address: Address, slot: U256, value: U256) -> U256 {
self.try_set_storage_unchecked(address, slot, value).expect("infallible")
}
pub fn set_bytecode_unchecked(
&mut self,
address: Address,
bytecode: Bytecode,
) -> Option<Bytecode> {
self.try_set_bytecode_unchecked(address, bytecode).expect("infallible")
}
pub fn increase_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 {
self.try_increase_balance_unchecked(address, amount).expect("infallible")
}
pub fn decrease_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 {
self.try_decrease_balance_unchecked(address, amount).expect("infallible")
}
pub fn set_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 {
self.try_set_balance_unchecked(address, amount).expect("infallible")
}
}
impl<Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'_, Ext, State<Db>, TrevmState> {
pub fn set_state_clear_flag(&mut self, flag: bool) {
self.inner.db_mut().set_state_clear_flag(flag)
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsCfg<'a, Ext, Db> {
pub fn fill_cfg<T: Cfg>(mut self, filler: &T) -> EvmNeedsBlock<'a, Ext, Db> {
filler.fill_cfg(&mut self.inner);
unsafe { core::mem::transmute(self) }
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasCfg> Trevm<'a, Ext, Db, TrevmState> {
pub fn set_code_size_limit(&mut self, limit: usize) -> Option<usize> {
let cfg = self.inner.cfg_mut();
cfg.limit_contract_code_size.replace(limit)
}
pub fn disable_code_size_limit(&mut self) -> Option<usize> {
self.inner.cfg_mut().limit_contract_code_size.take()
}
pub fn without_code_size_limit<F, NewState: HasCfg>(
mut self,
f: F,
) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let limit = self.disable_code_size_limit();
let mut new = f(self);
if let Some(limit) = limit {
new.set_code_size_limit(limit);
}
new
}
pub fn set_default_code_size_limit(&mut self) -> Option<usize> {
self.set_code_size_limit(0x6000)
}
pub fn with_cfg<C, F, NewState>(mut self, cfg: &C, f: F) -> Trevm<'a, Ext, Db, NewState>
where
C: Cfg,
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
NewState: HasCfg,
{
let previous = self.inner.cfg_mut().clone();
cfg.fill_cfg_env(self.inner.cfg_mut());
let mut this = f(self);
*this.inner.cfg_mut() = previous;
this
}
pub fn try_with_cfg<C, F, NewState, E>(
mut self,
cfg: &C,
f: F,
) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>
where
C: Cfg,
F: FnOnce(Self) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>,
NewState: HasCfg,
{
let previous = self.inner.cfg_mut().clone();
cfg.fill_cfg_env(self.inner.cfg_mut());
match f(self) {
Ok(mut evm) => {
*evm.inner.cfg_mut() = previous;
Ok(evm)
}
Err(mut evm) => {
*evm.inner.cfg_mut() = previous;
Err(evm)
}
}
}
#[cfg(feature = "c-kzg")]
pub fn set_kzg_settings(
&mut self,
settings: revm::primitives::EnvKzgSettings,
) -> revm::primitives::EnvKzgSettings {
let cfg = self.inner.cfg_mut();
core::mem::replace(&mut cfg.kzg_settings, settings)
}
#[cfg(feature = "memory_limit")]
pub fn set_memory_limit(&mut self, new_limit: u64) -> u64 {
let cfg = self.inner.cfg_mut();
core::mem::replace(&mut cfg.memory_limit, new_limit)
}
#[cfg(feature = "optional_balance_check")]
pub fn disable_balance_check(&mut self) {
self.inner.cfg_mut().disable_balance_check = true
}
#[cfg(feature = "optional_balance_check")]
pub fn enable_balance_check(&mut self) {
self.inner.cfg_mut().disable_balance_check = false
}
#[cfg(feature = "optional_balance_check")]
pub fn without_balance_check<F, NewState: HasCfg>(
mut self,
f: F,
) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_balance_check;
self.disable_balance_check();
let mut new = f(self);
new.inner.cfg_mut().disable_balance_check = previous;
new
}
#[cfg(feature = "optional_block_gas_limit")]
pub fn disable_block_gas_limit(&mut self) {
self.inner.cfg_mut().disable_beneficiary_reward = true
}
#[cfg(feature = "optional_block_gas_limit")]
pub fn enable_block_gas_limit(&mut self) {
self.inner.cfg_mut().disable_beneficiary_reward = false
}
#[cfg(feature = "optional_block_gas_limit")]
pub fn without_block_gas_limit<F, NewState: HasCfg>(
mut self,
f: F,
) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_block_gas_limit;
self.disable_block_gas_limit();
let mut new = f(self);
new.inner.cfg_mut().disable_block_gas_limit = previous;
new
}
#[cfg(feature = "optional_eip3607")]
pub fn disable_eip3607(&mut self) {
self.inner.cfg_mut().disable_eip3607 = true
}
#[cfg(feature = "optional_eip3607")]
pub fn enable_eip3607(&mut self) {
self.inner.cfg_mut().disable_eip3607 = false
}
#[cfg(feature = "optional_eip3607")]
pub fn without_eip3607<F, NewState: HasCfg>(mut self, f: F) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_eip3607;
self.disable_eip3607();
let mut new = f(self);
new.inner.cfg_mut().disable_eip3607 = previous;
new
}
#[cfg(feature = "optional_gas_refund")]
pub fn disable_gas_refund(&mut self) {
self.inner.cfg_mut().disable_gas_refund = true
}
#[cfg(feature = "optional_gas_refund")]
pub fn enable_gas_refund(&mut self) {
self.inner.cfg_mut().disable_gas_refund = false
}
#[cfg(feature = "optional_gas_refund")]
pub fn without_gas_refund<F, NewState: HasCfg>(mut self, f: F) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_gas_refund;
self.disable_gas_refund();
let mut new = f(self);
new.inner.cfg_mut().disable_gas_refund = previous;
new
}
#[cfg(feature = "optional_no_base_fee")]
pub fn disable_base_fee(&mut self) {
self.inner.cfg_mut().disable_base_fee = true;
}
#[cfg(feature = "optional_no_base_fee")]
pub fn enable_base_fee(&mut self) {
self.inner.cfg_mut().disable_base_fee = false
}
#[cfg(feature = "optional_no_base_fee")]
pub fn without_base_fee<F, NewState: HasCfg>(mut self, f: F) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_base_fee;
self.disable_base_fee();
let mut new = f(self);
new.inner.cfg_mut().disable_base_fee = previous;
new
}
#[cfg(feature = "optional_beneficiary_reward")]
pub fn disable_beneficiary_reward(&mut self) {
self.inner.cfg_mut().disable_beneficiary_reward = true;
}
#[cfg(feature = "optional_beneficiary_reward")]
pub fn enable_beneficiary_reward(&mut self) {
self.inner.cfg_mut().disable_beneficiary_reward = false
}
#[cfg(feature = "optional_beneficiary_reward")]
pub fn without_beneficiary_reward<F, NewState: HasCfg>(
mut self,
f: F,
) -> Trevm<'a, Ext, Db, NewState>
where
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
{
let previous = self.inner.cfg().disable_beneficiary_reward;
self.disable_beneficiary_reward();
let mut new = f(self);
new.inner.cfg_mut().disable_beneficiary_reward = previous;
new
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsBlock<'a, Ext, Db> {
pub fn drive_block<D>(self, driver: &mut D) -> DriveBlockResult<'a, Ext, Db, D>
where
D: BlockDriver<Ext>,
{
let trevm = self.fill_block(driver.block());
let trevm = driver.run_txns(trevm)?;
let trevm = trevm.close_block();
match driver.post_block(&trevm) {
Ok(_) => Ok(trevm),
Err(e) => Err(trevm.errored(e)),
}
}
pub fn drive_chain<D>(self, driver: &mut D) -> DriveChainResult<'a, Ext, Db, D>
where
D: ChainDriver<Ext>,
{
let block_count = driver.blocks().len();
let mut trevm = self
.drive_block(&mut driver.blocks()[0])
.map_err(EvmErrored::err_into::<<D as ChainDriver<Ext>>::Error<Db>>)?;
if let Err(e) = driver.interblock(&trevm, 0) {
return Err(trevm.errored(e));
}
for i in 1..block_count {
trevm = {
let trevm = trevm
.drive_block(&mut driver.blocks()[i])
.map_err(EvmErrored::err_into::<<D as ChainDriver<Ext>>::Error<Db>>)?;
if let Err(e) = driver.interblock(&trevm, i) {
return Err(trevm.errored(e));
}
trevm
};
}
Ok(trevm)
}
pub fn fill_block<B: Block>(mut self, filler: &B) -> EvmNeedsTx<'a, Ext, Db> {
filler.fill_block(self.inner_mut_unchecked());
unsafe { core::mem::transmute(self) }
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasBlock> Trevm<'a, Ext, Db, TrevmState> {
pub fn block(&self) -> &BlockEnv {
self.inner.block()
}
pub fn block_gas_limit(&self) -> U256 {
self.block().gas_limit
}
pub fn block_number(&self) -> U256 {
self.block().number
}
pub fn block_timestamp(&self) -> U256 {
self.block().timestamp
}
pub fn beneficiary(&self) -> Address {
self.block().coinbase
}
pub fn with_block<B, F, NewState>(mut self, b: &B, f: F) -> Trevm<'a, Ext, Db, NewState>
where
B: Block,
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
NewState: HasBlock,
{
let previous = self.inner.block_mut().clone();
b.fill_block_env(self.inner.block_mut());
let mut this = f(self);
*this.inner.block_mut() = previous;
this
}
pub fn try_with_block<B, F, NewState, E>(
mut self,
b: &B,
f: F,
) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>
where
F: FnOnce(Self) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>,
B: Block,
NewState: HasBlock,
{
let previous = self.inner.block_mut().clone();
b.fill_block_env(self.inner.block_mut());
match f(self) {
Ok(mut evm) => {
*evm.inner.block_mut() = previous;
Ok(evm)
}
Err(mut evm) => {
*evm.inner.block_mut() = previous;
Err(evm)
}
}
}
}
impl<Ext, Db: Database> EvmNeedsBlock<'_, Ext, State<Db>> {
pub fn finish(self) -> BundleState {
let Self { inner: mut evm, .. } = self;
evm.db_mut().merge_transitions(BundleRetention::Reverts);
let bundle = evm.db_mut().take_bundle();
bundle
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsTx<'a, Ext, Db> {
pub fn close_block(self) -> EvmNeedsBlock<'a, Ext, Db> {
unsafe { core::mem::transmute(self) }
}
pub fn drive_bundle<D>(self, driver: &mut D) -> DriveBundleResult<'a, Ext, Db, D>
where
D: BundleDriver<Ext>,
{
let trevm = driver.run_bundle(self)?;
match driver.post_bundle(&trevm) {
Ok(_) => Ok(trevm),
Err(e) => Err(trevm.errored(e)),
}
}
pub fn fill_tx<T: Tx>(mut self, filler: &T) -> EvmReady<'a, Ext, Db> {
filler.fill_tx(&mut self.inner);
unsafe { core::mem::transmute(self) }
}
pub fn run_tx<T: Tx>(
self,
filler: &T,
) -> Result<EvmTransacted<'a, Ext, Db>, EvmErrored<'a, Ext, Db>> {
self.fill_tx(filler).run()
}
pub fn estimate_tx_gas<T: Tx>(
self,
filler: &T,
) -> Result<(EstimationResult, EvmReady<'a, Ext, Db>), EvmErrored<'a, Ext, Db>> {
self.fill_tx(filler).estimate_gas()
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasTx> Trevm<'a, Ext, Db, TrevmState> {
fn try_with_gas_estimation_filler<E>(
self,
filler: &GasEstimationFiller,
f: impl FnOnce(Self) -> Result<Self, EvmErrored<'a, Ext, Db, E>>,
) -> Result<Self, EvmErrored<'a, Ext, Db, E>> {
self.try_with_cfg(filler, |this| this.try_with_tx(filler, f))
}
pub fn tx(&self) -> &TxEnv {
self.inner.tx()
}
pub fn is_transfer(&self) -> bool {
self.inner.tx().data.is_empty() && self.to().is_call()
}
pub fn is_create(&self) -> bool {
self.to().is_create()
}
pub fn data(&self) -> &Bytes {
&self.tx().data
}
pub fn to(&self) -> TxKind {
self.tx().transact_to
}
pub fn value(&self) -> U256 {
self.tx().value
}
pub fn gas_limit(&self) -> u64 {
self.tx().gas_limit
}
pub fn gas_price(&self) -> U256 {
self.tx().gas_price
}
pub fn caller(&self) -> Address {
self.tx().caller
}
pub fn caller_account(&mut self) -> Result<AccountInfo, EVMError<Db::Error>> {
self.try_read_account(self.caller())
.map(Option::unwrap_or_default)
.map_err(EVMError::Database)
}
pub fn callee(&self) -> Option<Address> {
self.to().into()
}
pub fn callee_account(&mut self) -> Result<Option<AccountInfo>, EVMError<Db::Error>> {
self.callee().map_or(Ok(None), |addr| {
self.try_read_account(addr)
.map(Option::unwrap_or_default)
.map(Some)
.map_err(EVMError::Database)
})
}
pub fn callee_account_ref(&self) -> Result<Option<AccountInfo>, <Db as DatabaseRef>::Error>
where
Db: DatabaseRef,
{
self.callee().map_or(Ok(None), |addr| self.try_read_account_ref(addr))
}
pub fn with_tx<T, F, NewState>(mut self, t: &T, f: F) -> Trevm<'a, Ext, Db, NewState>
where
T: Tx,
F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>,
NewState: HasTx,
{
let previous = self.inner.tx_mut().clone();
t.fill_tx_env(self.inner.tx_mut());
let mut this = f(self);
*this.inner.tx_mut() = previous;
this
}
pub fn try_with_tx<T, F, NewState, E>(
mut self,
t: &T,
f: F,
) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>
where
T: Tx,
F: FnOnce(Self) -> Result<Trevm<'a, Ext, Db, NewState>, EvmErrored<'a, Ext, Db, E>>,
NewState: HasTx,
{
let previous = self.inner.tx_mut().clone();
t.fill_tx_env(self.inner.tx_mut());
match f(self) {
Ok(mut evm) => {
*evm.inner.tx_mut() = previous;
Ok(evm)
}
Err(mut evm) => {
*evm.inner.tx_mut() = previous;
Err(evm)
}
}
}
pub fn gas_allowance(&mut self) -> Result<u64, EVMError<Db::Error>> {
let gas_price = self.gas_price();
if gas_price.is_zero() {
return Ok(u64::MAX);
}
let balance = self.try_read_balance(self.caller()).map_err(EVMError::Database)?;
Ok((balance / gas_price).saturating_to())
}
}
impl<Ext, Db: Database> EvmNeedsTx<'_, Ext, State<Db>> {
pub fn apply_block_overrides(mut self, overrides: &BlockOverrides) -> Self {
overrides.fill_block(&mut self.inner);
if let Some(hashes) = &overrides.block_hash {
self.inner.db_mut().block_hashes.extend(hashes)
}
self
}
pub fn maybe_apply_block_overrides(self, overrides: Option<&BlockOverrides>) -> Self {
if let Some(overrides) = overrides {
self.apply_block_overrides(overrides)
} else {
self
}
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmReady<'a, Ext, Db> {
pub fn clear_tx(self) -> EvmNeedsTx<'a, Ext, Db> {
unsafe { core::mem::transmute(self) }
}
pub fn run(mut self) -> Result<EvmTransacted<'a, Ext, Db>, EvmErrored<'a, Ext, Db>> {
let result = self.inner.transact();
let Trevm { inner, .. } = self;
match result {
Ok(result) => Ok(Trevm { inner, state: TransactedState { result } }),
Err(error) => Err(EvmErrored { inner, state: ErroredState { error } }),
}
}
fn calculate_initial_gas(&self) -> u64 {
calculate_initial_tx_gas(
self.spec_id(),
&[],
false,
&self.tx().access_list,
self.tx().authorization_list.as_ref().map(AuthorizationList::len).unwrap_or_default()
as u64,
)
.initial_gas
}
fn estimate_gas_simple_transfer(&mut self) -> Result<Option<u64>, EVMError<Db::Error>> {
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);
}
Ok(Some(self.calculate_initial_gas()))
}
fn run_estimate(
self,
filler: &GasEstimationFiller,
) -> Result<(EstimationResult, Self), EvmErrored<'a, Ext, Db>> {
let mut estimation = MaybeUninit::uninit();
let this = self.try_with_gas_estimation_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))
}
pub fn estimate_gas(mut self) -> Result<(EstimationResult, Self), EvmErrored<'a, Ext, Db>> {
if let Some(est) = unwrap_or_trevm_err!(self.estimate_gas_simple_transfer(), self) {
return Ok((EstimationResult::basic_transfer_success(est), self));
}
let initial_limit = self.gas_limit();
let mut search_range = SearchRange::new(MIN_TRANSACTION_GAS, initial_limit);
search_range.maybe_lower_max(self.block_gas_limit().saturating_to::<u64>());
let allowance = unwrap_or_trevm_err!(self.gas_allowance(), self);
search_range.maybe_lower_max(allowance);
search_range.maybe_raise_min(self.calculate_initial_gas());
let (mut estimate, mut trevm) = self.run_estimate(&search_range.max().into())?;
if estimate.is_failure() {
return Ok((estimate, trevm));
}
let mut gas_used = estimate.gas_estimation().expect("checked is_failure");
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 + CALL_STIPEND * 64 / 63;
if search_range.contains(needle) {
estimate_and_adjust!(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!(estimate, trevm, needle, search_range);
needle = search_range.midpoint();
}
Ok((estimate, trevm))
}
}
impl<'a, Ext, Db: Database + DatabaseCommit, E> EvmErrored<'a, Ext, Db, E> {
pub const fn error(&self) -> &E {
&self.state.error
}
pub fn inspect_err<F, T>(&self, f: F) -> T
where
F: FnOnce(&E) -> T,
{
f(self.error())
}
pub fn discard_error(self) -> EvmNeedsTx<'a, Ext, Db> {
Trevm { inner: self.inner, state: NeedsTx::new() }
}
pub fn into_error(self) -> E {
self.state.error
}
pub fn take_err(self) -> (E, EvmNeedsTx<'a, Ext, Db>) {
let Trevm { inner, state: ErroredState { error } } = self;
(error, Trevm { inner, state: NeedsTx::new() })
}
pub fn err_into<NewErr: From<E>>(self) -> EvmErrored<'a, Ext, Db, NewErr> {
self.map_err(Into::into)
}
pub fn map_err<F, NewErr>(self, f: F) -> EvmErrored<'a, Ext, Db, NewErr>
where
F: FnOnce(E) -> NewErr,
{
Trevm { inner: self.inner, state: ErroredState { error: f(self.state.error) } }
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmErrored<'a, Ext, Db> {
pub const fn is_transaction_error(&self) -> bool {
matches!(self.state.error, EVMError::Transaction(_))
}
pub const fn as_transaction_error(&self) -> Option<&InvalidTransaction> {
match &self.state.error {
EVMError::Transaction(err) => Some(err),
_ => None,
}
}
pub fn discard_transaction_error(self) -> Result<EvmNeedsTx<'a, Ext, Db>, Self> {
if self.is_transaction_error() {
Ok(self.discard_error())
} else {
Err(self)
}
}
}
impl<Ext, Db: Database + DatabaseCommit> AsRef<ResultAndState> for EvmTransacted<'_, Ext, Db> {
fn as_ref(&self) -> &ResultAndState {
&self.state.result
}
}
impl<Ext, Db: Database + DatabaseCommit> AsRef<ExecutionResult> for EvmTransacted<'_, Ext, Db> {
fn as_ref(&self) -> &ExecutionResult {
&self.state.result.result
}
}
impl<'a, Ext, Db: Database + DatabaseCommit> EvmTransacted<'a, Ext, Db> {
pub fn result(&self) -> &ExecutionResult {
self.as_ref()
}
pub fn result_mut_unchecked(&mut self) -> &mut ExecutionResult {
&mut self.state.result.result
}
pub const fn state(&self) -> &EvmState {
&self.state.result.state
}
pub fn state_mut_unchecked(&mut self) -> &mut EvmState {
&mut self.state.result.state
}
pub fn result_and_state(&self) -> &ResultAndState {
self.as_ref()
}
pub fn result_and_state_mut_unchecked(&mut self) -> &mut ResultAndState {
&mut self.state.result
}
pub fn output(&self) -> Option<&Bytes> {
self.result().output()
}
pub fn output_sol<T: alloy_sol_types::SolCall>(
&self,
validate: bool,
) -> Option<alloy_sol_types::Result<T::Return>>
where
T::Return: alloy_sol_types::SolType,
{
self.output().map(|output| T::abi_decode_returns(output, validate))
}
pub fn gas_used(&self) -> u64 {
self.state.result.result.gas_used()
}
pub fn reject(self) -> EvmNeedsTx<'a, Ext, Db> {
Trevm { inner: self.inner, state: NeedsTx::new() }
}
pub fn into_result_and_state(self) -> ResultAndState {
self.state.result
}
pub fn take_result_and_state(self) -> (ResultAndState, EvmNeedsTx<'a, Ext, Db>) {
let Trevm { inner, state: TransactedState { result } } = self;
(result, Trevm { inner, state: NeedsTx::new() })
}
pub fn take_result(self) -> (ExecutionResult, EvmNeedsTx<'a, Ext, Db>) {
let Trevm { inner, state: TransactedState { result } } = self;
(result.result, Trevm { inner, state: NeedsTx::new() })
}
pub fn accept(self) -> (ExecutionResult, EvmNeedsTx<'a, Ext, Db>) {
let Trevm { mut inner, state: TransactedState { result } } = self;
inner.db_mut().commit(result.state);
(result.result, Trevm { inner, state: NeedsTx::new() })
}
pub fn accept_state(self) -> EvmNeedsTx<'a, Ext, Db> {
self.accept().1
}
pub fn estimation(&self) -> EstimationResult {
self.result().into()
}
pub fn take_estimation(self) -> (EstimationResult, EvmReady<'a, Ext, Db>) {
let estimation = self.estimation();
(estimation, Trevm { inner: self.inner, state: Ready::new() })
}
}