Skip to content

Commit

Permalink
alp comments and refactored tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stackman27 committed Aug 18, 2023
1 parent 58dde1a commit c99cb1d
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 66 deletions.
24 changes: 14 additions & 10 deletions x/incentives/keeper/distribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ func (k Keeper) AllocateAcrossGauges(ctx sdk.Context) error {

// only allow distribution if the GroupGauge is Active
if !currTime.Before(gauge.StartTime) && (gauge.IsPerpetual || gauge.FilledEpochs < gauge.NumEpochsPaidOver) {
coinsToDistributePerInternalGauge, coinsToDistributeThisEpoch := k.CalcSplitPolicyCoins(ctx, groupGauge.SplittingPolicy, gauge, groupGauge)
if coinsToDistributePerInternalGauge == nil || coinsToDistributeThisEpoch == nil {
return fmt.Errorf("GroupGauge id %d doesnot have enought coins to distribute.", groupGauge.GroupGaugeId)
coinsToDistributePerInternalGauge, coinsToDistributeThisEpoch, err := k.CalcSplitPolicyCoins(ctx, groupGauge.SplittingPolicy, gauge, groupGauge)
if err != nil {
return err
}

for _, internalGaugeId := range groupGauge.InternalIds {
Expand All @@ -307,24 +307,28 @@ func (k Keeper) AllocateAcrossGauges(ctx sdk.Context) error {
}

// 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) {
func (k Keeper) CalcSplitPolicyCoins(ctx sdk.Context, policy types.SplittingPolicy, groupGauge *types.Gauge, groupGaugeObj types.GroupGauge) (sdk.Coins, sdk.Coins, error) {
// TODO: add volume split policy
if policy == types.Evenly {
remainCoins := groupGauge.Coins.Sub(groupGauge.DistributedCoins)

var coinsDistPerInternalGauge, coinsDistThisEpoch sdk.Coins
for _, coin := range remainCoins {
amountToDistributeThisEpoch := coin.Amount.Quo(sdk.NewIntFromUint64(groupGauge.NumEpochsPaidOver - groupGauge.FilledEpochs))
amountToDistributePerInternalGauge := amountToDistributeThisEpoch.Quo(sdk.NewInt(int64(len(groupGaugeObj.InternalIds))))
epochDiff := groupGauge.NumEpochsPaidOver - groupGauge.FilledEpochs
internalGaugeLen := len(groupGaugeObj.InternalIds)

distPerEpoch := coin.Amount.Quo(sdk.NewIntFromUint64(epochDiff))
distPerGauge := distPerEpoch.Quo(sdk.NewInt(int64(internalGaugeLen)))

coinsDistThisEpoch = coinsDistThisEpoch.Add(sdk.NewCoin(coin.Denom, amountToDistributeThisEpoch))
coinsDistPerInternalGauge = coinsDistPerInternalGauge.Add(sdk.NewCoin(coin.Denom, amountToDistributePerInternalGauge))
coinsDistThisEpoch = coinsDistThisEpoch.Add(sdk.NewCoin(coin.Denom, distPerEpoch))
coinsDistPerInternalGauge = coinsDistPerInternalGauge.Add(sdk.NewCoin(coin.Denom, distPerGauge))
}

return coinsDistPerInternalGauge, coinsDistThisEpoch
return coinsDistPerInternalGauge, coinsDistThisEpoch, nil
} else {
return nil, nil, fmt.Errorf("GroupGauge id %d doesnot have enought coins to distribute.", &groupGauge.Id)
}

return nil, nil
}

// distributeInternal runs the distribution logic for a gauge, and adds the sends to
Expand Down
132 changes: 85 additions & 47 deletions x/incentives/keeper/distribute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1126,60 +1126,78 @@ func (s *KeeperTestSuite) IncentivizeInternalGauge(poolIds []uint64, epochDurati
s.Require().NoError(err)
}
func (s *KeeperTestSuite) TestAllocateAcrossGauges() {
s.SetupTest()
expectedInternalGaugeCoins := sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(33_333_333)))
s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000)))) // 1,000 osmo
clPool := s.PrepareConcentratedPool() // gaugeid = 1

// 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
internalGauges = append(internalGauges, internalGauge)
tests := []struct {
name string
GroupGauge types.GroupGauge
expectedAllocationPerGroupGauge sdk.Coins
expectedAllocationPerInternalGauge sdk.Coins
expectError bool
}{
{
name: "Happy case: Valid perp Group Gauge",
GroupGauge: types.GroupGauge{
GroupGaugeId: 9,
InternalIds: []uint64{2, 3, 4},
SplittingPolicy: types.Evenly,
},
expectedAllocationPerGroupGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))),
expectedAllocationPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(33_333_333))),
expectError: false,
},
{
name: "Happy Case: Valid non-perp Group Gauge",
GroupGauge: types.GroupGauge{
GroupGaugeId: 10,
InternalIds: []uint64{5, 6, 7},
SplittingPolicy: types.Evenly,
},
expectedAllocationPerGroupGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(50_000_000))),
expectedAllocationPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(16_666_666))),
expectError: false,
},
}

// create group gauge
groupGauge, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], internalGauges) // gauge id = 3
s.Require().NoError(err)

// Call AllocateAcrossGauges
// Allocate 100 osmo across 3 internal gauges evenly
s.App.IncentivesKeeper.AllocateAcrossGauges(s.Ctx)

groupGaugePostAllocate, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, groupGauge)
s.Require().NoError(err)
for _, tc := range tests {
s.Run(tc.name, func() {
s.SetupTest()
s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))))
clPool := s.PrepareConcentratedPool()

s.Require().Equal(groupGaugePostAllocate.Coins.Sub(groupGaugePostAllocate.DistributedCoins), sdk.Coins(nil))
// create 3 internal Gauge
internalGauges := s.setupNoLockInternalGauge(clPool.GetId(), uint64(6)) // gauge Id = 2,3,4,5,6,7

for _, gauge := range internalGauges {
internalGauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, gauge)
s.Require().NoError(err)
// create non-perp internal Gauge
s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gaugeid= 8

s.Require().Equal(internalGauge.Coins.Sub(internalGauge.DistributedCoins), expectedInternalGaugeCoins)
}
// create perp group gauge
_, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], internalGauges[:3], lockuptypes.ByGroup, types.Evenly) // gauge id = 2,3,4
s.Require().NoError(err)

}
// create non-perp group gauge
_, err = s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(2), s.TestAccs[1], internalGauges[len(internalGauges)-3:], lockuptypes.ByGroup, types.Evenly) // gauge id = 5,6,7
s.Require().NoError(err)

func (s *KeeperTestSuite) SetupGroupGauge(clPoolId uint64, lockOwner sdk.AccAddress, numOfNoLockGauges uint64, numOfLockGauges uint64) []uint64 {
var internalGauges []uint64
// Call Testing function
err = s.App.IncentivesKeeper.AllocateAcrossGauges(s.Ctx)
if tc.expectError {
s.Require().Error(err)
} else {
s.Require().NoError(err)

// create Internal Gauges
for i := uint64(1); i <= numOfNoLockGauges; i++ {
clNoLockGaugeId := s.CreateNoLockExternalGauges(clPoolId, sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2,3,4
internalGauges = append(internalGauges, clNoLockGaugeId)
}
groupGaugePostAllocate, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, tc.GroupGauge.GroupGaugeId)
s.Require().NoError(err)

for i := uint64(1); i <= numOfLockGauges; i++ {
// setup lock
s.LockTokens(lockOwner, sdk.Coins{sdk.NewInt64Coin("lptoken", 10)}, time.Hour*7)
s.Require().Equal(groupGaugePostAllocate.DistributedCoins, tc.expectedAllocationPerGroupGauge)

// create gauge
gaugeID, _, _, _ := s.SetupNewGauge(true, sdk.NewCoins())
for _, gauge := range tc.GroupGauge.InternalIds {
internalGauge, err := s.App.IncentivesKeeper.GetGaugeByID(s.Ctx, gauge)
s.Require().NoError(err)

internalGauges = append(internalGauges, gaugeID)
s.Require().Equal(internalGauge.Coins, tc.expectedAllocationPerInternalGauge)
}
}
})
}

return internalGauges
}

func (s *KeeperTestSuite) WithBaseCaseDifferentCoins(baseCase GroupGaugeCreationFields, newCoins sdk.Coins) GroupGaugeCreationFields {
Expand Down Expand Up @@ -1213,10 +1231,12 @@ func (s *KeeperTestSuite) TestCreateGroupGaugeAndDistribute() {
}

tests := []struct {
name string
createGauge GroupGaugeCreationFields
expectedCoinsPerInternalGauge sdk.Coins
expectedCoinsDistributedPerEpoch sdk.Coins
name string
createGauge GroupGaugeCreationFields
expectedCoinsPerInternalGauge sdk.Coins
expectedCoinsDistributedPerEpoch sdk.Coins
expectCreateGroupGaugeError bool
expectDistributeToInternalGaugeError bool
}{
{
name: "Valid case: Valid perp-GroupGauge Creation and Distribution",
Expand Down Expand Up @@ -1254,6 +1274,16 @@ func (s *KeeperTestSuite) TestCreateGroupGaugeAndDistribute() {
expectedCoinsPerInternalGauge: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(12_500_000)), sdk.NewCoin("uatom", sdk.NewInt(12_500_000))),
expectedCoinsDistributedPerEpoch: sdk.NewCoins(fifetyKUosmo, fifetyKUatom),
},
{
name: "InValid case: Creating a GroupGauge with invalid internalIds",
createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{100, 101}),
expectCreateGroupGaugeError: true,
},
{
name: "InValid case: Creating a GroupGauge with non-perpetual internalId",
createGauge: s.WithBaseCaseDifferentInternalGauges(*baseCase, []uint64{2, 3, 4, 6}),
expectCreateGroupGaugeError: true,
},
}

for _, tc := range tests {
Expand All @@ -1267,7 +1297,15 @@ func (s *KeeperTestSuite) TestCreateGroupGaugeAndDistribute() {
epochInfo := s.App.IncentivesKeeper.GetEpochInfo(s.Ctx)
s.SetupGroupGauge(clPool.GetId(), lockOwner, uint64(3), uint64(1))

groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.createGauge.coins, tc.createGauge.numEpochPaidOver, tc.createGauge.owner, tc.createGauge.internalGaugeIds) // gauge id = 6
//create 1 non-perp internal Gauge
s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gauge id = 6

groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.createGauge.coins, tc.createGauge.numEpochPaidOver, tc.createGauge.owner, tc.createGauge.internalGaugeIds, lockuptypes.ByGroup, types.Evenly) // gauge id = 6
if tc.expectCreateGroupGaugeError {
s.Require().Error(err)
return
}

s.Require().NoError(err)

groupGaugeObj, err := s.App.IncentivesKeeper.GetGroupGaugeById(s.Ctx, groupGaugeId)
Expand Down
16 changes: 12 additions & 4 deletions x/incentives/keeper/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,19 @@ func (k Keeper) CreateGauge(ctx sdk.Context, isPerpetual bool, owner sdk.AccAddr
// CreateGroupGauge creates a new gauge, that allocates rewards dynamically across its internal gauges based on the given splitting policy.
// Note: we should expect that the internal gauges consist of the gauges that are automatically created for each pool upon pool creation, as even non-perpetual
// external incentives would likely want to route through these.
func (k Keeper) CreateGroupGauge(ctx sdk.Context, coins sdk.Coins, numEpochPaidOver uint64, owner sdk.AccAddress, internalGaugeIds []uint64) (uint64, error) {
func (k Keeper) CreateGroupGauge(ctx sdk.Context, coins sdk.Coins, numEpochPaidOver uint64, owner sdk.AccAddress, internalGaugeIds []uint64, gaugetype lockuptypes.LockQueryType, splittingPolicy types.SplittingPolicy) (uint64, error) {
if len(internalGaugeIds) == 0 {
return 0, fmt.Errorf("No internalGauge provided.")
}

if gaugetype != lockuptypes.ByGroup {
return 0, fmt.Errorf("Invalid gauge type needs to be ByGroup, got %s.", gaugetype)
}

if splittingPolicy != types.Evenly {
return 0, fmt.Errorf("Invalid splitting policy, needs to be Evenly got %s", splittingPolicy)
}

// check that all the internalGaugeIds exist
internalGauges, err := k.GetGaugeFromIDs(ctx, internalGaugeIds)
if err != nil {
Expand All @@ -235,7 +243,7 @@ func (k Keeper) CreateGroupGauge(ctx sdk.Context, coins sdk.Coins, numEpochPaidO
Id: nextGaugeId,
IsPerpetual: numEpochPaidOver == 1,
DistributeTo: lockuptypes.QueryCondition{
LockQueryType: lockuptypes.ByGroup,
LockQueryType: gaugetype,
},
Coins: coins,
StartTime: ctx.BlockTime(),
Expand All @@ -253,13 +261,13 @@ func (k Keeper) CreateGroupGauge(ctx sdk.Context, coins sdk.Coins, numEpochPaidO
newGroupGauge := types.GroupGauge{
GroupGaugeId: nextGaugeId,
InternalIds: internalGaugeIds,
SplittingPolicy: types.Evenly,
SplittingPolicy: splittingPolicy,
}

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

// TODO: check if this is necessary
// TODO: check if this is necessary, will investigate this in following PR.
combinedKeys := combineKeys(types.KeyPrefixUpcomingGauges, getTimeKey(gauge.StartTime))
activeOrUpcomingGauge := true

Expand Down
34 changes: 32 additions & 2 deletions x/incentives/keeper/gauge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,13 +590,17 @@ func (s *KeeperTestSuite) TestCreateGroupGauge() {
coins sdk.Coins
numEpochPaidOver uint64
internalGaugeIds []uint64
gaugeType lockuptypes.LockQueryType
splittiingPolicy types.SplittingPolicy
expectErr bool
}{
{
name: "Happy case: created valid gauge",
coins: coinsToAdd,
numEpochPaidOver: 1,
internalGaugeIds: []uint64{2, 3, 4},
gaugeType: lockuptypes.ByGroup,
splittiingPolicy: types.Evenly,
expectErr: false,
},

Expand All @@ -605,27 +609,53 @@ func (s *KeeperTestSuite) TestCreateGroupGauge() {
coins: coinsToAdd,
numEpochPaidOver: 1,
internalGaugeIds: []uint64{2, 3, 4, 5},
gaugeType: lockuptypes.ByGroup,
splittiingPolicy: types.Evenly,
expectErr: true,
},
{
name: "Error: owner doesnot have enough funds",
coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))),
numEpochPaidOver: 1,
internalGaugeIds: []uint64{2, 3, 4},
gaugeType: lockuptypes.ByGroup,
splittiingPolicy: types.Evenly,
expectErr: true,
},
{
name: "Error: One of the internal Gauge is non-perp",
coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))),
numEpochPaidOver: 1,
internalGaugeIds: []uint64{2, 3, 4, 5},
gaugeType: lockuptypes.ByGroup,
splittiingPolicy: types.Evenly,
expectErr: true,
},
{
name: "Error: No InternalGaugeIds provided",
coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))),
numEpochPaidOver: 1,
internalGaugeIds: []uint64{},
splittiingPolicy: types.Evenly,
gaugeType: lockuptypes.ByGroup,
expectErr: true,
},
{
name: "Error: Invalid Splitting Policy",
coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))),
numEpochPaidOver: 1,
internalGaugeIds: []uint64{},
gaugeType: lockuptypes.ByGroup,
splittiingPolicy: types.Volume,
expectErr: true,
},
{
name: "Error: Invalid gauge type",
coins: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(200_000_000))),
numEpochPaidOver: 1,
internalGaugeIds: []uint64{},
gaugeType: lockuptypes.NoLock,
splittiingPolicy: types.Evenly,
expectErr: true,
},
}
Expand All @@ -644,7 +674,7 @@ func (s *KeeperTestSuite) TestCreateGroupGauge() {
//create 1 non-perp internal Gauge
s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(2)) // gauge id = 5

groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.coins, tc.numEpochPaidOver, s.TestAccs[1], tc.internalGaugeIds) // gauge id = 6
groupGaugeId, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, tc.coins, tc.numEpochPaidOver, s.TestAccs[1], tc.internalGaugeIds, tc.gaugeType, tc.splittiingPolicy) // gauge id = 6
if tc.expectErr {
s.Require().Error(err)
} else {
Expand Down Expand Up @@ -719,7 +749,7 @@ func (s *KeeperTestSuite) TestAddToGaugeRewardsFromGauge() {
internalGauge1 := s.CreateNoLockExternalGauges(clPool.GetId(), sdk.NewCoins(), s.TestAccs[1], uint64(1)) // gauge id = 2

// create group gauge
_, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], []uint64{internalGauge1}) // gauge id = 3
_, err := s.App.IncentivesKeeper.CreateGroupGauge(s.Ctx, sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(100_000_000))), uint64(1), s.TestAccs[1], []uint64{internalGauge1}, lockuptypes.ByGroup, types.Evenly) // gauge id = 3
s.Require().NoError(err)

err = s.App.IncentivesKeeper.AddToGaugeRewardsFromGauge(s.Ctx, tc.groupGaugeId, tc.coinsToTransfer, tc.internalGaugeId)
Expand Down
26 changes: 26 additions & 0 deletions x/incentives/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}

func (s *KeeperTestSuite) SetupGroupGauge(clPoolId uint64, lockOwner sdk.AccAddress, numOfNoLockGauges uint64, numOfLockGauges uint64) []uint64 {
internalGauges := s.setupNoLockInternalGauge(clPoolId, numOfNoLockGauges)

for i := uint64(1); i <= numOfLockGauges; i++ {
// setup lock
s.LockTokens(lockOwner, sdk.Coins{sdk.NewInt64Coin("lptoken", 10)}, time.Hour*7)

// create gauge
gaugeID, _, _, _ := s.SetupNewGauge(true, sdk.NewCoins())
internalGauges = append(internalGauges, gaugeID)
}

return internalGauges
}

// setupNoLockInternalGauge create no lock perp internal gauges.
func (s *KeeperTestSuite) setupNoLockInternalGauge(poolId uint64, numberOfExistingGauges uint64) []uint64 {
var internalGauges []uint64
for i := uint64(1); i <= numberOfExistingGauges; i++ {
internalGauge := s.CreateNoLockExternalGauges(poolId, sdk.NewCoins(), s.TestAccs[1], uint64(1))
internalGauges = append(internalGauges, internalGauge)
}

return internalGauges
}

// ValidateDistributedGauge checks that the gauge is updated as expected after distribution
func (s *KeeperTestSuite) ValidateDistributedGauge(gaugeID uint64, expectedFilledEpoch uint64, expectedDistributions sdk.Coins) {
// Check that filled epcohs is not updated
Expand Down
Loading

0 comments on commit c99cb1d

Please sign in to comment.