package types import ( "math/big" "github.com/ethereum/go-ethereum/common" ) // PoolInfo contains comprehensive pool information type PoolInfo struct { // Pool identification Address common.Address Protocol ProtocolType PoolType string // e.g., "constant-product", "stable", "weighted" // Token information Token0 common.Address Token1 common.Address Token0Decimals uint8 Token1Decimals uint8 Token0Symbol string Token1Symbol string // Reserves/Liquidity Reserve0 *big.Int Reserve1 *big.Int Liquidity *big.Int // Fee information Fee uint32 // Fee in basis points (e.g., 30 = 0.3%) FeeGrowth0 *big.Int // UniswapV3 fee growth FeeGrowth1 *big.Int // UniswapV3 fee growth // Protocol-specific data SqrtPriceX96 *big.Int // UniswapV3/Camelot V3 Tick *int32 // UniswapV3/Camelot V3 TickSpacing int32 // UniswapV3/Camelot V3 AmpCoefficient *big.Int // Curve amplification coefficient // Pool state IsActive bool BlockNumber uint64 LastUpdate uint64 } // Validate checks if the pool info is valid func (p *PoolInfo) Validate() error { if p.Address == (common.Address{}) { return ErrInvalidPoolAddress } if p.Token0 == (common.Address{}) { return ErrInvalidToken0Address } if p.Token1 == (common.Address{}) { return ErrInvalidToken1Address } if p.Token0Decimals == 0 || p.Token0Decimals > 18 { return ErrInvalidToken0Decimals } if p.Token1Decimals == 0 || p.Token1Decimals > 18 { return ErrInvalidToken1Decimals } if p.Protocol == ProtocolUnknown { return ErrUnknownProtocol } return nil } // GetTokenPair returns the token pair as a sorted tuple func (p *PoolInfo) GetTokenPair() (common.Address, common.Address) { if p.Token0.Big().Cmp(p.Token1.Big()) < 0 { return p.Token0, p.Token1 } return p.Token1, p.Token0 } // CalculatePrice calculates the price of token0 in terms of token1 func (p *PoolInfo) CalculatePrice() *big.Float { if p.Reserve0 == nil || p.Reserve1 == nil || p.Reserve0.Sign() == 0 { return big.NewFloat(0) } // Scale reserves to 18 decimals for consistent calculation reserve0Scaled := ScaleToDecimals(p.Reserve0, p.Token0Decimals, 18) reserve1Scaled := ScaleToDecimals(p.Reserve1, p.Token1Decimals, 18) // Price = Reserve1 / Reserve0 reserve0Float := new(big.Float).SetInt(reserve0Scaled) reserve1Float := new(big.Float).SetInt(reserve1Scaled) price := new(big.Float).Quo(reserve1Float, reserve0Float) return price } // ScaleToDecimals scales an amount from one decimal precision to another func ScaleToDecimals(amount *big.Int, fromDecimals, toDecimals uint8) *big.Int { if fromDecimals == toDecimals { return new(big.Int).Set(amount) } if fromDecimals < toDecimals { // Scale up multiplier := new(big.Int).Exp( big.NewInt(10), big.NewInt(int64(toDecimals-fromDecimals)), nil, ) return new(big.Int).Mul(amount, multiplier) } // Scale down divisor := new(big.Int).Exp( big.NewInt(10), big.NewInt(int64(fromDecimals-toDecimals)), nil, ) return new(big.Int).Div(amount, divisor) }