Files
mev-beta/orig/pkg/arbitrum/discovery/pool_state.go
Administrator 803de231ba feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor:

**Structure Changes:**
- Moved all V1 code to orig/ folder (preserved with git mv)
- Created docs/planning/ directory
- Added orig/README_V1.md explaining V1 preservation

**Planning Documents:**
- 00_V2_MASTER_PLAN.md: Complete architecture overview
  - Executive summary of critical V1 issues
  - High-level component architecture diagrams
  - 5-phase implementation roadmap
  - Success metrics and risk mitigation

- 07_TASK_BREAKDOWN.md: Atomic task breakdown
  - 99+ hours of detailed tasks
  - Every task < 2 hours (atomic)
  - Clear dependencies and success criteria
  - Organized by implementation phase

**V2 Key Improvements:**
- Per-exchange parsers (factory pattern)
- Multi-layer strict validation
- Multi-index pool cache
- Background validation pipeline
- Comprehensive observability

**Critical Issues Addressed:**
- Zero address tokens (strict validation + cache enrichment)
- Parsing accuracy (protocol-specific parsers)
- No audit trail (background validation channel)
- Inefficient lookups (multi-index cache)
- Stats disconnection (event-driven metrics)

Next Steps:
1. Review planning documents
2. Begin Phase 1: Foundation (P1-001 through P1-010)
3. Implement parsers in Phase 2
4. Build cache system in Phase 3
5. Add validation pipeline in Phase 4
6. Migrate and test in Phase 5

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:14:26 +01: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
}