Repository: osmosis-labs/osmosis
Pull request #5000 (2023-04-25 12:15:17)

refactor(osmomath/cl): avoid using broken power function; panic on broken values
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v                               ✰  Thanks for creating a PR! ✰    
v    Before smashing the submit button please review the checkboxes.
v    If a checkbox is n/a - please still include it but + a little note why
v    If your PR doesn't close an issue, that's OK!  Just remove the Closes: #XXX line!
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

Addresses: #4967

## What is the purpose of the change

Addresses #4967 in the context of concentrated liquidity. Keeping #4967 open until completely resolved in the future

The `osmomath.Power` function is broken for base < 1. The reason is that the logarithm function is
negative between zero and 1, and the Exp2(k) is undefined for negative k.
As a result, this function panics if called for base < 1 as it is implemented by `Exp2(base.LogBase2().Mul(exponent))`

## Brief Changelog

- replace `Power` function with `PowerInteger` in `x/concentrated-liquidity module
- panic in `osmomath.Power` foe base < 1

## Testing and Verifying

- added test

## Documentation and Release Note

  - Does this pull request introduce a new feature or user-facing behavior changes? no
  - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`? yes
  - How is the feature or change documented? not applicable


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82d0c9703..76d1232a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   * [#4783](https://github.com/osmosis-labs/osmosis/pull/4783) Update wasmd to 0.31.0
   * [#4830](https://github.com/osmosis-labs/osmosis/pull/4830) Add gas cost when we AddToGaugeRewards, linearly increase with coins to add
   * [#4886](https://github.com/osmosis-labs/osmosis/pull/4886) Implement MsgSplitRouteSwapExactAmountIn and MsgSplitRouteSwapExactAmountOut that supports route splitting.
+  * [#5000](https://github.com/osmosis-labs/osmosis/pull/5000) osmomath.Power panics for base < 1 to temporarily restrict broken logic for such base.
 
 ### Misc Improvements
 
diff --git a/go.mod b/go.mod
index d740acbe7..c9e9aa3ac 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
 	github.com/mattn/go-sqlite3 v1.14.16
 	github.com/ory/dockertest/v3 v3.10.0
 	github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3
-	github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230328024000-175ec88e4304
+	github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230425121701-4d427b673864
 	github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230411200859-ae3065d0ca05
 	github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304
 	github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230331072320-5d6f6cfa2627
diff --git a/go.sum b/go.sum
index 75ab3faba..c24db5252 100644
--- a/go.sum
+++ b/go.sum
@@ -936,6 +936,8 @@ github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:Ylmch
 github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI=
 github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230328024000-175ec88e4304 h1:iSSlHl+SoewNpP/2N8JaUEHhOQRmJAnS8zaJ11yWslY=
 github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230328024000-175ec88e4304/go.mod h1:/h3CZIo25kMrM4Ojm7qBgMxKofTVwOycVWSa4rhEsaM=
+github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230425121701-4d427b673864 h1:UhkJ67kx4NP7FEEQ+ybpDLIiS0Rl8vX8G55FKZux98I=
+github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230425121701-4d427b673864/go.mod h1:Hu4OHAvKtnCaqA4GlodNf89JZ+2TFtH7IDNxNi6Vue8=
 github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230411200859-ae3065d0ca05 h1:fqVGxZPgUWuYWxVcMxHz5vrDV/aoxGJ7Kt0J4Vu/bsY=
 github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230411200859-ae3065d0ca05/go.mod h1:zyBrzl2rsZWGbOU+/1hzA+xoQlCshzZuHe/5mzdb/zo=
 github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo=
diff --git a/osmomath/decimal.go b/osmomath/decimal.go
index cf921f494..03b6fea77 100644
--- a/osmomath/decimal.go
+++ b/osmomath/decimal.go
@@ -1019,6 +1019,9 @@ func (d BigDec) PowerIntegerMut(power uint64) BigDec {
 // If a greater exponent is given, the function panics.
 // The error is not bounded but expected to be around 10^-18, use with care.
 // See the underlying Exp2, LogBase2 and Mul for the details of their bounds.
+// WARNING: This function is broken for base < 1. The reason is that logarithm function is
+// negative between zero and 1, and the Exp2(k) is undefined for negative k.
+// As a result, this function panics if called for d < 1.
 func (d BigDec) Power(power BigDec) BigDec {
 	if d.IsNegative() {
 		panic(fmt.Sprintf("negative base is not supported for Power(), base was (%s)", d))
@@ -1038,6 +1041,9 @@ func (d BigDec) Power(power BigDec) BigDec {
 	if d.IsZero() {
 		return ZeroDec()
 	}
+	if d.LT(OneDec()) {
+		panic(fmt.Sprintf("Power() is not supported for base < 1, base was (%s)", d))
+	}
 	if d.Equal(twoBigDec) {
 		return Exp2(power)
 	}
diff --git a/x/concentrated-liquidity/fees.go b/x/concentrated-liquidity/fees.go
index 2e7534a86..38dd9832c 100644
--- a/x/concentrated-liquidity/fees.go
+++ b/x/concentrated-liquidity/fees.go
@@ -21,9 +21,9 @@ func (k Keeper) createFeeAccumulator(ctx sdk.Context, poolId uint64) error {
 	return nil
 }
 
-// getFeeAccumulator gets the fee accumulator object using the given poolOd
+// GetFeeAccumulator gets the fee accumulator object using the given poolOd
 // returns error if accumulator for the given poolId does not exist.
-func (k Keeper) getFeeAccumulator(ctx sdk.Context, poolId uint64) (accum.AccumulatorObject, error) {
+func (k Keeper) GetFeeAccumulator(ctx sdk.Context, poolId uint64) (accum.AccumulatorObject, error) {
 	acc, err := accum.GetAccumulator(ctx.KVStore(k.storeKey), types.KeyFeePoolAccumulator(poolId))
 	if err != nil {
 		return accum.AccumulatorObject{}, err
@@ -36,7 +36,7 @@ func (k Keeper) getFeeAccumulator(ctx sdk.Context, poolId uint64) (accum.Accumul
 // the internal per-pool accumulator that tracks fee growth per one unit of
 // liquidity. Returns error if fails to get accumulator.
 func (k Keeper) chargeFee(ctx sdk.Context, poolId uint64, feeUpdate sdk.DecCoin) error {
-	feeAccumulator, err := k.getFeeAccumulator(ctx, poolId)
+	feeAccumulator, err := k.GetFeeAccumulator(ctx, poolId)
 	if err != nil {
 		return err
 	}
@@ -64,7 +64,7 @@ func (k Keeper) chargeFee(ctx sdk.Context, poolId uint64, feeUpdate sdk.DecCoin)
 // - fails to update the position's accumulator.
 func (k Keeper) initOrUpdateFeeAccumulatorPosition(ctx sdk.Context, poolId uint64, lowerTick, upperTick int64, positionId uint64, liquidityDelta sdk.Dec) error {
 	// Get the fee accumulator for the position's pool.
-	feeAccumulator, err := k.getFeeAccumulator(ctx, poolId)
+	feeAccumulator, err := k.GetFeeAccumulator(ctx, poolId)
 	if err != nil {
 		return err
 	}
@@ -138,7 +138,7 @@ func (k Keeper) getFeeGrowthOutside(ctx sdk.Context, poolId uint64, lowerTick, u
 		return sdk.DecCoins{}, err
 	}
 
-	poolFeeAccumulator, err := k.getFeeAccumulator(ctx, poolId)
+	poolFeeAccumulator, err := k.GetFeeAccumulator(ctx, poolId)
 	if err != nil {
 		return sdk.DecCoins{}, err
 	}
@@ -168,7 +168,7 @@ func (k Keeper) getInitialFeeGrowthOutsideForTick(ctx sdk.Context, poolId uint64
 
 	currentTick := pool.GetCurrentTick().Int64()
 	if currentTick >= tick {
-		feeAccumulator, err := k.getFeeAccumulator(ctx, poolId)
+		feeAccumulator, err := k.GetFeeAccumulator(ctx, poolId)
 		if err != nil {
 			return sdk.DecCoins{}, err
 		}
@@ -248,7 +248,7 @@ func (k Keeper) prepareClaimableFees(ctx sdk.Context, positionId uint64) (sdk.Co
 	}
 
 	// Get the fee accumulator for the position's pool.
-	feeAccumulator, err := k.getFeeAccumulator(ctx, position.PoolId)
+	feeAccumulator, err := k.GetFeeAccumulator(ctx, position.PoolId)
 	if err != nil {
 		return nil, err
 	}
diff --git a/x/concentrated-liquidity/genesis.go b/x/concentrated-liquidity/genesis.go
index 9e3908ead..8fc9acd9e 100644
--- a/x/concentrated-liquidity/genesis.go
+++ b/x/concentrated-liquidity/genesis.go
@@ -93,7 +93,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *genesis.GenesisState {
 		if err != nil {
 			panic(err)
 		}
-		accumObject, err := k.getFeeAccumulator(ctx, poolI.GetId())
+		accumObject, err := k.GetFeeAccumulator(ctx, poolI.GetId())
 		if err != nil {
 			panic(err)
 		}
@@ -117,7 +117,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *genesis.GenesisState {
 			panic(err)
 		}
 
-		incentivesAccum, err := k.getUptimeAccumulators(ctx, poolId)
+		incentivesAccum, err := k.GetUptimeAccumulators(ctx, poolId)
 		if err != nil {
 			panic(err)
 		}
diff --git a/x/concentrated-liquidity/incentives.go b/x/concentrated-liquidity/incentives.go
index 508419954..11106685f 100644
--- a/x/concentrated-liquidity/incentives.go
+++ b/x/concentrated-liquidity/incentives.go
@@ -38,10 +38,9 @@ func getUptimeTrackerValues(uptimeTrackers []model.UptimeTracker) []sdk.DecCoins
 	return trackerValues
 }
 
-// nolint: unused
-// getUptimeAccumulators gets the uptime accumulator objects for the given poolId
+// GetUptimeAccumulators gets the uptime accumulator objects for the given poolId
 // Returns error if accumulator for the given poolId does not exist.
-func (k Keeper) getUptimeAccumulators(ctx sdk.Context, poolId uint64) ([]accum.AccumulatorObject, error) {
+func (k Keeper) GetUptimeAccumulators(ctx sdk.Context, poolId uint64) ([]accum.AccumulatorObject, error) {
 	accums := make([]accum.AccumulatorObject, len(types.SupportedUptimes))
 	for uptimeIndex := range types.SupportedUptimes {
 		acc, err := accum.GetAccumulator(ctx.KVStore(k.storeKey), types.KeyUptimeAccumulator(poolId, uint64(uptimeIndex)))
@@ -59,7 +58,7 @@ func (k Keeper) getUptimeAccumulators(ctx sdk.Context, poolId uint64) ([]accum.A
 // getUptimeAccumulatorValues gets the accumulator values for the supported uptimes for the given poolId
 // Returns error if accumulator for the given poolId does not exist.
 func (k Keeper) getUptimeAccumulatorValues(ctx sdk.Context, poolId uint64) ([]sdk.DecCoins, error) {
-	uptimeAccums, err := k.getUptimeAccumulators(ctx, poolId)
+	uptimeAccums, err := k.GetUptimeAccumulators(ctx, poolId)
 	if err != nil {
 		return []sdk.DecCoins{}, err
 	}
@@ -185,7 +184,7 @@ func (k Keeper) prepareBalancerPoolAsFullRange(ctx sdk.Context, clPoolId uint64)
 
 	// Create a temporary position record on all uptime accumulators with this amount. We expect this to be cleared later
 	// with `claimAndResetFullRangeBalancerPool`
-	uptimeAccums, err := k.getUptimeAccumulators(ctx, clPoolId)
+	uptimeAccums, err := k.GetUptimeAccumulators(ctx, clPoolId)
 	if err != nil {
 		return 0, sdk.ZeroDec(), err
 	}
@@ -234,7 +233,7 @@ func (k Keeper) claimAndResetFullRangeBalancerPool(ctx sdk.Context, clPoolId uin
 	// Get all uptime accumulators for CL pool
 	// Create a temporary position record on all uptime accumulators with this amount. We expect this to be cleared later
 	// with `claimAndResetFullRangeBalancerPool`
-	uptimeAccums, err := k.getUptimeAccumulators(ctx, clPoolId)
+	uptimeAccums, err := k.GetUptimeAccumulators(ctx, clPoolId)
 	if err != nil {
 		return sdk.Coins{}, err
 	}
@@ -336,7 +335,7 @@ func (k Keeper) updateUptimeAccumulatorsToNow(ctx sdk.Context, poolId uint64) er
 		return err
 	}
 
-	uptimeAccums, err := k.getUptimeAccumulators(ctx, poolId)
+	uptimeAccums, err := k.GetUptimeAccumulators(ctx, poolId)
 	if err != nil {
 		return err
 	}
@@ -626,7 +625,7 @@ func (k Keeper) initOrUpdatePositionUptime(ctx sdk.Context, poolId uint64, liqui
 	}
 
 	// Create records for relevant uptime accumulators here.
-	uptimeAccumulators, err := k.getUptimeAccumulators(ctx, poolId)
+	uptimeAccumulators, err := k.GetUptimeAccumulators(ctx, poolId)
 	if err != nil {
 		return err
 	}
@@ -731,7 +730,7 @@ func (k Keeper) claimAllIncentivesForPosition(ctx sdk.Context, positionId uint64
 	}
 
 	// Retrieve the uptime accumulators for the position's pool.
-	uptimeAccumulators, err := k.getUptimeAccumulators(ctx, position.PoolId)
+	uptimeAccumulators, err := k.GetUptimeAccumulators(ctx, position.PoolId)
 	if err != nil {
 		return sdk.Coins{}, sdk.Coins{}, err
 	}
diff --git a/x/concentrated-liquidity/math/tick.go b/x/concentrated-liquidity/math/tick.go
index 4858782ea..bf0204ac8 100644
--- a/x/concentrated-liquidity/math/tick.go
+++ b/x/concentrated-liquidity/math/tick.go
@@ -133,9 +133,9 @@ func PowTenInternal(exponent sdk.Int) sdk.Dec {
 
 func powTenBigDec(exponent sdk.Int) osmomath.BigDec {
 	if exponent.GTE(sdk.ZeroInt()) {
-		return osmomath.NewBigDec(10).Power(osmomath.NewBigDec(exponent.Int64()))
+		return osmomath.NewBigDec(10).PowerInteger(exponent.Uint64())
 	}
-	return osmomath.OneDec().Quo(osmomath.NewBigDec(10).Power(osmomath.NewBigDec(exponent.Abs().Int64())))
+	return osmomath.OneDec().Quo(osmomath.NewBigDec(10).PowerInteger(exponent.Abs().Uint64()))
 }
 
 // CalculatePriceToTick takes in a price and returns the corresponding tick index.
diff --git a/x/concentrated-liquidity/position.go b/x/concentrated-liquidity/position.go
index d351b9e11..baa4965ae 100644
--- a/x/concentrated-liquidity/position.go
+++ b/x/concentrated-liquidity/position.go
@@ -433,7 +433,7 @@ func (k Keeper) fungifyChargedPosition(ctx sdk.Context, owner sdk.AccAddress, po
 
 	// Get the new position's store name as well as uptime accumulators for the pool.
 	newPositionName := string(types.KeyPositionId(newPositionId))
-	uptimeAccumulators, err := k.getUptimeAccumulators(ctx, newPosition.PoolId)
+	uptimeAccumulators, err := k.GetUptimeAccumulators(ctx, newPosition.PoolId)
 	if err != nil {
 		return 0, err
 	}
diff --git a/x/concentrated-liquidity/tick.go b/x/concentrated-liquidity/tick.go
index b14248dfe..a59bcece4 100644
--- a/x/concentrated-liquidity/tick.go
+++ b/x/concentrated-liquidity/tick.go
@@ -37,7 +37,7 @@ func (k Keeper) initOrUpdateTick(ctx sdk.Context, poolId uint64, currentTick int
 	// set the tick's fee growth outside to the fee accumulator's value
 	if liquidityBefore.IsZero() {
 		if tickIndex <= currentTick {
-			accum, err := k.getFeeAccumulator(ctx, poolId)
+			accum, err := k.GetFeeAccumulator(ctx, poolId)
 			if err != nil {
 				return err
 			}
@@ -70,7 +70,7 @@ func (k Keeper) crossTick(ctx sdk.Context, poolId uint64, tickIndex int64, swapS
 		return sdk.Dec{}, err
 	}
 
-	feeAccum, err := k.getFeeAccumulator(ctx, poolId)
+	feeAccum, err := k.GetFeeAccumulator(ctx, poolId)
 	if err != nil {
 		return sdk.Dec{}, err
 	}
@@ -83,7 +83,7 @@ func (k Keeper) crossTick(ctx sdk.Context, poolId uint64, tickIndex int64, swapS
 		return sdk.Dec{}, err
 	}
 
-	uptimeAccums, err := k.getUptimeAccumulators(ctx, poolId)
+	uptimeAccums, err := k.GetUptimeAccumulators(ctx, poolId)
 	if err != nil {
 		return sdk.Dec{}, err
 	}


Test patch:
diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index 7961637db..b0152e0a4 100644
--- a/osmomath/decimal_test.go
+++ b/osmomath/decimal_test.go
@@ -1416,6 +1416,12 @@ func (s *decimalTestSuite) TestPower() {
 			base:     osmomath.NewBigDec(1),
 			exponent: osmomath.MustNewDecFromStr("-4"),
 
+			expectPanic: true,
+		},
+		"base < 1 - panic (see godoc)": {
+			base:     osmomath.NewBigDec(1).Sub(osmomath.SmallestDec()),
+			exponent: osmomath.OneDec(),
+
 			expectPanic: true,
 		},
 	}
diff --git a/x/concentrated-liquidity/export_test.go b/x/concentrated-liquidity/export_test.go
index f985021c4..00d7747b3 100644
--- a/x/concentrated-liquidity/export_test.go
+++ b/x/concentrated-liquidity/export_test.go
@@ -121,10 +121,6 @@ func (k Keeper) CreateFeeAccumulator(ctx sdk.Context, poolId uint64) error {
 	return k.createFeeAccumulator(ctx, poolId)
 }
 
-func (k Keeper) GetFeeAccumulator(ctx sdk.Context, poolId uint64) (accum.AccumulatorObject, error) {
-	return k.getFeeAccumulator(ctx, poolId)
-}
-
 func (k Keeper) InitOrUpdateFeeAccumulatorPosition(ctx sdk.Context, poolId uint64, lowerTick, upperTick int64, positionId uint64, liquidity sdk.Dec) error {
 	return k.initOrUpdateFeeAccumulatorPosition(ctx, poolId, lowerTick, upperTick, positionId, liquidity)
 }
@@ -183,10 +179,6 @@ func (k Keeper) CreateUptimeAccumulators(ctx sdk.Context, poolId uint64) error {
 	return k.createUptimeAccumulators(ctx, poolId)
 }
 
-func (k Keeper) GetUptimeAccumulators(ctx sdk.Context, poolId uint64) ([]accum.AccumulatorObject, error) {
-	return k.getUptimeAccumulators(ctx, poolId)
-}
-
 func (k Keeper) GetUptimeAccumulatorValues(ctx sdk.Context, poolId uint64) ([]sdk.DecCoins, error) {
 	return k.getUptimeAccumulatorValues(ctx, poolId)
 }
diff --git a/x/superfluid/keeper/slash_test.go b/x/superfluid/keeper/slash_test.go
index 0b804836a..3f599f813 100644
--- a/x/superfluid/keeper/slash_test.go
+++ b/x/superfluid/keeper/slash_test.go
@@ -1,8 +1,10 @@
 package keeper_test
 
 import (
+	"github.com/osmosis-labs/osmosis/osmoutils/accum"
 	"github.com/osmosis-labs/osmosis/v15/app/apptesting"
 	cl "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity"
+	cltypes "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types"
 	lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types"
 	"github.com/osmosis-labs/osmosis/v15/x/superfluid/keeper"
 
@@ -260,6 +262,24 @@ func (suite *KeeperTestSuite) TestPrepareConcentratedLockForSlash() {
 
 			slashAmt := positionPreSlash.Liquidity.Mul(tc.slashPercent)
 
+			// Note the value of the fee accumulator before the slash for the position.
+			feeAccumPreSlash, err := suite.App.ConcentratedLiquidityKeeper.GetFeeAccumulator(suite.Ctx, clPoolId)
+			suite.Require().NoError(err)
+			feePositionKey := cltypes.KeyFeePositionAccumulator(positionId)
+			positionSizePreSlash, err := feeAccumPreSlash.GetPositionSize(feePositionKey)
+			suite.Require().NoError(err)
+
+			// Note the numShares value of the position at each of the uptime accumulators.
+			uptimeAccums, err := suite.App.ConcentratedLiquidityKeeper.GetUptimeAccumulators(suite.Ctx, clPoolId)
+			uptimePositionKey := string(cltypes.KeyPositionId(positionId))
+			suite.Require().NoError(err)
+			numShares := make([]sdk.Dec, len(uptimeAccums))
+			for i, uptimeAccum := range uptimeAccums {
+				position, err := accum.GetPosition(uptimeAccum, uptimePositionKey)
+				suite.Require().NoError(err)
+				numShares[i] = position.NumShares
+			}
+
 			// System under test
 			clPoolAddress, underlyingAssetsToSlash, err := suite.App.SuperfluidKeeper.PrepareConcentratedLockForSlash(suite.Ctx, lock, slashAmt)
 
@@ -273,6 +293,22 @@ func (suite *KeeperTestSuite) TestPrepareConcentratedLockForSlash() {
 			liquidityPostSlash := clPool.GetLiquidity()
 			if tc.expectedErr {
 				suite.Require().Error(err)
+
+				// Check that the position's fee accumulator has not changed.
+				feeAccumPostSlash, err := suite.App.ConcentratedLiquidityKeeper.GetFeeAccumulator(suite.Ctx, clPoolId)
+				suite.Require().NoError(err)
+				positionSizePostSlash, err := feeAccumPostSlash.GetPositionSize(feePositionKey)
+				suite.Require().NoError(err)
+				suite.Require().Equal(positionSizePreSlash.String(), positionSizePostSlash.String())
+
+				// Check that the numShares value of the position has not been updated in each of the uptime accumulators.
+				uptimeAccums, err := suite.App.ConcentratedLiquidityKeeper.GetUptimeAccumulators(suite.Ctx, clPoolId)
+				suite.Require().NoError(err)
+				for i, uptimeAccum := range uptimeAccums {
+					position, err := accum.GetPosition(uptimeAccum, uptimePositionKey)
+					suite.Require().NoError(err)
+					suite.Require().Equal(numShares[i].String(), position.NumShares.String())
+				}
 			} else {
 				suite.Require().NoError(err)
 				suite.Require().Equal(clPool.GetAddress(), clPoolAddress)
@@ -299,6 +335,22 @@ func (suite *KeeperTestSuite) TestPrepareConcentratedLockForSlash() {
 
 				suite.Require().Equal(0, errTolerance.Compare(asset0PreSlash.Sub(asset0PostSlash).Amount, underlyingAssetsToSlash[0].Amount))
 				suite.Require().Equal(0, errTolerance.Compare(asset1PreSlash.Sub(asset1PostSlash).Amount, underlyingAssetsToSlash[1].Amount))
+
+				// Check that the fee accumulator has been updated by the amount it was slashed.
+				feeAccumPostSlash, err := suite.App.ConcentratedLiquidityKeeper.GetFeeAccumulator(suite.Ctx, clPoolId)
+				suite.Require().NoError(err)
+				positionSizePostSlash, err := feeAccumPostSlash.GetPositionSize(feePositionKey)
+				suite.Require().NoError(err)
+				suite.Require().Equal(positionSizePreSlash.Sub(slashAmt).String(), positionSizePostSlash.String())
+
+				// Check that the numShares value of the position has been updated in each of the uptime accumulators.
+				uptimeAccums, err := suite.App.ConcentratedLiquidityKeeper.GetUptimeAccumulators(suite.Ctx, clPoolId)
+				suite.Require().NoError(err)
+				for i, uptimeAccum := range uptimeAccums {
+					position, err := accum.GetPosition(uptimeAccum, uptimePositionKey)
+					suite.Require().NoError(err)
+					suite.Require().Equal(numShares[i].Sub(slashAmt).String(), position.NumShares.String())
+				}
 			}
 		})
 	}