package mev import ( "context" "math" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/fraktal/mev-beta/internal/logger" mathpkg "github.com/fraktal/mev-beta/pkg/math" "github.com/fraktal/mev-beta/pkg/security" ) // MEVOpportunity represents a potential MEV opportunity type MEVOpportunity struct { TxHash string Block uint64 OpportunityType string EstimatedProfit *big.Int RequiredGas uint64 Competition int // Number of competing bots Confidence float64 } // BiddingStrategy represents the optimal bidding strategy type BiddingStrategy struct { PriorityFee *big.Int BaseFee *big.Int GasLimit uint64 SuccessProbability float64 TotalCost *big.Int NetProfit *big.Int } // CompetitionAnalysis represents the result of competition analysis type CompetitionAnalysis struct { CompetitorCount int AverageGasPrice *big.Int CompetitionLevel string // "low", "medium", "high" ThreatAssessment float64 // 0-1 scale RecommendedStrategy *BiddingStrategy AnalysisTime int64 } // CompetitionAnalyzer analyzes MEV competition type CompetitionAnalyzer struct { client *ethclient.Client logger *logger.Logger decimalConverter *mathpkg.DecimalConverter competitorData map[common.Address]*CompetitorProfile gasHistory []GasDataPoint profitThresholds map[string]*mathpkg.UniversalDecimal config CompetitionConfig } // CompetitorProfile represents a competing MEV bot type CompetitorProfile struct { Address common.Address TransactionCount int AverageGasPrice *big.Int SuccessRate float64 FavoredProtocols []string CompetitionScore float64 LastSeenBlock uint64 } // GasDataPoint represents a historical gas price data point type GasDataPoint struct { GasPrice *big.Int Timestamp int64 BlockNumber uint64 Profit *big.Int } // CompetitionConfig holds configuration for competition analysis type CompetitionConfig struct { BaseGasMultiplier float64 MaxGasMultiplier float64 GasHistorySize int CompetitorThreshold *mathpkg.UniversalDecimal MaxOpportunitySize *mathpkg.UniversalDecimal } // NewCompetitionAnalyzer creates a new competition analyzer func NewCompetitionAnalyzer(client *ethclient.Client, logger *logger.Logger) *CompetitionAnalyzer { analyzer := &CompetitionAnalyzer{ client: client, logger: logger, decimalConverter: mathpkg.NewDecimalConverter(), competitorData: make(map[common.Address]*CompetitorProfile), gasHistory: make([]GasDataPoint, 0), profitThresholds: make(map[string]*mathpkg.UniversalDecimal), } analyzer.config = CompetitionConfig{ BaseGasMultiplier: 1.2, // 20% premium MaxGasMultiplier: 3.0, // 200% max premium GasHistorySize: 100, // Track last 100 data points } // Set competitor threshold (0.005 ETH) analyzer.config.CompetitorThreshold, _ = analyzer.decimalConverter.FromString("0.005", 18, "ETH") // Set profit thresholds for different opportunity sizes analyzer.profitThresholds["small"], _ = analyzer.decimalConverter.FromString("0.01", 18, "ETH") // $10 analyzer.profitThresholds["medium"], _ = analyzer.decimalConverter.FromString("0.05", 18, "ETH") // $50 analyzer.profitThresholds["large"], _ = analyzer.decimalConverter.FromString("0.25", 18, "ETH") // $250 return analyzer } // AnalyzeCompetition analyzes the competitive landscape for an opportunity func (analyzer *CompetitionAnalyzer) AnalyzeCompetition(ctx context.Context, opportunity *MEVOpportunity) (*CompetitionData, error) { analyzer.logger.Info("Analyzing competition for MEV opportunity", "type", opportunity.OpportunityType, "estimated_profit", opportunity.EstimatedProfit.String()) // Get current base fee head, err := analyzer.client.HeaderByNumber(ctx, nil) if err != nil { return nil, err } baseFee := head.BaseFee // Calculate basic gas price estimate estimatedGasPrice := new(big.Int).Mul(baseFee, big.NewInt(12)) // 20% above base fee estimatedGasPrice.Div(estimatedGasPrice, big.NewInt(10)) // Adjust based on competition level competitionFactor := analyzer.calculateCompetitionFactor(opportunity) adjustedGasPrice := new(big.Int).Mul(estimatedGasPrice, big.NewInt(int64(competitionFactor*100))) adjustedGasPrice.Div(adjustedGasPrice, big.NewInt(100)) // Ensure minimum gas price minGasPrice := big.NewInt(2000000000) // 2 gwei minimum if adjustedGasPrice.Cmp(minGasPrice) < 0 { adjustedGasPrice = minGasPrice } // Calculate success probability based on competition successProbability := analyzer.estimateSuccessProbability(opportunity, competitionFactor) // Calculate net profit after gas costs // Calculate net profit after gas costs requiredGasInt64, err := security.SafeUint64ToInt64(opportunity.RequiredGas) if err != nil { analyzer.logger.Error("Required gas exceeds int64 maximum", "requiredGas", opportunity.RequiredGas, "error", err) // Use maximum safe value as fallback requiredGasInt64 = math.MaxInt64 } gasCost := new(big.Int).Mul(big.NewInt(requiredGasInt64), adjustedGasPrice) netProfit := new(big.Int).Sub(opportunity.EstimatedProfit, gasCost) // Create competition data competitionData := &CompetitionData{ OpportunityType: opportunity.OpportunityType, EstimatedProfit: opportunity.EstimatedProfit, NetProfit: netProfit, BaseFee: baseFee, SuggestedGasPrice: adjustedGasPrice, RequiredGas: opportunity.RequiredGas, CompetitionLevel: competitionFactor, SuccessProbability: successProbability, TotalCost: gasCost, Threshold: analyzer.config.CompetitorThreshold, } return competitionData, nil } // CalculateOptimalBid calculates the optimal bidding strategy func (analyzer *CompetitionAnalyzer) CalculateOptimalBid(ctx context.Context, opportunity *MEVOpportunity, competition *CompetitionData) (*BiddingStrategy, error) { analyzer.logger.Info("Calculating optimal bid for opportunity", "type", opportunity.OpportunityType, "estimated_profit", opportunity.EstimatedProfit.String()) // Start with the suggested gas price from competition analysis baseGasPrice := competition.SuggestedGasPrice // Calculate base strategy with safe gas conversion requiredGasInt64, err := security.SafeUint64ToInt64(opportunity.RequiredGas) if err != nil { analyzer.logger.Error("Required gas exceeds int64 maximum", "requiredGas", opportunity.RequiredGas, "error", err) // Use maximum safe value as fallback requiredGasInt64 = math.MaxInt64 } gasCostForBaseStrategy := new(big.Int).Mul(baseGasPrice, big.NewInt(requiredGasInt64)) strategy := &BiddingStrategy{ PriorityFee: baseGasPrice, BaseFee: competition.BaseFee, GasLimit: opportunity.RequiredGas, SuccessProbability: competition.SuccessProbability, TotalCost: gasCostForBaseStrategy, NetProfit: competition.NetProfit, } // Adjust for profitability - if opportunity is very profitable, we can afford higher gas profitRatio := new(big.Float).Quo( new(big.Float).SetInt(opportunity.EstimatedProfit), new(big.Float).SetInt(strategy.TotalCost), ) profitRatioFloat, _ := profitRatio.Float64() // If profit ratio is high, we can bid more aggressively if profitRatioFloat > 10 { // If profit is >10x gas cost // Increase bid by up to 50% for high-value opportunities aggressiveMultiplier := 1.5 increasedPriority := new(big.Int).Mul(strategy.PriorityFee, big.NewInt(int64(aggressiveMultiplier*100))) increasedPriority.Div(increasedPriority, big.NewInt(100)) // Cap at max multiplier maxGasPrice := new(big.Int).Mul(competition.BaseFee, big.NewInt(int64(analyzer.config.MaxGasMultiplier*100))) maxGasPrice.Div(maxGasPrice, big.NewInt(100)) if increasedPriority.Cmp(maxGasPrice) > 0 { increasedPriority = maxGasPrice } // Calculate new total cost with increased priority fee using safe conversion requiredGasInt64_safe, err := security.SafeUint64ToInt64(opportunity.RequiredGas) if err != nil { analyzer.logger.Error("Required gas exceeds int64 maximum for TotalCost in strategy", "requiredGas", opportunity.RequiredGas, "error", err) // Use maximum safe value as fallback requiredGasInt64_safe = math.MaxInt64 } strategy.TotalCost = new(big.Int).Mul(increasedPriority, big.NewInt(requiredGasInt64_safe)) strategy.PriorityFee = increasedPriority } // Update net profit strategy.NetProfit = new(big.Int).Sub(opportunity.EstimatedProfit, strategy.TotalCost) // Ensure net profit is positive if strategy.NetProfit.Sign() <= 0 { analyzer.logger.Warn("Opportunity not profitable after optimal bidding", "estimated_profit", opportunity.EstimatedProfit.String(), "total_cost", strategy.TotalCost.String()) return nil, nil } return strategy, nil } // calculateCompetitionFactor estimates competition level for an opportunity func (analyzer *CompetitionAnalyzer) calculateCompetitionFactor(opportunity *MEVOpportunity) float64 { // Base competition factor competitionFactor := 1.0 // Higher competition for more profitable opportunities if opportunity.EstimatedProfit.Cmp(big.NewInt(100000000000000000)) > 0 { // >0.1 ETH competitionFactor += 0.3 } if opportunity.EstimatedProfit.Cmp(big.NewInt(500000000000000000)) > 0 { // >0.5 ETH competitionFactor += 0.4 } // Factor in known competitors competitionFactor += float64(opportunity.Competition) * 0.1 // Cap at reasonable maximum if competitionFactor > analyzer.config.MaxGasMultiplier { competitionFactor = analyzer.config.MaxGasMultiplier } return competitionFactor } // estimateSuccessProbability estimates the success probability func (analyzer *CompetitionAnalyzer) estimateSuccessProbability(opportunity *MEVOpportunity, competitionFactor float64) float64 { // Base success probability is inversely related to competition baseSuccessRate := 1.0 / competitionFactor // Adjust based on opportunity characteristics if opportunity.OpportunityType == "sandwich" { // Sandwich attacks are harder to execute successfully baseSuccessRate *= 0.7 } else if opportunity.OpportunityType == "arbitrage" { // Arbitrage has higher success rate baseSuccessRate *= 0.9 } // Ensure it's between 0 and 1 if baseSuccessRate > 1.0 { baseSuccessRate = 1.0 } if baseSuccessRate < 0.05 { baseSuccessRate = 0.05 // Minimum 5% chance } return baseSuccessRate } // CompetitionData holds the results of competition analysis type CompetitionData struct { OpportunityType string EstimatedProfit *big.Int NetProfit *big.Int BaseFee *big.Int SuggestedGasPrice *big.Int RequiredGas uint64 CompetitionLevel float64 SuccessProbability float64 TotalCost *big.Int Threshold *mathpkg.UniversalDecimal }