Skip to main content

evm_sdk/
contract.rs

1/// The abstraction layer module for smart contracts.
2use crate::Evm;
3use crate::EvmError;
4use ethers::providers::Middleware;
5use ethers::types::{Address, Bytes, H256};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9
10/// Basic contract information
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ContractInfo {
13    pub address: Address,
14    pub bytecode: Bytes,
15    pub deployed_bytecode: Bytes,
16    pub is_contract: bool,
17    pub creation_block: Option<u64>,
18    pub creation_tx_hash: Option<H256>,
19    pub storage_slots: HashMap<H256, H256>,
20}
21
22/// Contract ABI information
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ContractABI {
25    pub raw_abi: Option<String>,
26    pub functions: Vec<FunctionInfo>,
27    pub events: Vec<EventInfo>,
28    pub errors: Vec<ErrorInfo>,
29}
30
31/// Function information
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct FunctionInfo {
34    pub name: String,
35    pub inputs: Vec<Param>,
36    pub outputs: Vec<Param>,
37    pub constant: bool,
38    pub payable: bool,
39    pub selector: Option<H256>,
40}
41
42/// Event information
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct EventInfo {
45    pub name: String,
46    pub inputs: Vec<Param>,
47    pub anonymous: bool,
48    pub signature: Option<H256>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ErrorInfo {
53    pub name: String,
54    pub inputs: Vec<Param>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct Param {
59    pub name: String,
60    pub type_: String,
61    pub indexed: bool,
62}
63
64/// Storage layout analysis
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct StorageLayout {
67    pub slots: Vec<StorageSlot>,
68    pub total_size: usize,
69}
70
71/// Storage slot information
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct StorageSlot {
74    pub slot: H256,
75    pub value: H256,
76    pub size: usize,
77}
78
79/// Contract analyzer for EVM-based contracts
80pub struct ContractAnalyzer {
81    evm: Arc<Evm>,
82}
83
84impl ContractAnalyzer {
85    pub fn new(evm: Arc<Evm>) -> Self {
86        Self { evm }
87    }
88
89    /// Retrieves comprehensive contract information
90    ///
91    /// # Example
92    /// ```rust
93    /// use ethers::types::Address;
94    /// use std::str::FromStr;
95    ///
96    /// let analyzer = ContractAnalyzer::new(evm_client);
97    /// let address = Address::from_str("0x742d35Cc6634C0532925a3b8D6B6f7C93D5A7A7A")?;
98    /// let contract_info = analyzer.get_contract_info(address).await?;
99    /// println!("Contract bytecode length: {}", contract_info.bytecode.len());
100    /// ```
101    pub async fn get_contract_info(&self, address: Address) -> Result<ContractInfo, EvmError> {
102        let bytecode = self.get_contract_bytecode(address).await?;
103        let is_contract = !bytecode.is_empty();
104        let deployed_bytecode = self.get_deployed_bytecode(address).await?;
105        let (creation_block, creation_tx_hash) = self.find_creation_info(address).await?;
106        let storage_slots = self.sample_storage_slots(address, 100).await?;
107        Ok(ContractInfo {
108            address,
109            bytecode,
110            deployed_bytecode,
111            is_contract,
112            creation_block,
113            creation_tx_hash,
114            storage_slots,
115        })
116    }
117
118    /// Retrieves contract bytecode from the blockchain
119    ///
120    /// # Example
121    /// ```rust
122    /// let bytecode = analyzer.get_contract_bytecode(address).await?;
123    /// println!("Bytecode length: {} bytes", bytecode.len());
124    /// ```
125    pub async fn get_contract_bytecode(&self, address: Address) -> Result<Bytes, EvmError> {
126        self.evm
127            .client
128            .provider
129            .get_code(address, None)
130            .await
131            .map_err(|e| EvmError::RpcError(format!("Failed to get contract bytecode: {}", e)))
132    }
133
134    /// Retrieves deployed bytecode (runtime bytecode)
135    pub async fn get_deployed_bytecode(&self, address: Address) -> Result<Bytes, EvmError> {
136        self.get_contract_bytecode(address).await
137    }
138
139    /// Finds contract creation block and transaction hash
140    async fn find_creation_info(
141        &self,
142        address: Address,
143    ) -> Result<(Option<u64>, Option<H256>), EvmError> {
144        let current_block = self.evm.get_block_number().await?;
145        let start_block = current_block.saturating_sub(1000);
146        for block_number in (start_block..=current_block).rev() {
147            if let Some(block) = self
148                .evm
149                .client
150                .provider
151                .get_block(block_number)
152                .await
153                .map_err(|e| {
154                    EvmError::RpcError(format!("Failed to get block {}: {}", block_number, e))
155                })?
156            {
157                if let transactions = block.transactions {
158                    for tx_hash in transactions {
159                        if let Some(receipt) = self.evm.get_transaction_receipt(tx_hash).await? {
160                            if let Some(contract_address) = receipt.contract_address {
161                                if contract_address == address {
162                                    return Ok((Some(block_number), Some(tx_hash)));
163                                }
164                            }
165                        }
166                    }
167                }
168            }
169        }
170        Ok((None, None))
171    }
172
173    /// Samples storage slots for analysis
174    async fn sample_storage_slots(
175        &self,
176        address: Address,
177        sample_count: usize,
178    ) -> Result<HashMap<H256, H256>, EvmError> {
179        let mut slots = HashMap::new();
180        for i in 0..sample_count {
181            let slot = H256::from_low_u64_be(i as u64);
182            if let Some(value) = self.get_storage_at(address, slot).await? {
183                slots.insert(slot, value);
184            }
185        }
186        Ok(slots)
187    }
188
189    /// Retrieves storage value at specific slot
190    ///
191    /// # Example
192    /// ```rust
193    /// let slot = H256::zero();
194    /// let value = analyzer.get_storage_at(address, slot).await?;
195    /// if let Some(storage_value) = value {
196    ///     println!("Storage value: {:?}", storage_value);
197    /// }
198    /// ```
199    pub async fn get_storage_at(
200        &self,
201        address: Address,
202        slot: H256,
203    ) -> Result<Option<H256>, EvmError> {
204        self.evm
205            .client
206            .provider
207            .get_storage_at(address, slot, None)
208            .await
209            .map(Some)
210            .map_err(|e| {
211                EvmError::RpcError(format!("Failed to get storage at slot {:?}: {}", slot, e))
212            })
213    }
214
215    /// Analyzes storage layout of a contract
216    ///
217    /// # Example
218    /// ```rust
219    /// let layout = analyzer.analyze_storage_layout(address).await?;
220    /// println!("Total storage size: {} bytes", layout.total_size);
221    /// for slot in layout.slots {
222    ///     println!("Slot {:?}: value {:?}, size {}", slot.slot, slot.value, slot.size);
223    /// }
224    /// ```
225    pub async fn analyze_storage_layout(
226        &self,
227        address: Address,
228    ) -> Result<StorageLayout, EvmError> {
229        let mut slots = Vec::new();
230        let mut total_size = 0;
231        for i in 0..50 {
232            let slot = H256::from_low_u64_be(i as u64);
233            if let Some(value) = self.get_storage_at(address, slot).await? {
234                let size = self.calculate_storage_size(value);
235                total_size += size;
236
237                slots.push(StorageSlot { slot, value, size });
238            }
239        }
240        Ok(StorageLayout { slots, total_size })
241    }
242
243    /// Calculates approximate storage size based on non-zero bytes
244    fn calculate_storage_size(&self, value: H256) -> usize {
245        value.as_bytes().iter().filter(|&&b| b != 0).count()
246    }
247
248    /// Extracts potential function selectors from bytecode
249    ///
250    /// # Example
251    /// ```rust
252    /// let bytecode = analyzer.get_contract_bytecode(address).await?;
253    /// let selectors = analyzer.extract_function_selectors(&bytecode);
254    /// println!("Found {} potential function selectors", selectors.len());
255    /// for selector in selectors {
256    ///     println!("Selector: {:?}", selector);
257    /// }
258    /// ```
259    pub fn extract_function_selectors(&self, bytecode: &Bytes) -> Vec<H256> {
260        let mut selectors = Vec::new();
261        let code = bytecode.as_ref();
262        for i in 0..code.len().saturating_sub(4) {
263            if i > 0 && code[i - 1] == 0x63 {
264                let selector_bytes = [code[i], code[i + 1], code[i + 2], code[i + 3]];
265                let selector = H256::from_slice(&{
266                    let mut full = [0u8; 32];
267                    full[28..32].copy_from_slice(&selector_bytes);
268                    full
269                });
270                selectors.push(selector);
271            }
272        }
273        selectors.dedup();
274        selectors
275    }
276
277    /// Analyzes bytecode features and characteristics
278    ///
279    /// # Example
280    /// ```rust
281    /// let features = analyzer.analyze_bytecode_features(address).await?;
282    /// println!("Is proxy: {}", features.is_proxy);
283    /// println!("Has selfdestruct: {}", features.has_selfdestruct);
284    /// println!("Bytecode length: {}", features.bytecode_length);
285    /// ```
286    pub async fn analyze_bytecode_features(
287        &self,
288        address: Address,
289    ) -> Result<BytecodeFeatures, EvmError> {
290        let bytecode = self.get_contract_bytecode(address).await?;
291        let function_selectors = self.extract_function_selectors(&bytecode);
292        let is_proxy = self.detect_proxy_pattern(&bytecode).await;
293        let has_selfdestruct = bytecode.contains(&0xff); // SELFDESTRUCT opcode
294        let has_delegatecall = bytecode.contains(&0xf4); // DELEGATECALL opcode
295        Ok(BytecodeFeatures {
296            address,
297            bytecode_length: bytecode.len(),
298            function_selectors,
299            is_proxy,
300            has_selfdestruct,
301            has_delegatecall,
302            opcode_distribution: self.analyze_opcode_distribution(&bytecode),
303        })
304    }
305
306    /// Detects proxy contract patterns in bytecode
307    async fn detect_proxy_pattern(&self, bytecode: &Bytes) -> bool {
308        let code = bytecode.as_ref();
309        let has_delegatecall = code.contains(&0xf4);
310        has_delegatecall
311    }
312
313    /// Analyzes opcode distribution in bytecode
314    fn analyze_opcode_distribution(&self, bytecode: &Bytes) -> HashMap<u8, usize> {
315        let mut distribution = HashMap::new();
316        for &opcode in bytecode.as_ref() {
317            *distribution.entry(opcode).or_insert(0) += 1;
318        }
319        distribution
320    }
321
322    /// Compares two contracts for similarity
323    ///
324    /// # Example
325    /// ```rust
326    /// async fn example() -> Result<(), Box<dyn std::error::Error>> {
327    /// let similarity = analyzer.compare_contracts(address1, address2).await?;
328    /// println!("Bytecode similarity: {:.2}%", similarity.bytecode_similarity * 100.0);
329    /// println!("Common function selectors: {}", similarity.common_function_selectors.len());
330    /// Ok(())
331    /// }
332    /// ```
333    pub async fn compare_contracts(
334        &self,
335        address1: Address,
336        address2: Address,
337    ) -> Result<ContractSimilarity, EvmError> {
338        let bytecode1 = self.get_contract_bytecode(address1).await?;
339        let bytecode2 = self.get_contract_bytecode(address2).await?;
340        let similarity = self.calculate_bytecode_similarity(&bytecode1, &bytecode2);
341        let selectors1 = self.extract_function_selectors(&bytecode1);
342        let selectors2 = self.extract_function_selectors(&bytecode2);
343        let common_selectors: Vec<H256> = selectors1
344            .iter()
345            .filter(|s| selectors2.contains(s))
346            .cloned()
347            .collect();
348        Ok(ContractSimilarity {
349            address1,
350            address2,
351            bytecode_similarity: similarity,
352            common_function_selectors: common_selectors,
353            bytecode1_length: bytecode1.len(),
354            bytecode2_length: bytecode2.len(),
355        })
356    }
357
358    /// Calculates similarity between two bytecodes
359    fn calculate_bytecode_similarity(&self, bytecode1: &Bytes, bytecode2: &Bytes) -> f64 {
360        if bytecode1.is_empty() && bytecode2.is_empty() {
361            return 1.0;
362        }
363        if bytecode1.is_empty() || bytecode2.is_empty() {
364            return 0.0;
365        }
366        let len1 = bytecode1.len();
367        let len2 = bytecode2.len();
368        let max_len = len1.max(len2) as f64;
369        if max_len == 0.0 {
370            return 1.0;
371        }
372        let common_prefix = bytecode1
373            .iter()
374            .zip(bytecode2.iter())
375            .take_while(|(a, b)| a == b)
376            .count();
377        common_prefix as f64 / max_len
378    }
379
380    /// Retrieves transaction statistics for a contract
381    ///
382    /// # Example
383    /// ```rust
384    /// let stats = analyzer.get_transaction_stats(address).await?;
385    /// println!("Total transactions: {}", stats.total_transactions);
386    /// println!("First seen block: {}", stats.first_seen_block);
387    /// println!("Last seen block: {}", stats.last_seen_block);
388    /// ```
389    pub async fn get_transaction_stats(
390        &self,
391        address: Address,
392    ) -> Result<TransactionStats, EvmError> {
393        let current_block = self.evm.get_block_number().await?;
394        let start_block = current_block.saturating_sub(10000);
395        let mut total_txs = 0;
396        let mut incoming_txs = 0;
397        let mut outgoing_txs = 0;
398        let filter = ethers::types::Filter::new()
399            .from_block(start_block)
400            .to_block(current_block)
401            .address(address);
402        let logs = self.evm.get_logs(filter).await?;
403        total_txs = logs.len();
404        Ok(TransactionStats {
405            address,
406            total_transactions: total_txs,
407            incoming_transactions: incoming_txs,
408            outgoing_transactions: outgoing_txs,
409            first_seen_block: start_block,
410            last_seen_block: current_block,
411        })
412    }
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct BytecodeFeatures {
417    pub address: Address,
418    pub bytecode_length: usize,
419    pub function_selectors: Vec<H256>,
420    pub is_proxy: bool,
421    pub has_selfdestruct: bool,
422    pub has_delegatecall: bool,
423    pub opcode_distribution: HashMap<u8, usize>,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
427pub struct ContractSimilarity {
428    pub address1: Address,
429    pub address2: Address,
430    pub bytecode_similarity: f64,
431    pub common_function_selectors: Vec<H256>,
432    pub bytecode1_length: usize,
433    pub bytecode2_length: usize,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct TransactionStats {
438    pub address: Address,
439    pub total_transactions: usize,
440    pub incoming_transactions: usize,
441    pub outgoing_transactions: usize,
442    pub first_seen_block: u64,
443    pub last_seen_block: u64,
444}
445
446impl From<ethers::providers::ProviderError> for EvmError {
447    fn from(error: ethers::providers::ProviderError) -> Self {
448        EvmError::RpcError(format!("Provider error: {}", error))
449    }
450}