Files
mev-beta/orig/tests/calculation-validation/validator.go
Administrator c54c569f30 refactor: move all remaining files to orig/ directory
Completed clean root directory structure:
- Root now contains only: .git, .env, docs/, orig/
- Moved all remaining files and directories to orig/:
  - Config files (.claude, .dockerignore, .drone.yml, etc.)
  - All .env variants (except active .env)
  - Git config (.gitconfig, .github, .gitignore, etc.)
  - Tool configs (.golangci.yml, .revive.toml, etc.)
  - Documentation (*.md files, @prompts)
  - Build files (Dockerfiles, Makefile, go.mod, go.sum)
  - Docker compose files
  - All source directories (scripts, tests, tools, etc.)
  - Runtime directories (logs, monitoring, reports)
  - Dependency files (node_modules, lib, cache)
  - Special files (--delete)

- Removed empty runtime directories (bin/, data/)

V2 structure is now clean:
- docs/planning/ - V2 planning documents
- orig/ - Complete V1 codebase preserved
- .env - Active environment config (not in git)

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

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

240 lines
6.9 KiB
Go

package calculation_validation
import (
"fmt"
"math/big"
)
// ProfitValidator validates arbitrage profit calculations
type ProfitValidator struct {
weiPerEth *big.Int
tolerance float64 // Acceptable percentage difference
}
// NewProfitValidator creates a new profit validator
func NewProfitValidator(tolerancePercent float64) *ProfitValidator {
return &ProfitValidator{
weiPerEth: big.NewInt(1e18),
tolerance: tolerancePercent,
}
}
// ValidateOpportunity validates an opportunity's profit calculation
func (pv *ProfitValidator) ValidateOpportunity(opp *OpportunityTestData) *ValidationResult {
result := &ValidationResult{
OpportunityID: opp.ID,
IsValid: true,
Errors: []string{},
Warnings: []string{},
}
// Validate threshold check
if opp.ThresholdCheck != nil {
thresholdValid := pv.validateThresholdCheck(opp.ThresholdCheck)
if !thresholdValid {
result.Errors = append(result.Errors, "threshold check failed")
result.IsValid = false
}
}
// Validate profit values
if opp.NetProfitETH != nil {
result.ExpectedProfit = opp.NetProfitETH
// Check if profit is positive
if opp.NetProfitETH.Sign() <= 0 && opp.IsExecutable {
result.Errors = append(result.Errors, "executable opportunity has non-positive profit")
result.IsValid = false
}
// Check if profit meets threshold
if opp.ThresholdCheck != nil && opp.IsExecutable {
if opp.NetProfitETH.Cmp(opp.ThresholdCheck.MinThreshold) < 0 {
result.Errors = append(result.Errors, "executable opportunity below minimum threshold")
result.IsValid = false
}
}
}
// Validate rejection reason consistency
if opp.IsExecutable && opp.RejectReason != "" {
result.Warnings = append(result.Warnings, "executable opportunity has reject reason")
}
if !opp.IsExecutable && opp.RejectReason == "" {
result.Warnings = append(result.Warnings, "non-executable opportunity missing reject reason")
}
return result
}
// validateThresholdCheck validates a threshold check calculation
func (pv *ProfitValidator) validateThresholdCheck(check *ThresholdCheck) bool {
if check == nil {
return false
}
// Verify the comparison is correct
expected := check.NetProfit.Cmp(check.MinThreshold) >= 0
if expected != check.Passed {
return false
}
return true
}
// ValidateV3Calculation validates a V3 swap calculation
func (pv *ProfitValidator) ValidateV3Calculation(calc SwapCalculation) *ValidationResult {
result := &ValidationResult{
OpportunityID: "v3_calculation",
IsValid: true,
Errors: []string{},
Warnings: []string{},
}
// Check for zero outputs
if calc.AmountOut.Sign() == 0 && calc.AmountIn.Sign() > 0 {
result.Warnings = append(result.Warnings, "zero output with non-zero input")
}
// Check if finalOut matches amountOut (should be slightly less due to fees)
if calc.FinalOut.Cmp(calc.AmountOut) > 0 {
result.Errors = append(result.Errors, "finalOut greater than amountOut (impossible)")
result.IsValid = false
}
// Calculate expected fee deduction
expectedFee := pv.calculateV3Fee(calc.AmountOut, calc.Fee)
expectedFinalOut := new(big.Int).Sub(calc.AmountOut, expectedFee)
// Check if finalOut is within tolerance
diff := new(big.Int).Sub(expectedFinalOut, calc.FinalOut)
diff.Abs(diff)
// Allow 1% difference for rounding
tolerance := new(big.Int).Div(calc.FinalOut, big.NewInt(100))
if diff.Cmp(tolerance) > 0 && calc.FinalOut.Sign() > 0 {
result.Warnings = append(result.Warnings,
fmt.Sprintf("finalOut differs from expected: got %s, expected ~%s",
calc.FinalOut.String(), expectedFinalOut.String()))
}
return result
}
// calculateV3Fee calculates the fee for a V3 swap
func (pv *ProfitValidator) calculateV3Fee(amount *big.Int, feeTier uint32) *big.Int {
// Fee tiers: 500 = 0.05%, 3000 = 0.3%, 10000 = 1%
fee := new(big.Int).Mul(amount, big.NewInt(int64(feeTier)))
fee.Div(fee, big.NewInt(1000000))
return fee
}
// ValidateBatch validates multiple opportunities and generates a report
func (pv *ProfitValidator) ValidateBatch(opportunities []*OpportunityTestData) *TestReport {
report := &TestReport{
ValidationResults: []*ValidationResult{},
}
var totalError float64
errorCount := 0
for _, opp := range opportunities {
result := pv.ValidateOpportunity(opp)
report.ValidationResults = append(report.ValidationResults, result)
if result.IsValid {
report.ValidCalculations++
} else {
report.InvalidCalculations++
}
if result.PercentError > 0 {
totalError += result.PercentError
errorCount++
}
// Track max error
if result.Difference != nil {
if report.MaxError == nil || result.Difference.Cmp(report.MaxError) > 0 {
report.MaxError = new(big.Float).Set(result.Difference)
}
}
}
report.TotalOpportunities = len(opportunities)
if errorCount > 0 {
report.AveragePercentError = totalError / float64(errorCount)
}
return report
}
// CompareETHtoWei validates ETH to wei conversion
func (pv *ProfitValidator) CompareETHtoWei(ethValue *big.Float, weiValue *big.Int) *ValidationResult {
result := &ValidationResult{
OpportunityID: "eth_wei_conversion",
IsValid: true,
Errors: []string{},
}
// Convert ETH to wei
expectedWei := new(big.Float).Mul(ethValue, new(big.Float).SetInt(pv.weiPerEth))
expectedWeiInt, _ := expectedWei.Int(nil)
// Compare
if expectedWeiInt.Cmp(weiValue) != 0 {
result.IsValid = false
result.Errors = append(result.Errors,
fmt.Sprintf("ETH to wei conversion mismatch: %s ETH should be %s wei, got %s wei",
ethValue.String(), expectedWeiInt.String(), weiValue.String()))
// Calculate difference
diff := new(big.Int).Sub(expectedWeiInt, weiValue)
result.Difference = new(big.Float).SetInt(diff)
}
return result
}
// ValidateThresholdComparison validates that a profit threshold comparison was done correctly
// This is the CRITICAL validation for the bug we fixed
func (pv *ProfitValidator) ValidateThresholdComparison(
netProfitETH *big.Float,
minProfitThresholdWei *big.Int,
wasExecutable bool,
) *ValidationResult {
result := &ValidationResult{
OpportunityID: "threshold_comparison",
IsValid: true,
Errors: []string{},
}
// Convert threshold from wei to ETH for comparison
minProfitETH := new(big.Float).Quo(
new(big.Float).SetInt(minProfitThresholdWei),
new(big.Float).SetInt(pv.weiPerEth),
)
// Expected result: netProfitETH >= minProfitETH
expectedExecutable := netProfitETH.Cmp(minProfitETH) >= 0
if expectedExecutable != wasExecutable {
result.IsValid = false
result.Errors = append(result.Errors,
fmt.Sprintf("threshold comparison incorrect: %.6f ETH vs %.6f ETH threshold, should be executable=%v, got executable=%v",
mustFloat64(netProfitETH), mustFloat64(minProfitETH), expectedExecutable, wasExecutable))
}
result.ExpectedProfit = netProfitETH
result.CalculatedProfit = minProfitETH
return result
}
// mustFloat64 safely converts big.Float to float64
func mustFloat64(f *big.Float) float64 {
val, _ := f.Float64()
return val
}