Files
mev-beta/pkg/uniswap/precision_test.go
2025-10-04 09:31:02 -05:00

181 lines
4.6 KiB
Go

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)
}
}