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

refactor(CL): rounding edge case in zfo strategy and tests #6379

Closed
wants to merge 12 commits into from
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [#6334](https://github.com/osmosis-labs/osmosis/pull/6334) fix: enable taker fee cli
* [#6352](https://github.com/osmosis-labs/osmosis/pull/6352) Reduce error blow-up in CalcAmount0Delta by changing the order of math operations.
* [#6379](https://github.com/osmosis-labs/osmosis/pull/6379) Fix rounding edge case in zfo strategy that leads to
infinite loop in swap out given in.

### API Breaks

* [#6256](https://github.com/osmosis-labs/osmosis/pull/6256) Refactor CalcPriceToTick to operate on BigDec price to support new price range.
* [#6317](https://github.com/osmosis-labs/osmosis/pull/6317) Remove price return from CL `math.TickToSqrtPrice`
* [#6368](https://github.com/osmosis-labs/osmosis/pull/6368) Convert priceLimit API in CL swaps to BigDec

## v19.0.0

Expand Down
10 changes: 5 additions & 5 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (k Keeper) SwapOutAmtGivenIn(
tokenIn sdk.Coin,
tokenOutDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
priceLimit osmomath.BigDec) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
return k.swapOutAmtGivenIn(ctx, sender, pool, tokenIn, tokenOutDenom, spreadFactor, priceLimit)
}

Expand All @@ -70,7 +70,7 @@ func (k Keeper) ComputeOutAmtGivenIn(
tokenInMin sdk.Coin,
tokenOutDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,

) (swapResult SwapResult, poolUpdates PoolUpdates, err error) {
return k.computeOutAmtGivenIn(ctx, poolId, tokenInMin, tokenOutDenom, spreadFactor, priceLimit)
Expand All @@ -83,7 +83,7 @@ func (k Keeper) SwapInAmtGivenOut(
desiredTokenOut sdk.Coin,
tokenInDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
priceLimit osmomath.BigDec) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
return k.swapInAmtGivenOut(ctx, sender, pool, desiredTokenOut, tokenInDenom, spreadFactor, priceLimit)
}

Expand All @@ -92,7 +92,7 @@ func (k Keeper) ComputeInAmtGivenOut(
desiredTokenOut sdk.Coin,
tokenInDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,
poolId uint64,

) (swapResult SwapResult, poolUpdates PoolUpdates, err error) {
Expand Down Expand Up @@ -321,7 +321,7 @@ func (k Keeper) GetLargestSupportedUptimeDuration(ctx sdk.Context) time.Duration

func (k Keeper) SetupSwapStrategy(ctx sdk.Context, p types.ConcentratedPoolExtension,
spreadFactor osmomath.Dec, tokenInDenom string,
priceLimit osmomath.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) {
priceLimit osmomath.BigDec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) {
return k.setupSwapStrategy(p, spreadFactor, tokenInDenom, priceLimit)
}

Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ func (s *KeeperTestSuite) swap(pool types.ConcentratedPoolExtension, swapInFunde
// // Execute swap
fmt.Printf("swap in: %s\n", swapInFunded)
cacheCtx, writeOutGivenIn := s.Ctx.CacheContext()
_, tokenOut, _, err := s.clk.SwapOutAmtGivenIn(cacheCtx, s.TestAccs[0], pool, swapInFunded, swapOutDenom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroDec())
_, tokenOut, _, err := s.clk.SwapOutAmtGivenIn(cacheCtx, s.TestAccs[0], pool, swapInFunded, swapOutDenom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroBigDec())
if errors.As(err, &types.InvalidAmountCalculatedError{}) {
// If the swap we're about to execute will not generate enough output, we skip the swap.
// it would error for a real user though. This is good though, since that user would just be burning funds.
Expand All @@ -307,7 +307,7 @@ func (s *KeeperTestSuite) swap(pool types.ConcentratedPoolExtension, swapInFunde
// We expect the returned amountIn to be roughly equal to the original swapInFunded.
cacheCtx, _ = s.Ctx.CacheContext()
fmt.Printf("swap out: %s\n", tokenOut)
amountInSwapResult, _, _, err := s.clk.SwapInAmtGivenOut(cacheCtx, s.TestAccs[0], pool, tokenOut, swapInFunded.Denom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroDec())
amountInSwapResult, _, _, err := s.clk.SwapInAmtGivenOut(cacheCtx, s.TestAccs[0], pool, tokenOut, swapInFunded.Denom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroBigDec())
if errors.As(err, &types.InvalidAmountCalculatedError{}) {
// If the swap we're about to execute will not generate enough output, we skip the swap.
// it would error for a real user though. This is good though, since that user would just be burning funds.
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ func (s *KeeperTestSuite) swapToMinTickAndBack(spreadFactor osmomath.Dec, incent
actualSwappedInZeroForOne, tokenOut, _, err := s.App.ConcentratedLiquidityKeeper.SwapOutAmtGivenIn(
s.Ctx, swapper, pool,
coinZeroIn, pool.GetToken1(),
spreadFactor, osmomath.ZeroDec(),
spreadFactor, osmomath.ZeroBigDec(),
)
s.Require().NoError(err)

Expand All @@ -562,7 +562,7 @@ func (s *KeeperTestSuite) swapToMinTickAndBack(spreadFactor osmomath.Dec, incent
actualSwappedInOneForZero, inverseTokenOut, _, err := s.App.ConcentratedLiquidityKeeper.SwapOutAmtGivenIn(
s.Ctx, swapper, pool,
tokenOut, pool.GetToken0(),
spreadFactor, osmomath.ZeroDec(),
spreadFactor, osmomath.ZeroBigDec(),
)
s.Require().NoError(err)

Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/position_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,7 @@ func (s *KeeperTestSuite) TestTickRoundingEdgeCase() {
swapAddr := testAccs[2]
desiredTokenOut := sdk.NewCoin(USDC, osmomath.NewInt(10000))
s.FundAcc(swapAddr, sdk.NewCoins(sdk.NewCoin(ETH, osmomath.NewInt(1000000000000000000))))
_, _, _, err := s.clk.SwapInAmtGivenOut(s.Ctx, swapAddr, pool, desiredTokenOut, ETH, osmomath.ZeroDec(), osmomath.ZeroDec())
_, _, _, err := s.clk.SwapInAmtGivenOut(s.Ctx, swapAddr, pool, desiredTokenOut, ETH, osmomath.ZeroDec(), osmomath.ZeroBigDec())
s.Require().NoError(err)

// Both positions should be able to withdraw successfully
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ func (s *KeeperTestSuite) executeRandomizedSwap(pool types.ConcentratedPoolExten
}

// Note that we set the price limit to zero to ensure that the swap can execute in either direction (gets automatically set to correct limit)
swappedIn, swappedOut, _, err := s.clk.SwapInAmtGivenOut(s.Ctx, swapAddress, pool, swapOutCoin, swapInDenom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroDec())
swappedIn, swappedOut, _, err := s.clk.SwapInAmtGivenOut(s.Ctx, swapAddress, pool, swapOutCoin, swapInDenom, pool.GetSpreadFactor(s.Ctx), osmomath.ZeroBigDec())
s.Require().NoError(err)

return swappedIn, swappedOut
Expand Down Expand Up @@ -439,7 +439,7 @@ func (s *KeeperTestSuite) getInitialPositionAssets(pool types.ConcentratedPoolEx

// Calculate asset amounts that would be required to get the required spot price (rounding up on asset1 to ensure we stay in the intended tick)
asset0Amount := osmomath.NewInt(100000000000000)
asset1Amount := osmomath.NewDecFromInt(asset0Amount).Mul(requiredPrice.Dec()).Ceil().TruncateInt()
asset1Amount := osmomath.BigDecFromDec(osmomath.NewDecFromInt(asset0Amount)).Mul(requiredPrice).Ceil().Dec().TruncateInt()

assetCoins := sdk.NewCoins(
sdk.NewCoin(pool.GetToken0(), asset0Amount),
Expand Down
16 changes: 8 additions & 8 deletions x/concentrated-liquidity/spread_rewards_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1371,16 +1371,16 @@ func (s *KeeperTestSuite) TestFunctional_SpreadRewards_Swaps() {
}

// Swap multiple times USDC for ETH, therefore increasing the spot price
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin1, ETH, types.MaxSpotPrice, positions.numSwaps)
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin1, ETH, types.MaxSpotPriceBigDec, positions.numSwaps)
s.CollectAndAssertSpreadRewards(s.Ctx, clPool.GetId(), totalSpreadRewardsExpected, positionIds, [][]int64{ticksActivatedAfterEachSwap}, onlyUSDC, positions)

// Swap multiple times ETH for USDC, therefore decreasing the spot price
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ = s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin0, USDC, types.MinSpotPrice, positions.numSwaps)
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ = s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin0, USDC, types.MinSpotPriceBigDec, positions.numSwaps)
s.CollectAndAssertSpreadRewards(s.Ctx, clPool.GetId(), totalSpreadRewardsExpected, positionIds, [][]int64{ticksActivatedAfterEachSwap}, onlyETH, positions)

// Do the same swaps as before, however this time we collect spread rewards after both swap directions are complete.
ticksActivatedAfterEachSwapUp, totalSpreadRewardsExpectedUp, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin1, ETH, types.MaxSpotPrice, positions.numSwaps)
ticksActivatedAfterEachSwapDown, totalSpreadRewardsExpectedDown, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin0, USDC, types.MinSpotPrice, positions.numSwaps)
ticksActivatedAfterEachSwapUp, totalSpreadRewardsExpectedUp, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin1, ETH, types.MaxSpotPriceBigDec, positions.numSwaps)
ticksActivatedAfterEachSwapDown, totalSpreadRewardsExpectedDown, _, _ := s.swapAndTrackXTimesInARow(clPool.GetId(), DefaultCoin0, USDC, types.MinSpotPriceBigDec, positions.numSwaps)
totalSpreadRewardsExpected = totalSpreadRewardsExpectedUp.Add(totalSpreadRewardsExpectedDown...)

// We expect all positions to have both denoms in their spread reward accumulators except USDC for the overlapping range position since
Expand Down Expand Up @@ -1414,15 +1414,15 @@ func (s *KeeperTestSuite) TestFunctional_SpreadRewards_LP() {
s.FundAcc(owner, fundCoins)

// Errors since no position.
_, _, _, err := s.App.ConcentratedLiquidityKeeper.SwapOutAmtGivenIn(s.Ctx, owner, pool, sdk.NewCoin(ETH, osmomath.OneInt()), USDC, pool.GetSpreadFactor(s.Ctx), types.MaxSpotPrice)
_, _, _, err := s.App.ConcentratedLiquidityKeeper.SwapOutAmtGivenIn(s.Ctx, owner, pool, sdk.NewCoin(ETH, osmomath.OneInt()), USDC, pool.GetSpreadFactor(s.Ctx), types.MaxSpotPriceBigDec)
s.Require().Error(err)

// Create position in the default range 1.
positionDataOne, err := concentratedLiquidityKeeper.CreatePosition(ctx, pool.GetId(), owner, DefaultCoins, osmomath.ZeroInt(), osmomath.ZeroInt(), DefaultLowerTick, DefaultUpperTick)
s.Require().NoError(err)

// Swap once.
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ := s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin1, ETH, types.MaxSpotPrice, 1)
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ := s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin1, ETH, types.MaxSpotPriceBigDec, 1)

// Withdraw half.
halfLiquidity := positionDataOne.Liquidity.Mul(osmomath.NewDecWithPrec(5, 1))
Expand All @@ -1447,7 +1447,7 @@ func (s *KeeperTestSuite) TestFunctional_SpreadRewards_LP() {
fullLiquidity := positionDataTwo.Liquidity

// Swap once in the other direction.
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ = s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin0, USDC, types.MinSpotPrice, 1)
ticksActivatedAfterEachSwap, totalSpreadRewardsExpected, _, _ = s.swapAndTrackXTimesInARow(pool.GetId(), DefaultCoin0, USDC, types.MinSpotPriceBigDec, 1)

// This should claim under the hood for position 2 since full liquidity is removed.
balanceBeforeWithdraw := s.App.BankKeeper.GetBalance(ctx, owner, ETH)
Expand Down Expand Up @@ -1583,7 +1583,7 @@ func (s *KeeperTestSuite) tickStatusInvariance(ticksActivatedAfterEachSwap [][]i

// swapAndTrackXTimesInARow performs `numSwaps` swaps and tracks the tick activated after each swap.
// It also returns the total spread rewards collected, the total token in, and the total token out.
func (s *KeeperTestSuite) swapAndTrackXTimesInARow(poolId uint64, coinIn sdk.Coin, coinOutDenom string, priceLimit osmomath.Dec, numSwaps int) (ticksActivatedAfterEachSwap []int64, totalSpreadRewards sdk.Coins, totalTokenIn sdk.Coin, totalTokenOut sdk.Coin) {
func (s *KeeperTestSuite) swapAndTrackXTimesInARow(poolId uint64, coinIn sdk.Coin, coinOutDenom string, priceLimit osmomath.BigDec, numSwaps int) (ticksActivatedAfterEachSwap []int64, totalSpreadRewards sdk.Coins, totalTokenIn sdk.Coin, totalTokenOut sdk.Coin) {
// Retrieve pool
clPool, err := s.App.ConcentratedLiquidityKeeper.GetPoolById(s.Ctx, poolId)
s.Require().NoError(err)
Expand Down
16 changes: 8 additions & 8 deletions x/concentrated-liquidity/swaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func (k Keeper) swapOutAmtGivenIn(
tokenIn sdk.Coin,
tokenOutDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,
) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
swapResult, poolUpdates, err := k.computeOutAmtGivenIn(ctx, pool.GetId(), tokenIn, tokenOutDenom, spreadFactor, priceLimit)
if err != nil {
Expand Down Expand Up @@ -247,7 +247,7 @@ func (k *Keeper) swapInAmtGivenOut(
desiredTokenOut sdk.Coin,
tokenInDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,
) (calcTokenIn, calcTokenOut sdk.Coin, poolUpdates PoolUpdates, err error) {
swapResult, poolUpdates, err := k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, spreadFactor, priceLimit, pool.GetId())
if err != nil {
Expand Down Expand Up @@ -278,7 +278,7 @@ func (k Keeper) CalcOutAmtGivenIn(
spreadFactor osmomath.Dec,
) (tokenOut sdk.Coin, err error) {
cacheCtx, _ := ctx.CacheContext()
swapResult, _, err := k.computeOutAmtGivenIn(cacheCtx, poolI.GetId(), tokenIn, tokenOutDenom, spreadFactor, osmomath.ZeroDec())
swapResult, _, err := k.computeOutAmtGivenIn(cacheCtx, poolI.GetId(), tokenIn, tokenOutDenom, spreadFactor, osmomath.ZeroBigDec())
if err != nil {
return sdk.Coin{}, err
}
Expand All @@ -293,7 +293,7 @@ func (k Keeper) CalcInAmtGivenOut(
spreadFactor osmomath.Dec,
) (sdk.Coin, error) {
cacheCtx, _ := ctx.CacheContext()
swapResult, _, err := k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, spreadFactor, osmomath.ZeroDec(), poolI.GetId())
swapResult, _, err := k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, spreadFactor, osmomath.ZeroBigDec(), poolI.GetId())
if err != nil {
return sdk.Coin{}, err
}
Expand Down Expand Up @@ -354,7 +354,7 @@ func (k Keeper) computeOutAmtGivenIn(
tokenInMin sdk.Coin,
tokenOutDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,
) (swapResult SwapResult, poolUpdates PoolUpdates, err error) {
p, spreadRewardAccumulator, uptimeAccums, err := k.swapSetup(ctx, poolId, tokenInMin.Denom, tokenOutDenom)
if err != nil {
Expand Down Expand Up @@ -481,7 +481,7 @@ func (k Keeper) computeInAmtGivenOut(
desiredTokenOut sdk.Coin,
tokenInDenom string,
spreadFactor osmomath.Dec,
priceLimit osmomath.Dec,
priceLimit osmomath.BigDec,
poolId uint64,
) (swapResult SwapResult, poolUpdates PoolUpdates, err error) {
p, spreadRewardAccumulator, uptimeAccums, err := k.swapSetup(ctx, poolId, tokenInDenom, desiredTokenOut.Denom)
Expand Down Expand Up @@ -734,7 +734,7 @@ func checkDenomValidity(inDenom, outDenom, asset0, asset1 string) error {
return nil
}

func (k Keeper) setupSwapStrategy(p types.ConcentratedPoolExtension, spreadFactor osmomath.Dec, tokenInDenom string, priceLimit osmomath.Dec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) {
func (k Keeper) setupSwapStrategy(p types.ConcentratedPoolExtension, spreadFactor osmomath.Dec, tokenInDenom string, priceLimit osmomath.BigDec) (strategy swapstrategy.SwapStrategy, sqrtPriceLimit osmomath.BigDec, err error) {
zeroForOne := getZeroForOne(tokenInDenom, p.GetToken0())

// take provided price limit and turn this into a sqrt price limit since formulas use sqrtPrice
Expand Down Expand Up @@ -838,7 +838,7 @@ func (k Keeper) ComputeMaxInAmtGivenMaxTicksCrossed(
}

// Setup the swap strategy
swapStrategy, _, err := k.setupSwapStrategy(p, p.GetSpreadFactor(cacheCtx), tokenInDenom, osmomath.ZeroDec())
swapStrategy, _, err := k.setupSwapStrategy(p, p.GetSpreadFactor(cacheCtx), tokenInDenom, osmomath.ZeroBigDec())
if err != nil {
return sdk.Coin{}, sdk.Coin{}, err
}
Expand Down
Loading