signet_evm/orders/
inspector.rs

1use crate::{FramedFilleds, FramedOrders};
2use alloy::{
3    primitives::{map::HashSet, Address, Log, U256},
4    sol_types::SolEvent,
5};
6use signet_types::{constants::SignetSystemConstants, AggregateFills, AggregateOrders};
7use signet_zenith::RollupOrders;
8use trevm::{
9    helpers::Ctx,
10    revm::{
11        interpreter::{
12            CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes,
13        },
14        Database, Inspector,
15    },
16};
17
18/// Inspector used to detect Signet Orders and inform the builder of the
19/// fill requirements.
20///
21/// This inspector is intended to be used with `trevm`. The EVM driver should
22/// - call [`OrderDetector::take_aggregates`] to get the aggregate orders
23///   and fills produced by that transaction.
24/// - ensure that net fills are sufficient to cover the order inputs via
25///   [`AggregateFills::checked_remove_ru_tx_events`].
26/// - reject transactions which are not sufficiently filled.
27///
28/// The [`SignetDriver`] has an example of this in the `check_fills_and_accept`
29/// function.
30///
31/// The `OrderDetector` allows an inner inspector to be used as well. This is
32/// useful for tracers and other tools that need to inspect the EVM state.
33///
34/// [`SignetDriver`]: crate::SignetDriver
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct OrderDetector {
38    /// The signet system constants.
39    contracts: HashSet<Address>,
40
41    /// True if only detecting fills
42    fills_only: bool,
43
44    /// The chain ID.
45    chain_id: u64,
46
47    /// Orders detected so far, account for EVM reverts
48    orders: FramedOrders,
49
50    /// Fills detected so far, accounting for EVM reverts
51    filleds: FramedFilleds,
52}
53
54impl OrderDetector {
55    /// Create a new [`OrderDetector`] with the given `contracts` addresses,
56    /// `chain_id`, and `fills_only` flag.
57    pub fn new(contracts: HashSet<Address>, chain_id: u64, fills_only: bool) -> Self {
58        Self {
59            contracts,
60            chain_id,
61            fills_only,
62            orders: Default::default(),
63            filleds: Default::default(),
64        }
65    }
66
67    /// Create a new [`OrderDetector`] for the rollup environment. This detector
68    /// will detect both orders and fills.
69    pub fn for_rollup(constants: SignetSystemConstants) -> OrderDetector {
70        Self::new(
71            std::iter::once(constants.rollup().orders()).collect(),
72            constants.ru_chain_id(),
73            false,
74        )
75    }
76
77    /// Create a new [`OrderDetector`] for the host environment. This detector
78    /// will only detect fills.
79    pub fn for_host(constants: SignetSystemConstants) -> OrderDetector {
80        Self::new(
81            std::iter::once(constants.host().orders()).collect(),
82            constants.host_chain_id(),
83            true,
84        )
85    }
86
87    /// Get the address of the orders contract.
88    pub fn is_contract(&self, address: Address) -> bool {
89        self.contracts.contains(&address)
90    }
91
92    /// Get the chain ID.
93    pub const fn chain_id(&self) -> u64 {
94        self.chain_id
95    }
96
97    /// Take the orders from the inspector, clearing it.
98    pub fn take(&mut self) -> (FramedOrders, FramedFilleds) {
99        (std::mem::take(&mut self.orders), std::mem::take(&mut self.filleds))
100    }
101
102    /// Take the orders from the inspector, clearing it, and convert them to
103    /// aggregate orders.
104    pub fn take_aggregates(&mut self) -> (AggregateFills, AggregateOrders) {
105        let (orders, filleds) = self.take();
106        (filleds.aggregate(self.chain_id()), orders.aggregate())
107    }
108
109    /// Take the inner inspector and the framed events.
110    pub fn into_parts(self) -> (FramedOrders, FramedFilleds) {
111        (self.orders, self.filleds)
112    }
113
114    /// Get a reference to the framed [`RollupOrders::Order`] events.
115    pub const fn orders(&self) -> &FramedOrders {
116        &self.orders
117    }
118
119    /// Get a reference to the framed [`RollupOrders::Filled`] events.
120    pub const fn filleds(&self) -> &FramedFilleds {
121        &self.filleds
122    }
123}
124
125impl<Db, Int> Inspector<Ctx<Db>, Int> for OrderDetector
126where
127    Db: Database,
128    Int: InterpreterTypes,
129{
130    fn log(&mut self, _interp: &mut Interpreter<Int>, _context: &mut Ctx<Db>, log: Log) {
131        // skip if the log is not from a configured orders contract
132        if !self.is_contract(log.address) {
133            return;
134        }
135
136        // Try to decode as a filled first
137        if let Ok(Log { data, .. }) = RollupOrders::Filled::decode_log(&log) {
138            self.filleds.add(data);
139            return;
140        }
141
142        // Skip any other logs if we're only tracking fills
143        if self.fills_only {
144            return;
145        }
146
147        // Try to decode as an order next
148        if let Ok(Log { data, .. }) = RollupOrders::Order::decode_log(&log) {
149            if self.fills_only {
150                return;
151            }
152            self.orders.add(data);
153        }
154    }
155
156    fn call(&mut self, _context: &mut Ctx<Db>, _inputs: &mut CallInputs) -> Option<CallOutcome> {
157        self.orders.enter_frame();
158        None
159    }
160
161    fn call_end(
162        &mut self,
163        _context: &mut Ctx<Db>,
164        _inputs: &CallInputs,
165        outcome: &mut CallOutcome,
166    ) {
167        if outcome.result.is_ok() {
168            self.orders.exit_frame();
169        } else {
170            self.orders.revert_frame();
171        }
172    }
173
174    fn create(
175        &mut self,
176        _context: &mut Ctx<Db>,
177        _inputs: &mut CreateInputs,
178    ) -> Option<CreateOutcome> {
179        self.orders.enter_frame();
180        None
181    }
182
183    fn create_end(
184        &mut self,
185        _context: &mut Ctx<Db>,
186        _inputs: &CreateInputs,
187        outcome: &mut CreateOutcome,
188    ) {
189        if outcome.result.is_ok() {
190            self.orders.exit_frame();
191        } else {
192            self.orders.revert_frame();
193        }
194    }
195
196    fn selfdestruct(&mut self, _contract: Address, _target: Address, _value: U256) {
197        self.orders.exit_frame();
198    }
199}