Skip to content

Commit

Permalink
feat: return bucket index of the current tick from LiquidityPerTickRa…
Browse files Browse the repository at this point in the history
…nge query (#6805)

Closes: https://app.clickup.com/t/86a19jj0x

## What is the purpose of the change

This PR is an update to the full range liquidity query. It adds a new return parameter that is the bucket index corresponding to the current tick.

This functionality is necessary for the SQS router in: #6785

By having this index, we don't have to iterate all ticks in search of the current tick. Instead, we can index into the desired bucket in constant time.

## Testing and Verifying

Added unit test and also tested on localosmosis

## Documentation and Release Note

  - [x] Does this pull request introduce a new feature or user-facing behavior changes?
  - [x] Changelog entry added to `Unreleased` section of `CHANGELOG.md`?

Where is the change documented?
  - [ ] Specification (`x/{module}/README.md`)
  - [ ] Osmosis documentation site
  - [ ] Code comments?
  - [ ] N/A

(cherry picked from commit 9755013)
  • Loading branch information
p0mvn authored and mergify[bot] committed Nov 1, 2023
1 parent f5ae903 commit 53ab649
Show file tree
Hide file tree
Showing 8 changed files with 412 additions and 183 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [#6788](https://github.com/osmosis-labs/osmosis/pull/6788) Improve error message when CL LP fails due to slippage bound hit.

### API Breaks

* [#6805](https://github.com/osmosis-labs/osmosis/pull/6805) return bucket index of the current tick from LiquidityPerTickRange query

## v20.0.0

### Features
Expand Down
2 changes: 2 additions & 0 deletions proto/osmosis/concentrated-liquidity/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ message LiquidityPerTickRangeRequest {
message LiquidityPerTickRangeResponse {
repeated LiquidityDepthWithRange liquidity = 1
[ (gogoproto.nullable) = false ];

int64 bucket_index = 2 [ (gogoproto.moretags) = "yaml:\"bucket_index\"" ];
}

// ===================== QueryClaimableSpreadRewards
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ func BenchmarkGetTickLiquidityForFullRange(b *testing.B) {
b.StartTimer()

// System under test
liquidityNet, err := clKeeper.GetTickLiquidityForFullRange(s.Ctx, pool.GetId())
liquidityNet, _, err := clKeeper.GetTickLiquidityForFullRange(s.Ctx, pool.GetId())
b.StopTimer()
noError(b, err)

Expand Down
12 changes: 12 additions & 0 deletions x/concentrated-liquidity/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func GetQueryCmd() *cobra.Command {
osmocli.AddQueryCmd(cmd, queryproto.NewQueryClient, GetTickLiquidityNetInDirection)
osmocli.AddQueryCmd(cmd, queryproto.NewQueryClient, GetPoolAccumulatorRewards)
osmocli.AddQueryCmd(cmd, queryproto.NewQueryClient, GetTickAccumulatorTrackers)
osmocli.AddQueryCmd(cmd, queryproto.NewQueryClient, GetLiquidityPerTickRange)
cmd.AddCommand(
osmocli.GetParams[*queryproto.ParamsRequest](
types.ModuleName, queryproto.NewQueryClient),
Expand Down Expand Up @@ -117,6 +118,17 @@ TODO: What does any of that mean...?`,
}, &queryproto.LiquidityNetInDirectionRequest{}
}

func GetLiquidityPerTickRange() (*osmocli.QueryDescriptor, *queryproto.LiquidityPerTickRangeRequest) {
return &osmocli.QueryDescriptor{
Use: "liquidity-per-tick-range",
Short: "Query liquidity per tick range",
Long: `{{.Short}}{{.ExampleHeader}}
{{.CommandPrefix}} 1
[poolid]`,
}, &queryproto.LiquidityPerTickRangeRequest{}
}

func GetPoolAccumulatorRewards() (*osmocli.QueryDescriptor, *queryproto.PoolAccumulatorRewardsRequest) {
return &osmocli.QueryDescriptor{
Use: "pool-accumulator-rewards",
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/client/query_proto_wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,15 @@ func (q Querier) Params(ctx sdk.Context, req clquery.ParamsRequest) (*clquery.Pa
// LiquidityPerTickRange returns the amount of liquidity per every tick range
// existing within the given pool. The amounts are returned as a slice of ranges with their liquidity depths.
func (q Querier) LiquidityPerTickRange(ctx sdk.Context, req clquery.LiquidityPerTickRangeRequest) (*clquery.LiquidityPerTickRangeResponse, error) {
liquidity, err := q.Keeper.GetTickLiquidityForFullRange(
liquidity, bucketIndex, err := q.Keeper.GetTickLiquidityForFullRange(
ctx,
req.PoolId,
)
if err != nil {
return nil, err
}

return &clquery.LiquidityPerTickRangeResponse{Liquidity: liquidity}, nil
return &clquery.LiquidityPerTickRangeResponse{Liquidity: liquidity, BucketIndex: bucketIndex}, nil
}

// LiquidityNetInDirection returns an array of LiquidityDepthWithRange, which contains the range(lower tick and upper tick), the liquidity amount in the range, and current sqrt price.
Expand Down
327 changes: 182 additions & 145 deletions x/concentrated-liquidity/client/queryproto/query.pb.go

Large diffs are not rendered by default.

54 changes: 41 additions & 13 deletions x/concentrated-liquidity/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,77 @@ import (
types "github.com/osmosis-labs/osmosis/v20/x/concentrated-liquidity/types"
)

const invalidTickIndex = int64(-1)

// This file contains query-related helper functions for the Concentrated Liquidity module

// GetTickLiquidityForFullRange returns an array of liquidity depth for all ticks existing from min tick ~ max tick.
func (k Keeper) GetTickLiquidityForFullRange(ctx sdk.Context, poolId uint64) ([]queryproto.LiquidityDepthWithRange, error) {
// GetTickLiquidityForFullRange returns a slice of liquidity buckets for all tick ranges existing from min tick ~ max tick.
// Returns index of the bucket that corresponds to the current tick.
// For cases where there is no liqudity in the bucket but there may be liquidity to the right, the value will be -1.
// For cases where there is no liquidity in the bucket but there may be liquidity to the left, the value will be len(liquidityDepthsForRange).
// Otherwise, the index points to the bucket that corresponds to the current tick.
func (k Keeper) GetTickLiquidityForFullRange(ctx sdk.Context, poolId uint64) ([]queryproto.LiquidityDepthWithRange, int64, error) {
// use false for zeroForOne since we're going from lower tick -> upper tick
zeroForOne := false
swapStrategy := swapstrategy.New(zeroForOne, osmomath.ZeroBigDec(), k.storeKey, osmomath.ZeroDec())

// set current tick to min tick, and find the first initialized tick starting from min tick -1.
// set leftmost tick to min tick, and find the first initialized tick starting from min tick -1.
// we do -1 to make min tick inclusive.
currentTick := types.MinCurrentTick
// Note that MinCurrentTick = MinInitializedTick - 1
leftMostTickIndex := types.MinCurrentTick

nextTickIter := swapStrategy.InitializeNextTickIterator(ctx, poolId, currentTick)
nextTickIter := swapStrategy.InitializeNextTickIterator(ctx, poolId, leftMostTickIndex)
defer nextTickIter.Close()
if !nextTickIter.Valid() {
return []queryproto.LiquidityDepthWithRange{}, types.RanOutOfTicksForPoolError{PoolId: poolId}
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, types.RanOutOfTicksForPoolError{PoolId: poolId}
}

nextTick, err := types.TickIndexFromBytes(nextTickIter.Key())
if err != nil {
return []queryproto.LiquidityDepthWithRange{}, err
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, err
}

tick, err := k.getTickByTickIndex(ctx, poolId, nextTick)
if err != nil {
return []queryproto.LiquidityDepthWithRange{}, err
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, err
}

liquidityDepthsForRange := []queryproto.LiquidityDepthWithRange{}

// use the smallest tick initialized as the starting point for calculating liquidity.
currentLiquidity := tick.LiquidityNet
currentTick = nextTick
leftMostTickIndex = nextTick
totalLiquidityWithinRange := currentLiquidity

previousTickIndex := currentTick
previousTickIndex := leftMostTickIndex

concentratedPool, err := k.getPoolById(ctx, poolId)
if err != nil {
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, err
}

var (
currentBucketIndex = invalidTickIndex
currentTick = concentratedPool.GetCurrentTick()
currentTickLiquidity = concentratedPool.GetLiquidity()
)

// start from the next index so that the current tick can become lower tick.
nextTickIter.Next()
for ; nextTickIter.Valid(); nextTickIter.Next() {
tickIndex, err := types.TickIndexFromBytes(nextTickIter.Key())
if err != nil {
return []queryproto.LiquidityDepthWithRange{}, err
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, err
}

tickStruct, err := ParseTickFromBz(nextTickIter.Value())
if err != nil {
return []queryproto.LiquidityDepthWithRange{}, err
return []queryproto.LiquidityDepthWithRange{}, invalidTickIndex, err
}

// Found the current bucket, update its index.
if currentBucketIndex == invalidTickIndex && concentratedPool.IsCurrentTickInRange(previousTickIndex, tickIndex) && currentTickLiquidity.Equal(totalLiquidityWithinRange) {
currentBucketIndex = int64(len(liquidityDepthsForRange))
}

liquidityDepthForRange := queryproto.LiquidityDepthWithRange{
Expand All @@ -80,7 +103,12 @@ func (k Keeper) GetTickLiquidityForFullRange(ctx sdk.Context, poolId uint64) ([]
totalLiquidityWithinRange = totalLiquidityWithinRange.Add(currentLiquidity)
}

return liquidityDepthsForRange, nil
// This signifies that currrent tick is above the max initialized tick
if currentTick >= previousTickIndex && currentTickLiquidity.IsZero() {
currentBucketIndex = int64(len(liquidityDepthsForRange))
}

return liquidityDepthsForRange, currentBucketIndex, nil
}

// GetLiquidityNetInDirection is a method that returns an array of TickLiquidityNet objects representing the net liquidity in a specified direction
Expand Down
Loading

0 comments on commit 53ab649

Please sign in to comment.