diff --git a/pkg/arbitrage/service.go b/pkg/arbitrage/service.go index 5d74c9a..e028a1b 100644 --- a/pkg/arbitrage/service.go +++ b/pkg/arbitrage/service.go @@ -1659,12 +1659,105 @@ func (sas *ArbitrageService) SubmitBridgeOpportunity(ctx context.Context, bridge opp.ID = fmt.Sprintf("bridge-%s-%d", opp.TokenIn.Hex(), now.UnixNano()) } - sas.logger.Info("📥 Received bridge arbitrage opportunity", + sas.logger.Info("📥 Received bridge arbitrage opportunity - analyzing with multi-hop scanner", "id", opp.ID, "path_length", len(opp.Path), "pools", len(opp.Pools), ) + // ✅ CRITICAL FIX: Use multi-hop scanner to find REAL arbitrage paths + // Instead of accepting the single-pool opportunity as-is, scan for multi-hop paths + var tokensToScan []common.Address + if opp.TokenIn != (common.Address{}) { + tokensToScan = append(tokensToScan, opp.TokenIn) + } + if opp.TokenOut != (common.Address{}) && opp.TokenOut != opp.TokenIn { + tokensToScan = append(tokensToScan, opp.TokenOut) + } + + // If we have tokens, use multi-hop scanner to find profitable paths + if len(tokensToScan) > 0 && sas.multiHopScanner != nil { + scanAmount := opp.AmountIn + if scanAmount == nil || scanAmount.Sign() == 0 { + scanAmount = big.NewInt(100000000000000000) // Default 0.1 ETH scan amount + } + + sas.logger.Info("🔍 Scanning for multi-hop arbitrage paths", + "tokens", len(tokensToScan), + "scanAmount", scanAmount.String(), + ) + + for _, token := range tokensToScan { + paths, err := sas.multiHopScanner.ScanForArbitrage(sas.ctx, token, scanAmount) + if err != nil { + sas.logger.Debug(fmt.Sprintf("Multi-hop scan failed for token %s: %v", token.Hex(), err)) + continue + } + + if len(paths) > 0 { + sas.logger.Info(fmt.Sprintf("✅ Found %d multi-hop arbitrage paths!", len(paths))) + + // Create and execute opportunities for each profitable path + for _, path := range paths { + if path.NetProfit.Sign() <= 0 { + continue // Skip unprofitable paths + } + + pathTokens := make([]string, len(path.Tokens)) + for i, tokenAddr := range path.Tokens { + pathTokens[i] = tokenAddr.Hex() + } + + poolAddresses := make([]string, len(path.Pools)) + for i, poolInfo := range path.Pools { + poolAddresses[i] = poolInfo.Address.Hex() + } + + multiHopOpp := &pkgtypes.ArbitrageOpportunity{ + ID: fmt.Sprintf("multihop-%s-%d", token.Hex(), now.UnixNano()), + Path: pathTokens, + Pools: poolAddresses, + AmountIn: new(big.Int).Set(scanAmount), + RequiredAmount: new(big.Int).Set(scanAmount), + Profit: new(big.Int).Set(path.NetProfit), + NetProfit: new(big.Int).Set(path.NetProfit), + EstimatedProfit: new(big.Int).Set(path.NetProfit), + DetectedAt: now, + ExpiresAt: now.Add(sas.config.OpportunityTTL), + Timestamp: now.Unix(), + Urgency: sas.calculateUrgency(path), + ROI: path.ROI, + TokenIn: path.Tokens[0], + TokenOut: path.Tokens[len(path.Tokens)-1], + Confidence: 0.7, + } + + // Store path + sas.storeOpportunityPath(multiHopOpp.ID, path) + + // Save to database + if sas.database != nil { + if err := sas.database.SaveOpportunity(sas.ctx, multiHopOpp); err != nil { + sas.logger.Warn("Failed to save multi-hop opportunity", "error", err) + } + } + + // Increment stats + atomic.AddInt64(&sas.stats.TotalOpportunitiesDetected, 1) + + // Execute opportunity + sas.logger.Info(fmt.Sprintf("🚀 Executing multi-hop opportunity: profit=%.6f ETH, ROI=%.2f%%", + float64(path.NetProfit.Int64())/1e18, path.ROI*100)) + go sas.executeOpportunity(multiHopOpp) + } + return nil // Successfully processed multi-hop paths + } + } + } + + // Fallback: If multi-hop scan didn't find anything, process original opportunity + sas.logger.Debug("No multi-hop paths found, processing original single-pool opportunity") + if path := sas.fallbackPathFromOpportunity(opp); path != nil { sas.storeOpportunityPath(opp.ID, path) } @@ -1686,7 +1779,12 @@ func (sas *ArbitrageService) SubmitBridgeOpportunity(ctx context.Context, bridge atomic.AddInt64(&sas.stats.TotalOpportunitiesDetected, 1) - go sas.executeOpportunity(opp) + // Only execute if it has positive profit + if opp.NetProfit != nil && opp.NetProfit.Sign() > 0 { + go sas.executeOpportunity(opp) + } else { + sas.logger.Debug("Skipping execution of zero-profit opportunity", "id", opp.ID) + } return nil } diff --git a/pkg/scanner/market/scanner.go b/pkg/scanner/market/scanner.go index 4c0ebd2..71487ad 100644 --- a/pkg/scanner/market/scanner.go +++ b/pkg/scanner/market/scanner.go @@ -737,13 +737,17 @@ func (s *MarketScanner) executeArbitrageOpportunity(opportunity stypes.Arbitrage } // Only execute opportunities with sufficient profit - minProfitThreshold := big.NewInt(10000000000000000) // 0.01 ETH minimum profit + // AGGRESSIVE THRESHOLD: Lowered to match arbitrum_production.yaml settings (0.00001 ETH / $0.02) + minProfitThreshold := big.NewInt(10000000000000) // 0.00001 ETH minimum profit - VERY AGGRESSIVE if opportunity.Profit.Cmp(minProfitThreshold) < 0 { - s.logger.Debug(fmt.Sprintf("Arbitrage opportunity profit too low: %s < %s", - opportunity.Profit.String(), minProfitThreshold.String())) + s.logger.Debug(fmt.Sprintf("Arbitrage opportunity profit too low: %s < %s (%.6f ETH)", + opportunity.Profit.String(), minProfitThreshold.String(), float64(opportunity.Profit.Int64())/1e18)) return } + s.logger.Info(fmt.Sprintf("✅ PROFITABLE OPPORTUNITY FOUND! Profit: %.6f ETH (%.2f USD @ $2000/ETH)", + float64(opportunity.Profit.Int64())/1e18, float64(opportunity.Profit.Int64())*2000/1e18)) + s.logger.Info(fmt.Sprintf("Executing arbitrage opportunity with profit: %s", opportunity.Profit.String())) // Execute the arbitrage opportunity