use alloy::primitives::{Address, Sign, B256, I256, U256};
use revm::{
bytecode::Bytecode,
database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState},
primitives::HashMap,
state::AccountInfo,
};
use std::{borrow::Cow, collections::BTreeMap};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InfoOutcome<'a> {
Created(Cow<'a, AccountInfo>),
Diff {
old: Cow<'a, AccountInfo>,
new: Cow<'a, AccountInfo>,
},
Destroyed(Cow<'a, AccountInfo>),
}
impl InfoOutcome<'_> {
pub const fn original(&self) -> Option<Cow<'_, AccountInfo>> {
match self {
Self::Created(_) => None,
Self::Diff { old: Cow::Owned(old), .. } => Some(Cow::Borrowed(old)),
Self::Diff { old: Cow::Borrowed(old), .. } => Some(Cow::Borrowed(*old)),
Self::Destroyed(Cow::Owned(old)) => Some(Cow::Borrowed(old)),
Self::Destroyed(Cow::Borrowed(old)) => Some(Cow::Borrowed(*old)),
}
}
pub fn updated(&self) -> Cow<'_, AccountInfo> {
match self {
Self::Created(info) => Cow::Borrowed(info),
Self::Diff { new, .. } => Cow::Borrowed(new),
Self::Destroyed(_) => Cow::Owned(Default::default()),
}
}
}
impl<'a> From<&'a BundleAccount> for InfoOutcome<'a> {
fn from(value: &'a BundleAccount) -> Self {
match (&value.original_info, &value.info) {
(None, Some(new)) => Self::Created(Cow::Borrowed(new)),
(Some(old), Some(new)) => {
Self::Diff { old: Cow::Borrowed(old), new: Cow::Borrowed(new) }
}
(Some(old), None) => {
Self::Destroyed(Cow::Borrowed(old))
}
_ => unreachable!("revm will never output a bundle account that went from not-existing to not-existing"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AcctDiff<'a> {
pub outcome: InfoOutcome<'a>,
pub storage_diff: BTreeMap<U256, Cow<'a, StorageSlot>>,
}
impl AcctDiff<'_> {
pub const fn original(&self) -> Option<Cow<'_, AccountInfo>> {
self.outcome.original()
}
pub fn updated(&self) -> Cow<'_, AccountInfo> {
self.outcome.updated()
}
pub fn balance_change(&self) -> I256 {
let old = self.original().map(|info| info.balance).unwrap_or_default();
let new = self.updated().balance;
let abs = core::cmp::max(new, old) - core::cmp::min(new, old);
let sign = if new > old { Sign::Positive } else { Sign::Negative };
I256::checked_from_sign_and_abs(sign, abs).expect("balance diff overflow")
}
}
impl<'a> From<&'a BundleAccount> for AcctDiff<'a> {
fn from(value: &'a BundleAccount) -> Self {
let outcome = InfoOutcome::from(value);
let storage_diff = value
.storage
.iter()
.filter(|(_, v)| v.is_changed())
.map(|(k, v)| (*k, Cow::Borrowed(v)))
.collect();
AcctDiff { outcome, storage_diff }
}
}
impl From<AcctDiff<'_>> for BundleAccount {
fn from(value: AcctDiff<'_>) -> Self {
let original_info = value.outcome.original().map(|info| info.into_owned());
let info = Some(value.outcome.updated().into_owned());
let storage = value.storage_diff.into_iter().map(|(k, v)| (k, v.into_owned())).collect();
Self { original_info, info, storage, status: AccountStatus::Changed }
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BundleStateIndex<'a> {
pub state: BTreeMap<Address, AcctDiff<'a>>,
pub new_contracts: BTreeMap<B256, Cow<'a, Bytecode>>,
}
impl<'a> From<&'a BundleState> for BundleStateIndex<'a> {
fn from(value: &'a BundleState) -> Self {
let state = value
.state
.iter()
.map(|(address, account)| (*address, AcctDiff::from(account)))
.collect();
let new_contracts = value.contracts.iter().map(|(k, v)| (*k, Cow::Borrowed(v))).collect();
BundleStateIndex { state, new_contracts }
}
}
impl From<BundleStateIndex<'_>> for BundleState {
fn from(value: BundleStateIndex<'_>) -> Self {
let mut state_size = 0;
let state: HashMap<_, _, _> = value
.state
.into_iter()
.map(|(address, info)| {
let original = info.original().map(Cow::into_owned);
let present = Some(info.updated().into_owned());
let storage =
info.storage_diff.into_iter().map(|(k, v)| (k, v.into_owned())).collect();
let account: BundleAccount =
BundleAccount::new(original, present, storage, AccountStatus::Changed);
state_size += account.size_hint();
(address, account)
})
.collect();
let contracts = value.new_contracts.into_iter().map(|(a, c)| (a, c.into_owned())).collect();
Self { state, reverts: Default::default(), contracts, state_size, reverts_size: 0 }
}
}