Files
mev-beta/pkg/exchanges/exchanges.go
2025-11-08 10:37:52 -06:00

366 lines
16 KiB
Go

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
}