revm_trace/
types.rs

1use std::collections::HashMap;
2
3/// Override state for contract storage during simulation
4#[derive(Debug, Clone, Default)]
5pub struct StateOverride {
6    pub storages: HashMap<Address, Vec<(U256, U256)>>, // slot-value
7    pub balances: HashMap<Address, U256>,              // address-balance
8}
9
10pub type StorageDiff = HashMap<Address, Vec<SlotAccess>>;
11
12/// SlotAccessType , used to filter slot access types
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SlotAccessType {
15    Read,
16    Write,
17    All,
18}
19
20impl CallTrace {
21    /// Internal recursive function to collect all slot accesses with filter
22    fn collect_slot_accesses<'a>(&'a self, filter: SlotAccessType, out: &mut Vec<&'a SlotAccess>) {
23        for access in &self.slot_accesses {
24            match filter {
25                SlotAccessType::All => out.push(access),
26                SlotAccessType::Read if !access.is_write => out.push(access),
27                SlotAccessType::Write if access.is_write => out.push(access),
28                _ => {}
29            }
30        }
31        for sub in &self.subtraces {
32            sub.collect_slot_accesses(filter, out);
33        }
34    }
35
36    /// Returns all slot_accesses references (filtered by type: Read, Write, or All)
37    pub fn all_slot_accesses(&self, filter: SlotAccessType) -> Vec<&SlotAccess> {
38        let mut result = Vec::new();
39        self.collect_slot_accesses(filter, &mut result);
40        result
41    }
42}
43
44use crate::MyWrapDatabaseAsync;
45use alloy::{
46    network::AnyNetwork,
47    primitives::{fixed_bytes, Address, Bytes, FixedBytes, Log, TxKind, U256},
48    providers::{
49        fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller},
50        Identity, RootProvider,
51    },
52};
53pub use revm::{
54    context::BlockEnv,
55    database::AlloyDB,
56    interpreter::{CallScheme, CreateScheme},
57};
58use serde::{Deserialize, Serialize};
59
60pub const ERC20_TRANSFER_EVENT_SIGNATURE: FixedBytes<32> =
61    fixed_bytes!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
62pub const ERC1155_TRANSFER_BATCH_EVENT_SIGNATURE: FixedBytes<32> =
63    fixed_bytes!("0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb");
64pub const ERC1155_TRANSFER_SINGLE_EVENT_SIGNATURE: FixedBytes<32> =
65    fixed_bytes!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62");
66
67// ========================= Provider Type Definitions =========================
68//
69// These type aliases create a layered provider system using alloy's filler pattern.
70// Fillers automatically populate transaction fields during execution.
71
72/// Base filler layer that handles nonce and chain ID management
73///
74/// Combines:
75/// - `NonceFiller`: Automatically sets transaction nonce from account state
76/// - `ChainIdFiller`: Automatically sets chain ID from provider
77type BaseFiller = JoinFill<NonceFiller, ChainIdFiller>;
78
79/// Blob filler layer that adds EIP-4844 blob gas management
80///
81/// Extends BaseFiller with:
82/// - `BlobGasFiller`: Handles blob gas pricing for blob transactions (EIP-4844)
83type BlobFiller = JoinFill<BlobGasFiller, BaseFiller>;
84
85/// Gas filler layer that adds general gas management
86///
87/// Extends BlobFiller with:
88/// - `GasFiller`: Automatically estimates and sets gas limit and gas price
89type GasFillers = JoinFill<GasFiller, BlobFiller>;
90
91/// Complete filler stack with identity layer
92///
93/// Adds the identity filler on top of all gas management layers:
94/// - `Identity`: Pass-through filler that preserves existing values
95/// - Provides a complete transaction filling pipeline
96type AllFillers = JoinFill<Identity, GasFillers>;
97
98/// HTTP provider with automatic transaction filling
99///
100/// A fully configured HTTP provider that:
101/// - Uses `AnyNetwork` for maximum blockchain compatibility
102/// - Automatically fills transaction fields using `AllFillers`
103/// - Provides type-safe access to Ethereum JSON-RPC methods
104///
105/// This is the primary provider type for single-threaded operations.
106pub type AnyNetworkProvider = FillProvider<AllFillers, RootProvider<AnyNetwork>, AnyNetwork>;
107
108pub type ArcAnyNetworkProvider = std::sync::Arc<AnyNetworkProvider>;
109
110pub const NATIVE_TOKEN_ADDRESS: Address = Address::ZERO;
111
112pub type AllDBType = MyWrapDatabaseAsync<AlloyDB<AnyNetwork, AnyNetworkProvider>>;
113
114#[derive(Debug, Clone, Serialize)]
115pub struct TokenInfo {
116    pub name: String,
117    /// Token symbol (e.g., "ETH", "USDC")
118    pub symbol: String,
119    /// Number of decimal places for value formatting
120    pub decimals: u8,
121    /// Total supply of the token
122    pub total_supply: U256,
123}
124
125#[derive(Debug, Clone)]
126pub struct SimulationTx {
127    /// Address initiating the transaction
128    pub caller: Address,
129    /// Amount of native token (ETH) to send
130    pub value: U256,
131    /// Transaction calldata
132    pub data: Bytes,
133    /// Transaction target (address for calls, None for creation)
134    pub transact_to: TxKind,
135}
136
137/// Batch transaction simulation parameters
138///
139/// Allows execution of multiple transactions in sequence with
140/// configurable state handling between transactions.
141#[derive(Debug, Clone)]
142pub struct SimulationBatch {
143    /// Sequence of transactions to execute
144    pub transactions: Vec<SimulationTx>,
145    /// Whether to preserve state between transactions
146    ///
147    /// Set to true when:
148    /// - Deploying then interacting with contracts
149    /// - Executing dependent transactions
150    /// - State changes should affect subsequent transactions
151    ///
152    /// Set to false when:
153    /// - Simulating independent scenarios
154    /// - Comparing different outcomes from same starting state
155    pub is_stateful: bool,
156    /// Optional state overrides for the simulation
157    pub overrides: Option<StateOverride>,
158}
159
160/// Type of token transfer (supports future extensibility)
161#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
162#[non_exhaustive]
163pub enum TokenType {
164    Native,
165    ERC20,
166    ERC721,
167    ERC1155,
168    // More token types can be added in future
169}
170
171/// Record of a token transfer event
172///
173/// Captures all relevant information about a token transfer,
174/// supporting both native (ETH) and ERC20/ERC721/ERC1155 tokens.
175/// - For ERC20: `value` is the transfer amount, `id` is None.
176/// - For ERC721: `value` is the tokenId, `id` is Some(tokenId).
177/// - For ERC1155: `value` is the transfer amount, `id` is Some(tokenId).
178/// - For native token: `value` is the amount, `id` is None.
179#[derive(Debug, Clone, Serialize)]
180pub struct TokenTransfer {
181    /// Token address (NATIVE_TOKEN_ADDRESS for ETH)
182    pub token: Address,
183    /// Transfer sender
184    pub from: Address,
185    /// Transfer recipient (None if contract creation failed)
186    pub to: Option<Address>,
187    /// Transfer amount in token's smallest unit
188    pub value: U256,
189    /// Type of token being transferred
190    pub token_type: TokenType,
191    /// ERC721/1155 id (Some for ERC721/ERC1155, None for ERC20/Native)
192    pub id: Option<U256>,
193}
194
195impl TokenTransfer {
196    /// Check if this transfer is for the native token
197    pub fn is_native_token(&self) -> bool {
198        self.token == NATIVE_TOKEN_ADDRESS
199    }
200}
201
202/// Type of contract interaction
203#[derive(Debug, Clone)]
204pub enum CallType {
205    /// Regular contract call
206    Call,
207    /// Contract creation
208    Create,
209}
210
211/// Status of a contract call
212#[derive(Debug, Clone, Serialize, Default)]
213pub enum CallStatus {
214    /// Call completed successfully
215    #[default]
216    Success,
217    /// Call reverted with reason
218    Revert(String),
219    /// Call halted due to error
220    Halt(String),
221    /// Fatal error occurred
222    FatalError,
223    /// Call is still in progress
224    InProgress,
225}
226
227impl CallStatus {
228    /// Check if the call was successful
229    pub fn is_success(&self) -> bool {
230        matches!(self, CallStatus::Success)
231    }
232}
233
234/// Storage slot change during a contract call
235#[derive(Debug, Clone, Serialize, Deserialize, Default)]
236pub struct SlotAccess {
237    pub address: Address,
238    pub slot: U256,
239    pub old_value: U256,
240    pub new_value: U256,
241    pub is_write: bool, // true=write, false=read
242}
243
244/// Detailed trace of a contract call
245#[derive(Debug, Clone, Serialize, Default)]
246pub struct CallTrace {
247    /// Caller address
248    pub from: Address,
249    /// Target address
250    pub to: Address,
251    /// Native token value
252    pub value: U256,
253    /// Call input data
254    pub input: Bytes,
255    /// Call scheme if regular call
256    pub call_scheme: Option<CallScheme>,
257    /// Create scheme if contract creation
258    pub create_scheme: Option<CreateScheme>,
259    /// Gas used by this call
260    pub gas_used: U256,
261    /// Call output data
262    pub output: Bytes,
263    /// Call execution status
264    pub status: CallStatus,
265    /// Whether this call is the source of an error
266    pub error_origin: bool,
267    /// Nested calls made by this call
268    pub subtraces: Vec<CallTrace>,
269    /// Position in the call tree
270    pub trace_address: Vec<usize>,
271    /// Access to contract storage slots during this call
272    pub slot_accesses: Vec<SlotAccess>,
273}
274
275impl TokenTransfer {
276    /// Parses a token transfer log and returns a vector of TokenTransfer objects
277    pub fn get_token_transfers(log: &Log) -> Vec<TokenTransfer> {
278        let mut results = vec![];
279        // erc20/erc721 transfer
280        if log.topics()[0] == ERC20_TRANSFER_EVENT_SIGNATURE {
281            if log.topics().len() == 3 {
282                let from = Address::from_slice(&log.topics()[1].as_slice()[12..]);
283                let to = Address::from_slice(&log.topics()[2].as_slice()[12..]);
284                let data = &log.data.data;
285                let amount = U256::from_be_slice(data);
286                if !amount.is_zero() {
287                    results.push(TokenTransfer {
288                        token: log.address,
289                        from,
290                        to: Some(to),
291                        value: amount,
292                        token_type: TokenType::ERC20,
293                        id: None,
294                    });
295                }
296            } else if log.topics().len() == 4 {
297                let from = Address::from_slice(&log.topics()[1].as_slice()[12..]);
298                let to = Address::from_slice(&log.topics()[2].as_slice()[12..]);
299                let id = U256::from_be_slice(log.topics()[3].as_slice());
300                let amount = U256::from(1);
301                results.push(TokenTransfer {
302                    token: log.address,
303                    from,
304                    to: Some(to),
305                    value: amount,
306                    token_type: TokenType::ERC721,
307                    id: Some(id),
308                });
309            }
310        } else if log.topics()[0] == ERC1155_TRANSFER_BATCH_EVENT_SIGNATURE
311            && log.topics().len() == 4
312        {
313            let data = &log.data.data;
314            if data.len() >= 96 {
315                let from = Address::from_slice(&log.topics()[2].as_slice()[12..]);
316                let to = Address::from_slice(&log.topics()[3].as_slice()[12..]);
317                let ids_len = U256::from_be_slice(&data[64..96]).to::<usize>();
318                let mut ids = Vec::with_capacity(ids_len);
319                let mut offset = 96;
320                for _ in 0..ids_len {
321                    ids.push(U256::from_be_slice(&data[offset..offset + 32]));
322                    offset += 32;
323                }
324                // 解析 values
325                let values_len = U256::from_be_slice(&data[offset..offset + 32]).to::<usize>();
326                offset += 32;
327                let mut values = Vec::with_capacity(values_len);
328                for _ in 0..values_len {
329                    values.push(U256::from_be_slice(&data[offset..offset + 32]));
330                    offset += 32;
331                }
332                // 匹配 ids 和 values
333                for (id, value) in ids.into_iter().zip(values.into_iter()) {
334                    results.push(TokenTransfer {
335                        token: log.address,
336                        from,
337                        to: Some(to),
338                        value,
339                        token_type: TokenType::ERC1155,
340                        id: Some(id),
341                    });
342                }
343            }
344        } else if log.topics()[0] == ERC1155_TRANSFER_SINGLE_EVENT_SIGNATURE
345            && log.topics().len() == 4
346        {
347            let data = &log.data.data;
348            if data.len() >= 64 {
349                let from = Address::from_slice(&log.topics()[2].as_slice()[12..]);
350                let to = Address::from_slice(&log.topics()[3].as_slice()[12..]);
351                let id = U256::from_be_slice(&data[..32]);
352                let value = U256::from_be_slice(&data[32..64]);
353                results.push(TokenTransfer {
354                    token: log.address,
355                    from,
356                    to: Some(to),
357                    value,
358                    token_type: TokenType::ERC1155,
359                    id: Some(id),
360                });
361            }
362        }
363        results
364    }
365}