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

fix: zero for one tick crossing bug for swap in given out #5591

Merged
merged 3 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions x/concentrated-liquidity/swaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,15 +330,15 @@ func (k Keeper) computeOutAmtGivenIn(
// if zeroForOneStrategy, we look to the left of the tick the current sqrt price is at
// if oneForZeroStrategy, we look to the right of the tick the current sqrt price is at
// if no ticks are initialized (no users have created liquidity positions) then we return an error
nextTick, err := types.TickIndexFromBytes(nextInitTickIter.Key())
nextInitializedTick, err := types.TickIndexFromBytes(nextInitTickIter.Key())
if err != nil {
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err
}

// Utilizing the next initialized tick, we find the corresponding nextInitializedTickSqrtPrice (the target sqrt price).
_, nextInitializedTickSqrtPrice, err := math.TickToSqrtPrice(nextTick)
_, nextInitializedTickSqrtPrice, err := math.TickToSqrtPrice(nextInitializedTick)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextTick)
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextInitializedTick)
}

// If nextInitializedTickSqrtPrice exceeds the given price limit, we set the sqrtPriceTarget to the price limit.
Expand Down Expand Up @@ -369,13 +369,11 @@ func (k Keeper) computeOutAmtGivenIn(
// If ComputeSwapWithinBucketOutGivenIn(...) calculated a computedSqrtPrice that is equal to the nextInitializedTickSqrtPrice, this means all liquidity in the current
// bucket has been consumed and we must move on to the next bucket to complete the swap
if nextInitializedTickSqrtPrice.Equal(computedSqrtPrice) {
swapState, err = k.swapCrossTickLogic(ctx, swapState,
nextTick, nextInitTickIter, p, spreadRewardAccumulator, uptimeAccums, tokenInMin.Denom)
swapState, err = k.swapCrossTickLogic(ctx, swapState, swapStrategy,
nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, uptimeAccums, tokenInMin.Denom)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err
}
// Update the swapState's tick with the tick we retrieved liquidity from
swapState.tick = swapStrategy.UpdateTickAfterCrossing(nextTick)
} else if !sqrtPriceStart.Equal(computedSqrtPrice) {
// Otherwise if the sqrtPrice calculated from ComputeSwapWithinBucketOutGivenIn(...) does not equal the sqrtPriceStart we started with at the
// beginning of this iteration, we set the swapState tick to the corresponding tick of the computedSqrtPrice calculated from ComputeSwapWithinBucketOutGivenIn(...)
Expand Down Expand Up @@ -509,15 +507,15 @@ func (k Keeper) computeInAmtGivenOut(
// If the ComputeSwapWithinBucketInGivenOut(...) calculated a computedSqrtPrice that is equal to the nextInitializedTickSqrtPrice, this means all liquidity in the current
// bucket has been consumed and we must move on to the next bucket by crossing a tick to complete the swap
if nextInitializedTickSqrtPrice.Equal(computedSqrtPrice) {
swapState, err = k.swapCrossTickLogic(ctx, swapState,
swapState, err = k.swapCrossTickLogic(ctx, swapState, swapStrategy,
nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, uptimeAccums, tokenInDenom)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err
}
} else if !sqrtPriceStart.Equal(computedSqrtPrice) {
// Otherwise, if the computedSqrtPrice calculated from ComputeSwapWithinBucketInGivenOut(...) does not equal the sqrtPriceStart we started with at the
// beginning of this iteration, we set the swapState tick to the corresponding tick of the computedSqrtPrice calculated from ComputeSwapWithinBucketInGivenOut(...)
swapState.tick, err = math.SqrtPriceToTickRoundDownSpacing(computedSqrtPrice, p.GetTickSpacing())
swapState.tick, err = math.CalculateSqrtPriceToTick(computedSqrtPrice)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, sdk.Dec{}, err
}
Expand Down Expand Up @@ -553,7 +551,7 @@ func emitSwapDebugLogs(ctx sdk.Context, swapState SwapState, reachedPrice, amoun

// logic for crossing a tick during a swap
func (k Keeper) swapCrossTickLogic(ctx sdk.Context,
swapState SwapState,
swapState SwapState, strategy swapstrategy.SwapStrategy,
nextInitializedTick int64, nextTickIter db.Iterator,
p types.ConcentratedPoolExtension,
spreadRewardAccum accum.AccumulatorObject, uptimeAccums []accum.AccumulatorObject,
Expand All @@ -580,6 +578,9 @@ func (k Keeper) swapCrossTickLogic(ctx sdk.Context,
// Update the swapState's liquidity with the new tick's liquidity
swapState.liquidity.AddMut(liquidityNet)

// Update the swapState's tick with the tick we retrieved liquidity from
swapState.tick = strategy.UpdateTickAfterCrossing(nextInitializedTick)

return swapState, nil
}

Expand Down
34 changes: 17 additions & 17 deletions x/concentrated-liquidity/swaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin(USDC, sdk.NewInt(42000000)),
expectedTokenIn: sdk.NewCoin(ETH, sdk.NewInt(8404)),
expectedTick: 30996000,
expectedTick: 30996087,
expectedSqrtPrice: sdk.MustNewDecFromStr("70.683007989825007163"),
},
"single position within one tick: usdc (in) -> eth (out) ofz": {
Expand All @@ -786,7 +786,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(13370)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(66891663)),
expectedTick: 31006200,
expectedTick: 31006234,
// True value with arbitrary precision: 70.7547471884689004674...
// Expected value due to our monotonic sqrt's >= true value guarantee: 70.754747188468900468
expectedSqrtPrice: sdk.MustNewDecFromStr("70.754747188468900468"),
Expand Down Expand Up @@ -818,7 +818,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(66829187)),
expectedTokenIn: sdk.NewCoin("eth", sdk.NewInt(13370)),
expectedTick: 30996800,
expectedTick: 30996887,
// This value is the direct output of sqrt_next in the script above.
// The precision is exact because we properly handle rounding behavior in intermediate steps.
expectedSqrtPrice: sdk.MustNewDecFromStr("70.688664163727643651"),
Expand All @@ -844,7 +844,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin("eth", sdk.NewInt(8398)),
expectedTokenIn: sdk.NewCoin("usdc", sdk.NewInt(41998216)),
expectedTick: 31001900,
expectedTick: 31001956,
expectedSqrtPrice: sdk.MustNewDecFromStr("70.724512595179305567"),
// two positions with same liquidity entered
poolLiqAmount0: sdk.NewInt(1000000).MulRaw(2),
Expand Down Expand Up @@ -887,7 +887,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(9103422788)),
expectedTokenIn: sdk.NewCoin("eth", sdk.NewInt(2000000)),
expectedTick: 30095100,
expectedTick: 30095166,

expectedSqrtPrice: sdk.MustNewDecFromStr("63.993489023888951975"),
expectedLowerTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(sdk.NewDec(2)),
Expand Down Expand Up @@ -937,7 +937,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1820630)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(9999999570)),
expectedTick: 32105400,
expectedTick: 32105414,
// True value with arbitrary precision: 78.1371488370367515544...
// Expected value due to our monotonic sqrt's >= true value guarantee: 78.137148837036751555
expectedSqrtPrice: sdk.MustNewDecFromStr("78.137148837036751555"),
Expand Down Expand Up @@ -994,7 +994,7 @@ var (
// print(token_in)
expectedTokenIn: sdk.NewCoin("eth", sdk.NewInt(2000000)),
expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(9321276930)),
expectedTick: 30129000,
expectedTick: 30129083,
expectedSqrtPrice: sdk.MustNewDecFromStr("64.257943796086567725"),
// Started from DefaultSpreadRewardAccumCoins * 3, crossed tick once, thus becoming
// DefaultSpreadRewardAccumCoins * 3 - DefaultSpreadRewardAccumCoins = DefaultSpreadRewardAccumCoins * 2
Expand Down Expand Up @@ -1050,7 +1050,7 @@ var (
// print(token_in)
expectedTokenIn: sdk.NewCoin(ETH, sdk.NewInt(1800000)),
expectedTokenOut: sdk.NewCoin(USDC, sdk.NewInt(8479320318)),
expectedTick: 30292000,
expectedTick: 30292059,
expectedSqrtPrice: sdk.MustNewDecFromStr("65.513815286452064191"),
// Started from DefaultSpreadRewardAccumCoins * 3, crossed tick once, thus becoming
// DefaultSpreadRewardAccumCoins * 3 - DefaultSpreadRewardAccumCoins = DefaultSpreadRewardAccumCoins * 2
Expand Down Expand Up @@ -1111,7 +1111,7 @@ var (
// print(token_in)
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(9999994688)),
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1864161)),
expectedTick: 32055900,
expectedTick: 32055918,
// True value with arbitrary precision: 77.8197817118765535784...
// Expected value due to our monotonic sqrt's >= true value guarantee: 77.819781711876553579
expectedSqrtPrice: sdk.MustNewDecFromStr("77.819781711876553579"),
Expand Down Expand Up @@ -1165,7 +1165,7 @@ var (
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1609138)),
expectedSecondLowerTickSpreadRewardGrowth: secondPosition{tickIndex: 310010, expectedSpreadRewardGrowth: cl.EmptyCoins},
expectedSecondUpperTickSpreadRewardGrowth: secondPosition{tickIndex: 322500, expectedSpreadRewardGrowth: cl.EmptyCoins},
expectedTick: 31712600,
expectedTick: 31712695,
// True value with arbitrary precision: 75.5823723551285943429...
// Expected value due to our monotonic sqrt's >= true value guarantee: 75.582372355128594343
expectedSqrtPrice: sdk.MustNewDecFromStr("75.582372355128594343"),
Expand Down Expand Up @@ -1212,7 +1212,7 @@ var (
// print(token_in)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1820545)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(9999994756)),
expectedTick: 32105500,
expectedTick: 32105554,
// True value with arbitrary precision: 78.1380507971736470319
// Expected value due to our monotonic sqrt's >= true value guarantee: 78.138050797173647032
expectedSqrtPrice: sdk.MustNewDecFromStr("78.138050797173647032"),
Expand Down Expand Up @@ -1278,7 +1278,7 @@ var (
// print(spread_rewards_growth)
expectedTokenOut: sdk.NewCoin(USDC, sdk.NewInt(42000000)),
expectedTokenIn: sdk.NewCoin(ETH, sdk.NewInt(8489)),
expectedTick: 30996000,
expectedTick: 30996087,
// This value is the direct output of sqrt_next in the script above.
// The precision is exact because we properly handle rounding behavior in intermediate steps.
expectedSqrtPrice: sdk.MustNewDecFromStr("70.683007989825007163"),
Expand Down Expand Up @@ -1310,7 +1310,7 @@ var (
// print(spread_rewards_growth)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(8398)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(43297130)),
expectedTick: 31001900,
expectedTick: 31001956,
// True value with arbitrary precision: 70.7245125951793055663...
// Expected value due to our monotonic sqrt's >= true value guarantee: 70.724512595179305567
expectedSqrtPrice: sdk.MustNewDecFromStr("70.724512595179305567"),
Expand Down Expand Up @@ -1360,7 +1360,7 @@ var (
// print(spread_rewards_growth)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1820630)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(10010009580)),
expectedTick: 32105400,
expectedTick: 32105414,
// True value with arbitrary precision is 78.1371488370367515544...,
// which we expect to be pushed up to 78.137148837036751555 given our
// sqrt function's >= true value guarantee
Expand Down Expand Up @@ -1419,7 +1419,7 @@ var (
// print(spread_rewards_growth)
expectedTokenIn: sdk.NewCoin("eth", sdk.NewInt(2222223)),
expectedTokenOut: sdk.NewCoin("usdc", sdk.NewInt(9321276930)),
expectedTick: 30129000,
expectedTick: 30129083,
expectedSqrtPrice: sdk.MustNewDecFromStr("64.257943796086567725"),
// Started from DefaultSpreadRewardAccumCoins * 3, crossed tick once, thus becoming
// DefaultSpreadRewardAccumCoins * 3 - DefaultSpreadRewardAccumCoins = DefaultSpreadRewardAccumCoins * 2
Expand Down Expand Up @@ -1482,7 +1482,7 @@ var (
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1609138)),
expectedSecondLowerTickSpreadRewardGrowth: secondPosition{tickIndex: 310010, expectedSpreadRewardGrowth: cl.EmptyCoins},
expectedSecondUpperTickSpreadRewardGrowth: secondPosition{tickIndex: 322500, expectedSpreadRewardGrowth: cl.EmptyCoins},
expectedTick: 31712600,
expectedTick: 31712695,
// True value with arbitrary precision: 75.5823723551285943429...
// Expected value due to our monotonic sqrt's >= true value guarantee: 75.582372355128594343
expectedSqrtPrice: sdk.MustNewDecFromStr("75.582372355128594343"),
Expand Down Expand Up @@ -1528,7 +1528,7 @@ var (
// print(spread_rewards_growth)
expectedTokenOut: sdk.NewCoin(ETH, sdk.NewInt(1820545)),
expectedTokenIn: sdk.NewCoin(USDC, sdk.NewInt(10002995655)),
expectedTick: 32105500,
expectedTick: 32105554,
// True value with arbitrary precision: 78.13805079717364703195...
// Expected value due to our monotonic sqrt's >= true value guarantee: 78.138050797173647032
expectedSqrtPrice: sdk.MustNewDecFromStr("78.138050797173647032"),
Expand Down