Skip to content

Commit

Permalink
[CL Incentives] Implement proper accumulator checkpointing for positi…
Browse files Browse the repository at this point in the history
…ons `initOrUpdatePositionUptime` (osmosis-labs#4455)

* 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 (osmosis-labs#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

* add and test helper & set up tick initialization logic

* implement accumulator updates on tick crossing and clean up proto

* clean up comments

* minor test cleanup

* ensure accum initalization follows conventions and clean up tests

* update pool proto and CL pool extension for tracking liq changes

* update new field at pool initialization and add tests

* comment todos

* clean up naming and move empty options to global var

* add clarifying comments and lint

* implement IncentiveRecord proto

* add comments

* lint

* set up wiring for proto

* implement updateUptimeAccumulatorsToNow

* implement and test calcAccruedIncentivesForAccum

* add tests for new helpers

* minor comment updates

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

* accum test updates

* lint

* clean up diff

* lint

* update go.mod for e2e

* clean up proto and add error message

* lint

* sketch out testing approach

* set up keys

* clean up diff

* clean up comments

* lint

* implement set and get for incentive records

* comment cleanup

* minor comment and test cleanup

* clean up tests and comments

* [CL Incentives] Implement `setIncentiveRecords` and `getIncentiveRecords` (osmosis-labs#4283)

* set up keys

* implement set and get for incentive records

* comment cleanup

* minor comment and test cleanup

* Revert "[CL Incentives] Implement `setIncentiveRecords` and `getIncentiveRecords` (osmosis-labs#4283)" (osmosis-labs#4294)

This reverts commit de7c146.

* set incentive records in new position tests

* fix merge conflicts

* add check for init position record values

* fix and finalize uptime accum position tests

* fix go mod for e2e

* clean up diff

* add err check to accum update

* fix rounding error

* implement tick init uptime tracker logic

* fix test compatibility

* implement fix for global accums falling out of sync before tick init

* add tests to ensure uptime trackers are initialized properly

* add comment for new helper

* add core logic

* go mod tidy

* fix conflicts

* fix e2e

* update go mod

* fix and further test tick init logic

* remove redundant values from incentive record state

* cleanup from review

* comment cleanup

* set up cross tick tests

* add further tests

* test and comment cleanup

* implement core logic

* update osmoutils go mod and remove curTick as fn param

* thorough tests for new functionality

* clean up new functions for readability

* clean up test comments

* fix test names

* lint

* add godoc and further comments

* update go mod for e2e

* update liq change logic

* update uptime accums before gettings them for tick crossing

* abstract uptime init and update logic to helper

* add thorough tests for initOrUpdatePositionUptime

* update liquidity update logic and tests to use new helper

* add todo for frozenuntil issue

* lint

* lint

* clean up logic and add tests for helper

* go mod for osmoutils e2e

* move position key computation outside loop

* clean up imports and go sum

* fix imports

---------

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 Mar 3, 2023
1 parent 7af3ce9 commit 035dba5
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 49 deletions.
6 changes: 6 additions & 0 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var (
EmptyCoins = emptyCoins
HundredFooCoins = sdk.NewDecCoin("foo", sdk.NewInt(100))
HundredBarCoins = sdk.NewDecCoin("bar", sdk.NewInt(100))
TwoHundredFooCoins = sdk.NewDecCoin("foo", sdk.NewInt(200))
TwoHundredBarCoins = sdk.NewDecCoin("bar", sdk.NewInt(200))
)

// OrderInitialPoolDenoms sets the pool denoms of a cl pool
Expand Down Expand Up @@ -221,3 +223,7 @@ func (k Keeper) SetMultipleIncentiveRecords(ctx sdk.Context, incentiveRecords []
func (k Keeper) GetInitialUptimeGrowthOutsidesForTick(ctx sdk.Context, poolId uint64, tick int64) ([]sdk.DecCoins, error) {
return k.getInitialUptimeGrowthOutsidesForTick(ctx, poolId, tick)
}

func (k Keeper) InitOrUpdatePositionUptime(ctx sdk.Context, poolId uint64, position *model.Position, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, frozenUntil time.Time) error {
return k.initOrUpdatePositionUptime(ctx, poolId, position, owner, lowerTick, upperTick, liquidityDelta, frozenUntil)
}
12 changes: 6 additions & 6 deletions x/concentrated-liquidity/fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,16 +278,16 @@ func formatFeePositionAccumulatorKey(poolId uint64, owner sdk.AccAddress, lowerT

// preparePositionAccumulator is called prior to updating unclaimed rewards,
// as we must set the position's accumulator value to the sum of
// - the fee growth inside at position creation time (position.InitAccumValue)
// - fee growth outside at the current block time (feeGrowthOutside)
func preparePositionAccumulator(feeAccumulator accum.AccumulatorObject, positionKey string, feeGrowthOutside sdk.DecCoins) error {
position, err := accum.GetPosition(feeAccumulator, positionKey)
// - the fee/uptime growth inside at position creation time (position.InitAccumValue)
// - fee/uptime growth outside at the current block time (feeGrowthOutside/uptimeGrowthOutside)
func preparePositionAccumulator(accumulator accum.AccumulatorObject, positionKey string, growthOutside sdk.DecCoins) error {
position, err := accum.GetPosition(accumulator, positionKey)
if err != nil {
return err
}

customAccumulatorValue := position.InitAccumValue.Add(feeGrowthOutside...)
err = feeAccumulator.SetPositionCustomAcc(positionKey, customAccumulatorValue)
customAccumulatorValue := position.InitAccumValue.Add(growthOutside...)
err = accumulator.SetPositionCustomAcc(positionKey, customAccumulatorValue)
if err != nil {
return err
}
Expand Down
73 changes: 73 additions & 0 deletions x/concentrated-liquidity/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,76 @@ func (k Keeper) GetUptimeGrowthOutsideRange(ctx sdk.Context, poolId uint64, lowe

return osmoutils.SubDecCoinArrays(globalUptimeValues, uptimeGrowthInside)
}

// initOrUpdatePositionUptime either adds or updates records for all uptime accumulators `position` qualifies for
func (k Keeper) initOrUpdatePositionUptime(ctx sdk.Context, poolId uint64, position *model.Position, owner sdk.AccAddress, lowerTick, upperTick int64, liquidityDelta sdk.Dec, frozenUntil time.Time) error {
// We update accumulators _prior_ to any position-related updates to ensure
// past rewards aren't distributed to new liquidity. We also update pool's
// LastLiquidityUpdate here.
err := k.updateUptimeAccumulatorsToNow(ctx, poolId)
if err != nil {
return err
}

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

globalUptimeGrowthInsideRange, err := k.GetUptimeGrowthInsideRange(ctx, poolId, lowerTick, upperTick)
if err != nil {
return err
}

globalUptimeGrowthOutsideRange, err := k.GetUptimeGrowthOutsideRange(ctx, poolId, lowerTick, upperTick)
if err != nil {
return err
}

// Loop through uptime accums for all supported uptimes on the pool and init or update position's records
positionName := string(types.KeyFullPosition(poolId, owner, lowerTick, upperTick, frozenUntil))
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.

// TODO: consider replacing BlockTime with a new field, JoinTime, so that post-join updates are not skipped
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.
recordExists, err := curUptimeAccum.HasPosition(positionName)
if err != nil {
return err
}

if !recordExists {
// Since the position should only be entitled to uptime growth within its range, we checkpoint globalUptimeGrowthInsideRange as
// its accumulator's init value. During the claiming (or, equivalently, position updating) process, we ensure that incentives are
// not overpaid.
err = curUptimeAccum.NewPositionCustomAcc(positionName, position.Liquidity, globalUptimeGrowthInsideRange[uptimeIndex], emptyOptions)
if err != nil {
return err
}
} else {
// Prep accum since we claim rewards first under the hood before any update (otherwise we would overpay)
err = preparePositionAccumulator(curUptimeAccum, positionName, globalUptimeGrowthOutsideRange[uptimeIndex])
if err != nil {
return err
}

// Note that even though "unclaimed rewards" accrue in the accumulator prior to frozenUntil, since position withdrawal
// and incentive collection are only allowed when current time is past frozenUntil these rewards are not accessible until then.
err = curUptimeAccum.UpdatePositionCustomAcc(positionName, liquidityDelta, globalUptimeGrowthInsideRange[uptimeIndex])
if err != nil {
return err
}
}
}
}

return nil
}
Loading

0 comments on commit 035dba5

Please sign in to comment.