Coverage for src/alprina_cli/agents/web3_auditor/mev_detector.py: 20%

142 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-14 11:27 +0100

1""" 

2MEV (Miner Extractable Value) Detection Engine 

3 

4WEEK 3 DAY 3: MEV Detection 

5============================ 

6 

7Detects vulnerabilities that allow miners/validators to extract value by: 

8- Front-running transactions 

9- Sandwich attacks on DEX swaps 

10- Liquidation manipulation 

11- Timestamp manipulation 

12 

13Background: 

14- 2024 MEV Stats: $500M+ extracted, $100M+ from malicious MEV 

15- Common attack vectors: DEX arbitrage, liquidations, oracle updates 

16 

17Author: Alprina Development Team 

18Date: 2025-11-12 

19 

20References: 

21- Flashbots: MEV research and data 

22- MEV-Explore: Historical MEV extraction data 

23- DAIAN et al.: "Flash Boys 2.0" (2019) 

24""" 

25 

26import re 

27from typing import List, Dict, Any, Optional, Tuple 

28from dataclasses import dataclass 

29from enum import Enum 

30 

31try: 

32 from .solidity_analyzer import SolidityVulnerability, VulnerabilityType 

33except ImportError: 

34 import sys 

35 from pathlib import Path 

36 sys.path.insert(0, str(Path(__file__).parent)) 

37 from solidity_analyzer import SolidityVulnerability, VulnerabilityType 

38 

39 

40class MEVType(Enum): 

41 """Types of MEV vulnerabilities""" 

42 FRONTRUNNING = "frontrunning" 

43 SANDWICH_ATTACK = "sandwich_attack" 

44 LIQUIDATION_MEV = "liquidation_mev" 

45 TIMESTAMP_MANIPULATION = "timestamp_manipulation" 

46 ORACLE_MEV = "oracle_mev" 

47 

48 

49@dataclass 

50class MEVVulnerability: 

51 """MEV vulnerability with profit estimation""" 

52 mev_type: MEVType 

53 severity: str 

54 title: str 

55 description: str 

56 line_number: int 

57 function_name: str 

58 estimated_mev_profit: Tuple[float, float] # Min, max profit in USD 

59 user_loss_per_tx: Tuple[float, float] # Min, max loss per transaction 

60 attack_complexity: str # "low", "medium", "high" 

61 time_to_exploit: str # "immediate", "hours", "days" 

62 historical_examples: List[str] 

63 confidence: int = 90 

64 

65 

66class MEVDetector: 

67 """ 

68 Detect MEV vulnerabilities in smart contracts 

69 

70 Week 3 Day 3 Implementation: 

71 1. Front-running detection (oracle updates, approvals, etc.) 

72 2. Sandwich attack detection (DEX swaps without slippage) 

73 3. Liquidation MEV detection (public liquidations) 

74 4. Timestamp manipulation detection 

75 

76 MEV Categories: 

77 - Front-running: $200M+ in 2024 

78 - Sandwich attacks: $150M+ in 2024 

79 - Liquidation MEV: $100M+ in 2024 

80 """ 

81 

82 def __init__(self): 

83 self.vulnerabilities: List[MEVVulnerability] = [] 

84 

85 # Historical MEV data for context 

86 self.mev_historical = { 

87 "frontrunning": { 

88 "examples": [ 

89 "Bancor front-running (2020): $500K+", 

90 "Uniswap V2 front-runs (2021): $2M+", 

91 "NFT mint front-runs (2021-2022): $10M+" 

92 ], 

93 "avg_profit_per_tx": (100, 5000), 

94 "total_2024": 200_000_000 

95 }, 

96 "sandwich": { 

97 "examples": [ 

98 "jaredfromsubway.eth: $40M+ (2023-2024)", 

99 "MEV bot 0x000: $20M+ (2024)", 

100 "Various sandwich bots: $100M+ (2024)" 

101 ], 

102 "avg_profit_per_tx": (50, 2000), 

103 "total_2024": 150_000_000 

104 }, 

105 "liquidation": { 

106 "examples": [ 

107 "Aave liquidations: $50M+ MEV (2024)", 

108 "Compound liquidations: $30M+ MEV (2024)", 

109 "MakerDAO liquidations: $20M+ MEV (2024)" 

110 ], 

111 "avg_profit_per_tx": (500, 50000), 

112 "total_2024": 100_000_000 

113 } 

114 } 

115 

116 def analyze_contract(self, contract_code: str, file_path: str) -> List[SolidityVulnerability]: 

117 """ 

118 Analyze contract for MEV vulnerabilities 

119 

120 Returns standard SolidityVulnerability objects for integration 

121 """ 

122 self.vulnerabilities = [] 

123 

124 # Extract functions 

125 functions = self._extract_functions(contract_code) 

126 

127 for func in functions: 

128 # Detect front-running vulnerabilities 

129 self._detect_frontrunning(func, contract_code) 

130 

131 # Detect sandwich attack vulnerabilities 

132 self._detect_sandwich_attacks(func, contract_code) 

133 

134 # Detect liquidation MEV 

135 self._detect_liquidation_mev(func, contract_code) 

136 

137 # Detect timestamp manipulation 

138 self._detect_timestamp_manipulation(func, contract_code) 

139 

140 # Convert to standard format 

141 return self._convert_to_standard_format(file_path) 

142 

143 def _extract_functions(self, contract_code: str) -> List[Dict[str, Any]]: 

144 """Extract function definitions from contract""" 

145 functions = [] 

146 lines = contract_code.split('\n') 

147 

148 i = 0 

149 while i < len(lines): 

150 line = lines[i].strip() 

151 

152 # Match function definition 

153 func_match = re.match( 

154 r'function\s+(\w+)\s*\([^)]*\)\s*(public|external|internal|private)?', 

155 line 

156 ) 

157 

158 if func_match: 

159 func_name = func_match.group(1) 

160 visibility = func_match.group(2) or 'internal' 

161 

162 # Extract function body 

163 start_line = i 

164 brace_count = 0 

165 body_lines = [] 

166 

167 # Find opening brace 

168 while i < len(lines) and '{' not in lines[i]: 

169 i += 1 

170 

171 if i < len(lines): 

172 brace_count = lines[i].count('{') - lines[i].count('}') 

173 body_lines.append(lines[i]) 

174 i += 1 

175 

176 # Extract until closing brace 

177 while i < len(lines) and brace_count > 0: 

178 line = lines[i] 

179 brace_count += line.count('{') - line.count('}') 

180 body_lines.append(line) 

181 i += 1 

182 

183 functions.append({ 

184 'name': func_name, 

185 'visibility': visibility, 

186 'start_line': start_line + 1, 

187 'body': '\n'.join(body_lines), 

188 'body_lines': body_lines 

189 }) 

190 

191 i += 1 

192 

193 return functions 

194 

195 def _detect_frontrunning(self, func: Dict[str, Any], contract_code: str): 

196 """ 

197 Detect front-running vulnerabilities 

198 

199 Patterns: 

200 1. Oracle price update + immediate use 

201 2. Approval + transferFrom in same tx 

202 3. Public state changes used in price calculations 

203 4. Order placement without commitment scheme 

204 """ 

205 func_name = func['name'] 

206 body = func['body'] 

207 start_line = func['start_line'] 

208 

209 # Pattern 1: Oracle update + price usage 

210 if self._has_oracle_update_and_use(body): 

211 self.vulnerabilities.append(MEVVulnerability( 

212 mev_type=MEVType.FRONTRUNNING, 

213 severity="critical", 

214 title=f"Front-Running: Oracle Price Update in {func_name}", 

215 description=( 

216 f"Function {func_name} updates oracle price and immediately uses it. " 

217 f"Attackers can front-run this transaction to profit from the price change." 

218 ), 

219 line_number=start_line, 

220 function_name=func_name, 

221 estimated_mev_profit=(10_000, 100_000), 

222 user_loss_per_tx=(1_000, 50_000), 

223 attack_complexity="low", 

224 time_to_exploit="immediate", 

225 historical_examples=self.mev_historical["frontrunning"]["examples"], 

226 confidence=95 

227 )) 

228 

229 # Pattern 2: Approval pattern (approve + action) 

230 if 'approve(' in body and ('transfer' in body or 'swap' in body): 

231 self.vulnerabilities.append(MEVVulnerability( 

232 mev_type=MEVType.FRONTRUNNING, 

233 severity="high", 

234 title=f"Front-Running: Approval Pattern in {func_name}", 

235 description=( 

236 f"Function {func_name} contains approval followed by action. " 

237 f"Attackers can front-run the approval to gain advantage." 

238 ), 

239 line_number=start_line, 

240 function_name=func_name, 

241 estimated_mev_profit=(100, 5_000), 

242 user_loss_per_tx=(50, 1_000), 

243 attack_complexity="medium", 

244 time_to_exploit="immediate", 

245 historical_examples=["ERC20 approval races: $5M+ (2020-2024)"], 

246 confidence=85 

247 )) 

248 

249 # Pattern 3: Public state change affecting price 

250 if self._has_price_affecting_public_change(body, func): 

251 self.vulnerabilities.append(MEVVulnerability( 

252 mev_type=MEVType.FRONTRUNNING, 

253 severity="high", 

254 title=f"Front-Running: Public Price-Affecting Change in {func_name}", 

255 description=( 

256 f"Function {func_name} makes public state changes that affect prices. " 

257 f"MEV bots can front-run to profit from predictable price impact." 

258 ), 

259 line_number=start_line, 

260 function_name=func_name, 

261 estimated_mev_profit=(1_000, 50_000), 

262 user_loss_per_tx=(500, 10_000), 

263 attack_complexity="medium", 

264 time_to_exploit="immediate", 

265 historical_examples=["DEX arbitrage front-runs: $200M+ (2024)"], 

266 confidence=80 

267 )) 

268 

269 def _detect_sandwich_attacks(self, func: Dict[str, Any], contract_code: str): 

270 """ 

271 Detect sandwich attack vulnerabilities 

272 

273 Patterns: 

274 1. Swap without slippage protection 

275 2. Missing deadline parameter 

276 3. Predictable swap routing 

277 4. Large swap without price impact protection 

278 """ 

279 func_name = func['name'] 

280 body = func['body'] 

281 start_line = func['start_line'] 

282 

283 # Pattern 1: Swap without slippage protection 

284 has_swap = any(keyword in body.lower() for keyword in ['swap', 'exchange', 'trade']) 

285 

286 if has_swap: 

287 has_slippage = any(keyword in body.lower() for keyword in [ 

288 'minamount', 'min_amount', 'slippage', 'amountoutmin' 

289 ]) 

290 

291 if not has_slippage: 

292 self.vulnerabilities.append(MEVVulnerability( 

293 mev_type=MEVType.SANDWICH_ATTACK, 

294 severity="critical", 

295 title=f"Sandwich Attack: No Slippage Protection in {func_name}", 

296 description=( 

297 f"Function {func_name} performs swap without slippage protection. " 

298 f"MEV bots can sandwich attack: buy before (front-run), " 

299 f"user swap at worse price, sell after (back-run) for profit." 

300 ), 

301 line_number=start_line, 

302 function_name=func_name, 

303 estimated_mev_profit=(50, 5_000), 

304 user_loss_per_tx=(25, 2_000), 

305 attack_complexity="low", 

306 time_to_exploit="immediate", 

307 historical_examples=self.mev_historical["sandwich"]["examples"], 

308 confidence=95 

309 )) 

310 

311 # Pattern 2: Missing deadline protection 

312 if has_swap: 

313 has_deadline = 'deadline' in body.lower() 

314 

315 if not has_deadline: 

316 self.vulnerabilities.append(MEVVulnerability( 

317 mev_type=MEVType.SANDWICH_ATTACK, 

318 severity="high", 

319 title=f"Sandwich Attack: Missing Deadline in {func_name}", 

320 description=( 

321 f"Function {func_name} performs swap without deadline parameter. " 

322 f"Transaction can be held in mempool and executed at worst price." 

323 ), 

324 line_number=start_line, 

325 function_name=func_name, 

326 estimated_mev_profit=(100, 10_000), 

327 user_loss_per_tx=(50, 5_000), 

328 attack_complexity="low", 

329 time_to_exploit="immediate", 

330 historical_examples=["Delayed execution attacks: $10M+ (2023-2024)"], 

331 confidence=90 

332 )) 

333 

334 # Pattern 3: getAmountsOut without TWAP (spot price vulnerability) 

335 if 'getamountsout' in body.lower() or 'getamountout' in body.lower(): 

336 has_twap = 'twap' in body.lower() or 'observe' in body.lower() 

337 

338 if not has_twap: 

339 self.vulnerabilities.append(MEVVulnerability( 

340 mev_type=MEVType.SANDWICH_ATTACK, 

341 severity="critical", 

342 title=f"Sandwich Attack: Spot Price Usage in {func_name}", 

343 description=( 

344 f"Function {func_name} uses spot price (getAmountsOut) without TWAP. " 

345 f"Highly vulnerable to sandwich attacks and price manipulation." 

346 ), 

347 line_number=start_line, 

348 function_name=func_name, 

349 estimated_mev_profit=(1_000, 50_000), 

350 user_loss_per_tx=(500, 20_000), 

351 attack_complexity="low", 

352 time_to_exploit="immediate", 

353 historical_examples=["Spot price manipulation: $100M+ (2024)"], 

354 confidence=95 

355 )) 

356 

357 def _detect_liquidation_mev(self, func: Dict[str, Any], contract_code: str): 

358 """ 

359 Detect liquidation MEV vulnerabilities 

360 

361 Patterns: 

362 1. Public liquidation function without priority queue 

363 2. Missing liquidation delay 

364 3. First-come-first-serve liquidation rewards 

365 4. Full liquidation without gradual approach 

366 """ 

367 func_name = func['name'] 

368 body = func['body'] 

369 start_line = func['start_line'] 

370 visibility = func['visibility'] 

371 

372 # Pattern 1: Public liquidation function 

373 is_liquidation = 'liquidat' in func_name.lower() or 'liquidat' in body.lower() 

374 

375 if is_liquidation and visibility in ['public', 'external']: 

376 has_priority = any(keyword in body.lower() for keyword in [ 

377 'priority', 'queue', 'delay', 'timelock' 

378 ]) 

379 

380 if not has_priority: 

381 self.vulnerabilities.append(MEVVulnerability( 

382 mev_type=MEVType.LIQUIDATION_MEV, 

383 severity="high", 

384 title=f"Liquidation MEV: No Priority Protection in {func_name}", 

385 description=( 

386 f"Function {func_name} allows public liquidation without priority mechanism. " 

387 f"MEV bots with better infrastructure will always win liquidations, " 

388 f"extracting maximum value." 

389 ), 

390 line_number=start_line, 

391 function_name=func_name, 

392 estimated_mev_profit=(500, 100_000), 

393 user_loss_per_tx=(0, 10_000), # Borrower loss 

394 attack_complexity="medium", 

395 time_to_exploit="hours", 

396 historical_examples=self.mev_historical["liquidation"]["examples"], 

397 confidence=90 

398 )) 

399 

400 # Pattern 2: Full liquidation (100% at once) 

401 if is_liquidation: 

402 has_partial = any(keyword in body.lower() for keyword in [ 

403 'partial', 'percentage', 'ratio', 'closefactor' 

404 ]) 

405 

406 if not has_partial: 

407 self.vulnerabilities.append(MEVVulnerability( 

408 mev_type=MEVType.LIQUIDATION_MEV, 

409 severity="medium", 

410 title=f"Liquidation MEV: Full Liquidation in {func_name}", 

411 description=( 

412 f"Function {func_name} allows full (100%) liquidation. " 

413 f"This incentivizes MEV bots to race for maximum profit, " 

414 f"potentially causing unfair borrower losses." 

415 ), 

416 line_number=start_line, 

417 function_name=func_name, 

418 estimated_mev_profit=(1_000, 50_000), 

419 user_loss_per_tx=(500, 20_000), 

420 attack_complexity="low", 

421 time_to_exploit="hours", 

422 historical_examples=["Full liquidation races: $50M+ (2024)"], 

423 confidence=80 

424 )) 

425 

426 def _detect_timestamp_manipulation(self, func: Dict[str, Any], contract_code: str): 

427 """ 

428 Detect timestamp manipulation vulnerabilities 

429 

430 Patterns: 

431 1. block.timestamp used in critical logic 

432 2. Randomness based on timestamp 

433 3. Time-based rewards without protection 

434 """ 

435 func_name = func['name'] 

436 body = func['body'] 

437 start_line = func['start_line'] 

438 

439 # Pattern 1: block.timestamp in conditional 

440 if 'block.timestamp' in body: 

441 # Check if used in critical operations 

442 has_critical_use = any(keyword in body.lower() for keyword in [ 

443 'reward', 'mint', 'claim', 'unlock', 'vesting' 

444 ]) 

445 

446 if has_critical_use: 

447 self.vulnerabilities.append(MEVVulnerability( 

448 mev_type=MEVType.TIMESTAMP_MANIPULATION, 

449 severity="medium", 

450 title=f"Timestamp Manipulation: Critical Use in {func_name}", 

451 description=( 

452 f"Function {func_name} uses block.timestamp in critical logic. " 

453 f"Miners can manipulate timestamp by ~15 seconds, " 

454 f"potentially affecting rewards or access control." 

455 ), 

456 line_number=start_line, 

457 function_name=func_name, 

458 estimated_mev_profit=(100, 10_000), 

459 user_loss_per_tx=(0, 5_000), 

460 attack_complexity="high", 

461 time_to_exploit="hours", 

462 historical_examples=["Timestamp manipulation: $5M+ (2020-2024)"], 

463 confidence=75 

464 )) 

465 

466 # Pattern 2: Randomness from timestamp 

467 if 'block.timestamp' in body and any(keyword in body.lower() for keyword in [ 

468 'random', 'seed', 'keccak', 'hash' 

469 ]): 

470 self.vulnerabilities.append(MEVVulnerability( 

471 mev_type=MEVType.TIMESTAMP_MANIPULATION, 

472 severity="high", 

473 title=f"Timestamp Manipulation: Weak Randomness in {func_name}", 

474 description=( 

475 f"Function {func_name} uses block.timestamp for randomness. " 

476 f"Miners can manipulate this to predict or influence outcomes." 

477 ), 

478 line_number=start_line, 

479 function_name=func_name, 

480 estimated_mev_profit=(1_000, 100_000), 

481 user_loss_per_tx=(500, 50_000), 

482 attack_complexity="medium", 

483 time_to_exploit="immediate", 

484 historical_examples=["Weak randomness exploits: $20M+ (2020-2024)"], 

485 confidence=90 

486 )) 

487 

488 def _has_oracle_update_and_use(self, body: str) -> bool: 

489 """Check if function updates oracle and uses price""" 

490 has_update = any(keyword in body.lower() for keyword in [ 

491 'updateprice', 'setprice', 'oracle.update', 'pricefeed.update' 

492 ]) 

493 

494 has_use = any(keyword in body.lower() for keyword in [ 

495 'getprice', 'price()', 'swap', 'trade', 'exchange' 

496 ]) 

497 

498 return has_update and has_use 

499 

500 def _has_price_affecting_public_change(self, body: str, func: Dict[str, Any]) -> bool: 

501 """Check if public function affects prices""" 

502 is_public = func['visibility'] in ['public', 'external'] 

503 

504 affects_price = any(keyword in body.lower() for keyword in [ 

505 'reserves', 'liquidity', 'balance', 'supply', 'mint', 'burn' 

506 ]) 

507 

508 return is_public and affects_price 

509 

510 def _convert_to_standard_format(self, file_path: str) -> List[SolidityVulnerability]: 

511 """Convert MEV vulnerabilities to standard format""" 

512 standard_vulns = [] 

513 

514 for vuln in self.vulnerabilities: 

515 # Map MEV types to standard vulnerability types 

516 vuln_type_map = { 

517 MEVType.FRONTRUNNING: VulnerabilityType.LOGIC_ERROR, 

518 MEVType.SANDWICH_ATTACK: VulnerabilityType.ORACLE_MANIPULATION, 

519 MEVType.LIQUIDATION_MEV: VulnerabilityType.ACCESS_CONTROL, 

520 MEVType.TIMESTAMP_MANIPULATION: VulnerabilityType.TIMESTAMP_DEPENDENCE, 

521 MEVType.ORACLE_MEV: VulnerabilityType.ORACLE_MANIPULATION, 

522 } 

523 

524 vuln_type = vuln_type_map.get(vuln.mev_type, VulnerabilityType.LOGIC_ERROR) 

525 

526 # Create code snippet with MEV details 

527 mev_details = ( 

528 f"MEV Profit Potential: ${vuln.estimated_mev_profit[0]:,} - ${vuln.estimated_mev_profit[1]:,}\n" 

529 f"User Loss Per TX: ${vuln.user_loss_per_tx[0]:,} - ${vuln.user_loss_per_tx[1]:,}\n" 

530 f"Attack Complexity: {vuln.attack_complexity}\n" 

531 f"Time to Exploit: {vuln.time_to_exploit}\n" 

532 f"Historical Examples: {', '.join(vuln.historical_examples[:2])}" 

533 ) 

534 

535 # Create remediation advice 

536 remediation = self._get_remediation(vuln.mev_type) 

537 

538 standard_vuln = SolidityVulnerability( 

539 vulnerability_type=vuln_type, 

540 severity=vuln.severity, 

541 title=f"[MEV] {vuln.title}", 

542 description=vuln.description, 

543 file_path=file_path, 

544 line_number=vuln.line_number, 

545 function_name=vuln.function_name, 

546 contract_name="unknown", 

547 code_snippet=mev_details, 

548 remediation=remediation, 

549 confidence=vuln.confidence 

550 ) 

551 

552 standard_vulns.append(standard_vuln) 

553 

554 return standard_vulns 

555 

556 def _get_remediation(self, mev_type: MEVType) -> str: 

557 """Get remediation advice for MEV vulnerability""" 

558 remediation_map = { 

559 MEVType.FRONTRUNNING: ( 

560 "Use commit-reveal schemes or time-locks for sensitive operations. " 

561 "Consider using private mempools (e.g., Flashbots Protect) or " 

562 "submarine sends to hide transactions from front-runners." 

563 ), 

564 MEVType.SANDWICH_ATTACK: ( 

565 "Add slippage protection with amountOutMin parameter. " 

566 "Include deadline parameter to prevent delayed execution. " 

567 "Use TWAP (Time-Weighted Average Price) instead of spot prices. " 

568 "Consider using MEV-aware DEX designs (e.g., CoWSwap, 1inch Fusion)." 

569 ), 

570 MEVType.LIQUIDATION_MEV: ( 

571 "Implement liquidation priority queue or Dutch auction mechanism. " 

572 "Use partial liquidations with close factor < 100%. " 

573 "Add liquidation delay to allow borrowers to self-liquidate. " 

574 "Consider keeper-based liquidation systems." 

575 ), 

576 MEVType.TIMESTAMP_MANIPULATION: ( 

577 "Avoid using block.timestamp for critical logic. " 

578 "Use block.number with estimated block times for time-based logic. " 

579 "Never use block.timestamp for randomness (use Chainlink VRF instead)." 

580 ), 

581 MEVType.ORACLE_MEV: ( 

582 "Separate oracle updates from price usage across transactions. " 

583 "Use commit-reveal for oracle updates. " 

584 "Implement TWAP or median prices to prevent manipulation." 

585 ) 

586 } 

587 

588 return remediation_map.get(mev_type, "Review MEV implications carefully.") 

589 

590 

591# Example usage and testing 

592if __name__ == "__main__": 

593 detector = MEVDetector() 

594 

595 # Test case: DEX with multiple MEV vulnerabilities 

596 test_contract = """ 

597 contract VulnerableDEX { 

598 IUniswapV2Router router; 

599 IPriceOracle oracle; 

600 

601 // VULNERABLE: Oracle update + immediate use (front-running) 

602 function updateAndSwap(uint256 amountIn) external { 

603 oracle.updatePrice(); 

604 uint256 price = oracle.getPrice(); 

605 _swap(amountIn, price); 

606 } 

607 

608 // VULNERABLE: Swap without slippage protection (sandwich attack) 

609 function swapTokens(uint256 amountIn) external { 

610 address[] memory path = new address[](2); 

611 path[0] = address(tokenA); 

612 path[1] = address(tokenB); 

613 

614 router.swapExactTokensForTokens( 

615 amountIn, 

616 0, // NO SLIPPAGE PROTECTION! 

617 path, 

618 msg.sender, 

619 block.timestamp + 3600 

620 ); 

621 } 

622 

623 // VULNERABLE: Public liquidation without priority (liquidation MEV) 

624 function liquidate(address user) external { 

625 require(isLiquidatable(user), "Not liquidatable"); 

626 

627 uint256 debt = getDebt(user); 

628 _liquidate(user, debt); // Full liquidation, MEV race! 

629 } 

630 

631 // VULNERABLE: Timestamp used for rewards (timestamp manipulation) 

632 function claimRewards() external { 

633 uint256 timeElapsed = block.timestamp - lastClaim[msg.sender]; 

634 uint256 reward = timeElapsed * rewardRate; 

635 

636 _mint(msg.sender, reward); 

637 } 

638 } 

639 """ 

640 

641 vulns = detector.analyze_contract(test_contract, "test.sol") 

642 

643 print(f"Found {len(vulns)} MEV vulnerabilities:\n") 

644 for vuln in vulns: 

645 print(f"{vuln.severity.upper()}: {vuln.title}") 

646 print(f" {vuln.description}") 

647 print(f" {vuln.code_snippet}") 

648 print()