From 19eb3be05366db01ea84d69050b092157a0c82b5 Mon Sep 17 00:00:00 2001 From: 0xTopaz Date: Sun, 15 Dec 2024 17:04:16 +0900 Subject: [PATCH] refactor: Overflow check for type conversion to int256 in delta amount calculation --- _deploy/p/gnoswap/pool/consts.gno | 1 + _deploy/p/gnoswap/pool/sqrt_price_math.gno | 33 ++++++-- .../p/gnoswap/pool/sqrt_price_math_test.gno | 82 +++++++++++++++++++ 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/_deploy/p/gnoswap/pool/consts.gno b/_deploy/p/gnoswap/pool/consts.gno index 17acbe4ef..e8b0b33e1 100644 --- a/_deploy/p/gnoswap/pool/consts.gno +++ b/_deploy/p/gnoswap/pool/consts.gno @@ -8,6 +8,7 @@ const ( MAX_UINT128 string = "340282366920938463463374607431768211455" MAX_UINT160 string = "1461501637330902918203684832716283019655932542975" MAX_UINT256 string = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + MAX_INT256 string = "57896044618658097711785492504343953926634992332820282019728792003956564819967" Q64 string = "18446744073709551616" // 2 ** 64 Q96 string = "79228162514264337593543950336" // 2 ** 96 diff --git a/_deploy/p/gnoswap/pool/sqrt_price_math.gno b/_deploy/p/gnoswap/pool/sqrt_price_math.gno index 4d8ab4291..29b16feaf 100644 --- a/_deploy/p/gnoswap/pool/sqrt_price_math.gno +++ b/_deploy/p/gnoswap/pool/sqrt_price_math.gno @@ -325,7 +325,10 @@ func sqrtPriceMathGetAmount1DeltaHelper( // // Notes: // - This function relies on the helper function `sqrtPriceMathGetAmount0DeltaHelper` to perform the core calculation. -// - The rounding behavior of the result is controlled by the `roundUp` parameter passed to the helper function: +// - The helper function calculates the absolute difference between token0 amounts within the range. +// - If the computed result exceeds the maximum allowable value for int256 (2**255 - 1), the function will panic +// with an appropriate overflow error. +// - The rounding behavior of the result is controlled by the `roundUp` parameter passed to the helper function: // - For negative liquidity, rounding is always down. // - For positive liquidity, rounding is always up. func SqrtPriceMathGetAmount0DeltaStr( @@ -335,12 +338,20 @@ func SqrtPriceMathGetAmount0DeltaStr( ) string { if liquidity.IsNeg() { u := sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), false) + if u.Gt(u256.MustFromDecimal(MAX_INT256)) { + // if u > (2**255 - 1), cannot cast to int256 + panic("SqrtPriceMathGetAmount0DeltaStr: overflow") + } i := i256.FromUint256(u) return i256.Zero().Neg(i).ToString() + } else { + u := sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true) + if u.Gt(u256.MustFromDecimal(MAX_INT256)) { + // if u > (2**255 - 1), cannot cast to int256 + panic("SqrtPriceMathGetAmount0DeltaStr: overflow") + } + return i256.FromUint256(u).ToString() } - - u := sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true) - return i256.FromUint256(u).ToString() } // SqrtPriceMathGetAmount1DeltaStr calculates the difference in the amount of token1 @@ -370,10 +381,18 @@ func SqrtPriceMathGetAmount1DeltaStr( ) string { if liquidity.IsNeg() { u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), false) + if u.Gt(u256.MustFromDecimal(MAX_INT256)) { + // if u > (2**255 - 1), cannot cast to int256 + panic("SqrtPriceMathGetAmount1DeltaStr: overflow") + } i := i256.FromUint256(u) return i256.Zero().Neg(i).ToString() + } else { + u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true) + if u.Gt(u256.MustFromDecimal(MAX_INT256)) { + // if u > (2**255 - 1), cannot cast to int256 + panic("SqrtPriceMathGetAmount1DeltaStr: overflow") + } + return i256.FromUint256(u).ToString() } - - u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true) - return i256.FromUint256(u).ToString() } diff --git a/_deploy/p/gnoswap/pool/sqrt_price_math_test.gno b/_deploy/p/gnoswap/pool/sqrt_price_math_test.gno index 927e00c98..24f450d51 100644 --- a/_deploy/p/gnoswap/pool/sqrt_price_math_test.gno +++ b/_deploy/p/gnoswap/pool/sqrt_price_math_test.gno @@ -88,6 +88,41 @@ func TestSqrtPriceMathGetAmount0DeltaStr(t *testing.T) { t.Error("Result should be negative for negative liquidity") } }) + + t.Run("panic overflow when getting amount0 with positive liquidity", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for overflow amount0") + } else { + uassert.Equal(t, "SqrtPriceMathGetAmount0DeltaStr: overflow", r) + } + }() + + // Inputs to trigger panic + sqrtRatioAX96 := u256.MustFromDecimal("1") // very low value + sqrtRatioBX96 := u256.MustFromDecimal("340282366920938463463374607431768211455") // very high value(2^128-1) + liquidity := i256.FromUint256(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")) + + SqrtPriceMathGetAmount0DeltaStr(sqrtRatioAX96, sqrtRatioBX96, liquidity) + }) + + t.Run("panic overflow when getting amount0 with negative liquidity", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for overflow amount0") + } else { + uassert.Equal(t, "SqrtPriceMathGetAmount0DeltaStr: overflow", r) + } + }() + + // Inputs to trigger panic + sqrtRatioAX96 := u256.MustFromDecimal("1") // very low value + sqrtRatioBX96 := u256.MustFromDecimal("340282366920938463463374607431768211455") // very high value(2^128-1) + liquidity := i256.FromUint256(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")) + liquidity = liquidity.Neg(liquidity) // Make liquidity negative + + SqrtPriceMathGetAmount0DeltaStr(sqrtRatioAX96, sqrtRatioBX96, liquidity) + }) } func TestSqrtPriceMathGetAmount1DeltaStr(t *testing.T) { @@ -102,6 +137,53 @@ func TestSqrtPriceMathGetAmount1DeltaStr(t *testing.T) { t.Error("Result should be positive for positive liquidity") } }) + + t.Run("negative liquidity", func(t *testing.T) { + ratioA := u256.MustFromDecimal("1000000") + ratioB := u256.MustFromDecimal("2000000") + liquidity := i256.Zero().Neg(i256.FromUint256(u256.MustFromDecimal("5000000"))) + + result := SqrtPriceMathGetAmount0DeltaStr(ratioA, ratioB, liquidity) + + if result[0] != '-' { + t.Error("Result should be negative for negative liquidity") + } + }) + + t.Run("panic overflow when getting amount1 with positive liquidity", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for overflow amount1") + } else { + uassert.Equal(t, "SqrtPriceMathGetAmount1DeltaStr: overflow", r) + } + }() + + // Inputs to trigger panic + sqrtRatioAX96 := u256.MustFromDecimal("1") // very low value + sqrtRatioBX96 := u256.MustFromDecimal("79228162514264337593543950335") // slightly below Q96 + liquidity := i256.FromUint256(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")) + + SqrtPriceMathGetAmount1DeltaStr(sqrtRatioAX96, sqrtRatioBX96, liquidity) + }) + + t.Run("panic overflow when getting amount1 with negative liquidity", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for overflow amount1") + } else { + uassert.Equal(t, "SqrtPriceMathGetAmount1DeltaStr: overflow", r) + } + }() + + // Inputs to trigger panic + sqrtRatioAX96 := u256.MustFromDecimal("1") // very low value + sqrtRatioBX96 := u256.MustFromDecimal("79228162514264337593543950335") // slightly below Q96 + liquidity := i256.FromUint256(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")) + liquidity = liquidity.Neg(liquidity) // Make liquidity negative + + SqrtPriceMathGetAmount1DeltaStr(sqrtRatioAX96, sqrtRatioBX96, liquidity) + }) } func TestSqrtPriceMathGetNextSqrtPriceFromInput(t *testing.T) {