diff --git a/_deploy/p/demo/gnoswap/pool/sqrt_price_math.gno b/_deploy/p/demo/gnoswap/pool/sqrt_price_math.gno index c934f21e..1754c8cd 100644 --- a/_deploy/p/demo/gnoswap/pool/sqrt_price_math.gno +++ b/_deploy/p/demo/gnoswap/pool/sqrt_price_math.gno @@ -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 @@ -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) } } @@ -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)) } } @@ -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() } @@ -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) - } -} diff --git a/_deploy/p/demo/gnoswap/pool/swap_math.gno b/_deploy/p/demo/gnoswap/pool/swap_math.gno index 100ecc22..f0744c6f 100644 --- a/_deploy/p/demo/gnoswap/pool/swap_math.gno +++ b/_deploy/p/demo/gnoswap/pool/swap_math.gno @@ -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) @@ -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() } diff --git a/_deploy/p/demo/gnoswap/uint256/gs_overflow_calculation.gno b/_deploy/p/demo/gnoswap/uint256/gs_overflow_calculation.gno index 88f7dae4..86d54cf2 100644 --- a/_deploy/p/demo/gnoswap/uint256/gs_overflow_calculation.gno +++ b/_deploy/p/demo/gnoswap/uint256/gs_overflow_calculation.gno @@ -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() +} diff --git a/_deploy/r/demo/gnoswap/common/liquidity_amounts.gno b/_deploy/r/demo/gnoswap/common/liquidity_amounts.gno index 3b0ec2f2..46c4334f 100644 --- a/_deploy/r/demo/gnoswap/common/liquidity_amounts.gno +++ b/_deploy/r/demo/gnoswap/common/liquidity_amounts.gno @@ -3,6 +3,7 @@ package common import ( "gno.land/r/demo/gnoswap/consts" + i256 "gno.land/p/demo/gnoswap/int256" u256 "gno.land/p/demo/gnoswap/uint256" ) @@ -16,34 +17,25 @@ func toAscendingOrder(a, b *u256.Uint) (*u256.Uint, *u256.Uint) { return a, b } -// calcIntermediateValue computes the intermediate value -// used in liquidity calculations. -func calcIntermediateValue(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint) *u256.Uint { - - res := new(u256.Uint).Mul(sqrtRatioAX96, sqrtRatioBX96) - res = res.Div(res, u256.MustFromDecimal(consts.Q96)) - return res -} - // computeLiquidityForAmount0 calculates liquidity for a given amount of token 0. func computeLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0 *u256.Uint) *u256.Uint { sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96) - intermediate := calcIntermediateValue(sqrtRatioAX96, sqrtRatioBX96) + + intermediate := u256.MulDiv(sqrtRatioAX96, sqrtRatioBX96, u256.MustFromDecimal(consts.Q96)) + diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) - // we don't need to care about division by zero here. - res := new(u256.Uint).Mul(amount0, intermediate) - res = res.Div(res, diff) + res := u256.MulDiv(amount0, intermediate, diff) return res } // computeLiquidityForAmount1 calculates liquidity for a given amount of token 1. func computeLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1 *u256.Uint) *u256.Uint { sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96) + diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) - res := new(u256.Uint).Mul(amount1, u256.MustFromDecimal(consts.Q96)) - res = res.Div(res, diff) + res := u256.MulDiv(amount1, u256.MustFromDecimal(consts.Q96), diff) return res } @@ -71,48 +63,74 @@ func GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, return liquidity } -// computeAmount0ForLiquidity calculates the amount of token 0 for a given liquidity. +// computeAmount0ForLiquidity calculates the amount of token0 for a given liquidity. func computeAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) *u256.Uint { sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96) - diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) - res := new(u256.Uint).Lsh(liquidity, 96) - res = res.Mul(res, diff) + val1 := new(u256.Uint).Lsh(liquidity, 96) + val2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + + res := u256.MulDiv(val1, val2, sqrtRatioBX96) - _tmp := new(u256.Uint).Mul(sqrtRatioBX96, sqrtRatioAX96) - res = res.Div(res, _tmp) + res = res.Div(res, sqrtRatioAX96) return res } -// computeAmount1ForLiquidity calculates the amount of token 1 for a given liquidity. +// computeAmount1ForLiquidity calculates the amount of token1 for a given liquidity. func computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) *u256.Uint { sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96) - diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) - res := new(u256.Uint).Mul(liquidity, diff) - res = res.Div(res, u256.MustFromDecimal(consts.Q96)) + val2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + res := u256.MulDiv(liquidity, val2, u256.MustFromDecimal(consts.Q96)) return res } -// GetAmountsForLiquidity calculates the amounts of token 0 and token 1 for a given liquidity. -func GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) (*u256.Uint, *u256.Uint) { - amount0 := u256.Zero() - amount1 := u256.Zero() +// GetAmountsForLiquidity calculates the amounts of token0 and token1 for a given liquidity. +// FROM [POOL] position_modify.gno +func GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96 *u256.Uint, liquidity *i256.Int) (string, string) { + var amount0, amount1 *i256.Int + + if !(liquidity.IsZero()) { + if sqrtRatioX96.Lt(sqrtRatioAX96) { // currentTick < tickLower + _amount0Str := SqrtPriceMathGetAmount0DeltaStr( + sqrtRatioAX96, + sqrtRatioBX96, + liquidity, + ) + amount0 = i256.MustFromDecimal(_amount0Str) + + } else if sqrtRatioX96.Lt(sqrtRatioBX96) { // currentTick < tickUpper + _amount0Str := SqrtPriceMathGetAmount0DeltaStr( + sqrtRatioX96, + sqrtRatioBX96, + liquidity, + ) + amount0 = i256.MustFromDecimal(_amount0Str) + + _amount1Str := SqrtPriceMathGetAmount1DeltaStr( + sqrtRatioAX96, + sqrtRatioX96, + liquidity, + ) + amount1 = i256.MustFromDecimal(_amount1Str) - if sqrtRatioAX96.Gt(sqrtRatioBX96) { - sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 - } + } else { + _amount1Str := SqrtPriceMathGetAmount1DeltaStr( + sqrtRatioAX96, + sqrtRatioBX96, + liquidity, + ) + amount1 = i256.MustFromDecimal(_amount1Str) + } - if sqrtRatioX96.Lte(sqrtRatioAX96) { - amount0 = computeAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity) - } else if sqrtRatioX96.Lt(sqrtRatioBX96) { - amount0 = computeAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity) - amount1 = computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity) - } else { - amount1 = computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity) } - return amount0, amount1 + // if position is out of range, one of amount0 or amount1 can be nil + // > handle as 0 + amount0 = amount0.NilToZero() + amount1 = amount1.NilToZero() + + return amount0.ToString(), amount1.ToString() } diff --git a/_deploy/r/demo/gnoswap/common/sqrt_price_math.gno b/_deploy/r/demo/gnoswap/common/sqrt_price_math.gno new file mode 100644 index 00000000..8d92c0ba --- /dev/null +++ b/_deploy/r/demo/gnoswap/common/sqrt_price_math.gno @@ -0,0 +1,181 @@ +package common + +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("common_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(consts.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(consts.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("common_sqrt price math #2") + } + + return new(u256.Uint).Sub(sqrtPX96, quotient) + } +} + +func sqrtPriceMathGetNextSqrtPriceFromInput( + sqrtPX96 *u256.Uint, // uint160 + liquidity *u256.Uint, // uint128 + amountIn *u256.Uint, // uint256 + zeroForOne bool, // bool +) *u256.Uint { // uint160 + if zeroForOne { + return sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) + } + return sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true) +} + +func sqrtPriceMathGetNextSqrtPriceFromOutput( + sqrtPX96 *u256.Uint, // uint160 + liquidity *u256.Uint, // uint128 + amountOut *u256.Uint, // uint256 + zeroForOne bool, // bool +) *u256.Uint { // uint160 + if zeroForOne { + return sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) + } + + return sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false) +} + +func sqrtPriceMathGetAmount0DeltaHelper( + sqrtRatioAX96 *u256.Uint, // uint160 + sqrtRatioBX96 *u256.Uint, // uint160 + liquidity *u256.Uint, // uint160 + roundUp bool, +) *u256.Uint { // uint256 + if sqrtRatioAX96.Gt(sqrtRatioBX96) { + sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 + } + + numerator1 := new(u256.Uint).Lsh(liquidity, 96) + numerator2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + + if !(sqrtRatioAX96.Gt(u256.Zero())) { + panic("common_sqrt price math #3") + } + + value := u256.MulDiv(numerator1, numerator2, sqrtRatioBX96) + + if roundUp { + value1 := u256.MulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96) + return u256.DivRoundingUp(value1, sqrtRatioAX96) + } else { + value1 := u256.MulDiv(numerator1, numerator2, sqrtRatioBX96) + return new(u256.Uint).Div(value1, sqrtRatioAX96) + } +} + +func sqrtPriceMathGetAmount1DeltaHelper( + sqrtRatioAX96 *u256.Uint, // uint160 + sqrtRatioBX96 *u256.Uint, // uint160 + liquidity *u256.Uint, // uint160 + roundUp bool, +) *u256.Uint { // uint256 + if sqrtRatioAX96.Gt(sqrtRatioBX96) { + sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 + } + + if roundUp { + diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + return u256.MulDivRoundingUp(liquidity, diff, u256.MustFromDecimal(consts.Q96)) + } else { + diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + return u256.MulDiv(liquidity, diff, u256.MustFromDecimal(consts.Q96)) + } +} + +func SqrtPriceMathGetAmount0DeltaStr( + sqrtRatioAX96 *u256.Uint, // uint160 + 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() +} + +func SqrtPriceMathGetAmount1DeltaStr( + sqrtRatioAX96 *u256.Uint, // uint160 + sqrtRatioBX96 *u256.Uint, // uint160 + liquidity *i256.Int, // int128 +) string { // int256 + if liquidity.IsNeg() { + u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), false) + i := i256.FromUint256(u) + return i256.Zero().Neg(i).ToString() + } + + u := sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs(), true) + return i256.FromUint256(u).ToString() +} diff --git a/pool/pool.gno b/pool/pool.gno index cee41e48..49cde9aa 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -285,10 +285,8 @@ func Swap( // update global fee tracker if state.liquidity.Gt(u256.Zero()) { - value1 := new(u256.Uint).Mul(step.feeAmount, u256.MustFromDecimal(consts.Q128)) - value2 := new(u256.Uint).Div(value1, state.liquidity) - - state.feeGrowthGlobalX128 = new(u256.Uint).Add(state.feeGrowthGlobalX128, value2) + update := u256.MulDiv(step.feeAmount, u256.MustFromDecimal(consts.Q128), state.liquidity) + state.feeGrowthGlobalX128 = new(u256.Uint).Add(state.feeGrowthGlobalX128, update) } // shift tick if we reached the next price diff --git a/pool/position.gno b/pool/position.gno index 8eb7e684..80c64e45 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -55,13 +55,17 @@ func positionUpdate( liquidityNext = liquidityMathAddDelta(self.liquidity, liquidityDelta) } - tokensOwed0 := new(u256.Uint).Sub(feeGrowthInside0X128, self.feeGrowthInside0LastX128) - tokensOwed0 = tokensOwed0.Mul(tokensOwed0, self.liquidity) - tokensOwed0 = tokensOwed0.Div(tokensOwed0, u256.MustFromDecimal(consts.Q128)) + tokensOwed0 := u256.Zero() + { + diff := new(u256.Uint).Sub(feeGrowthInside0X128, self.feeGrowthInside0LastX128) + tokensOwed0 = u256.MulDiv(diff, self.liquidity, u256.MustFromDecimal(consts.Q128)) + } - tokensOwed1 := new(u256.Uint).Sub(feeGrowthInside1X128, self.feeGrowthInside1LastX128) - tokensOwed1 = tokensOwed1.Mul(tokensOwed1, self.liquidity) - tokensOwed1 = tokensOwed1.Div(tokensOwed1, u256.MustFromDecimal(consts.Q128)) + tokensOwed1 := u256.Zero() + { + diff := new(u256.Uint).Sub(feeGrowthInside1X128, self.feeGrowthInside1LastX128) + tokensOwed1 = u256.MulDiv(diff, self.liquidity, u256.MustFromDecimal(consts.Q128)) + } if !(liquidityDelta.IsZero()) { self.liquidity = liquidityNext diff --git a/pool/position_modify.gno b/pool/position_modify.gno index 8265bea8..b3f67df3 100644 --- a/pool/position_modify.gno +++ b/pool/position_modify.gno @@ -57,7 +57,7 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *i2 } // if position is out of range, one of amount0 or amount1 can be nil - // handle as 0 + // > handle as 0 amount0 = amount0.NilToZero() amount1 = amount1.NilToZero() diff --git a/position/_RPC_api.gno b/position/_RPC_api.gno index 8616e497..d257d10c 100644 --- a/position/_RPC_api.gno +++ b/position/_RPC_api.gno @@ -13,6 +13,7 @@ import ( pl "gno.land/r/demo/pool" + i256 "gno.land/p/demo/gnoswap/int256" u256 "gno.land/p/demo/gnoswap/uint256" ) @@ -119,7 +120,7 @@ func rpcMakePosition(lpTokenId uint64) RpcPosition { currentX96, lowerX96, upperX96, - position.liquidity, + i256.FromUint256(position.liquidity), ) unclaimedFee0 := u256.Zero() @@ -140,8 +141,8 @@ func rpcMakePosition(lpTokenId uint64) RpcPosition { FeeGrowthInside1LastX128: position.feeGrowthInside1LastX128.ToString(), TokensOwed0: position.tokensOwed0.ToString(), TokensOwed1: position.tokensOwed1.ToString(), - Token0Balance: token0Balance.ToString(), - Token1Balance: token1Balance.ToString(), + Token0Balance: token0Balance, + Token1Balance: token1Balance, FeeUnclaimed0: unclaimedFee0.ToString(), FeeUnclaimed1: unclaimedFee1.ToString(), } diff --git a/position/_RPC_dry.gno b/position/_RPC_dry.gno index 4812248b..b112f969 100644 --- a/position/_RPC_dry.gno +++ b/position/_RPC_dry.gno @@ -36,32 +36,37 @@ func DryMint( var amount0, amount1 *i256.Int if !(liquidity.IsZero()) { // != 0 if tickCurrent < tickLower { - amount0 = sqrtPriceMathGetAmount0Delta( + amount0Str := common.SqrtPriceMathGetAmount0DeltaStr( sqrtLowerX96, sqrtUpperX96, i256Liquidity, ) + amount0 = i256.MustFromDecimal(amount0Str) } else if tickCurrent < tickUpper { - amount0 = sqrtPriceMathGetAmount0Delta( + amount0Str := common.SqrtPriceMathGetAmount0DeltaStr( sqrtRatioX96, sqrtUpperX96, i256Liquidity, ) + amount0 = i256.MustFromDecimal(amount0Str) - amount1 = sqrtPriceMathGetAmount1Delta( + amount1Str := common.SqrtPriceMathGetAmount1DeltaStr( sqrtLowerX96, sqrtRatioX96, i256Liquidity, ) + amount1 = i256.MustFromDecimal(amount1Str) } else { - amount1 = sqrtPriceMathGetAmount1Delta( + amount1Str := common.SqrtPriceMathGetAmount1DeltaStr( sqrtLowerX96, sqrtUpperX96, i256Liquidity, ) + amount1 = i256.MustFromDecimal(amount1Str) } } amount0 = amount0.NilToZero() amount1 = amount1.NilToZero() + return amount0.ToString(), amount1.ToString() } diff --git a/router/router_dry.gno b/router/router_dry.gno index a9dc8c8c..3ee3af28 100644 --- a/router/router_dry.gno +++ b/router/router_dry.gno @@ -37,6 +37,9 @@ func DrySwapRoute( if amountSpecified.IsZero() { panic("[ROUTER] router_dry.gno__SwapRoute() || amountSpecified == 0") } + if amountSpecified.IsNeg() { + panic("[ROUTER] router.gno__SwapRoute() || amountSpecified < 0") + } switch swapType { case "EXACT_IN": diff --git a/staker/staker.gno b/staker/staker.gno index 9a5ce763..e085fef4 100644 --- a/staker/staker.gno +++ b/staker/staker.gno @@ -16,6 +16,7 @@ import ( pl "gno.land/r/demo/pool" pn "gno.land/r/demo/position" + i256 "gno.land/p/demo/gnoswap/int256" u256 "gno.land/p/demo/gnoswap/uint256" ) @@ -163,7 +164,7 @@ func StakeToken(tokenId uint64) (string, string, string) { // poolPath, token0Am } token0Amount, token1Amount := getTokenPairBalanceFromPosition(tokenId) - return poolKey, token0Amount.ToString(), token1Amount.ToString() + return poolKey, token0Amount, token1Amount } // CollectReward collects staked reward from tokenId @@ -237,7 +238,7 @@ func UnstakeToken(tokenId uint64) (string, string, string) { // poolPath, token0 poolPath := pn.PositionGetPositionPoolKey(tokenId) token0Amount, token1Amount := getTokenPairBalanceFromPosition(tokenId) - return poolPath, token0Amount.ToString(), token1Amount.ToString() + return poolPath, token0Amount, token1Amount } // EndExternalIncentive ends the external incentive @@ -329,7 +330,7 @@ func transferDeposit(tokenId uint64, to std.Address) { gnft.TransferFrom(a2u(owner), a2u(to), tid(tokenId)) } -func getTokenPairBalanceFromPosition(tokenId uint64) (*u256.Uint, *u256.Uint) { +func getTokenPairBalanceFromPosition(tokenId uint64) (string, string) { poolKey := pn.PositionGetPositionPoolKey(tokenId) pool := pl.GetPoolFromPoolPath(poolKey) @@ -341,14 +342,14 @@ func getTokenPairBalanceFromPosition(tokenId uint64) (*u256.Uint, *u256.Uint) { currentX96, lowerX96, upperX96, - pn.PositionGetPositionLiquidity(tokenId), + i256.FromUint256(pn.PositionGetPositionLiquidity(tokenId)), ) - if token0Balance == nil { - token0Balance = u256.Zero() + if token0Balance == "" { + token0Balance = "0" } - if token1Balance == nil { - token1Balance = u256.Zero() + if token1Balance == "" { + token1Balance = "0" } return token0Balance, token1Balance