Skip to content

Commit

Permalink
Merge pull request #216 from gnoswap-labs/GSW-1045-update-uint-256-ov…
Browse files Browse the repository at this point in the history
…erflow-calculation-logic

GSW-1045 #2: fix test cases + rounding error
  • Loading branch information
notJoon authored May 3, 2024
2 parents ae01c8f + abc2119 commit 1ede86b
Show file tree
Hide file tree
Showing 44 changed files with 2,746 additions and 413 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Clone the `gno` repository from `gnoswap-labs`. And switch to the `master_202404
$ cd $WORKDIR
$ git clone https://github.com/gnoswap-labs/gno.git gno
$ cd gno
$ git checkout master_20240401
$ git checkout master_gs
$ make install
```

Expand All @@ -42,17 +42,17 @@ To run unit tests, follow these steps:

```
# copy grc20 tokens
$ cp -R __local/grc20_tokens/* $WORK_DIR/gno/examples/gno.land/r/demo
$ cp -R _deploy/r/demo/* $WORK_DIR/gno/examples/gno.land/r/demo
$ cp -R __local/grc20_tokens/* $WORKDIR/gno/examples/gno.land/r/demo
$ cp -R _deploy/r/demo/* $WORKDIR/gno/examples/gno.land/r/demo
# copy gnoswap base packages ( includes uint256, int256 and bit of pool calculation )
$ cp -R _deploy/p/demo/gnoswap $WORK_DIR/gno/examples/gno.land/p/demo
$ cp -R _deploy/p/demo/gnoswap $WORKDIR/gno/examples/gno.land/p/demo
# copy gnoswap base realms ( includes common logic, variables and consts )
$ cp -R _deploy/r/gnoswap $WORK_DIR/gno/examples/gno.land/r/gnoswap
$ cp -R _deploy/r/demo/gnoswap $WORKDIR/gno/examples/gno.land/r/demo/gnoswap
# copy gnoswap realms
$ cp -R gov pool position router staker $WORK_DIR/gno/examples/gno.land/r/demo
$ cp -R gov pool position router staker $WORKDIR/gno/examples/gno.land/r/demo
```

3. Move all test cases into its own directory:
Expand Down Expand Up @@ -82,7 +82,8 @@ To run unit tests, follow these steps:
### Run the Test Cases

```
$ gno test -root-dir $WORKDIR/gno-for-swap -verbose=true $WORKDIR/gno/examples/gno.land/r/demo/{CONTRACT_FOLDER_HERE}
$ cd $WORKDIR
$ gno test -root-dir $WORKDIR/gno -v=true $WORKDIR/gno/examples/gno.land/r/demo/{CONTRACT_FOLDER_HERE}
```

## Integration Tests
Expand Down
202 changes: 102 additions & 100 deletions __local/test/test_data.mk

Large diffs are not rendered by default.

169 changes: 85 additions & 84 deletions _deploy/p/demo/gnoswap/pool/sqrt_price_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,81 @@ package pool
import (
i256 "gno.land/p/demo/gnoswap/int256"
u256 "gno.land/p/demo/gnoswap/uint256"

"gno.land/r/demo/gnoswap/consts"
)

func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(
sqrtPX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint128
amount *u256.Uint, // uint256
add bool,
) *u256.Uint { // uint160
if amount.IsZero() {
return sqrtPX96
}

numerator1 := new(u256.Uint).Lsh(liquidity, 96)
product := new(u256.Uint).Mul(amount, sqrtPX96)

if add {
if new(u256.Uint).Div(product, amount).Eq(sqrtPX96) {
denominator := new(u256.Uint).Add(numerator1, product)

if denominator.Gte(numerator1) {
return u256.MulDivRoundingUp(numerator1, sqrtPX96, denominator)
}
}

div := new(u256.Uint).Div(numerator1, sqrtPX96)
add := new(u256.Uint).Add(div, amount)
return u256.DivRoundingUp(numerator1, add)
} else {
cond1 := new(u256.Uint).Div(product, amount).Eq(sqrtPX96)
cond2 := numerator1.Gt(product)

if !(cond1 && cond2) {
panic("pool_sqrt price math #1")
}

denominator := new(u256.Uint).Sub(numerator1, product)
return u256.MulDivRoundingUp(numerator1, sqrtPX96, denominator)
}
}

func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(
sqrtPX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint1288
amount *u256.Uint, // uint256
add bool,
) *u256.Uint { // uint160
quotient := u256.Zero()

if add {
if amount.Lte(u256.MustFromDecimal(MAX_UINT160)) {
value1 := new(u256.Uint).Lsh(amount, 96)
quotient = new(u256.Uint).Div(value1, liquidity)
} else {
quotient = u256.MulDiv(amount, u256.MustFromDecimal(consts.Q96), liquidity)
}

return new(u256.Uint).Add(sqrtPX96, quotient)
} else {
if amount.Lte(u256.MustFromDecimal(MAX_UINT160)) {
value1 := new(u256.Uint).Lsh(amount, 96)
quotient = u256.DivRoundingUp(value1, liquidity)
} else {
quotient = u256.MulDivRoundingUp(amount, u256.MustFromDecimal(consts.Q96), liquidity)
}

if !(sqrtPX96.Gt(quotient)) {
panic("pool_sqrt price math #2")
}

return new(u256.Uint).Sub(sqrtPX96, quotient)
}
}

func sqrtPriceMathGetNextSqrtPriceFromInput(
sqrtPX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint128
Expand Down Expand Up @@ -40,18 +113,21 @@ func sqrtPriceMathGetAmount0DeltaHelper(
sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96
}

numerator1 := new(u256.Uint).Lsh(liquidity, 96)
numerator2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

if !(sqrtRatioAX96.Gt(u256.Zero())) {
panic("pool_sqrt price math #3")
}

numerator1 := new(u256.Uint).Lsh(liquidity, 96)
numerator2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)
value := u256.MulDiv(numerator1, numerator2, sqrtRatioBX96)

if roundUp {
return u256.DivRoundingUp(value, sqrtRatioAX96)
value1 := u256.MulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96)
return u256.DivRoundingUp(value1, sqrtRatioAX96)
} else {
return new(u256.Uint).Div(value, sqrtRatioAX96)
value1 := u256.MulDiv(numerator1, numerator2, sqrtRatioBX96)
return new(u256.Uint).Div(value1, sqrtRatioAX96)
}
}

Expand All @@ -65,12 +141,12 @@ func sqrtPriceMathGetAmount1DeltaHelper(
sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96
}

value := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

if roundUp {
return u256.MulDiv(liquidity, value, u256.MustFromDecimal(Q96))
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)
return u256.MulDivRoundingUp(liquidity, diff, u256.MustFromDecimal(consts.Q96))
} else {
return u256.MulDiv(liquidity, value, u256.MustFromDecimal(Q96))
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)
return u256.MulDiv(liquidity, diff, u256.MustFromDecimal(consts.Q96))
}
}

Expand All @@ -79,12 +155,12 @@ func SqrtPriceMathGetAmount0DeltaStr(
sqrtRatioBX96 *u256.Uint, // uint160
liquidity *i256.Int, // int128
) string { // int256

if liquidity.IsNeg() {
u := sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), false)
i := i256.FromUint256(u)
return i256.Zero().Neg(i).ToString()
}

u := sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true)
return i256.FromUint256(u).ToString()
}
Expand All @@ -103,78 +179,3 @@ func SqrtPriceMathGetAmount1DeltaStr(
u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true)
return i256.FromUint256(u).ToString()
}

// private
func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(
sqrtPX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint128
amount *u256.Uint, // uint256
add bool,
) *u256.Uint { // uint160
if amount.IsZero() {
return sqrtPX96
}

numerator1 := new(u256.Uint).Lsh(liquidity, 96)
product := new(u256.Uint).Mul(amount, sqrtPX96)

if add {
if new(u256.Uint).Div(product, amount).Eq(sqrtPX96) {
denominator := new(u256.Uint).Add(numerator1, product)

if denominator.Gte(numerator1) {
return u256.MulDiv(numerator1, sqrtPX96, denominator)
}
}

_value1 := new(u256.Uint).Div(numerator1, sqrtPX96)
_value2 := new(u256.Uint).Add(_value1, amount)
return u256.DivRoundingUp(numerator1, _value2)
} else {
cond1 := new(u256.Uint).Div(product, amount).Eq(sqrtPX96)
cond2 := numerator1.Gt(product)

if !(cond1 && cond2) {
panic("pool_sqrt price math #1")
}
denominator := new(u256.Uint).Sub(numerator1, product)

return u256.MulDiv(numerator1, sqrtPX96, denominator)
}
}

func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(
sqrtPX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint1288
amount *u256.Uint, // uint256
add bool,
) *u256.Uint { // uint160
quotient := u256.Zero()

if !(amount.Lte(u256.MustFromDecimal(MAX_UINT160))) {
quotient = u256.MulDiv(amount, u256.MustFromDecimal(Q96), liquidity)
}

if add {
if amount.Lte(u256.MustFromDecimal(MAX_UINT160)) {
quotient = new(u256.Uint).Lsh(amount, 96)
quotient = new(u256.Uint).Div(quotient, liquidity)
}

quotient = new(u256.Uint).Sub(quotient, u256.One())
return new(u256.Uint).Add(sqrtPX96, quotient)
} else {
if amount.Lte(u256.MustFromDecimal(MAX_UINT160)) {
quotient = new(u256.Uint).Lsh(amount, 96)
quotient = u256.DivRoundingUp(quotient, liquidity)
}

if !(sqrtPX96.Gt(quotient)) {
panic("pool_sqrt price math #2")
}

// roundDown
quotient = new(u256.Uint).Sub(quotient, u256.One())
return new(u256.Uint).Sub(sqrtPX96, quotient)
}
}
11 changes: 2 additions & 9 deletions _deploy/p/demo/gnoswap/pool/swap_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ func SwapMathComputeSwapStepStr(
feeAmount := u256.Zero()

if exactIn {
amountRemainingLessFee := u256.Zero()
_amountRemaining := amountRemaining.Abs()
amountRemainingLessFee = amountRemainingLessFee.Mul(_amountRemaining, u256.NewUint(1000000-feePips))
amountRemainingLessFee = amountRemainingLessFee.Div(amountRemainingLessFee, u256.NewUint(1000000))
amountRemainingLessFee := u256.MulDiv(amountRemaining.Abs(), u256.NewUint(1000000-feePips), u256.NewUint(1000000))

if isToken1Expensive {
amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
Expand Down Expand Up @@ -100,12 +97,8 @@ func SwapMathComputeSwapStepStr(
if exactIn && !(sqrtRatioNextX96.Eq(sqrtRatioTargetX96)) {
feeAmount = new(u256.Uint).Sub(amountRemaining.Abs(), amountIn)
} else {
feeAmount = new(u256.Uint).Mul(amountIn, u256.NewUint(feePips))
feeAmount = feeAmount.Div(feeAmount, u256.NewUint(1000000-feePips))
feeAmount = u256.MulDivRoundingUp(amountIn, u256.NewUint(feePips), new(u256.Uint).Sub(u256.NewUint(1000000), u256.NewUint(feePips)))
}

if !exactIn && !(amountOut.IsZero()) {
amountOut = amountOut.Add(amountOut, u256.NewUint(1))
}
return sqrtRatioNextX96.ToString(), amountIn.ToString(), amountOut.ToString(), feeAmount.ToString()
}
19 changes: 19 additions & 0 deletions _deploy/p/demo/gnoswap/uint256/gs_overflow_calculation.gno
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,22 @@ func MulDivRoundingUp(

return result
}

// UnsafeMath
// https://github.com/Uniswap/v3-core/blob/d8b1c635c275d2a9450bd6a78f3fa2484fef73eb/contracts/libraries/UnsafeMath.sol
func DivRoundingUp(
x, y *Uint,
) *Uint {
div := new(Uint).Div(x, y)
mod := new(Uint).Mod(x, y)

z := new(Uint).Add(div, gt(mod, Zero()))
return z
}

func gt(x, y *Uint) *Uint {
if x.Gt(y) {
return One()
}
return Zero()
}
25 changes: 0 additions & 25 deletions _deploy/p/demo/gnoswap/uint256/gs_overflow_calculation_test.gno

This file was deleted.

Loading

0 comments on commit 1ede86b

Please sign in to comment.