use alloy::primitives::{Address, B256, U256};
use revm::{
primitives::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot, HashMap},
Database, DatabaseCommit,
};
pub trait EvmExtUnchecked<Db: Database> {
fn db_mut_ext(&mut self) -> &mut Db;
fn account(&mut self, address: Address) -> Result<Account, Db::Error> {
let info = self.db_mut_ext().basic(address)?;
let created = info.is_none();
let mut acct = Account { info: info.unwrap_or_default(), ..Default::default() };
if created {
acct.mark_created();
}
Ok(acct)
}
fn storage(&mut self, address: Address, index: U256) -> Result<U256, Db::Error> {
self.db_mut_ext().storage(address, index)
}
fn bytecode(&mut self, code_hash: B256) -> Result<Bytecode, Db::Error> {
self.db_mut_ext().code_by_hash(code_hash)
}
fn modify_accounts<I, F>(
&mut self,
changes: I,
f: F,
) -> Result<HashMap<Address, AccountInfo>, Db::Error>
where
I: IntoIterator<Item = Address>,
F: Fn(&mut AccountInfo),
Db: DatabaseCommit,
{
let mut state = HashMap::default();
let mut old = HashMap::default();
for addr in changes.into_iter() {
let mut acct = self.account(addr)?;
old.insert(addr, acct.info.clone());
f(&mut acct.info);
acct.mark_touch();
state.insert(addr, acct);
}
self.db_mut_ext().commit(state);
Ok(old)
}
fn modify_account<F>(&mut self, addr: Address, f: F) -> Result<AccountInfo, Db::Error>
where
F: FnOnce(&mut AccountInfo),
Db: DatabaseCommit,
{
let mut acct = self.account(addr)?;
let old = acct.info.clone();
f(&mut acct.info);
acct.mark_touch();
let changes: EvmState = [(addr, acct)].into_iter().collect();
self.db_mut_ext().commit(changes);
Ok(old)
}
fn set_storage(&mut self, address: Address, index: U256, value: U256) -> Result<U256, Db::Error>
where
Db: DatabaseCommit,
{
let mut acct = self.account(address)?;
let old = self.storage(address, index)?;
let change = EvmStorageSlot::new_changed(old, value);
acct.storage.insert(index, change);
acct.mark_touch();
let changes = [(address, acct)].into_iter().collect();
self.db_mut_ext().commit(changes);
Ok(old)
}
fn set_bytecode(
&mut self,
address: Address,
bytecode: Bytecode,
) -> Result<Option<Bytecode>, Db::Error>
where
Db: DatabaseCommit,
{
let mut acct = self.account(address)?;
let old = self.db_mut_ext().code_by_hash(acct.info.code_hash).map(|old| {
if old.is_empty() {
None
} else {
Some(old)
}
})?;
acct.info.code_hash = bytecode.hash_slow();
acct.info.code = Some(bytecode);
acct.mark_touch();
let changes = [(address, acct)].into_iter().collect();
self.db_mut_ext().commit(changes);
Ok(old)
}
fn increase_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.balance = info.balance.saturating_add(amount))
.map(|info| info.balance)
}
fn decrease_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.balance = info.balance.saturating_sub(amount))
.map(|info| info.balance)
}
fn set_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.balance = amount).map(|info| info.balance)
}
fn set_nonce(&mut self, address: Address, nonce: u64) -> Result<u64, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.nonce = nonce).map(|info| info.nonce)
}
fn increment_nonce(&mut self, address: Address) -> Result<u64, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.nonce = info.nonce.saturating_add(1))
.map(|info| info.nonce)
}
fn decrement_nonce(&mut self, address: Address) -> Result<u64, Db::Error>
where
Db: DatabaseCommit,
{
self.modify_account(address, |info| info.nonce = info.nonce.saturating_sub(1))
.map(|info| info.nonce)
}
}
impl<Ext, Db: Database> EvmExtUnchecked<Db> for revm::Evm<'_, Ext, Db> {
fn db_mut_ext(&mut self) -> &mut Db {
self.db_mut()
}
}