alloy_evm/
precompiles.rs

1//! Helpers for dealing with Precompiles.
2
3use crate::{Database, EvmInternals};
4use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc};
5use alloy_consensus::transaction::Either;
6use alloy_primitives::{
7    map::{HashMap, HashSet},
8    Address, Bytes, U256,
9};
10use core::fmt::Debug;
11use revm::{
12    context::LocalContextTr,
13    handler::{EthPrecompiles, PrecompileProvider},
14    interpreter::{CallInput, CallInputs, Gas, InstructionResult, InterpreterResult},
15    precompile::{PrecompileError, PrecompileFn, PrecompileId, PrecompileResult, Precompiles},
16    Context, Journal,
17};
18
19/// A mapping of precompile contracts that can be either static (builtin) or dynamic.
20///
21/// This is an optimization that allows us to keep using the static precompiles
22/// until we need to modify them, at which point we convert to the dynamic representation.
23#[derive(Clone)]
24pub struct PrecompilesMap {
25    /// The wrapped precompiles in their current representation.
26    precompiles: PrecompilesKind,
27    /// An optional dynamic precompile loader that can lookup precompiles dynamically.
28    lookup: Option<Arc<dyn PrecompileLookup>>,
29}
30
31impl PrecompilesMap {
32    /// Creates the [`PrecompilesMap`] from a static reference.
33    pub fn from_static(precompiles: &'static Precompiles) -> Self {
34        Self::new(Cow::Borrowed(precompiles))
35    }
36
37    /// Creates a new set of precompiles for a spec.
38    pub fn new(precompiles: Cow<'static, Precompiles>) -> Self {
39        Self { precompiles: PrecompilesKind::Builtin(precompiles), lookup: None }
40    }
41
42    /// Maps a precompile at the given address using the provided function.
43    pub fn map_precompile<F>(&mut self, address: &Address, f: F)
44    where
45        F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
46    {
47        let dyn_precompiles = self.ensure_dynamic_precompiles();
48
49        // get the current precompile at the address
50        if let Some(dyn_precompile) = dyn_precompiles.inner.remove(address) {
51            // apply the transformation function
52            let transformed = f(dyn_precompile);
53
54            // update the precompile at the address
55            dyn_precompiles.inner.insert(*address, transformed);
56        }
57    }
58
59    /// Maps all precompiles using the provided function.
60    pub fn map_precompiles<F>(&mut self, f: F)
61    where
62        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
63    {
64        self.map_precompiles_filtered(f, |_, _| true);
65    }
66
67    /// Maps all pure precompiles using the provided function.
68    ///
69    /// This is a variant of [`Self::map_precompiles`] that only applies the transformation
70    /// to precompiles that are pure, see [`Precompile::is_pure`].
71    pub fn map_pure_precompiles<F>(&mut self, f: F)
72    where
73        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
74    {
75        self.map_precompiles_filtered(f, |_, precompile| precompile.is_pure());
76    }
77
78    /// Internal helper to map precompiles with an optional filter.
79    ///
80    /// The `filter` decides whether to apply the mapping function `f` to a given
81    /// precompile. If the filter returns `false`, the original precompile is kept.
82    #[inline]
83    fn map_precompiles_filtered<F, P>(&mut self, mut f: F, mut filter: P)
84    where
85        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
86        P: FnMut(&Address, &DynPrecompile) -> bool,
87    {
88        let dyn_precompiles = self.ensure_dynamic_precompiles();
89
90        // apply the transformation to each precompile
91        let entries = dyn_precompiles.inner.drain();
92        let mut new_map =
93            HashMap::with_capacity_and_hasher(entries.size_hint().0, Default::default());
94        for (addr, precompile) in entries {
95            if filter(&addr, &precompile) {
96                let transformed = f(&addr, precompile);
97                new_map.insert(addr, transformed);
98            } else {
99                new_map.insert(addr, precompile);
100            }
101        }
102
103        dyn_precompiles.inner = new_map;
104    }
105
106    /// Applies a transformation to the precompile at the given address.
107    ///
108    /// This method allows you to add, update, or remove a precompile by applying a closure
109    /// to the existing precompile (if any) at the specified address.
110    ///
111    /// # Behavior
112    ///
113    /// The closure receives:
114    /// - `Some(precompile)` if a precompile exists at the address
115    /// - `None` if no precompile exists at the address
116    ///
117    /// Based on what the closure returns:
118    /// - `Some(precompile)` - Insert or replace the precompile at the address
119    /// - `None` - Remove the precompile from the address (if it exists)
120    ///
121    /// # Examples
122    ///
123    /// ```ignore
124    /// // Add a new precompile
125    /// precompiles.apply_precompile(&address, |_| Some(my_precompile));
126    ///
127    /// // Update an existing precompile
128    /// precompiles.apply_precompile(&address, |existing| {
129    ///     existing.map(|p| wrap_with_logging(p))
130    /// });
131    ///
132    /// // Remove a precompile
133    /// precompiles.apply_precompile(&address, |_| None);
134    ///
135    /// // Conditionally update
136    /// precompiles.apply_precompile(&address, |existing| {
137    ///     if let Some(p) = existing {
138    ///         Some(modify_precompile(p))
139    ///     } else {
140    ///         Some(create_default_precompile())
141    ///     }
142    /// });
143    /// ```
144    pub fn apply_precompile<F>(&mut self, address: &Address, f: F)
145    where
146        F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
147    {
148        let dyn_precompiles = self.ensure_dynamic_precompiles();
149        let current = dyn_precompiles.inner.get(address).cloned();
150
151        // apply the transformation function
152        let result = f(current);
153
154        match result {
155            Some(transformed) => {
156                // insert the transformed precompile
157                dyn_precompiles.inner.insert(*address, transformed);
158                dyn_precompiles.addresses.insert(*address);
159            }
160            None => {
161                // remove the precompile if the transformation returned None
162                dyn_precompiles.inner.remove(address);
163                dyn_precompiles.addresses.remove(address);
164            }
165        }
166    }
167
168    /// Builder-style method that maps a precompile at the given address using the provided
169    /// function.
170    ///
171    /// This is a consuming version of [`map_precompile`](Self::map_precompile) that returns `Self`.
172    pub fn with_mapped_precompile<F>(mut self, address: &Address, f: F) -> Self
173    where
174        F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
175    {
176        self.map_precompile(address, f);
177        self
178    }
179
180    /// Builder-style method that maps all precompiles using the provided function.
181    ///
182    /// This is a consuming version of [`map_precompiles`](Self::map_precompiles) that returns
183    /// `Self`.
184    pub fn with_mapped_precompiles<F>(mut self, f: F) -> Self
185    where
186        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
187    {
188        self.map_precompiles(f);
189        self
190    }
191
192    /// Builder-style method that applies a transformation to the precompile at the given address.
193    ///
194    /// This is a consuming version of [`apply_precompile`](Self::apply_precompile) that returns
195    /// `Self`. See [`apply_precompile`](Self::apply_precompile) for detailed behavior and
196    /// examples.
197    pub fn with_applied_precompile<F>(mut self, address: &Address, f: F) -> Self
198    where
199        F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
200    {
201        self.apply_precompile(address, f);
202        self
203    }
204
205    /// Extends the precompile map with multiple precompiles.
206    ///
207    /// This is a convenience method for inserting or replacing multiple precompiles at once.
208    /// Each precompile in the iterator is applied to its corresponding address.
209    ///
210    /// **Note**: This method will **replace** any existing precompiles at the given addresses.
211    /// If you need to modify existing precompiles, use [`map_precompile`](Self::map_precompile)
212    /// or [`apply_precompile`](Self::apply_precompile) instead.
213    ///
214    /// # Examples
215    ///
216    /// ```ignore
217    /// let precompiles = vec![
218    ///     (address1, my_precompile1),
219    ///     (address2, my_precompile2),
220    /// ];
221    /// precompiles_map.extend_precompiles(precompiles);
222    /// ```
223    pub fn extend_precompiles<I>(&mut self, precompiles: I)
224    where
225        I: IntoIterator<Item = (Address, DynPrecompile)>,
226    {
227        for (addr, precompile) in precompiles {
228            self.apply_precompile(&addr, |_| Some(precompile));
229        }
230    }
231
232    /// Builder-style method that extends the precompile map with multiple precompiles.
233    ///
234    /// This is a consuming version of [`extend_precompiles`](Self::extend_precompiles) that returns
235    /// `Self`.
236    ///
237    /// **Note**: This method will **replace** any existing precompiles at the given addresses.
238    /// If you need to modify existing precompiles, use
239    /// [`with_mapped_precompile`](Self::with_mapped_precompile)
240    /// or [`with_applied_precompile`](Self::with_applied_precompile) instead.
241    ///
242    /// # Examples
243    ///
244    /// ```ignore
245    /// let precompiles = vec![
246    ///     (address1, my_precompile1),
247    ///     (address2, my_precompile2),
248    /// ];
249    /// let map = PrecompilesMap::new(precompiles_cow)
250    ///     .with_extended_precompiles(precompiles);
251    /// ```
252    pub fn with_extended_precompiles<I>(mut self, precompiles: I) -> Self
253    where
254        I: IntoIterator<Item = (Address, DynPrecompile)>,
255    {
256        self.extend_precompiles(precompiles);
257        self
258    }
259
260    /// Sets a dynamic precompile lookup function that is called for addresses not found
261    /// in the static precompile map.
262    ///
263    /// This method allows you to provide runtime-resolved precompiles that aren't known
264    /// at initialization time. The lookup function is called whenever a precompile check
265    /// is performed for an address that doesn't exist in the main precompile map.
266    ///
267    /// # Important Notes
268    ///
269    /// - **Priority**: Static precompiles take precedence. The lookup function is only called if
270    ///   the address is not found in the main precompile map.
271    /// - **Gas accounting**: Addresses resolved through this lookup are always treated as cold,
272    ///   meaning they incur cold access costs even on repeated calls within the same transaction.
273    ///   See also [`PrecompileProvider::warm_addresses`].
274    /// - **Performance**: The lookup function is called on every precompile check for
275    ///   non-registered addresses, so it should be efficient.
276    ///
277    /// # Example
278    ///
279    /// ```ignore
280    /// precompiles.set_precompile_lookup(|address| {
281    ///     // Dynamically resolve precompiles based on address pattern
282    ///     if address.as_slice().starts_with(&[0xDE, 0xAD]) {
283    ///         Some(DynPrecompile::new(|input| {
284    ///             // Custom precompile logic
285    ///             Ok(PrecompileOutput {
286    ///                 gas_used: 100,
287    ///                 bytes: Bytes::from("dynamic precompile"),
288    ///             })
289    ///         }))
290    ///     } else {
291    ///         None
292    ///     }
293    /// });
294    /// ```
295    pub fn set_precompile_lookup<L>(&mut self, lookup: L)
296    where
297        L: PrecompileLookup + 'static,
298    {
299        self.lookup = Some(Arc::new(lookup));
300    }
301
302    /// Builder-style method to set a dynamic precompile lookup function.
303    ///
304    /// This is a consuming version of [`set_precompile_lookup`](Self::set_precompile_lookup)
305    /// that returns `Self` for method chaining.
306    ///
307    /// See [`set_precompile_lookup`](Self::set_precompile_lookup) for detailed behavior,
308    /// important notes, and examples.
309    pub fn with_precompile_lookup<L>(mut self, lookup: L) -> Self
310    where
311        L: PrecompileLookup + 'static,
312    {
313        self.set_precompile_lookup(lookup);
314        self
315    }
316
317    /// Consumes the type and returns a set of [`DynPrecompile`].
318    pub fn into_dyn_precompiles(mut self) -> DynPrecompiles {
319        self.ensure_dynamic_precompiles();
320        match self.precompiles {
321            PrecompilesKind::Dynamic(dynamic) => dynamic,
322            _ => unreachable!("We just ensured that this is a Dynamic variant"),
323        }
324    }
325
326    /// Ensures that precompiles are in their dynamic representation.
327    /// If they are already dynamic, this is a no-op.
328    /// Returns a mutable reference to the dynamic precompiles.
329    pub fn ensure_dynamic_precompiles(&mut self) -> &mut DynPrecompiles {
330        if let PrecompilesKind::Builtin(ref precompiles_cow) = self.precompiles {
331            let mut dynamic = DynPrecompiles::default();
332
333            let static_precompiles = match precompiles_cow {
334                Cow::Borrowed(static_ref) => static_ref,
335                Cow::Owned(owned) => owned,
336            };
337
338            for (&addr, pc) in static_precompiles.inner().iter() {
339                dynamic.inner.insert(addr, DynPrecompile::from(*pc.precompile()));
340                dynamic.addresses.insert(addr);
341            }
342
343            self.precompiles = PrecompilesKind::Dynamic(dynamic);
344        }
345
346        match &mut self.precompiles {
347            PrecompilesKind::Dynamic(dynamic) => dynamic,
348            _ => unreachable!("We just ensured that this is a Dynamic variant"),
349        }
350    }
351
352    /// Returns an iterator over the [`PrecompileId`]s of the installed precompiles.
353    pub fn identifiers(&self) -> impl Iterator<Item = &PrecompileId> {
354        match &self.precompiles {
355            PrecompilesKind::Builtin(precompiles) => {
356                Either::Left(precompiles.inner().values().map(|p| p.precompile_id()))
357            }
358            PrecompilesKind::Dynamic(dyn_precompiles) => {
359                Either::Right(dyn_precompiles.inner.values().map(|p| p.precompile_id()))
360            }
361        }
362    }
363
364    /// Returns an iterator over references to precompile addresses.
365    pub fn addresses(&self) -> impl Iterator<Item = &Address> {
366        match &self.precompiles {
367            PrecompilesKind::Builtin(precompiles) => Either::Left(precompiles.addresses()),
368            PrecompilesKind::Dynamic(dyn_precompiles) => {
369                Either::Right(dyn_precompiles.addresses.iter())
370            }
371        }
372    }
373
374    /// Gets a reference to the precompile at the given address.
375    ///
376    /// This method first checks the static precompile map, and if not found,
377    /// falls back to the dynamic lookup function (if set).
378    pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
379        // First check static precompiles
380        let static_result = match &self.precompiles {
381            PrecompilesKind::Builtin(precompiles) => precompiles.get(address).map(Either::Left),
382            PrecompilesKind::Dynamic(dyn_precompiles) => {
383                dyn_precompiles.inner.get(address).map(Either::Right)
384            }
385        };
386
387        // If found in static precompiles, wrap in Left and return
388        if let Some(precompile) = static_result {
389            return Some(Either::Left(precompile));
390        }
391
392        // Otherwise, try the lookup function if available
393        let lookup = self.lookup.as_ref()?;
394        lookup.lookup(address).map(Either::Right)
395    }
396}
397
398impl From<EthPrecompiles> for PrecompilesMap {
399    fn from(value: EthPrecompiles) -> Self {
400        Self::from_static(value.precompiles)
401    }
402}
403
404impl core::fmt::Debug for PrecompilesMap {
405    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
406        match &self.precompiles {
407            PrecompilesKind::Builtin(_) => f.debug_struct("PrecompilesMap::Builtin").finish(),
408            PrecompilesKind::Dynamic(precompiles) => f
409                .debug_struct("PrecompilesMap::Dynamic")
410                .field("addresses", &precompiles.addresses)
411                .finish(),
412        }
413    }
414}
415
416impl<BlockEnv, TxEnv, CfgEnv, DB, Chain>
417    PrecompileProvider<Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>> for PrecompilesMap
418where
419    BlockEnv: revm::context::Block,
420    TxEnv: revm::context::Transaction,
421    CfgEnv: revm::context::Cfg,
422    DB: Database,
423{
424    type Output = InterpreterResult;
425
426    fn set_spec(&mut self, _spec: CfgEnv::Spec) -> bool {
427        false
428    }
429
430    fn run(
431        &mut self,
432        context: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>,
433        inputs: &CallInputs,
434    ) -> Result<Option<InterpreterResult>, String> {
435        // Get the precompile at the address
436        let Some(precompile) = self.get(&inputs.bytecode_address) else {
437            return Ok(None);
438        };
439
440        let mut result = InterpreterResult {
441            result: InstructionResult::Return,
442            gas: Gas::new(inputs.gas_limit),
443            output: Bytes::new(),
444        };
445
446        let (local, journal) = (&context.local, &mut context.journaled_state);
447
448        // Execute the precompile
449        let r;
450        let input_bytes = match &inputs.input {
451            CallInput::SharedBuffer(range) => {
452                // `map_or` does not work here as we use `r` to extend lifetime of the slice
453                // and return it.
454                #[allow(clippy::option_if_let_else)]
455                if let Some(slice) = local.shared_memory_buffer_slice(range.clone()) {
456                    r = slice;
457                    &*r
458                } else {
459                    &[]
460                }
461            }
462            CallInput::Bytes(bytes) => bytes.as_ref(),
463        };
464
465        let precompile_result = precompile.call(PrecompileInput {
466            data: input_bytes,
467            gas: inputs.gas_limit,
468            caller: inputs.caller,
469            value: inputs.call_value(),
470            internals: EvmInternals::new(journal, &context.block),
471            target_address: inputs.target_address,
472            bytecode_address: inputs.bytecode_address,
473        });
474
475        match precompile_result {
476            Ok(output) => {
477                let underflow = result.gas.record_cost(output.gas_used);
478                assert!(underflow, "Gas underflow is not possible");
479                result.result = if output.reverted {
480                    InstructionResult::Revert
481                } else {
482                    InstructionResult::Return
483                };
484                result.output = output.bytes;
485            }
486            Err(PrecompileError::Fatal(e)) => return Err(e),
487            Err(e) => {
488                result.result = if e.is_oog() {
489                    InstructionResult::PrecompileOOG
490                } else {
491                    InstructionResult::PrecompileError
492                };
493            }
494        };
495
496        Ok(Some(result))
497    }
498
499    fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
500        Box::new(self.addresses().copied())
501    }
502
503    fn contains(&self, address: &Address) -> bool {
504        self.get(address).is_some()
505    }
506}
507
508/// A mapping of precompile contracts that can be either static (builtin) or dynamic.
509///
510/// This is an optimization that allows us to keep using the static precompiles
511/// until we need to modify them, at which point we convert to the dynamic representation.
512#[derive(Clone)]
513enum PrecompilesKind {
514    /// Static builtin precompiles.
515    Builtin(Cow<'static, Precompiles>),
516    /// Dynamic precompiles that can be modified at runtime.
517    Dynamic(DynPrecompiles),
518}
519
520/// A dynamic precompile implementation that can be modified at runtime.
521#[derive(Clone)]
522pub struct DynPrecompile(pub(crate) Arc<dyn Precompile + Send + Sync>);
523
524impl DynPrecompile {
525    /// Creates a new [`DynPrecompiles`] with the given closure.
526    pub fn new<F>(id: PrecompileId, f: F) -> Self
527    where
528        F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
529    {
530        Self(Arc::new((id, f)))
531    }
532
533    /// Creates a new [`DynPrecompiles`] with the given closure and [`Precompile::is_pure`]
534    /// returning `false`.
535    pub fn new_stateful<F>(id: PrecompileId, f: F) -> Self
536    where
537        F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
538    {
539        Self(Arc::new(StatefulPrecompile((id, f))))
540    }
541
542    /// Flips [`Precompile::is_pure`] to `false`.
543    pub fn stateful(self) -> Self {
544        Self(Arc::new(StatefulPrecompile(self.0)))
545    }
546}
547
548impl core::fmt::Debug for DynPrecompile {
549    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
550        f.debug_struct("DynPrecompile").finish()
551    }
552}
553
554/// A mutable representation of precompiles that allows for runtime modification.
555///
556/// This structure stores dynamic precompiles that can be modified at runtime,
557/// unlike the static `Precompiles` struct from revm.
558#[derive(Clone, Default)]
559pub struct DynPrecompiles {
560    /// Precompiles
561    inner: HashMap<Address, DynPrecompile>,
562    /// Addresses of precompile
563    addresses: HashSet<Address>,
564}
565
566impl DynPrecompiles {
567    /// Consumes the type and returns an iterator over the addresses and the corresponding
568    /// precompile.
569    pub fn into_precompiles(self) -> impl Iterator<Item = (Address, DynPrecompile)> {
570        self.inner.into_iter()
571    }
572}
573
574impl core::fmt::Debug for DynPrecompiles {
575    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
576        f.debug_struct("DynPrecompiles").field("addresses", &self.addresses).finish()
577    }
578}
579
580/// Input for a precompile call.
581#[derive(Debug)]
582pub struct PrecompileInput<'a> {
583    /// Input data bytes.
584    pub data: &'a [u8],
585    /// Gas limit.
586    pub gas: u64,
587    /// Caller address.
588    pub caller: Address,
589    /// Value sent with the call.
590    pub value: U256,
591    /// Target address of the call. Would be the same as `bytecode_address` unless it's a
592    /// DELEGATECALL.
593    pub target_address: Address,
594    /// Bytecode address of the call.
595    pub bytecode_address: Address,
596    /// Various hooks for interacting with the EVM state.
597    pub internals: EvmInternals<'a>,
598}
599
600impl<'a> PrecompileInput<'a> {
601    /// Returns the calldata of the call.
602    pub const fn data(&self) -> &[u8] {
603        self.data
604    }
605
606    /// Returns the caller address of the call.
607    pub const fn caller(&self) -> &Address {
608        &self.caller
609    }
610
611    /// Returns the gas limit of the call.
612    pub const fn gas(&self) -> u64 {
613        self.gas
614    }
615
616    /// Returns the value of the call.
617    pub const fn value(&self) -> &U256 {
618        &self.value
619    }
620
621    /// Returns the target address of the call.
622    pub const fn target_address(&self) -> &Address {
623        &self.target_address
624    }
625
626    /// Returns the bytecode address of the call.
627    pub const fn bytecode_address(&self) -> &Address {
628        &self.bytecode_address
629    }
630
631    /// Returns whether the call is a direct call, i.e when precompile was called directly and not
632    /// via a DELEGATECALL/CALLCODE.
633    pub fn is_direct_call(&self) -> bool {
634        self.target_address == self.bytecode_address
635    }
636
637    /// Returns the [`EvmInternals`].
638    pub const fn internals(&self) -> &EvmInternals<'_> {
639        &self.internals
640    }
641
642    /// Returns a mutable reference to the [`EvmInternals`].
643    pub const fn internals_mut(&mut self) -> &mut EvmInternals<'a> {
644        &mut self.internals
645    }
646}
647
648/// Trait for implementing precompiled contracts.
649#[auto_impl::auto_impl(&, Arc)]
650pub trait Precompile {
651    /// Returns precompile ID.
652    fn precompile_id(&self) -> &PrecompileId;
653
654    /// Execute the precompile with the given input data, gas limit, and caller address.
655    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult;
656
657    /// Returns whether the precompile is pure.
658    ///
659    /// A pure precompile has deterministic output based solely on its input.
660    /// Non-pure precompiles may produce different outputs for the same input
661    /// based on the current state or other external factors.
662    ///
663    /// # Default
664    ///
665    /// Returns `true` by default, indicating the precompile is pure
666    /// and its results should be cached as this is what most of the precompiles are.
667    ///
668    /// # Examples
669    ///
670    /// Override this method to return `false` for non-deterministic precompiles:
671    ///
672    /// ```ignore
673    /// impl Precompile for MyDeterministicPrecompile {
674    ///     fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
675    ///         // non-deterministic computation dependent on state
676    ///     }
677    ///
678    ///     fn is_pure(&self) -> bool {
679    ///         false // This precompile might produce different output for the same input
680    ///     }
681    /// }
682    /// ```
683    fn is_pure(&self) -> bool {
684        true
685    }
686}
687
688impl<F> Precompile for (PrecompileId, F)
689where
690    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
691{
692    fn precompile_id(&self) -> &PrecompileId {
693        &self.0
694    }
695
696    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
697        self.1(input)
698    }
699}
700
701impl<F> Precompile for (&PrecompileId, F)
702where
703    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
704{
705    fn precompile_id(&self) -> &PrecompileId {
706        self.0
707    }
708
709    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
710        self.1(input)
711    }
712}
713
714impl Precompile for revm::precompile::Precompile {
715    fn precompile_id(&self) -> &PrecompileId {
716        self.id()
717    }
718
719    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
720        self.precompile()(input.data, input.gas)
721    }
722}
723
724impl<F> From<F> for DynPrecompile
725where
726    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
727{
728    fn from(f: F) -> Self {
729        Self::new(PrecompileId::Custom("closure".into()), f)
730    }
731}
732
733impl From<PrecompileFn> for DynPrecompile {
734    fn from(f: PrecompileFn) -> Self {
735        let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
736        p.into()
737    }
738}
739
740impl<F> From<(PrecompileId, F)> for DynPrecompile
741where
742    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
743{
744    fn from((id, f): (PrecompileId, F)) -> Self {
745        Self(Arc::new((id, f)))
746    }
747}
748
749impl From<(PrecompileId, PrecompileFn)> for DynPrecompile {
750    fn from((id, f): (PrecompileId, PrecompileFn)) -> Self {
751        let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
752        (id, p).into()
753    }
754}
755
756impl Precompile for DynPrecompile {
757    fn precompile_id(&self) -> &PrecompileId {
758        self.0.precompile_id()
759    }
760
761    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
762        self.0.call(input)
763    }
764
765    fn is_pure(&self) -> bool {
766        self.0.is_pure()
767    }
768}
769
770impl<A: Precompile, B: Precompile> Precompile for Either<A, B> {
771    fn precompile_id(&self) -> &PrecompileId {
772        match self {
773            Self::Left(p) => p.precompile_id(),
774            Self::Right(p) => p.precompile_id(),
775        }
776    }
777
778    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
779        match self {
780            Self::Left(p) => p.call(input),
781            Self::Right(p) => p.call(input),
782        }
783    }
784
785    fn is_pure(&self) -> bool {
786        match self {
787            Self::Left(p) => p.is_pure(),
788            Self::Right(p) => p.is_pure(),
789        }
790    }
791}
792
793struct StatefulPrecompile<P>(P);
794
795impl<P: Precompile> Precompile for StatefulPrecompile<P> {
796    fn precompile_id(&self) -> &PrecompileId {
797        self.0.precompile_id()
798    }
799
800    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
801        self.0.call(input)
802    }
803
804    fn is_pure(&self) -> bool {
805        false
806    }
807}
808
809/// Trait for dynamically resolving precompile contracts.
810///
811/// This trait allows for runtime resolution of precompiles that aren't known
812/// at initialization time.
813pub trait PrecompileLookup: Send + Sync {
814    /// Looks up a precompile at the given address.
815    ///
816    /// Returns `Some(precompile)` if a precompile exists at the address,
817    /// or `None` if no precompile is found.
818    fn lookup(&self, address: &Address) -> Option<DynPrecompile>;
819}
820
821/// Implement PrecompileLookup for closure types
822impl<F> PrecompileLookup for F
823where
824    F: Fn(&Address) -> Option<DynPrecompile> + Send + Sync,
825{
826    fn lookup(&self, address: &Address) -> Option<DynPrecompile> {
827        self(address)
828    }
829}
830
831#[cfg(test)]
832mod tests {
833    use super::*;
834    use crate::eth::EthEvmContext;
835    use alloy_primitives::{address, Bytes};
836    use revm::{
837        context::Block,
838        database::EmptyDB,
839        precompile::{PrecompileId, PrecompileOutput},
840    };
841
842    #[test]
843    fn test_map_precompile() {
844        let eth_precompiles = EthPrecompiles::default();
845        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
846
847        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
848
849        // create a test input for the precompile (identity precompile)
850        let identity_address = address!("0x0000000000000000000000000000000000000004");
851        let test_input = Bytes::from_static(b"test data");
852        let gas_limit = 1000;
853
854        // Ensure we're using dynamic precompiles
855        spec_precompiles.ensure_dynamic_precompiles();
856
857        // using the dynamic precompiles interface
858        let dyn_precompile = match &spec_precompiles.precompiles {
859            PrecompilesKind::Dynamic(dyn_precompiles) => {
860                dyn_precompiles.inner.get(&identity_address).unwrap()
861            }
862            _ => panic!("Expected dynamic precompiles"),
863        };
864
865        let result = dyn_precompile
866            .call(PrecompileInput {
867                data: &test_input,
868                gas: gas_limit,
869                caller: Address::ZERO,
870                value: U256::ZERO,
871                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
872                target_address: identity_address,
873                bytecode_address: identity_address,
874            })
875            .unwrap();
876        assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
877
878        // define a function to modify the precompile
879        // this will change the identity precompile to always return a fixed value
880        let constant_bytes = Bytes::from_static(b"constant value");
881
882        // define a function to modify the precompile to always return a constant value
883        spec_precompiles.map_precompile(&identity_address, move |_original_dyn| {
884            // create a new DynPrecompile that always returns our constant
885            (|_input: PrecompileInput<'_>| -> PrecompileResult {
886                Ok(PrecompileOutput::new(10, Bytes::from_static(b"constant value")))
887            })
888            .into()
889        });
890
891        // get the modified precompile and check it
892        let dyn_precompile = match &spec_precompiles.precompiles {
893            PrecompilesKind::Dynamic(dyn_precompiles) => {
894                dyn_precompiles.inner.get(&identity_address).unwrap()
895            }
896            _ => panic!("Expected dynamic precompiles"),
897        };
898
899        let result = dyn_precompile
900            .call(PrecompileInput {
901                data: &test_input,
902                gas: gas_limit,
903                caller: Address::ZERO,
904                value: U256::ZERO,
905                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
906                target_address: identity_address,
907                bytecode_address: identity_address,
908            })
909            .unwrap();
910        assert_eq!(
911            result.bytes, constant_bytes,
912            "Modified precompile should return the constant value"
913        );
914    }
915
916    #[test]
917    fn test_closure_precompile() {
918        let test_input = Bytes::from_static(b"test data");
919        let expected_output = Bytes::from_static(b"processed: test data");
920        let gas_limit = 1000;
921
922        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
923
924        // define a closure that implements the precompile functionality
925        let closure_precompile = |input: PrecompileInput<'_>| -> PrecompileResult {
926            let _timestamp = input.internals.block_env().timestamp();
927            let mut output = b"processed: ".to_vec();
928            output.extend_from_slice(input.data.as_ref());
929            Ok(PrecompileOutput::new(15, Bytes::from(output)))
930        };
931
932        let dyn_precompile: DynPrecompile = closure_precompile.into();
933
934        let result = dyn_precompile
935            .call(PrecompileInput {
936                data: &test_input,
937                gas: gas_limit,
938                caller: Address::ZERO,
939                value: U256::ZERO,
940                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
941                target_address: Address::ZERO,
942                bytecode_address: Address::ZERO,
943            })
944            .unwrap();
945        assert_eq!(result.gas_used, 15);
946        assert_eq!(result.bytes, expected_output);
947    }
948
949    #[test]
950    fn test_is_pure() {
951        // Test default behavior (should be false)
952        let closure_precompile = |_input: PrecompileInput<'_>| -> PrecompileResult {
953            Ok(PrecompileOutput::new(10, Bytes::from_static(b"output")))
954        };
955
956        let dyn_precompile: DynPrecompile = closure_precompile.into();
957        assert!(dyn_precompile.is_pure(), "should be pure by default");
958
959        // Test custom precompile with overridden is_pure
960        let stateful_precompile =
961            DynPrecompile::new_stateful(PrecompileId::Custom("closure".into()), closure_precompile);
962        assert!(!stateful_precompile.is_pure(), "PurePrecompile should return true for is_pure");
963
964        let either_left = Either::<DynPrecompile, DynPrecompile>::Left(stateful_precompile);
965        assert!(!either_left.is_pure(), "Either::Left with non-pure should return false");
966
967        let either_right = Either::<DynPrecompile, DynPrecompile>::Right(dyn_precompile);
968        assert!(either_right.is_pure(), "Either::Right with pure should return true");
969    }
970
971    #[test]
972    fn test_precompile_lookup() {
973        let eth_precompiles = EthPrecompiles::default();
974        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
975
976        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
977
978        // Define a custom address pattern for dynamic precompiles
979        let dynamic_prefix = [0xDE, 0xAD];
980
981        // Set up the lookup function
982        spec_precompiles.set_precompile_lookup(move |address: &Address| {
983            if address.as_slice().starts_with(&dynamic_prefix) {
984                Some(DynPrecompile::new(PrecompileId::Custom("dynamic".into()), |_input| {
985                    Ok(PrecompileOutput {
986                        gas_used: 100,
987                        gas_refunded: 0,
988                        bytes: Bytes::from("dynamic precompile response"),
989                        reverted: false,
990                    })
991                }))
992            } else {
993                None
994            }
995        });
996
997        // Test that static precompiles still work
998        let identity_address = address!("0x0000000000000000000000000000000000000004");
999        assert!(spec_precompiles.get(&identity_address).is_some());
1000
1001        // Test dynamic lookup for matching address
1002        let dynamic_address = address!("0xDEAD000000000000000000000000000000000001");
1003        let dynamic_precompile = spec_precompiles.get(&dynamic_address);
1004        assert!(dynamic_precompile.is_some(), "Dynamic precompile should be found");
1005
1006        // Execute the dynamic precompile
1007        let result = dynamic_precompile
1008            .unwrap()
1009            .call(PrecompileInput {
1010                data: &[],
1011                gas: 1000,
1012                caller: Address::ZERO,
1013                value: U256::ZERO,
1014                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1015                target_address: dynamic_address,
1016                bytecode_address: dynamic_address,
1017            })
1018            .unwrap();
1019        assert_eq!(result.gas_used, 100);
1020        assert_eq!(result.bytes, Bytes::from("dynamic precompile response"));
1021
1022        // Test non-matching address returns None
1023        let non_matching_address = address!("0x1234000000000000000000000000000000000001");
1024        assert!(spec_precompiles.get(&non_matching_address).is_none());
1025    }
1026
1027    #[test]
1028    fn test_get_precompile() {
1029        let eth_precompiles = EthPrecompiles::default();
1030        let spec_precompiles = PrecompilesMap::from(eth_precompiles);
1031
1032        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1033
1034        let identity_address = address!("0x0000000000000000000000000000000000000004");
1035        let test_input = Bytes::from_static(b"test data");
1036        let gas_limit = 1000;
1037
1038        let precompile = spec_precompiles.get(&identity_address);
1039        assert!(precompile.is_some(), "Identity precompile should exist");
1040
1041        let result = precompile
1042            .unwrap()
1043            .call(PrecompileInput {
1044                data: &test_input,
1045                gas: gas_limit,
1046                caller: Address::ZERO,
1047                value: U256::ZERO,
1048                target_address: identity_address,
1049                bytecode_address: identity_address,
1050                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1051            })
1052            .unwrap();
1053        assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
1054
1055        let nonexistent_address = address!("0x0000000000000000000000000000000000000099");
1056        assert!(
1057            spec_precompiles.get(&nonexistent_address).is_none(),
1058            "Non-existent precompile should not be found"
1059        );
1060
1061        let mut dynamic_precompiles = spec_precompiles;
1062        dynamic_precompiles.ensure_dynamic_precompiles();
1063
1064        let dyn_precompile = dynamic_precompiles.get(&identity_address);
1065        assert!(
1066            dyn_precompile.is_some(),
1067            "Identity precompile should exist after conversion to dynamic"
1068        );
1069
1070        let result = dyn_precompile
1071            .unwrap()
1072            .call(PrecompileInput {
1073                data: &test_input,
1074                gas: gas_limit,
1075                caller: Address::ZERO,
1076                value: U256::ZERO,
1077                internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1078                target_address: identity_address,
1079                bytecode_address: identity_address,
1080            })
1081            .unwrap();
1082        assert_eq!(
1083            result.bytes, test_input,
1084            "Identity precompile should return the input data after conversion to dynamic"
1085        );
1086    }
1087}