use alloy_evm::{precompiles::PrecompilesMap, Database, EvmEnv};
use op_revm::L1BlockInfo;
use revm::{context::result::EVMError, Inspector};
use crate::{
DynPrecompilesBuilder, EmptyExternalEnv, EvmTxRuntimeLimits, ExternalEnvFactory, MegaContext,
MegaEvm, MegaHaltReason, MegaSpecId, MegaTransaction, MegaTransactionError,
};
#[derive(derive_more::Debug, Clone)]
#[non_exhaustive]
pub struct MegaEvmFactory<ExtEnvFactory> {
external_env_factory: ExtEnvFactory,
#[debug(ignore)]
dyn_precompiles_builder: Option<DynPrecompilesBuilder>,
}
impl Default for MegaEvmFactory<EmptyExternalEnv> {
fn default() -> Self {
Self::new()
}
}
impl MegaEvmFactory<EmptyExternalEnv> {
pub fn new() -> Self {
Self { external_env_factory: EmptyExternalEnv, dyn_precompiles_builder: None }
}
}
impl<ExtEnvFactory> MegaEvmFactory<ExtEnvFactory> {
pub fn with_dyn_precompiles_builder(
mut self,
dyn_precompiles_builder: DynPrecompilesBuilder,
) -> Self {
self.dyn_precompiles_builder = Some(dyn_precompiles_builder);
self
}
pub fn external_env_factory(&self) -> &ExtEnvFactory {
&self.external_env_factory
}
pub fn with_external_env_factory<NewExtEnvFactory: ExternalEnvFactory>(
self,
external_env_factory: NewExtEnvFactory,
) -> MegaEvmFactory<NewExtEnvFactory> {
MegaEvmFactory {
external_env_factory,
dyn_precompiles_builder: self.dyn_precompiles_builder,
}
}
}
impl<ExtEnvFactory: ExternalEnvFactory + Clone> alloy_evm::EvmFactory
for MegaEvmFactory<ExtEnvFactory>
{
type Evm<DB: Database, I: Inspector<Self::Context<DB>>> =
MegaEvm<DB, I, ExtEnvFactory::EnvTypes>;
type Context<DB: Database> = MegaContext<DB, ExtEnvFactory::EnvTypes>;
type Tx = MegaTransaction;
type Error<DBError: core::error::Error + Send + Sync + 'static> =
EVMError<DBError, MegaTransactionError>;
type HaltReason = MegaHaltReason;
type Spec = MegaSpecId;
type Precompiles = PrecompilesMap;
fn create_evm<DB: Database>(
&self,
db: DB,
evm_env: EvmEnv<Self::Spec>,
) -> Self::Evm<DB, revm::inspector::NoOpInspector> {
let spec_id = *evm_env.spec_id();
let block_number = evm_env.block_env.number.to();
let runtime_limits = EvmTxRuntimeLimits::from_spec(spec_id);
let ctx = MegaContext::new(db, spec_id)
.with_external_envs(self.external_env_factory.external_envs(block_number))
.with_tx(MegaTransaction::default())
.with_block(evm_env.block_env)
.with_cfg(evm_env.cfg_env)
.with_chain(L1BlockInfo::default())
.with_tx_runtime_limits(runtime_limits);
MegaEvm::new(ctx).with_dyn_precompiles(
self.dyn_precompiles_builder
.as_ref()
.map_or_else(Default::default, |builder| builder(spec_id)),
)
}
fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
&self,
db: DB,
input: EvmEnv<Self::Spec>,
inspector: I,
) -> Self::Evm<DB, I> {
Self::create_evm(self, db, input).with_inspector(inspector)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_external_env_factory_getter() {
let factory = MegaEvmFactory::new().with_external_env_factory(EmptyExternalEnv);
let got: &EmptyExternalEnv = factory.external_env_factory();
assert!(core::ptr::eq(got, factory.external_env_factory()));
}
}