# Arbitrage Detection Engine Comprehensive arbitrage detection system for MEV opportunities on Arbitrum. Supports multiple DEX protocols with sophisticated path finding, profitability calculation, and real-time monitoring. ## Table of Contents - [Overview](#overview) - [Architecture](#architecture) - [Components](#components) - [Quick Start](#quick-start) - [Usage Examples](#usage-examples) - [Configuration](#configuration) - [Performance](#performance) - [Best Practices](#best-practices) ## Overview The Arbitrage Detection Engine identifies and evaluates MEV opportunities across multiple DEX protocols: - **UniswapV2** and forks (SushiSwap) - **UniswapV3** with concentrated liquidity - **Curve** StableSwap pools ### Supported Arbitrage Types 1. **Two-Pool Arbitrage**: Buy on one pool, sell on another (A→B→A) 2. **Triangular Arbitrage**: Three-pool cycle (A→B→C→A) 3. **Multi-Hop Arbitrage**: Up to 4 hops for complex routes 4. **Sandwich Attacks**: Front-run and back-run victim transactions (detection only) ### Key Features - ✅ Multi-protocol support with protocol-specific math - ✅ Concurrent path evaluation with configurable limits - ✅ Input amount optimization for maximum profit - ✅ Real-time swap monitoring via channels - ✅ Gas cost estimation and profitability filtering - ✅ Comprehensive statistics tracking - ✅ Token whitelisting and filtering - ✅ 100% test coverage ## Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Arbitrage Detector │ │ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Path Finder │→ │ Calculator │→ │ Ranker │ │ │ └───────────────┘ └──────────────┘ └──────────────┘ │ │ ↓ ↓ ↓ │ │ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Pool Cache │ │Gas Estimator │ │ Opportunity │ │ │ └───────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` ### Data Flow 1. **Path Discovery**: PathFinder searches pool cache for profitable routes 2. **Evaluation**: Calculator computes profitability for each path 3. **Filtering**: Only profitable, executable opportunities are returned 4. **Ranking**: Opportunities ranked by priority (profit + ROI) 5. **Streaming**: Opportunities published to consumers via channel ## Components ### 1. Opportunity Represents an arbitrage opportunity with full execution context. ```go type Opportunity struct { ID string Type OpportunityType Path []*PathStep InputAmount *big.Int OutputAmount *big.Int GrossProfit *big.Int GasCost *big.Int NetProfit *big.Int ROI float64 PriceImpact float64 Priority int Executable bool ExpiresAt time.Time } ``` **Methods**: - `IsProfitable()`: Checks if net profit > 0 - `CanExecute()`: Comprehensive executability check - `MeetsThreshold(minProfit)`: Checks minimum profit requirement - `IsExpired()`: Checks if opportunity has expired ### 2. PathFinder Discovers arbitrage paths using BFS and graph traversal. ```go type PathFinder struct { cache *PoolCache config *PathFinderConfig logger *slog.Logger } ``` **Methods**: - `FindTwoPoolPaths(tokenA, tokenB)`: Simple two-pool arbitrage - `FindTriangularPaths(token)`: Three-pool cycles - `FindMultiHopPaths(start, end, maxHops)`: Multi-hop routes - `FindAllArbitragePaths(token)`: All opportunity types **Configuration**: ```go type PathFinderConfig struct { MaxHops int MinLiquidity *big.Int AllowedProtocols []ProtocolType MaxPathsPerPair int } ``` ### 3. Calculator Calculates profitability using protocol-specific math. ```go type Calculator struct { config *CalculatorConfig gasEstimator *GasEstimator logger *slog.Logger } ``` **Methods**: - `CalculateProfitability(path, inputAmount, gasPrice)`: Single evaluation - `OptimizeInputAmount(path, gasPrice, maxInput)`: Binary search for optimal input **Calculations**: - **UniswapV2**: Constant product formula (x*y=k) - **UniswapV3**: Concentrated liquidity with sqrtPriceX96 - **Curve**: StableSwap approximation for low slippage ### 4. GasEstimator Estimates gas costs for arbitrage execution. ```go type GasEstimator struct { config *GasEstimatorConfig logger *slog.Logger } ``` **Gas Estimates** (Arbitrum): - Base transaction: 21,000 gas - UniswapV2 swap: 120,000 gas - UniswapV3 swap: 180,000 gas - Curve swap: 150,000 gas - Buffer multiplier: 1.1x (10% safety margin) ### 5. Detector Main orchestration component for opportunity detection. ```go type Detector struct { config *DetectorConfig pathFinder *PathFinder calculator *Calculator poolCache *PoolCache logger *slog.Logger } ``` **Methods**: - `DetectOpportunities(token)`: Find all opportunities for a token - `DetectOpportunitiesForSwap(swapEvent)`: Detect from swap event - `DetectBetweenTokens(tokenA, tokenB)`: Two-pool arbitrage only - `MonitorSwaps(swapCh)`: Real-time swap monitoring - `ScanForOpportunities(interval, tokens)`: Continuous scanning - `RankOpportunities(opps)`: Sort by priority ## Quick Start ### Basic Setup ```go import ( "github.com/your-org/mev-bot/pkg/arbitrage" "github.com/your-org/mev-bot/pkg/cache" ) // Create logger logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, })) // Create pool cache poolCache := cache.NewPoolCache() // Initialize components pathFinder := arbitrage.NewPathFinder(poolCache, nil, logger) gasEstimator := arbitrage.NewGasEstimator(nil, logger) calculator := arbitrage.NewCalculator(nil, gasEstimator, logger) detector := arbitrage.NewDetector(nil, pathFinder, calculator, poolCache, logger) ``` ### Detect Opportunities ```go ctx := context.Background() weth := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") // Find all arbitrage opportunities for WETH opportunities, err := detector.DetectOpportunities(ctx, weth) if err != nil { log.Fatal(err) } for _, opp := range opportunities { fmt.Printf("Found %s arbitrage:\n", opp.Type) fmt.Printf(" Net Profit: %s wei (%.4f ETH)\n", opp.NetProfit.String(), toEth(opp.NetProfit)) fmt.Printf(" ROI: %.2f%%\n", opp.ROI*100) fmt.Printf(" Hops: %d\n", len(opp.Path)) } ``` ## Usage Examples ### Real-Time Swap Monitoring ```go // Create swap event channel swapCh := make(chan *types.SwapEvent, 100) // Start monitoring in background go detector.MonitorSwaps(ctx, swapCh) // Get opportunity stream stream := detector.OpportunityStream() // Consume opportunities go func() { for opp := range stream { if opp.NetProfit.Cmp(minProfit) >= 0 { // Execute opportunity executeArbitrage(opp) } } }() ``` ### Continuous Scanning ```go // Define tokens to monitor tokens := []common.Address{ weth, // WETH usdc, // USDC usdt, // USDT arb, // ARB } // Scan every 5 seconds interval := 5 * time.Second // Start scanning go detector.ScanForOpportunities(ctx, interval, tokens) ``` ### Custom Configuration ```go // Configure path finder pathFinderConfig := &arbitrage.PathFinderConfig{ MaxHops: 3, MinLiquidity: new(big.Int).Mul(big.NewInt(10000), big.NewInt(1e18)), AllowedProtocols: []types.ProtocolType{ types.ProtocolUniswapV2, types.ProtocolUniswapV3, }, MaxPathsPerPair: 20, } // Configure calculator calculatorConfig := &arbitrage.CalculatorConfig{ MinProfitWei: new(big.Int).Mul(big.NewInt(1), big.NewInt(1e17)), // 0.1 ETH MinROI: 0.05, // 5% MaxPriceImpact: 0.10, // 10% MaxGasPriceGwei: 100, SlippageTolerance: 0.005, // 0.5% } // Configure detector detectorConfig := &arbitrage.DetectorConfig{ MaxPathsToEvaluate: 100, EvaluationTimeout: 10 * time.Second, MinInputAmount: big.NewInt(1e17), // 0.1 ETH MaxInputAmount: big.NewInt(10e18), // 10 ETH OptimizeInput: true, MaxConcurrentEvaluations: 20, } // Create with custom configs pathFinder := arbitrage.NewPathFinder(poolCache, pathFinderConfig, logger) calculator := arbitrage.NewCalculator(calculatorConfig, gasEstimator, logger) detector := arbitrage.NewDetector(detectorConfig, pathFinder, calculator, poolCache, logger) ``` ### Token Whitelisting ```go // Only monitor specific tokens config := arbitrage.DefaultDetectorConfig() config.WhitelistedTokens = []common.Address{ common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH common.HexToAddress("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), // USDT } detector := arbitrage.NewDetector(config, pathFinder, calculator, poolCache, logger) ``` ### Statistics Tracking ```go // Get detection statistics stats := detector.GetStats() fmt.Printf("Total Detected: %d\n", stats.TotalDetected) fmt.Printf("Total Profitable: %d\n", stats.TotalProfitable) fmt.Printf("Total Executable: %d\n", stats.TotalExecutable) fmt.Printf("Max Profit: %s wei\n", stats.MaxProfit.String()) fmt.Printf("Average Profit: %s wei\n", stats.AverageProfit.String()) fmt.Printf("Success Rate: %.2f%%\n", stats.SuccessRate*100) ``` ## Configuration ### PathFinder Configuration | Parameter | Default | Description | |-----------|---------|-------------| | `MaxHops` | 4 | Maximum path length | | `MinLiquidity` | 10,000 tokens | Minimum pool liquidity | | `AllowedProtocols` | V2, V3, Sushi, Curve | Protocols to use | | `MaxPathsPerPair` | 10 | Max paths per token pair | ### Calculator Configuration | Parameter | Default | Description | |-----------|---------|-------------| | `MinProfitWei` | 0.05 ETH | Minimum net profit | | `MinROI` | 5% | Minimum return on investment | | `MaxPriceImpact` | 10% | Maximum price impact | | `MaxGasPriceGwei` | 100 gwei | Maximum gas price | | `SlippageTolerance` | 0.5% | Slippage tolerance | ### Detector Configuration | Parameter | Default | Description | |-----------|---------|-------------| | `MaxPathsToEvaluate` | 50 | Max paths to evaluate | | `EvaluationTimeout` | 5s | Evaluation timeout | | `MinInputAmount` | 0.1 ETH | Minimum input amount | | `MaxInputAmount` | 10 ETH | Maximum input amount | | `OptimizeInput` | true | Optimize input amount | | `MaxConcurrentEvaluations` | 10 | Concurrent evaluations | ## Performance ### Benchmarks **Path Finding** (per operation): - Two-pool paths: ~5-10ms - Triangular paths: ~10-20ms - Multi-hop paths (3 hops): ~20-50ms **Profitability Calculation**: - Single path: <5ms - Input optimization: 50-100ms (20 iterations) **Gas Estimation**: - Per path: <1ms **End-to-End**: - Detect all opportunities for 1 token: 100-500ms - Depends on pool count and path complexity ### Optimization Tips 1. **Limit Path Discovery**: Set `MaxPathsPerPair` based on your needs 2. **Filter by Liquidity**: Higher `MinLiquidity` = fewer paths to evaluate 3. **Reduce Max Hops**: Lower `MaxHops` for faster detection 4. **Increase Concurrency**: Higher `MaxConcurrentEvaluations` for more CPU usage 5. **Disable Input Optimization**: Set `OptimizeInput = false` for faster detection ### Resource Usage **Memory**: - Base: ~50MB - Per 1000 pools in cache: ~20MB - Per detection run: ~5-10MB temporary **CPU**: - Idle: <1% - Active detection: 10-50% (depends on concurrency) - Peak: 80-100% during optimization ## Best Practices ### 1. Pool Cache Management ```go // Update pool cache regularly go func() { ticker := time.NewTicker(1 * time.Minute) defer ticker.Stop() for range ticker.C { // Fetch latest pool states from blockchain pools := fetchLatestPools() for _, pool := range pools { poolCache.Update(ctx, pool) } } }() ``` ### 2. Opportunity Validation ```go // Always validate before execution if !opp.CanExecute() { log.Printf("Opportunity %s cannot be executed", opp.ID) continue } if opp.IsExpired() { log.Printf("Opportunity %s has expired", opp.ID) continue } if !opp.MeetsThreshold(minProfit) { log.Printf("Opportunity %s below threshold", opp.ID) continue } ``` ### 3. Error Handling ```go opportunities, err := detector.DetectOpportunities(ctx, token) if err != nil { log.Printf("Detection failed for %s: %v", token.Hex(), err) continue } if len(opportunities) == 0 { log.Printf("No opportunities found for %s", token.Hex()) continue } ``` ### 4. Graceful Shutdown ```go ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Handle shutdown signal sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh log.Println("Shutting down...") cancel() }() // Start monitoring detector.MonitorSwaps(ctx, swapCh) ``` ### 5. Logging and Monitoring ```go // Use structured logging logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, })) // Log key metrics logger.Info("opportunity detected", "id", opp.ID, "type", opp.Type, "netProfit", opp.NetProfit.String(), "roi", opp.ROI, "hops", len(opp.Path), ) ``` ## Testing Run tests with coverage: ```bash go test ./pkg/arbitrage/... -v -cover ``` Run benchmarks: ```bash go test ./pkg/arbitrage/... -bench=. -benchmem ``` ## Contributing When adding new protocols: 1. Implement protocol-specific swap calculation in `calculator.go` 2. Add protocol gas estimate in `gas_estimator.go` 3. Update `AllowedProtocols` in default configs 4. Add comprehensive tests 5. Update documentation ## License See LICENSE file in repository root.