Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(v18: feat) Volume-Split, setup gauges to split evenly #6085

Merged
merged 7 commits into from
Aug 23, 2023

Conversation

stackman27
Copy link
Contributor

@stackman27 stackman27 commented Aug 16, 2023

Part of : #6057

What is the purpose of the change

  • this feature contains creation of GroupGauges using the gauge architecture as well as splitting incentives rewards evenly across a specified InternalGaugeIds. Once we have the logic for volume split, we will split based on that.

Testing and Verifying

  • added unit test for functions
  • added functional test for the whole flow

Documentation and Release Note

  • Does this pull request introduce a new feature or user-facing behavior changes?
  • Changelog entry added to Unreleased section of CHANGELOG.md?

Where is the change documented?

  • Specification (x/{module}/README.md)
  • Osmosis documentation site
  • Code comments?
  • N/A

Comment on lines +892 to +910
// - we only distribute external incentive in epoch 1.
// - Check that incentive record has been correctly created and gauge has been correctly updated.
// - all perpetual gauges must finish distributing records
// - ClPool1 will recieve full 1Musdc, 1Meth in this epoch.
// - ClPool2 will recieve 500kusdc, 500keth in this epoch.
// - ClPool3 will recieve full 1Musdc, 1Meth in this epoch whereas
//
// 6. Remove distribution records for internal incentives using HandleReplacePoolIncentivesProposal
// 7. let epoch 2 pass
// - We distribute internal incentive in epoch 2.
// - check only external non-perpetual gauges with 2 epochs distributed
// - check gauge has been correctly updated
// - ClPool1 will already have 1Musdc, 1Meth (from epoch1) as external incentive. Will recieve 750Kstake as internal incentive.
// - ClPool2 will already have 500kusdc, 500keth (from epoch1) as external incentive. Will recieve 500kusdc, 500keth (from epoch 2) as external incentive and 750Kstake as internal incentive.
// - ClPool3 will already have 1M, 1M (from epoch1) as external incentive. This pool will not recieve any internal incentive.
// - We distribute internal incentive in epoch 2.
// - check only external non-perpetual gauges with 2 epochs distributed
// - check gauge has been correctly updated
// - ClPool1 will already have 1Musdc, 1Meth (from epoch1) as external incentive. Will recieve 750Kstake as internal incentive.
// - ClPool2 will already have 500kusdc, 500keth (from epoch1) as external incentive. Will recieve 500kusdc, 500keth (from epoch 2) as external incentive and 750Kstake as internal incentive.
// - ClPool3 will already have 1M, 1M (from epoch1) as external incentive. This pool will not recieve any internal incentive.
//
// 8. let epoch 3 pass
// - nothing distributes as non-perpetual gauges with 2 epochs have ended and perpetual gauges have not been reloaded
// - nothing should change in terms of incentive records
// - nothing distributes as non-perpetual gauges with 2 epochs have ended and perpetual gauges have not been reloaded
// - nothing should change in terms of incentive records
Copy link
Contributor Author

@stackman27 stackman27 Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linter change, can ignore

@stackman27
Copy link
Contributor Author

@devbot-wizard help

@devbot-wizard
Copy link
Collaborator

Hi! I'm DevBot, a bot that helps with common tasks in the development process. Commands:

  • devbot fix: Fix basic errors in the PR. (e.g. imports, changelog conflicts, go mod conflicts)
  • devbot merge base: Merge the base branch into the PR branch.
  • devbot add changelog [misc/feat/bug/api/sec] [message]: Add a changelog entry to the PR. (e.g. devbot add changelog feat Added a new feature)
    • If message is blank, defaults to PR title.
  • devbot re-pr: Re-PR the PR, useful for external contributors where we have no edit perms.

@stackman27
Copy link
Contributor Author

devbot add changelog (v18: feat) Volume-Split, setup gauges to split evenly

@stackman27 stackman27 added the V:state/breaking State machine breaking PR label Aug 16, 2023
@stackman27 stackman27 marked this pull request as ready for review August 16, 2023 19:29
Copy link
Contributor

@AlpinYukseloglu AlpinYukseloglu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core logic looks generally good, but I had a couple of concerns around checks & testing that will require a little bit more work before this is merge-ready

x/incentives/keeper/distribute.go Outdated Show resolved Hide resolved
// internalGauges.
message GroupGauge {
uint64 group_gauge_id = 1;
repeated uint64 internal_ids = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include a splitting policy field here as an enum, even if it currently just evenly splits

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x/incentives/keeper/distribute.go Outdated Show resolved Hide resolved
@@ -441,7 +480,10 @@ func (k Keeper) Distribute(ctx sdk.Context, gauges []types.Gauge) (sdk.Coins, er
ctx.Logger().Debug("distributeSyntheticInternal, gauge id %d, %d", "module", types.ModuleName, "gaugeId", gauge.Id, "height", ctx.BlockHeight())
gaugeDistributedCoins, err = k.distributeSyntheticInternal(ctx, gauge, filteredLocks, &distrInfo)
} else {
gaugeDistributedCoins, err = k.distributeInternal(ctx, gauge, filteredLocks, &distrInfo)
// Donot distribue if LockQueryType = Group, because if we distribute here we will be double distributing.
if gauge.DistributeTo.LockQueryType != lockuptypes.ByGroup {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe skipping in this way might lead to totalDistributedCoins just double counting the previous loop's gaugeDistributedCoins (if this is the case, let's make sure that tests catch this in the future).

The more appropriate logic seems to be if [same condition], continue and then leave the distributeInternal unchanged

Copy link
Contributor Author

@stackman27 stackman27 Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup makes sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x/incentives/keeper/distribute_test.go Show resolved Hide resolved
var (
epochInfo = s.App.IncentivesKeeper.GetEpochInfo(s.Ctx)

expectedCoinsDistributed = sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(11_111_111)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, having new logic rely entirely on a single hard coded test vector is probably not a sufficient testing practice. Happy to discuss what might be a more robust approach offline if you'd like

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x/incentives/keeper/gauge_test.go Show resolved Hide resolved
// create 3 internal Gauge
var internalGauges []uint64
for i := 0; i <= 2; i++ {
internalGauge := s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2,3,4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of more readable & robust tests, it would probably be better to abstract this setup into a test helper and add numberOfExistingGauges as a test parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created helper for creating internalGauges for GroupGauges
c99cb1d#diff-1000dbecb1333fe09226de57d089fe167e289bc9edae3aac2f040029bfa91b1eR51

Comment on lines 58 to 59
Liquidity = 1;
Evenly = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Liquidity = 1;
Evenly = 2;
Evenly = 1;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're likely to simply remove liquidity based splitting for the reasons described in #6093

x/incentives/types/keys.go Outdated Show resolved Hide resolved
internalGaugeIds: []uint64{2, 3, 4, 5},
}

tests := []struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo the map[string]struct structure is much cleaner (e.g. see how it's done here – no need for name as a field and you can pick up the test case name from the first for loop field on line 1289 below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, i personally like writing name field, it just gives me more clarity while writing tests 😅

happy to change tho if we want to stay consistent with rest of the test

x/incentives/keeper/distribute.go Outdated Show resolved Hide resolved
}

// CalcSplitPolicyCoins calculates tokens to split given a policy and groupGauge.
func (k Keeper) CalcSplitPolicyCoins(ctx sdk.Context, policy types.SplittingPolicy, groupGauge *types.Gauge, groupGaugeObj types.GroupGauge) (sdk.Coins, sdk.Coins, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this should probably be a private function

}

for _, groupGauge := range groupGauges {
gauge, err := k.GetGaugeByID(ctx, groupGauge.GroupGaugeId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err what are we actually getting here? Don't we already have the group gauge?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetAllGroupGauges returns the ids for ex: {GroupGaugeId, InternalIds and Splitting policy}, we still have to fetch the actual gauge to get its fields

}

// only allow distribution if the GroupGauge is Active
if !currTime.Before(gauge.StartTime) && (gauge.IsPerpetual || gauge.FilledEpochs < gauge.NumEpochsPaidOver) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for general readability, I think doing something like "if currTime.After(startTime) && (!gaugeIsActive bool), then continue" would be cleaner instead of having half the function in an if branch.

Ofc feel free to ignore this if you think the current approach is more readable (hence the nit) – my thinking is just that you need to keep track of fewer things mentally when you're not in a branch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup i think we can just replace this with
gauge.IsActiveGauge(currTime) as well. Looks cleaner

x/incentives/keeper/gauge.go Show resolved Hide resolved
@stackman27 stackman27 added the F: volume-splitting feat: Volume-splitting incentives label Aug 19, 2023
Copy link
Contributor

@AlpinYukseloglu AlpinYukseloglu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice work

k.SetGroupGauge(ctx, newGroupGauge)
k.SetLastGaugeID(ctx, gauge.Id)

// TODO: check if this is necessary, will investigate this in following PR.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you flag this in an issue?

@AlpinYukseloglu AlpinYukseloglu merged commit eca86f7 into main Aug 23, 2023
1 check passed
@AlpinYukseloglu AlpinYukseloglu deleted the stack/vol-split-inc branch August 23, 2023 04:27
nicolaslara pushed a commit that referenced this pull request Aug 31, 2023
* (v18: feat) Volume-Split, setup gauges to split evenly

* update changelog

* halfway done refactoring test

* refactored test

* alp comments and refactored tests

* alp comments

* nits

---------

Co-authored-by: devbot-wizard <141283918+devbot-wizard@users.noreply.github.com>
@github-actions github-actions bot mentioned this pull request Mar 15, 2024
@github-actions github-actions bot mentioned this pull request Apr 1, 2024
@github-actions github-actions bot mentioned this pull request Apr 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:x/incentives C:x/lockup F: volume-splitting feat: Volume-splitting incentives V:state/breaking State machine breaking PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants