package math import ( "fmt" "math" "math/big" ) // ExchangeMath provides exchange-specific mathematical calculations type ExchangeMath interface { CalculateAmountOut(amountIn, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) CalculateAmountIn(amountOut, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) CalculatePriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) GetSpotPrice(reserveIn, reserveOut *big.Int) (*big.Float, error) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) } // UniswapV2Math implements Uniswap V2 constant product formula type UniswapV2Math struct{} // UniswapV3Math implements Uniswap V3 concentrated liquidity math type UniswapV3Math struct{} // CurveMath implements Curve Finance StableSwap math type CurveMath struct{} // BalancerMath implements Balancer weighted pool math type BalancerMath struct{} // ConstantSumMath implements basic constant sum AMM math type ConstantSumMath struct{} // ========== Uniswap V2 Math ========== // NewUniswapV2Math creates a new Uniswap V2 math calculator func NewUniswapV2Math() *UniswapV2Math { return &UniswapV2Math{} } // CalculateAmountOut calculates output amount for Uniswap V2 (x * y = k) func (u *UniswapV2Math) CalculateAmountOut(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: amountIn=%s, reserveIn=%s, reserveOut=%s", amountIn.String(), reserveIn.String(), reserveOut.String()) } // Calculate fee (default 3000 = 0.3%) if fee == 0 { fee = 3000 } // amountInWithFee = amountIn * (10000 - fee) feeFactor := big.NewInt(int64(10000 - fee)) amountInWithFee := new(big.Int).Mul(amountIn, feeFactor) // numerator = amountInWithFee * reserveOut numerator := new(big.Int).Mul(amountInWithFee, reserveOut) // denominator = reserveIn * 10000 + amountInWithFee denominator := new(big.Int).Mul(reserveIn, big.NewInt(10000)) denominator.Add(denominator, amountInWithFee) // Check for division by zero if denominator.Sign() == 0 { return nil, fmt.Errorf("division by zero in amountOut calculation") } // amountOut = numerator / denominator amountOut := new(big.Int).Div(numerator, denominator) return amountOut, nil } // CalculateAmountIn calculates input amount for Uniswap V2 func (u *UniswapV2Math) CalculateAmountIn(amountOut, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) { if amountOut.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } if amountOut.Cmp(reserveOut) >= 0 { return nil, fmt.Errorf("insufficient liquidity") } if fee == 0 { fee = 3000 } // numerator = reserveIn * amountOut * 10000 numerator := new(big.Int).Mul(reserveIn, amountOut) numerator.Mul(numerator, big.NewInt(10000)) // denominator = (reserveOut - amountOut) * (10000 - fee) denominator := new(big.Int).Sub(reserveOut, amountOut) // Check if the calculation is valid (amountOut must be less than reserveOut) if denominator.Sign() <= 0 { return nil, fmt.Errorf("invalid swap: amountOut (%s) >= reserveOut (%s)", amountOut.String(), reserveOut.String()) } denominator.Mul(denominator, big.NewInt(int64(10000-fee))) // Check for division by zero if denominator.Sign() == 0 { return nil, fmt.Errorf("division by zero in amountIn calculation") } // amountIn = numerator / denominator + 1 (round up) amountIn := new(big.Int).Div(numerator, denominator) amountIn.Add(amountIn, big.NewInt(1)) return amountIn, nil } // CalculatePriceImpact calculates price impact for Uniswap V2 func (u *UniswapV2Math) CalculatePriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { // Check for nil pointers first if amountIn == nil || reserveIn == nil || reserveOut == nil { return 0, fmt.Errorf("nil pointer encountered") } if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Price before = reserveOut / reserveIn priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) // Calculate amount out amountOut, err := u.CalculateAmountOut(amountIn, reserveIn, reserveOut, 3000) if err != nil { return 0, err } // New reserves after swap newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Price after = newReserveOut / newReserveIn priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Price impact = (priceBefore - priceAfter) / priceBefore impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // GetSpotPrice returns current spot price for Uniswap V2 func (u *UniswapV2Math) GetSpotPrice(reserveIn, reserveOut *big.Int) (*big.Float, error) { if reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid reserves") } return new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)), nil } // CalculateSlippage calculates slippage between expected and actual output func (u *UniswapV2Math) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Slippage = (expectedOut - actualOut) / expectedOut diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Uniswap V3 Math ========== // NewUniswapV3Math creates a new Uniswap V3 math calculator func NewUniswapV3Math() *UniswapV3Math { return &UniswapV3Math{} } // CalculateAmountOut calculates output for Uniswap V3 concentrated liquidity func (u *UniswapV3Math) CalculateAmountOut(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") } if fee == 0 { fee = 3000 // Default 0.3% } // 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)) // Simplified V3 calculation (for exact implementation, need tick math) // This approximates the swap for small amounts // Calculate price change Q96 := new(big.Int).Lsh(big.NewInt(1), 96) priceChange := new(big.Int).Mul(amountInWithFee, Q96) priceChange.Div(priceChange, liquidity) // New sqrt price newSqrtPriceX96 := new(big.Int).Add(sqrtPriceX96, priceChange) // Calculate amount out using price difference priceDiff := new(big.Int).Sub(newSqrtPriceX96, sqrtPriceX96) amountOut := new(big.Int).Mul(liquidity, priceDiff) amountOut.Div(amountOut, sqrtPriceX96) return amountOut, nil } // CalculateAmountIn calculates input for Uniswap V3 func (u *UniswapV3Math) CalculateAmountIn(amountOut, sqrtPriceX96, liquidity *big.Int, fee uint32) (*big.Int, error) { if amountOut.Sign() <= 0 || sqrtPriceX96.Sign() <= 0 || liquidity.Sign() <= 0 { return nil, fmt.Errorf("invalid parameters") } // Simplified reverse calculation Q96 := new(big.Int).Lsh(big.NewInt(1), 96) // Calculate required price change priceChange := new(big.Int).Mul(amountOut, sqrtPriceX96) priceChange.Div(priceChange, liquidity) // Calculate amount in before fees amountInBeforeFee := new(big.Int).Mul(priceChange, liquidity) amountInBeforeFee.Div(amountInBeforeFee, Q96) // Apply fee if fee == 0 { fee = 3000 } feeFactor := big.NewInt(int64(1000000 - fee)) amountIn := new(big.Int).Mul(amountInBeforeFee, big.NewInt(1000000)) amountIn.Div(amountIn, feeFactor) return amountIn, nil } // CalculatePriceImpact calculates price impact for Uniswap V3 func (u *UniswapV3Math) CalculatePriceImpact(amountIn, sqrtPriceX96, liquidity *big.Int) (float64, error) { // Check for nil pointers first if amountIn == nil || sqrtPriceX96 == nil || liquidity == nil { return 0, fmt.Errorf("nil pointer encountered") } if amountIn.Sign() <= 0 || sqrtPriceX96.Sign() <= 0 || liquidity.Sign() <= 0 { return 0, fmt.Errorf("invalid parameters") } // Calculate new sqrt price after swap Q96 := new(big.Int).Lsh(big.NewInt(1), 96) priceChange := new(big.Int).Mul(amountIn, Q96) priceChange.Div(priceChange, liquidity) newSqrtPriceX96 := new(big.Int).Add(sqrtPriceX96, priceChange) // Convert to regular prices using big.Float for precision Q96Float := new(big.Float).SetInt(Q96) // priceBefore = (sqrtPriceX96 / 2^96)^2 priceBefore := new(big.Float).SetInt(sqrtPriceX96) priceBefore.Quo(priceBefore, Q96Float) priceBefore.Mul(priceBefore, priceBefore) // priceAfter = (newSqrtPriceX96 / 2^96)^2 priceAfter := new(big.Float).SetInt(newSqrtPriceX96) priceAfter.Quo(priceAfter, Q96Float) priceAfter.Mul(priceAfter, priceAfter) // Check if priceBefore is zero or very small if priceBefore.Sign() == 0 { return 0, fmt.Errorf("price before is zero - invalid calculation") } // Check if priceBefore is too small (less than 1e-18) minPrice := big.NewFloat(1e-18) if priceBefore.Cmp(minPrice) < 0 { return 0, fmt.Errorf("price too small for reliable calculation") } // Calculate impact = (priceAfter - priceBefore) / priceBefore impact := new(big.Float).Sub(priceAfter, priceBefore) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // GetSpotPrice returns current spot price for Uniswap V3 func (u *UniswapV3Math) GetSpotPrice(sqrtPriceX96, _ *big.Int) (*big.Float, error) { if sqrtPriceX96.Sign() <= 0 { return nil, fmt.Errorf("invalid sqrt price") } // Price = (sqrtPriceX96 / 2^96)^2 Q96 := new(big.Int).Lsh(big.NewInt(1), 96) price := new(big.Int).Mul(sqrtPriceX96, sqrtPriceX96) price.Div(price, new(big.Int).Mul(Q96, Q96)) return new(big.Float).SetInt(price), nil } // CalculateSlippage calculates slippage for Uniswap V3 func (u *UniswapV3Math) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Curve Finance Math ========== // NewCurveMath creates a new Curve math calculator func NewCurveMath() *CurveMath { return &CurveMath{} } // CalculateAmountOut calculates output for Curve StableSwap func (c *CurveMath) CalculateAmountOut(amountIn, balance0, balance1 *big.Int, fee uint32) (*big.Int, error) { if amountIn.Sign() <= 0 || balance0.Sign() <= 0 || balance1.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } // Simplified Curve calculation (A = 100 for stable pools) A := big.NewInt(100) // Calculate D (total deposit) D := c.calculateD(balance0, balance1, A) // New balance after adding amountIn newBalance0 := new(big.Int).Add(balance0, amountIn) // Calculate new balance1 using Curve formula newBalance1 := c.getY(newBalance0, D, A) // Amount out = balance1 - newBalance1 amountOut := new(big.Int).Sub(balance1, newBalance1) // Apply fee if fee == 0 { fee = 400 // Default 0.04% } feeAmount := new(big.Int).Mul(amountOut, big.NewInt(int64(fee))) feeAmount.Div(feeAmount, big.NewInt(1000000)) amountOut.Sub(amountOut, feeAmount) return amountOut, nil } // calculateD calculates the D invariant for Curve func (c *CurveMath) calculateD(balance0, balance1, A *big.Int) *big.Int { // Simplified D calculation for 2-coin pool // D = 2 * sqrt(x * y) for stable coins (approximation) sum := new(big.Int).Add(balance0, balance1) product := new(big.Int).Mul(balance0, balance1) // Newton's method approximation for D D := new(big.Int).Set(sum) for i := 0; i < 10; i++ { // 10 iterations for convergence // Calculate new D numerator := new(big.Int).Mul(product, big.NewInt(4)) denominator := new(big.Int).Mul(D, D) if denominator.Sign() == 0 { break } ratio := new(big.Int).Div(numerator, denominator) newD := new(big.Int).Add(D, ratio) newD.Div(newD, big.NewInt(2)) // Check convergence diff := new(big.Int).Sub(newD, D) if diff.CmpAbs(big.NewInt(1)) <= 0 { break } D = newD } return D } // getY calculates the new balance using Curve formula func (c *CurveMath) getY(newX, D, A *big.Int) *big.Int { // Simplified calculation for 2-coin pool // Solve for y in the Curve invariant equation // For stable coins, approximately: y = D - x y := new(big.Int).Sub(D, newX) // Ensure positive result if y.Sign() <= 0 { y = big.NewInt(1) } return y } // CalculateAmountIn calculates input for Curve func (c *CurveMath) CalculateAmountIn(amountOut, balance0, balance1 *big.Int, fee uint32) (*big.Int, error) { // Reverse calculation - simplified A := big.NewInt(100) D := c.calculateD(balance0, balance1, A) // Calculate new balance1 after removing amountOut newBalance1 := new(big.Int).Sub(balance1, amountOut) // Calculate required balance0 newBalance0 := c.getY(newBalance1, D, A) // Amount in = newBalance0 - balance0 amountIn := new(big.Int).Sub(newBalance0, balance0) // Apply fee if fee == 0 { fee = 400 } feeMultiplier := big.NewInt(int64(1000000 + fee)) amountIn.Mul(amountIn, feeMultiplier) amountIn.Div(amountIn, big.NewInt(1000000)) return amountIn, nil } // CalculatePriceImpact calculates price impact for Curve func (c *CurveMath) CalculatePriceImpact(amountIn, balance0, balance1 *big.Int) (float64, error) { // Check for nil pointers first if amountIn == nil || balance0 == nil || balance1 == nil { return 0, fmt.Errorf("nil pointer encountered") } if amountIn.Sign() <= 0 || balance0.Sign() <= 0 || balance1.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Price before = balance1 / balance0 priceBefore := new(big.Float).Quo(new(big.Float).SetInt(balance1), new(big.Float).SetInt(balance0)) // Calculate amount out amountOut, err := c.CalculateAmountOut(amountIn, balance0, balance1, 400) if err != nil { return 0, err } // New balances newBalance0 := new(big.Int).Add(balance0, amountIn) newBalance1 := new(big.Int).Sub(balance1, amountOut) // Price after priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newBalance1), new(big.Float).SetInt(newBalance0)) // Calculate impact impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // GetSpotPrice returns current spot price for Curve func (c *CurveMath) GetSpotPrice(balance0, balance1 *big.Int) (*big.Float, error) { if balance0.Sign() <= 0 || balance1.Sign() <= 0 { return nil, fmt.Errorf("invalid balances") } return new(big.Float).Quo(new(big.Float).SetInt(balance1), new(big.Float).SetInt(balance0)), nil } // CalculateSlippage calculates slippage for Curve func (c *CurveMath) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Kyber Math ========== // CalculateAmountOut calculates output for Kyber Elastic func (k *KyberMath) CalculateAmountOut(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 } // CalculateAmountIn calculates input for Kyber Elastic func (k *KyberMath) CalculateAmountIn(amountOut, sqrtPriceX96, liquidity *big.Int, fee uint32) (*big.Int, error) { if amountOut.Sign() <= 0 || sqrtPriceX96.Sign() <= 0 || liquidity.Sign() <= 0 { return nil, fmt.Errorf("invalid parameters") } if fee == 0 { fee = 1000 } // Calculate required price change Q96 := new(big.Int).Lsh(big.NewInt(1), 96) priceChange := new(big.Int).Mul(amountOut, sqrtPriceX96) priceChange.Div(priceChange, liquidity) // Calculate amount in before fees amountInBeforeFee := new(big.Int).Mul(priceChange, liquidity) amountInBeforeFee.Div(amountInBeforeFee, Q96) // Apply fee feeFactor := big.NewInt(int64(1000000 - fee)) amountIn := new(big.Int).Mul(amountInBeforeFee, big.NewInt(1000000)) amountIn.Div(amountIn, feeFactor) return amountIn, nil } // CalculatePriceImpact calculates price impact for Kyber func (k *KyberMath) CalculatePriceImpact(amountIn, sqrtPriceX96, liquidity *big.Int) (float64, error) { if amountIn.Sign() <= 0 || sqrtPriceX96.Sign() <= 0 || liquidity.Sign() <= 0 { return 0, fmt.Errorf("invalid parameters") } // Calculate new sqrt price after swap Q96 := new(big.Int).Lsh(big.NewInt(1), 96) priceChange := new(big.Int).Mul(amountIn, Q96) priceChange.Div(priceChange, liquidity) newSqrtPriceX96 := new(big.Int).Add(sqrtPriceX96, priceChange) // Convert to regular prices using big.Float for precision Q96Float := new(big.Float).SetInt(Q96) // priceBefore = (sqrtPriceX96 / 2^96)^2 priceBefore := new(big.Float).SetInt(sqrtPriceX96) priceBefore.Quo(priceBefore, Q96Float) priceBefore.Mul(priceBefore, priceBefore) // priceAfter = (newSqrtPriceX96 / 2^96)^2 priceAfter := new(big.Float).SetInt(newSqrtPriceX96) priceAfter.Quo(priceAfter, Q96Float) priceAfter.Mul(priceAfter, priceAfter) // Check if priceBefore is zero or very small if priceBefore.Sign() == 0 { return 0, fmt.Errorf("price before is zero - invalid calculation") } // Check if priceBefore is too small (less than 1e-18) minPrice := big.NewFloat(1e-18) if priceBefore.Cmp(minPrice) < 0 { return 0, fmt.Errorf("price too small for reliable calculation") } // Calculate impact = (priceAfter - priceBefore) / priceBefore impact := new(big.Float).Sub(priceAfter, priceBefore) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // GetSpotPrice returns current spot price for Kyber func (k *KyberMath) GetSpotPrice(sqrtPriceX96, _ *big.Int) (*big.Float, error) { if sqrtPriceX96.Sign() <= 0 { return nil, fmt.Errorf("invalid sqrt price") } // Price = (sqrtPriceX96 / 2^96)^2 Q96 := new(big.Int).Lsh(big.NewInt(1), 96) price := new(big.Int).Mul(sqrtPriceX96, sqrtPriceX96) price.Div(price, new(big.Int).Mul(Q96, Q96)) return new(big.Float).SetInt(price), nil } // CalculateSlippage calculates slippage for Kyber func (k *KyberMath) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Balancer Math ========== // CalculateAmountOut calculates output for Balancer weighted pools func (b *BalancerMath) CalculateAmountOut(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") } // For Balancer, we use weighted pool formula // amountOut = reserveOut * (1 - (reserveIn / (reserveIn + amountIn * feeFactor))^weightRatio) if fee == 0 { fee = 1000 // Default 0.1% fee } // Calculate fee factor feeFactor := float64(10000-fee) / 10000.0 // Calculate effective input amount after fee amountInWithFee := new(big.Float).SetInt(amountIn) amountInWithFee.Mul(amountInWithFee, big.NewFloat(feeFactor)) // Convert to big.Float for precise calculations reserveInFloat := new(big.Float).SetInt(reserveIn) reserveOutFloat := new(big.Float).SetInt(reserveOut) // Calculate numerator: reserveIn + (amountIn * feeFactor) numerator := new(big.Float).Add(reserveInFloat, amountInWithFee) // Calculate ratio: reserveIn / numerator ratio := new(big.Float).Quo(reserveInFloat, numerator) // For equal weights (simplified approach) // Calculate amountOut = reserveOut * (1 - ratio) denominatorFloat := new(big.Float).SetInt(big.NewInt(1)) result := new(big.Float).Sub(denominatorFloat, ratio) result.Mul(reserveOutFloat, result) // Convert back to big.Int amountOut := new(big.Int) result.Int(amountOut) return amountOut, nil } // CalculateAmountIn calculates input for Balancer weighted pools func (b *BalancerMath) CalculateAmountIn(amountOut, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) { if amountOut.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } if amountOut.Cmp(reserveOut) >= 0 { return nil, fmt.Errorf("insufficient liquidity") } if fee == 0 { fee = 1000 } // Calculate fee factor feeFactor := float64(10000) / float64(10000-fee) // Calculate reserveOut after swap newReserveOut := new(big.Int).Sub(reserveOut, amountOut) if newReserveOut.Sign() <= 0 { return nil, fmt.Errorf("insufficient liquidity") } // Calculate amountIn using weighted pool formula // Using simplified approach for equal weights reserveOutFloat := new(big.Float).SetInt(reserveOut) newReserveOutFloat := new(big.Float).SetInt(newReserveOut) // Calculate ratio: reserveOut / newReserveOut ratio := new(big.Float).Quo(reserveOutFloat, newReserveOutFloat) // For equal weights, we just take the ratio as is and subtract 1 denominatorFloat := new(big.Float).SetInt(big.NewInt(1)) result := new(big.Float).Sub(ratio, denominatorFloat) // Multiply by reserveIn reserveInFloat := new(big.Float).SetInt(reserveIn) result.Mul(reserveInFloat, result) // Adjust by fee factor result.Mul(result, big.NewFloat(feeFactor)) // Convert back to big.Int amountIn := new(big.Int) result.Int(amountIn) // Add 1 to ensure we have enough for the swap (rounding up) amountIn.Add(amountIn, big.NewInt(1)) return amountIn, nil } // CalculatePriceImpact calculates price impact for Balancer func (b *BalancerMath) CalculatePriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { // Check for nil pointers first if amountIn == nil || reserveIn == nil || reserveOut == nil { return 0, fmt.Errorf("nil pointer encountered") } if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Price before = reserveOut / reserveIn priceBefore := new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)) // Calculate amount out amountOut, err := b.CalculateAmountOut(amountIn, reserveIn, reserveOut, 1000) if err != nil { return 0, err } // New reserves after swap newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) // Price after = newReserveOut / newReserveIn priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn)) // Price impact = (priceBefore - priceAfter) / priceBefore impact := new(big.Float).Sub(priceBefore, priceAfter) impact.Quo(impact, priceBefore) impactFloat, _ := impact.Float64() return math.Abs(impactFloat), nil } // GetSpotPrice returns current spot price for Balancer func (b *BalancerMath) GetSpotPrice(reserveIn, reserveOut *big.Int) (*big.Float, error) { if reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid reserves") } return new(big.Float).Quo(new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn)), nil } // CalculateSlippage calculates slippage for Balancer func (b *BalancerMath) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Slippage = (expectedOut - actualOut) / expectedOut diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Constant Sum Math ========== // CalculateAmountOut calculates output for Constant Sum AMM (like Uniswap V1) func (c *ConstantSumMath) CalculateAmountOut(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") } // In constant sum, price is always 1:1 (ignoring fees) // amountOut = amountIn * (1 - fee) if fee == 0 { fee = 3000 // Default 0.3% fee } feeFactor := big.NewInt(int64(10000 - fee)) amountOut := new(big.Int).Mul(amountIn, feeFactor) amountOut.Div(amountOut, big.NewInt(10000)) return amountOut, nil } // CalculateAmountIn calculates input for Constant Sum AMM func (c *ConstantSumMath) CalculateAmountIn(amountOut, reserveIn, reserveOut *big.Int, fee uint32) (*big.Int, error) { if amountOut.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid amounts") } if fee == 0 { fee = 3000 } // amountIn = amountOut / (1 - fee) feeFactor := big.NewInt(int64(10000 - fee)) amountIn := new(big.Int).Mul(amountOut, big.NewInt(10000)) amountIn.Div(amountIn, feeFactor) return amountIn, nil } // CalculatePriceImpact calculates price impact for Constant Sum (should be 0) func (c *ConstantSumMath) CalculatePriceImpact(amountIn, reserveIn, reserveOut *big.Int) (float64, error) { if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // In constant sum, there is no price impact (ignoring fees) return 0, nil } // GetSpotPrice returns current spot price for Constant Sum (should be 1, ignoring fees) func (c *ConstantSumMath) GetSpotPrice(reserveIn, reserveOut *big.Int) (*big.Float, error) { if reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 { return nil, fmt.Errorf("invalid reserves") } // In constant sum, price is always 1:1 (ignoring fees) return big.NewFloat(1.0), nil } // CalculateSlippage calculates slippage for Constant Sum (same as fees) func (c *ConstantSumMath) CalculateSlippage(expectedOut, actualOut *big.Int) (float64, error) { if expectedOut.Sign() <= 0 || actualOut.Sign() <= 0 { return 0, fmt.Errorf("invalid amounts") } // Slippage = (expectedOut - actualOut) / expectedOut diff := new(big.Float).Sub(new(big.Float).SetInt(expectedOut), new(big.Float).SetInt(actualOut)) slippage := new(big.Float).Quo(diff, new(big.Float).SetInt(expectedOut)) slippageFloat, _ := slippage.Float64() return math.Abs(slippageFloat), nil } // ========== Math Factory ========== // MathCalculator provides a unified interface for all exchange math type MathCalculator struct { uniswapV2 *UniswapV2Math uniswapV3 *UniswapV3Math curve *CurveMath kyber *KyberMath balancer *BalancerMath constantSum *ConstantSumMath } // NewMathCalculator creates a new unified math calculator func NewMathCalculator() *MathCalculator { return &MathCalculator{ uniswapV2: NewUniswapV2Math(), uniswapV3: NewUniswapV3Math(), curve: NewCurveMath(), kyber: &KyberMath{}, balancer: &BalancerMath{}, constantSum: &ConstantSumMath{}, } } // GetMathForExchange returns the appropriate math calculator for an exchange func (mc *MathCalculator) GetMathForExchange(exchangeType string) ExchangeMath { switch exchangeType { case "uniswap_v2", "sushiswap": return mc.uniswapV2 case "uniswap_v3", "camelot_v3": return mc.uniswapV3 case "curve": return mc.curve case "kyber_elastic", "kyber_classic": return mc.kyber case "balancer": return mc.balancer case "constant_sum": return mc.constantSum default: return mc.uniswapV2 // Default fallback } } // CalculateOptimalArbitrage calculates optimal arbitrage between exchanges func (mc *MathCalculator) CalculateOptimalArbitrage( exchangeA, exchangeB string, reservesA, reservesB [2]*big.Int, feesA, feesB uint32, ) (*ArbitrageResult, error) { mathA := mc.GetMathForExchange(exchangeA) mathB := mc.GetMathForExchange(exchangeB) // Get spot prices priceA, err := mathA.GetSpotPrice(reservesA[0], reservesA[1]) if err != nil { return nil, err } priceB, err := mathB.GetSpotPrice(reservesB[0], reservesB[1]) if err != nil { return nil, err } // Calculate price difference priceDiff := new(big.Float).Sub(priceA, priceB) priceDiff.Quo(priceDiff, priceA) priceDiffFloat, _ := priceDiff.Float64() // Only proceed if price difference > 0.5% if math.Abs(priceDiffFloat) < 0.005 { return nil, fmt.Errorf("insufficient price difference: %f", priceDiffFloat) } // Find optimal amount using binary search optimalAmount := mc.findOptimalAmount(mathA, mathB, reservesA, reservesB, feesA, feesB) // Calculate expected profit amountOut1, _ := mathA.CalculateAmountOut(optimalAmount, reservesA[0], reservesA[1], feesA) amountOut2, _ := mathB.CalculateAmountIn(amountOut1, reservesB[1], reservesB[0], feesB) profit := new(big.Int).Sub(amountOut2, optimalAmount) return &ArbitrageResult{ AmountIn: optimalAmount, ExpectedProfit: profit, PriceDiff: priceDiffFloat, ExchangeA: exchangeA, ExchangeB: exchangeB, }, nil } // ArbitrageResult represents the result of arbitrage calculation type ArbitrageResult struct { AmountIn *big.Int ExpectedProfit *big.Int PriceDiff float64 ExchangeA string ExchangeB string } // findOptimalAmount uses binary search to find optimal arbitrage amount func (mc *MathCalculator) findOptimalAmount( mathA, mathB ExchangeMath, reservesA, reservesB [2]*big.Int, feesA, feesB uint32, ) *big.Int { // Binary search for optimal amount min := big.NewInt(1000000000000000) // 0.001 ETH max := new(big.Int).Div(reservesA[0], big.NewInt(10)) // 10% of reserve bestAmount := new(big.Int).Set(min) bestProfit := big.NewInt(0) for i := 0; i < 20; i++ { // 20 iterations mid := new(big.Int).Add(min, max) mid.Div(mid, big.NewInt(2)) // Calculate profit at this amount amountOut1, err1 := mathA.CalculateAmountOut(mid, reservesA[0], reservesA[1], feesA) if err1 != nil { max = mid continue } amountOut2, err2 := mathB.CalculateAmountIn(amountOut1, reservesB[1], reservesB[0], feesB) if err2 != nil { max = mid continue } profit := new(big.Int).Sub(amountOut2, mid) if profit.Cmp(bestProfit) > 0 { bestProfit = profit bestAmount = new(big.Int).Set(mid) } // Adjust search range if profit.Sign() > 0 { min = mid } else { max = mid } } return bestAmount }