package risk import ( "fmt" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/fraktal/mev-beta/internal/logger" ) // ProfitValidator validates profitability of MEV opportunities type ProfitValidator struct { logger *logger.Logger mu sync.RWMutex // Profit thresholds minProfitUSD float64 // Minimum profit in USD minProfitETH *big.Int // Minimum profit in ETH minProfitMargin float64 // Minimum profit margin percentage maxSlippage float64 // Maximum acceptable slippage maxGasPrice *big.Int // Maximum gas price willing to pay // Historical performance tracking totalOpportunities uint64 profitableOps uint64 unprofitableOps uint64 totalProfitETH *big.Int totalGasCostETH *big.Int averageGasCost *big.Int // Performance metrics validationSuccessRate float64 averageProfitMargin float64 averageSlippage float64 // Token price tracking tokenPrices map[common.Address]*TokenPrice priceMu sync.RWMutex // Validation configuration strictValidation bool // Whether to use strict validation rules } // TokenPrice represents real-time token pricing data type TokenPrice struct { Address common.Address PriceUSD float64 LastUpdated time.Time Confidence float64 // Price confidence 0-1 Volume24h float64 Volatility float64 } // ProfitValidationResult represents the result of a profit validation type ProfitValidationResult struct { OpportunityID string Valid bool Reason string ExpectedProfitETH *big.Int ExpectedProfitUSD float64 GasCostETH *big.Int GasCostUSD float64 NetProfitETH *big.Int NetProfitUSD float64 ProfitMargin float64 Slippage float64 GasPrice *big.Int Acceptable bool Recommendation string Confidence float64 RiskScore float64 ValidationTime time.Duration } // NewProfitValidator creates a new profit validator func NewProfitValidator(logger *logger.Logger) *ProfitValidator { return &ProfitValidator{ logger: logger, minProfitUSD: 5.0, // $5 minimum profit minProfitETH: big.NewInt(10000000000000000), // 0.01 ETH minimum profit minProfitMargin: 0.005, // 0.5% minimum margin maxSlippage: 0.01, // 1% maximum slippage maxGasPrice: big.NewInt(20000000000), // 20 gwei max gas price totalOpportunities: 0, profitableOps: 0, unprofitableOps: 0, totalProfitETH: big.NewInt(0), totalGasCostETH: big.NewInt(0), averageGasCost: big.NewInt(0), validationSuccessRate: 0.0, averageProfitMargin: 0.0, averageSlippage: 0.0, tokenPrices: make(map[common.Address]*TokenPrice), strictValidation: true, // Default to strict validation } } // ValidateProfit validates the profitability of an MEV opportunity func (pv *ProfitValidator) ValidateProfit(opportunityID string, expectedProfitETH *big.Int, gasCostETH *big.Int, slippage float64, gasPrice *big.Int) *ProfitValidationResult { startTime := time.Now() pv.mu.Lock() pv.totalOpportunities++ pv.mu.Unlock() result := &ProfitValidationResult{ OpportunityID: opportunityID, Valid: false, Reason: "", ExpectedProfitETH: expectedProfitETH, ExpectedProfitUSD: 0.0, GasCostETH: gasCostETH, GasCostUSD: 0.0, NetProfitETH: big.NewInt(0), NetProfitUSD: 0.0, ProfitMargin: 0.0, Slippage: slippage, GasPrice: gasPrice, Acceptable: false, Recommendation: "", Confidence: 0.0, RiskScore: 0.0, ValidationTime: 0, } // Calculate USD values using token prices expectedProfitUSD := pv.convertETHToUSD(expectedProfitETH) gasCostUSD := pv.convertETHToUSD(gasCostETH) result.ExpectedProfitUSD = expectedProfitUSD result.GasCostUSD = gasCostUSD // Calculate net profit netProfitETH := new(big.Int).Sub(expectedProfitETH, gasCostETH) if netProfitETH.Sign() < 0 { netProfitETH = big.NewInt(0) } result.NetProfitETH = netProfitETH result.NetProfitUSD = pv.convertETHToUSD(netProfitETH) // Calculate profit margin if expectedProfitETH.Sign() > 0 { margin := new(big.Float).Quo(new(big.Float).SetInt(netProfitETH), new(big.Float).SetInt(expectedProfitETH)) marginFloat, _ := margin.Float64() result.ProfitMargin = marginFloat * 100 // Convert to percentage } // Perform validation checks valid, reason := pv.performValidationChecks(expectedProfitETH, netProfitETH, gasCostETH, slippage, gasPrice) result.Valid = valid result.Reason = reason // Determine if opportunity is acceptable acceptable, recommendation := pv.isAcceptable(expectedProfitETH, netProfitETH, gasCostETH, slippage, gasPrice) result.Acceptable = acceptable result.Recommendation = recommendation // Calculate confidence based on validation results result.Confidence = pv.calculateConfidence(valid, acceptable, result.ProfitMargin, slippage) result.RiskScore = pv.calculateRiskScore(slippage, gasPrice) // Update statistics pv.mu.Lock() if valid && acceptable { pv.profitableOps++ pv.totalProfitETH.Add(pv.totalProfitETH, netProfitETH) pv.totalGasCostETH.Add(pv.totalGasCostETH, gasCostETH) } else { pv.unprofitableOps++ } pv.updateValidationMetrics() pv.mu.Unlock() result.ValidationTime = time.Since(startTime) // Log validation result if result.Valid && result.Acceptable { pv.logger.Info(fmt.Sprintf("✅ Profit validation PASSED for %s: Net profit %s ETH ($%.2f), Margin %.2f%%", opportunityID, formatEther(result.NetProfitETH), result.NetProfitUSD, result.ProfitMargin)) } else { pv.logger.Debug(fmt.Sprintf("❌ Profit validation FAILED for %s: %s", opportunityID, result.Reason)) } return result } // performValidationChecks performs all validation checks func (pv *ProfitValidator) performValidationChecks(expectedProfitETH, netProfitETH, gasCostETH *big.Int, slippage float64, gasPrice *big.Int) (bool, string) { // Check minimum profit in ETH if expectedProfitETH.Cmp(pv.minProfitETH) < 0 { return false, fmt.Sprintf("Expected profit %s ETH below minimum %s ETH", formatEther(expectedProfitETH), formatEther(pv.minProfitETH)) } // Check minimum profit in USD expectedProfitUSD := pv.convertETHToUSD(expectedProfitETH) if expectedProfitUSD < pv.minProfitUSD { return false, fmt.Sprintf("Expected profit $%.2f below minimum $%.2f", expectedProfitUSD, pv.minProfitUSD) } // Check net profit is positive if netProfitETH.Sign() <= 0 { return false, fmt.Sprintf("Net profit %s ETH is not positive", formatEther(netProfitETH)) } // Check slippage tolerance if slippage > pv.maxSlippage { return false, fmt.Sprintf("Slippage %.2f%% exceeds maximum %.2f%%", slippage*100, pv.maxSlippage*100) } // Check gas price limits if gasPrice.Cmp(pv.maxGasPrice) > 0 { return false, fmt.Sprintf("Gas price %s gwei exceeds maximum %s gwei", formatGwei(gasPrice), formatGwei(pv.maxGasPrice)) } // Check gas cost vs profit ratio if gasCostETH.Sign() > 0 && expectedProfitETH.Sign() > 0 { gasRatio := new(big.Float).Quo(new(big.Float).SetInt(gasCostETH), new(big.Float).SetInt(expectedProfitETH)) gasRatioFloat, _ := gasRatio.Float64() if gasRatioFloat > 0.5 { // Gas cost > 50% of expected profit return false, fmt.Sprintf("Gas cost %s ETH (%.1f%%) too high relative to profit %s ETH", formatEther(gasCostETH), gasRatioFloat*100, formatEther(expectedProfitETH)) } } return true, "All validation checks passed" } // isAcceptable determines if an opportunity is acceptable func (pv *ProfitValidator) isAcceptable(expectedProfitETH, netProfitETH, gasCostETH *big.Int, slippage float64, gasPrice *big.Int) (bool, string) { // Calculate profit margin var profitMargin float64 if expectedProfitETH.Sign() > 0 { margin := new(big.Float).Quo(new(big.Float).SetInt(netProfitETH), new(big.Float).SetInt(expectedProfitETH)) marginFloat, _ := margin.Float64() profitMargin = marginFloat * 100 // Convert to percentage } // Check minimum profit margin if profitMargin < pv.minProfitMargin*100 { return false, fmt.Sprintf("Profit margin %.2f%% below minimum %.2f%%", profitMargin, pv.minProfitMargin*100) } // For strict validation, apply additional checks if pv.strictValidation { // Check if net profit is at least 2x gas cost doubleGasCost := new(big.Int).Mul(gasCostETH, big.NewInt(2)) if netProfitETH.Cmp(doubleGasCost) < 0 { return false, fmt.Sprintf("Net profit %s ETH not at least 2x gas cost %s ETH", formatEther(netProfitETH), formatEther(gasCostETH)) } // Check if profit margin is at least 2x minimum if profitMargin < pv.minProfitMargin*200 { return false, fmt.Sprintf("Profit margin %.2f%% not at least 2x minimum %.2f%%", profitMargin, pv.minProfitMargin*200) } } return true, "Opportunity is acceptable" } // calculateConfidence calculates confidence in the validation result func (pv *ProfitValidator) calculateConfidence(valid, acceptable bool, profitMargin, slippage float64) float64 { if !valid || !acceptable { return 0.1 // Low confidence for invalid opportunities } // Start with base confidence confidence := 0.5 // Increase confidence based on profit margin if profitMargin > pv.minProfitMargin*300 { // 3x minimum margin confidence += 0.3 } else if profitMargin > pv.minProfitMargin*200 { // 2x minimum margin confidence += 0.2 } else if profitMargin > pv.minProfitMargin*150 { // 1.5x minimum margin confidence += 0.1 } // Increase confidence for low slippage if slippage < pv.maxSlippage*0.3 { // 30% of max slippage confidence += 0.1 } else if slippage < pv.maxSlippage*0.5 { // 50% of max slippage confidence += 0.05 } // Cap at maximum confidence if confidence > 0.95 { confidence = 0.95 } return confidence } // calculateRiskScore calculates a risk score for the opportunity func (pv *ProfitValidator) calculateRiskScore(slippage float64, gasPrice *big.Int) float64 { // Base risk (0-0.2) baseRisk := 0.1 // Slippage risk (0-0.3) slippageRisk := slippage / pv.maxSlippage * 0.3 if slippageRisk > 0.3 { slippageRisk = 0.3 } // Gas price risk (0-0.3) gasRisk := 0.0 if gasPrice != nil && pv.maxGasPrice != nil && pv.maxGasPrice.Sign() > 0 { gasRatio := new(big.Float).Quo(new(big.Float).SetInt(gasPrice), new(big.Float).SetInt(pv.maxGasPrice)) gasRatioFloat, _ := gasRatio.Float64() gasRisk = gasRatioFloat * 0.3 if gasRisk > 0.3 { gasRisk = 0.3 } } // Calculate total risk score totalRisk := baseRisk + slippageRisk + gasRisk if totalRisk > 1.0 { totalRisk = 1.0 } return totalRisk } // updateValidationMetrics updates validation metrics func (pv *ProfitValidator) updateValidationMetrics() { if pv.totalOpportunities > 0 { pv.validationSuccessRate = float64(pv.profitableOps) / float64(pv.totalOpportunities) } // Update average metrics (simplified) if pv.profitableOps > 0 { avgProfitMargin := new(big.Float).Quo(new(big.Float).SetInt(pv.totalProfitETH), big.NewFloat(float64(pv.profitableOps))) avgMarginFloat, _ := avgProfitMargin.Float64() pv.averageProfitMargin = avgMarginFloat * 100 // Convert to percentage } } // convertETHToUSD converts ETH amount to USD using current token prices func (pv *ProfitValidator) convertETHToUSD(ethAmount *big.Int) float64 { if ethAmount == nil { return 0.0 } // Get current ETH price in USD ethPrice := pv.getTokenPriceUSD(common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")) // WETH if ethPrice == 0.0 { ethPrice = 2000.0 // Default to $2000 ETH } // Convert ETH to USD ethFloat := new(big.Float).SetInt(ethAmount) ethDivisor := new(big.Float).SetFloat64(1e18) // Convert wei to ETH ethETH := new(big.Float).Quo(ethFloat, ethDivisor) usdFloat := new(big.Float).Mul(ethETH, big.NewFloat(ethPrice)) usdValue, _ := usdFloat.Float64() return usdValue } // getTokenPriceUSD gets the USD price of a token func (pv *ProfitValidator) getTokenPriceUSD(tokenAddr common.Address) float64 { pv.priceMu.RLock() defer pv.priceMu.RUnlock() if price, exists := pv.tokenPrices[tokenAddr]; exists { // Check if price is recent (within 5 minutes) if time.Since(price.LastUpdated) < 5*time.Minute { return price.PriceUSD } } // Return known prices for common tokens knownPrices := map[common.Address]float64{ common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1"): 2000.0, // WETH common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831"): 1.0, // USDC common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"): 1.0, // USDC.e common.HexToAddress("0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"): 1.0, // USDT common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"): 43000.0, // WBTC common.HexToAddress("0x912ce59144191c1204e64559fe8253a0e49e6548"): 0.75, // ARB common.HexToAddress("0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a"): 45.0, // GMX common.HexToAddress("0xf97f4df75117a78c1a5a0dbb814af92458539fb4"): 12.0, // LINK common.HexToAddress("0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0"): 8.0, // UNI common.HexToAddress("0xba5ddd1f9d7f570dc94a51479a000e3bce967196"): 85.0, // AAVE } if price, exists := knownPrices[tokenAddr]; exists { return price } // Default to $0 for unknown tokens return 0.0 } // UpdateTokenPrice updates the price of a token func (pv *ProfitValidator) UpdateTokenPrice(tokenAddr common.Address, priceUSD float64, confidence float64) { pv.priceMu.Lock() defer pv.priceMu.Unlock() pv.tokenPrices[tokenAddr] = &TokenPrice{ Address: tokenAddr, PriceUSD: priceUSD, LastUpdated: time.Now(), Confidence: confidence, Volume24h: 0.0, // Would be populated from real data Volatility: 0.0, // Would be populated from real data } } // GetStatistics returns validation statistics func (pv *ProfitValidator) GetStatistics() map[string]interface{} { pv.mu.RLock() defer pv.mu.RUnlock() return map[string]interface{}{ "total_opportunities": pv.totalOpportunities, "profitable_opportunities": pv.profitableOps, "unprofitable_opportunities": pv.unprofitableOps, "validation_success_rate": pv.validationSuccessRate * 100, // Convert to percentage "average_profit_margin": pv.averageProfitMargin, "average_slippage": pv.averageSlippage * 100, // Convert to percentage "total_profit_eth": formatEther(pv.totalProfitETH), "total_gas_cost_eth": formatEther(pv.totalGasCostETH), "tracked_tokens": len(pv.tokenPrices), "strict_validation": pv.strictValidation, } } // SetStrictValidation enables or disables strict validation func (pv *ProfitValidator) SetStrictValidation(strict bool) { pv.mu.Lock() defer pv.mu.Unlock() pv.strictValidation = strict } // SetMinProfitUSD sets the minimum profit threshold in USD func (pv *ProfitValidator) SetMinProfitUSD(minProfit float64) { pv.mu.Lock() defer pv.mu.Unlock() pv.minProfitUSD = minProfit } // SetMinProfitETH sets the minimum profit threshold in ETH func (pv *ProfitValidator) SetMinProfitETH(minProfit *big.Int) { pv.mu.Lock() defer pv.mu.Unlock() pv.minProfitETH = minProfit } // SetMinProfitMargin sets the minimum profit margin percentage func (pv *ProfitValidator) SetMinProfitMargin(minMargin float64) { pv.mu.Lock() defer pv.mu.Unlock() pv.minProfitMargin = minMargin } // SetMaxSlippage sets the maximum acceptable slippage func (pv *ProfitValidator) SetMaxSlippage(maxSlippage float64) { pv.mu.Lock() defer pv.mu.Unlock() pv.maxSlippage = maxSlippage } // SetMaxGasPrice sets the maximum gas price willing to pay func (pv *ProfitValidator) SetMaxGasPrice(maxGasPrice *big.Int) { pv.mu.Lock() defer pv.mu.Unlock() pv.maxGasPrice = maxGasPrice }