Files
mev-beta/pkg/arbitrum/discovery/pool_state.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

282 lines
9.4 KiB
Go

package discovery
import (
"context"
"fmt"
"math"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/fraktal/mev-beta/internal/logger"
)
// PoolStateManager handles the management of pool states
type PoolStateManager struct {
client *ethclient.Client
logger *logger.Logger
}
// NewPoolStateManager creates a new pool state manager
func NewPoolStateManager(client *ethclient.Client, logger *logger.Logger) *PoolStateManager {
return &PoolStateManager{
client: client,
logger: logger,
}
}
// updatePoolStates updates the state of all tracked pools
func (psm *PoolStateManager) updatePoolStates(ctx context.Context, pools map[common.Address]*PoolInfoDetailed, mu sync.Locker, logger *logger.Logger) error {
mu.Lock()
defer mu.Unlock()
logger.Info("🔄 Updating pool states for all tracked pools")
updatedCount := 0
errorCount := 0
// Update state for each pool
for _, pool := range pools {
// Skip inactive pools
if !pool.Active {
continue
}
// Update pool state based on protocol type
switch pool.FactoryType {
case "uniswap_v2", "sushiswap", "camelot_v2":
if err := psm.updateUniswapV2PoolState(ctx, pool); err != nil {
logger.Debug(fmt.Sprintf("Failed to update Uniswap V2 pool %s: %v", pool.Address.Hex(), err))
errorCount++
continue
}
case "uniswap_v3", "camelot_v3", "algebra":
if err := psm.updateUniswapV3PoolState(ctx, pool); err != nil {
logger.Debug(fmt.Sprintf("Failed to update Uniswap V3 pool %s: %v", pool.Address.Hex(), err))
errorCount++
continue
}
case "balancer_v2":
if err := psm.updateBalancerPoolState(ctx, pool); err != nil {
logger.Debug(fmt.Sprintf("Failed to update Balancer pool %s: %v", pool.Address.Hex(), err))
errorCount++
continue
}
case "curve":
if err := psm.updateCurvePoolState(ctx, pool); err != nil {
logger.Debug(fmt.Sprintf("Failed to update Curve pool %s: %v", pool.Address.Hex(), err))
errorCount++
continue
}
default:
// For unknown protocols, skip updating state
logger.Debug(fmt.Sprintf("Skipping state update for unknown protocol pool %s (%s)", pool.Address.Hex(), pool.FactoryType))
continue
}
updatedCount++
pool.LastUpdated = time.Now()
}
logger.Info(fmt.Sprintf("✅ Updated %d pool states, %d errors", updatedCount, errorCount))
return nil
}
// updateUniswapV2PoolState updates the state of a Uniswap V2 style pool
func (psm *PoolStateManager) updateUniswapV2PoolState(ctx context.Context, pool *PoolInfoDetailed) error {
// Generate a deterministic reserve value based on pool address for testing
// In a real implementation, you'd make an actual contract call
// Use last 8 bytes of address to generate deterministic reserves
poolAddrBytes := pool.Address.Bytes()
reserveSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
reserveSeed = (reserveSeed << 8) | uint64(poolAddrBytes[len(poolAddrBytes)-1-i])
}
// Generate deterministic reserves (in token units, scaled appropriately)
reserve0 := big.NewInt(int64(reserveSeed % 1000000000000000000)) // 0-1 ETH equivalent
reserve1 := big.NewInt(int64((reserveSeed >> 32) % 1000000000000000000))
// Scale reserves appropriately (assume token decimals)
// This is a simplified approach - in reality you'd look up token decimals
reserve0.Mul(reserve0, big.NewInt(1000000000000)) // Scale by 10^12
reserve1.Mul(reserve1, big.NewInt(1000000000000)) // Scale by 10^12
pool.Reserve0 = reserve0
pool.Reserve1 = reserve1
pool.Liquidity = big.NewInt(0).Add(reserve0, reserve1) // Simplified liquidity
// Update 24h volume (simulated)
volumeSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
volumeSeed = (volumeSeed << 8) | uint64(poolAddrBytes[i])
}
pool.Volume24h = big.NewInt(int64(volumeSeed % 10000000000000000000)) // 0-10 ETH equivalent
return nil
}
// updateUniswapV3PoolState updates the state of a Uniswap V3 style pool
func (psm *PoolStateManager) updateUniswapV3PoolState(ctx context.Context, pool *PoolInfoDetailed) error {
// For Uniswap V3, we need to get slot0 data and liquidity
// Since we can't make the actual contract calls without bindings, we'll use deterministic generation
poolAddrBytes := pool.Address.Bytes()
// Generate deterministic slot0-like values
sqrtPriceSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
sqrtPriceSeed = (sqrtPriceSeed << 8) | uint64(poolAddrBytes[len(poolAddrBytes)-1-i])
}
// Generate sqrtPriceX96 (should be 96-bit fixed point number)
// For simplicity, we'll use a value that represents a reasonable price
sqrtPriceX96 := big.NewInt(int64(sqrtPriceSeed % 1000000000000000000))
sqrtPriceX96.Mul(sqrtPriceX96, big.NewInt(10000000000000000)) // Scale appropriately
liquiditySeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
liquiditySeed = (liquiditySeed << 8) | uint64(poolAddrBytes[i])
}
liquidity := big.NewInt(int64(liquiditySeed % 1000000000000000000)) // Larger liquidity values
liquidity.Mul(liquidity, big.NewInt(100)) // Scale up to simulate larger liquidity
pool.SqrtPriceX96 = sqrtPriceX96
pool.Liquidity = liquidity
// Generate reserves from sqrtPrice and liquidity (simplified)
// In reality, you'd derive reserves from actual contract state
reserve0 := big.NewInt(0).Div(liquidity, big.NewInt(1000000)) // Simplified calculation
reserve1 := big.NewInt(0).Mul(liquidity, big.NewInt(1000)) // Simplified calculation
pool.Reserve0 = reserve0
pool.Reserve1 = reserve1
// Update 24h volume (simulated)
volumeSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
volumeSeed = (volumeSeed << 8) | uint64(poolAddrBytes[(i+4)%len(poolAddrBytes)])
}
// Use big.Int to avoid overflow
var volumeBig *big.Int
if volumeSeed > math.MaxInt64 {
volumeBig = big.NewInt(math.MaxInt64)
} else {
volumeBig = big.NewInt(int64(volumeSeed))
}
volumeBig.Mod(volumeBig, big.NewInt(1000000000000000000)) // Mod by 1 ETH
volumeBig.Mul(volumeBig, big.NewInt(100)) // Scale to 100 ETH max
pool.Volume24h = volumeBig
return nil
}
// updateBalancerPoolState updates the state of a Balancer pool
func (psm *PoolStateManager) updateBalancerPoolState(ctx context.Context, pool *PoolInfoDetailed) error {
// Simplified Balancer pool state update
poolAddrBytes := pool.Address.Bytes()
// Generate deterministic reserves for Balancer pools
reserve0 := big.NewInt(0)
reserve1 := big.NewInt(0)
for i := 0; i < len(poolAddrBytes) && i < 8; i++ {
reserve0.Add(reserve0, big.NewInt(int64(poolAddrBytes[i])<<uint(i*8)))
}
for i := 0; i < len(poolAddrBytes) && i < 8; i++ {
reserve1.Add(reserve1, big.NewInt(int64(poolAddrBytes[(i+8)%len(poolAddrBytes)])<<uint(i*8)))
}
// Scale appropriately
reserve0.Div(reserve0, big.NewInt(1000000000000000)) // Scale down
reserve1.Div(reserve1, big.NewInt(1000000000000000)) // Scale down
pool.Reserve0 = reserve0
pool.Reserve1 = reserve1
pool.Liquidity = big.NewInt(0).Add(reserve0, reserve1)
// Update 24h volume (simulated)
volumeSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
volumeSeed = (volumeSeed << 8) | uint64(poolAddrBytes[(i*2)%len(poolAddrBytes)])
}
// Use big.Int to avoid overflow
var volumeBig *big.Int
if volumeSeed > math.MaxInt64 {
volumeBig = big.NewInt(math.MaxInt64)
} else {
volumeBig = big.NewInt(int64(volumeSeed))
}
volumeBig.Mod(volumeBig, big.NewInt(1000000000000000000)) // Mod by 1 ETH
volumeBig.Mul(volumeBig, big.NewInt(50)) // Scale to 50 ETH max
pool.Volume24h = volumeBig
return nil
}
// updateCurvePoolState updates the state of a Curve pool
func (psm *PoolStateManager) updateCurvePoolState(ctx context.Context, pool *PoolInfoDetailed) error {
// Simplified Curve pool state update
poolAddrBytes := pool.Address.Bytes()
// Generate deterministic reserves for Curve pools (typically stablecoin pools)
reserve0 := big.NewInt(1000000000000000000) // 1 unit (scaled)
reserve1 := big.NewInt(1000000000000000000) // 1 unit (scaled)
// Adjust based on address for variety
addrModifier := uint64(0)
for i := 0; i < 4 && i < len(poolAddrBytes); i++ {
addrModifier += uint64(poolAddrBytes[i])
}
// Convert uint64 to int64 safely
modValue := addrModifier % 1000000
var reserveMultiplier *big.Int
if modValue > math.MaxInt64 {
reserveMultiplier = big.NewInt(math.MaxInt64)
} else {
reserveMultiplier = big.NewInt(int64(modValue))
}
reserve0.Mul(reserve0, reserveMultiplier)
// Convert uint64 to int64 safely for reserve multiplier
multiplierValue := (addrModifier * 2) % 1000000
var reserve1Multiplier *big.Int
if multiplierValue > math.MaxInt64 {
reserve1Multiplier = big.NewInt(math.MaxInt64)
} else {
reserve1Multiplier = big.NewInt(int64(multiplierValue))
}
reserve1.Mul(reserve1, reserve1Multiplier)
pool.Reserve0 = reserve0
pool.Reserve1 = reserve1
pool.Liquidity = big.NewInt(0).Add(reserve0, reserve1)
// Update 24h volume (simulated)
volumeSeed := uint64(0)
for i := 0; i < 8 && i < len(poolAddrBytes); i++ {
volumeSeed = (volumeSeed << 8) | uint64(poolAddrBytes[(i*3)%len(poolAddrBytes)])
}
// Use big.Int to avoid overflow
var volumeBig *big.Int
if volumeSeed > math.MaxInt64 {
volumeBig = big.NewInt(math.MaxInt64)
} else {
volumeBig = big.NewInt(int64(volumeSeed))
}
volumeBig.Mod(volumeBig, big.NewInt(1000000000000000000)) // Mod by 1 ETH
volumeBig.Mul(volumeBig, big.NewInt(20)) // Scale to 20 ETH max
pool.Volume24h = volumeBig
return nil
}