Update delegator rewards to multi-reward index (#945)

* update claim attribute type to MultiRewardIndexes

* update param attribute type to MultiRewardPeriods

* keeper: update params to match types

* keeper: update delegator core keeper methods

* keeper: update InitializeHardDelegatorReward

* keeper: update SynchronizeHardDelegatorRewards

* remove reward factor in favor of reward indexes

* update querier

* fix test: delegator init test

* fix test: delegator sync test

* implement delegator reward accumulation

* fix test: delegator general tests

* add legact types, update v0_11 -> v0_14 migration

* remove duplicate import form v0_15 migration

* implement v0_15incentive migration

* test data and migration test

* add multiple reward denoms to init/sync tests

* update delegator test with multiple reward coins

* clean up simulation sync
This commit is contained in:
Denali Marsh 2021-07-06 00:01:25 +02:00 committed by GitHub
parent baf17b4ec8
commit bc33b94822
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1533 additions and 245 deletions

View File

@ -23,8 +23,8 @@ import (
v0_14committee "github.com/kava-labs/kava/x/committee/legacy/v0_14"
v0_14hard "github.com/kava-labs/kava/x/hard"
v0_11hard "github.com/kava-labs/kava/x/hard/legacy/v0_11"
v0_14incentive "github.com/kava-labs/kava/x/incentive"
v0_11incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_11"
v0_14incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_14"
"github.com/kava-labs/kava/x/kavadist"
v0_11pricefeed "github.com/kava-labs/kava/x/pricefeed"
v0_14pricefeed "github.com/kava-labs/kava/x/pricefeed"

View File

@ -24,8 +24,8 @@ import (
v0_14committee "github.com/kava-labs/kava/x/committee/legacy/v0_14"
v0_14hard "github.com/kava-labs/kava/x/hard"
v0_11hard "github.com/kava-labs/kava/x/hard/legacy/v0_11"
v0_14incentive "github.com/kava-labs/kava/x/incentive"
v0_11incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_11"
v0_14incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_14"
v0_11pricefeed "github.com/kava-labs/kava/x/pricefeed"
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"

View File

@ -12,13 +12,16 @@ import (
"github.com/kava-labs/kava/app"
v0_14committee "github.com/kava-labs/kava/x/committee/legacy/v0_14"
"github.com/kava-labs/kava/x/committee/types"
v0_15committee "github.com/kava-labs/kava/x/committee/types"
v0_14incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_14"
v0_15incentive "github.com/kava-labs/kava/x/incentive/types"
)
var (
// TODO: update GenesisTime for kava-8 launch
GenesisTime = time.Date(2021, 4, 8, 15, 0, 0, 0, time.UTC)
// TODO: update SWP reward per second amount before production
SwpRewardsPerSecond = sdk.NewCoin("swp", sdk.OneInt())
)
// Migrate translates a genesis file from kava v0.14 format to kava v0.15 format
@ -47,6 +50,15 @@ func Migrate(genDoc tmtypes.GenesisDoc) tmtypes.GenesisDoc {
// MigrateAppState migrates application state from v0.14 format to a kava v0.15 format
func MigrateAppState(v0_14AppState genutil.AppMap) genutil.AppMap {
v0_15AppState := v0_14AppState
cdc := app.MakeCodec()
// Migrate incentive app state
if v0_14AppState[v0_14incentive.ModuleName] != nil {
var incentiveGenState v0_14incentive.GenesisState
cdc.MustUnmarshalJSON(v0_14AppState[v0_15incentive.ModuleName], &incentiveGenState)
delete(v0_14AppState, v0_14incentive.ModuleName)
v0_15AppState[v0_15incentive.ModuleName] = cdc.MustMarshalJSON(Incentive(incentiveGenState))
}
// Migrate commmittee app state
if v0_14AppState[v0_14committee.ModuleName] != nil {
@ -75,7 +87,7 @@ func Committee(genesisState v0_14committee.GenesisState) v0_15committee.GenesisS
for _, com := range genesisState.Committees {
if com.ID == 1 {
// Initialize member committee without permissions
stabilityCom := types.NewMemberCommittee(com.ID, com.Description, com.Members,
stabilityCom := v0_15committee.NewMemberCommittee(com.ID, com.Description, com.Members,
[]v0_15committee.Permission{}, com.VoteThreshold, com.ProposalDuration,
v0_15committee.FirstPastThePost)
@ -171,7 +183,7 @@ func Committee(genesisState v0_14committee.GenesisState) v0_15committee.GenesisS
newStabilityCom := v0_15committee.MemberCommittee{BaseCommittee: baseStabilityCom}
committees = append(committees, newStabilityCom)
} else {
safetyCom := types.NewMemberCommittee(com.ID, com.Description, com.Members,
safetyCom := v0_15committee.NewMemberCommittee(com.ID, com.Description, com.Members,
[]v0_15committee.Permission{v0_15committee.SoftwareUpgradePermission{}},
com.VoteThreshold, com.ProposalDuration, v0_15committee.FirstPastThePost)
committees = append(committees, safetyCom)
@ -179,7 +191,7 @@ func Committee(genesisState v0_14committee.GenesisState) v0_15committee.GenesisS
}
for _, v := range genesisState.Votes {
newVote := v0_15committee.NewVote(v.ProposalID, v.Voter, types.Yes)
newVote := v0_15committee.NewVote(v.ProposalID, v.Voter, v0_15committee.Yes)
votes = append(votes, v0_15committee.Vote(newVote))
}
@ -191,3 +203,141 @@ func Committee(genesisState v0_14committee.GenesisState) v0_15committee.GenesisS
return v0_15committee.NewGenesisState(
genesisState.NextProposalID, committees, proposals, votes)
}
// Incentive migrates from a v0.14 incentive genesis state to a v0.15 incentive genesis state
func Incentive(incentiveGS v0_14incentive.GenesisState) v0_15incentive.GenesisState {
// Migrate params
var claimMultipliers v0_15incentive.Multipliers
for _, m := range incentiveGS.Params.ClaimMultipliers {
newMultiplier := v0_15incentive.NewMultiplier(v0_15incentive.MultiplierName(m.Name), m.MonthsLockup, m.Factor)
claimMultipliers = append(claimMultipliers, newMultiplier)
}
var usdxMintingRewardPeriods v0_15incentive.RewardPeriods
for _, rp := range incentiveGS.Params.USDXMintingRewardPeriods {
usdxMintingRewardPeriod := v0_15incentive.NewRewardPeriod(rp.Active,
rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
usdxMintingRewardPeriods = append(usdxMintingRewardPeriods, usdxMintingRewardPeriod)
}
var hardSupplyRewardPeriods v0_15incentive.MultiRewardPeriods
for _, rp := range incentiveGS.Params.HardSupplyRewardPeriods {
hardSupplyRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
hardSupplyRewardPeriods = append(hardSupplyRewardPeriods, hardSupplyRewardPeriod)
}
var hardBorrowRewardPeriods v0_15incentive.MultiRewardPeriods
for _, rp := range incentiveGS.Params.HardBorrowRewardPeriods {
hardBorrowRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
hardBorrowRewardPeriods = append(hardBorrowRewardPeriods, hardBorrowRewardPeriod)
}
var hardDelegatorRewardPeriods v0_15incentive.MultiRewardPeriods
for _, rp := range incentiveGS.Params.HardDelegatorRewardPeriods {
rewardsPerSecond := sdk.NewCoins(rp.RewardsPerSecond, SwpRewardsPerSecond)
hardDelegatorRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
rp.CollateralType, rp.Start, rp.End, rewardsPerSecond)
hardDelegatorRewardPeriods = append(hardDelegatorRewardPeriods, hardDelegatorRewardPeriod)
}
// Build new params from migrated values
params := v0_15incentive.NewParams(
usdxMintingRewardPeriods,
hardSupplyRewardPeriods,
hardBorrowRewardPeriods,
hardDelegatorRewardPeriods,
claimMultipliers,
incentiveGS.Params.ClaimEnd,
)
// Migrate accumulation times
var usdxAccumulationTimes v0_15incentive.GenesisAccumulationTimes
for _, t := range incentiveGS.USDXAccumulationTimes {
newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
usdxAccumulationTimes = append(usdxAccumulationTimes, newAccumulationTime)
}
var hardSupplyAccumulationTimes v0_15incentive.GenesisAccumulationTimes
for _, t := range incentiveGS.HardSupplyAccumulationTimes {
newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
hardSupplyAccumulationTimes = append(hardSupplyAccumulationTimes, newAccumulationTime)
}
var hardBorrowAccumulationTimes v0_15incentive.GenesisAccumulationTimes
for _, t := range incentiveGS.HardBorrowAccumulationTimes {
newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
hardBorrowAccumulationTimes = append(hardBorrowAccumulationTimes, newAccumulationTime)
}
var hardDelegatorAccumulationTimes v0_15incentive.GenesisAccumulationTimes
for _, t := range incentiveGS.HardDelegatorAccumulationTimes {
newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
hardDelegatorAccumulationTimes = append(hardDelegatorAccumulationTimes, newAccumulationTime)
}
// Migrate USDX minting claims
var usdxMintingClaims v0_15incentive.USDXMintingClaims
for _, claim := range incentiveGS.USDXMintingClaims {
var rewardIndexes v0_15incentive.RewardIndexes
for _, ri := range claim.RewardIndexes {
rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
rewardIndexes = append(rewardIndexes, rewardIndex)
}
usdxMintingClaim := v0_15incentive.NewUSDXMintingClaim(claim.Owner, claim.Reward, rewardIndexes)
usdxMintingClaims = append(usdxMintingClaims, usdxMintingClaim)
}
// Migrate Hard protocol claims (including delegation rewards)
var hardClaims v0_15incentive.HardLiquidityProviderClaims
for _, claim := range incentiveGS.HardLiquidityProviderClaims {
// Migrate supply multi reward indexes
var supplyMultiRewardIndexes v0_15incentive.MultiRewardIndexes
for _, sri := range claim.SupplyRewardIndexes {
var rewardIndexes v0_15incentive.RewardIndexes
for _, ri := range sri.RewardIndexes {
rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
rewardIndexes = append(rewardIndexes, rewardIndex)
}
supplyMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(sri.CollateralType, rewardIndexes)
supplyMultiRewardIndexes = append(supplyMultiRewardIndexes, supplyMultiRewardIndex)
}
// Migrate borrow multi reward indexes
var borrowMultiRewardIndexes v0_15incentive.MultiRewardIndexes
for _, bri := range claim.BorrowRewardIndexes {
var rewardIndexes v0_15incentive.RewardIndexes
for _, ri := range bri.RewardIndexes {
rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
rewardIndexes = append(rewardIndexes, rewardIndex)
}
borrowMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(bri.CollateralType, rewardIndexes)
borrowMultiRewardIndexes = append(borrowMultiRewardIndexes, borrowMultiRewardIndex)
}
// Migrate delegator reward indexes to multi reward indexes under BondDenom
var delegatorMultiRewardIndexes v0_15incentive.MultiRewardIndexes
var delegatorRewardIndexes v0_15incentive.RewardIndexes
for _, ri := range claim.DelegatorRewardIndexes {
delegatorRewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
delegatorRewardIndexes = append(delegatorRewardIndexes, delegatorRewardIndex)
}
delegatorMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(v0_15incentive.BondDenom, delegatorRewardIndexes)
delegatorMultiRewardIndexes = append(delegatorMultiRewardIndexes, delegatorMultiRewardIndex)
hardClaim := v0_15incentive.NewHardLiquidityProviderClaim(claim.Owner, claim.Reward,
supplyMultiRewardIndexes, borrowMultiRewardIndexes, delegatorMultiRewardIndexes)
hardClaims = append(hardClaims, hardClaim)
}
return v0_15incentive.NewGenesisState(
params,
usdxAccumulationTimes,
hardSupplyAccumulationTimes,
hardBorrowAccumulationTimes,
hardDelegatorAccumulationTimes,
usdxMintingClaims,
hardClaims,
)
}

View File

@ -12,7 +12,8 @@ import (
"github.com/kava-labs/kava/app"
v0_14committee "github.com/kava-labs/kava/x/committee/legacy/v0_14"
v0_15committee "github.com/kava-labs/kava/x/committee/types"
v0_14incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_14"
v0_15incentive "github.com/kava-labs/kava/x/incentive/types"
"github.com/stretchr/testify/require"
)
@ -54,3 +55,23 @@ func TestCommittee(t *testing.T) {
require.Equal(t, len(oldSPCP.AllowedMarkets), len(newSPCP.AllowedMarkets))
require.Equal(t, len(oldSPCP.AllowedMoneyMarkets), len(newSPCP.AllowedMoneyMarkets))
}
func TestIncentive(t *testing.T) {
bz, err := ioutil.ReadFile(filepath.Join("testdata", "kava-7-incentive-state.json"))
require.NoError(t, err)
var oldIncentiveGenState v0_14incentive.GenesisState
cdc := app.MakeCodec()
require.NotPanics(t, func() {
cdc.MustUnmarshalJSON(bz, &oldIncentiveGenState)
})
newGenState := v0_15incentive.GenesisState{}
require.NotPanics(t, func() {
newGenState = Incentive(oldIncentiveGenState)
})
err = newGenState.Validate()
require.NoError(t, err)
require.Equal(t, len(oldIncentiveGenState.USDXMintingClaims), len(newGenState.USDXMintingClaims))
require.Equal(t, len(oldIncentiveGenState.HardLiquidityProviderClaims), len(newGenState.HardLiquidityProviderClaims))
}

File diff suppressed because one or more lines are too long

View File

@ -104,7 +104,7 @@ var (
PreviousHardSupplyRewardAccrualTimeKeyPrefix = types.PreviousHardSupplyRewardAccrualTimeKeyPrefix
HardBorrowRewardIndexesKeyPrefix = types.HardBorrowRewardIndexesKeyPrefix
PreviousHardBorrowRewardAccrualTimeKeyPrefix = types.PreviousHardBorrowRewardAccrualTimeKeyPrefix
HardDelegatorRewardFactorKeyPrefix = types.HardDelegatorRewardFactorKeyPrefix
HardDelegatorRewardIndexesKeyPrefix = types.HardDelegatorRewardIndexesKeyPrefix
PreviousHardDelegatorRewardAccrualTimeKeyPrefix = types.PreviousHardDelegatorRewardAccrualTimeKeyPrefix
USDXMintingRewardDenom = types.USDXMintingRewardDenom
HardLiquidityRewardDenom = types.HardLiquidityRewardDenom

View File

@ -48,8 +48,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
k.SetHardBorrowRewardIndexes(ctx, mrp.CollateralType, newRewardIndexes)
}
for _, rp := range gs.Params.HardDelegatorRewardPeriods {
k.SetHardDelegatorRewardFactor(ctx, rp.CollateralType, sdk.ZeroDec())
for _, drp := range gs.Params.HardDelegatorRewardPeriods {
newRewardIndexes := types.RewardIndexes{}
for _, rc := range drp.RewardsPerSecond {
ri := types.NewRewardIndex(rc.Denom, sdk.ZeroDec())
newRewardIndexes = append(newRewardIndexes, ri)
}
k.SetHardDelegatorRewardIndexes(ctx, drp.CollateralType, newRewardIndexes)
}
k.SetParams(ctx, gs.Params)
@ -94,9 +99,11 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
}
}
}
for j, ri := range claim.DelegatorRewardIndexes {
if ri.RewardFactor != sdk.ZeroDec() {
gs.HardLiquidityProviderClaims[i].DelegatorRewardIndexes[j].RewardFactor = sdk.ZeroDec()
for j, dri := range claim.DelegatorRewardIndexes {
for k, ri := range dri.RewardIndexes {
if ri.RewardFactor != sdk.ZeroDec() {
gs.HardLiquidityProviderClaims[i].DelegatorRewardIndexes[j].RewardIndexes[k].RewardFactor = sdk.ZeroDec()
}
}
}
k.SetHardLiquidityProviderClaim(ctx, claim)
@ -140,8 +147,10 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
claim.SupplyRewardIndexes[i].RewardIndexes[j].RewardFactor = sdk.ZeroDec()
}
}
for i := range claim.DelegatorRewardIndexes {
claim.DelegatorRewardIndexes[i].RewardFactor = sdk.ZeroDec()
for i, dri := range claim.DelegatorRewardIndexes {
for j := range dri.RewardIndexes {
claim.DelegatorRewardIndexes[i].RewardIndexes[j].RewardFactor = sdk.ZeroDec()
}
}
synchronizedHardClaims = append(synchronizedHardClaims, claim)
}

View File

@ -64,8 +64,7 @@ func (suite *GenesisTestSuite) SetupTest() {
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), c("ukava", 122354))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "ukava", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), c("hard", 122354))},
incentive.Multipliers{incentive.NewMultiplier(incentive.Small, 1, d("0.25")), incentive.NewMultiplier(incentive.Large, 12, d("1.0"))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "ukava", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))}, incentive.Multipliers{incentive.NewMultiplier(incentive.Small, 1, d("0.25")), incentive.NewMultiplier(incentive.Large, 12, d("1.0"))},
suite.genesisTime.Add(5*oneYear),
),
incentive.DefaultGenesisAccumulationTimes,

View File

@ -47,7 +47,7 @@ func (suite *HandlerTestSuite) SetupTest() {
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
incentive.Multipliers{incentive.NewMultiplier(incentive.MultiplierName("small"), 1, d("0.25")), incentive.NewMultiplier(incentive.MultiplierName("large"), 12, d("1.0"))},
time.Date(2025, 12, 15, 14, 0, 0, 0, time.UTC),
),
@ -88,10 +88,9 @@ func (suite *HandlerTestSuite) addHardLiquidityProviderClaim() {
err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000)))
suite.Require().NoError(err)
rewardPeriod := types.RewardIndexes{types.NewRewardIndex("bnb-s", sdk.ZeroDec())}
multiRewardIndex := types.NewMultiRewardIndex("bnb-s", rewardPeriod)
multiRewardIndexes := types.MultiRewardIndexes{multiRewardIndex}
c1 := incentive.NewHardLiquidityProviderClaim(suite.addrs[0], cs(c("ukava", 1000000)), multiRewardIndexes, multiRewardIndexes, rewardPeriod)
c1 := incentive.NewHardLiquidityProviderClaim(suite.addrs[0], cs(c("ukava", 1000000)), multiRewardIndexes, multiRewardIndexes, multiRewardIndexes)
suite.NotPanics(func() {
suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, c1)
})

View File

@ -157,7 +157,7 @@ func NewIncentiveGenState(previousAccumTime, endTime time.Time, rewardPeriods ..
rewardPeriods,
types.MultiRewardPeriods{},
types.MultiRewardPeriods{},
types.RewardPeriods{},
types.MultiRewardPeriods{},
incentive.Multipliers{
incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
incentive.NewMultiplier(incentive.Large, 12, d("1.0")),

View File

@ -278,7 +278,7 @@ func (builder IncentiveGenesisBuilder) WithSimpleSupplyRewardPeriod(ctype string
rewardsPerSecond,
))
}
func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(period types.RewardPeriod) IncentiveGenesisBuilder {
func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
builder.Params.HardDelegatorRewardPeriods = append(builder.Params.HardDelegatorRewardPeriods, period)
accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
@ -286,8 +286,8 @@ func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(peri
return builder
}
func (builder IncentiveGenesisBuilder) WithSimpleDelegatorRewardPeriod(ctype string, rewardsPerSecond sdk.Coin) IncentiveGenesisBuilder {
return builder.WithInitializedDelegatorRewardPeriod(types.NewRewardPeriod(
func (builder IncentiveGenesisBuilder) WithSimpleDelegatorRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
return builder.WithInitializedDelegatorRewardPeriod(types.NewMultiRewardPeriod(
true,
ctype,
builder.genesisTime,

View File

@ -273,32 +273,34 @@ func (k Keeper) IterateHardBorrowRewardIndexes(ctx sdk.Context, cb func(denom st
}
}
// GetHardDelegatorRewardFactor returns the current reward factor for an individual collateral type
func (k Keeper) GetHardDelegatorRewardFactor(ctx sdk.Context, ctype string) (factor sdk.Dec, found bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardFactorKeyPrefix)
bz := store.Get([]byte(ctype))
// GetHardDelegatorRewardIndexes gets the current reward indexes for an individual denom
func (k Keeper) GetHardDelegatorRewardIndexes(ctx sdk.Context, denom string) (types.RewardIndexes, bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardIndexesKeyPrefix)
bz := store.Get([]byte(denom))
if bz == nil {
return sdk.ZeroDec(), false
return types.RewardIndexes{}, false
}
k.cdc.MustUnmarshalBinaryBare(bz, &factor)
return factor, true
var rewardIndexes types.RewardIndexes
k.cdc.MustUnmarshalBinaryBare(bz, &rewardIndexes)
return rewardIndexes, true
}
// SetHardDelegatorRewardFactor sets the current reward factor for an individual collateral type
func (k Keeper) SetHardDelegatorRewardFactor(ctx sdk.Context, ctype string, factor sdk.Dec) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardFactorKeyPrefix)
store.Set([]byte(ctype), k.cdc.MustMarshalBinaryBare(factor))
// SetHardDelegatorRewardIndexes sets the current reward indexes for an individual denom
func (k Keeper) SetHardDelegatorRewardIndexes(ctx sdk.Context, denom string, indexes types.RewardIndexes) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardIndexesKeyPrefix)
bz := k.cdc.MustMarshalBinaryBare(indexes)
store.Set([]byte(denom), bz)
}
// IterateHardDelegatorRewardFactors iterates over all Hard delegator reward factor objects in the store and preforms a callback function
func (k Keeper) IterateHardDelegatorRewardFactors(ctx sdk.Context, cb func(denom string, factor sdk.Dec) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardFactorKeyPrefix)
// IterateHardDelegatorRewardIndexes iterates over all Delegator reward index objects in the store and preforms a callback function
func (k Keeper) IterateHardDelegatorRewardIndexes(ctx sdk.Context, cb func(denom string, indexes types.RewardIndexes) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardIndexesKeyPrefix)
iterator := sdk.KVStorePrefixIterator(store, []byte{})
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var factor sdk.Dec
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &factor)
if cb(string(iterator.Key()), factor) {
var indexes types.RewardIndexes
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &indexes)
if cb(string(iterator.Key()), indexes) {
break
}
}

View File

@ -54,14 +54,14 @@ func (k Keeper) GetHardBorrowRewardPeriods(ctx sdk.Context, denom string) (types
}
// GetHardDelegatorRewardPeriod returns the reward period with the specified collateral type if it's found in the params
func (k Keeper) GetHardDelegatorRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
func (k Keeper) GetHardDelegatorRewardPeriods(ctx sdk.Context, denom string) (types.MultiRewardPeriod, bool) {
params := k.GetParams(ctx)
for _, rp := range params.HardDelegatorRewardPeriods {
if rp.CollateralType == denom {
return rp, true
}
}
return types.RewardPeriod{}, false
return types.MultiRewardPeriod{}, false
}
// GetMultiplier returns the multiplier with the specified name if it's found in the params

View File

@ -223,9 +223,9 @@ func queryGetRewardFactors(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]
if found {
rewardFactor.HardBorrowRewardFactors = hardBorrowRewardIndexes
}
hardDelegatorRewardFactor, found := k.GetHardDelegatorRewardFactor(ctx, params.Denom)
hardDelegatorRewardIndexes, found := k.GetHardDelegatorRewardIndexes(ctx, params.Denom)
if found {
rewardFactor.HardDelegatorRewardFactor = hardDelegatorRewardFactor
rewardFactor.HardDelegatorRewardFactors = hardDelegatorRewardIndexes
}
rewardFactors = append(rewardFactors, rewardFactor)
} else {
@ -263,12 +263,12 @@ func queryGetRewardFactors(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]
})
// Populate mapping with Hard delegator reward factors
k.IterateHardDelegatorRewardFactors(ctx, func(denom string, factor sdk.Dec) (stop bool) {
k.IterateHardDelegatorRewardIndexes(ctx, func(denom string, indexes types.RewardIndexes) (stop bool) {
rewardFactor, ok := rewardFactorMap[denom]
if !ok {
rewardFactor = types.RewardFactor{Denom: denom, HardDelegatorRewardFactor: factor}
rewardFactor = types.RewardFactor{Denom: denom, HardDelegatorRewardFactors: indexes}
} else {
rewardFactor.HardDelegatorRewardFactor = factor
rewardFactor.HardDelegatorRewardFactors = indexes
}
rewardFactorMap[denom] = rewardFactor
return false

View File

@ -9,37 +9,57 @@ import (
)
// AccumulateHardDelegatorRewards updates the rewards accumulated for the input reward period
func (k Keeper) AccumulateHardDelegatorRewards(ctx sdk.Context, rewardPeriod types.RewardPeriod) error {
previousAccrualTime, found := k.GetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriod.CollateralType)
func (k Keeper) AccumulateHardDelegatorRewards(ctx sdk.Context, rewardPeriods types.MultiRewardPeriod) error {
previousAccrualTime, found := k.GetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriods.CollateralType)
if !found {
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriods.CollateralType, ctx.BlockTime())
return nil
}
timeElapsed := CalculateTimeElapsed(rewardPeriod.Start, rewardPeriod.End, ctx.BlockTime(), previousAccrualTime)
timeElapsed := CalculateTimeElapsed(rewardPeriods.Start, rewardPeriods.End, ctx.BlockTime(), previousAccrualTime)
if timeElapsed.IsZero() {
return nil
}
if rewardPeriod.RewardsPerSecond.Amount.IsZero() {
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
if rewardPeriods.RewardsPerSecond.IsZero() {
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriods.CollateralType, ctx.BlockTime())
return nil
}
totalBonded := k.stakingKeeper.TotalBondedTokens(ctx).ToDec()
if totalBonded.IsZero() {
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriods.CollateralType, ctx.BlockTime())
return nil
}
newRewards := timeElapsed.Mul(rewardPeriod.RewardsPerSecond.Amount)
rewardFactor := newRewards.ToDec().Quo(totalBonded)
previousRewardFactor, found := k.GetHardDelegatorRewardFactor(ctx, rewardPeriod.CollateralType)
previousRewardIndexes, found := k.GetHardDelegatorRewardIndexes(ctx, rewardPeriods.CollateralType)
if !found {
previousRewardFactor = sdk.ZeroDec()
for _, rewardCoin := range rewardPeriods.RewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
previousRewardIndexes = append(previousRewardIndexes, rewardIndex)
}
k.SetHardDelegatorRewardIndexes(ctx, rewardPeriods.CollateralType, previousRewardIndexes)
}
newRewardFactor := previousRewardFactor.Add(rewardFactor)
k.SetHardDelegatorRewardFactor(ctx, rewardPeriod.CollateralType, newRewardFactor)
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
newRewardIndexes := previousRewardIndexes
for _, rewardCoin := range rewardPeriods.RewardsPerSecond {
newRewards := rewardCoin.Amount.ToDec().Mul(timeElapsed.ToDec())
previousRewardIndex, found := previousRewardIndexes.GetRewardIndex(rewardCoin.Denom)
if !found {
previousRewardIndex = types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
}
// Calculate new reward factor and update reward index
rewardFactor := newRewards.Quo(totalBonded)
newRewardFactorValue := previousRewardIndex.RewardFactor.Add(rewardFactor)
newRewardIndex := types.NewRewardIndex(rewardCoin.Denom, newRewardFactorValue)
i, found := newRewardIndexes.GetFactorIndex(rewardCoin.Denom)
if found {
newRewardIndexes[i] = newRewardIndex
} else {
newRewardIndexes = append(newRewardIndexes, newRewardIndex)
}
}
k.SetHardDelegatorRewardIndexes(ctx, rewardPeriods.CollateralType, newRewardIndexes)
k.SetPreviousHardDelegatorRewardAccrualTime(ctx, rewardPeriods.CollateralType, ctx.BlockTime())
return nil
}
@ -53,13 +73,14 @@ func (k Keeper) InitializeHardDelegatorReward(ctx sdk.Context, delegator sdk.Acc
claim, _ = k.GetHardLiquidityProviderClaim(ctx, delegator)
}
globalRewardFactor, found := k.GetHardDelegatorRewardFactor(ctx, types.BondDenom)
var delegatorRewardIndexes types.MultiRewardIndexes
globalRewardIndexes, found := k.GetHardDelegatorRewardIndexes(ctx, types.BondDenom)
if !found {
// If there's no global delegator reward factor, initialize the claim with a delegator factor of zero.
globalRewardFactor = sdk.ZeroDec()
globalRewardIndexes = types.RewardIndexes{}
}
delegatorRewardIndexes = delegatorRewardIndexes.With(types.BondDenom, globalRewardIndexes)
claim.DelegatorRewardIndexes = types.RewardIndexes{types.NewRewardIndex(types.BondDenom, globalRewardFactor)}
claim.DelegatorRewardIndexes = delegatorRewardIndexes
k.SetHardLiquidityProviderClaim(ctx, claim)
}
@ -72,7 +93,7 @@ func (k Keeper) SynchronizeHardDelegatorRewards(ctx sdk.Context, delegator sdk.A
return
}
globalRewardFactor, found := k.GetHardDelegatorRewardFactor(ctx, types.BondDenom)
globalRewardIndexes, found := k.GetHardDelegatorRewardIndexes(ctx, types.BondDenom)
if !found {
// The global factor is only not found if
// - the bond denom has not started accumulating rewards yet (either there is no reward specified in params, or the reward start time hasn't been hit)
@ -83,27 +104,25 @@ func (k Keeper) SynchronizeHardDelegatorRewards(ctx sdk.Context, delegator sdk.A
return
}
userRewardFactor, found := claim.DelegatorRewardIndexes.Get(types.BondDenom)
userRewardIndexes, found := claim.DelegatorRewardIndexes.Get(types.BondDenom)
if !found {
// Normally the factor should always be found, as it is added in InitializeHardDelegatorReward when a user delegates.
// However if there were no delegator rewards (ie no reward period in params) then a reward period is added, existing claims will not have the factor.
// So assume the factor is the starting value for any global factor: 0.
userRewardFactor = sdk.ZeroDec()
userRewardIndexes = types.RewardIndexes{}
}
totalDelegated := k.GetTotalDelegated(ctx, delegator, valAddr, shouldIncludeValidator)
rewardsEarned, err := k.CalculateSingleReward(userRewardFactor, globalRewardFactor, totalDelegated)
rewardsEarned, err := k.CalculateRewards(userRewardIndexes, globalRewardIndexes, totalDelegated)
if err != nil {
// Global reward factors should never decrease, as it would lead to a negative update to claim.Rewards.
// This panics if a global reward factor decreases or disappears between the old and new indexes.
panic(fmt.Sprintf("corrupted global reward indexes found: %v", err))
}
newRewardsCoin := sdk.NewCoin(types.HardLiquidityRewardDenom, rewardsEarned)
claim.Reward = claim.Reward.Add(newRewardsCoin)
claim.DelegatorRewardIndexes = claim.DelegatorRewardIndexes.With(types.BondDenom, globalRewardFactor)
claim.Reward = claim.Reward.Add(rewardsEarned...)
claim.DelegatorRewardIndexes = claim.DelegatorRewardIndexes.With(types.BondDenom, globalRewardIndexes)
k.SetHardLiquidityProviderClaim(ctx, claim)
}

View File

@ -27,9 +27,10 @@ func TestInitializeHardDelegatorReward(t *testing.T) {
suite.Run(t, new(InitializeHardDelegatorRewardTests))
}
func (suite *InitializeHardDelegatorRewardTests) storeGlobalDelegatorFactor(rewardIndexes types.RewardIndexes) {
factor := rewardIndexes[0]
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, factor.CollateralType, factor.RewardFactor)
// Hardcoded to use bond denom
func (suite *InitializeHardDelegatorRewardTests) storeGlobalDelegatorFactor(multiRewardIndexes types.MultiRewardIndexes) {
multiRewardIndex, _ := multiRewardIndexes.GetRewardIndex(types.BondDenom)
suite.keeper.SetHardDelegatorRewardIndexes(suite.ctx, types.BondDenom, multiRewardIndex.RewardIndexes)
}
func (suite *InitializeHardDelegatorRewardTests) TestClaimIndexesAreSetWhenClaimDoesNotExist() {
@ -70,19 +71,30 @@ func (suite *InitializeHardDelegatorRewardTests) TestClaimIsSyncedAndIndexesAreS
// Set the global factor to a value different to one in claim so
// we can detect if it is overwritten.
globalIndex := increaseRewardFactors(claim.DelegatorRewardIndexes)
suite.storeGlobalDelegatorFactor(globalIndex)
rewardIndexes, _ := claim.DelegatorRewardIndexes.Get(types.BondDenom)
globalIndexes := increaseRewardFactors(rewardIndexes)
// Update the claim object with the new global factor
bondIndex, _ := claim.DelegatorRewardIndexes.GetRewardIndexIndex(types.BondDenom)
claim.DelegatorRewardIndexes[bondIndex].RewardIndexes = globalIndexes
suite.storeGlobalDelegatorFactor(claim.DelegatorRewardIndexes)
suite.keeper.InitializeHardDelegatorReward(suite.ctx, claim.Owner)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(globalIndex, syncedClaim.DelegatorRewardIndexes)
suite.Equal(globalIndexes, syncedClaim.DelegatorRewardIndexes[bondIndex].RewardIndexes)
suite.Truef(syncedClaim.Reward.IsAllGT(claim.Reward), "'%s' not greater than '%s'", syncedClaim.Reward, claim.Reward)
}
// arbitraryDelegatorRewardIndexes contains only one reward index as there is only every one bond denom
var arbitraryDelegatorRewardIndexes = types.RewardIndexes{
types.NewRewardIndex(types.BondDenom, d("0.2")),
var arbitraryDelegatorRewardIndexes = types.MultiRewardIndexes{
types.NewMultiRewardIndex(
types.BondDenom,
types.RewardIndexes{
types.NewRewardIndex("hard", d("0.2")),
types.NewRewardIndex("swp", d("0.2")),
},
),
}
type fakeStakingKeeper struct {

View File

@ -28,9 +28,9 @@ func TestSynchronizeHardDelegatorReward(t *testing.T) {
suite.Run(t, new(SynchronizeHardDelegatorRewardTests))
}
func (suite *SynchronizeHardDelegatorRewardTests) storeGlobalDelegatorFactor(rewardIndexes types.RewardIndexes) {
factor := rewardIndexes[0]
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, factor.CollateralType, factor.RewardFactor)
func (suite *SynchronizeHardDelegatorRewardTests) storeGlobalDelegatorFactor(multiRewardIndexes types.MultiRewardIndexes) {
multiRewardIndex, _ := multiRewardIndexes.GetRewardIndex(types.BondDenom)
suite.keeper.SetHardDelegatorRewardIndexes(suite.ctx, types.BondDenom, multiRewardIndex.RewardIndexes)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestClaimIndexesAreUnchangedWhenGlobalFactorUnchanged() {
@ -68,14 +68,20 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestClaimIndexesAreUpdatedWhen
}
suite.storeClaim(claim)
globalIndexes := increaseRewardFactors(claim.DelegatorRewardIndexes)
suite.storeGlobalDelegatorFactor(globalIndexes)
rewardIndexes, _ := claim.DelegatorRewardIndexes.Get(types.BondDenom)
globalIndexes := increaseRewardFactors(rewardIndexes)
// Update the claim object with the new global factor
bondIndex, _ := claim.DelegatorRewardIndexes.GetRewardIndexIndex(types.BondDenom)
claim.DelegatorRewardIndexes[bondIndex].RewardIndexes = globalIndexes
suite.storeGlobalDelegatorFactor(claim.DelegatorRewardIndexes)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(globalIndexes, syncedClaim.DelegatorRewardIndexes)
suite.Equal(globalIndexes, syncedClaim.DelegatorRewardIndexes[bondIndex].RewardIndexes)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsUnchangedWhenGlobalFactorUnchanged() {
delegator := arbitraryAddress()
validatorAddress := arbitraryValidatorAddress()
@ -98,9 +104,16 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsUnchangedWhenGloba
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{{
DelegatorRewardIndexes: types.MultiRewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
RewardIndexes: types.RewardIndexes{
{
CollateralType: "hard", RewardFactor: d("0.1"),
},
{
CollateralType: "swp", RewardFactor: d("0.2"),
},
},
}},
}
suite.storeClaim(claim)
@ -136,13 +149,20 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenNewRe
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{},
DelegatorRewardIndexes: types.MultiRewardIndexes{},
}
suite.storeClaim(claim)
newGlobalIndexes := types.RewardIndexes{{
newGlobalIndexes := types.MultiRewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
RewardIndexes: types.RewardIndexes{
{
CollateralType: "hard", RewardFactor: d("0.1"),
},
{
CollateralType: "swp", RewardFactor: d("0.2"),
},
},
}}
suite.storeGlobalDelegatorFactor(newGlobalIndexes)
@ -152,7 +172,10 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenNewRe
suite.Equal(newGlobalIndexes, syncedClaim.DelegatorRewardIndexes)
suite.Equal(
cs(c(types.HardLiquidityRewardDenom, 100)).Add(claim.Reward...),
cs(
c(types.HardLiquidityRewardDenom, 100),
c("swp", 200),
).Add(claim.Reward...),
syncedClaim.Reward,
)
}
@ -179,16 +202,33 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenGloba
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{{
DelegatorRewardIndexes: types.MultiRewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
RewardIndexes: types.RewardIndexes{
{
CollateralType: "hard", RewardFactor: d("0.1"),
},
{
CollateralType: "swp", RewardFactor: d("0.2"),
},
},
}},
}
suite.storeClaim(claim)
suite.storeGlobalDelegatorFactor(
types.RewardIndexes{
types.NewRewardIndex(types.BondDenom, d("0.2")),
types.MultiRewardIndexes{
types.NewMultiRewardIndex(
types.BondDenom,
types.RewardIndexes{
{
CollateralType: "hard", RewardFactor: d("0.2"),
},
{
CollateralType: "swp", RewardFactor: d("0.4"),
},
},
),
},
)
@ -197,7 +237,10 @@ func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenGloba
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(
cs(c(types.HardLiquidityRewardDenom, 100)).Add(claim.Reward...),
cs(
c(types.HardLiquidityRewardDenom, 100),
c("swp", 200),
).Add(claim.Reward...),
syncedClaim.Reward,
)
}

View File

@ -66,10 +66,10 @@ func (suite *DelegatorRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGe
func (suite *DelegatorRewardsTestSuite) TestAccumulateHardDelegatorRewards() {
type args struct {
delegation sdk.Coin
rewardsPerSecond sdk.Coin
timeElapsed int
expectedRewardFactor sdk.Dec
delegation sdk.Coin
rewardsPerSecond sdk.Coins
timeElapsed int
expectedRewardIndexes types.RewardIndexes
}
type test struct {
name string
@ -79,28 +79,46 @@ func (suite *DelegatorRewardsTestSuite) TestAccumulateHardDelegatorRewards() {
{
"7 seconds",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
timeElapsed: 7,
expectedRewardFactor: d("0.428239000000000000"),
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)),
timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.428239000000000000")),
},
},
},
{
"1 day",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
timeElapsed: 86400,
expectedRewardFactor: d("5285.692800000000000000"),
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)),
timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("5285.692800000000000000")),
},
},
},
{
"0 seconds",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
timeElapsed: 0,
expectedRewardFactor: d("0.0"),
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)),
timeElapsed: 0,
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.0")),
},
},
},
{
"multiple reward coins",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354), c("swp", 567889)),
timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.428239000000000000")),
types.NewRewardIndex("swp", d("1.987611500000000000")),
},
},
},
}
@ -127,24 +145,24 @@ func (suite *DelegatorRewardsTestSuite) TestAccumulateHardDelegatorRewards() {
runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
runCtx := suite.ctx.WithBlockTime(runAtTime)
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(runCtx, tc.args.delegation.Denom)
rewardPeriods, found := suite.keeper.GetHardDelegatorRewardPeriods(runCtx, tc.args.delegation.Denom)
suite.Require().True(found)
err = suite.keeper.AccumulateHardDelegatorRewards(runCtx, rewardPeriod)
err = suite.keeper.AccumulateHardDelegatorRewards(runCtx, rewardPeriods)
suite.Require().NoError(err)
rewardFactor, _ := suite.keeper.GetHardDelegatorRewardFactor(runCtx, tc.args.delegation.Denom)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
rewardIndexes, _ := suite.keeper.GetHardDelegatorRewardIndexes(runCtx, tc.args.delegation.Denom)
suite.Require().Equal(tc.args.expectedRewardIndexes, rewardIndexes)
})
}
}
func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
type args struct {
delegation sdk.Coin
rewardsPerSecond sdk.Coin
blockTimes []int
expectedRewardFactor sdk.Dec
expectedRewards sdk.Coins
delegation sdk.Coin
rewardsPerSecond sdk.Coins
blockTimes []int
expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins
}
type test struct {
name string
@ -155,31 +173,50 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
{
"10 blocks",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardFactor: d("6.117700000000000000"),
expectedRewards: cs(c("hard", 6117700)),
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("6.117700000000000000")),
},
expectedRewards: cs(c("hard", 6117700)),
},
},
{
"10 blocks - long block time",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardFactor: d("52856.928000000000000000"),
expectedRewards: cs(c("hard", 52856928000)),
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("52856.928000000000000000")),
},
expectedRewards: cs(c("hard", 52856928000)),
},
},
{
"delegator reward index updated when reward is zero",
args{
delegation: c("ukava", 1),
rewardsPerSecond: c("hard", 1),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardFactor: d("0.000099999900000100"),
expectedRewards: nil,
delegation: c("ukava", 1),
rewardsPerSecond: cs(c("hard", 1)),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.000099999900000100")),
},
expectedRewards: nil,
},
},
{
"multiple reward coins",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354), c("swp", 56789)),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("6.117700000000000000")),
types.NewRewardIndex("swp", d("2.839450000000000000")),
},
expectedRewards: cs(c("hard", 6117700), c("swp", 2839450)),
},
},
}
@ -215,7 +252,9 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
// Check that Staking hooks initialized a HardLiquidityProviderClaim
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), claim.DelegatorRewardIndexes[0].RewardFactor)
for _, rewardIndex := range claim.DelegatorRewardIndexes[0].RewardIndexes {
suite.Require().Equal(sdk.ZeroDec(), rewardIndex.RewardFactor)
}
// Run accumulator at several intervals
var timeElapsed int
@ -226,10 +265,10 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
previousBlockTime = updatedBlockTime
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom)
rewardPeriods, found := suite.keeper.GetHardDelegatorRewardPeriods(blockCtx, tc.args.delegation.Denom)
suite.Require().True(found)
err := suite.keeper.AccumulateHardDelegatorRewards(blockCtx, rewardPeriod)
err := suite.keeper.AccumulateHardDelegatorRewards(blockCtx, rewardPeriods)
suite.Require().NoError(err)
}
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
@ -241,12 +280,17 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
})
// Check that reward factor and claim have been updated as expected
rewardFactor, _ := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
rewardIndexes, _ := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, tc.args.delegation.Denom)
for i, rewardPerSecond := range tc.args.rewardsPerSecond {
rewardFactor, _ := rewardIndexes.Get(rewardPerSecond.Denom)
suite.Require().Equal(tc.args.expectedRewardIndexes[i].RewardFactor, rewardFactor)
}
claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
suite.Require().Equal(tc.args.expectedRewardFactor, claim.DelegatorRewardIndexes[0].RewardFactor)
for i, delegatorRewardIndex := range claim.DelegatorRewardIndexes[0].RewardIndexes {
suite.Require().Equal(tc.args.expectedRewardIndexes[i].RewardFactor, delegatorRewardIndex.RewardFactor)
}
suite.Require().Equal(tc.args.expectedRewards, claim.Reward)
})
}
@ -255,7 +299,7 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
type args struct {
delegation sdk.Coin
rewardsPerSecond sdk.Coin
rewardsPerSecond sdk.Coins
blockTimes []int
expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins
@ -270,9 +314,9 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchroni
"10 blocks",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
rewardsPerSecond: cs(c("hard", 122354)),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("6.117700000000000000"))}, // Here the reward index stores data differently than inside a MultiRewardIndex
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("6.117700000000000000"))},
expectedRewards: cs(c("hard", 6117700)),
},
},
@ -280,12 +324,25 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchroni
"10 blocks - long block time",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354),
rewardsPerSecond: cs(c("hard", 122354)),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("52856.928000000000000000"))},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("52856.928000000000000000"))},
expectedRewards: cs(c("hard", 52856928000)),
},
},
{
"multiple rewards coins",
args{
delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354), c("swp", 56789)),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("6.117700000000000000")),
types.NewRewardIndex("swp", d("2.839450000000000000")),
},
expectedRewards: cs(c("hard", 6117700), c("swp", 2839450)),
},
},
}
for _, tc := range testCases {
@ -311,7 +368,9 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchroni
// Check that Staking hooks initialized a HardLiquidityProviderClaim
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), claim.DelegatorRewardIndexes[0].RewardFactor)
for _, rewardIndex := range claim.DelegatorRewardIndexes[0].RewardIndexes {
suite.Require().Equal(sdk.ZeroDec(), rewardIndex.RewardFactor)
}
// Run accumulator at several intervals
var timeElapsed int
@ -323,9 +382,9 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchroni
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
// Accumulate hard delegator rewards
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom)
rewardPeriods, found := suite.keeper.GetHardDelegatorRewardPeriods(blockCtx, tc.args.delegation.Denom)
suite.Require().True(found)
err := suite.keeper.AccumulateHardDelegatorRewards(blockCtx, rewardPeriod)
err := suite.keeper.AccumulateHardDelegatorRewards(blockCtx, rewardPeriods)
suite.Require().NoError(err)
}
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
@ -333,11 +392,12 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchroni
// Check that the synced claim held in memory has properly simulated syncing
syncedClaim := suite.keeper.SimulateHardSynchronization(suite.ctx, claim)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
for i, expectedRewardIndex := range tc.args.expectedRewardIndexes {
// Check that the user's claim's reward index matches the expected reward index
rewardIndex, found := syncedClaim.DelegatorRewardIndexes.GetRewardIndex(expectedRewardIndex.CollateralType)
multiRewardIndex, found := syncedClaim.DelegatorRewardIndexes.Get(types.BondDenom)
suite.Require().True(found)
suite.Require().Equal(expectedRewardIndex, rewardIndex)
suite.Require().Equal(expectedRewardIndex, multiRewardIndex[i])
// Check that the user's claim holds the expected amount of reward coins
suite.Require().Equal(
@ -395,7 +455,7 @@ func (suite *DelegatorRewardsTestSuite) TestUnbondingValidatorSyncsClaim() {
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[2]), cs(c("ukava", 1e9)))
rewardsPerSecond := c("hard", 122354)
rewardsPerSecond := cs(c("hard", 122354))
bondDenom := "ukava"
incentBuilder := NewIncentiveGenesisBuilder().
@ -446,14 +506,16 @@ func (suite *DelegatorRewardsTestSuite) TestUnbondingValidatorSyncsClaim() {
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
globalIndex, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
rewardIndexes, found := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
globalIndex, found := rewardIndexes.Get(rewardsPerSecond[0].Denom)
suite.Require().True(found)
claimIndex, found := claim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, claimIndex.RewardFactor)
suite.Require().Equal(globalIndex, claimIndex.RewardIndexes[0].RewardFactor)
suite.Require().Equal(
cs(c(rewardsPerSecond.Denom, 76471)),
cs(c(rewardsPerSecond[0].Denom, 76471)),
claim.Reward,
)
@ -471,9 +533,11 @@ func (suite *DelegatorRewardsTestSuite) TestUnbondingValidatorSyncsClaim() {
// claim index has been updated to latest global value
laterClaimIndex, found := laterClaim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
globalIndex, found = suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
rewardIndexes, found = suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, laterClaimIndex.RewardFactor)
globalIndex, found = rewardIndexes.Get(rewardsPerSecond[0].Denom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, laterClaimIndex.RewardIndexes[0].RewardFactor)
}
// given a user has a delegation to an unbonded validator, when the validator becomes bonded, the user starts accumulating rewards
@ -485,7 +549,7 @@ func (suite *DelegatorRewardsTestSuite) TestBondingValidatorSyncsClaim() {
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[2]), cs(c("ukava", 1e9)))
rewardsPerSecond := c("hard", 122354)
rewardsPerSecond := cs(c("hard", 122354))
bondDenom := "ukava"
incentBuilder := NewIncentiveGenesisBuilder().
@ -536,11 +600,13 @@ func (suite *DelegatorRewardsTestSuite) TestBondingValidatorSyncsClaim() {
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
globalIndex, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
rewardIndexes, found := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
globalIndex, found := rewardIndexes.Get(rewardsPerSecond[0].Denom)
suite.Require().True(found)
claimIndex, found := claim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, claimIndex.RewardFactor)
suite.Require().Equal(globalIndex, claimIndex.RewardIndexes[0].RewardFactor)
suite.Require().Equal(
sdk.Coins(nil),
@ -561,9 +627,11 @@ func (suite *DelegatorRewardsTestSuite) TestBondingValidatorSyncsClaim() {
// claim index has been updated to latest global value
laterClaimIndex, found := laterClaim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
globalIndex, found = suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
rewardIndexes, found = suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, laterClaimIndex.RewardFactor)
globalIndex, found = rewardIndexes.Get(rewardsPerSecond[0].Denom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, laterClaimIndex.RewardIndexes[0].RewardFactor)
}
// If a validator is slashed delegators should have their claims synced
@ -573,7 +641,7 @@ func (suite *DelegatorRewardsTestSuite) TestSlashingValidatorSyncsClaim() {
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9)))
rewardsPerSecond := c("hard", 122354)
rewardsPerSecond := cs(c("hard", 122354))
bondDenom := "ukava"
incentBuilder := NewIncentiveGenesisBuilder().
@ -608,11 +676,11 @@ func (suite *DelegatorRewardsTestSuite) TestSlashingValidatorSyncsClaim() {
// Check that claim has been created with synced reward index but no reward coins
initialClaim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.True(found)
initialGlobalIndex, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
initialGlobalIndex, found := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.True(found)
initialClaimIndex, found := initialClaim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.True(found)
suite.Require().Equal(initialGlobalIndex, initialClaimIndex.RewardFactor)
suite.Require().Equal(initialGlobalIndex, initialClaimIndex.RewardIndexes)
suite.True(initialClaim.Reward.Empty()) // Initial claim should not have any rewards
// Start a new block to accumulate some delegation rewards for the user.
@ -631,20 +699,20 @@ func (suite *DelegatorRewardsTestSuite) TestSlashingValidatorSyncsClaim() {
// Check that the user's claim has been synced. ie rewards added, index updated
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
globalIndex, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
globalIndex, found := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
claimIndex, found := claim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, claimIndex.RewardFactor)
suite.Require().Equal(globalIndex, claimIndex.RewardIndexes)
// Check that rewards were added
suite.Require().Equal(
cs(c(rewardsPerSecond.Denom, 58264)),
cs(c(rewardsPerSecond[0].Denom, 58264)),
claim.Reward,
)
// Check that reward factor increased from initial value
suite.True(claimIndex.RewardFactor.GT(initialClaimIndex.RewardFactor))
suite.True(claimIndex.RewardIndexes[0].RewardFactor.GT(initialClaimIndex.RewardIndexes[0].RewardFactor))
}
// Given a delegation to a bonded validator, when a user redelegates everything to another (bonded) validator, the user's claim is synced
@ -654,7 +722,7 @@ func (suite *DelegatorRewardsTestSuite) TestRedelegationSyncsClaim() {
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9)))
rewardsPerSecond := c("hard", 122354)
rewardsPerSecond := cs(c("hard", 122354))
bondDenom := "ukava"
incentBuilder := NewIncentiveGenesisBuilder().
@ -689,13 +757,13 @@ func (suite *DelegatorRewardsTestSuite) TestRedelegationSyncsClaim() {
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
suite.Require().True(found)
globalIndex, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, bondDenom)
globalIndex, found := suite.keeper.GetHardDelegatorRewardIndexes(suite.ctx, bondDenom)
suite.Require().True(found)
claimIndex, found := claim.DelegatorRewardIndexes.GetRewardIndex(bondDenom)
suite.Require().True(found)
suite.Require().Equal(globalIndex, claimIndex.RewardFactor)
suite.Require().Equal(globalIndex, claimIndex.RewardIndexes)
suite.Require().Equal(
cs(c(rewardsPerSecond.Denom, 76471)),
cs(c(rewardsPerSecond[0].Denom, 76471)),
claim.Reward,
)
}

View File

@ -300,58 +300,56 @@ func (k Keeper) SimulateHardSynchronization(ctx sdk.Context, claim types.HardLiq
}
}
// 3. Simulate Hard delegator rewards
delagatorFactor, found := k.GetHardDelegatorRewardFactor(ctx, types.BondDenom)
if !found {
return claim
}
delegatorIndex, hasDelegatorRewardIndex := claim.HasDelegatorRewardIndex(types.BondDenom)
if !hasDelegatorRewardIndex {
return claim
}
userRewardFactor := claim.DelegatorRewardIndexes[delegatorIndex].RewardFactor
rewardsAccumulatedFactor := delagatorFactor.Sub(userRewardFactor)
if rewardsAccumulatedFactor.IsZero() {
return claim
}
claim.DelegatorRewardIndexes[delegatorIndex].RewardFactor = delagatorFactor
totalDelegated := sdk.ZeroDec()
delegations := k.stakingKeeper.GetDelegatorDelegations(ctx, claim.GetOwner(), 200)
for _, delegation := range delegations {
validator, found := k.stakingKeeper.GetValidator(ctx, delegation.GetValidatorAddr())
if !found {
// 3. Simulate delegator rewards
for _, ri := range claim.DelegatorRewardIndexes {
// For each Delegator reward index (there's only one: the bond denom 'ukava')
globalRewardIndexes, foundGlobalRewardIndexes := k.GetHardDelegatorRewardIndexes(ctx, ri.CollateralType)
if !foundGlobalRewardIndexes {
continue
}
// Delegators don't accumulate rewards if their validator is unbonded/slashed
if validator.GetStatus() != sdk.Bonded {
userRewardIndexes, foundUserRewardIndexes := claim.DelegatorRewardIndexes.GetRewardIndex(ri.CollateralType)
if !foundUserRewardIndexes {
continue
}
if validator.GetTokens().IsZero() {
userRewardIndexIndex, foundUserRewardIndexIndex := claim.DelegatorRewardIndexes.GetRewardIndexIndex(ri.CollateralType)
if !foundUserRewardIndexIndex {
continue
}
delegatedTokens := validator.TokensFromShares(delegation.GetShares())
if delegatedTokens.IsZero() || delegatedTokens.IsNegative() {
continue
amtDelegated := k.GetTotalDelegated(ctx, claim.GetOwner(), sdk.ValAddress(claim.Owner.String()), true)
for _, globalRewardIndex := range globalRewardIndexes {
userRewardIndex, foundUserRewardIndex := userRewardIndexes.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
if !foundUserRewardIndex {
userRewardIndex = types.NewRewardIndex(globalRewardIndex.CollateralType, sdk.ZeroDec())
userRewardIndexes.RewardIndexes = append(userRewardIndexes.RewardIndexes, userRewardIndex)
claim.DelegatorRewardIndexes[userRewardIndexIndex].RewardIndexes = append(claim.DelegatorRewardIndexes[userRewardIndexIndex].RewardIndexes, userRewardIndex)
}
globalRewardFactor := globalRewardIndex.RewardFactor
userRewardFactor := userRewardIndex.RewardFactor
rewardsAccumulatedFactor := globalRewardFactor.Sub(userRewardFactor)
if rewardsAccumulatedFactor.IsZero() {
continue
}
rewardsEarned := rewardsAccumulatedFactor.Mul(amtDelegated).RoundInt()
if rewardsEarned.IsZero() || rewardsEarned.IsNegative() {
continue
}
factorIndex, foundFactorIndex := userRewardIndexes.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
if !foundFactorIndex {
continue
}
claim.DelegatorRewardIndexes[userRewardIndexIndex].RewardIndexes[factorIndex].RewardFactor = globalRewardIndex.RewardFactor
newRewardsCoin := sdk.NewCoin(userRewardIndex.CollateralType, rewardsEarned)
claim.Reward = claim.Reward.Add(newRewardsCoin)
}
totalDelegated = totalDelegated.Add(delegatedTokens)
}
rewardsEarned := rewardsAccumulatedFactor.Mul(totalDelegated).RoundInt()
if rewardsEarned.IsZero() || rewardsEarned.IsNegative() {
return claim
}
// Add rewards to delegator's hard claim
newRewardsCoin := sdk.NewCoin(types.HardLiquidityRewardDenom, rewardsEarned)
claim.Reward = claim.Reward.Add(newRewardsCoin)
return claim
}

View File

@ -0,0 +1,965 @@
package v0_14
import (
"errors"
"fmt"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params"
tmtime "github.com/tendermint/tendermint/types/time"
cdptypes "github.com/kava-labs/kava/x/cdp/types"
kavadistTypes "github.com/kava-labs/kava/x/kavadist/types"
)
// Valid reward multipliers
const (
Small MultiplierName = "small"
Medium MultiplierName = "medium"
Large MultiplierName = "large"
USDXMintingClaimType = "usdx_minting"
HardLiquidityProviderClaimType = "hard_liquidity_provider"
BondDenom = "ukava"
ModuleName = "incentive"
)
// Parameter keys and default values
var (
KeyUSDXMintingRewardPeriods = []byte("USDXMintingRewardPeriods")
KeyHardSupplyRewardPeriods = []byte("HardSupplyRewardPeriods")
KeyHardBorrowRewardPeriods = []byte("HardBorrowRewardPeriods")
KeyHardDelegatorRewardPeriods = []byte("HardDelegatorRewardPeriods")
KeyClaimEnd = []byte("ClaimEnd")
KeyMultipliers = []byte("ClaimMultipliers")
DefaultActive = false
DefaultRewardPeriods = RewardPeriods{}
DefaultMultiRewardPeriods = MultiRewardPeriods{}
DefaultMultipliers = Multipliers{}
DefaultUSDXClaims = USDXMintingClaims{}
DefaultHardClaims = HardLiquidityProviderClaims{}
DefaultGenesisAccumulationTimes = GenesisAccumulationTimes{}
DefaultClaimEnd = tmtime.Canonical(time.Unix(1, 0))
GovDenom = cdptypes.DefaultGovDenom
PrincipalDenom = "usdx"
IncentiveMacc = kavadistTypes.ModuleName
)
// GenesisState is the state that must be provided at genesis.
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
USDXAccumulationTimes GenesisAccumulationTimes `json:"usdx_accumulation_times" yaml:"usdx_accumulation_times"`
HardSupplyAccumulationTimes GenesisAccumulationTimes `json:"hard_supply_accumulation_times" yaml:"hard_supply_accumulation_times"`
HardBorrowAccumulationTimes GenesisAccumulationTimes `json:"hard_borrow_accumulation_times" yaml:"hard_borrow_accumulation_times"`
HardDelegatorAccumulationTimes GenesisAccumulationTimes `json:"hard_delegator_accumulation_times" yaml:"hard_delegator_accumulation_times"`
USDXMintingClaims USDXMintingClaims `json:"usdx_minting_claims" yaml:"usdx_minting_claims"`
HardLiquidityProviderClaims HardLiquidityProviderClaims `json:"hard_liquidity_provider_claims" yaml:"hard_liquidity_provider_claims"`
}
// NewGenesisState returns a new genesis state
func NewGenesisState(params Params, usdxAccumTimes, hardSupplyAccumTimes, hardBorrowAccumTimes, hardDelegatorAccumTimes GenesisAccumulationTimes, c USDXMintingClaims, hc HardLiquidityProviderClaims) GenesisState {
return GenesisState{
Params: params,
USDXAccumulationTimes: usdxAccumTimes,
HardSupplyAccumulationTimes: hardSupplyAccumTimes,
HardBorrowAccumulationTimes: hardBorrowAccumTimes,
HardDelegatorAccumulationTimes: hardDelegatorAccumTimes,
USDXMintingClaims: c,
HardLiquidityProviderClaims: hc,
}
}
// DefaultGenesisState returns a default genesis state
func DefaultGenesisState() GenesisState {
return GenesisState{
Params: DefaultParams(),
USDXAccumulationTimes: GenesisAccumulationTimes{},
HardSupplyAccumulationTimes: GenesisAccumulationTimes{},
HardBorrowAccumulationTimes: GenesisAccumulationTimes{},
HardDelegatorAccumulationTimes: GenesisAccumulationTimes{},
USDXMintingClaims: DefaultUSDXClaims,
HardLiquidityProviderClaims: DefaultHardClaims,
}
}
// Validate performs basic validation of genesis data returning an
// error for any failed validation criteria.
func (gs GenesisState) Validate() error {
if err := gs.Params.Validate(); err != nil {
return err
}
if err := gs.USDXAccumulationTimes.Validate(); err != nil {
return err
}
if err := gs.HardSupplyAccumulationTimes.Validate(); err != nil {
return err
}
if err := gs.HardBorrowAccumulationTimes.Validate(); err != nil {
return err
}
if err := gs.HardDelegatorAccumulationTimes.Validate(); err != nil {
return err
}
if err := gs.HardLiquidityProviderClaims.Validate(); err != nil {
return err
}
return gs.USDXMintingClaims.Validate()
}
// GenesisAccumulationTime stores the previous reward distribution time and its corresponding collateral type
type GenesisAccumulationTime struct {
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
PreviousAccumulationTime time.Time `json:"previous_accumulation_time" yaml:"previous_accumulation_time"`
}
// NewGenesisAccumulationTime returns a new GenesisAccumulationTime
func NewGenesisAccumulationTime(ctype string, prevTime time.Time) GenesisAccumulationTime {
return GenesisAccumulationTime{
CollateralType: ctype,
PreviousAccumulationTime: prevTime,
}
}
// GenesisAccumulationTimes slice of GenesisAccumulationTime
type GenesisAccumulationTimes []GenesisAccumulationTime
// Validate performs validation of GenesisAccumulationTimes
func (gats GenesisAccumulationTimes) Validate() error {
for _, gat := range gats {
if err := gat.Validate(); err != nil {
return err
}
}
return nil
}
// Validate performs validation of GenesisAccumulationTime
func (gat GenesisAccumulationTime) Validate() error {
if len(gat.CollateralType) == 0 {
return fmt.Errorf("genesis accumulation time's collateral type must be defined")
}
return nil
}
// Params governance parameters for the incentive module
type Params struct {
USDXMintingRewardPeriods RewardPeriods `json:"usdx_minting_reward_periods" yaml:"usdx_minting_reward_periods"`
HardSupplyRewardPeriods MultiRewardPeriods `json:"hard_supply_reward_periods" yaml:"hard_supply_reward_periods"`
HardBorrowRewardPeriods MultiRewardPeriods `json:"hard_borrow_reward_periods" yaml:"hard_borrow_reward_periods"`
HardDelegatorRewardPeriods RewardPeriods `json:"hard_delegator_reward_periods" yaml:"hard_delegator_reward_periods"`
ClaimMultipliers Multipliers `json:"claim_multipliers" yaml:"claim_multipliers"`
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
}
// NewParams returns a new params object
func NewParams(usdxMinting RewardPeriods, hardSupply, hardBorrow MultiRewardPeriods,
hardDelegator RewardPeriods, multipliers Multipliers, claimEnd time.Time) Params {
return Params{
USDXMintingRewardPeriods: usdxMinting,
HardSupplyRewardPeriods: hardSupply,
HardBorrowRewardPeriods: hardBorrow,
HardDelegatorRewardPeriods: hardDelegator,
ClaimMultipliers: multipliers,
ClaimEnd: claimEnd,
}
}
// DefaultParams returns default params for incentive module
func DefaultParams() Params {
return NewParams(DefaultRewardPeriods, DefaultMultiRewardPeriods,
DefaultMultiRewardPeriods, DefaultRewardPeriods, DefaultMultipliers, DefaultClaimEnd)
}
// String implements fmt.Stringer
func (p Params) String() string {
return fmt.Sprintf(`Params:
USDX Minting Reward Periods: %s
Hard Supply Reward Periods: %s
Hard Borrow Reward Periods: %s
Hard Delegator Reward Periods: %s
Claim Multipliers :%s
Claim End Time: %s
`, p.USDXMintingRewardPeriods, p.HardSupplyRewardPeriods, p.HardBorrowRewardPeriods,
p.HardDelegatorRewardPeriods, p.ClaimMultipliers, p.ClaimEnd)
}
// ParamKeyTable Key declaration for parameters
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable().RegisterParamSet(&Params{})
}
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
params.NewParamSetPair(KeyUSDXMintingRewardPeriods, &p.USDXMintingRewardPeriods, validateRewardPeriodsParam),
params.NewParamSetPair(KeyHardSupplyRewardPeriods, &p.HardSupplyRewardPeriods, validateMultiRewardPeriodsParam),
params.NewParamSetPair(KeyHardBorrowRewardPeriods, &p.HardBorrowRewardPeriods, validateMultiRewardPeriodsParam),
params.NewParamSetPair(KeyHardDelegatorRewardPeriods, &p.HardDelegatorRewardPeriods, validateRewardPeriodsParam),
params.NewParamSetPair(KeyClaimEnd, &p.ClaimEnd, validateClaimEndParam),
params.NewParamSetPair(KeyMultipliers, &p.ClaimMultipliers, validateMultipliersParam),
}
}
// Validate checks that the parameters have valid values.
func (p Params) Validate() error {
if err := validateMultipliersParam(p.ClaimMultipliers); err != nil {
return err
}
if err := validateRewardPeriodsParam(p.USDXMintingRewardPeriods); err != nil {
return err
}
if err := validateMultiRewardPeriodsParam(p.HardSupplyRewardPeriods); err != nil {
return err
}
if err := validateMultiRewardPeriodsParam(p.HardBorrowRewardPeriods); err != nil {
return err
}
return validateRewardPeriodsParam(p.HardDelegatorRewardPeriods)
}
func validateRewardPeriodsParam(i interface{}) error {
rewards, ok := i.(RewardPeriods)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return rewards.Validate()
}
func validateMultiRewardPeriodsParam(i interface{}) error {
rewards, ok := i.(MultiRewardPeriods)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return rewards.Validate()
}
func validateMultipliersParam(i interface{}) error {
multipliers, ok := i.(Multipliers)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return multipliers.Validate()
}
func validateClaimEndParam(i interface{}) error {
endTime, ok := i.(time.Time)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
if endTime.Unix() <= 0 {
return fmt.Errorf("end time should not be zero")
}
return nil
}
// RewardPeriod stores the state of an ongoing reward
type RewardPeriod struct {
Active bool `json:"active" yaml:"active"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Start time.Time `json:"start" yaml:"start"`
End time.Time `json:"end" yaml:"end"`
RewardsPerSecond sdk.Coin `json:"rewards_per_second" yaml:"rewards_per_second"` // per second reward payouts
}
// String implements fmt.Stringer
func (rp RewardPeriod) String() string {
return fmt.Sprintf(`Reward Period:
Collateral Type: %s,
Start: %s,
End: %s,
Rewards Per Second: %s,
Active %t,
`, rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond, rp.Active)
}
// NewRewardPeriod returns a new RewardPeriod
func NewRewardPeriod(active bool, collateralType string, start time.Time, end time.Time, reward sdk.Coin) RewardPeriod {
return RewardPeriod{
Active: active,
CollateralType: collateralType,
Start: start,
End: end,
RewardsPerSecond: reward,
}
}
// Validate performs a basic check of a RewardPeriod fields.
func (rp RewardPeriod) Validate() error {
if rp.Start.Unix() <= 0 {
return errors.New("reward period start time cannot be 0")
}
if rp.End.Unix() <= 0 {
return errors.New("reward period end time cannot be 0")
}
if rp.Start.After(rp.End) {
return fmt.Errorf("end period time %s cannot be before start time %s", rp.End, rp.Start)
}
if !rp.RewardsPerSecond.IsValid() {
return fmt.Errorf("invalid reward amount: %s", rp.RewardsPerSecond)
}
if strings.TrimSpace(rp.CollateralType) == "" {
return fmt.Errorf("reward period collateral type cannot be blank: %s", rp)
}
return nil
}
// RewardPeriods array of RewardPeriod
type RewardPeriods []RewardPeriod
// Validate checks if all the RewardPeriods are valid and there are no duplicated
// entries.
func (rps RewardPeriods) Validate() error {
seenPeriods := make(map[string]bool)
for _, rp := range rps {
if seenPeriods[rp.CollateralType] {
return fmt.Errorf("duplicated reward period with collateral type %s", rp.CollateralType)
}
if err := rp.Validate(); err != nil {
return err
}
seenPeriods[rp.CollateralType] = true
}
return nil
}
// Multiplier amount the claim rewards get increased by, along with how long the claim rewards are locked
type Multiplier struct {
Name MultiplierName `json:"name" yaml:"name"`
MonthsLockup int64 `json:"months_lockup" yaml:"months_lockup"`
Factor sdk.Dec `json:"factor" yaml:"factor"`
}
// NewMultiplier returns a new Multiplier
func NewMultiplier(name MultiplierName, lockup int64, factor sdk.Dec) Multiplier {
return Multiplier{
Name: name,
MonthsLockup: lockup,
Factor: factor,
}
}
// Validate multiplier param
func (m Multiplier) Validate() error {
if err := m.Name.IsValid(); err != nil {
return err
}
if m.MonthsLockup < 0 {
return fmt.Errorf("expected non-negative lockup, got %d", m.MonthsLockup)
}
if m.Factor.IsNegative() {
return fmt.Errorf("expected non-negative factor, got %s", m.Factor.String())
}
return nil
}
// String implements fmt.Stringer
func (m Multiplier) String() string {
return fmt.Sprintf(`Claim Multiplier:
Name: %s
Months Lockup %d
Factor %s
`, m.Name, m.MonthsLockup, m.Factor)
}
// Multipliers slice of Multiplier
type Multipliers []Multiplier
// Validate validates each multiplier
func (ms Multipliers) Validate() error {
for _, m := range ms {
if err := m.Validate(); err != nil {
return err
}
}
return nil
}
// String implements fmt.Stringer
func (ms Multipliers) String() string {
out := "Claim Multipliers\n"
for _, s := range ms {
out += fmt.Sprintf("%s\n", s)
}
return out
}
// MultiplierName name for valid multiplier
type MultiplierName string
// IsValid checks if the input is one of the expected strings
func (mn MultiplierName) IsValid() error {
switch mn {
case Small, Medium, Large:
return nil
}
return fmt.Errorf("invalid multiplier name: %s", mn)
}
// Claim is an interface for handling common claim actions
type Claim interface {
GetOwner() sdk.AccAddress
GetReward() sdk.Coin
GetType() string
}
// Claims is a slice of Claim
type Claims []Claim
// BaseClaim is a common type shared by all Claims
type BaseClaim struct {
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Reward sdk.Coin `json:"reward" yaml:"reward"`
}
// GetOwner is a getter for Claim Owner
func (c BaseClaim) GetOwner() sdk.AccAddress { return c.Owner }
// GetReward is a getter for Claim Reward
func (c BaseClaim) GetReward() sdk.Coin { return c.Reward }
// GetType returns the claim type, used to identify auctions in event attributes
func (c BaseClaim) GetType() string { return "base" }
// Validate performs a basic check of a BaseClaim fields
func (c BaseClaim) Validate() error {
if c.Owner.Empty() {
return errors.New("claim owner cannot be empty")
}
if !c.Reward.IsValid() {
return fmt.Errorf("invalid reward amount: %s", c.Reward)
}
return nil
}
// String implements fmt.Stringer
func (c BaseClaim) String() string {
return fmt.Sprintf(`Claim:
Owner: %s,
Reward: %s,
`, c.Owner, c.Reward)
}
// BaseMultiClaim is a common type shared by all Claims with multiple reward denoms
type BaseMultiClaim struct {
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
Reward sdk.Coins `json:"reward" yaml:"reward"`
}
// GetOwner is a getter for Claim Owner
func (c BaseMultiClaim) GetOwner() sdk.AccAddress { return c.Owner }
// GetReward is a getter for Claim Reward
func (c BaseMultiClaim) GetReward() sdk.Coins { return c.Reward }
// GetType returns the claim type, used to identify auctions in event attributes
func (c BaseMultiClaim) GetType() string { return "base" }
// Validate performs a basic check of a BaseClaim fields
func (c BaseMultiClaim) Validate() error {
if c.Owner.Empty() {
return errors.New("claim owner cannot be empty")
}
if !c.Reward.IsValid() {
return fmt.Errorf("invalid reward amount: %s", c.Reward)
}
return nil
}
// String implements fmt.Stringer
func (c BaseMultiClaim) String() string {
return fmt.Sprintf(`Claim:
Owner: %s,
Reward: %s,
`, c.Owner, c.Reward)
}
// -------------- Custom Claim Types --------------
// USDXMintingClaim is for USDX minting rewards
type USDXMintingClaim struct {
BaseClaim `json:"base_claim" yaml:"base_claim"`
RewardIndexes RewardIndexes `json:"reward_indexes" yaml:"reward_indexes"`
}
// NewUSDXMintingClaim returns a new USDXMintingClaim
func NewUSDXMintingClaim(owner sdk.AccAddress, reward sdk.Coin, rewardIndexes RewardIndexes) USDXMintingClaim {
return USDXMintingClaim{
BaseClaim: BaseClaim{
Owner: owner,
Reward: reward,
},
RewardIndexes: rewardIndexes,
}
}
// GetType returns the claim's type
func (c USDXMintingClaim) GetType() string { return USDXMintingClaimType }
// GetReward returns the claim's reward coin
func (c USDXMintingClaim) GetReward() sdk.Coin { return c.Reward }
// GetOwner returns the claim's owner
func (c USDXMintingClaim) GetOwner() sdk.AccAddress { return c.Owner }
// Validate performs a basic check of a Claim fields
func (c USDXMintingClaim) Validate() error {
if err := c.RewardIndexes.Validate(); err != nil {
return err
}
return c.BaseClaim.Validate()
}
// String implements fmt.Stringer
func (c USDXMintingClaim) String() string {
return fmt.Sprintf(`%s
Reward Indexes: %s,
`, c.BaseClaim, c.RewardIndexes)
}
// HasRewardIndex check if a claim has a reward index for the input collateral type
func (c USDXMintingClaim) HasRewardIndex(collateralType string) (int64, bool) {
for index, ri := range c.RewardIndexes {
if ri.CollateralType == collateralType {
return int64(index), true
}
}
return 0, false
}
// USDXMintingClaims slice of USDXMintingClaim
type USDXMintingClaims []USDXMintingClaim
// Validate checks if all the claims are valid and there are no duplicated
// entries.
func (cs USDXMintingClaims) Validate() error {
for _, c := range cs {
if err := c.Validate(); err != nil {
return err
}
}
return nil
}
// HardLiquidityProviderClaim stores the hard liquidity provider rewards that can be claimed by owner
type HardLiquidityProviderClaim struct {
BaseMultiClaim `json:"base_claim" yaml:"base_claim"`
SupplyRewardIndexes MultiRewardIndexes `json:"supply_reward_indexes" yaml:"supply_reward_indexes"`
BorrowRewardIndexes MultiRewardIndexes `json:"borrow_reward_indexes" yaml:"borrow_reward_indexes"`
DelegatorRewardIndexes RewardIndexes `json:"delegator_reward_indexes" yaml:"delegator_reward_indexes"`
}
// NewHardLiquidityProviderClaim returns a new HardLiquidityProviderClaim
func NewHardLiquidityProviderClaim(owner sdk.AccAddress, rewards sdk.Coins, supplyRewardIndexes,
borrowRewardIndexes MultiRewardIndexes, delegatorRewardIndexes RewardIndexes) HardLiquidityProviderClaim {
return HardLiquidityProviderClaim{
BaseMultiClaim: BaseMultiClaim{
Owner: owner,
Reward: rewards,
},
SupplyRewardIndexes: supplyRewardIndexes,
BorrowRewardIndexes: borrowRewardIndexes,
DelegatorRewardIndexes: delegatorRewardIndexes,
}
}
// GetType returns the claim's type
func (c HardLiquidityProviderClaim) GetType() string { return HardLiquidityProviderClaimType }
// GetReward returns the claim's reward coin
func (c HardLiquidityProviderClaim) GetReward() sdk.Coins { return c.Reward }
// GetOwner returns the claim's owner
func (c HardLiquidityProviderClaim) GetOwner() sdk.AccAddress { return c.Owner }
// Validate performs a basic check of a HardLiquidityProviderClaim fields
func (c HardLiquidityProviderClaim) Validate() error {
if err := c.SupplyRewardIndexes.Validate(); err != nil {
return err
}
if err := c.BorrowRewardIndexes.Validate(); err != nil {
return err
}
if err := c.DelegatorRewardIndexes.Validate(); err != nil {
return err
}
return c.BaseMultiClaim.Validate()
}
// String implements fmt.Stringer
func (c HardLiquidityProviderClaim) String() string {
return fmt.Sprintf(`%s
Supply Reward Indexes: %s,
Borrow Reward Indexes: %s,
Delegator Reward Indexes: %s,
`, c.BaseMultiClaim, c.SupplyRewardIndexes, c.BorrowRewardIndexes, c.DelegatorRewardIndexes)
}
// HasSupplyRewardIndex check if a claim has a supply reward index for the input collateral type
func (c HardLiquidityProviderClaim) HasSupplyRewardIndex(denom string) (int64, bool) {
for index, ri := range c.SupplyRewardIndexes {
if ri.CollateralType == denom {
return int64(index), true
}
}
return 0, false
}
// HasBorrowRewardIndex check if a claim has a borrow reward index for the input collateral type
func (c HardLiquidityProviderClaim) HasBorrowRewardIndex(denom string) (int64, bool) {
for index, ri := range c.BorrowRewardIndexes {
if ri.CollateralType == denom {
return int64(index), true
}
}
return 0, false
}
// HasDelegatorRewardIndex check if a claim has a delegator reward index for the input collateral type
func (c HardLiquidityProviderClaim) HasDelegatorRewardIndex(collateralType string) (int64, bool) {
for index, ri := range c.DelegatorRewardIndexes {
if ri.CollateralType == collateralType {
return int64(index), true
}
}
return 0, false
}
// HardLiquidityProviderClaims slice of HardLiquidityProviderClaim
type HardLiquidityProviderClaims []HardLiquidityProviderClaim
// Validate checks if all the claims are valid and there are no duplicated
// entries.
func (cs HardLiquidityProviderClaims) Validate() error {
for _, c := range cs {
if err := c.Validate(); err != nil {
return err
}
}
return nil
}
// ---------------------- Reward periods are used by the params ----------------------
// MultiRewardPeriod supports multiple reward types
type MultiRewardPeriod struct {
Active bool `json:"active" yaml:"active"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Start time.Time `json:"start" yaml:"start"`
End time.Time `json:"end" yaml:"end"`
RewardsPerSecond sdk.Coins `json:"rewards_per_second" yaml:"rewards_per_second"` // per second reward payouts
}
// String implements fmt.Stringer
func (mrp MultiRewardPeriod) String() string {
return fmt.Sprintf(`Reward Period:
Collateral Type: %s,
Start: %s,
End: %s,
Rewards Per Second: %s,
Active %t,
`, mrp.CollateralType, mrp.Start, mrp.End, mrp.RewardsPerSecond, mrp.Active)
}
// NewMultiRewardPeriod returns a new MultiRewardPeriod
func NewMultiRewardPeriod(active bool, collateralType string, start time.Time, end time.Time, reward sdk.Coins) MultiRewardPeriod {
return MultiRewardPeriod{
Active: active,
CollateralType: collateralType,
Start: start,
End: end,
RewardsPerSecond: reward,
}
}
// Validate performs a basic check of a MultiRewardPeriod.
func (mrp MultiRewardPeriod) Validate() error {
if mrp.Start.IsZero() {
return errors.New("reward period start time cannot be 0")
}
if mrp.End.IsZero() {
return errors.New("reward period end time cannot be 0")
}
if mrp.Start.After(mrp.End) {
return fmt.Errorf("end period time %s cannot be before start time %s", mrp.End, mrp.Start)
}
if !mrp.RewardsPerSecond.IsValid() {
return fmt.Errorf("invalid reward amount: %s", mrp.RewardsPerSecond)
}
if strings.TrimSpace(mrp.CollateralType) == "" {
return fmt.Errorf("reward period collateral type cannot be blank: %s", mrp)
}
return nil
}
// MultiRewardPeriods array of MultiRewardPeriod
type MultiRewardPeriods []MultiRewardPeriod
// GetMultiRewardPeriod fetches a MultiRewardPeriod from an array of MultiRewardPeriods by its denom
func (mrps MultiRewardPeriods) GetMultiRewardPeriod(denom string) (MultiRewardPeriod, bool) {
for _, rp := range mrps {
if rp.CollateralType == denom {
return rp, true
}
}
return MultiRewardPeriod{}, false
}
// GetMultiRewardPeriodIndex returns the index of a MultiRewardPeriod inside array MultiRewardPeriods
func (mrps MultiRewardPeriods) GetMultiRewardPeriodIndex(denom string) (int, bool) {
for i, rp := range mrps {
if rp.CollateralType == denom {
return i, true
}
}
return -1, false
}
// Validate checks if all the RewardPeriods are valid and there are no duplicated
// entries.
func (mrps MultiRewardPeriods) Validate() error {
seenPeriods := make(map[string]bool)
for _, rp := range mrps {
if seenPeriods[rp.CollateralType] {
return fmt.Errorf("duplicated reward period with collateral type %s", rp.CollateralType)
}
if err := rp.Validate(); err != nil {
return err
}
seenPeriods[rp.CollateralType] = true
}
return nil
}
// ---------------------- Reward indexes are used internally in the store ----------------------
// RewardIndex stores reward accumulation information
type RewardIndex struct {
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
RewardFactor sdk.Dec `json:"reward_factor" yaml:"reward_factor"`
}
// NewRewardIndex returns a new RewardIndex
func NewRewardIndex(collateralType string, factor sdk.Dec) RewardIndex {
return RewardIndex{
CollateralType: collateralType,
RewardFactor: factor,
}
}
func (ri RewardIndex) String() string {
return fmt.Sprintf(`Collateral Type: %s, RewardFactor: %s`, ri.CollateralType, ri.RewardFactor)
}
// Validate validates reward index
func (ri RewardIndex) Validate() error {
if ri.RewardFactor.IsNegative() {
return fmt.Errorf("reward factor value should be positive, is %s for %s", ri.RewardFactor, ri.CollateralType)
}
if strings.TrimSpace(ri.CollateralType) == "" {
return fmt.Errorf("collateral type should not be empty")
}
return nil
}
// RewardIndexes slice of RewardIndex
type RewardIndexes []RewardIndex
// GetRewardIndex fetches a RewardIndex by its denom
func (ris RewardIndexes) GetRewardIndex(denom string) (RewardIndex, bool) {
for _, ri := range ris {
if ri.CollateralType == denom {
return ri, true
}
}
return RewardIndex{}, false
}
// Get fetches a RewardFactor by it's denom
func (ris RewardIndexes) Get(denom string) (sdk.Dec, bool) {
for _, ri := range ris {
if ri.CollateralType == denom {
return ri.RewardFactor, true
}
}
return sdk.Dec{}, false
}
// With returns a copy of the indexes with a new reward factor added
func (ris RewardIndexes) With(denom string, factor sdk.Dec) RewardIndexes {
newIndexes := make(RewardIndexes, len(ris))
copy(newIndexes, ris)
for i, ri := range newIndexes {
if ri.CollateralType == denom {
newIndexes[i].RewardFactor = factor
return newIndexes
}
}
return append(newIndexes, NewRewardIndex(denom, factor))
}
// GetFactorIndex gets the index of a specific reward index inside the array by its index
func (ris RewardIndexes) GetFactorIndex(denom string) (int, bool) {
for i, ri := range ris {
if ri.CollateralType == denom {
return i, true
}
}
return -1, false
}
// Validate validation for reward indexes
func (ris RewardIndexes) Validate() error {
for _, ri := range ris {
if err := ri.Validate(); err != nil {
return err
}
}
return nil
}
// MultiRewardIndex stores reward accumulation information on multiple reward types
type MultiRewardIndex struct {
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
RewardIndexes RewardIndexes `json:"reward_indexes" yaml:"reward_indexes"`
}
// NewMultiRewardIndex returns a new MultiRewardIndex
func NewMultiRewardIndex(collateralType string, indexes RewardIndexes) MultiRewardIndex {
return MultiRewardIndex{
CollateralType: collateralType,
RewardIndexes: indexes,
}
}
// GetFactorIndex gets the index of a specific reward index inside the array by its index
func (mri MultiRewardIndex) GetFactorIndex(denom string) (int, bool) {
for i, ri := range mri.RewardIndexes {
if ri.CollateralType == denom {
return i, true
}
}
return -1, false
}
func (mri MultiRewardIndex) String() string {
return fmt.Sprintf(`Collateral Type: %s, Reward Indexes: %s`, mri.CollateralType, mri.RewardIndexes)
}
// Validate validates multi-reward index
func (mri MultiRewardIndex) Validate() error {
for _, rf := range mri.RewardIndexes {
if rf.RewardFactor.IsNegative() {
return fmt.Errorf("reward index's factor value cannot be negative: %s", rf)
}
}
if strings.TrimSpace(mri.CollateralType) == "" {
return fmt.Errorf("collateral type should not be empty")
}
return nil
}
// MultiRewardIndexes slice of MultiRewardIndex
type MultiRewardIndexes []MultiRewardIndex
// GetRewardIndex fetches a RewardIndex from a MultiRewardIndex by its denom
func (mris MultiRewardIndexes) GetRewardIndex(denom string) (MultiRewardIndex, bool) {
for _, ri := range mris {
if ri.CollateralType == denom {
return ri, true
}
}
return MultiRewardIndex{}, false
}
// Get fetches a RewardIndexes by it's denom
func (mris MultiRewardIndexes) Get(denom string) (RewardIndexes, bool) {
for _, mri := range mris {
if mri.CollateralType == denom {
return mri.RewardIndexes, true
}
}
return nil, false
}
// GetRewardIndexIndex fetches a specific reward index inside the array by its denom
func (mris MultiRewardIndexes) GetRewardIndexIndex(denom string) (int, bool) {
for i, ri := range mris {
if ri.CollateralType == denom {
return i, true
}
}
return -1, false
}
// With returns a copy of the indexes with a new RewardIndexes added
func (mris MultiRewardIndexes) With(denom string, indexes RewardIndexes) MultiRewardIndexes {
newIndexes := mris.copy()
for i, mri := range newIndexes {
if mri.CollateralType == denom {
newIndexes[i].RewardIndexes = indexes
return newIndexes
}
}
return append(newIndexes, NewMultiRewardIndex(denom, indexes))
}
// GetCollateralTypes returns a slice of containing all collateral types
func (mris MultiRewardIndexes) GetCollateralTypes() []string {
var collateralTypes []string
for _, ri := range mris {
collateralTypes = append(collateralTypes, ri.CollateralType)
}
return collateralTypes
}
// RemoveRewardIndex removes a denom's reward interest factor value
func (mris MultiRewardIndexes) RemoveRewardIndex(denom string) MultiRewardIndexes {
for i, ri := range mris {
if ri.CollateralType == denom {
// copy the slice and underlying array to avoid altering the original
copy := mris.copy()
return append(copy[:i], copy[i+1:]...)
}
}
return mris
}
// Validate validation for reward indexes
func (mris MultiRewardIndexes) Validate() error {
for _, mri := range mris {
if err := mri.Validate(); err != nil {
return err
}
}
return nil
}
// copy returns a copy of the slice and underlying array
func (mris MultiRewardIndexes) copy() MultiRewardIndexes {
newIndexes := make(MultiRewardIndexes, len(mris))
copy(newIndexes, mris)
return newIndexes
}

View File

@ -167,12 +167,12 @@ type HardLiquidityProviderClaim struct {
BaseMultiClaim `json:"base_claim" yaml:"base_claim"`
SupplyRewardIndexes MultiRewardIndexes `json:"supply_reward_indexes" yaml:"supply_reward_indexes"`
BorrowRewardIndexes MultiRewardIndexes `json:"borrow_reward_indexes" yaml:"borrow_reward_indexes"`
DelegatorRewardIndexes RewardIndexes `json:"delegator_reward_indexes" yaml:"delegator_reward_indexes"`
DelegatorRewardIndexes MultiRewardIndexes `json:"delegator_reward_indexes" yaml:"delegator_reward_indexes"`
}
// NewHardLiquidityProviderClaim returns a new HardLiquidityProviderClaim
func NewHardLiquidityProviderClaim(owner sdk.AccAddress, rewards sdk.Coins, supplyRewardIndexes,
borrowRewardIndexes MultiRewardIndexes, delegatorRewardIndexes RewardIndexes) HardLiquidityProviderClaim {
borrowRewardIndexes, delegatorRewardIndexes MultiRewardIndexes) HardLiquidityProviderClaim {
return HardLiquidityProviderClaim{
BaseMultiClaim: BaseMultiClaim{
Owner: owner,

View File

@ -54,7 +54,7 @@ func TestGenesisStateValidate(t *testing.T) {
},
DefaultMultiRewardPeriods,
DefaultMultiRewardPeriods,
DefaultRewardPeriods,
DefaultMultiRewardPeriods,
Multipliers{
NewMultiplier(Small, 1, sdk.MustNewDecFromStr("0.33")),
},

View File

@ -27,11 +27,11 @@ var (
USDXMintingRewardFactorKeyPrefix = []byte{0x02} // prefix for key that stores USDX minting reward factors
PreviousUSDXMintingRewardAccrualTimeKeyPrefix = []byte{0x03} // prefix for key that stores the blocktime
HardLiquidityClaimKeyPrefix = []byte{0x04} // prefix for keys that store Hard liquidity claims
HardSupplyRewardIndexesKeyPrefix = []byte{0x05} // prefix for key that stores Hard supply reward factors
HardSupplyRewardIndexesKeyPrefix = []byte{0x05} // prefix for key that stores Hard supply reward indexes
PreviousHardSupplyRewardAccrualTimeKeyPrefix = []byte{0x06} // prefix for key that stores the previous time Hard supply rewards accrued
HardBorrowRewardIndexesKeyPrefix = []byte{0x07} // prefix for key that stores Hard borrow reward factors
HardBorrowRewardIndexesKeyPrefix = []byte{0x07} // prefix for key that stores Hard borrow reward indexes
PreviousHardBorrowRewardAccrualTimeKeyPrefix = []byte{0x08} // prefix for key that stores the previous time Hard borrow rewards accrued
HardDelegatorRewardFactorKeyPrefix = []byte{0x09} // prefix for key that stores Hard delegator reward factors
HardDelegatorRewardIndexesKeyPrefix = []byte{0x09} // prefix for key that stores Hard delegator reward indexes
PreviousHardDelegatorRewardAccrualTimeKeyPrefix = []byte{0x10} // prefix for key that stores the previous time Hard delegator rewards accrued
USDXMintingRewardDenom = "ukava"

View File

@ -48,14 +48,14 @@ type Params struct {
USDXMintingRewardPeriods RewardPeriods `json:"usdx_minting_reward_periods" yaml:"usdx_minting_reward_periods"`
HardSupplyRewardPeriods MultiRewardPeriods `json:"hard_supply_reward_periods" yaml:"hard_supply_reward_periods"`
HardBorrowRewardPeriods MultiRewardPeriods `json:"hard_borrow_reward_periods" yaml:"hard_borrow_reward_periods"`
HardDelegatorRewardPeriods RewardPeriods `json:"hard_delegator_reward_periods" yaml:"hard_delegator_reward_periods"`
HardDelegatorRewardPeriods MultiRewardPeriods `json:"hard_delegator_reward_periods" yaml:"hard_delegator_reward_periods"`
ClaimMultipliers Multipliers `json:"claim_multipliers" yaml:"claim_multipliers"`
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
}
// NewParams returns a new params object
func NewParams(usdxMinting RewardPeriods, hardSupply, hardBorrow MultiRewardPeriods,
hardDelegator RewardPeriods, multipliers Multipliers, claimEnd time.Time) Params {
func NewParams(usdxMinting RewardPeriods, hardSupply, hardBorrow, hardDelegator MultiRewardPeriods,
multipliers Multipliers, claimEnd time.Time) Params {
return Params{
USDXMintingRewardPeriods: usdxMinting,
HardSupplyRewardPeriods: hardSupply,
@ -69,7 +69,9 @@ func NewParams(usdxMinting RewardPeriods, hardSupply, hardBorrow MultiRewardPeri
// DefaultParams returns default params for incentive module
func DefaultParams() Params {
return NewParams(DefaultRewardPeriods, DefaultMultiRewardPeriods,
DefaultMultiRewardPeriods, DefaultRewardPeriods, DefaultMultipliers, DefaultClaimEnd)
DefaultMultiRewardPeriods, DefaultMultiRewardPeriods,
DefaultMultipliers, DefaultClaimEnd,
)
}
// String implements fmt.Stringer
@ -96,7 +98,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
params.NewParamSetPair(KeyUSDXMintingRewardPeriods, &p.USDXMintingRewardPeriods, validateRewardPeriodsParam),
params.NewParamSetPair(KeyHardSupplyRewardPeriods, &p.HardSupplyRewardPeriods, validateMultiRewardPeriodsParam),
params.NewParamSetPair(KeyHardBorrowRewardPeriods, &p.HardBorrowRewardPeriods, validateMultiRewardPeriodsParam),
params.NewParamSetPair(KeyHardDelegatorRewardPeriods, &p.HardDelegatorRewardPeriods, validateRewardPeriodsParam),
params.NewParamSetPair(KeyHardDelegatorRewardPeriods, &p.HardDelegatorRewardPeriods, validateMultiRewardPeriodsParam),
params.NewParamSetPair(KeyClaimEnd, &p.ClaimEnd, validateClaimEndParam),
params.NewParamSetPair(KeyMultipliers, &p.ClaimMultipliers, validateMultipliersParam),
}
@ -121,7 +123,7 @@ func (p Params) Validate() error {
return err
}
return validateRewardPeriodsParam(p.HardDelegatorRewardPeriods)
return validateMultiRewardPeriodsParam(p.HardDelegatorRewardPeriods)
}
func validateRewardPeriodsParam(i interface{}) error {

View File

@ -23,7 +23,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
usdxMintingRewardPeriods types.RewardPeriods
hardSupplyRewardPeriods types.MultiRewardPeriods
hardBorrowRewardPeriods types.MultiRewardPeriods
hardDelegatorRewardPeriods types.RewardPeriods
hardDelegatorRewardPeriods types.MultiRewardPeriods
multipliers types.Multipliers
end time.Time
}
@ -45,7 +45,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
usdxMintingRewardPeriods: types.DefaultRewardPeriods,
hardSupplyRewardPeriods: types.DefaultMultiRewardPeriods,
hardBorrowRewardPeriods: types.DefaultMultiRewardPeriods,
hardDelegatorRewardPeriods: types.DefaultRewardPeriods,
hardDelegatorRewardPeriods: types.DefaultMultiRewardPeriods,
multipliers: types.DefaultMultipliers,
end: types.DefaultClaimEnd,
},
@ -70,7 +70,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
},
hardSupplyRewardPeriods: types.DefaultMultiRewardPeriods,
hardBorrowRewardPeriods: types.DefaultMultiRewardPeriods,
hardDelegatorRewardPeriods: types.DefaultRewardPeriods,
hardDelegatorRewardPeriods: types.DefaultMultiRewardPeriods,
end: time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
},
errArgs{

View File

@ -117,22 +117,22 @@ func NewQueryRewardFactorsParams(denom string) QueryRewardFactorsParams {
// RewardFactor is a unique type returned by reward factor queries
type RewardFactor struct {
Denom string `json:"denom" yaml:"denom"`
USDXMintingRewardFactor sdk.Dec `json:"usdx_minting_reward_factor" yaml:"usdx_minting_reward_factor"`
HardSupplyRewardFactors RewardIndexes `json:"hard_supply_reward_factors" yaml:"hard_supply_reward_factors"`
HardBorrowRewardFactors RewardIndexes `json:"hard_borrow_reward_factors" yaml:"hard_borrow_reward_factors"`
HardDelegatorRewardFactor sdk.Dec `json:"hard_delegator_reward_factor" yaml:"hard_delegator_reward_factor"`
Denom string `json:"denom" yaml:"denom"`
USDXMintingRewardFactor sdk.Dec `json:"usdx_minting_reward_factor" yaml:"usdx_minting_reward_factor"`
HardSupplyRewardFactors RewardIndexes `json:"hard_supply_reward_factors" yaml:"hard_supply_reward_factors"`
HardBorrowRewardFactors RewardIndexes `json:"hard_borrow_reward_factors" yaml:"hard_borrow_reward_factors"`
HardDelegatorRewardFactors RewardIndexes `json:"hard_delegator_reward_factors" yaml:"hard_delegator_reward_factors"`
}
// NewRewardFactor returns a new instance of RewardFactor
func NewRewardFactor(denom string, usdxMintingRewardFactor sdk.Dec, hardSupplyRewardFactors,
hardBorrowRewardFactors RewardIndexes, hardDelegatorRewardFactor sdk.Dec) RewardFactor {
hardBorrowRewardFactors, hardDelegatorRewardFactors RewardIndexes) RewardFactor {
return RewardFactor{
Denom: denom,
USDXMintingRewardFactor: usdxMintingRewardFactor,
HardSupplyRewardFactors: hardSupplyRewardFactors,
HardBorrowRewardFactors: hardBorrowRewardFactors,
HardDelegatorRewardFactor: hardDelegatorRewardFactor,
Denom: denom,
USDXMintingRewardFactor: usdxMintingRewardFactor,
HardSupplyRewardFactors: hardSupplyRewardFactors,
HardBorrowRewardFactors: hardBorrowRewardFactors,
HardDelegatorRewardFactors: hardDelegatorRewardFactors,
}
}