Skip to content

Commit

Permalink
fix: duplicate price for multiple ticks (#5196)
Browse files Browse the repository at this point in the history
* initial push

* update gomod

* revert nolint

* update gomod
  • Loading branch information
czarcas7ic authored May 16, 2023
1 parent 78c0266 commit 30fe496
Show file tree
Hide file tree
Showing 27 changed files with 394 additions and 200 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ require (
github.com/mattn/go-sqlite3 v1.14.16
github.com/ory/dockertest/v3 v3.10.0
github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230511015306-615fa4fcbe56
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230516205127-c213fddde069
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230516205127-c213fddde069
github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304
github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230331072320-5d6f6cfa2627
github.com/pkg/errors v0.9.1
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -938,8 +938,16 @@ github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:Ylmch
github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111 h1:1ahWbf9iF9sxDOjxrHWFaBGLE0nWFdpiX1pqObUaJO8=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230516200639-ee915a97c5c1 h1:NM8H0Nqhy+qvtx0P06Q46GWUPB/qyIUBzXj53Rv25is=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230516200639-ee915a97c5c1/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230516205127-c213fddde069 h1:ZgDrTJ2GCH4CJGbV6rudw4O9rPMAuwWoLVZnG6cUr+A=
github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230516205127-c213fddde069/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230511015306-615fa4fcbe56 h1:27yWLC0uXSatRy8aRn0yinHU+K31bkjBRmNnQUDO0Ks=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230511015306-615fa4fcbe56/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230516200639-ee915a97c5c1 h1:hB/CvutX3XhtWec01Esr/GMBS1uE4JGO+PtCUvAgsf8=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230516200639-ee915a97c5c1/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230516205127-c213fddde069 h1:9C/n+Nx5rre/AHPMlPsQrk1isgydrCNB68aqer86ygE=
github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230516205127-c213fddde069/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A=
github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo=
github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304/go.mod h1:yPWoJTj5RKrXKUChAicp+G/4Ni/uVEpp27mi/FF/L9c=
github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230331072320-5d6f6cfa2627 h1:A0SwZgp4bmJFbivYJc8mmVhMjrr3EdUZluBYFke11+w=
Expand Down
2 changes: 0 additions & 2 deletions osmomath/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,6 @@ func (i BigInt) MarshalAmino() ([]byte, error) { return i.Marshal() }
func (i *BigInt) UnmarshalAmino(bz []byte) error { return i.Unmarshal(bz) }

// intended to be used with require/assert: require.True(IntEq(...))
//
//nolint:thelper
func IntEq(t *testing.T, exp, got BigInt) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}
6 changes: 6 additions & 0 deletions proto/osmosis/concentrated-liquidity/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ message MsgCreatePositionResponse {
(gogoproto.moretags) = "yaml:\"liquidity_created\"",
(gogoproto.nullable) = false
];
// the lower and upper tick are in the response because there are
// instances in which multiple ticks represent the same price, so
// we may move their provided tick to the canonical tick that represents
// the same price.
int64 lower_tick = 6 [ (gogoproto.moretags) = "yaml:\"lower_tick\"" ];
int64 upper_tick = 7 [ (gogoproto.moretags) = "yaml:\"upper_tick\"" ];
}

// ===================== MsgAddToPosition
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,9 @@ func (s *IntegrationTestSuite) TestConcentratedLiquidity() {
nextInitTick := sdk.NewInt(40000) // address1 position1's upper tick

// Calculate sqrtPrice after and at the next initialized tick (upperTick of address1 position1 - 40000)
sqrtPriceAfterNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick.Add(tickOffset))
_, sqrtPriceAfterNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick.Add(tickOffset))
s.Require().NoError(err)
sqrtPriceAtNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick)
_, sqrtPriceAtNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick)
s.Require().NoError(err)

// Calculate Δ(sqrtPrice):
Expand Down Expand Up @@ -513,9 +513,9 @@ func (s *IntegrationTestSuite) TestConcentratedLiquidity() {
// Using: CalcAmount0Delta = liquidity * ((sqrtPriceB - sqrtPriceA) / (sqrtPriceB * sqrtPriceA))

// Calculate sqrtPrice after and at the next initialized tick (which is upperTick of address1 position1 - 40000)
sqrtPricebBelowNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick.Sub(tickOffset))
_, sqrtPricebBelowNextInitializedTick, err := cl.TickToSqrtPrice(nextInitTick.Sub(tickOffset))
s.Require().NoError(err)
sqrtPriceAtNextInitializedTick, err = cl.TickToSqrtPrice(nextInitTick)
_, sqrtPriceAtNextInitializedTick, err = cl.TickToSqrtPrice(nextInitTick)
s.Require().NoError(err)

// Calculate numerators
Expand Down
9 changes: 9 additions & 0 deletions x/concentrated-liquidity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,15 @@ a) Preventing trade at a desirable spot price or
b) Having the front end round the tick's actual price to the nearest
human readable/desirable spot price

One side effect of increasing precision as we get closer to the minimum tick
is that multiple ticks can represent the same price. For example, tick
-161795100 (along with the ticks surrounding it) correlate to a price
of 0.000000000000000002. To get around any issues this may cause, when a
position is created with a user defined lower and upper tick, we determine
if a larger tick exists that represents the same price. If so, we use that tick
instead of the user defined tick. In the above example, the tick would be
changed to -161000000, which is the first tick that represents the same price.

## Concentrated Liquidity Module Messages

### `MsgCreatePosition`
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type BenchTestSuite struct {
func (s BenchTestSuite) createPosition(accountIndex int, poolId uint64, coin0, coin1 sdk.Coin, lowerTick, upperTick int64) {
tokensDesired := sdk.NewCoins(coin0, coin1)

_, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick)
_, _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[accountIndex], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick)
if err != nil {
// This can happen for ticks that map to the very small prices
// e.g 2 * 10^(-18) ends up mapping to the same sqrt price
Expand Down Expand Up @@ -100,7 +100,7 @@ func BenchmarkSwapExactAmountIn(b *testing.B) {
tokenDesired0 := sdk.NewCoin(denom0, sdk.NewInt(100))
tokenDesired1 := sdk.NewCoin(denom1, sdk.NewInt(100))
tokensDesired := sdk.NewCoins(tokenDesired0, tokenDesired1)
_, _, _, _, _, err = clKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinTick, types.MaxTick)
_, _, _, _, _, _, _, err = clKeeper.CreatePosition(s.Ctx, poolId, s.TestAccs[0], tokensDesired, sdk.ZeroInt(), sdk.ZeroInt(), types.MinTick, types.MaxTick)

pool, err := clKeeper.GetPoolById(s.Ctx, poolId)
noError(err)
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/export_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import (
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/math"
)

func TickToSqrtPrice(tickIndex sdk.Int) (price sdk.Dec, err error) {
func TickToSqrtPrice(tickIndex sdk.Int) (price sdk.Dec, sqrtPrice sdk.Dec, err error) {
return math.TickToSqrtPrice(tickIndex)
}
6 changes: 5 additions & 1 deletion x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ func (k Keeper) SetPositionIdToLock(ctx sdk.Context, positionId, underlyingLockI
k.setPositionIdToLock(ctx, positionId, underlyingLockId)
}

func RoundTickToCanonicalPriceTick(lowerTick, upperTick int64, priceTickLower, priceTickUpper sdk.Dec, tickSpacing uint64) (int64, int64, error) {
return roundTickToCanonicalPriceTick(lowerTick, upperTick, priceTickLower, priceTickUpper, tickSpacing)
}

// fees methods
func (k Keeper) CreateFeeAccumulator(ctx sdk.Context, poolId uint64) error {
return k.createFeeAccumulator(ctx, poolId)
Expand Down Expand Up @@ -191,7 +195,7 @@ func PreparePositionAccumulator(feeAccumulator accum.AccumulatorObject, position
return preparePositionAccumulator(feeAccumulator, positionKey, feeGrowthOutside)
}

func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min, amount1Min sdk.Int, lowerTick, upperTick int64) (uint64, sdk.Int, sdk.Int, sdk.Dec, time.Time, error) {
func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min, amount1Min sdk.Int, lowerTick, upperTick int64) (positionId uint64, actualAmount0 sdk.Int, actualAmount1 sdk.Int, liquidityDelta sdk.Dec, joinTime time.Time, lowerTickResult int64, upperTickResult int64, err error) {
return k.createPosition(ctx, poolId, owner, tokensProvided, amount0Min, amount1Min, lowerTick, upperTick)
}

Expand Down
6 changes: 3 additions & 3 deletions x/concentrated-liquidity/fees_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,7 @@ func (s *KeeperTestSuite) TestFunctional_Fees_LP() {
s.Require().Error(err)

// Create position in the default range 1.
positionIdOne, _, _, liquidity, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
positionIdOne, _, _, liquidity, _, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

// Swap once.
Expand All @@ -1527,7 +1527,7 @@ func (s *KeeperTestSuite) TestFunctional_Fees_LP() {
s.validatePositionFeeGrowth(pool.GetId(), positionIdOne, cl.EmptyCoins)

// Create position in the default range 2.
positionIdTwo, _, _, fullLiquidity, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
positionIdTwo, _, _, fullLiquidity, _, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

// Swap once in the other direction.
Expand Down Expand Up @@ -1555,7 +1555,7 @@ func (s *KeeperTestSuite) TestFunctional_Fees_LP() {
s.Require().Equal(expectesFeesCollected.String(), feesCollected.AmountOf(ETH).String())

// Create position in the default range 3.
positionIdThree, _, _, fullLiquidity, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
positionIdThree, _, _, fullLiquidity, _, _, _, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

collectedThree, err := s.App.ConcentratedLiquidityKeeper.CollectFees(ctx, owner, positionIdThree)
Expand Down
6 changes: 3 additions & 3 deletions x/concentrated-liquidity/incentives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ func (s *KeeperTestSuite) TestUpdateUptimeAccumulatorsToNow() {
if !tc.isInvalidBalancerPool {
depositedCoins := sdk.NewCoins(sdk.NewCoin(clPool.GetToken0(), testQualifyingDepositsOne), sdk.NewCoin(clPool.GetToken1(), testQualifyingDepositsOne))
s.FundAcc(testAddressOne, depositedCoins)
_, _, _, qualifyingLiquidity, _, err = clKeeper.CreatePosition(s.Ctx, clPool.GetId(), testAddressOne, depositedCoins, sdk.ZeroInt(), sdk.ZeroInt(), clPool.GetCurrentTick().Int64()-100, clPool.GetCurrentTick().Int64()+100)
_, _, _, qualifyingLiquidity, _, _, _, err = clKeeper.CreatePosition(s.Ctx, clPool.GetId(), testAddressOne, depositedCoins, sdk.ZeroInt(), sdk.ZeroInt(), clPool.GetCurrentTick().Int64()-100, clPool.GetCurrentTick().Int64()+100)
s.Require().NoError(err)

// If a canonical balancer pool exists, we add its respective shares to the qualifying amount as well.
Expand Down Expand Up @@ -3589,7 +3589,7 @@ func (s *KeeperTestSuite) TestFunctional_ClaimIncentices_LiquidityChange_Varying
s.Require().NoError(err)

// Set up position
positionIdOne, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
positionIdOne, _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

// Increase block time by the fully charged duration (first time)
Expand All @@ -3604,7 +3604,7 @@ func (s *KeeperTestSuite) TestFunctional_ClaimIncentices_LiquidityChange_Varying
s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(testFullChargeDuration))

// Create another position
positionIdTwo, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
positionIdTwo, _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, defaultPoolId, defaultAddress, DefaultCoins, sdk.ZeroInt(), sdk.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

// Increase block time by the fully charged duration (third time)
Expand Down
6 changes: 3 additions & 3 deletions x/concentrated-liquidity/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (s *KeeperTestSuite) SetupDefaultPosition(poolId uint64) {

func (s *KeeperTestSuite) SetupPosition(poolId uint64, owner sdk.AccAddress, providedCoins sdk.Coins, lowerTick, upperTick int64, joinTime time.Time) (sdk.Dec, uint64) {
s.FundAcc(owner, providedCoins)
positionId, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick)
positionId, _, _, _, _, _, _, err := s.App.ConcentratedLiquidityKeeper.CreatePosition(s.Ctx, poolId, owner, providedCoins, sdk.ZeroInt(), sdk.ZeroInt(), lowerTick, upperTick)
s.Require().NoError(err)
liquidity, err := s.App.ConcentratedLiquidityKeeper.GetPositionLiquidity(s.Ctx, positionId)
s.Require().NoError(err)
Expand Down Expand Up @@ -136,13 +136,13 @@ func (s *KeeperTestSuite) validateTickUpdates(ctx sdk.Context, poolId uint64, ow
s.Require().NoError(err)
s.Require().Equal(expectedRemainingLiquidity.String(), lowerTickInfo.LiquidityGross.String())
s.Require().Equal(expectedRemainingLiquidity.String(), lowerTickInfo.LiquidityNet.String())
s.Require().Equal(lowerTickInfo.FeeGrowthOppositeDirectionOfLastTraversal.String(), expectedLowerFeeGrowthOppositeDirectionOfLastTraversal.String())
s.Require().Equal(expectedLowerFeeGrowthOppositeDirectionOfLastTraversal.String(), lowerTickInfo.FeeGrowthOppositeDirectionOfLastTraversal.String())

upperTickInfo, err := s.App.ConcentratedLiquidityKeeper.GetTickInfo(s.Ctx, poolId, upperTick)
s.Require().NoError(err)
s.Require().Equal(expectedRemainingLiquidity.String(), upperTickInfo.LiquidityGross.String())
s.Require().Equal(expectedRemainingLiquidity.Neg().String(), upperTickInfo.LiquidityNet.String())
s.Require().Equal(upperTickInfo.FeeGrowthOppositeDirectionOfLastTraversal.String(), expectedUpperFeeGrowthOppositeDirectionOfLastTraversal.String())
s.Require().Equal(expectedUpperFeeGrowthOppositeDirectionOfLastTraversal.String(), upperTickInfo.FeeGrowthOppositeDirectionOfLastTraversal.String())
}

func (s *KeeperTestSuite) initializeTick(ctx sdk.Context, currentTick int64, tickIndex int64, initialLiquidity sdk.Dec, feeGrowthOppositeDirectionOfTraversal sdk.DecCoins, uptimeTrackers []model.UptimeTracker, isLower bool) {
Expand Down
Loading

0 comments on commit 30fe496

Please sign in to comment.