Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CL]: Increase min tick past canonical tick range #5551

Merged
merged 15 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions x/concentrated-liquidity/lp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,25 +160,6 @@ var (
amount0Expected: DefaultAmt0Expected.Add(roundingError),
amount1Expected: DefaultAmt1Expected,
},
"use ticks that are not the canonical tick for a given price, expect them to be rounded to the proper tick": {
lowerTick: -161987600,
expectedLowerTick: -161000000,
upperTick: -160009800,
expectedUpperTick: -160000000,
currentTick: DefaultUpperTick,

isNotFirstPositionWithSameAccount: true,
positionId: 2,

liquidityAmount: sdk.MustNewDecFromStr("15731321859400083838.506717486806808937").MulInt64(2),
preSetChargeSpreadRewards: oneEth,
expectedSpreadRewardGrowthOutsideLower: oneEthCoins,
expectedSpreadRewardGrowthOutsideUpper: oneEthCoins,

// Rounding up in favor of the pool.
amount0Expected: sdk.ZeroInt(),
amount1Expected: DefaultAmt1,
},
}
)

Expand Down Expand Up @@ -447,8 +428,20 @@ func (s *KeeperTestSuite) TestWithdrawPosition() {
setupConfig: baseCase,
sutConfigOverwrite: &lpTest{
// Note: subtracting one due to truncations in favor of the pool when withdrawing.
amount0Expected: DefaultAmt0.Sub(sdk.OneInt()),
amount1Expected: DefaultAmt1.Sub(sdk.OneInt()),
// amount0Expected = (liquidity * (sqrtPriceB - sqrtPriceA)) / (sqrtPriceB * sqrtPriceA)
// Where:
// * liquidity = FullRangeLiquidityAmt
// * sqrtPriceB = MaxSqrtPrice
// * sqrtPriceA = DefaultCurrSqrtPrice
// Exact calculation: https://www.wolframalpha.com/input?i=70710678.118654752940000000+*+%2810000000000000000000.000000000000000000+-+70.710678118654752440%29+%2F+%2810000000000000000000.000000000000000000+*+70.710678118654752440%29
amount0Expected: sdk.NewInt(999999),
// amount1Expected = liq * (sqrtPriceB - sqrtPriceA)
// Where:
// * liquidity = FullRangeLiquidityAmt
// * sqrtPriceB = DefaultCurrSqrtPrice
// * sqrtPriceA = MinSqrtPrice
// Exact calculation: https://www.wolframalpha.com/input?i=70710678.118654752940000000+*+%2870.710678118654752440+-+0.000001000000000000%29
amount1Expected: sdk.NewInt(4999999929),
liquidityAmount: FullRangeLiquidityAmt,
underlyingLockId: 1,
},
Expand Down Expand Up @@ -835,8 +828,11 @@ func (s *KeeperTestSuite) TestAddToPosition() {
// 1998976eth (amount withdrawn with rounded down amounts) + 998977(token amount in)
amount0Expected: sdk.NewInt(2997953),
// tokens Provided for token1 is 9999999999 (amount withdrawn) + 5000000000 = 14999999999usdc.
// we calcualte calc amount1 by using: https://www.wolframalpha.com/input?i=70.728769315114743567+*+212041526.154556192320661969
amount1Expected: sdk.NewInt(14997436189),
// We calculate calc amount1 by using the following equation:
// liq * (sqrtPriceB - sqrtPriceA), where liq is equal to the original joined liq + added liq, sqrtPriceB is current sqrt price, and sqrtPriceA is min sqrt price.
// Note that these numbers were calculated using `GetLiquidityFromAmounts` and `TickToSqrtPrice` and thus assume correctness of those functions.
// https://www.wolframalpha.com/input?i=212041526.154556192317664016+*+%2870.728769315114743566+-+0.000001000000000000%29
amount1Expected: sdk.NewInt(14997435977),
Comment on lines +833 to +835
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that these numbers were calculated using GetLiquidityFromAmounts and TickToSqrtPrice and thus assume correctness of those functions.

Are you explaining being off by one compared to the Wolfram calculation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes sorry – the expected output is bankers rounded instead of truncated due to GetLiquidityFromAmounts being bankers rounded. This does not pose any security risks as the balance is still deducted from the caller.

},
timeElapsed: defaultTimeElapsed,
amount0ToAdd: amount0PerfectRatio,
Expand Down
126 changes: 79 additions & 47 deletions x/concentrated-liquidity/math/tick_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ func (suite *ConcentratedMathTestSuite) TestTickToSqrtPrice() {
expectedPrice: types.MaxSpotPrice,
},
"Min tick and max k": {
tickIndex: -162000000,
tickIndex: types.MinTick,
expectedPrice: types.MinSpotPrice,
},
"error: tickIndex less than minimum": {
tickIndex: -162000000 - 1,
expectedError: types.TickIndexMinimumError{MinTick: -162000000},
tickIndex: types.MinTick - 1,
expectedError: types.TickIndexMinimumError{MinTick: types.MinTick},
},
"error: tickIndex greater than maximum": {
tickIndex: 342000000 + 1,
Expand Down Expand Up @@ -234,25 +234,25 @@ func (suite *ConcentratedMathTestSuite) TestTicksToSqrtPrice() {
expectedUpperPrice: sdk.MustNewDecFromStr("10733"),
},
"Max tick and min k": {
lowerTickIndex: sdk.NewInt(-162000000),
upperTickIndex: sdk.NewInt(342000000),
lowerTickIndex: sdk.NewInt(types.MinTick),
upperTickIndex: sdk.NewInt(types.MaxTick),
expectedUpperPrice: types.MaxSpotPrice,
expectedLowerPrice: types.MinSpotPrice,
},
"error: lowerTickIndex less than minimum": {
lowerTickIndex: sdk.NewInt(-162000000 - 1),
lowerTickIndex: sdk.NewInt(types.MinTick - 1),
upperTickIndex: sdk.NewInt(36073300),
expectedError: types.TickIndexMinimumError{MinTick: -162000000},
expectedError: types.TickIndexMinimumError{MinTick: types.MinTick},
},
"error: upperTickIndex greater than maximum": {
lowerTickIndex: sdk.NewInt(-162000000),
upperTickIndex: sdk.NewInt(342000000 + 1),
expectedError: types.TickIndexMaximumError{MaxTick: 342000000},
lowerTickIndex: sdk.NewInt(types.MinTick),
upperTickIndex: sdk.NewInt(types.MaxTick + 1),
expectedError: types.TickIndexMaximumError{MaxTick: types.MaxTick},
},
"error: provided lower tick and upper tick are same": {
lowerTickIndex: sdk.NewInt(-162000000),
upperTickIndex: sdk.NewInt(-162000000),
expectedError: types.InvalidLowerUpperTickError{LowerTick: sdk.NewInt(-162000000).Int64(), UpperTick: sdk.NewInt(-162000000).Int64()},
lowerTickIndex: sdk.NewInt(types.MinTick),
upperTickIndex: sdk.NewInt(types.MinTick),
expectedError: types.InvalidLowerUpperTickError{LowerTick: sdk.NewInt(types.MinTick).Int64(), UpperTick: sdk.NewInt(types.MinTick).Int64()},
},
}

Expand Down Expand Up @@ -425,9 +425,11 @@ func (suite *ConcentratedMathTestSuite) TestPriceToTickRoundDown() {
tickExpected: types.MinTick,
},
"tick spacing 100, Spot price one tick above min, one tick above min -> MinTick": {
price: types.MinSpotPrice.Add(sdk.SmallestDec()),
tickSpacing: defaultTickSpacing,
tickExpected: closestTickAboveMinPriceDefaultTickSpacing.Int64(),
price: types.MinSpotPrice.Add(sdk.SmallestDec()),
tickSpacing: defaultTickSpacing,
// Since the tick should always be the closest tick below (and `smallestDec` isn't sufficient
// to push us into the next tick), we expect MinTick to be returned here.
tickExpected: types.MinTick,
},
"tick spacing 100, Spot price one tick below max, one tick below max -> MaxTick - 1": {
price: closestPriceBelowMaxPriceDefaultTickSpacing,
Expand Down Expand Up @@ -505,49 +507,41 @@ func (suite *ConcentratedMathTestSuite) TestTickToSqrtPricePriceToTick_InverseRe
},
"min spot price": {
price: types.MinSpotPrice,
tickExpected: -162000000,
tickExpected: types.MinTick,
},
"smallest + min price increment": {
price: sdk.MustNewDecFromStr("0.000000000000000002"),
tickExpected: -161000000,
"smallest + min price + tick": {
price: sdk.MustNewDecFromStr("0.000000000001000001"),
tickExpected: types.MinTick + 1,
},
"min price increment 10^1": {
price: sdk.MustNewDecFromStr("0.000000000000000009"),
tickExpected: -154000000,
},
"smallest + min price increment 10^1": {
price: sdk.MustNewDecFromStr("0.000000000000000010"),
tickExpected: -153000000,
price: sdk.MustNewDecFromStr("0.000000000010000000"),
tickExpected: types.MinTick + (9 * 1e6),
},
"smallest + min price increment * 10^2": {
price: sdk.MustNewDecFromStr("0.000000000000000100"),
tickExpected: -144000000,
"min price increment 10^2": {
price: sdk.MustNewDecFromStr("0.000000000100000000"),
tickExpected: types.MinTick + (2 * 9 * 1e6),
},
"smallest + min price increment * 10^3": {
price: sdk.MustNewDecFromStr("0.000000000000001000"),
tickExpected: -135000000,
"min price increment 10^3": {
price: sdk.MustNewDecFromStr("0.000000001000000000"),
tickExpected: types.MinTick + (3 * 9 * 1e6),
},
"smallest + min price increment * 10^4": {
price: sdk.MustNewDecFromStr("0.000000000000010000"),
tickExpected: -126000000,
"min price increment 10^4": {
price: sdk.MustNewDecFromStr("0.000000010000000000"),
tickExpected: types.MinTick + (4 * 9 * 1e6),
},
"smallest + min price * increment 10^5": {
price: sdk.MustNewDecFromStr("0.000000000000100000"),
tickExpected: -117000000,
"min price increment 10^5": {
price: sdk.MustNewDecFromStr("0.000000100000000000"),
tickExpected: types.MinTick + (5 * 9 * 1e6),
},
"smallest + min price * increment 10^6": {
price: sdk.MustNewDecFromStr("0.000000000001000000"),
tickExpected: -108000000,
"min price increment 10^6": {
price: sdk.MustNewDecFromStr("0.000001000000000000"),
tickExpected: types.MinTick + (6 * 9 * 1e6),
},
"smallest + min price * increment 10^6 + tick": {
price: sdk.MustNewDecFromStr("0.000000000001000001"),
tickExpected: -107999999,
},
"smallest + min price * increment 10^17": {
"min price * increment 10^11": {
price: sdk.MustNewDecFromStr("0.100000000000000000"),
tickExpected: -9000000,
},
"smallest + min price * increment 10^18": {
"min price * increment 10^12": {
price: sdk.MustNewDecFromStr("1.000000000000000000"),
tickExpected: 0,
},
Expand Down Expand Up @@ -755,6 +749,10 @@ func (s *ConcentratedMathTestSuite) TestSqrtPriceToTickRoundDownSpacing() {
s.Require().NoError(err)
_, sqpMaxTickSubOne, err := math.TickToSqrtPrice(types.MaxTick - 1)
s.Require().NoError(err)
_, sqpMinTickPlusOne, err := math.TickToSqrtPrice(types.MinTick + 1)
s.Require().NoError(err)
_, sqpMinTickPlusTwo, err := math.TickToSqrtPrice(types.MinTick + 2)
s.Require().NoError(err)

testCases := map[string]struct {
sqrtPrice sdk.Dec
Expand Down Expand Up @@ -836,6 +834,40 @@ func (s *ConcentratedMathTestSuite) TestSqrtPriceToTickRoundDownSpacing() {
tickSpacing: defaultTickSpacing,
tickExpected: types.MaxTick - defaultTickSpacing,
},
"sqrt price corresponds exactly to min tick + 1 (tick spacing 1)": {
sqrtPrice: sqpMinTickPlusOne,
tickSpacing: 1,
tickExpected: types.MinTick + 1,
},
"sqrt price corresponds exactly to min tick + 1 minus 1 ULP (tick spacing 1)": {
// Calculated using TickToSqrtPrice(types.MinTick + 1) - 1 ULP
sqrtPrice: sqpMinTickPlusOne.Sub(sdk.SmallestDec()),
tickSpacing: 1,
tickExpected: types.MinTick,
},
"sqrt price corresponds exactly to min tick + 1 plus 1 ULP (tick spacing 1)": {
// Calculated using TickToSqrtPrice(types.MinTick + 1) + 1 ULP
sqrtPrice: sqpMinTickPlusOne.Add(sdk.SmallestDec()),
tickSpacing: 1,
tickExpected: types.MinTick + 1,
},
"sqrt price corresponds exactly to min tick + 2 (tick spacing 1)": {
sqrtPrice: sqpMinTickPlusTwo,
tickSpacing: 1,
tickExpected: types.MinTick + 2,
},
"sqrt price corresponds exactly to min tick + 2 plus 1 ULP (tick spacing 1)": {
// Calculated using TickToSqrtPrice(types.MinTick + 2) + 1 ULP
sqrtPrice: sqpMinTickPlusTwo.Add(sdk.SmallestDec()),
tickSpacing: 1,
tickExpected: types.MinTick + 2,
},
"sqrt price corresponds exactly to min tick + 2 minus 1 ULP (tick spacing 1)": {
// Calculated using TickToSqrtPrice(types.MinTick + 2) - 1 ULP
sqrtPrice: sqpMinTickPlusTwo.Sub(sdk.SmallestDec()),
tickSpacing: 1,
tickExpected: types.MinTick + 1,
},
}
for name, tc := range testCases {
s.Run(name, func() {
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/python/swap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from math import *

# Precomputed sqrt values using osmomath.MonotonicSqrt
minSqrtPrice = Decimal("0.000000001000000000")
minSqrtPrice = Decimal("0.000001000000000000")
sqrt4000 = Decimal("63.245553203367586640")
sqrt4545 = Decimal("67.416615162732695594")
sqrt4994 = Decimal("70.668238976219012614")
Expand Down
86 changes: 0 additions & 86 deletions x/concentrated-liquidity/tick_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1405,89 +1405,3 @@ func (s *KeeperTestSuite) TestValidateTickRangeIsValid() {
})
}
}

func (s *KeeperTestSuite) TestRoundTickToCanonicalPriceTick() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed? Is there a replacement for these test vectors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notion of canonical price ticks no longer exist, so these vectors no longer make sense. All the test vectors added relating to the behavior in ticks surrounding the new MinTick ensure the appropriate properties hold.

tests := []struct {
name string
lowerTick int64
upperTick int64
expectedNewLowerTick int64
expectedNewUpperTick int64
expectedError error
}{
{
name: "exact upper tick for 0.000000000000000003 to exact lower tick for 0.000000000000000002",
lowerTick: -161000000,
expectedNewLowerTick: -161000000,
upperTick: -160000000,
expectedNewUpperTick: -160000000,
},
{
name: "exact upper tick for 0.000000000000000003 to inexact lower tick for 0.000000000000000002",
lowerTick: -161001234,
expectedNewLowerTick: -161000000,
upperTick: -160000000,
expectedNewUpperTick: -160000000,
},
{
name: "inexact upper tick for 0.000000000000000003 to exact lower tick for 0.000000000000000002",
lowerTick: -161000000,
expectedNewLowerTick: -161000000,
upperTick: -160001234,
expectedNewUpperTick: -160000000,
},
{
name: "inexact upper tick for 0.000000000000000003 to inexact lower tick for 0.000000000000000002",
lowerTick: -161001234,
expectedNewLowerTick: -161000000,
upperTick: -160001234,
expectedNewUpperTick: -160000000,
},
{
name: "upper tick one tick away from lower tick",
lowerTick: -161001234,
expectedNewLowerTick: -161000000,
upperTick: -160999999,
expectedNewUpperTick: -160000000,
},
{
name: "error: new upper tick is lower than new lower tick",
lowerTick: -160001234,
expectedNewLowerTick: -160000000,
upperTick: -161001234,
expectedNewUpperTick: -161000000,
expectedError: types.InvalidLowerUpperTickError{LowerTick: -160000000, UpperTick: -161000000},
},
{
name: "error: new upper tick is the same as new lower tick",
lowerTick: -160001234,
expectedNewLowerTick: -160000000,
upperTick: -160000000,
expectedNewUpperTick: -160000000,
expectedError: types.InvalidLowerUpperTickError{LowerTick: -160000000, UpperTick: -160000000},
},
}

for _, test := range tests {
s.Run(test.name, func() {
s.SetupTest()

_, sqrtPriceTickLower, err := math.TickToSqrtPrice(test.lowerTick)
s.Require().NoError(err)
_, sqrtPriceTickUpper, err := math.TickToSqrtPrice(test.upperTick)
s.Require().NoError(err)

// System Under Test
newLowerTick, newUpperTick, err := cl.RoundTickToCanonicalPriceTick(test.lowerTick, test.upperTick, sqrtPriceTickLower, sqrtPriceTickUpper, DefaultTickSpacing)

if test.expectedError != nil {
s.Require().Error(err)
s.Require().ErrorContains(err, test.expectedError.Error())
} else {
s.Require().NoError(err)
s.Require().Equal(test.expectedNewLowerTick, newLowerTick)
s.Require().Equal(test.expectedNewUpperTick, newUpperTick)
}
})
}
}
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

const (
// Precomputed values for min and max tick
MinTick, MaxTick int64 = -162000000, 342000000
MinTick, MaxTick int64 = -108000000, 342000000
ExponentAtPriceOne int64 = -6
ConcentratedGasFeeForSwap = 10_000
BaseGasFeeForNewIncentive = 10_000
Expand All @@ -19,7 +19,7 @@ const (

var (
MaxSpotPrice = sdk.MustNewDecFromStr("100000000000000000000000000000000000000")
MinSpotPrice = sdk.MustNewDecFromStr("0.000000000000000001") // 10^-18
MinSpotPrice = sdk.MustNewDecFromStr("0.000000000001") // 10^-12
MaxSqrtPrice = osmomath.MustMonotonicSqrt(MaxSpotPrice)
MinSqrtPrice = osmomath.MustMonotonicSqrt(MinSpotPrice)
// Supported uptimes preset to 1 ns, 1 min, 1 hr, 1D, 1W, 2W
Expand Down
Loading