package exchanges import ( "fmt" "math/big" "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/math" ) // TokenInfo represents token information type TokenInfo struct { Address string Symbol string Name string Decimals uint8 } // TokenPair represents a pair of tokens type TokenPair struct { Token0 TokenInfo Token1 TokenInfo } // Use ExchangeType from math package to avoid duplication // ExchangeConfig holds configuration for an exchange type ExchangeConfig struct { Type math.ExchangeType Name string FactoryAddress common.Address RouterAddress common.Address PoolInitCodeHash string SwapSelector []byte StableSwapSelector []byte ChainID int64 SupportsFlashSwaps bool RequiresApproval bool MaxHops int DefaultSlippagePercent float64 Url string ApiUrl string } // PoolDetector interface for detecting pools type PoolDetector interface { GetAllPools(token0, token1 common.Address) ([]common.Address, error) GetPoolForPair(token0, token1 common.Address) (common.Address, error) GetSupportedFeeTiers() []int64 GetPoolType() string } // LiquidityFetcher interface for fetching liquidity data type LiquidityFetcher interface { GetPoolData(poolAddress common.Address) (*math.PoolData, error) GetTokenReserves(poolAddress, token0, token1 common.Address) (*big.Int, *big.Int, error) GetPoolPrice(poolAddress common.Address) (*big.Float, error) GetLiquidityDepth(poolAddress, tokenIn common.Address, amount *big.Int) (*big.Int, error) } // SwapRouter interface for executing swaps type SwapRouter interface { CalculateSwap(tokenIn, tokenOut common.Address, amountIn *big.Int) (*big.Int, error) GenerateSwapData(tokenIn, tokenOut common.Address, amountIn, minAmountOut *big.Int, deadline *big.Int) ([]byte, error) GetSwapRoute(tokenIn, tokenOut common.Address) ([]common.Address, error) ValidateSwap(tokenIn, tokenOut common.Address, amountIn *big.Int) error } // ExchangeRegistry manages multiple exchanges type ExchangeRegistry struct { client *ethclient.Client logger *logger.Logger exchanges map[math.ExchangeType]*ExchangeConfig poolDetectors map[math.ExchangeType]PoolDetector liquidityFetchers map[math.ExchangeType]LiquidityFetcher swapRouters map[math.ExchangeType]SwapRouter marketScanner *MarketScanner } // MarketScanner interface for scanning markets type MarketScanner interface { GetPoolsForTokenPair(token0, token1 common.Address) []common.Address GetPoolInfo(poolAddress common.Address) (*math.PoolData, error) } // NewExchangeRegistry creates a new exchange registry func NewExchangeRegistry(client *ethclient.Client, logger *logger.Logger) *ExchangeRegistry { return &ExchangeRegistry{ client: client, logger: logger, exchanges: make(map[math.ExchangeType]*ExchangeConfig), poolDetectors: make(map[math.ExchangeType]PoolDetector), liquidityFetchers: make(map[math.ExchangeType]LiquidityFetcher), swapRouters: make(map[math.ExchangeType]SwapRouter), } } // LoadArbitrumExchanges loads all supported exchanges on Arbitrum func (er *ExchangeRegistry) LoadArbitrumExchanges() error { // Load Uniswap V3 uniswapV3Config := &ExchangeConfig{ Type: math.ExchangeUniswapV3, Name: "Uniswap V3", FactoryAddress: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), RouterAddress: common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"), PoolInitCodeHash: "0xe34f199b19b2b4f47d6b5b39c7bde33e2a0a4c36149e248e847a490f543b7f3f", SwapSelector: []byte{0x38, 0xed, 0x17, 0x39}, // exactInputSingle StableSwapSelector: []byte{}, ChainID: 42161, SupportsFlashSwaps: true, RequiresApproval: true, MaxHops: 2, DefaultSlippagePercent: 0.5, Url: "https://uniswap.org", ApiUrl: "https://api.uniswap.org", } er.exchanges[math.ExchangeUniswapV3] = uniswapV3Config // Load SushiSwap sushiSwapConfig := &ExchangeConfig{ Type: math.ExchangeSushiSwap, Name: "SushiSwap", FactoryAddress: common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"), RouterAddress: common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"), PoolInitCodeHash: "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303", SwapSelector: []byte{0x18, 0x2d, 0x2e, 0xdb}, // swapExactTokensForTokens StableSwapSelector: []byte{}, ChainID: 42161, SupportsFlashSwaps: true, RequiresApproval: true, MaxHops: 3, DefaultSlippagePercent: 0.5, Url: "https://sushi.com", ApiUrl: "https://api.sushi.com", } er.exchanges[math.ExchangeSushiSwap] = sushiSwapConfig // Load Curve curveConfig := &ExchangeConfig{ Type: math.ExchangeCurve, Name: "Curve", FactoryAddress: common.HexToAddress("0xb1748c7970d18f7d0ebf0f89e1c3f3b98e65e8ad"), RouterAddress: common.HexToAddress("0x4d7c19704623c38f54ec0d144c0ac63d0b76106e"), PoolInitCodeHash: "", SwapSelector: []byte{0x5b, 0x40, 0x2d, 0x3c}, // exchange StableSwapSelector: []byte{0x79, 0x1a, 0xc9, 0x47}, // exchange_underlying ChainID: 42161, SupportsFlashSwaps: false, RequiresApproval: true, MaxHops: 2, DefaultSlippagePercent: 0.1, Url: "https://curve.fi", ApiUrl: "https://api.curve.fi", } er.exchanges[math.ExchangeCurve] = curveConfig // Load Balancer balancerConfig := &ExchangeConfig{ Type: math.ExchangeBalancer, Name: "Balancer", FactoryAddress: common.HexToAddress("0xb33c178f5e23f3abf72a0dbfe955f1e69f2e47a6"), RouterAddress: common.HexToAddress("0xba12222222228d8ba445958a75a0704d566bf2c8"), PoolInitCodeHash: "", SwapSelector: []byte{0x3e, 0x2, 0x76, 0x5e}, // swap StableSwapSelector: []byte{}, ChainID: 42161, SupportsFlashSwaps: true, RequiresApproval: true, MaxHops: 3, DefaultSlippagePercent: 0.3, Url: "https://balancer.fi", ApiUrl: "https://api.balancer.fi", } er.exchanges[math.ExchangeBalancer] = balancerConfig return nil } // GetExchangeByType returns an exchange configuration by type func (er *ExchangeRegistry) GetExchangeByType(exchangeType math.ExchangeType) *ExchangeConfig { config, exists := er.exchanges[exchangeType] if !exists { return nil } return config } // GetExchangesForPair returns exchanges that support a token pair func (er *ExchangeRegistry) GetExchangesForPair(token0, token1 common.Address) []*ExchangeConfig { var supported []*ExchangeConfig for _, config := range er.exchanges { // In a real implementation, this would check if pools exist for this pair // For now, we'll assume all exchanges support all pairs for testing supported = append(supported, config) } return supported } // GetAllExchanges returns all registered exchanges func (er *ExchangeRegistry) GetAllExchanges() []*ExchangeConfig { var configs []*ExchangeConfig for _, config := range er.exchanges { configs = append(configs, config) } return configs } // GetAllExchangesMap returns all registered exchanges as a map func (er *ExchangeRegistry) GetAllExchangesMap() map[math.ExchangeType]*ExchangeConfig { return er.exchanges } // GetHighPriorityTokens returns high-priority tokens for scanning func (er *ExchangeRegistry) GetHighPriorityTokens(limit int) []TokenInfo { // Define high-priority tokens (ETH, USDC, USDT, WBTC, etc.) // CRITICAL FIX: Use correct Arbitrum token addresses (not Ethereum/other chains) highPriorityTokens := []TokenInfo{ {Address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", Symbol: "WETH", Name: "Wrapped Ether", Decimals: 18}, {Address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", Symbol: "USDC", Name: "USD Coin", Decimals: 6}, // FIXED: Correct Arbitrum USDC {Address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", Symbol: "USDT", Name: "Tether USD", Decimals: 6}, {Address: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", Symbol: "WBTC", Name: "Wrapped BTC", Decimals: 8}, // FIXED: Correct Arbitrum WBTC {Address: "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", Symbol: "LINK", Name: "ChainLink Token", Decimals: 18}, // FIXED: Correct Arbitrum LINK {Address: "0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978", Symbol: "CRV", Name: "Curve DAO Token", Decimals: 18}, // FIXED: Correct Arbitrum CRV {Address: "0x912CE59144191C1204E64559FE8253a0e49E6548", Symbol: "ARB", Name: "Arbitrum", Decimals: 18}, // ADDED: Arbitrum native token {Address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", Symbol: "DAI", Name: "Dai Stablecoin", Decimals: 18}, // ADDED: DAI {Address: "0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a", Symbol: "GMX", Name: "GMX", Decimals: 18}, // ADDED: GMX {Address: "0x9623063377AD1B27544C965cCd7342f7EA7e88C7", Symbol: "GRT", Name: "The Graph", Decimals: 18}, // ADDED: GRT } if limit > len(highPriorityTokens) { limit = len(highPriorityTokens) } return highPriorityTokens[:limit] } // IsPairSupported checks if a token pair is supported by any exchange func (er *ExchangeRegistry) IsPairSupported(token0, token1 common.Address) bool { supportedExchanges := er.GetExchangesForPair(token0, token1) return len(supportedExchanges) > 0 } // ArbitragePath represents a simple arbitrage path for the exchanges package type ArbitragePath struct { TokenIn common.Address TokenOut common.Address Exchanges []math.ExchangeType Pools []common.Address } // GetPoolDetector returns the pool detector for a specific exchange type func (er *ExchangeRegistry) GetPoolDetector(exchangeType math.ExchangeType) PoolDetector { return er.poolDetectors[exchangeType] } // GetLiquidityFetcher returns the liquidity fetcher for a specific exchange type func (er *ExchangeRegistry) GetLiquidityFetcher(exchangeType math.ExchangeType) LiquidityFetcher { return er.liquidityFetchers[exchangeType] } // GetSwapRouter returns the swap router for a specific exchange type func (er *ExchangeRegistry) GetSwapRouter(exchangeType math.ExchangeType) SwapRouter { return er.swapRouters[exchangeType] } // FindAllPaths finds all possible arbitrage paths between two tokens func (er *ExchangeRegistry) FindAllPaths(tokenA, tokenB common.Address, maxHops int) ([]*ArbitragePath, error) { // Simplified implementation for compilation - would be enhanced with actual path finding paths := []*ArbitragePath{} // For now, create a simple direct path if er.IsPairSupported(tokenA, tokenB) { path := &ArbitragePath{ TokenIn: tokenA, TokenOut: tokenB, Exchanges: []math.ExchangeType{math.ExchangeUniswapV3}, Pools: []common.Address{}, // Would be populated with actual pool addresses } paths = append(paths, path) } return paths, nil } // RegisterExchangeComponents registers pool detector, liquidity fetcher, and swap router for an exchange func (er *ExchangeRegistry) RegisterExchangeComponents( exchangeType math.ExchangeType, poolDetector PoolDetector, liquidityFetcher LiquidityFetcher, swapRouter SwapRouter, ) { if er.poolDetectors == nil { er.poolDetectors = make(map[math.ExchangeType]PoolDetector) } if er.liquidityFetchers == nil { er.liquidityFetchers = make(map[math.ExchangeType]LiquidityFetcher) } if er.swapRouters == nil { er.swapRouters = make(map[math.ExchangeType]SwapRouter) } er.poolDetectors[exchangeType] = poolDetector er.liquidityFetchers[exchangeType] = liquidityFetcher er.swapRouters[exchangeType] = swapRouter } // InitializeExchangeComponents initializes all exchange components for an arbitrum chain func (er *ExchangeRegistry) InitializeExchangeComponents(engine *math.ExchangePricingEngine) error { if er.client == nil { return fmt.Errorf("ethclient is required to initialize exchange components") } // Initialize Uniswap V2 components uniswapV2PoolDetector := NewUniswapV2PoolDetector(er.client, er.logger, er.exchanges[math.ExchangeUniswapV2]) uniswapV2LiquidityFetcher := NewUniswapV2LiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeUniswapV2], engine) uniswapV2Router := NewUniswapV2SwapRouter(er.client, er.logger, er.exchanges[math.ExchangeUniswapV2], engine) er.RegisterExchangeComponents(math.ExchangeUniswapV2, uniswapV2PoolDetector, uniswapV2LiquidityFetcher, uniswapV2Router) // Initialize SushiSwap components (similar to Uniswap V2) sushiPoolDetector := NewSushiSwapPoolDetector(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap]) sushiLiquidityFetcher := NewSushiSwapLiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap], engine) sushiRouter := NewSushiSwapSwapRouter(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap], engine) er.RegisterExchangeComponents(math.ExchangeSushiSwap, sushiPoolDetector, sushiLiquidityFetcher, sushiRouter) // Initialize Curve components curvePoolDetector := NewCurvePoolDetector(er.client, er.logger, er.exchanges[math.ExchangeCurve]) curveLiquidityFetcher := NewCurveLiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeCurve], engine) curveRouter := NewCurveSwapRouter(er.client, er.logger, er.exchanges[math.ExchangeCurve], engine) er.RegisterExchangeComponents(math.ExchangeCurve, curvePoolDetector, curveLiquidityFetcher, curveRouter) // Initialize Balancer components balancerPoolDetector := NewBalancerPoolDetector(er.client, er.logger, er.exchanges[math.ExchangeBalancer]) balancerLiquidityFetcher := NewBalancerLiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeBalancer], engine) balancerRouter := NewBalancerSwapRouter(er.client, er.logger, er.exchanges[math.ExchangeBalancer], engine) er.RegisterExchangeComponents(math.ExchangeBalancer, balancerPoolDetector, balancerLiquidityFetcher, balancerRouter) // Initialize PancakeSwap components pancakePoolDetector := NewPancakeSwapPoolDetector(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap]) pancakeLiquidityFetcher := NewPancakeSwapLiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap], engine) pancakeRouter := NewPancakeSwapSwapRouter(er.client, er.logger, er.exchanges[math.ExchangeSushiSwap], engine) er.RegisterExchangeComponents(math.ExchangeSushiSwap, pancakePoolDetector, pancakeLiquidityFetcher, pancakeRouter) // Initialize Kyber components kyberPoolDetector := NewKyberPoolDetector(er.client, er.logger, er.exchanges[math.ExchangeKyber]) kyberLiquidityFetcher := NewKyberLiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeKyber], engine) kyberRouter := NewKyberSwapRouter(er.client, er.logger, er.exchanges[math.ExchangeKyber], engine) er.RegisterExchangeComponents(math.ExchangeKyber, kyberPoolDetector, kyberLiquidityFetcher, kyberRouter) // Initialize Uniswap V4 components uniswapV4PoolDetector := NewUniswapV4PoolDetector(er.client, er.logger, er.exchanges[math.ExchangeUniswapV4]) uniswapV4LiquidityFetcher := NewUniswapV4LiquidityFetcher(er.client, er.logger, er.exchanges[math.ExchangeUniswapV4], engine) uniswapV4Router := NewUniswapV4SwapRouter(er.client, er.logger, er.exchanges[math.ExchangeUniswapV4], engine) er.RegisterExchangeComponents(math.ExchangeUniswapV4, uniswapV4PoolDetector, uniswapV4LiquidityFetcher, uniswapV4Router) return nil }