Skip to content

Commit

Permalink
[CL Incentives] Track relevant uptime accumulators in new positions (#…
Browse files Browse the repository at this point in the history
…4138)

* initial push

* push

* untracked files

* add untracked files

* merge branches

* add changes required for fees

* lint

* fix test errors

* clean up

* remove print lines

* feat: single fee accum (#4116)

* single fee accum

* lint

* no longer need to get all positions for fee

* incentive accum initialization

* lint

* Update x/concentrated-liquidity/store_test.go

Co-authored-by: Sishir Giri <[email protected]>

* add lower level tests and make uptime accum access private

* lint

* add gotests for helper fn

* Update test to use existing variable

Co-authored-by: Roman <[email protected]>

* create or update records upon position creation/update

* add tests and clean up

* sync position-related changes

* add support for negative liquidity delta

* minor test cleanup

* clean up naming and move empty options to global var

* add clarifying comments and lint

* lint

* minor comment updates

Co-authored-by: Roman <[email protected]>

---------

Co-authored-by: Adam Tucker <[email protected]>
Co-authored-by: Adam Tucker <[email protected]>
Co-authored-by: Sishir Giri <[email protected]>
Co-authored-by: Roman <[email protected]>
  • Loading branch information
5 people authored Feb 3, 2023
1 parent 826832a commit 237161a
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 28 deletions.
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,6 @@ func (k Keeper) GetUptimeAccumulators(ctx sdk.Context, poolId uint64) ([]accum.A
return k.getUptimeAccumulators(ctx, poolId)
}

func GetUptimeAccumulatorName(poolId, uptimeId uint64) string {
return getUptimeAccumulatorName(poolId, uptimeId)
func GetUptimeAccumulatorName(poolId, uptimeIndex uint64) string {
return getUptimeAccumulatorName(poolId, uptimeIndex)
}
9 changes: 4 additions & 5 deletions x/concentrated-liquidity/fees_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ func (s *KeeperTestSuite) TestCalculateFeeGrowth() {
s.Require().Equal(feeGrowth, tc.expectedFeeGrowth)
})
}

}

func (suite *KeeperTestSuite) TestGetInitialFeeGrowthOutsideForTick() {
Expand Down Expand Up @@ -464,6 +463,9 @@ func (suite *KeeperTestSuite) TestGetInitialFeeGrowthOutsideForTick() {
suite.Require().NoError(err)

// Setup test position to make sure that tick is initialized
// We also set up uptime accums to ensure position creation works as intended
err = clKeeper.CreateUptimeAccumulators(ctx, validPoolId)
suite.Require().NoError(err)
suite.SetupDefaultPosition(validPoolId)

err = clKeeper.ChargeFee(ctx, validPoolId, tc.initialGlobalFeeGrowth)
Expand Down Expand Up @@ -556,9 +558,7 @@ func (suite *KeeperTestSuite) TestChargeFee() {
}

func (s *KeeperTestSuite) TestCollectFees() {
var (
ownerWithValidPosition = s.TestAccs[0]
)
ownerWithValidPosition := s.TestAccs[0]
defaultFrozenUntil := s.Ctx.BlockTime().Add(DefaultFreezeDuration)

tests := map[string]struct {
Expand Down Expand Up @@ -949,5 +949,4 @@ func (s *KeeperTestSuite) TestUpdateFeeAccumulatorPosition() {
}
})
}

}
16 changes: 8 additions & 8 deletions x/concentrated-liquidity/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const (
// createUptimeAccumulators creates accumulator objects in store for each supported uptime for the given poolId.
// The accumulators are initialized with the default (zero) values.
func (k Keeper) createUptimeAccumulators(ctx sdk.Context, poolId uint64) error {
for uptimeId := range types.SupportedUptimes {
err := accum.MakeAccumulator(ctx.KVStore(k.storeKey), getUptimeAccumulatorName(poolId, uint64(uptimeId)))
for uptimeIndex := range types.SupportedUptimes {
err := accum.MakeAccumulator(ctx.KVStore(k.storeKey), getUptimeAccumulatorName(poolId, uint64(uptimeIndex)))
if err != nil {
return err
}
Expand All @@ -27,24 +27,24 @@ func (k Keeper) createUptimeAccumulators(ctx sdk.Context, poolId uint64) error {
return nil
}

func getUptimeAccumulatorName(poolId uint64, uptimeId uint64) string {
func getUptimeAccumulatorName(poolId uint64, uptimeIndex uint64) string {
poolIdStr := strconv.FormatUint(poolId, uintBase)
uptimeIdStr := strconv.FormatUint(uptimeId, uintBase)
return strings.Join([]string{uptimeAccumPrefix, poolIdStr, uptimeIdStr}, "/")
uptimeIndexStr := strconv.FormatUint(uptimeIndex, uintBase)
return strings.Join([]string{uptimeAccumPrefix, poolIdStr, uptimeIndexStr}, "/")
}

// nolint: unused
// 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) {
accums := make([]accum.AccumulatorObject, len(types.SupportedUptimes))
for uptimeId := range types.SupportedUptimes {
acc, err := accum.GetAccumulator(ctx.KVStore(k.storeKey), getUptimeAccumulatorName(poolId, uint64(uptimeId)))
for uptimeIndex := range types.SupportedUptimes {
acc, err := accum.GetAccumulator(ctx.KVStore(k.storeKey), getUptimeAccumulatorName(poolId, uint64(uptimeIndex)))
if err != nil {
return []accum.AccumulatorObject{}, err
}

accums[uptimeId] = acc
accums[uptimeIndex] = acc
}

return accums, nil
Expand Down
24 changes: 12 additions & 12 deletions x/concentrated-liquidity/incentives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,26 @@ func (s *KeeperTestSuite) TestCreateAndGetUptimeAccumulators() {

type initUptimeAccumTest struct {
poolId uint64
initializePool bool
initializePoolAccum bool
expectedAccumValues []sdk.DecCoins

expectedPass bool
}
tests := map[string]initUptimeAccumTest{
"default pool setup": {
poolId: defaultPoolId,
initializePool: true,
initializePoolAccum: true,
expectedAccumValues: curExpectedAccumValues,
expectedPass: true,
},
"setup with different poolId": {
poolId: defaultPoolId + 1,
initializePool: true,
initializePoolAccum: true,
expectedAccumValues: curExpectedAccumValues,
expectedPass: true,
},
"pool not initialized": {
initializePool: false,
initializePoolAccum: false,
poolId: defaultPoolId,
expectedAccumValues: []sdk.DecCoins{},
expectedPass: false,
Expand All @@ -54,9 +54,9 @@ func (s *KeeperTestSuite) TestCreateAndGetUptimeAccumulators() {
clKeeper := s.App.ConcentratedLiquidityKeeper

// system under test
var err error
if tc.initializePool {
err = clKeeper.CreateUptimeAccumulators(s.Ctx, tc.poolId)
if tc.initializePoolAccum {
err := clKeeper.CreateUptimeAccumulators(s.Ctx, tc.poolId)
s.Require().NoError(err)
}
poolUptimeAccumulators, err := clKeeper.GetUptimeAccumulators(s.Ctx, tc.poolId)

Expand Down Expand Up @@ -85,23 +85,23 @@ func (s *KeeperTestSuite) TestCreateAndGetUptimeAccumulators() {
func (s *KeeperTestSuite) TestGetUptimeAccumulatorName() {
type getUptimeNameTest struct {
poolId uint64
uptimeId uint64
uptimeIndex uint64
expectedAccumName string
}
tests := map[string]getUptimeNameTest{
"pool id 1, uptime id 0": {
poolId: defaultPoolId,
uptimeId: uint64(0),
uptimeIndex: uint64(0),
expectedAccumName: "uptime/1/0",
},
"pool id 1, uptime id 999": {
poolId: defaultPoolId,
uptimeId: uint64(999),
uptimeIndex: uint64(999),
expectedAccumName: "uptime/1/999",
},
"pool id 999, uptime id 1": {
poolId: uint64(999),
uptimeId: uint64(1),
uptimeIndex: uint64(1),
expectedAccumName: "uptime/999/1",
},
}
Expand All @@ -112,7 +112,7 @@ func (s *KeeperTestSuite) TestGetUptimeAccumulatorName() {
s.SetupTest()

// system under test
accumName := cl.GetUptimeAccumulatorName(tc.poolId, tc.uptimeId)
accumName := cl.GetUptimeAccumulatorName(tc.poolId, tc.uptimeIndex)
s.Require().Equal(tc.expectedAccumName, accumName)
})
}
Expand Down
38 changes: 38 additions & 0 deletions x/concentrated-liquidity/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/osmosis/osmoutils"
"github.com/osmosis-labs/osmosis/osmoutils/accum"
"github.com/osmosis-labs/osmosis/v14/x/concentrated-liquidity/model"
types "github.com/osmosis-labs/osmosis/v14/x/concentrated-liquidity/types"
)

var emptyOptions = &accum.Options{}

// getOrInitPosition retrieves the position for the given tick range. If it doesn't exist, it returns an initialized position with zero liquidity.
func (k Keeper) getOrInitPosition(
ctx sdk.Context,
Expand Down Expand Up @@ -62,6 +65,41 @@ func (k Keeper) initOrUpdatePosition(

// TODO: consider deleting position if liquidity becomes zero

// Create records for relevant uptime accumulators here.
uptimeAccumulators, err := k.getUptimeAccumulators(ctx, poolId)
if err != nil {
return err
}

for uptimeIndex, uptime := range types.SupportedUptimes {
// We assume every position update requires the position to be frozen for the
// min uptime again. Thus, the difference between the position's `FrozenUntil`
// and the blocktime when the update happens should be greater than or equal
// to the required uptime.
if position.FrozenUntil.Sub(ctx.BlockTime()) >= uptime {
curUptimeAccum := uptimeAccumulators[uptimeIndex]

// If a record does not exist for this uptime accumulator, create a new position.
// Otherwise, add to existing record.
positionName := string(types.KeyFullPosition(poolId, owner, lowerTick, upperTick, frozenUntil))
recordExists, err := curUptimeAccum.HasPosition(positionName)
if err != nil {
return err
}

if !recordExists {
err = curUptimeAccum.NewPosition(positionName, position.Liquidity, emptyOptions)
} else if !liquidityDelta.IsNegative() {
err = curUptimeAccum.AddToPosition(positionName, liquidityDelta)
} else {
err = curUptimeAccum.RemoveFromPosition(positionName, liquidityDelta.Neg())
}
if err != nil {
return err
}
}
}

k.setPosition(ctx, poolId, owner, lowerTick, upperTick, position, frozenUntil)
return nil
}
Expand Down
17 changes: 16 additions & 1 deletion x/concentrated-liquidity/position_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ func (s *KeeperTestSuite) TestInitOrUpdatePosition() {

// Check that the initialized or updated position matches our expectation
s.Require().Equal(test.expectedLiquidity, positionInfo.Liquidity)

// Check that the relevant uptime accumulators were properly checkpointed
positionName := string(types.KeyFullPosition(validPoolId, s.TestAccs[0], test.param.lowerTick, test.param.upperTick, test.param.frozenUntil))
uptimeAccums, err := s.App.ConcentratedLiquidityKeeper.GetUptimeAccumulators(s.Ctx, test.param.poolId)
s.Require().NoError(err)

// If frozen for more than a specific uptime's period, the record should exist
for uptimeIndex, uptime := range types.SupportedUptimes {
recordExists, err := uptimeAccums[uptimeIndex].HasPosition(positionName)
s.Require().NoError(err)
if test.param.frozenUntil.Sub(s.Ctx.BlockTime()) >= uptime {
s.Require().True(recordExists)
} else {
s.Require().False(recordExists)
}
}
})
}
}
Expand Down Expand Up @@ -210,7 +226,6 @@ func (s *KeeperTestSuite) TestGetPosition() {
s.Require().NoError(err)
s.Require().Equal(test.expectedPosition, position)
}

})
}
}
Expand Down

0 comments on commit 237161a

Please sign in to comment.