diff --git a/CHANGELOG.md b/CHANGELOG.md index 606cf3e43a5..d90cf9c1ecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#7665](https://github.com/osmosis-labs/osmosis/pull/7665) feat(x/protorev): Use Transient store to store swap backruns. * [#7685](https://github.com/osmosis-labs/osmosis/pull/7685) Speedup CL actions by only marshalling for CL hooks if they will be used. * [#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) ## v23.0.5 & v23.0.5-iavl-v1 diff --git a/x/concentrated-liquidity/export_test.go b/x/concentrated-liquidity/export_test.go index 3b213bb3716..1be7a8043c5 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 a1cc5ea0574..ba6ee798ddb 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, updateAccumulators) if err != nil { return SwapResult{}, PoolUpdates{}, err } @@ -615,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. @@ -652,29 +665,32 @@ 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