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): Add Liquidity Net In Direction Query #4714

Merged
merged 6 commits into from
Mar 29, 2023
Merged

(CL): Add Liquidity Net In Direction Query #4714

merged 6 commits into from
Mar 29, 2023

Conversation

mattverse
Copy link
Member

Closes: #XXX

What is the purpose of the change

A query to better support front-end. Given ZeroForOne and the tick designated to iterate until(optional), iterates over ticks that are initialized until we meet the designated tick, and returns the tick indexes along with the liquidity net in each tick.

Brief Changelog

  • Add liquidity Net in Direction Query

Testing and Verifying

  • Tests are added

Documentation and Release Note

  • Does this pull request introduce a new feature or user-facing behavior changes? (yes / no)
  • Is a relevant changelog entry added to the Unreleased section in CHANGELOG.md? (yes / no)
  • How is the feature or change documented? (not applicable / specification (x/<module>/spec/) / Osmosis docs repo / not documented)

@mattverse mattverse added the V:state/breaking State machine breaking PR label Mar 23, 2023
Copy link
Member

@p0mvn p0mvn left a comment

Choose a reason for hiding this comment

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

Great work. A couple of nits but the general logic looks solid.

x/concentrated-liquidity/tick.go Outdated Show resolved Hide resolved
x/concentrated-liquidity/tick.go Outdated Show resolved Hide resolved
x/concentrated-liquidity/tick.go Outdated Show resolved Hide resolved
x/concentrated-liquidity/tick_test.go Outdated Show resolved Hide resolved
@mattverse mattverse requested a review from p0mvn March 24, 2023 11:55
@mattverse mattverse requested a review from p0mvn March 27, 2023 14:05
return nil, err
}

return &clquery.QueryLiquidityNetInDirectionResponse{LiquidityDepths: liquidityDepths, CurrentLiquidity: pool.GetLiquidity()}, nil
Copy link
Member

@p0mvn p0mvn Mar 27, 2023

Choose a reason for hiding this comment

The 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 liquidityDepths. However, current tick's liquidity_net is irrelevant for FE swap estimate.

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 CurrentTick similar to CurrentLiquidity

Let me know what you think

Copy link
Member

Choose a reason for hiding this comment

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

CC: @jonator for awareness on this suggestion

return nil, err
}

return &clquery.QueryLiquidityNetInDirectionResponse{LiquidityDepths: liquidityDepths, CurrentLiquidity: pool.GetLiquidity()}, nil
Copy link
Member

Choose a reason for hiding this comment

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

Consider renaming liquidityDepths -> liquidityNetAmounts or something like that because net amount is different from depths IMO

Copy link
Member

@p0mvn p0mvn left a comment

Choose a reason for hiding this comment

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

Overall LGTM

Some minor comments about handling the current tick separately from the rest. I resolved all comments that I think were addressed, leaving only the comments that still require replies/changes.

I'm ready to merge once the current tick request is addressed and the overall design is accepted by Jon.

Will wait till Jon's acceptance before approving

Comment on lines +52 to +54
// 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.
Copy link
Member

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

@@ -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.
Copy link
Member

Choose a reason for hiding this comment

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

nit: ditto for godoc updates to reflect additional returns

x/concentrated-liquidity/tick.go Show resolved Hide resolved
x/concentrated-liquidity/tick_test.go Show resolved Hide resolved
message QueryLiquidityNetInDirectionRequest {
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
string token_in = 2 [ (gogoproto.moretags) = "yaml:\"token_in\"" ];
string bound_tick = 3 [
Copy link
Member

@jonator jonator Mar 28, 2023

Choose a reason for hiding this comment

The 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:

  • Try to reduce query size and roughly estimate how many ticks we'll need based on size of trade and liquidity in pool.

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:

  • If we run out of ticks in simulation iteration, we'll assume it's because we didn't request enough ticks. Then we'll try again with all ticks in a given direction, by passing in either maxTick into bound_tick or using some special token/flag?? @mattverse @p0mvn
  • Only after we've tried getting the remaining tick net values, can we conclude the pool doesn't have enough liquidity to handle the user's current trade, where we will show an error.

Copy link
Member

Choose a reason for hiding this comment

The 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:

  • swapping y for x:

$$\Delta \sqrt P = \Delta y / L$$

  • swapping x for y:

$$\Delta \sqrt P = L / \Delta x$$

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 $$\Delta T$$ and if that wasn't enough, we query the next:

  • $$2 \Delta T$$
  • $$4 \Delta T$$

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

Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that sounds right

@jonator
Copy link
Member

jonator commented Mar 29, 2023

Todo after this PR @p0mvn @mattverse

Inspired by this thread

  • Support optional min ticks, since we'll be refetching for the same pool and shouldn't transmit the same ticks
    • Haven't decided if I'll actually use this on frontend yet, as it may be beneficial/more accurate to requery all ticks since they change often. Regardless, I think it's still worth adding
  • Not sure how this affects queries: some way to estimate the upper needed tick based on the user's trade size

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants