Files
mev-beta/tests/calculation-validation/replay.go
Administrator 803de231ba feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor:

**Structure Changes:**
- Moved all V1 code to orig/ folder (preserved with git mv)
- Created docs/planning/ directory
- Added orig/README_V1.md explaining V1 preservation

**Planning Documents:**
- 00_V2_MASTER_PLAN.md: Complete architecture overview
  - Executive summary of critical V1 issues
  - High-level component architecture diagrams
  - 5-phase implementation roadmap
  - Success metrics and risk mitigation

- 07_TASK_BREAKDOWN.md: Atomic task breakdown
  - 99+ hours of detailed tasks
  - Every task < 2 hours (atomic)
  - Clear dependencies and success criteria
  - Organized by implementation phase

**V2 Key Improvements:**
- Per-exchange parsers (factory pattern)
- Multi-layer strict validation
- Multi-index pool cache
- Background validation pipeline
- Comprehensive observability

**Critical Issues Addressed:**
- Zero address tokens (strict validation + cache enrichment)
- Parsing accuracy (protocol-specific parsers)
- No audit trail (background validation channel)
- Inefficient lookups (multi-index cache)
- Stats disconnection (event-driven metrics)

Next Steps:
1. Review planning documents
2. Begin Phase 1: Foundation (P1-001 through P1-010)
3. Implement parsers in Phase 2
4. Build cache system in Phase 3
5. Add validation pipeline in Phase 4
6. Migrate and test in Phase 5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:14:26 +01:00

263 lines
9.3 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"math/big"
"os"
"path/filepath"
"time"
validation "mev-bot/tests/calculation-validation"
)
func main() {
// Parse command line flags
testDir := flag.String("dir", "tests/calculation-validation", "Test directory containing extracted logs")
tolerance := flag.Float64("tolerance", 1.0, "Tolerance percentage for validation")
verbose := flag.Bool("verbose", false, "Verbose output")
flag.Parse()
fmt.Println("═══════════════════════════════════════════════════════════════════════")
fmt.Println(" MEV Bot - Calculation Replay & Validation Tool")
fmt.Println("═══════════════════════════════════════════════════════════════════════")
fmt.Println()
// Initialize parser and validator
parser := validation.NewLogParser()
validator := validation.NewProfitValidator(*tolerance)
// Define file paths
extractedDir := filepath.Join(*testDir, "extracted")
executableFile := filepath.Join(extractedDir, "executable_opportunities.log")
detailsFile := filepath.Join(extractedDir, "opportunity_details.log")
v3CalcFile := filepath.Join(extractedDir, "v3_calculations.log")
thresholdFile := filepath.Join(extractedDir, "threshold_checks.log")
fmt.Println("📂 Loading test data...")
// Parse executable opportunities
executableOpps, err := parser.ParseExecutableOpportunities(executableFile)
if err != nil {
log.Printf("Warning: Could not parse executable opportunities: %v\n", err)
executableOpps = []*validation.OpportunityTestData{}
}
fmt.Printf(" ✓ Loaded %d executable opportunities\n", len(executableOpps))
// Parse detailed opportunities
detailedOpps, err := parser.ParseOpportunityDetails(detailsFile)
if err != nil {
log.Printf("Warning: Could not parse opportunity details: %v\n", err)
detailedOpps = []*validation.OpportunityTestData{}
}
fmt.Printf(" ✓ Loaded %d detailed opportunities\n", len(detailedOpps))
// Parse V3 calculations
v3Calcs, err := parser.ParseV3Calculations(v3CalcFile)
if err != nil {
log.Printf("Warning: Could not parse V3 calculations: %v\n", err)
v3Calcs = []validation.SwapCalculation{}
}
fmt.Printf(" ✓ Loaded %d V3 calculations\n", len(v3Calcs))
// Parse threshold checks
thresholdChecks, err := parser.ParseThresholdChecks(thresholdFile)
if err != nil {
log.Printf("Warning: Could not parse threshold checks: %v\n", err)
thresholdChecks = []*validation.ThresholdCheck{}
}
fmt.Printf(" ✓ Loaded %d threshold checks\n", len(thresholdChecks))
fmt.Println()
fmt.Println("🔍 Validating calculations...")
fmt.Println()
// Validate executable opportunities
if len(executableOpps) > 0 {
fmt.Println("━━━ Executable Opportunities ━━━")
report := validator.ValidateBatch(executableOpps)
printReport(report, *verbose)
}
// Validate detailed opportunities
if len(detailedOpps) > 0 {
fmt.Println("\n━━━ Detailed Opportunities ━━━")
report := validator.ValidateBatch(detailedOpps)
printReport(report, *verbose)
}
// Validate V3 calculations
if len(v3Calcs) > 0 {
fmt.Println("\n━━━ V3 Swap Calculations ━━━")
validV3 := 0
invalidV3 := 0
warningsV3 := 0
for i, calc := range v3Calcs {
result := validator.ValidateV3Calculation(calc)
if result.IsValid {
validV3++
} else {
invalidV3++
}
if len(result.Warnings) > 0 {
warningsV3++
}
if *verbose && (!result.IsValid || len(result.Warnings) > 0) {
fmt.Printf(" V3 Calc #%d:\n", i+1)
fmt.Printf(" AmountIn: %s\n", calc.AmountIn.String())
fmt.Printf(" AmountOut: %s\n", calc.AmountOut.String())
fmt.Printf(" Fee: %d\n", calc.Fee)
fmt.Printf(" FinalOut: %s\n", calc.FinalOut.String())
if len(result.Errors) > 0 {
fmt.Printf(" ❌ Errors: %v\n", result.Errors)
}
if len(result.Warnings) > 0 {
fmt.Printf(" ⚠️ Warnings: %v\n", result.Warnings)
}
}
}
fmt.Printf(" Total: %d\n", len(v3Calcs))
fmt.Printf(" ✅ Valid: %d\n", validV3)
fmt.Printf(" ❌ Invalid: %d\n", invalidV3)
fmt.Printf(" ⚠️ Warnings: %d\n", warningsV3)
}
// Validate threshold checks
if len(thresholdChecks) > 0 {
fmt.Println("\n━━━ Profit Threshold Checks ━━━")
validThresholds := 0
invalidThresholds := 0
for i, check := range thresholdChecks {
// Validate the threshold comparison logic
wasExecutable := check.Passed
result := validator.ValidateThresholdComparison(
check.NetProfit,
big.NewInt(100000000000000), // 0.0001 ETH in wei (default threshold)
wasExecutable,
)
if result.IsValid {
validThresholds++
} else {
invalidThresholds++
if *verbose {
fmt.Printf(" ❌ Threshold Check #%d FAILED:\n", i+1)
fmt.Printf(" Net Profit: %s ETH\n", check.NetProfit.String())
fmt.Printf(" Min Threshold: %s ETH\n", check.MinThreshold.String())
fmt.Printf(" Marked Executable: %v\n", wasExecutable)
fmt.Printf(" Errors: %v\n", result.Errors)
}
}
}
fmt.Printf(" Total: %d\n", len(thresholdChecks))
fmt.Printf(" ✅ Valid: %d\n", validThresholds)
fmt.Printf(" ❌ Invalid: %d\n", invalidThresholds)
fmt.Printf(" Success Rate: %.2f%%\n", float64(validThresholds)/float64(len(thresholdChecks))*100)
}
// Calculate and display profit statistics
if len(executableOpps) > 0 {
fmt.Println("\n━━━ Profit Statistics ━━━")
profits := validation.ExtractProfitValues(executableOpps)
total, average, max, min := validation.CalculateStatistics(profits)
totalFloat, _ := total.Float64()
avgFloat, _ := average.Float64()
maxFloat, _ := max.Float64()
minFloat, _ := min.Float64()
fmt.Printf(" Total Profit: %.6f ETH\n", totalFloat)
fmt.Printf(" Average Profit: %.6f ETH\n", avgFloat)
fmt.Printf(" Maximum Profit: %.6f ETH\n", maxFloat)
fmt.Printf(" Minimum Profit: %.6f ETH\n", minFloat)
fmt.Printf(" Opportunities: %d\n", len(profits))
}
// Test the CRITICAL bug fix
fmt.Println("\n━━━ Critical Bug Fix Validation ━━━")
testCriticalBugFix(validator)
fmt.Println()
fmt.Println("═══════════════════════════════════════════════════════════════════════")
fmt.Println(" Validation Complete!")
fmt.Println("═══════════════════════════════════════════════════════════════════════")
}
func printReport(report *validation.TestReport, verbose bool) {
fmt.Printf(" Total: %d\n", report.TotalOpportunities)
fmt.Printf(" ✅ Valid: %d\n", report.ValidCalculations)
fmt.Printf(" ❌ Invalid: %d\n", report.InvalidCalculations)
fmt.Printf(" Success Rate: %.2f%%\n", float64(report.ValidCalculations)/float64(report.TotalOpportunities)*100)
if verbose {
for _, result := range report.ValidationResults {
if !result.IsValid || len(result.Warnings) > 0 {
fmt.Printf("\n Opportunity: %s\n", result.OpportunityID)
if len(result.Errors) > 0 {
fmt.Printf(" ❌ Errors: %v\n", result.Errors)
}
if len(result.Warnings) > 0 {
fmt.Printf(" ⚠️ Warnings: %v\n", result.Warnings)
}
}
}
}
}
func testCriticalBugFix(validator *validation.ProfitValidator) {
// Test the exact bug scenario: 834.210302 ETH profit vs 0.0001 ETH threshold
netProfit := big.NewFloat(834.210302)
minThresholdWei := big.NewInt(100000000000000) // 0.0001 ETH in wei
fmt.Println(" Testing bug fix scenario:")
fmt.Printf(" Net Profit: %.6f ETH\n", mustFloat64(netProfit))
fmt.Printf(" Min Threshold: 0.0001 ETH (100000000000000 wei)\n")
// Before fix: netProfit.Int(nil) would return 834, comparing 834 < 100000000000000 = FALSE (rejected)
// After fix: Should compare 834.210302 >= 0.0001 = TRUE (executable)
result := validator.ValidateThresholdComparison(netProfit, minThresholdWei, true)
if result.IsValid {
fmt.Println(" ✅ BUG FIX VALIDATED: Correctly marked as executable")
} else {
fmt.Println(" ❌ BUG FIX FAILED: Incorrectly rejected")
fmt.Printf(" Errors: %v\n", result.Errors)
}
// Test edge case: profit exactly at threshold
edgeProfit := big.NewFloat(0.0001)
edgeResult := validator.ValidateThresholdComparison(edgeProfit, minThresholdWei, true)
fmt.Println("\n Testing edge case (profit == threshold):")
fmt.Printf(" Net Profit: %.6f ETH\n", mustFloat64(edgeProfit))
if edgeResult.IsValid {
fmt.Println(" ✅ Edge case handled correctly")
} else {
fmt.Println(" ❌ Edge case failed")
}
// Test rejection case: profit below threshold
lowProfit := big.NewFloat(0.00001)
lowResult := validator.ValidateThresholdComparison(lowProfit, minThresholdWei, false)
fmt.Println("\n Testing rejection case (profit < threshold):")
fmt.Printf(" Net Profit: %.6f ETH\n", mustFloat64(lowProfit))
if lowResult.IsValid {
fmt.Println(" ✅ Correctly rejected")
} else {
fmt.Println(" ❌ Should have been rejected")
}
}
func mustFloat64(f *big.Float) float64 {
val, _ := f.Float64()
return val
}