package arbitrum import ( "context" "fmt" "math/big" "sort" "time" "github.com/ethereum/go-ethereum/common" "github.com/fraktal/mev-beta/internal/logger" "github.com/fraktal/mev-beta/pkg/types" ) // MEVStrategyEngine implements profitable MEV strategies type MEVStrategyEngine struct { logger *logger.Logger protocolRegistry *ArbitrumProtocolRegistry profitCalculator *RealTimeProfitCalculator riskManager *RiskManager // Strategy configuration minProfitUSD float64 // Minimum profit in USD maxGasPrice *big.Int // Maximum gas price willing to pay maxSlippage float64 // Maximum slippage tolerance // Performance tracking successfulTrades uint64 totalProfit *big.Int totalGasCost *big.Int } // RealTimeProfitCalculator provides real-time profit calculations type RealTimeProfitCalculator struct { // Token prices (token address -> price in USD) tokenPrices map[common.Address]*TokenPrice // Gas pricing currentGasPrice *big.Int // Exchange rates and fees exchangeFees map[string]float64 // protocol -> fee percentage // Liquidity data poolLiquidity map[common.Address]*PoolLiquidity } // RiskManager manages risk parameters for MEV strategies type RiskManager struct { maxPositionSize *big.Int // Maximum position size in wei maxDailyLoss *big.Int // Maximum daily loss maxConcurrentTxs int // Maximum concurrent transactions // Current risk metrics dailyLoss *big.Int activeTxs int lastResetTime time.Time } // 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 } // PoolLiquidity represents pool liquidity information type PoolLiquidity struct { Pool common.Address Token0 common.Address Token1 common.Address Reserve0 *big.Int Reserve1 *big.Int TotalLiquidity *big.Int Fee float64 LastUpdated time.Time } // ProfitableStrategy represents a profitable MEV strategy type ProfitableStrategy struct { Type string // "arbitrage", "sandwich", "liquidation" Priority int // Higher = more urgent ExpectedProfit *big.Int // Expected profit in wei GasCost *big.Int // Estimated gas cost NetProfit *big.Int // Net profit after gas ProfitMarginPct float64 // Profit margin percentage RiskScore float64 // Risk score 0-1 (higher = riskier) Confidence float64 // Confidence in profit estimate 0-1 ExecutionTime time.Duration // Estimated execution time Parameters map[string]interface{} // Strategy-specific parameters } // ArbitrageParams holds arbitrage-specific parameters type ArbitrageParams struct { TokenIn common.Address `json:"token_in"` TokenOut common.Address `json:"token_out"` AmountIn *big.Int `json:"amount_in"` Path []common.Address `json:"path"` Exchanges []string `json:"exchanges"` PriceDiff float64 `json:"price_diff"` Slippage float64 `json:"slippage"` } // SandwichParams holds sandwich attack parameters type SandwichParams struct { TargetTx string `json:"target_tx"` TokenIn common.Address `json:"token_in"` TokenOut common.Address `json:"token_out"` FrontrunAmount *big.Int `json:"frontrun_amount"` BackrunAmount *big.Int `json:"backrun_amount"` Pool common.Address `json:"pool"` MaxSlippage float64 `json:"max_slippage"` } // LiquidationParams holds liquidation-specific parameters type LiquidationParams struct { Protocol string `json:"protocol"` Borrower common.Address `json:"borrower"` CollateralToken common.Address `json:"collateral_token"` DebtToken common.Address `json:"debt_token"` MaxLiquidation *big.Int `json:"max_liquidation"` HealthFactor float64 `json:"health_factor"` LiquidationBonus float64 `json:"liquidation_bonus"` } // NewMEVStrategyEngine creates a new MEV strategy engine func NewMEVStrategyEngine(logger *logger.Logger, protocolRegistry *ArbitrumProtocolRegistry) *MEVStrategyEngine { return &MEVStrategyEngine{ logger: logger, protocolRegistry: protocolRegistry, minProfitUSD: 50.0, // $50 minimum profit maxGasPrice: big.NewInt(20000000000), // 20 gwei max maxSlippage: 0.005, // 0.5% max slippage totalProfit: big.NewInt(0), totalGasCost: big.NewInt(0), profitCalculator: NewRealTimeProfitCalculator(), riskManager: NewRiskManager(), } } // NewRealTimeProfitCalculator creates a new profit calculator func NewRealTimeProfitCalculator() *RealTimeProfitCalculator { return &RealTimeProfitCalculator{ tokenPrices: make(map[common.Address]*TokenPrice), exchangeFees: make(map[string]float64), poolLiquidity: make(map[common.Address]*PoolLiquidity), currentGasPrice: big.NewInt(5000000000), // 5 gwei default } } // NewRiskManager creates a new risk manager func NewRiskManager() *RiskManager { return &RiskManager{ maxPositionSize: big.NewInt(1000000000000000000), // 1 ETH max position maxDailyLoss: big.NewInt(100000000000000000), // 0.1 ETH max daily loss maxConcurrentTxs: 5, dailyLoss: big.NewInt(0), activeTxs: 0, lastResetTime: time.Now(), } } // AnalyzeArbitrageOpportunity analyzes potential arbitrage opportunities func (engine *MEVStrategyEngine) AnalyzeArbitrageOpportunity(ctx context.Context, swapEvent interface{}) (interface{}, error) { // Type assert the swapEvent to *SwapEvent swap, ok := swapEvent.(*SwapEvent) if !ok { return nil, fmt.Errorf("invalid swap event type") } // Parse token addresses tokenIn := common.HexToAddress(swap.TokenIn) tokenOut := common.HexToAddress(swap.TokenOut) // Get current prices across exchanges prices, err := engine.profitCalculator.GetCrossExchangePrices(ctx, tokenIn, tokenOut) if err != nil { return nil, fmt.Errorf("failed to get cross-exchange prices: %w", err) } // Find best arbitrage path bestArb := engine.findBestArbitragePath(prices, tokenIn, tokenOut) if bestArb == nil { return nil, nil // No profitable arbitrage } // Calculate gas costs gasCost := engine.calculateArbitrageGasCost(bestArb) // Calculate net profit netProfit := new(big.Int).Sub(bestArb.Profit, gasCost) // Convert to USD for minimum profit check profitUSD := engine.profitCalculator.WeiToUSD(netProfit, common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")) // WETH if profitUSD < engine.minProfitUSD { return nil, nil // Below minimum profit threshold } // Check risk parameters riskScore := engine.riskManager.CalculateRiskScore(bestArb) if riskScore > 0.7 { // Risk too high return nil, nil } return &ProfitableStrategy{ Type: "arbitrage", Priority: engine.calculatePriority(profitUSD, riskScore), ExpectedProfit: bestArb.Profit, GasCost: gasCost, NetProfit: netProfit, ProfitMarginPct: (float64(netProfit.Uint64()) / float64(bestArb.AmountIn.Uint64())) * 100, RiskScore: riskScore, Confidence: bestArb.Confidence, ExecutionTime: time.Duration(15) * time.Second, // Estimated execution time Parameters: map[string]interface{}{ "arbitrage": &ArbitrageParams{ TokenIn: tokenIn, TokenOut: tokenOut, AmountIn: bestArb.AmountIn, Path: []common.Address{bestArb.TokenIn, bestArb.TokenOut}, Exchanges: bestArb.Pools, // Use pools as exchanges PriceDiff: engine.calculatePriceDifference(prices), Slippage: engine.maxSlippage, }, }, }, nil } // AnalyzeSandwichOpportunity analyzes potential sandwich attack opportunities func (engine *MEVStrategyEngine) AnalyzeSandwichOpportunity(ctx context.Context, targetTx *SwapEvent) (*ProfitableStrategy, error) { // Only analyze large transactions that can be sandwiched profitably amountIn, ok := new(big.Int).SetString(targetTx.AmountIn, 10) if !ok || amountIn.Cmp(big.NewInt(100000000000000000)) < 0 { // < 0.1 ETH return nil, nil } // Check if transaction has sufficient slippage tolerance if targetTx.PriceImpact < 0.01 { // < 1% price impact return nil, nil } tokenIn := common.HexToAddress(targetTx.TokenIn) tokenOut := common.HexToAddress(targetTx.TokenOut) // Calculate optimal sandwich amounts frontrunAmount, backrunAmount := engine.calculateOptimalSandwichAmounts(amountIn, targetTx.PriceImpact) // Estimate profit from price manipulation expectedProfit := engine.calculateSandwichProfit(frontrunAmount, backrunAmount, targetTx.PriceImpact) // Calculate gas costs (frontrun + backrun + priority fees) gasCost := engine.calculateSandwichGasCost() // Calculate net profit netProfit := new(big.Int).Sub(expectedProfit, gasCost) // Convert to USD profitUSD := engine.profitCalculator.WeiToUSD(netProfit, tokenOut) if profitUSD < engine.minProfitUSD { return nil, nil } // Calculate risk (sandwich attacks are inherently risky) riskScore := 0.6 + (targetTx.PriceImpact * 0.3) // Base risk + impact risk return &ProfitableStrategy{ Type: "sandwich", Priority: engine.calculatePriority(profitUSD, riskScore), ExpectedProfit: expectedProfit, GasCost: gasCost, NetProfit: netProfit, ProfitMarginPct: (float64(netProfit.Uint64()) / float64(amountIn.Uint64())) * 100, RiskScore: riskScore, Confidence: 0.7, // Moderate confidence due to MEV competition ExecutionTime: time.Duration(3) * time.Second, // Fast execution required Parameters: map[string]interface{}{ "sandwich": &SandwichParams{ TargetTx: targetTx.TxHash, TokenIn: tokenIn, TokenOut: tokenOut, FrontrunAmount: frontrunAmount, BackrunAmount: backrunAmount, Pool: common.HexToAddress(targetTx.Pool), MaxSlippage: engine.maxSlippage, }, }, }, nil } // AnalyzeLiquidationOpportunity analyzes potential liquidation opportunities func (engine *MEVStrategyEngine) AnalyzeLiquidationOpportunity(ctx context.Context, liquidationEvent *LiquidationEvent) (*ProfitableStrategy, error) { // Only analyze under-collateralized positions if liquidationEvent.HealthFactor >= 1.0 { return nil, nil } // Calculate liquidation profitability collateralAmount, ok := new(big.Int).SetString(liquidationEvent.CollateralAmount, 10) if !ok { return nil, fmt.Errorf("invalid collateral amount") } debtAmount, ok := new(big.Int).SetString(liquidationEvent.DebtAmount, 10) if !ok { return nil, fmt.Errorf("invalid debt amount") } // Calculate liquidation bonus (usually 5-15%) bonusAmount, ok := new(big.Int).SetString(liquidationEvent.Bonus, 10) if !ok { bonusAmount = new(big.Int).Div(collateralAmount, big.NewInt(20)) // 5% default } // Estimate gas costs for liquidation gasCost := engine.calculateLiquidationGasCost(liquidationEvent.Protocol) // Calculate net profit (bonus - gas costs) netProfit := new(big.Int).Sub(bonusAmount, gasCost) // Convert to USD collateralToken := common.HexToAddress(liquidationEvent.CollateralToken) profitUSD := engine.profitCalculator.WeiToUSD(netProfit, collateralToken) if profitUSD < engine.minProfitUSD { return nil, nil } // Calculate risk score (liquidations are generally lower risk) riskScore := 0.3 + (1.0-liquidationEvent.HealthFactor)*0.2 return &ProfitableStrategy{ Type: "liquidation", Priority: engine.calculatePriority(profitUSD, riskScore), ExpectedProfit: bonusAmount, GasCost: gasCost, NetProfit: netProfit, ProfitMarginPct: (float64(netProfit.Uint64()) / float64(debtAmount.Uint64())) * 100, RiskScore: riskScore, Confidence: 0.9, // High confidence for liquidations ExecutionTime: time.Duration(10) * time.Second, Parameters: map[string]interface{}{ "liquidation": &LiquidationParams{ Protocol: liquidationEvent.Protocol, Borrower: common.HexToAddress(liquidationEvent.Borrower), CollateralToken: collateralToken, DebtToken: common.HexToAddress(liquidationEvent.DebtToken), MaxLiquidation: collateralAmount, HealthFactor: liquidationEvent.HealthFactor, LiquidationBonus: float64(bonusAmount.Uint64()) / float64(collateralAmount.Uint64()), }, }, }, nil } // GetCrossExchangePrices gets prices across different exchanges func (calc *RealTimeProfitCalculator) GetCrossExchangePrices(ctx context.Context, tokenIn, tokenOut common.Address) (map[string]float64, error) { prices := make(map[string]float64) // Get prices from major exchanges exchanges := []string{"uniswap_v3", "uniswap_v2", "sushiswap", "camelot_v3", "balancer_v2"} for _, exchange := range exchanges { price, err := calc.getTokenPairPrice(ctx, exchange, tokenIn, tokenOut) if err != nil { continue // Skip if price unavailable } prices[exchange] = price } return prices, nil } // Helper methods for calculations func (engine *MEVStrategyEngine) findBestArbitragePath(prices map[string]float64, tokenIn, tokenOut common.Address) *types.ArbitrageOpportunity { if len(prices) < 2 { return nil } // Find highest and lowest prices var minPrice, maxPrice float64 var minExchange, maxExchange string first := true for exchange, price := range prices { if first { minPrice = price maxPrice = price minExchange = exchange maxExchange = exchange first = false continue } if price < minPrice { minPrice = price minExchange = exchange } if price > maxPrice { maxPrice = price maxExchange = exchange } } // Calculate potential profit priceDiff := (maxPrice - minPrice) / minPrice if priceDiff < 0.005 { // Minimum 0.5% price difference return nil } // Estimate amounts and profit amountIn := big.NewInt(100000000000000000) // 0.1 ETH test amount expectedProfit := new(big.Int).Mul(amountIn, big.NewInt(int64(priceDiff*1000))) expectedProfit = new(big.Int).Div(expectedProfit, big.NewInt(1000)) return &types.ArbitrageOpportunity{ Path: []string{tokenIn.Hex(), tokenOut.Hex()}, Pools: []string{minExchange + "-pool", maxExchange + "-pool"}, AmountIn: amountIn, Profit: expectedProfit, NetProfit: expectedProfit, // Simplified - gas will be calculated later GasEstimate: big.NewInt(200000), // Estimate ROI: 0.0, // Will be calculated when gas is known Protocol: "multi-exchange", ExecutionTime: 4000, // 4 seconds Confidence: 0.8, PriceImpact: 0.005, // 0.5% estimated MaxSlippage: 0.02, // 2% max slippage TokenIn: tokenIn, TokenOut: tokenOut, Timestamp: time.Now().Unix(), Risk: 0.3, // Medium risk for cross-exchange arbitrage } } func (engine *MEVStrategyEngine) calculateOptimalSandwichAmounts(targetAmount *big.Int, priceImpact float64) (*big.Int, *big.Int) { // Optimal frontrun is typically 10-30% of target transaction frontrunPct := 0.15 + (priceImpact * 0.15) // Scale with price impact frontrunAmount := new(big.Int).Mul(targetAmount, big.NewInt(int64(frontrunPct*100))) frontrunAmount = new(big.Int).Div(frontrunAmount, big.NewInt(100)) // Backrun amount should be similar to frontrun backrunAmount := new(big.Int).Set(frontrunAmount) return frontrunAmount, backrunAmount } func (engine *MEVStrategyEngine) calculateSandwichProfit(frontrunAmount, backrunAmount *big.Int, priceImpact float64) *big.Int { // Simplified calculation: profit = frontrun_amount * (price_impact - fees) profitPct := priceImpact - 0.006 // Subtract 0.6% for fees and slippage if profitPct <= 0 { return big.NewInt(0) } profit := new(big.Int).Mul(frontrunAmount, big.NewInt(int64(profitPct*1000))) profit = new(big.Int).Div(profit, big.NewInt(1000)) return profit } // Gas cost calculation methods func (engine *MEVStrategyEngine) calculateArbitrageGasCost(arb *types.ArbitrageOpportunity) *big.Int { // Estimate gas usage: swap + transfer operations gasUsage := big.NewInt(300000) // ~300k gas for complex arbitrage return new(big.Int).Mul(gasUsage, engine.maxGasPrice) } func (engine *MEVStrategyEngine) calculateSandwichGasCost() *big.Int { // Frontrun + backrun + priority fees gasUsage := big.NewInt(400000) // ~400k gas total priorityFee := big.NewInt(10000000000) // 10 gwei priority totalGasPrice := new(big.Int).Add(engine.maxGasPrice, priorityFee) return new(big.Int).Mul(gasUsage, totalGasPrice) } func (engine *MEVStrategyEngine) calculateLiquidationGasCost(protocol string) *big.Int { // Different protocols have different gas costs gasUsage := big.NewInt(200000) // ~200k gas for liquidation if protocol == "gmx" { gasUsage = big.NewInt(350000) // GMX is more expensive } return new(big.Int).Mul(gasUsage, engine.maxGasPrice) } // Utility methods func (engine *MEVStrategyEngine) calculatePriority(profitUSD, riskScore float64) int { // Higher profit and lower risk = higher priority priority := int((profitUSD / 10.0) * (1.0 - riskScore) * 100) if priority > 1000 { priority = 1000 // Cap at 1000 } return priority } func (engine *MEVStrategyEngine) calculatePriceDifference(prices map[string]float64) float64 { if len(prices) < 2 { return 0 } var min, max float64 first := true for _, price := range prices { if first { min = price max = price first = false continue } if price < min { min = price } if price > max { max = price } } return (max - min) / min } // WeiToUSD converts wei amount to USD using token price func (calc *RealTimeProfitCalculator) WeiToUSD(amount *big.Int, token common.Address) float64 { price, exists := calc.tokenPrices[token] if !exists { return 0 } // Convert wei to token units (assume 18 decimals) tokenAmount := new(big.Float).SetInt(amount) tokenAmount.Quo(tokenAmount, big.NewFloat(1e18)) tokenAmountFloat, _ := tokenAmount.Float64() return tokenAmountFloat * price.PriceUSD } func (calc *RealTimeProfitCalculator) getTokenPairPrice(ctx context.Context, exchange string, tokenIn, tokenOut common.Address) (float64, error) { // This would connect to actual DEX contracts or price oracles // For now, we'll return an error to indicate this needs implementation return 0, fmt.Errorf("getTokenPairPrice not implemented - needs connection to actual DEX contracts or price oracles") } // CalculateRiskScore calculates risk score for a strategy func (rm *RiskManager) CalculateRiskScore(arb *types.ArbitrageOpportunity) float64 { // Base risk factors baseRisk := 0.1 // Size risk - larger positions are riskier sizeRisk := float64(arb.AmountIn.Uint64()) / 1e18 * 0.1 // 0.1 per ETH // Confidence risk confidenceRisk := (1.0 - arb.Confidence) * 0.3 // Path complexity risk pathRisk := float64(len(arb.Path)-2) * 0.05 // Additional risk for each hop totalRisk := baseRisk + sizeRisk + confidenceRisk + pathRisk // Cap at 1.0 if totalRisk > 1.0 { totalRisk = 1.0 } return totalRisk } // GetTopStrategies returns the most profitable strategies sorted by priority func (engine *MEVStrategyEngine) GetTopStrategies(strategies []*ProfitableStrategy, limit int) []*ProfitableStrategy { // Sort by priority (highest first) sort.Slice(strategies, func(i, j int) bool { return strategies[i].Priority > strategies[j].Priority }) // Apply limit if len(strategies) > limit { strategies = strategies[:limit] } return strategies } // UpdatePerformanceMetrics updates strategy performance tracking func (engine *MEVStrategyEngine) UpdatePerformanceMetrics(strategy *ProfitableStrategy, actualProfit *big.Int, gasCost *big.Int) { if actualProfit.Sign() > 0 { engine.successfulTrades++ engine.totalProfit.Add(engine.totalProfit, actualProfit) } engine.totalGasCost.Add(engine.totalGasCost, gasCost) } // GetPerformanceStats returns performance statistics func (engine *MEVStrategyEngine) GetPerformanceStats() map[string]interface{} { netProfit := new(big.Int).Sub(engine.totalProfit, engine.totalGasCost) return map[string]interface{}{ "successful_trades": engine.successfulTrades, "total_profit_wei": engine.totalProfit.String(), "total_gas_cost": engine.totalGasCost.String(), "net_profit_wei": netProfit.String(), "profit_ratio": float64(engine.totalProfit.Uint64()) / float64(engine.totalGasCost.Uint64()), } }