package math import ( "fmt" "math" "math/big" "github.com/holiman/uint256" ) // UniswapV4Math implements Uniswap V4 mathematical calculations type UniswapV4Math struct{} // AlgebraV1Math implements Algebra V1.9 mathematical calculations type AlgebraV1Math struct{} // IntegralMath implements Integral mathematical calculations type IntegralMath struct{} // KyberMath implements Kyber mathematical calculations type KyberMath struct{} // OneInchMath implements 1Inch mathematical calculations type OneInchMath struct{} // ========== Uniswap V4 Math ========= // NewUniswapV4Math creates a new Uniswap V4 math calculator func NewUniswapV4Math() *UniswapV4Math { return &UniswapV4Math{} } // CalculateAmountOutV4 calculates output amount for Uniswap V4 // Uniswap V4 uses hooks and pre/post-swap hooks for additional functionality func (u *UniswapV4Math) CalculateAmountOutV4(amountIn, sqrtPriceX96, liquidity, currentTick, tickSpacing, fee uint256.Int) (*uint256.Int, error) { if amountIn.IsZero() || sqrtPriceX96.IsZero() || liquidity.IsZero() { return nil, fmt.Errorf("invalid parameters") } // For Uniswap V4, we reuse V3 calculations with hook considerations // In practice, V4 introduces hooks which can modify the calculation // This is a simplified implementation based on V3 // Apply fee: amountInWithFee = amountIn * (1000000 - fee) / 1000000 feeFactor := uint256.NewInt(1000000).Sub(uint256.NewInt(1000000), &fee) amountInWithFee := new(uint256.Int).Mul(&amountIn, feeFactor) amountInWithFee.Div(amountInWithFee, uint256.NewInt(1000000)) // Calculate price change using liquidity and amountIn Q96 := uint256.NewInt(1).Lsh(uint256.NewInt(1), 96) priceChange := new(uint256.Int).Mul(amountInWithFee, Q96) priceChange.Div(priceChange, &liquidity) // Calculate new sqrt price after swap newSqrtPriceX96 := new(uint256.Int).Add(&sqrtPriceX96, priceChange) // Calculate amount out based on price difference and liquidity priceDiff := new(uint256.Int).Sub(newSqrtPriceX96, &sqrtPriceX96) amountOut := new(uint256.Int).Mul(&liquidity, priceDiff) amountOut.Div(amountOut, &sqrtPriceX96) return amountOut, nil } // ========== Algebra V1.9 Math ========== // NewAlgebraV1Math creates a new Algebra V1.9 math calculator func NewAlgebraV1Math() *AlgebraV1Math { return &AlgebraV1Math{} } // CalculateAmountOutAlgebra calculates output amount for Algebra V1.9 func (a *AlgebraV1Math) CalculateAmountOutAlgebra(amountIn, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } // Algebra uses a dynamic fee model based on volatility if fee == 0 { fee = 500 // Default 0.05% for Algebra } // Calculate fee amount (10000 = 100%) feeFactor := big.NewInt(int64(10000 - fee)) amountInWithFee := new(big.Int).Mul(amountIn, feeFactor) // For Algebra, we also consider dynamic fees and volatility // This is a simplified implementation based on Uniswap V2 with dynamic fee consideration numerator := new(big.Int).Mul(amountInWithFee, reserveOut) denominator := new(big.Int).Mul(reserveIn, big.NewInt(10000)) denominator.Add(denominator, amountInWithFee) if denominator.Sign() == 0 { return nil, fmt.Errorf("division by zero in amountOut calculation") } amountOut := new(big.Int).Div(numerator, denominator) return amountOut, nil } // CalculatePriceImpactAlgebra calculates price impact for Algebra V1.9 func (a *AlgebraV1Math) CalculatePriceImpactAlgebra(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Calculate new reserves after swap amountOut, err := a.CalculateAmountOutAlgebra(amountIn, reserveIn, reserveOut, 500) if err != nil { return 0, err } newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Calculate price before and after swap priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Calculate price impact impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // ========== Integral Math ========== // NewIntegralMath creates a new Integral math calculator func NewIntegralMath() *IntegralMath { return &IntegralMath{} } // CalculateAmountOutIntegral calculates output for Integral with base fee model func (i *IntegralMath) CalculateAmountOutIntegral(amountIn, reserveIn, reserveOut *big.Int, baseFee uint32) (*big.Int, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } // Integral uses a base fee model for more efficient gas usage // Calculate effective fee based on base fee and market conditions if baseFee == 0 { baseFee = 100 // Default base fee of 0.01% } // For Integral, we implement the base fee model feeFactor := big.NewInt(int64(10000 - baseFee)) amountInWithFee := new(big.Int).Mul(amountIn, feeFactor) // Calculate amount out with base fee numerator := new(big.Int).Mul(amountInWithFee, reserveOut) denominator := new(big.Int).Mul(reserveIn, big.NewInt(10000)) denominator.Add(denominator, amountInWithFee) if denominator.Sign() == 0 { return nil, fmt.Errorf("division by zero in amountOut calculation") } amountOut := new(big.Int).Div(numerator, denominator) return amountOut, nil } // ========== Kyber Math ========== // NewKyberMath creates a new Kyber math calculator func NewKyberMath() *KyberMath { return &KyberMath{} } // CalculateAmountOutKyber calculates output for Kyber Elastic and Classic func (k *KyberMath) CalculateAmountOutKyber(amountIn, sqrtPriceX96, liquidity *big.Int, fee uint32) (*big.Int, error) { if amountIn.Sign() <= 0 || sqrtPriceX96.Sign() <= 0 || liquidity.Sign() <= 0 { return nil, fmt.Errorf("invalid parameters") } // Kyber Elastic uses concentrated liquidity similar to Uniswap V3 // but with different fee structures and mechanisms if fee == 0 { fee = 1000 // Default 0.1% for Kyber } // Apply fee: amountInWithFee = amountIn * (1000000 - fee) / 1000000 feeFactor := big.NewInt(int64(1000000 - fee)) amountInWithFee := new(big.Int).Mul(amountIn, feeFactor) amountInWithFee.Div(amountInWithFee, big.NewInt(1000000)) // Calculate price change using liquidity and amountIn Q96 := new(big.Int).Lsh(big.NewInt(1), 96) priceChange := new(big.Int).Mul(amountInWithFee, Q96) priceChange.Div(priceChange, liquidity) // Calculate new sqrt price after swap newSqrtPriceX96 := new(big.Int).Add(sqrtPriceX96, priceChange) // Calculate amount out based on price difference and liquidity priceDiff := new(big.Int).Sub(newSqrtPriceX96, sqrtPriceX96) amountOut := new(big.Int).Mul(liquidity, priceDiff) amountOut.Div(amountOut, sqrtPriceX96) return amountOut, nil } // CalculateAmountOutKyberClassic calculates output for Kyber Classic reserves func (k *KyberMath) CalculateAmountOutKyberClassic(amountIn, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } // Kyber Classic has a different mechanism than Elastic // This is a simplified implementation based on Kyber Classic formula if fee == 0 { fee = 2500 // Default 0.25% for Kyber Classic } // Calculate fee amount feeFactor := big.NewInt(int64(10000 - fee)) amountInWithFee := new(big.Int).Mul(amountIn, feeFactor) // Calculate amount out with consideration for Kyber's amplification factor numerator := new(big.Int).Mul(amountInWithFee, reserveOut) denominator := new(big.Int).Mul(reserveIn, big.NewInt(10000)) denominator.Add(denominator, amountInWithFee) if denominator.Sign() == 0 { return nil, fmt.Errorf("division by zero in amountOut calculation") } amountOut := new(big.Int).Div(numerator, denominator) return amountOut, nil } // ========== 1Inch Math ========== // NewOneInchMath creates a new 1Inch math calculator func NewOneInchMath() *OneInchMath { return &OneInchMath{} } // CalculateAmountOutOneInch calculates output for 1Inch aggregation func (o *OneInchMath) CalculateAmountOutOneInch(amountIn *big.Int, multiHopPath []PathElement) (*big.Int, error) { if amountIn.Sign() <= 0 { return nil, fmt.Errorf("invalid amountIn") } result := new(big.Int).Set(amountIn) // 1Inch aggregates multiple DEXs with different routing algorithms // This is a simplified multi-hop calculation for _, pathElement := range multiHopPath { var amountOut *big.Int var err error switch pathElement.Protocol { case "uniswap_v2": amountOut, err = NewUniswapV2Math().CalculateAmountOut(result, pathElement.ReserveIn, pathElement.ReserveOut, pathElement.Fee) case "uniswap_v3": amountOut, err = NewUniswapV3Math().CalculateAmountOut(result, pathElement.SqrtPriceX96, pathElement.Liquidity, pathElement.Fee) case "kyber_elastic", "kyber_classic": amountOut, err = NewKyberMath().CalculateAmountOut(result, pathElement.SqrtPriceX96, pathElement.Liquidity, pathElement.Fee) case "curve": amountOut, err = NewCurveMath().CalculateAmountOut(result, pathElement.ReserveIn, pathElement.ReserveOut, pathElement.Fee) default: return nil, fmt.Errorf("unsupported protocol: %s", pathElement.Protocol) } if err != nil { return nil, err } result = amountOut } return result, nil } // PathElement represents a single step in a multi-hop path type PathElement struct { Protocol string ReserveIn *big.Int ReserveOut *big.Int SqrtPriceX96 *big.Int Liquidity *big.Int Fee uint32 } // ========== Price Movement Detection Functions ========== // WillSwapMovePrice determines if a swap will significantly move the price of a pool func WillSwapMovePrice(amountIn, reserveIn, reserveOut *big.Int, threshold float64) (bool, float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return false, 0, fmt.Errorf("invalid parameters") } // Calculate price impact priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) // Calculate output for the proposed swap amountOut, err := NewUniswapV2Math().CalculateAmountOut(amountIn, reserveIn, reserveOut, 3000) if err != nil { return false, 0, err } newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Calculate price impact as percentage impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impact.Abs(impact) impactFloat, _ := impact.Float64() // Check if price impact exceeds threshold (e.g., 1%) movesPrice := impactFloat >= threshold return movesPrice, impactFloat, nil } // WillLiquidityMovePrice determines if a liquidity addition/removal will significantly move the price func WillLiquidityMovePrice(amount0, amount1, reserve0, reserve1 *big.Int, threshold float64) (bool, float64, error) { if reserve0.Sign() <= 0 || reserve1.Sign() <= 0 { return false, 0, fmt.Errorf("invalid reserves") } // Check if amounts are valid for the provided reserves if (amount0.Sign() < 0 && new(big.Int).Abs(amount0).Cmp(reserve0) > 0) || (amount1.Sign() < 0 && new(big.Int).Abs(amount1).Cmp(reserve1) > 0) { return false, 0, fmt.Errorf("removing more liquidity than available") } // Calculate price before liquidity change priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserve1), new(big.Float).SetInt(reserve0)) // Calculate new reserves after liquidity change newReserve0 := new(big.Int).Add(reserve0, amount0) newReserve1 := new(big.Int).Add(reserve1, amount1) // Ensure reserves don't go negative if newReserve0.Sign() <= 0 || newReserve1.Sign() <= 0 { return false, 0, fmt.Errorf("liquidity change would result in negative reserves") } priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserve1), new(big.Float).SetInt(newReserve0)) // Calculate price impact as percentage impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impact.Abs(impact) impactFloat, _ := impact.Float64() // Check if price impact exceeds threshold movesPrice := impactFloat >= threshold return movesPrice, impactFloat, nil } // CalculateRequiredAmountForPriceMove calculates how much would need to be swapped to move price by a certain percentage func CalculateRequiredAmountForPriceMove(targetPriceMove float64, reserveIn, reserveOut *big.Int) (*big.Int, error) { if targetPriceMove <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid parameters") } // This is a simplified calculation - in practice this would require more complex math // using binary search or other numerical methods // This is an estimation, for exact calculation, we'd need to use more sophisticated methods // such as binary search to find the exact amount required estimatedAmount := new(big.Int).Div(reserveIn, big.NewInt(100)) // 1% of reserve as estimation estimatedAmount.Mul(estimatedAmount, big.NewInt(int64(targetPriceMove*100))) return estimatedAmount, nil }