package uniswap import ( "math/big" "testing" ) // TestPricePrecisionRoundTrip verifies precision of price conversions func TestPricePrecisionRoundTrip(t *testing.T) { testCases := []struct { name string sqrtPriceX96 string expectedTick int }{ { name: "Price 1.0001 (tick 1)", sqrtPriceX96: "79232123823823952808969600", // Approximately 1.0001^0.5 * 2^96 expectedTick: 1, }, { name: "Price 1.0 (tick 0)", sqrtPriceX96: "79228162514264337593543950336", // 2^96 expectedTick: 0, }, { name: "High price test", sqrtPriceX96: "1267650600228229401496703205376", // High price expectedTick: 23027, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Parse input sqrtPriceX96, ok := new(big.Int).SetString(tc.sqrtPriceX96, 10) if !ok { t.Fatalf("Failed to parse sqrtPriceX96: %s", tc.sqrtPriceX96) } // Convert to tick calculatedTick := SqrtPriceX96ToTick(sqrtPriceX96) // Convert back to sqrtPriceX96 roundTripSqrtPrice := TickToSqrtPriceX96(calculatedTick) // Calculate precision loss originalFloat := new(big.Float).SetInt(sqrtPriceX96) roundTripFloat := new(big.Float).SetInt(roundTripSqrtPrice) // Calculate percentage difference diff := new(big.Float).Sub(originalFloat, roundTripFloat) diff.Quo(diff, originalFloat) diff.Abs(diff) precisionLoss, _ := diff.Float64() t.Logf("Original sqrtPriceX96: %s", sqrtPriceX96.String()) t.Logf("Calculated tick: %d", calculatedTick) t.Logf("Round-trip sqrtPriceX96: %s", roundTripSqrtPrice.String()) t.Logf("Precision loss: %.10f%%", precisionLoss*100) // Verify tick is within reasonable range if calculatedTick < -887272 || calculatedTick > 887272 { t.Errorf("Tick %d is outside valid range [-887272, 887272]", calculatedTick) } // Verify precision loss is acceptable (less than 0.01%) if precisionLoss > 0.0001 { t.Errorf("Precision loss %.10f%% exceeds threshold 0.01%%", precisionLoss*100) } }) } } // TestPriceConversionAccuracy tests specific known price conversions func TestPriceConversionAccuracy(t *testing.T) { testCases := []struct { name string sqrtPriceX96 string expectedPrice float64 tolerance float64 }{ { name: "Price 1.0", sqrtPriceX96: "79228162514264337593543950336", // 2^96 expectedPrice: 1.0, tolerance: 0.000001, }, { name: "Price 4.0", sqrtPriceX96: "158456325028528675187087900672", // 2 * 2^96 expectedPrice: 4.0, tolerance: 0.000001, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { sqrtPriceX96, ok := new(big.Int).SetString(tc.sqrtPriceX96, 10) if !ok { t.Fatalf("Failed to parse sqrtPriceX96: %s", tc.sqrtPriceX96) } price := SqrtPriceX96ToPrice(sqrtPriceX96) priceFloat, _ := price.Float64() diff := priceFloat - tc.expectedPrice if diff < 0 { diff = -diff } t.Logf("Calculated price: %.10f", priceFloat) t.Logf("Expected price: %.10f", tc.expectedPrice) t.Logf("Difference: %.10f", diff) if diff > tc.tolerance { t.Errorf("Price difference %.10f exceeds tolerance %.10f", diff, tc.tolerance) } }) } } // TestTickBoundaries verifies tick calculations at boundaries func TestTickBoundaries(t *testing.T) { testCases := []struct { name string tick int }{ {"Minimum tick", -887272}, {"Maximum tick", 887272}, {"Zero tick", 0}, {"Positive tick", 100000}, {"Negative tick", -100000}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Convert tick to sqrtPriceX96 sqrtPriceX96 := TickToSqrtPriceX96(tc.tick) // Convert back to tick roundTripTick := SqrtPriceX96ToTick(sqrtPriceX96) // Calculate tick difference tickDiff := tc.tick - roundTripTick if tickDiff < 0 { tickDiff = -tickDiff } t.Logf("Original tick: %d", tc.tick) t.Logf("Round-trip tick: %d", roundTripTick) t.Logf("Tick difference: %d", tickDiff) // Verify tick difference is within acceptable range if tickDiff > 1 { t.Errorf("Tick difference %d exceeds threshold 1", tickDiff) } }) } } // BenchmarkSqrtPriceConversion benchmarks price conversion performance func BenchmarkSqrtPriceConversion(b *testing.B) { sqrtPriceX96, _ := new(big.Int).SetString("79228162514264337593543950336", 10) b.ResetTimer() for i := 0; i < b.N; i++ { SqrtPriceX96ToPrice(sqrtPriceX96) } } // BenchmarkTickConversion benchmarks tick conversion performance func BenchmarkTickConversion(b *testing.B) { tick := 100000 b.ResetTimer() for i := 0; i < b.N; i++ { TickToSqrtPriceX96(tick) } }