Skip to content

Commit

Permalink
Make CL calculations not update accumulators (#7689)
Browse files Browse the repository at this point in the history
* Make CL calculations not update accumulators

* Fix lint

* Fix calcInAmtGivenOut case

* Update Changelog
  • Loading branch information
ValarDragon authored Mar 13, 2024
1 parent ed2fbb3 commit 9f8d711
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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) {
Expand Down
86 changes: 51 additions & 35 deletions x/concentrated-liquidity/swaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -335,15 +335,18 @@ 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
}
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
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand Down
8 changes: 4 additions & 4 deletions x/concentrated-liquidity/swaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9f8d711

Please sign in to comment.