Files
mev-beta/pkg/exchanges/sushiswap.go
Krypto Kajun 850223a953 fix(multicall): resolve critical multicall parsing corruption issues
- Added comprehensive bounds checking to prevent buffer overruns in multicall parsing
- Implemented graduated validation system (Strict/Moderate/Permissive) to reduce false positives
- Added LRU caching system for address validation with 10-minute TTL
- Enhanced ABI decoder with missing Universal Router and Arbitrum-specific DEX signatures
- Fixed duplicate function declarations and import conflicts across multiple files
- Added error recovery mechanisms with multiple fallback strategies
- Updated tests to handle new validation behavior for suspicious addresses
- Fixed parser test expectations for improved validation system
- Applied gofmt formatting fixes to ensure code style compliance
- Fixed mutex copying issues in monitoring package by introducing MetricsSnapshot
- Resolved critical security vulnerabilities in heuristic address extraction
- Progress: Updated TODO audit from 10% to 35% complete

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 00:12:55 -05:00

262 lines
9.0 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"
)
// SushiSwapPoolDetector implements PoolDetector for SushiSwap
type SushiSwapPoolDetector struct {
client *ethclient.Client
logger *logger.Logger
config *ExchangeConfig
}
// NewSushiSwapPoolDetector creates a new SushiSwap pool detector
func NewSushiSwapPoolDetector(client *ethclient.Client, logger *logger.Logger, config *ExchangeConfig) *SushiSwapPoolDetector {
return &SushiSwapPoolDetector{
client: client,
logger: logger,
config: config,
}
}
// GetAllPools returns all pools containing the specified tokens
func (d *SushiSwapPoolDetector) GetAllPools(token0, token1 common.Address) ([]common.Address, error) {
// In a real implementation, this would query the factory contract
// For now, we'll return an empty slice
return []common.Address{}, nil
}
// GetPoolForPair returns the pool address for a specific token pair
func (d *SushiSwapPoolDetector) GetPoolForPair(token0, token1 common.Address) (common.Address, error) {
// Calculate pool address using SushiSwap factory formula (same as Uniswap V2)
// In a real implementation, this would call the factory's getPair function
poolAddress := common.HexToAddress("0x0") // Placeholder
// For now, return empty address to indicate pool not found
return poolAddress, nil
}
// GetSupportedFeeTiers returns supported fee tiers for SushiSwap (standard 0.3%)
func (d *SushiSwapPoolDetector) GetSupportedFeeTiers() []int64 {
return []int64{3000} // 0.3% in basis points
}
// GetPoolType returns the pool type
func (d *SushiSwapPoolDetector) GetPoolType() string {
return "sushiswap_constant_product"
}
// SushiSwapLiquidityFetcher implements LiquidityFetcher for SushiSwap
type SushiSwapLiquidityFetcher struct {
client *ethclient.Client
logger *logger.Logger
config *ExchangeConfig
engine *math.ExchangePricingEngine
}
// NewSushiSwapLiquidityFetcher creates a new SushiSwap liquidity fetcher
func NewSushiSwapLiquidityFetcher(client *ethclient.Client, logger *logger.Logger, config *ExchangeConfig, engine *math.ExchangePricingEngine) *SushiSwapLiquidityFetcher {
return &SushiSwapLiquidityFetcher{
client: client,
logger: logger,
config: config,
engine: engine,
}
}
// GetPoolData fetches pool information for SushiSwap
func (f *SushiSwapLiquidityFetcher) GetPoolData(poolAddress common.Address) (*math.PoolData, error) {
// In a real implementation, this would call the pool contract to get reserves
// For now, return a placeholder pool data
fee, err := math.NewUniversalDecimal(big.NewInt(250), 4, "FEE")
if err != nil {
return nil, fmt.Errorf("error creating fee decimal: %w", err)
}
reserve0, err := math.NewUniversalDecimal(big.NewInt(1000000), 18, "RESERVE0")
if err != nil {
return nil, fmt.Errorf("error creating reserve0 decimal: %w", err)
}
reserve1, err := math.NewUniversalDecimal(big.NewInt(1000000), 18, "RESERVE1")
if err != nil {
return nil, fmt.Errorf("error creating reserve1 decimal: %w", err)
}
return &math.PoolData{
Address: poolAddress.Hex(),
ExchangeType: math.ExchangeSushiSwap,
Fee: fee,
Token0: math.TokenInfo{Address: "0x0", Symbol: "TOKEN0", Decimals: 18},
Token1: math.TokenInfo{Address: "0x1", Symbol: "TOKEN1", Decimals: 18},
Reserve0: reserve0,
Reserve1: reserve1,
}, nil
}
// GetTokenReserves fetches reserves for a specific token pair in a pool
func (f *SushiSwapLiquidityFetcher) GetTokenReserves(poolAddress, token0, token1 common.Address) (*big.Int, *big.Int, error) {
// In a real implementation, this would query the pool contract
// For now, return placeholder values
return big.NewInt(1000000), big.NewInt(1000000), nil
}
// GetPoolPrice calculates the price of token1 in terms of token0
func (f *SushiSwapLiquidityFetcher) GetPoolPrice(poolAddress common.Address) (*big.Float, error) {
poolData, err := f.GetPoolData(poolAddress)
if err != nil {
return nil, err
}
pricer, err := f.engine.GetExchangePricer(poolData.ExchangeType)
if err != nil {
return nil, err
}
spotPrice, err := pricer.GetSpotPrice(poolData)
if err != nil {
return nil, err
}
// Convert the UniversalDecimal Value to a *big.Float
result := new(big.Float).SetInt(spotPrice.Value)
return result, nil
}
// GetLiquidityDepth calculates the liquidity depth for an amount
func (f *SushiSwapLiquidityFetcher) GetLiquidityDepth(poolAddress, tokenIn common.Address, amount *big.Int) (*big.Int, error) {
// In a real implementation, this would calculate how much of the token
// can be swapped before the price impact becomes too large
return amount, nil
}
// SushiSwapSwapRouter implements SwapRouter for SushiSwap
type SushiSwapSwapRouter struct {
client *ethclient.Client
logger *logger.Logger
config *ExchangeConfig
engine *math.ExchangePricingEngine
}
// NewSushiSwapSwapRouter creates a new SushiSwap swap router
func NewSushiSwapSwapRouter(client *ethclient.Client, logger *logger.Logger, config *ExchangeConfig, engine *math.ExchangePricingEngine) *SushiSwapSwapRouter {
return &SushiSwapSwapRouter{
client: client,
logger: logger,
config: config,
engine: engine,
}
}
// CalculateSwap calculates the expected output amount for a swap
func (r *SushiSwapSwapRouter) CalculateSwap(tokenIn, tokenOut common.Address, amountIn *big.Int) (*big.Int, error) {
// Find pool for the token pair
poolAddress, err := r.findPoolForPair(tokenIn, tokenOut)
if err != nil {
return nil, fmt.Errorf("failed to find pool for pair: %w", err)
}
// Get pool data
poolData, err := r.GetPoolData(poolAddress)
if err != nil {
return nil, fmt.Errorf("failed to get pool data: %w", err)
}
// Create a UniversalDecimal from the amountIn
decimalAmountIn, err := math.NewUniversalDecimal(amountIn, 18, "AMOUNT_IN")
if err != nil {
return nil, fmt.Errorf("error creating amount in decimal: %w", err)
}
// Get the pricer
pricer, err := r.engine.GetExchangePricer(poolData.ExchangeType)
if err != nil {
return nil, err
}
// Calculate amount out
amountOut, err := pricer.CalculateAmountOut(decimalAmountIn, poolData)
if err != nil {
return nil, err
}
return amountOut.Value, nil
}
// findPoolForPair finds the pool address for a given token pair
func (r *SushiSwapSwapRouter) findPoolForPair(token0, token1 common.Address) (common.Address, error) {
// In a real implementation, this would query the factory contract
// For now, return a placeholder address
return common.HexToAddress("0x0"), nil
}
// GetPoolData is a helper to fetch pool data (for internal use)
func (r *SushiSwapSwapRouter) GetPoolData(poolAddress common.Address) (*math.PoolData, error) {
fetcher := NewSushiSwapLiquidityFetcher(r.client, r.logger, r.config, r.engine)
return fetcher.GetPoolData(poolAddress)
}
// GenerateSwapData generates the calldata for a swap transaction
func (r *SushiSwapSwapRouter) GenerateSwapData(tokenIn, tokenOut common.Address, amountIn, minAmountOut *big.Int, deadline *big.Int) ([]byte, error) {
// In a real implementation, this would generate the encoded function call
// For SushiSwap, this would typically be swapExactTokensForTokens
return []byte{}, nil
}
// GetSwapRoute returns the route for a swap (for SushiSwap, this is typically direct)
func (r *SushiSwapSwapRouter) GetSwapRoute(tokenIn, tokenOut common.Address) ([]common.Address, error) {
// SushiSwap typically requires a direct swap
return []common.Address{tokenIn, tokenOut}, nil
}
// ValidateSwap validates a swap before execution
func (r *SushiSwapSwapRouter) ValidateSwap(tokenIn, tokenOut common.Address, amountIn *big.Int) error {
if amountIn.Sign() <= 0 {
return fmt.Errorf("amountIn must be positive")
}
if tokenIn == tokenOut {
return fmt.Errorf("tokenIn and tokenOut cannot be the same")
}
if tokenIn == common.HexToAddress("0x0") || tokenOut == common.HexToAddress("0x0") {
return fmt.Errorf("invalid token addresses")
}
return nil
}
// RegisterSushiSwapWithRegistry registers SushiSwap implementation with the exchange registry
func RegisterSushiSwapWithRegistry(registry *ExchangeRegistry) error {
config := &ExchangeConfig{
Type: math.ExchangeSushiSwap,
Name: "SushiSwap",
FactoryAddress: common.HexToAddress("0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac"), // SushiSwap Factory on mainnet
RouterAddress: common.HexToAddress("0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F"), // SushiSwap Router on mainnet
PoolInitCodeHash: "0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303",
SwapSelector: []byte{0x18, 0x2d, 0x2e, 0xdb}, // swapExactTokensForTokens
StableSwapSelector: []byte{},
ChainID: 1, // Ethereum mainnet
SupportsFlashSwaps: true,
RequiresApproval: true,
MaxHops: 3,
DefaultSlippagePercent: 0.5,
Url: "https://sushi.com",
ApiUrl: "https://api.sushi.com",
}
registry.exchanges[math.ExchangeSushiSwap] = config
// Register the implementations as well
return nil
}