use crate::{SignetCallBundle, SignetCallBundleResponse};
use alloy::{consensus::TxEnvelope, primitives::U256};
use signet_evm::{DriveBundleResult, EvmNeedsTx, EvmTransacted, SignetInspector, SignetLayered};
use std::fmt::Debug;
use tracing::{debug_span, instrument, Level};
use trevm::{
helpers::Ctx,
revm::{context::result::EVMError, Database, DatabaseCommit, Inspector},
trevm_bail, trevm_ensure, trevm_try, BundleDriver, BundleError,
};
#[derive(Debug)]
pub struct SignetBundleDriver<'a> {
bundle: &'a SignetCallBundle,
response: SignetCallBundleResponse,
}
impl<'a> From<&'a SignetCallBundle> for SignetBundleDriver<'a> {
fn from(bundle: &'a SignetCallBundle) -> Self {
Self::new(bundle)
}
}
impl<'a> SignetBundleDriver<'a> {
pub fn new(bundle: &'a SignetCallBundle) -> Self {
Self { bundle, response: Default::default() }
}
}
impl SignetBundleDriver<'_> {
pub const fn bundle(&self) -> &SignetCallBundle {
self.bundle
}
pub const fn response(&self) -> &SignetCallBundleResponse {
&self.response
}
pub fn into_response(self) -> SignetCallBundleResponse {
self.response
}
pub fn clear(&mut self) -> SignetCallBundleResponse {
std::mem::take(&mut self.response)
}
fn accept_and_accumulate<Db, Insp>(
&mut self,
trevm: EvmTransacted<Db, Insp>,
tx: &TxEnvelope,
pre_sim_coinbase_balance: &mut U256,
basefee: u64,
) -> DriveBundleResult<Self, Db, Insp>
where
Db: Database + DatabaseCommit,
Insp: Inspector<Ctx<Db>>,
{
let beneficiary = trevm.beneficiary();
let (execution_result, mut trevm) = trevm.accept();
let post_sim_coinbase_balance = trevm_try!(
trevm
.try_read_balance(beneficiary)
.map_err(EVMError::Database)
.map_err(BundleError::from),
trevm
);
let coinbase_diff = post_sim_coinbase_balance.saturating_sub(*pre_sim_coinbase_balance);
trevm_try!(
self.response.accumulate_tx(tx, coinbase_diff, basefee, execution_result),
trevm
);
*pre_sim_coinbase_balance = post_sim_coinbase_balance;
Ok(trevm)
}
}
impl<Db, Insp> BundleDriver<Db, SignetLayered<Insp>> for SignetBundleDriver<'_>
where
Db: Database + DatabaseCommit,
Insp: Inspector<Ctx<Db>>,
{
type Error = BundleError<Db>;
#[instrument(skip_all, level = Level::DEBUG)]
fn run_bundle(&mut self, trevm: EvmNeedsTx<Db, Insp>) -> DriveBundleResult<Self, Db, Insp> {
let bundle = &self.bundle.bundle;
trevm_ensure!(!bundle.txs.is_empty(), trevm, BundleError::BundleEmpty);
trevm_ensure!(
trevm.block_number().to::<u64>() == bundle.block_number,
trevm,
BundleError::BlockNumberMismatch
);
self.response.state_block_number = trevm.block_number().to();
trevm_ensure!(
bundle.state_block_number.is_number()
&& bundle.state_block_number.as_number().unwrap_or_default() != 0,
trevm,
BundleError::BlockNumberMismatch
);
let txs = trevm_try!(self.bundle.decode_and_validate_txs(), trevm);
trevm.try_with_block(self.bundle, |mut trevm| {
let coinbase = trevm.beneficiary();
let basefee = trevm.block().basefee;
let initial_coinbase_balance = trevm_try!(
trevm
.try_read_balance(coinbase)
.map_err(EVMError::Database)
.map_err(BundleError::from),
trevm
);
let mut pre_sim_coinbase_balance = initial_coinbase_balance;
let span = debug_span!("bundle loop", count = txs.len()).entered();
for (idx, tx) in txs.iter().enumerate() {
let _span = debug_span!("tx loop", tx = %tx.tx_hash(), idx).entered();
let run_result = trevm.run_tx(tx);
let transacted_trevm = run_result.map_err(|e| e.map_err(Into::into))?;
trevm = self.accept_and_accumulate(
transacted_trevm,
tx,
&mut pre_sim_coinbase_balance,
basefee,
)?;
}
drop(span);
self.response.coinbase_diff =
pre_sim_coinbase_balance.saturating_sub(initial_coinbase_balance);
self.response.eth_sent_to_coinbase =
self.response.coinbase_diff.saturating_sub(self.response.gas_fees);
self.response.bundle_gas_price = self
.response
.coinbase_diff
.checked_div(U256::from(self.response.total_gas_used))
.unwrap_or_default();
self.response.bundle_hash = self.bundle.bundle_hash();
let (fills, orders) =
trevm.inner_mut_unchecked().inspector.as_mut_detector().take_aggregates();
self.response.orders = orders;
self.response.fills = fills;
Ok(trevm)
})
}
fn post_bundle(&mut self, _trevm: &EvmNeedsTx<Db, Insp>) -> Result<(), Self::Error> {
Ok(())
}
}