package arbitrage import ( "context" "fmt" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/fraktal/mev-beta/internal/logger" "github.com/fraktal/mev-beta/pkg/arbitrum" "github.com/fraktal/mev-beta/pkg/exchanges" "github.com/fraktal/mev-beta/pkg/math" "github.com/fraktal/mev-beta/pkg/mev" "github.com/fraktal/mev-beta/pkg/security" pkgtypes "github.com/fraktal/mev-beta/pkg/types" ) // LiveExecutionFramework orchestrates the complete MEV bot pipeline type LiveExecutionFramework struct { // Core components client *ethclient.Client logger *logger.Logger keyManager *security.KeyManager gasEstimator *arbitrum.L2GasEstimator decimalConverter *math.DecimalConverter // Exchange and market components exchangeRegistry *exchanges.ExchangeRegistry pricingEngine *math.ExchangePricingEngine calculator *math.ArbitrageCalculator // Detection and execution detectionEngine *ArbitrageDetectionEngine flashExecutor *FlashSwapExecutor competitionAnalyzer *mev.CompetitionAnalyzer // Configuration config FrameworkConfig // State management isRunning bool runningMutex sync.RWMutex stopChan chan struct{} // Performance tracking stats *FrameworkStats statsMutex sync.RWMutex // Opportunity processing opportunityQueue chan *pkgtypes.ArbitrageOpportunity executionQueue chan *ExecutionTask workerPool *ExecutionWorkerPool } // FrameworkConfig configures the live execution framework type FrameworkConfig struct { // Detection settings DetectionConfig DetectionConfig // Execution settings ExecutionConfig ExecutionConfig // Risk management MaxConcurrentExecutions int DailyProfitTarget *math.UniversalDecimal DailyLossLimit *math.UniversalDecimal MaxPositionSize *math.UniversalDecimal // Performance settings WorkerPoolSize int OpportunityQueueSize int ExecutionQueueSize int // Emergency controls EmergencyStopEnabled bool CircuitBreakerEnabled bool MaxFailureRate float64 // Stop if failure rate exceeds this HealthCheckInterval time.Duration } // ExecutionTask is defined in executor.go to avoid duplication // LiveExecutionMetrics contains metrics from the live execution framework type LiveExecutionMetrics struct { OpportunitiesDetected int64 OpportunitiesExecuted int64 SuccessfulExecutions int64 FailedExecutions int64 TotalProfit *math.UniversalDecimal TotalGasCost *math.UniversalDecimal AverageExecutionTime time.Duration AverageGasUsed uint64 CurrentWorkers int QueueLength int HealthStatus string LastExecutionTime time.Time LastOpportunityTime time.Time } // TaskPriority represents execution priority type TaskPriority int const ( PriorityLow TaskPriority = 1 PriorityMedium TaskPriority = 2 PriorityHigh TaskPriority = 3 PriorityCritical TaskPriority = 4 ) // FrameworkStats tracks framework performance type FrameworkStats struct { StartTime time.Time TotalOpportunitiesDetected uint64 TotalOpportunitiesQueued uint64 TotalExecutionsAttempted uint64 TotalExecutionsSuccessful uint64 TotalProfitRealized *math.UniversalDecimal TotalGasCostPaid *math.UniversalDecimal AverageExecutionTime time.Duration CurrentSuccessRate float64 DailyStats map[string]*DailyStats } // DailyStats tracks daily performance type DailyStats struct { Date string OpportunitiesDetected uint64 ExecutionsAttempted uint64 ExecutionsSuccessful uint64 ProfitRealized *math.UniversalDecimal GasCostPaid *math.UniversalDecimal NetProfit *math.UniversalDecimal } // ExecutionWorkerPool manages concurrent execution workers type ExecutionWorkerPool struct { workers int taskChan chan *ExecutionTask wg sync.WaitGroup ctx context.Context cancel context.CancelFunc framework *LiveExecutionFramework } // NewLiveExecutionFramework creates a new live execution framework func NewLiveExecutionFramework( client *ethclient.Client, logger *logger.Logger, keyManager *security.KeyManager, gasEstimator *arbitrum.L2GasEstimator, flashSwapContract, arbitrageContract common.Address, config FrameworkConfig, ) (*LiveExecutionFramework, error) { // Initialize exchange registry exchangeRegistry := exchanges.NewExchangeRegistry(client, logger) // Initialize pricing engine pricingEngine := math.NewExchangePricingEngine() // Create gas estimator wrapper for calculator gasEstWrapper := &GasEstimatorWrapper{gasEstimator: gasEstimator} // Initialize arbitrage calculator calculator := math.NewArbitrageCalculator(gasEstWrapper) // Initialize competition analyzer competitionAnalyzer := mev.NewCompetitionAnalyzer(client, logger) // Initialize detection engine detectionEngine := NewArbitrageDetectionEngine( exchangeRegistry, gasEstWrapper, logger, config.DetectionConfig, ) // Initialize flash executor flashExecutor := NewFlashSwapExecutor( client, logger, keyManager, gasEstimator, flashSwapContract, arbitrageContract, config.ExecutionConfig, ) // Initialize statistics stats := &FrameworkStats{ StartTime: time.Now(), DailyStats: make(map[string]*DailyStats), } dc := math.NewDecimalConverter() stats.TotalProfitRealized, _ = dc.FromString("0", 18, "ETH") stats.TotalGasCostPaid, _ = dc.FromString("0", 18, "ETH") framework := &LiveExecutionFramework{ client: client, logger: logger, keyManager: keyManager, gasEstimator: gasEstimator, decimalConverter: dc, exchangeRegistry: exchangeRegistry, pricingEngine: pricingEngine, calculator: calculator, detectionEngine: detectionEngine, flashExecutor: flashExecutor, competitionAnalyzer: competitionAnalyzer, config: config, stats: stats, stopChan: make(chan struct{}), opportunityQueue: make(chan *pkgtypes.ArbitrageOpportunity, config.OpportunityQueueSize), executionQueue: make(chan *ExecutionTask, config.ExecutionQueueSize), } // Set default configuration framework.setDefaultConfig() return framework, nil } // setDefaultConfig sets default configuration values func (framework *LiveExecutionFramework) setDefaultConfig() { if framework.config.MaxConcurrentExecutions == 0 { framework.config.MaxConcurrentExecutions = 5 } if framework.config.WorkerPoolSize == 0 { framework.config.WorkerPoolSize = 10 } if framework.config.OpportunityQueueSize == 0 { framework.config.OpportunityQueueSize = 1000 } if framework.config.ExecutionQueueSize == 0 { framework.config.ExecutionQueueSize = 100 } if framework.config.MaxFailureRate == 0 { framework.config.MaxFailureRate = 0.5 // 50% failure rate threshold } if framework.config.HealthCheckInterval == 0 { framework.config.HealthCheckInterval = 30 * time.Second } if framework.config.DailyProfitTarget == nil { framework.config.DailyProfitTarget, _ = framework.decimalConverter.FromString("1", 18, "ETH") } if framework.config.DailyLossLimit == nil { framework.config.DailyLossLimit, _ = framework.decimalConverter.FromString("0.1", 18, "ETH") } if framework.config.MaxPositionSize == nil { framework.config.MaxPositionSize, _ = framework.decimalConverter.FromString("10", 18, "ETH") } } // Start begins the live execution framework func (framework *LiveExecutionFramework) Start(ctx context.Context) error { framework.runningMutex.Lock() defer framework.runningMutex.Unlock() if framework.isRunning { return fmt.Errorf("framework is already running") } framework.logger.Info("🚀 Starting Live MEV Execution Framework...") framework.logger.Info("================================================") framework.logger.Info(fmt.Sprintf("⚙️ Max Concurrent Executions: %d", framework.config.MaxConcurrentExecutions)) framework.logger.Info(fmt.Sprintf("💰 Daily Profit Target: %s ETH", framework.decimalConverter.ToHumanReadable(framework.config.DailyProfitTarget))) framework.logger.Info(fmt.Sprintf("🛡️ Daily Loss Limit: %s ETH", framework.decimalConverter.ToHumanReadable(framework.config.DailyLossLimit))) framework.logger.Info(fmt.Sprintf("📊 Worker Pool Size: %d", framework.config.WorkerPoolSize)) // Initialize worker pool framework.initializeWorkerPool(ctx) // Start detection engine if err := framework.detectionEngine.Start(ctx); err != nil { return fmt.Errorf("failed to start detection engine: %w", err) } framework.isRunning = true // Start main processing loops go framework.opportunityProcessor(ctx) go framework.executionCoordinator(ctx) go framework.healthMonitor(ctx) go framework.performanceTracker(ctx) framework.logger.Info("✅ Live Execution Framework started successfully!") framework.logger.Info("🔍 Monitoring for arbitrage opportunities...") return nil } // Stop halts the live execution framework func (framework *LiveExecutionFramework) Stop() error { framework.runningMutex.Lock() defer framework.runningMutex.Unlock() if !framework.isRunning { return fmt.Errorf("framework is not running") } framework.logger.Info("🛑 Stopping Live Execution Framework...") // Signal stop close(framework.stopChan) // Stop detection engine if err := framework.detectionEngine.Stop(); err != nil { framework.logger.Warn(fmt.Sprintf("Error stopping detection engine: %v", err)) } // Stop worker pool if framework.workerPool != nil { framework.workerPool.Stop() } framework.isRunning = false // Print final statistics framework.printFinalStats() framework.logger.Info("✅ Live Execution Framework stopped successfully") return nil } // initializeWorkerPool sets up the execution worker pool func (framework *LiveExecutionFramework) initializeWorkerPool(ctx context.Context) { framework.workerPool = &ExecutionWorkerPool{ workers: framework.config.WorkerPoolSize, taskChan: framework.executionQueue, framework: framework, } workerCtx, cancel := context.WithCancel(ctx) framework.workerPool.ctx = workerCtx framework.workerPool.cancel = cancel framework.workerPool.Start() } // opportunityProcessor processes detected opportunities func (framework *LiveExecutionFramework) opportunityProcessor(ctx context.Context) { framework.logger.Debug("Starting opportunity processor...") for { select { case <-ctx.Done(): return case <-framework.stopChan: return case opportunity := <-framework.detectionEngine.GetOpportunityChannel(): framework.processOpportunity(ctx, opportunity) } } } // convertArbitrageOpportunityToMEVOpportunity converts types.ArbitrageOpportunity to mev.MEVOpportunity func (framework *LiveExecutionFramework) convertArbitrageOpportunityToMEVOpportunity(opportunity *pkgtypes.ArbitrageOpportunity) *mev.MEVOpportunity { // Convert the arbitrage opportunity to MEV opportunity format for competition analysis estimatedProfit := big.NewInt(0) if opportunity.NetProfit != nil && opportunity.NetProfit.Value != nil { estimatedProfit = opportunity.NetProfit.Value } // Calculate required gas estimate (placeholder - would be more precise in production) requiredGas := uint64(800000) // typical for flash swaps if len(opportunity.Path) > 0 { requiredGas += uint64(len(opportunity.Path)-1) * 100000 // additional for each hop } return &mev.MEVOpportunity{ TxHash: "", // Will be populated later Block: 0, // Will be determined at execution time OpportunityType: "arbitrage", // or "sandwich", "liquidation", etc. EstimatedProfit: estimatedProfit, RequiredGas: requiredGas, Competition: 0, // This will be determined by the analyzer Confidence: opportunity.Confidence, } } // processOpportunity processes a single arbitrage opportunity func (framework *LiveExecutionFramework) processOpportunity(ctx context.Context, opportunity *pkgtypes.ArbitrageOpportunity) { framework.statsMutex.Lock() framework.stats.TotalOpportunitiesDetected++ framework.statsMutex.Unlock() framework.logger.Debug(fmt.Sprintf("Processing opportunity: %s profit", framework.decimalConverter.ToHumanReadable(opportunity.NetProfit))) // Perform risk checks if !framework.performRiskChecks(opportunity) { framework.logger.Debug("Opportunity failed risk checks, skipping") return } // Convert opportunity for competition analysis mevOpportunity := framework.convertArbitrageOpportunityToMEVOpportunity(opportunity) // Analyze competition competitionAnalysis, err := framework.competitionAnalyzer.AnalyzeCompetition(ctx, mevOpportunity) if err != nil { framework.logger.Warn(fmt.Sprintf("Competition analysis failed: %v", err)) return } // Check if we should proceed based on competition if !framework.shouldExecuteBasedOnCompetition(competitionAnalysis) { framework.logger.Debug("Skipping opportunity due to competition analysis") return } // Determine priority priority := framework.calculatePriority(opportunity, competitionAnalysis) // Create execution task task := &ExecutionTask{ Opportunity: opportunity, CompetitionAnalysis: competitionAnalysis, Priority: int(priority), // Convert TaskPriority to int SubmissionTime: time.Now(), ResultChan: make(chan *ExecutionResult, 1), } // Queue for execution select { case framework.executionQueue <- task: framework.statsMutex.Lock() framework.stats.TotalOpportunitiesQueued++ framework.statsMutex.Unlock() framework.logger.Info(fmt.Sprintf("🎯 Queued opportunity for execution: %s profit, Priority: %d", framework.decimalConverter.ToHumanReadable(opportunity.NetProfit), priority)) default: framework.logger.Warn("Execution queue full, dropping opportunity") } } // executionCoordinator coordinates the execution of queued opportunities func (framework *LiveExecutionFramework) executionCoordinator(ctx context.Context) { framework.logger.Debug("Starting execution coordinator...") activExecutions := 0 maxConcurrent := framework.config.MaxConcurrentExecutions for { select { case <-ctx.Done(): return case <-framework.stopChan: return case task := <-framework.executionQueue: // Check if we can start another execution if activExecutions >= maxConcurrent { framework.logger.Debug("Max concurrent executions reached, queuing task") // Put the task back in queue (simplified - production would use priority queue) go func() { time.Sleep(100 * time.Millisecond) select { case framework.executionQueue <- task: default: framework.logger.Warn("Failed to requeue task") } }() continue } activExecutions++ // Execute asynchronously go func(t *ExecutionTask) { defer func() { activExecutions-- }() framework.executeOpportunity(ctx, t) }(task) } } } // ExecuteOpportunity executes a single arbitrage opportunity func (framework *LiveExecutionFramework) ExecuteOpportunity(ctx context.Context, task *ExecutionTask) (*ExecutionResult, error) { // Submit the task framework.SubmitExecutionTask(ctx, task) // Wait for completion with timeout select { case result := <-task.ResultChan: if result == nil { return nil, fmt.Errorf("execution returned nil result") } return result, nil case <-time.After(45 * time.Second): // 45s timeout return nil, fmt.Errorf("execution timeout") } } // executeOpportunity executes a single arbitrage opportunity (internal worker method) func (framework *LiveExecutionFramework) executeOpportunity(ctx context.Context, task *ExecutionTask) { framework.statsMutex.Lock() framework.stats.TotalExecutionsAttempted++ framework.statsMutex.Unlock() framework.logger.Info(fmt.Sprintf("🚀 Executing arbitrage: %s expected profit", framework.decimalConverter.ToHumanReadable(task.Opportunity.NetProfit))) startTime := time.Now() // Execute the arbitrage result, err := framework.flashExecutor.ExecuteArbitrage(ctx, task.Opportunity) if err != nil { framework.logger.Error(fmt.Sprintf("Execution failed: %v", err)) result = &ExecutionResult{ Success: false, Error: err, ErrorMessage: err.Error(), } } executionTime := time.Since(startTime) // Update statistics framework.updateExecutionStats(result, executionTime) // Log result if result.Success { profitDecimal, _ := math.NewUniversalDecimal(result.ProfitRealized, 18, "ETH") framework.logger.Info(fmt.Sprintf("✅ Execution successful: %s profit realized in %v", framework.decimalConverter.ToHumanReadable(profitDecimal), executionTime)) } else { framework.logger.Warn(fmt.Sprintf("❌ Execution failed: %s", result.ErrorMessage)) } // Send result back if someone is waiting select { case task.ResultChan <- result: default: } } // Helper methods for risk management and decision making func (framework *LiveExecutionFramework) performRiskChecks(opportunity *pkgtypes.ArbitrageOpportunity) bool { // Check position size if comp, _ := framework.decimalConverter.Compare(opportunity.InputAmount, framework.config.MaxPositionSize); comp > 0 { return false } // Check daily loss limit today := time.Now().Format("2006-01-02") if dailyStats, exists := framework.stats.DailyStats[today]; exists { if dailyStats.NetProfit.IsNegative() { if comp, _ := framework.decimalConverter.Compare(dailyStats.NetProfit, framework.config.DailyLossLimit); comp < 0 { framework.logger.Warn("Daily loss limit reached, skipping opportunity") return false } } } // Check if we've hit daily profit target if dailyStats, exists := framework.stats.DailyStats[today]; exists { if comp, _ := framework.decimalConverter.Compare(dailyStats.ProfitRealized, framework.config.DailyProfitTarget); comp >= 0 { framework.logger.Info("Daily profit target reached, being conservative") // Could still execute high-confidence opportunities if opportunity.Confidence < 0.9 { return false } } } return true } func (framework *LiveExecutionFramework) shouldExecuteBasedOnCompetition(analysis *mev.CompetitionData) bool { // Skip if competition is too intense // CompetitionLevel for CompetitionData is a float64 representing intensity (0.0-1.0) if analysis.CompetitionLevel > 0.8 { // Considered extreme competition return false } // Skip if profit after gas is negative // NetProfit in CompetitionData represents profit after gas if analysis.NetProfit.Sign() < 0 { return false } return true } func (framework *LiveExecutionFramework) calculatePriority( opportunity *pkgtypes.ArbitrageOpportunity, competition *mev.CompetitionData, ) TaskPriority { // Base priority on profit size largeProfit, _ := framework.decimalConverter.FromString("0.1", 18, "ETH") mediumProfit, _ := framework.decimalConverter.FromString("0.05", 18, "ETH") var basePriority TaskPriority if comp, _ := framework.decimalConverter.Compare(opportunity.NetProfit, largeProfit); comp > 0 { basePriority = PriorityHigh } else if comp, _ := framework.decimalConverter.Compare(opportunity.NetProfit, mediumProfit); comp > 0 { basePriority = PriorityMedium } else { basePriority = PriorityLow } // Adjust for confidence if opportunity.Confidence > 0.9 && basePriority == PriorityHigh { basePriority = PriorityCritical } // Adjust for competition - CompetitionData uses CompetitionLevel (0.0-1.0) instead of CompetitionLevel string if competition.CompetitionLevel > 0.6 { // high competition if basePriority > PriorityLow { basePriority-- } } return basePriority } func (framework *LiveExecutionFramework) updateExecutionStats(result *ExecutionResult, executionTime time.Duration) { framework.statsMutex.Lock() defer framework.statsMutex.Unlock() if result.Success { framework.stats.TotalExecutionsSuccessful++ if result.ProfitRealized != nil { profitDecimal, _ := math.NewUniversalDecimal(result.ProfitRealized, 18, "ETH") framework.stats.TotalProfitRealized, _ = framework.decimalConverter.Add( framework.stats.TotalProfitRealized, profitDecimal, ) } } if result.GasCost != nil { framework.stats.TotalGasCostPaid, _ = framework.decimalConverter.Add( framework.stats.TotalGasCostPaid, result.GasCost, ) } // Update success rate if framework.stats.TotalExecutionsAttempted > 0 { framework.stats.CurrentSuccessRate = float64(framework.stats.TotalExecutionsSuccessful) / float64(framework.stats.TotalExecutionsAttempted) } // Update average execution time framework.stats.AverageExecutionTime = (framework.stats.AverageExecutionTime + executionTime) / 2 // Update daily stats framework.updateDailyStats(result) } func (framework *LiveExecutionFramework) updateDailyStats(result *ExecutionResult) { today := time.Now().Format("2006-01-02") if _, exists := framework.stats.DailyStats[today]; !exists { framework.stats.DailyStats[today] = &DailyStats{ Date: today, } framework.stats.DailyStats[today].ProfitRealized, _ = framework.decimalConverter.FromString("0", 18, "ETH") framework.stats.DailyStats[today].GasCostPaid, _ = framework.decimalConverter.FromString("0", 18, "ETH") framework.stats.DailyStats[today].NetProfit, _ = framework.decimalConverter.FromString("0", 18, "ETH") } dailyStats := framework.stats.DailyStats[today] dailyStats.ExecutionsAttempted++ if result.Success { dailyStats.ExecutionsSuccessful++ if result.ProfitRealized != nil { profitDecimal, _ := math.NewUniversalDecimal(result.ProfitRealized, 18, "ETH") dailyStats.ProfitRealized, _ = framework.decimalConverter.Add(dailyStats.ProfitRealized, profitDecimal) } } if result.GasCost != nil { dailyStats.GasCostPaid, _ = framework.decimalConverter.Add(dailyStats.GasCostPaid, result.GasCost) } // Calculate net profit dailyStats.NetProfit, _ = framework.decimalConverter.Subtract(dailyStats.ProfitRealized, dailyStats.GasCostPaid) } // healthMonitor monitors the health of the framework func (framework *LiveExecutionFramework) healthMonitor(ctx context.Context) { ticker := time.NewTicker(framework.config.HealthCheckInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-framework.stopChan: return case <-ticker.C: framework.performHealthCheck() } } } func (framework *LiveExecutionFramework) performHealthCheck() { framework.statsMutex.RLock() successRate := framework.stats.CurrentSuccessRate framework.statsMutex.RUnlock() // Check if failure rate exceeds threshold if successRate < (1.0 - framework.config.MaxFailureRate) { framework.logger.Warn(fmt.Sprintf("⚠️ Success rate below threshold: %.1f%%", successRate*100)) if framework.config.CircuitBreakerEnabled { framework.logger.Warn("🔥 Circuit breaker triggered - stopping framework") framework.Stop() } } // Log health status framework.logger.Debug(fmt.Sprintf("Health check - Success rate: %.1f%%, Active: %t", successRate*100, framework.isRunning)) } // performanceTracker tracks and logs performance metrics func (framework *LiveExecutionFramework) performanceTracker(ctx context.Context) { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-framework.stopChan: return case <-ticker.C: framework.logPerformanceMetrics() } } } func (framework *LiveExecutionFramework) logPerformanceMetrics() { framework.statsMutex.RLock() stats := framework.stats framework.statsMutex.RUnlock() framework.logger.Info("📊 Performance Metrics:") framework.logger.Info(fmt.Sprintf(" Opportunities Detected: %d", stats.TotalOpportunitiesDetected)) framework.logger.Info(fmt.Sprintf(" Executions Attempted: %d", stats.TotalExecutionsAttempted)) framework.logger.Info(fmt.Sprintf(" Success Rate: %.1f%%", stats.CurrentSuccessRate*100)) framework.logger.Info(fmt.Sprintf(" Total Profit: %s ETH", framework.decimalConverter.ToHumanReadable(stats.TotalProfitRealized))) framework.logger.Info(fmt.Sprintf(" Total Gas Cost: %s ETH", framework.decimalConverter.ToHumanReadable(stats.TotalGasCostPaid))) // Calculate net profit netProfit, _ := framework.decimalConverter.Subtract(stats.TotalProfitRealized, stats.TotalGasCostPaid) framework.logger.Info(fmt.Sprintf(" Net Profit: %s ETH", framework.decimalConverter.ToHumanReadable(netProfit))) framework.logger.Info(fmt.Sprintf(" Average Execution Time: %v", stats.AverageExecutionTime)) } func (framework *LiveExecutionFramework) printFinalStats() { framework.statsMutex.RLock() stats := framework.stats framework.statsMutex.RUnlock() framework.logger.Info("📈 Final Statistics:") framework.logger.Info("==================") framework.logger.Info(fmt.Sprintf("Runtime: %v", time.Since(stats.StartTime))) framework.logger.Info(fmt.Sprintf("Opportunities Detected: %d", stats.TotalOpportunitiesDetected)) framework.logger.Info(fmt.Sprintf("Opportunities Queued: %d", stats.TotalOpportunitiesQueued)) framework.logger.Info(fmt.Sprintf("Executions Attempted: %d", stats.TotalExecutionsAttempted)) framework.logger.Info(fmt.Sprintf("Executions Successful: %d", stats.TotalExecutionsSuccessful)) framework.logger.Info(fmt.Sprintf("Final Success Rate: %.1f%%", stats.CurrentSuccessRate*100)) framework.logger.Info(fmt.Sprintf("Total Profit Realized: %s ETH", framework.decimalConverter.ToHumanReadable(stats.TotalProfitRealized))) framework.logger.Info(fmt.Sprintf("Total Gas Cost Paid: %s ETH", framework.decimalConverter.ToHumanReadable(stats.TotalGasCostPaid))) netProfit, _ := framework.decimalConverter.Subtract(stats.TotalProfitRealized, stats.TotalGasCostPaid) framework.logger.Info(fmt.Sprintf("Final Net Profit: %s ETH", framework.decimalConverter.ToHumanReadable(netProfit))) } // GetStats returns current framework statistics func (framework *LiveExecutionFramework) GetStats() *FrameworkStats { framework.statsMutex.RLock() defer framework.statsMutex.RUnlock() // Return a copy to avoid race conditions statsCopy := *framework.stats return &statsCopy } // GetMetrics returns live execution metrics func (framework *LiveExecutionFramework) GetMetrics() *LiveExecutionMetrics { stats := framework.GetStats() return &LiveExecutionMetrics{ OpportunitiesDetected: int64(stats.TotalOpportunitiesDetected), SuccessfulExecutions: int64(stats.TotalExecutionsSuccessful), FailedExecutions: int64(stats.TotalExecutionsAttempted - stats.TotalExecutionsSuccessful), // Failed = Attempted - Successful TotalProfit: stats.TotalProfitRealized, AverageExecutionTime: stats.AverageExecutionTime, CurrentWorkers: int(framework.config.WorkerPoolSize), // Using configured worker pool size as a proxy for active workers QueueLength: int(len(framework.executionQueue)), // Current queue length } } // SubmitExecutionTask submits an execution task to the framework queue func (framework *LiveExecutionFramework) SubmitExecutionTask(ctx context.Context, task *ExecutionTask) { // Check if the framework is running framework.runningMutex.RLock() isRunning := framework.isRunning framework.runningMutex.RUnlock() if !isRunning { framework.logger.Error("Cannot submit task: framework is not running") return } // Add the task to the execution queue select { case framework.executionQueue <- task: framework.logger.Info(fmt.Sprintf("🎯 Queued execution task for opportunity with priority: %d", task.Priority)) framework.statsMutex.Lock() framework.stats.TotalOpportunitiesQueued++ framework.statsMutex.Unlock() case <-ctx.Done(): framework.logger.Warn("Context cancelled while trying to submit execution task") case <-time.After(5 * time.Second): // Timeout to avoid blocking indefinitely framework.logger.Error("Failed to submit execution task: queue is full") } } // SetMonitoringMode sets the framework to monitoring-only mode func (framework *LiveExecutionFramework) SetMonitoringMode(enabled bool) { framework.runningMutex.Lock() defer framework.runningMutex.Unlock() // In a real implementation, this would control whether execution is enabled // For now, we'll just log the change if enabled { framework.logger.Info("✅ Live execution framework set to monitoring mode") } else { framework.logger.Info("✅ Live execution framework set to active mode") } } // Worker pool implementation func (pool *ExecutionWorkerPool) Start() { for i := 0; i < pool.workers; i++ { pool.wg.Add(1) go pool.worker() } } func (pool *ExecutionWorkerPool) Stop() { pool.cancel() pool.wg.Wait() } func (pool *ExecutionWorkerPool) worker() { defer pool.wg.Done() for { select { case <-pool.ctx.Done(): return case task := <-pool.taskChan: pool.framework.executeOpportunity(pool.ctx, task) } } } // GasEstimatorWrapper wraps the Arbitrum gas estimator to implement the math.GasEstimator interface type GasEstimatorWrapper struct { gasEstimator *arbitrum.L2GasEstimator } func (w *GasEstimatorWrapper) EstimateSwapGas(exchangeType math.ExchangeType, poolData *math.PoolData) (uint64, error) { // Return estimates based on exchange type switch exchangeType { case math.ExchangeUniswapV3, math.ExchangeCamelot: return 200000, nil // Concentrated liquidity swaps case math.ExchangeUniswapV2, math.ExchangeSushiSwap: return 150000, nil // Simple AMM swaps case math.ExchangeBalancer: return 250000, nil // Weighted pool swaps case math.ExchangeCurve: return 180000, nil // Stable swaps default: return 200000, nil // Default estimate } } func (w *GasEstimatorWrapper) EstimateFlashSwapGas(route []*math.PoolData) (uint64, error) { baseGas := uint64(300000) // Base flash swap overhead gasPerHop := uint64(150000) // Additional gas per hop return baseGas + gasPerHop*uint64(len(route)), nil } func (w *GasEstimatorWrapper) GetCurrentGasPrice() (*math.UniversalDecimal, error) { // Return a mock gas price - production would get from the gas estimator dc := math.NewDecimalConverter() gasPrice, _ := dc.FromString("0.1", 9, "GWEI") // 0.1 gwei return gasPrice, nil }