1use alloy_evm::precompiles::DynPrecompile;
2use alloy_primitives::Address;
3use std::fmt::Debug;
4
5#[cfg(feature = "optimism")]
6mod optimism;
7
8pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
11 fn precompiles(&self) -> Vec<(Address, DynPrecompile)>;
13}
14
15#[cfg(test)]
16mod tests {
17 use std::convert::Infallible;
18
19 use crate::PrecompileFactory;
20 use alloy_evm::{
21 EthEvm, Evm,
22 eth::EthEvmContext,
23 precompiles::{DynPrecompile, PrecompilesMap},
24 };
25 use alloy_primitives::{Address, Bytes, TxKind, address};
26 use itertools::Itertools;
27 use revm::{
28 Journal,
29 context::{BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv},
30 database::{EmptyDB, EmptyDBTyped},
31 handler::{EthPrecompiles, instructions::EthInstructions},
32 inspector::NoOpInspector,
33 interpreter::interpreter::EthInterpreter,
34 precompile::{PrecompileOutput, PrecompileSpecId, PrecompileStatus, Precompiles},
35 primitives::hardfork::SpecId,
36 };
37
38 pub(super) const ETH_PRAGUE_PRECOMPILE: Address =
40 address!("0x0000000000000000000000000000000000000011");
41
42 const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100");
44
45 pub(super) const PRECOMPILE_ADDR: Address =
47 address!("0x0000000000000000000000000000000000000071");
48 pub(super) const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
49
50 #[derive(Debug)]
51 pub(super) struct CustomPrecompileFactory;
52
53 impl PrecompileFactory for CustomPrecompileFactory {
54 fn precompiles(&self) -> Vec<(Address, DynPrecompile)> {
55 use alloy_evm::precompiles::PrecompileInput;
56 vec![(
57 PRECOMPILE_ADDR,
58 DynPrecompile::from(|input: PrecompileInput<'_>| {
59 Ok(PrecompileOutput {
60 status: PrecompileStatus::Success,
61 bytes: Bytes::copy_from_slice(input.data),
62 gas_used: 0,
63 gas_refunded: 0,
64 state_gas_used: 0,
65 reservoir: input.reservoir,
66 })
67 }),
68 )]
69 }
70 }
71
72 fn create_eth_evm(
74 spec: SpecId,
75 ) -> (TxEnv, EthEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>) {
76 let tx_env = TxEnv {
77 kind: TxKind::Call(PRECOMPILE_ADDR),
78 data: PAYLOAD.into(),
79 ..Default::default()
80 };
81
82 let eth_evm_context = EthEvmContext {
83 journaled_state: Journal::new(EmptyDB::default()),
84 block: BlockEnv::default(),
85 cfg: CfgEnv::new_with_spec(spec),
86 tx: tx_env.clone(),
87 chain: (),
88 local: LocalContext::default(),
89 error: Ok(()),
90 };
91
92 let eth_precompiles = EthPrecompiles {
93 precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
94 spec,
95 }
96 .precompiles;
97 let eth_evm = EthEvm::new(
98 RevmEvm::new_with_inspector(
99 eth_evm_context,
100 NoOpInspector,
101 EthInstructions::<EthInterpreter, EthEvmContext<EmptyDB>>::new_mainnet_with_spec(
102 spec,
103 ),
104 PrecompilesMap::from_static(eth_precompiles),
105 ),
106 true,
107 );
108
109 (tx_env, eth_evm)
110 }
111
112 #[test]
113 fn build_eth_evm_with_extra_precompiles_osaka_spec() {
114 let (tx_env, mut evm) = create_eth_evm(SpecId::OSAKA);
115
116 assert!(evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
117 assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
118 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
119
120 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
121
122 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
123
124 let result = evm.transact(tx_env).unwrap();
125 assert!(result.result.is_success());
126 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
127 }
128
129 #[test]
130 fn build_eth_evm_with_extra_precompiles_london_spec() {
131 let (tx_env, mut evm) = create_eth_evm(SpecId::LONDON);
132
133 assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
134 assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
135 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
136
137 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
138
139 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
140
141 let result = evm.transact(tx_env).unwrap();
142 assert!(result.result.is_success());
143 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
144 }
145
146 #[test]
147 fn build_eth_evm_with_extra_precompiles_prague_spec() {
148 let (tx_env, mut evm) = create_eth_evm(SpecId::PRAGUE);
149
150 assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
151 assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
152 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
153
154 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
155
156 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
157
158 let result = evm.transact(tx_env).unwrap();
159 assert!(result.result.is_success());
160 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
161 }
162}