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 }