-
Notifications
You must be signed in to change notification settings - Fork 607
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
fix(ProtoRev): Updating Binary Search Range with CL Pools #5930
Conversation
CHANGELOG.md
Outdated
@@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
* [#5831](https://github.com/osmosis-labs/osmosis/pull/5831) Fix superfluid_delegations query | |||
* [#5835](https://github.com/osmosis-labs/osmosis/pull/5835) Fix println's for "amountZeroInRemainingBigDec before fee" making it into production | |||
* [#5841] (https://github.com/osmosis-labs/osmosis/pull/5841) Fix protorev's out of gas erroring of the user's transcation. | |||
* [#5930] (https://github.com/osmosis-labs/osmosis/pull/5930) Updating Binary Search Range with CL Pools |
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.
* [#5930] (https://github.com/osmosis-labs/osmosis/pull/5930) Updating Binary Search Range with CL Pools | |
* [#5930] (https://github.com/osmosis-labs/osmosis/pull/5930) Updating Protorev Binary Search Range Logic with CL Pools |
continue | ||
} | ||
profit = uosmoProfit | ||
profit, err := k.ConvertProfits(ctx, inputCoin, profit) |
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.
I think we may also need to add bounding in ConvertProfits. If a CL pool is the highest liquidity pool then we will swap the entire amount without any tick bounds.
If a CL pool is the highest liquidity than I imagine this won't run into any issues, but maybe good to note in as a comment if we don't want to add the tick checking here that we are not doing so.
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.
adding a comment
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.
Am I correct in saying we went for the former and didn't just add a comment right?
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.
wait lol adding comment rn, misread the comment
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.
added the comment
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.
Per convo offline, we decided to not make the check. More likely than not we will not run into the issue of over-stepping MaxTicksCrossed
in ConvertProfits
. The upper bound check should be sufficient enough to cover most cases.
x/protorev/keeper/rebalance.go
Outdated
profit = uosmoProfit | ||
profit, err := k.ConvertProfits(ctx, inputCoin, profit) | ||
if err != nil { | ||
k.Logger(ctx).Error("Error converting profits: ", err) |
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.
This error won't propagate properly because it accepts kv pairs, let's follow this format:
osmosis/x/protorev/keeper/posthandler.go
Line 61 in a706848
ctx.Logger().Error("ProtoRevTrade failed with error: " + err.Error()) |
Realized I missed all places that use the format k.Logger(ctx).Error
, I replaced all the ctx.Logger().Error
instances, can we add this fix in this PR too for all k.Logger(ctx).Error
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.
gotchu fam
x/protorev/keeper/rebalance.go
Outdated
} | ||
|
||
// If the profit for the maximum amount in is still increasing, then we can increase the range of the binary search | ||
if maxInProfit.GTE(sdk.ZeroInt()) { | ||
maxInPlusOne := curRight.Add(sdk.OneInt()).Mul(route.StepSize) | ||
if updatedMax.LT(maxInPlusOne) { |
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.
I think updatedMax is not multiplied by stepsize since the check previously in UpdateSearchRangeIfNeeded
is
if updatedMax.LT(curRight) {
return curLeft, updatedMax, nil
}
so this check smells wrong to me
x/protorev/keeper/rebalance.go
Outdated
|
||
// MaxInputAmount returns the max input amount that can be used in any route. We use uosmo as the base | ||
// unit of account for the max input amount. | ||
func (k Keeper) MaxInputAmount(ctx sdk.Context) (sdk.Int, sdk.Int, error) { |
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.
Re: our in-person sync, if we go the reverse iteration method I don't think we need to choose any base unit of account for this so should be able to remove
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.
left some comments and synced in person about using a reverse iteration approach to reduce the bounds
x/protorev/keeper/keeper_test.go
Outdated
s.CreateFullRangePosition(clPool, fundCoins) | ||
|
||
// Create a concentrated liquidity pool for range testing | ||
// Pool 52 |
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.
// Pool 52 | |
// Pool 53 |
Will update the doomsday gas test case in a follow up PR (where you can configure max ticks moved). |
@@ -340,12 +340,12 @@ func (s *KeeperTestSuite) TestAnteHandle() { | |||
params: param{ | |||
trades: []types.Trade{ | |||
{ | |||
Pool: 51, | |||
Pool: 54, |
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.
Synced in person: So after the tick change, this test no longer tests what we want it to since this does not run out of gas. So in the follow up PR that allows maxTicks to be configurable, we'll make the maxTicks really large for this test so that the rebalance logic runs out of gas again
x/protorev/types/constants.go
Outdated
@@ -27,6 +27,10 @@ const MaxPoolPointsPerTx uint64 = 50 | |||
// to the maximum execution time (in ms) of protorev per block | |||
const MaxPoolPointsPerBlock uint64 = 200 | |||
|
|||
// Max number of ticks we can move in a concentrated pool swap. This will be parameterized in a | |||
// follow up PR. | |||
const MaxTicksMoved uint64 = 5 |
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.
Let's call this MaxTicksCrossed to stay consistent with the CL method naming of ComputeMaxInAmtGivenMaxTicksCrossed
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.
Nice work on this, was super clear/easy to review despite lacking context.
continue | ||
} | ||
profit = uosmoProfit | ||
profit, err := k.ConvertProfits(ctx, inputCoin, profit) |
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.
Am I correct in saying we went for the former and didn't just add a comment right?
} | ||
|
||
// Ensure that the normalized upper bound is not greater than the extended max input amount | ||
upperBound := intermidiateCoin.Amount.Quo(route.StepSize) |
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.
Is it possible for a route's step size to be zero?
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.
tldr no, we manually set these and enforce that the step size cannot be zero (when we run validate basic on a msg).
x/protorev/keeper/rebalance.go
Outdated
// At most we can swap half of the liquidity in the pool | ||
liquidTokenAmt := liquidity.AmountOf(outputCoin.Denom).Quo(sdk.NewInt(2)) |
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.
Is this just a self imposed limit?
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.
Yea, this would allow us to still capture some arb on illiquid pools while ensuring we never error out when we make the calculation on upper bound. Additionally, on larger/more liquid pools, the trade size will never be larger than ExtendedMaxInputAmount
which most certainly will be less than half the liquidity in a liquid pool.
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.
I will admit that this is a bit of a hacky work around atm.
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.
lgtm
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.
this lgtm, can we please update the README.md with this changes
return sdk.NewCoin(tokenInDenom, sdk.ZeroInt()), err | ||
} | ||
|
||
// At most we can swap half of the liquidity in the pool |
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.
This is not merge-blocking, but due to the way we handle rounding (always in the pool's favor and can be meaningfully high in some scenarios), this assumption does not necessarily true.
Rounding error can be nontrivial here and accumulates over time, so while this is likely the simplest way to handle this specific case, it should be kept in mind for future logic leaning on assumptions related to absolute assets in the pool.
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.
Yea, totally agree. We've been going back and forth on how to best estimate maximal swap in when CL/stable/balancer pools are involved and we more or less ran into the same issue with all of our approaches. I'll decrease this to 1/4
for now and will revisit this once we have a better idea of how frequently this is an actual issue.
Also related, we plan on doing some structural refactoring - decoupling the various components of protorev - sometime soon which will let us navigate this with more granularity.
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.
@AlpinYukseloglu Based on your understanding, does the reduction to 1/4 improve the situation or are you thinking of a different approach here?
@stackman27 anything specific you want updated in the |
maybe something around how "UpdateSearchRangeIfNeeded" updates the search range for BS. And what we do when BS gets gas intensive with CL pools |
Updated the readme. @stackman27 |
What is the purpose of the change
This PR introduces a fix to
protorev
to update the binary search range in the case where it is too gas intensive to search over the hard-coded range. This is particularly useful for Concentrated Liquidity pools where moving across several ticks is very expensive. By determining if a route has an CL pools in it, we can bound the upper input amount by figuring out the maximum input amount respecting a maximum number of ticks. We do so across all CL pools and use this as our maximum input amount on the entire route.Note,
MaxTicksMoved
is currently hard coded, but we plan to update that in a follow up PR. This will be configurable by theDeveloperAccount
.Testing and Verifying
This change added tests and can be verified as follows:
TestFindMaxProfitRoute
that test liquid and illiquid CL poolsDocumentation and Release Note
This PR is related to these open issues (#5858) and (#5884)