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] Change calcOutAmtGivenIn and calcInAmtGivenOut to be non-mutative #3910

Merged
merged 12 commits into from
Jan 12, 2023

Conversation

mattverse
Copy link
Member

Closes: #3889

What is the purpose of the change

This PR changes the two calc methods, namely calcOutAmtGivenIn and calcInAmtGivenOut to be non mutative methods, as previously they have been mutating ticks of the pool when upon crossing tick during iteration process of the calculation.

Also added existing checks to existing test cases that ensures that pool has not been changed after performing calculation method.

Testing and Verifying

Changed existing test to cover changes made within this PR

@mattverse mattverse added the V:state/compatible/no_backport State machine compatible PR, depends on prior breaks label Jan 3, 2023
@@ -210,6 +210,7 @@ func (k Keeper) calcOutAmtGivenIn(ctx sdk.Context,
priceLimit sdk.Dec,
poolId uint64,
) (tokenIn, tokenOut sdk.Coin, updatedTick sdk.Int, updatedLiquidity, updatedSqrtPrice sdk.Dec, err error) {
ctx, _ = ctx.CacheContext()
Copy link
Member

Choose a reason for hiding this comment

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

We should return the second return of CacheContext from the caller (calcOutAmtGivenIn) itself. The second return is a callback to write the cache context.

The reason for this requirement is that if calcOutAmtGivenIn is called from swapOtAmtGivenIn, we actually do want to write the cache context to storage.

Copy link
Member Author

Choose a reason for hiding this comment

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

I also noticed that part in the original issue, but did not quite understand.

What's the reason we want to use write cache context here? Do we not store in state anyways using the results from calling the calc method anyways within the swap method?(https://github.com/osmosis-labs/osmosis/blob/main/x/concentrated-liquidity/swaps.go#L167)

Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if this is what you are saying Roman, but shouldnt we pass this cache ctx as a return value and then write in the swap function that calls this? If that is what you are saying, I agree. Without doing this, despite us calling applySwap, the tick boundaries that got passed wont be updated.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that's what I meant

Copy link
Member Author

Choose a reason for hiding this comment

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

Gotcha, this makes sense

@mattverse
Copy link
Member Author

@p0mvn @czarcas7ic Would it be possible to give a quick conceptACK on the direction we're going that I put in da1c910 before I make the changes further on?

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.

This is what I meant in an earlier discussion

@@ -209,10 +209,11 @@ func (k Keeper) calcOutAmtGivenIn(ctx sdk.Context,
swapFee sdk.Dec,
priceLimit sdk.Dec,
poolId uint64,
) (tokenIn, tokenOut sdk.Coin, updatedTick sdk.Int, updatedLiquidity, updatedSqrtPrice sdk.Dec, err error) {
) (cacheCtx sdk.Context, tokenIn, tokenOut sdk.Coin, updatedTick sdk.Int, updatedLiquidity, updatedSqrtPrice sdk.Dec, err error) {
ctx, _ = ctx.CacheContext()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ctx, _ = ctx.CacheContext()
ctx, writeCtx := ctx.CacheContext()

@@ -335,7 +336,7 @@ func (k Keeper) calcOutAmtGivenIn(ctx sdk.Context,
tokenIn = sdk.NewCoin(tokenInMin.Denom, amt0)
tokenOut = sdk.NewCoin(tokenOutDenom, amt1)

return tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil
return ctx, tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return ctx, tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil
return ctx, tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil
Suggested change
return ctx, tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil
return writeCtx, tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil

@@ -178,12 +178,12 @@ func (k Keeper) CalcOutAmtGivenIn(
tokenIn sdk.Coin,
tokenOutDenom string,
swapFee sdk.Dec,
) (tokenOut sdk.Coin, err error) {
_, tokenOut, _, _, _, err = k.calcOutAmtGivenIn(ctx, tokenIn, tokenOutDenom, swapFee, sdk.ZeroDec(), poolI.GetId())
) (newCtx sdk.Context, tokenOut sdk.Coin, err error) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
) (newCtx sdk.Context, tokenOut sdk.Coin, err error) {
) (writeCache func(), tokenOut sdk.Coin, err error) {

@mattverse
Copy link
Member Author

@p0mvn Thanks for the clarification. I'm not knowledgable of writeCtx() function that much, but AFAIU, writeCtx works in pair with the cached context returned by ctx.CacheContext(). That is, AFAIU, only returning and calling the writeCtx function would not have effect on the actual context being used. See 1738558#diff-1a552fcc0a3d36556402345e840b658a7daccb101d7f73bca9ee2e3057ade2e6R584-R593 for example. This was why I originally tried the approach of returning the context itself. Is there something I'm misunderstanding about how writeCtx works?

@p0mvn
Copy link
Member

p0mvn commented Jan 11, 2023

@p0mvn Thanks for the clarification. I'm not knowledgable of writeCtx() function that much, but AFAIU, writeCtx works in pair with the cached context returned by ctx.CacheContext(). That is, AFAIU, only returning and calling the writeCtx function would not have effect on the actual context being used. See 1738558#diff-1a552fcc0a3d36556402345e840b658a7daccb101d7f73bca9ee2e3057ade2e6R584-R593 for example. This was why I originally tried the approach of returning the context itself. Is there something I'm misunderstanding about how writeCtx works?

What are you trying to test in the linked example? It seems that you're trying to assert that the pool has been updated.

However, it would not be because we write pool data to store in applySwap function:

err = k.applySwap(ctx, tokenIn, tokenOut, poolId, newLiquidity, newCurrentTick, newSqrtPrice)

In fact, currently, we do not write anything to store inside calcOutAmtGivenIn and calcInAmtGivenOut. However, we should be writing ticks: #3890

This trick this writeCtx function is to make sure that we control when ticks are written to store once #3890 is implemented

@mattverse
Copy link
Member Author

@p0mvn Thanks for the clarification. I'm not knowledgable of writeCtx() function that much, but AFAIU, writeCtx works in pair with the cached context returned by ctx.CacheContext(). That is, AFAIU, only returning and calling the writeCtx function would not have effect on the actual context being used. See 1738558#diff-1a552fcc0a3d36556402345e840b658a7daccb101d7f73bca9ee2e3057ade2e6R584-R593 for example. This was why I originally tried the approach of returning the context itself. Is there something I'm misunderstanding about how writeCtx works?

What are you trying to test in the linked example? It seems that you're trying to assert that the pool has been updated.

However, it would not be because we write pool data to store in applySwap function:

err = k.applySwap(ctx, tokenIn, tokenOut, poolId, newLiquidity, newCurrentTick, newSqrtPrice)

In fact, currently, we do not write anything to store inside calcOutAmtGivenIn and calcInAmtGivenOut. However, we should be writing ticks: #3890

This trick this writeCtx function is to make sure that we control when ticks are written to store once #3890 is implemented

I see, then do you have suggestion upon testing on state change by calling the writeCtx function as linked in my original question? If not, would pass on the testings for this specific case until PR #3890 is implemented

@p0mvn
Copy link
Member

p0mvn commented Jan 12, 2023

@p0mvn Thanks for the clarification. I'm not knowledgable of writeCtx() function that much, but AFAIU, writeCtx works in pair with the cached context returned by ctx.CacheContext(). That is, AFAIU, only returning and calling the writeCtx function would not have effect on the actual context being used. See 1738558#diff-1a552fcc0a3d36556402345e840b658a7daccb101d7f73bca9ee2e3057ade2e6R584-R593 for example. This was why I originally tried the approach of returning the context itself. Is there something I'm misunderstanding about how writeCtx works?

What are you trying to test in the linked example? It seems that you're trying to assert that the pool has been updated.
However, it would not be because we write pool data to store in applySwap function:

err = k.applySwap(ctx, tokenIn, tokenOut, poolId, newLiquidity, newCurrentTick, newSqrtPrice)

In fact, currently, we do not write anything to store inside calcOutAmtGivenIn and calcInAmtGivenOut. However, we should be writing ticks: #3890
This trick this writeCtx function is to make sure that we control when ticks are written to store once #3890 is implemented

I see, then do you have suggestion upon testing on state change by calling the writeCtx function as linked in my original question? If not, would pass on the testings for this specific case until PR #3890 is implemented

I think it's fine to pass testing until #3890 is implemented. Could you please make sure that we track adding these tests in #3890?

@mattverse
Copy link
Member Author

@p0mvn made #3992 to track this. Also this PR is r4r, please feel free to give reviews for this PR

@mattverse mattverse requested a review from p0mvn January 12, 2023 07:39
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.

LGTM

x/concentrated-liquidity/swaps.go Show resolved Hide resolved
x/concentrated-liquidity/swaps.go Show resolved Hide resolved
@@ -203,16 +205,18 @@ func (k Keeper) CalcInAmtGivenOut(
// calcOutAmtGivenIn calculates tokens to be swapped out given the provided amount and fee deducted. It also returns
// what the updated tick, liquidity, and currentSqrtPrice for the pool would be after this swap.
// Note this method is non-mutative, so the values returned by CalcOutAmtGivenIn do not get stored
// Instead, we return writeCtx function so that the caller of this method can decide to write the cached ctx to store or not.
Copy link
Member

Choose a reason for hiding this comment

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

Do you mind adding spec for calcInAmtGivenOut as a drive by change in this PR please?

Just noticed it's missing

@p0mvn
Copy link
Member

p0mvn commented Jan 12, 2023

Going to merge this as discussed offline.

We can iterate on this further in #3890 and #3992. Thanks for making the issue

@p0mvn p0mvn merged commit fa47daa into main Jan 12, 2023
@p0mvn p0mvn deleted the mattverse/cl-calc-non-mutative branch January 12, 2023 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:x/concentrated-liquidity V:state/compatible/no_backport State machine compatible PR, depends on prior breaks
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug(CL): make calcOutAmtGivenIn and calcInAmtGivenOut non-mutative
3 participants