-
Notifications
You must be signed in to change notification settings - Fork 621
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): Add Liquidity Net In Direction Query #4714
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,15 @@ service Query { | |
"/osmosis/concentratedliquidity/v1beta1/total_liquidity_for_range"; | ||
} | ||
|
||
// LiquidityNetInDirection returns liquidity net in the direction given. | ||
// Uses the bound if specified, if not uses either min tick / max tick | ||
// depending on the direction. | ||
rpc LiquidityNetInDirection(QueryLiquidityNetInDirectionRequest) | ||
returns (QueryLiquidityNetInDirectionResponse) { | ||
option (google.api.http).get = "/osmosis/concentratedliquidity/v1beta1/" | ||
"query_liquidity_net_in_direction"; | ||
} | ||
|
||
// ClaimableFees returns the amount of fees that can be claimed by a position | ||
// with the given id. | ||
rpc ClaimableFees(QueryClaimableFeesRequest) | ||
|
@@ -154,7 +163,26 @@ message LiquidityDepthWithRange { | |
]; | ||
} | ||
|
||
//=============================== TickLiquidityInBatches | ||
//=============================== LiquidityNetInDirection | ||
message QueryLiquidityNetInDirectionRequest { | ||
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ]; | ||
string token_in = 2 [ (gogoproto.moretags) = "yaml:\"token_in\"" ]; | ||
string bound_tick = 3 [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How can we know we're requesting all ticks (in a direction)? Should we just pass in the hardcoded max_tick value? I'm thinking in the frontend, the estimates will go like:
Above is an edge case, as I want the above to work in most common cases (trading a moderate amount against a higher liquidity pool), this is an edge case scenario:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To estimate the expected final tick very roughly, we could take the current tick liquidity, take some proportion of that (e.g. 95%) because we assume that as we move away from the current tick, the liquidity decreases. Note, that we can come up with an algorithm to estimate the proportion too but can be hard coded to start. Finally, compute sqrt price delta in the following way:
Once we have that, we compute the "next expected sqrt price". From sqrt price, we can compute the expected final tick. If that fails, we could then do some form of binary search where we take the estimated tick delta
exclusively from the ticks we already received during prior querying to avoid redundant processing. Similar to how Go slices reallocate buffer as we append to them. Do you think we can keep the current design of querying until max bound for v1 and then work out a design for bounds estimates separately? @jonator @mattverse I think merging this would allow @mattverse and I to parallelize and further iterate on improvements Let me know what you all think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we can run that calculation to get the next tick index then perhaps add a few extra ticks to avoid any rounding error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that sounds right |
||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", | ||
(gogoproto.moretags) = "yaml:\"bound_tick\"", | ||
(gogoproto.nullable) = true | ||
]; | ||
} | ||
message QueryLiquidityNetInDirectionResponse { | ||
repeated LiquidityDepth liquidity_depths = 1 [ (gogoproto.nullable) = false ]; | ||
string current_liquidity = 2 [ | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.moretags) = "yaml:\"current_liquidity\"", | ||
(gogoproto.nullable) = false | ||
]; | ||
} | ||
|
||
//=============================== TotalLiquidityForRange | ||
message QueryTotalLiquidityForRangeRequest { | ||
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ]; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -211,6 +211,39 @@ func (q Querier) TotalLiquidityForRange(goCtx context.Context, req *clquery.Quer | |
return &clquery.QueryTotalLiquidityForRangeResponse{Liquidity: liquidity}, nil | ||
} | ||
|
||
// LiquidityNetInDirection returns an array of LiquidityDepthWithRange, which contains the range(lower tick and upper tick) and the liquidity amount in the range. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: ditto for godoc updates to reflect additional returns |
||
func (q Querier) LiquidityNetInDirection(goCtx context.Context, req *clquery.QueryLiquidityNetInDirectionRequest) (*clquery.QueryLiquidityNetInDirectionResponse, error) { | ||
if req == nil { | ||
return nil, status.Error(codes.InvalidArgument, "empty request") | ||
} | ||
ctx := sdk.UnwrapSDKContext(goCtx) | ||
|
||
// convert values from pointers | ||
var boundTick sdk.Int | ||
if req.BoundTick == nil { | ||
boundTick = sdk.Int{} | ||
} else { | ||
boundTick = *req.BoundTick | ||
} | ||
|
||
liquidityDepths, err := q.Keeper.GetLiquidityNetInDirection( | ||
ctx, | ||
req.PoolId, | ||
req.TokenIn, | ||
boundTick, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pool, err := q.Keeper.getPoolById(ctx, req.PoolId) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &clquery.QueryLiquidityNetInDirectionResponse{LiquidityDepths: liquidityDepths, CurrentLiquidity: pool.GetLiquidity()}, nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that we include "current_tick" and it's liquidity net in the In terms of the current tick, we only need to know a) the current tick index b) current tick liquidity (which we already return) So I suggest only including liquidity net amounts starting from the "next tick" from current in the swap direction Instead, we can have a separate field called Let me know what you think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CC: @jonator for awareness on this suggestion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming |
||
} | ||
|
||
func (q Querier) ClaimableFees(ctx context.Context, req *clquery.QueryClaimableFeesRequest) (*clquery.QueryClaimableFeesResponse, error) { | ||
if req == nil { | ||
return nil, status.Error(codes.InvalidArgument, "empty request") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if we go with the separate "current tick" and "current tick liquidity", it might be worth updating the comment to reflect these returns as well