oxidized_builder/core/
simulation.rs

1use crate::common::error::AppError;
2use crate::network::provider::HttpProvider;
3use alloy::providers::Provider;
4use alloy::rpc::types::eth::simulate::{SimBlock, SimulatePayload};
5use alloy::rpc::types::eth::state::StateOverride;
6use alloy::rpc::types::eth::Transaction;
7use alloy::rpc::types::eth::TransactionRequest;
8
9#[derive(Debug, Clone)]
10pub struct SimulationOutcome {
11    pub success: bool,
12    pub gas_used: u64,
13    pub return_data: Vec<u8>,
14}
15
16#[derive(Clone)]
17pub struct Simulator {
18    provider: HttpProvider,
19}
20
21impl Simulator {
22    pub fn new(provider: HttpProvider) -> Self {
23        Self { provider }
24    }
25
26    pub async fn simulate_transaction(
27        &self,
28        tx: &Transaction,
29    ) -> Result<SimulationOutcome, AppError> {
30        let req = tx.clone().into_request();
31        self.simulate_request(req, None).await
32    }
33
34    pub async fn simulate_request(
35        &self,
36        req: TransactionRequest,
37        state_override: Option<StateOverride>,
38    ) -> Result<SimulationOutcome, AppError> {
39        if state_override.is_some() {
40            let block = SimBlock {
41                block_overrides: None,
42                state_overrides: state_override.clone(),
43                calls: vec![req.clone()],
44            };
45            let payload = SimulatePayload {
46                block_state_calls: vec![block],
47                trace_transfers: false,
48                validation: false,
49                return_full_transactions: false,
50            };
51
52            if let Ok(simulated) = self.provider.simulate(&payload).await {
53                if let Some(call) = simulated.first().and_then(|b| b.calls.first()) {
54                    return Ok(SimulationOutcome {
55                        success: call.error.is_none() && call.status,
56                        gas_used: call.gas_used,
57                        return_data: call.return_data.to_vec(),
58                    });
59                }
60            }
61        }
62
63        let gas_used = match self.provider.estimate_gas(req.clone()).await {
64            Ok(g) => g,
65            Err(e) => {
66                return Ok(SimulationOutcome {
67                    success: false,
68                    gas_used: 0,
69                    return_data: format!("estimate_gas failed: {e}").into_bytes(),
70                })
71            }
72        };
73
74        let call_res = self.provider.call(req).await;
75
76        let (success, return_data) = match call_res {
77            Ok(bytes) => (true, bytes.to_vec()),
78            Err(_) => (false, Vec::new()),
79        };
80
81        Ok(SimulationOutcome {
82            success,
83            gas_used,
84            return_data,
85        })
86    }
87
88    pub async fn simulate_bundle(
89        &self,
90        txs: &[Transaction],
91        state_override: Option<StateOverride>,
92    ) -> Result<Vec<SimulationOutcome>, AppError> {
93        let reqs: Vec<TransactionRequest> = txs.iter().cloned().map(|t| t.into_request()).collect();
94        self.simulate_bundle_requests(&reqs, state_override).await
95    }
96
97    pub async fn simulate_bundle_requests(
98        &self,
99        txs: &[TransactionRequest],
100        state_override: Option<StateOverride>,
101    ) -> Result<Vec<SimulationOutcome>, AppError> {
102        if txs.is_empty() {
103            return Ok(Vec::new());
104        }
105
106        let calls = txs.to_vec();
107        let block = SimBlock {
108            block_overrides: None,
109            state_overrides: state_override.clone(),
110            calls,
111        };
112        let payload = SimulatePayload {
113            block_state_calls: vec![block],
114            trace_transfers: false,
115            validation: false,
116            return_full_transactions: false,
117        };
118
119        match self.provider.simulate(&payload).await {
120            Ok(blocks) => {
121                let mut out = Vec::new();
122                for blk in blocks {
123                    for tx in blk.calls {
124                        let success = tx.error.is_none();
125                        let gas_used = tx.gas_used;
126                        let return_data = tx.return_data.to_vec();
127                        out.push(SimulationOutcome {
128                            success,
129                            gas_used,
130                            return_data,
131                        });
132                    }
133                }
134                return Ok(out);
135            }
136            Err(_) => {
137                let mut out = Vec::new();
138                for tx in txs {
139                    out.push(
140                        self.simulate_request(tx.clone(), state_override.clone())
141                            .await?,
142                    );
143                }
144                Ok(out)
145            }
146        }
147    }
148}