Files
mev-beta/pkg/mev/competition.go
Krypto Kajun 850223a953 fix(multicall): resolve critical multicall parsing corruption issues
- Added comprehensive bounds checking to prevent buffer overruns in multicall parsing
- Implemented graduated validation system (Strict/Moderate/Permissive) to reduce false positives
- Added LRU caching system for address validation with 10-minute TTL
- Enhanced ABI decoder with missing Universal Router and Arbitrum-specific DEX signatures
- Fixed duplicate function declarations and import conflicts across multiple files
- Added error recovery mechanisms with multiple fallback strategies
- Updated tests to handle new validation behavior for suspicious addresses
- Fixed parser test expectations for improved validation system
- Applied gofmt formatting fixes to ensure code style compliance
- Fixed mutex copying issues in monitoring package by introducing MetricsSnapshot
- Resolved critical security vulnerabilities in heuristic address extraction
- Progress: Updated TODO audit from 10% to 35% complete

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 00:12:55 -05:00

311 lines
11 KiB
Go

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
}