Allocate Hard supply/borrow rewards to legacy suppliers/borrowers (#833)

* initialize hard supply reward for empty rewards

* add god committee to integration test

* organize claim types, add helper methods

* reorder integration test's god committee

* legacy suppliers earn rewards + tests

* update InitializeHardBorrowReward + test

* remove formatting comments from tests

* allocate rewards to legacy borrowers + test

* apply change to update index denom methods

* Update querier to show synced rewards for legacy deposits/borrows (#834)

* update simulated sync method to show rewards for legacy deposits/borrows

* more explicity debuging logs

* revisions

Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>
This commit is contained in:
Denali Marsh 2021-02-19 21:02:51 +01:00 committed by GitHub
parent c5fc1c6b4f
commit 6045a94b39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1200 additions and 281 deletions

View File

@ -4,9 +4,14 @@ import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp"
committeetypes "github.com/kava-labs/kava/x/committee/types"
"github.com/kava-labs/kava/x/hard"
"github.com/kava-labs/kava/x/pricefeed"
)
@ -109,6 +114,7 @@ func NewPricefeedGenStateMulti() app.GenesisState {
{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "busd:usd", BaseAsset: "busd", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "zzz:usd", BaseAsset: "zzz", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
},
},
PostedPrices: []pricefeed.PostedPrice{
@ -142,6 +148,12 @@ func NewPricefeedGenStateMulti() app.GenesisState {
Price: sdk.OneDec(),
Expiry: time.Now().Add(1 * time.Hour),
},
{
MarketID: "zzz:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("2.00"),
Expiry: time.Now().Add(1 * time.Hour),
},
},
}
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
@ -158,6 +170,7 @@ func NewHardGenStateMulti() app.GenesisState {
hard.NewMoneyMarket("bnb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "bnb:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
hard.NewMoneyMarket("btcb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "btc:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
hard.NewMoneyMarket("xrp", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "xrp:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
hard.NewMoneyMarket("zzz", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "zzz:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
},
sdk.NewDec(10),
), hard.DefaultAccumulationTimes, hard.DefaultDeposits, hard.DefaultBorrows,
@ -166,3 +179,72 @@ func NewHardGenStateMulti() app.GenesisState {
return app.GenesisState{hard.ModuleName: hard.ModuleCdc.MustMarshalJSON(hardGS)}
}
func NewAuthGenState(addresses []sdk.AccAddress, coins sdk.Coins) app.GenesisState {
coinsList := []sdk.Coins{}
for range addresses {
coinsList = append(coinsList, coins)
}
// Load up our primary user address
if len(addresses) >= 4 {
coinsList[3] = sdk.NewCoins(
sdk.NewCoin("bnb", sdk.NewInt(1000000000000000)),
sdk.NewCoin("ukava", sdk.NewInt(1000000000000000)),
sdk.NewCoin("btcb", sdk.NewInt(1000000000000000)),
sdk.NewCoin("xrp", sdk.NewInt(1000000000000000)),
sdk.NewCoin("zzz", sdk.NewInt(1000000000000000)),
)
}
return app.NewAuthGenState(addresses, coinsList)
}
func NewStakingGenesisState() app.GenesisState {
genState := staking.DefaultGenesisState()
genState.Params.BondDenom = "ukava"
return app.GenesisState{
staking.ModuleName: staking.ModuleCdc.MustMarshalJSON(genState),
}
}
func (suite *KeeperTestSuite) SetupWithGenState() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, allAddrs := app.GeneratePrivKeyAddressPairs(10)
suite.addrs = allAddrs[:5]
for _, a := range allAddrs[5:] {
suite.validatorAddrs = append(suite.validatorAddrs, sdk.ValAddress(a))
}
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
tApp.InitializeFromGenesisStates(
NewAuthGenState(allAddrs, cs(c("ukava", 5_000_000))),
NewStakingGenesisState(),
NewPricefeedGenStateMulti(),
NewCDPGenStateMulti(),
NewHardGenStateMulti(),
)
// Set up a god committee
committeeModKeeper := tApp.GetCommitteeKeeper()
godCommittee := committeetypes.Committee{
ID: 1,
Description: "This committee is for testing.",
Members: suite.addrs[:2],
Permissions: []committeetypes.Permission{committeetypes.GodPermission{}},
VoteThreshold: d("0.667"),
ProposalDuration: time.Hour * 24 * 7,
}
committeeModKeeper.SetCommittee(ctx, godCommittee)
suite.app = tApp
suite.ctx = ctx
suite.keeper = tApp.GetIncentiveKeeper()
suite.hardKeeper = tApp.GetHardKeeper()
suite.stakingKeeper = tApp.GetStakingKeeper()
suite.committeeKeeper = committeeModKeeper
}

View File

@ -16,6 +16,7 @@ import (
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
"github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types"
@ -25,13 +26,14 @@ import (
type KeeperTestSuite struct {
suite.Suite
keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
stakingKeeper stakingkeeper.Keeper
app app.TestApp
ctx sdk.Context
addrs []sdk.AccAddress
validatorAddrs []sdk.ValAddress
keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
stakingKeeper stakingkeeper.Keeper
committeeKeeper committeekeeper.Keeper
app app.TestApp
ctx sdk.Context
addrs []sdk.AccAddress
validatorAddrs []sdk.ValAddress
}
// The default state used by each test

View File

@ -262,10 +262,12 @@ func (k Keeper) InitializeHardSupplyReward(ctx sdk.Context, deposit hardtypes.De
var supplyRewardIndexes types.MultiRewardIndexes
for _, coin := range deposit.Amount {
globalRewardIndexes, foundGlobalRewardIndexes := k.GetHardSupplyRewardIndexes(ctx, coin.Denom)
if !foundGlobalRewardIndexes {
continue
var multiRewardIndex types.MultiRewardIndex
if foundGlobalRewardIndexes {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
} else {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
}
multiRewardIndex := types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
supplyRewardIndexes = append(supplyRewardIndexes, multiRewardIndex)
}
@ -296,8 +298,8 @@ func (k Keeper) SynchronizeHardSupplyReward(ctx sdk.Context, deposit hardtypes.D
continue
}
userRewardIndexes, foundUserRewardIndexes := claim.SupplyRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserRewardIndexes {
userMultiRewardIndex, foundUserMultiRewardIndex := claim.SupplyRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserMultiRewardIndex {
continue
}
@ -308,9 +310,14 @@ func (k Keeper) SynchronizeHardSupplyReward(ctx sdk.Context, deposit hardtypes.D
}
for _, globalRewardIndex := range globalRewardIndexes {
userRewardIndex, foundUserRewardIndex := userRewardIndexes.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
userRewardIndex, foundUserRewardIndex := userMultiRewardIndex.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
if !foundUserRewardIndex {
continue
// User deposited this coin type before it had rewards. When new rewards are added, legacy depositors
// should immediately begin earning rewards. Enable users to do so by updating their claim with the global
// reward index denom and start their reward factor at 0.0
userRewardIndex = types.NewRewardIndex(globalRewardIndex.CollateralType, sdk.ZeroDec())
userMultiRewardIndex.RewardIndexes = append(userMultiRewardIndex.RewardIndexes, userRewardIndex)
claim.SupplyRewardIndexes[userRewardIndexIndex] = userMultiRewardIndex
}
globalRewardFactor := globalRewardIndex.RewardFactor
@ -324,7 +331,7 @@ func (k Keeper) SynchronizeHardSupplyReward(ctx sdk.Context, deposit hardtypes.D
continue
}
factorIndex, foundFactorIndex := userRewardIndexes.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
factorIndex, foundFactorIndex := userMultiRewardIndex.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
if !foundFactorIndex {
fmt.Printf("[LOG]: factor index for %s should always be found", coin.Denom) // TODO: remove before production
continue
@ -348,10 +355,12 @@ func (k Keeper) InitializeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Bor
var borrowRewardIndexes types.MultiRewardIndexes
for _, coin := range borrow.Amount {
globalRewardIndexes, foundGlobalRewardIndexes := k.GetHardBorrowRewardIndexes(ctx, coin.Denom)
if !foundGlobalRewardIndexes {
continue
var multiRewardIndex types.MultiRewardIndex
if foundGlobalRewardIndexes {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
} else {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
}
multiRewardIndex := types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
borrowRewardIndexes = append(borrowRewardIndexes, multiRewardIndex)
}
@ -373,8 +382,8 @@ func (k Keeper) SynchronizeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Bo
continue
}
userRewardIndexes, foundUserRewardIndexes := claim.BorrowRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserRewardIndexes {
userMultiRewardIndex, foundUserMultiRewardIndex := claim.BorrowRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserMultiRewardIndex {
continue
}
@ -385,9 +394,14 @@ func (k Keeper) SynchronizeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Bo
}
for _, globalRewardIndex := range globalRewardIndexes {
userRewardIndex, foundUserRewardIndex := userRewardIndexes.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
userRewardIndex, foundUserRewardIndex := userMultiRewardIndex.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
if !foundUserRewardIndex {
continue
// User borrowed this coin type before it had rewards. When new rewards are added, legacy borrowers
// should immediately begin earning rewards. Enable users to do so by updating their claim with the global
// reward index denom and start their reward factor at 0.0
userRewardIndex = types.NewRewardIndex(globalRewardIndex.CollateralType, sdk.ZeroDec())
userMultiRewardIndex.RewardIndexes = append(userMultiRewardIndex.RewardIndexes, userRewardIndex)
claim.BorrowRewardIndexes[userRewardIndexIndex] = userMultiRewardIndex
}
globalRewardFactor := globalRewardIndex.RewardFactor
@ -401,7 +415,7 @@ func (k Keeper) SynchronizeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Bo
continue
}
factorIndex, foundFactorIndex := userRewardIndexes.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
factorIndex, foundFactorIndex := userMultiRewardIndex.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
if !foundFactorIndex {
fmt.Printf("\n[LOG]: factor index for %s should always be found", coin.Denom) // TODO: remove before production
continue
@ -425,11 +439,13 @@ func (k Keeper) UpdateHardSupplyIndexDenoms(ctx sdk.Context, deposit hardtypes.D
for _, coin := range deposit.Amount {
_, foundUserRewardIndexes := claim.SupplyRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserRewardIndexes {
globalRewardIndexes, foundGlobalRewardIndexes := k.GetHardSupplyRewardIndexes(ctx, coin.Denom)
if !foundGlobalRewardIndexes {
continue // No rewards for this coin type
globalSupplyRewardIndexes, foundGlobalSupplyRewardIndexes := k.GetHardSupplyRewardIndexes(ctx, coin.Denom)
var multiRewardIndex types.MultiRewardIndex
if foundGlobalSupplyRewardIndexes {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalSupplyRewardIndexes)
} else {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
}
multiRewardIndex := types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
supplyRewardIndexes = append(supplyRewardIndexes, multiRewardIndex)
}
}
@ -451,11 +467,13 @@ func (k Keeper) UpdateHardBorrowIndexDenoms(ctx sdk.Context, borrow hardtypes.Bo
for _, coin := range borrow.Amount {
_, foundUserRewardIndexes := claim.BorrowRewardIndexes.GetRewardIndex(coin.Denom)
if !foundUserRewardIndexes {
globalRewardIndexes, foundGlobalRewardIndexes := k.GetHardBorrowRewardIndexes(ctx, coin.Denom)
if !foundGlobalRewardIndexes {
continue // No rewards for this coin type
globalBorrowRewardIndexes, foundGlobalBorrowRewardIndexes := k.GetHardBorrowRewardIndexes(ctx, coin.Denom)
var multiRewardIndex types.MultiRewardIndex
if foundGlobalBorrowRewardIndexes {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalBorrowRewardIndexes)
} else {
multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
}
multiRewardIndex := types.NewMultiRewardIndex(coin.Denom, globalRewardIndexes)
borrowRewardIndexes = append(borrowRewardIndexes, multiRewardIndex)
}
}
@ -678,14 +696,16 @@ func (k Keeper) SimulateHardSynchronization(ctx sdk.Context, claim types.HardLiq
userRewardIndexIndex, foundUserRewardIndexIndex := claim.SupplyRewardIndexes.GetRewardIndexIndex(ri.CollateralType)
if !foundUserRewardIndexIndex {
fmt.Printf("\n[LOG]: factor index for %s should always be found", ri.CollateralType) // TODO: remove before production
fmt.Printf("\n[LOG]: claim.SupplyRewardIndexes.GetRewardIndexIndex for %s should always be found", ri.CollateralType) // TODO: remove before production
continue
}
for _, globalRewardIndex := range globalRewardIndexes {
userRewardIndex, foundUserRewardIndex := userRewardIndexes.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
if !foundUserRewardIndex {
continue
userRewardIndex = types.NewRewardIndex(globalRewardIndex.CollateralType, sdk.ZeroDec())
userRewardIndexes.RewardIndexes = append(userRewardIndexes.RewardIndexes, userRewardIndex)
claim.SupplyRewardIndexes[userRewardIndexIndex].RewardIndexes = append(claim.SupplyRewardIndexes[userRewardIndexIndex].RewardIndexes, userRewardIndex)
}
globalRewardFactor := globalRewardIndex.RewardFactor
@ -705,7 +725,7 @@ func (k Keeper) SimulateHardSynchronization(ctx sdk.Context, claim types.HardLiq
factorIndex, foundFactorIndex := userRewardIndexes.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
if !foundFactorIndex {
fmt.Printf("[LOG]: factor index for %s should always be found", ri.CollateralType) // TODO: remove before production
fmt.Printf("[LOG]: userRewardIndexes.RewardIndexes.GetFactorIndex for %s should always be found", globalRewardIndex.CollateralType) // TODO: remove before production
continue
}
claim.SupplyRewardIndexes[userRewardIndexIndex].RewardIndexes[factorIndex].RewardFactor = globalRewardIndex.RewardFactor
@ -728,14 +748,16 @@ func (k Keeper) SimulateHardSynchronization(ctx sdk.Context, claim types.HardLiq
userRewardIndexIndex, foundUserRewardIndexIndex := claim.BorrowRewardIndexes.GetRewardIndexIndex(ri.CollateralType)
if !foundUserRewardIndexIndex {
fmt.Printf("\n[LOG]: factor index for %s should always be found", ri.CollateralType) // TODO: remove before production
fmt.Printf("\n[LOG]: claim.BorrowRewardIndexes.GetRewardIndexIndex for %s should always be found", ri.CollateralType) // TODO: remove before production
continue
}
for _, globalRewardIndex := range globalRewardIndexes {
userRewardIndex, foundUserRewardIndex := userRewardIndexes.RewardIndexes.GetRewardIndex(globalRewardIndex.CollateralType)
if !foundUserRewardIndex {
continue
userRewardIndex = types.NewRewardIndex(globalRewardIndex.CollateralType, sdk.ZeroDec())
userRewardIndexes.RewardIndexes = append(userRewardIndexes.RewardIndexes, userRewardIndex)
claim.BorrowRewardIndexes[userRewardIndexIndex].RewardIndexes = append(claim.BorrowRewardIndexes[userRewardIndexIndex].RewardIndexes, userRewardIndex)
}
globalRewardFactor := globalRewardIndex.RewardFactor
@ -755,7 +777,7 @@ func (k Keeper) SimulateHardSynchronization(ctx sdk.Context, claim types.HardLiq
factorIndex, foundFactorIndex := userRewardIndexes.RewardIndexes.GetFactorIndex(globalRewardIndex.CollateralType)
if !foundFactorIndex {
fmt.Printf("[LOG]: factor index for %s should always be found", ri.CollateralType) // TODO: remove before production
fmt.Printf("[LOG]: userRewardIndexes.RewardIndexes.GetFactorIndex for %s should always be found", globalRewardIndex.CollateralType) // TODO: remove before production
continue
}
claim.BorrowRewardIndexes[userRewardIndexIndex].RewardIndexes[factorIndex].RewardFactor = globalRewardIndex.RewardFactor

File diff suppressed because it is too large Load Diff

View File

@ -264,9 +264,101 @@ func (cs HardLiquidityProviderClaims) Validate() error {
return nil
}
// -------------- Subcomponents of Custom Claim Types --------------
// ---------------------- Reward periods are used by the params ----------------------
// TODO: refactor RewardPeriod name from 'collateralType' to 'denom'
// 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 {
@ -330,80 +422,6 @@ func (ris RewardIndexes) Validate() error {
return nil
}
// -------------------------------------------------------------------------
// 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
// 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
}
// MultiRewardIndex stores reward accumulation information on multiple reward types
type MultiRewardIndex struct {
CollateralType string `json:"collateral_type" yaml:"collateral_type"`