From 50e66189fb254aa7a61adbb3542e7f2c30813a13 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 7 Mar 2024 20:31:28 +0100 Subject: [PATCH 1/4] Make CL calculations not update accumulators --- x/concentrated-liquidity/export_test.go | 8 +-- x/concentrated-liquidity/swaps.go | 83 +++++++++++++++---------- x/concentrated-liquidity/swaps_test.go | 8 +-- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/x/concentrated-liquidity/export_test.go b/x/concentrated-liquidity/export_test.go index 269d61ddb7d..8cce07e6c86 100644 --- a/x/concentrated-liquidity/export_test.go +++ b/x/concentrated-liquidity/export_test.go @@ -70,9 +70,9 @@ func (k Keeper) ComputeOutAmtGivenIn( tokenOutDenom string, spreadFactor osmomath.Dec, priceLimit osmomath.BigDec, - + updateAccumulators bool, ) (swapResult SwapResult, poolUpdates PoolUpdates, err error) { - return k.computeOutAmtGivenIn(ctx, poolId, tokenInMin, tokenOutDenom, spreadFactor, priceLimit) + return k.computeOutAmtGivenIn(ctx, poolId, tokenInMin, tokenOutDenom, spreadFactor, priceLimit, updateAccumulators) } func (k Keeper) SwapInAmtGivenOut( @@ -93,9 +93,9 @@ func (k Keeper) ComputeInAmtGivenOut( spreadFactor osmomath.Dec, priceLimit osmomath.BigDec, poolId uint64, - + updateAccumulators bool, ) (swapResult SwapResult, poolUpdates PoolUpdates, err error) { - return k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, spreadFactor, priceLimit, poolId) + return k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, spreadFactor, priceLimit, poolId, updateAccumulators) } func (k Keeper) InitOrUpdateTick(ctx sdk.Context, poolId uint64, tickIndex int64, liquidityIn osmomath.Dec, upper bool) (tickIsEmpty bool, err error) { diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 310f5d2eba8..876f253fae7 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -250,7 +250,7 @@ func (k Keeper) swapOutAmtGivenIn( spreadFactor 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) + swapResult, poolUpdates, err := k.computeOutAmtGivenIn(ctx, pool.GetId(), tokenIn, tokenOutDenom, spreadFactor, priceLimit, true) if err != nil { return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, err } @@ -281,7 +281,7 @@ func (k *Keeper) swapInAmtGivenOut( spreadFactor 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()) + swapResult, poolUpdates, err := k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, spreadFactor, priceLimit, pool.GetId(), true) if err != nil { return sdk.Coin{}, sdk.Coin{}, PoolUpdates{}, err } @@ -310,7 +310,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.ZeroBigDec()) + swapResult, _, err := k.computeOutAmtGivenIn(cacheCtx, poolI.GetId(), tokenIn, tokenOutDenom, spreadFactor, osmomath.ZeroBigDec(), false) if err != nil { return sdk.Coin{}, err } @@ -325,7 +325,7 @@ func (k Keeper) CalcInAmtGivenOut( spreadFactor osmomath.Dec, ) (sdk.Coin, error) { cacheCtx, _ := ctx.CacheContext() - swapResult, _, err := k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, spreadFactor, osmomath.ZeroBigDec(), poolI.GetId()) + swapResult, _, err := k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, spreadFactor, osmomath.ZeroBigDec(), poolI.GetId(), false) if err != nil { return sdk.Coin{}, err } @@ -335,7 +335,8 @@ func (k Keeper) CalcInAmtGivenOut( func (k Keeper) swapSetup(ctx sdk.Context, poolId uint64, tokenInDenom string, - tokenOutDenom string) (pool types.ConcentratedPoolExtension, spreadAccum *accum.AccumulatorObject, err error) { + tokenOutDenom string, + getAccumulators bool) (pool types.ConcentratedPoolExtension, spreadAccum *accum.AccumulatorObject, err error) { pool, err = k.getPoolForSwap(ctx, poolId) if err != nil { return pool, spreadAccum, err @@ -343,7 +344,9 @@ func (k Keeper) swapSetup(ctx sdk.Context, if err := checkDenomValidity(tokenInDenom, tokenOutDenom, pool.GetToken0(), pool.GetToken1()); err != nil { return pool, spreadAccum, err } - spreadAccum, err = k.GetSpreadRewardAccumulator(ctx, poolId) + if getAccumulators { + spreadAccum, err = k.GetSpreadRewardAccumulator(ctx, poolId) + } return pool, spreadAccum, err } @@ -387,8 +390,9 @@ func (k Keeper) computeOutAmtGivenIn( tokenOutDenom string, spreadFactor osmomath.Dec, priceLimit osmomath.BigDec, + updateAccumulators bool, ) (swapResult SwapResult, poolUpdates PoolUpdates, err error) { - p, spreadRewardAccumulator, err := k.swapSetup(ctx, poolId, tokenInMin.Denom, tokenOutDenom) + p, spreadRewardAccumulator, err := k.swapSetup(ctx, poolId, tokenInMin.Denom, tokenOutDenom, updateAccumulators) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -434,11 +438,13 @@ func (k Keeper) computeOutAmtGivenIn( return SwapResult{}, PoolUpdates{}, err } - // Update the spread reward growth for the entire swap using the total spread factors charged. - spreadFactorsAccruedPerUnitOfLiquidity := swapState.updateSpreadRewardGrowthGlobal(spreadRewardCharge) + if updateAccumulators { + // Update the spread reward growth for the entire swap using the total spread factors charged. + spreadFactorsAccruedPerUnitOfLiquidity := swapState.updateSpreadRewardGrowthGlobal(spreadRewardCharge) - // Emit telemetry to detect spread reward truncation. - emitAccumulatorUpdateTelemetry(types.SpreadFactorTruncationTelemetryName, spreadFactorsAccruedPerUnitOfLiquidity, spreadRewardCharge, poolId, swapState.liquidity, "is_out_given_in", "true") + // Emit telemetry to detect spread reward truncation. + emitAccumulatorUpdateTelemetry(types.SpreadFactorTruncationTelemetryName, spreadFactorsAccruedPerUnitOfLiquidity, spreadRewardCharge, poolId, swapState.liquidity, "is_out_given_in", "true") + } ctx.Logger().Debug("cl calc out given in") emitSwapDebugLogs(ctx, swapState, computedSqrtPrice, amountIn, amountOut, spreadRewardCharge) @@ -454,7 +460,7 @@ func (k Keeper) computeOutAmtGivenIn( // 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, swapStrategy, - nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInMin.Denom) + nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInMin.Denom, updateAccumulators) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -489,8 +495,10 @@ func (k Keeper) computeOutAmtGivenIn( } // Add spread reward growth per share to the pool-global spread reward accumulator. - spreadRewardGrowth := sdk.DecCoin{Denom: tokenInMin.Denom, Amount: swapState.globalSpreadRewardGrowthPerUnitLiquidity} - spreadRewardAccumulator.AddToAccumulator(sdk.NewDecCoins(spreadRewardGrowth)) + if updateAccumulators { + spreadRewardGrowth := sdk.DecCoin{Denom: tokenInMin.Denom, Amount: swapState.globalSpreadRewardGrowthPerUnitLiquidity} + spreadRewardAccumulator.AddToAccumulator(sdk.NewDecCoins(spreadRewardGrowth)) + } // Coin amounts require int values // Round amountIn up to avoid under charging @@ -519,8 +527,9 @@ func (k Keeper) computeInAmtGivenOut( spreadFactor osmomath.Dec, priceLimit osmomath.BigDec, poolId uint64, + updateAccumulators bool, ) (swapResult SwapResult, poolUpdates PoolUpdates, err error) { - p, spreadRewardAccumulator, err := k.swapSetup(ctx, poolId, tokenInDenom, desiredTokenOut.Denom) + p, spreadRewardAccumulator, err := k.swapSetup(ctx, poolId, tokenInDenom, desiredTokenOut.Denom, updateAccumulators) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -564,10 +573,12 @@ func (k Keeper) computeInAmtGivenOut( return SwapResult{}, PoolUpdates{}, err } - spreadFactorsAccruedPerUnitOfLiquidity := swapState.updateSpreadRewardGrowthGlobal(spreadRewardChargeTotal) + if updateAccumulators { + spreadFactorsAccruedPerUnitOfLiquidity := swapState.updateSpreadRewardGrowthGlobal(spreadRewardChargeTotal) - // Emit telemetry to detect spread reward truncation. - emitAccumulatorUpdateTelemetry(types.SpreadFactorTruncationTelemetryName, spreadFactorsAccruedPerUnitOfLiquidity, spreadRewardChargeTotal, poolId, swapState.liquidity, "is_out_given_in", "false") + // Emit telemetry to detect spread reward truncation. + emitAccumulatorUpdateTelemetry(types.SpreadFactorTruncationTelemetryName, spreadFactorsAccruedPerUnitOfLiquidity, spreadRewardChargeTotal, poolId, swapState.liquidity, "is_out_given_in", "false") + } ctx.Logger().Debug("cl calc in given out") emitSwapDebugLogs(ctx, swapState, computedSqrtPrice, amountIn, amountOut, spreadRewardChargeTotal) @@ -581,7 +592,7 @@ func (k Keeper) computeInAmtGivenOut( // 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, swapStrategy, - nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInDenom) + nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInDenom, true) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -652,29 +663,33 @@ func (k Keeper) swapCrossTickLogic(ctx sdk.Context, nextInitializedTick int64, nextTickIter db.Iterator, p types.ConcentratedPoolExtension, spreadRewardAccum *accum.AccumulatorObject, uptimeAccums *[]*accum.AccumulatorObject, - tokenInDenom string) (SwapState, error) { + tokenInDenom string, updateAccumulators bool) (SwapState, error) { nextInitializedTickInfo, err := ParseTickFromBz(nextTickIter.Value()) if err != nil { return swapState, err } - if *uptimeAccums == nil { - uptimeAccumsRaw, err := k.GetUptimeAccumulators(ctx, p.GetId()) - if err != nil { - return swapState, err + if updateAccumulators { + + if *uptimeAccums == nil { + uptimeAccumsRaw, err := k.GetUptimeAccumulators(ctx, p.GetId()) + if err != nil { + return swapState, err + } + uptimeAccums = &uptimeAccumsRaw } - uptimeAccums = &uptimeAccumsRaw - } - if err := k.updateGivenPoolUptimeAccumulatorsToNow(ctx, p, *uptimeAccums); err != nil { - return swapState, err - } + if err := k.updateGivenPoolUptimeAccumulatorsToNow(ctx, p, *uptimeAccums); err != nil { + return swapState, err + } - // Retrieve the liquidity held in the next closest initialized tick - spreadRewardGrowth := sdk.DecCoin{Denom: tokenInDenom, Amount: swapState.globalSpreadRewardGrowthPerUnitLiquidity} - liquidityNet, err := k.crossTick(ctx, p.GetId(), nextInitializedTick, &nextInitializedTickInfo, spreadRewardGrowth, spreadRewardAccum.GetValue(), *uptimeAccums) - if err != nil { - return swapState, err + // Retrieve the liquidity held in the next closest initialized tick + spreadRewardGrowth := sdk.DecCoin{Denom: tokenInDenom, Amount: swapState.globalSpreadRewardGrowthPerUnitLiquidity} + _, err := k.crossTick(ctx, p.GetId(), nextInitializedTick, &nextInitializedTickInfo, spreadRewardGrowth, spreadRewardAccum.GetValue(), *uptimeAccums) + if err != nil { + return swapState, err + } } + liquidityNet := nextInitializedTickInfo.LiquidityNet // Move next tick iterator to the next tick as the tick is crossed. nextTickIter.Next() diff --git a/x/concentrated-liquidity/swaps_test.go b/x/concentrated-liquidity/swaps_test.go index abfd97d9b71..75282794a64 100644 --- a/x/concentrated-liquidity/swaps_test.go +++ b/x/concentrated-liquidity/swaps_test.go @@ -2027,7 +2027,7 @@ func (s *KeeperTestSuite) TestComputeAndSwapOutAmtGivenIn() { cacheCtx, pool.GetId(), test.TokenIn, test.TokenOutDenom, - test.SpreadFactor, test.PriceLimit) + test.SpreadFactor, test.PriceLimit, true) if test.ExpectErr { s.Require().Error(err) @@ -2171,7 +2171,7 @@ func (s *KeeperTestSuite) TestComputeAndSwapInAmtGivenOut() { swapResult, poolUpdates, err := s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( cacheCtx, test.TokenOut, test.TokenInDenom, - test.SpreadFactor, test.PriceLimit, pool.GetId()) + test.SpreadFactor, test.PriceLimit, pool.GetId(), true) if test.ExpectErr { s.Require().Error(err) } else { @@ -2671,7 +2671,7 @@ func (s *KeeperTestSuite) TestComputeOutAmtGivenIn() { s.Ctx, poolBeforeCalc.GetId(), test.TokenIn, test.TokenOutDenom, - test.SpreadFactor, test.PriceLimit) + test.SpreadFactor, test.PriceLimit, true) s.Require().NoError(err) // check that the pool has not been modified after performing calc @@ -2756,7 +2756,7 @@ func (s *KeeperTestSuite) TestComputeInAmtGivenOut() { _, _, err := s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( s.Ctx, test.TokenOut, test.TokenInDenom, - test.SpreadFactor, test.PriceLimit, poolBeforeCalc.GetId()) + test.SpreadFactor, test.PriceLimit, poolBeforeCalc.GetId(), true) s.Require().NoError(err) // check that the pool has not been modified after performing calc From 0d64bb6a9de401c1714b953e19fa54ae59336f0d Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 7 Mar 2024 20:50:34 +0100 Subject: [PATCH 2/4] Fix lint --- x/concentrated-liquidity/swaps.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 876f253fae7..8b07aa6ac78 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -669,7 +669,6 @@ func (k Keeper) swapCrossTickLogic(ctx sdk.Context, return swapState, err } if updateAccumulators { - if *uptimeAccums == nil { uptimeAccumsRaw, err := k.GetUptimeAccumulators(ctx, p.GetId()) if err != nil { From f03ae19c64634b0d3002a1c2e61e9fcc2f206c53 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 7 Mar 2024 21:13:34 +0100 Subject: [PATCH 3/4] Fix calcInAmtGivenOut case --- x/concentrated-liquidity/swaps.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 8b07aa6ac78..68f822bc58c 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -592,7 +592,7 @@ func (k Keeper) computeInAmtGivenOut( // 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, swapStrategy, - nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInDenom, true) + nextInitializedTick, nextInitTickIter, p, spreadRewardAccumulator, &uptimeAccums, tokenInDenom, updateAccumulators) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -626,7 +626,9 @@ func (k Keeper) computeInAmtGivenOut( } // Add spread reward growth per share to the pool-global spread reward accumulator. - spreadRewardAccumulator.AddToAccumulator(sdk.NewDecCoins(sdk.NewDecCoinFromDec(tokenInDenom, swapState.globalSpreadRewardGrowthPerUnitLiquidity))) + if updateAccumulators { + spreadRewardAccumulator.AddToAccumulator(sdk.NewDecCoins(sdk.NewDecCoinFromDec(tokenInDenom, swapState.globalSpreadRewardGrowthPerUnitLiquidity))) + } // coin amounts require int values // Round amount in up to avoid under charging the user. From 4515abdd3bbbc898cb2a9ed1d29e46ed6bbf0f2c Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 13 Mar 2024 01:37:35 +0100 Subject: [PATCH 4/4] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08876d821b0..9e508e978a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#7622](https://github.com/osmosis-labs/osmosis/pull/7622) Remove duplicate CL accumulator update logic. * [#7665](https://github.com/osmosis-labs/osmosis/pull/7665) feat(x/protorev): Use Transient store to store swap backruns. * [#7503](https://github.com/osmosis-labs/osmosis/pull/7503) Add IBC wasm light clients module +* [#7689](https://github.com/osmosis-labs/osmosis/pull/7689) Make CL price estimations not cause state writes (speed and gas improvements) ### State Compatible