//go:build examples // +build examples package parsers // This file demonstrates how to use the parser factory with multiple protocol parsers, // swap logging, and Arbiscan validation for MEV bot operations. import ( "context" "fmt" "log/slog" "math/big" "os" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/your-org/mev-bot/pkg/cache" "github.com/your-org/mev-bot/pkg/observability" mevtypes "github.com/your-org/mev-bot/pkg/types" "github.com/your-org/mev-bot/pkg/validation" ) // ExampleSetup demonstrates complete parser setup with all supported protocols func ExampleSetup() { ctx := context.Background() // 1. Create logger logger := observability.NewLogger(slog.LevelInfo) // 2. Create pool cache poolCache := cache.NewPoolCache() // 3. Populate cache with known pools (would come from pool discovery in production) populatePoolCache(ctx, poolCache) // 4. Create parser factory factory := NewFactory() // 5. Register all protocol parsers uniswapV2Parser := NewUniswapV2Parser(poolCache, logger) uniswapV3Parser := NewUniswapV3Parser(poolCache, logger) curveParser := NewCurveParser(poolCache, logger) factory.RegisterParser(mevtypes.ProtocolUniswapV2, uniswapV2Parser) factory.RegisterParser(mevtypes.ProtocolUniswapV3, uniswapV3Parser) factory.RegisterParser(mevtypes.ProtocolCurve, curveParser) // 6. Create swap logger for testing and validation swapLogger, _ := NewSwapLogger("./logs/swaps", logger) // 7. Create Arbiscan validator arbiscanAPIKey := os.Getenv("ARBISCAN_API_KEY") arbiscanValidator := NewArbiscanValidator(arbiscanAPIKey, logger, swapLogger) // 8. Create validator with rules validationRules := validation.DefaultValidationRules() validator := validation.NewValidator(validationRules) // Now ready to parse transactions fmt.Println("✅ Parser factory initialized with 3 protocols") fmt.Println("✅ Swap logging enabled") fmt.Println("✅ Arbiscan validation enabled") // Example usage (see ExampleParseTransaction) _ = factory _ = validator _ = swapLogger _ = arbiscanValidator } // ExampleParseTransaction shows how to parse a transaction with multiple swaps func ExampleParseTransaction( factory *factory, tx *types.Transaction, receipt *types.Receipt, validator validation.Validator, swapLogger *SwapLogger, ) ([]*mevtypes.SwapEvent, error) { ctx := context.Background() // 1. Parse all swap events from the transaction events, err := factory.ParseTransaction(ctx, tx, receipt) if err != nil { return nil, fmt.Errorf("failed to parse transaction: %w", err) } // 2. Validate each event validEvents := validator.FilterValid(ctx, events) // 3. Log valid swaps for testing/analysis if len(validEvents) > 0 { swapLogger.LogSwapBatch(ctx, validEvents, "multi-protocol") } // 4. Return valid events for arbitrage detection return validEvents, nil } // ExampleArbitrageDetection shows how to detect arbitrage opportunities func ExampleArbitrageDetection(events []*mevtypes.SwapEvent, poolCache cache.PoolCache) { ctx := context.Background() // Group events by token pairs type TokenPair struct { Token0, Token1 common.Address } eventsByPair := make(map[TokenPair][]*mevtypes.SwapEvent) for _, event := range events { pair := TokenPair{ Token0: event.Token0, Token1: event.Token1, } eventsByPair[pair] = append(eventsByPair[pair], event) } // For each token pair, compare prices across protocols for pair, pairEvents := range eventsByPair { if len(pairEvents) < 2 { continue // Need at least 2 events to compare } // Compare V2 vs V3 prices for i, event1 := range pairEvents { for j, event2 := range pairEvents { if i >= j { continue } // Check if protocols are different if event1.Protocol == event2.Protocol { continue } // Calculate implied prices price1 := calculateImpliedPrice(event1) price2 := calculateImpliedPrice(event2) // Calculate price difference priceDiff := new(big.Float).Sub(price1, price2) priceDiff.Abs(priceDiff) // If price difference > threshold, we have an arbitrage opportunity threshold := big.NewFloat(0.001) // 0.1% if priceDiff.Cmp(threshold) > 0 { fmt.Printf("🎯 Arbitrage opportunity found!\n") fmt.Printf(" Pair: %s/%s\n", pair.Token0.Hex()[:10], pair.Token1.Hex()[:10]) fmt.Printf(" %s price: %s\n", event1.Protocol, price1.Text('f', 6)) fmt.Printf(" %s price: %s\n", event2.Protocol, price2.Text('f', 6)) fmt.Printf(" Difference: %s\n", priceDiff.Text('f', 6)) // Calculate potential profit profit := simulateArbitrage(ctx, event1, event2, poolCache) if profit.Sign() > 0 { fmt.Printf(" 💰 Estimated profit: %s ETH\n", profit.Text('f', 6)) } } } } } } // ExampleMultiHopArbitrage shows how to detect multi-hop arbitrage (A→B→C→A) func ExampleMultiHopArbitrage(poolCache cache.PoolCache) { ctx := context.Background() // Example: WETH → USDC → DAI → WETH arbitrage on Uniswap V3 // Pool 1: WETH/USDC poolWETH_USDC, _ := poolCache.GetByAddress(ctx, common.HexToAddress("0x1111")) // Pool 2: USDC/DAI poolUSDC_DAI, _ := poolCache.GetByAddress(ctx, common.HexToAddress("0x2222")) // Pool 3: DAI/WETH poolDAI_WETH, _ := poolCache.GetByAddress(ctx, common.HexToAddress("0x3333")) // Simulate route: 1 WETH → USDC → DAI → WETH startAmount := big.NewInt(1000000000000000000) // 1 WETH // Step 1: WETH → USDC usdcAmount, priceAfter1, _ := CalculateSwapAmounts( poolWETH_USDC.SqrtPriceX96, poolWETH_USDC.Liquidity, startAmount, true, // WETH = token0 3000, // 0.3% fee ) // Step 2: USDC → DAI daiAmount, priceAfter2, _ := CalculateSwapAmounts( poolUSDC_DAI.SqrtPriceX96, poolUSDC_DAI.Liquidity, usdcAmount, true, // USDC = token0 500, // 0.05% fee (Curve-like) ) // Step 3: DAI → WETH finalWETH, priceAfter3, _ := CalculateSwapAmounts( poolDAI_WETH.SqrtPriceX96, poolDAI_WETH.Liquidity, daiAmount, false, // WETH = token1 3000, // 0.3% fee ) // Calculate profit profit := new(big.Int).Sub(finalWETH, startAmount) if profit.Sign() > 0 { fmt.Printf("🚀 Multi-hop arbitrage opportunity!\n") fmt.Printf(" Route: WETH → USDC → DAI → WETH\n") fmt.Printf(" Input: %s WETH\n", formatAmount(startAmount, 18)) fmt.Printf(" Output: %s WETH\n", formatAmount(finalWETH, 18)) fmt.Printf(" 💰 Profit: %s WETH\n", formatAmount(profit, 18)) fmt.Printf(" Prices: %v → %v → %v\n", priceAfter1, priceAfter2, priceAfter3) } else { fmt.Printf("❌ No profit: %s WETH loss\n", formatAmount(new(big.Int).Abs(profit), 18)) } } // Helper functions func populatePoolCache(ctx context.Context, poolCache cache.PoolCache) { // Example pools (would come from discovery service in production) // Uniswap V2: WETH/USDC poolCache.Add(ctx, &mevtypes.PoolInfo{ Address: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), Protocol: mevtypes.ProtocolUniswapV2, Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH Token1: common.HexToAddress("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC Token0Decimals: 18, Token1Decimals: 6, Fee: 30, // 0.3% IsActive: true, }) // Uniswap V3: WETH/USDC 0.05% poolCache.Add(ctx, &mevtypes.PoolInfo{ Address: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa444"), Protocol: mevtypes.ProtocolUniswapV3, Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH Token1: common.HexToAddress("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC Token0Decimals: 18, Token1Decimals: 6, Fee: 500, // 0.05% SqrtPriceX96: new(big.Int).Lsh(big.NewInt(1), 96), Liquidity: big.NewInt(1000000000000), IsActive: true, }) // Curve: USDC/USDT poolCache.Add(ctx, &mevtypes.PoolInfo{ Address: common.HexToAddress("0x7f90122BF0700F9E7e1F688fe926940E8839F353"), Protocol: mevtypes.ProtocolCurve, Token0: common.HexToAddress("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC Token1: common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), // USDT Token0Decimals: 6, Token1Decimals: 6, Fee: 4, // 0.04% AmpCoefficient: big.NewInt(2000), IsActive: true, }) } func calculateImpliedPrice(event *mevtypes.SwapEvent) *big.Float { // Calculate price as amountOut / amountIn var amountIn, amountOut *big.Int if event.Amount0In.Sign() > 0 { amountIn = event.Amount0In amountOut = event.Amount1Out } else { amountIn = event.Amount1In amountOut = event.Amount0Out } if amountIn.Sign() == 0 { return big.NewFloat(0) } amountInFloat := new(big.Float).SetInt(amountIn) amountOutFloat := new(big.Float).SetInt(amountOut) price := new(big.Float).Quo(amountOutFloat, amountInFloat) return price } func simulateArbitrage( ctx context.Context, event1, event2 *mevtypes.SwapEvent, poolCache cache.PoolCache, ) *big.Float { // Simplified arbitrage simulation // In production, this would: // 1. Calculate optimal trade size // 2. Account for gas costs // 3. Account for slippage // 4. Check liquidity constraints // For now, return mock profit return big.NewFloat(0.05) // 0.05 ETH profit } func formatAmount(amount *big.Int, decimals uint8) string { // Convert to float and format amountFloat := new(big.Float).SetInt(amount) divisor := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) result := new(big.Float).Quo(amountFloat, divisor) return result.Text('f', 6) } // ExampleRealTimeMonitoring shows how to monitor pending transactions func ExampleRealTimeMonitoring() { fmt.Println("📡 Real-time MEV bot monitoring pattern:") fmt.Println("") fmt.Println("1. Subscribe to pending transactions (mempool)") fmt.Println("2. Parse swaps using factory.ParseTransaction()") fmt.Println("3. Validate using validator.FilterValid()") fmt.Println("4. Detect arbitrage across protocols") fmt.Println("5. Calculate profitability (profit - gas)") fmt.Println("6. Execute if profitable (front-run, sandwich, or arbitrage)") fmt.Println("7. Log results with swapLogger for analysis") fmt.Println("8. Validate accuracy with arbiscanValidator") fmt.Println("") fmt.Println("Performance targets:") fmt.Println(" - Parse: < 5ms") fmt.Println(" - Validate: < 2ms") fmt.Println(" - Detect: < 10ms") fmt.Println(" - Execute: < 30ms") fmt.Println(" - Total: < 50ms end-to-end") }