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>
This commit is contained in:
262
tests/calculation-validation/replay.go
Normal file
262
tests/calculation-validation/replay.go
Normal file
@@ -0,0 +1,262 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user