package keeper_test import ( "testing" "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" earntypes "github.com/kava-labs/kava/x/earn/types" "github.com/kava-labs/kava/x/incentive/types" ) type AccumulateEarnRewardsTests struct { unitTester } func (suite *AccumulateEarnRewardsTests) storedTimeEquals(vaultDenom string, expected time.Time) { storedTime, found := suite.keeper.GetEarnRewardAccrualTime(suite.ctx, vaultDenom) suite.Equal(found, expected != time.Time{}, "expected time is %v but time found = %v", expected, found) if found { suite.Equal(expected, storedTime) } else { suite.Empty(storedTime) } } func (suite *AccumulateEarnRewardsTests) storedIndexesEqual(vaultDenom string, expected types.RewardIndexes) { storedIndexes, found := suite.keeper.GetEarnRewardIndexes(suite.ctx, vaultDenom) suite.Equal(found, expected != nil, "expected indexes is %v but indexes found = %v", expected, found) if found { suite.Equal(expected, storedIndexes) } else { suite.Empty(storedIndexes) } } func TestAccumulateEarnRewards(t *testing.T) { suite.Run(t, new(AccumulateEarnRewardsTests)) } func (suite *AccumulateEarnRewardsTests) TestStateUpdatedWhenBlockTimeHasIncreased() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper().addVault(vaultDenom, earntypes.NewVaultShare(vaultDenom, d("1000000"))) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) suite.storeGlobalEarnIndexes(types.MultiRewardIndexes{ { CollateralType: vaultDenom, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, }) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom, previousAccrualTime) newAccrualTime := previousAccrualTime.Add(1 * time.Hour) suite.ctx = suite.ctx.WithBlockTime(newAccrualTime) period := types.NewMultiRewardPeriod( true, vaultDenom, time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // check time and factors suite.storedTimeEquals(vaultDenom, newAccrualTime) suite.storedIndexesEqual(vaultDenom, types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("7.22"), }, { CollateralType: "ukava", RewardFactor: d("3.64"), }, }) } func (suite *AccumulateEarnRewardsTests) TestStateUpdatedWhenBlockTimeHasIncreased_bkava() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(previousAccrualTime) earnKeeper := newFakeEarnKeeper(). addVault(vaultDenom1, earntypes.NewVaultShare(vaultDenom1, d("800000"))). addVault(vaultDenom2, earntypes.NewVaultShare(vaultDenom2, d("200000"))) liquidKeeper := newFakeLiquidKeeper(). addDerivative(suite.ctx, vaultDenom1, i(800000)). addDerivative(suite.ctx, vaultDenom2, i(200000)) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) globalIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom1, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, { CollateralType: vaultDenom2, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(globalIndexes) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom1, previousAccrualTime) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom2, previousAccrualTime) newAccrualTime := previousAccrualTime.Add(1 * time.Hour) suite.ctx = suite.ctx.WithBlockTime(newAccrualTime) rewardPeriod := types.NewMultiRewardPeriod( true, "bkava", // reward period is set for "bkava" to apply to all vaults time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, rewardPeriod) // check time and factors suite.storedTimeEquals(vaultDenom1, newAccrualTime) suite.storedTimeEquals(vaultDenom2, newAccrualTime) // Each vault gets the same ukava per second, assuming shares prices are the same. // The share amount determines how much is actually distributed to the vault. expectedIndexes := types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("7.22"), }, { CollateralType: "ukava", RewardFactor: d("3.64"). // base incentive Add(d("360")), // staking rewards, 10% of total bkava per second }, } suite.storedIndexesEqual(vaultDenom1, expectedIndexes) suite.storedIndexesEqual(vaultDenom2, expectedIndexes) } func (suite *AccumulateEarnRewardsTests) TestStateUpdatedWhenBlockTimeHasIncreased_bkava_partialDeposit() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" vaultDenom1Supply := i(800000) vaultDenom2Supply := i(200000) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(previousAccrualTime) liquidKeeper := newFakeLiquidKeeper(). addDerivative(suite.ctx, vaultDenom1, vaultDenom1Supply). addDerivative(suite.ctx, vaultDenom2, vaultDenom2Supply) vault1Shares := d("700000") vault2Shares := d("100000") // More bkava minted than deposited into earn // Rewards are higher per-share as a result earnKeeper := newFakeEarnKeeper(). addVault(vaultDenom1, earntypes.NewVaultShare(vaultDenom1, vault1Shares)). addVault(vaultDenom2, earntypes.NewVaultShare(vaultDenom2, vault2Shares)) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) globalIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom1, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, { CollateralType: vaultDenom2, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(globalIndexes) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom1, previousAccrualTime) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom2, previousAccrualTime) newAccrualTime := previousAccrualTime.Add(1 * time.Hour) suite.ctx = suite.ctx.WithBlockTime(newAccrualTime) rewardPeriod := types.NewMultiRewardPeriod( true, "bkava", // reward period is set for "bkava" to apply to all vaults time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, rewardPeriod) // check time and factors suite.storedTimeEquals(vaultDenom1, newAccrualTime) suite.storedTimeEquals(vaultDenom2, newAccrualTime) // Slightly increased rewards due to less bkava deposited suite.storedIndexesEqual(vaultDenom1, types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("8.248571428571428571"), }, { CollateralType: "ukava", RewardFactor: d("4.154285714285714286"). // base incentive Add(sdk.NewDecFromInt(vaultDenom1Supply). // staking rewards QuoInt64(10). MulInt64(3600). Quo(vault1Shares), ), }, }) // Much higher rewards per share because only a small amount of bkava is // deposited. The **total** amount of incentives distributed to this vault // is still the same proportional amount. // Fixed amount total rewards distributed to the vault // Fewer shares deposited -> higher rewards per share // 7.2ukava shares per second for 1 hour (started with 0.04) // total rewards claimable = 7.2 * 100000 shares = 720000 ukava // 720000ukava distributed which is 20% of total bkava ukava rewards // total rewards for *all* bkava vaults for 1 hour // = 1000ukava per second * 3600 == 3600000ukava // vaultDenom2 has 20% of the total bkava amount so it should get 20% of 3600000ukava == 720000ukava vault2expectedIndexes := types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("14.42"), }, { CollateralType: "ukava", RewardFactor: d("7.24"). Add(sdk.NewDecFromInt(vaultDenom2Supply). QuoInt64(10). MulInt64(3600). Quo(vault2Shares), ), }, } suite.storedIndexesEqual(vaultDenom2, vault2expectedIndexes) } func (suite *AccumulateEarnRewardsTests) TestStateUnchangedWhenBlockTimeHasNotIncreased() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper().addVault(vaultDenom, earntypes.NewVaultShare(vaultDenom, d("1000000"))) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) previousIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(previousIndexes) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom, previousAccrualTime) suite.ctx = suite.ctx.WithBlockTime(previousAccrualTime) period := types.NewMultiRewardPeriod( true, vaultDenom, time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // check time and factors suite.storedTimeEquals(vaultDenom, previousAccrualTime) expected, f := previousIndexes.Get(vaultDenom) suite.True(f) suite.storedIndexesEqual(vaultDenom, expected) } func (suite *AccumulateEarnRewardsTests) TestStateUnchangedWhenBlockTimeHasNotIncreased_bkava() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(previousAccrualTime) earnKeeper := newFakeEarnKeeper(). addVault(vaultDenom1, earntypes.NewVaultShare(vaultDenom1, d("1000000"))). addVault(vaultDenom2, earntypes.NewVaultShare(vaultDenom2, d("1000000"))) liquidKeeper := newFakeLiquidKeeper(). addDerivative(suite.ctx, vaultDenom1, i(1000000)). addDerivative(suite.ctx, vaultDenom2, i(1000000)) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) previousIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom1, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, { CollateralType: vaultDenom2, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(previousIndexes) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom1, previousAccrualTime) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom2, previousAccrualTime) period := types.NewMultiRewardPeriod( true, "bkava", time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // check time and factors suite.storedTimeEquals(vaultDenom1, previousAccrualTime) suite.storedTimeEquals(vaultDenom2, previousAccrualTime) expected, f := previousIndexes.Get(vaultDenom1) suite.True(f) suite.storedIndexesEqual(vaultDenom1, expected) expected, f = previousIndexes.Get(vaultDenom2) suite.True(f) suite.storedIndexesEqual(vaultDenom2, expected) } func (suite *AccumulateEarnRewardsTests) TestNoAccumulationWhenSourceSharesAreZero() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper() // no vault, so no source shares liquidKeeper := newFakeLiquidKeeper() suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) previousIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(previousIndexes) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom, previousAccrualTime) firstAccrualTime := previousAccrualTime.Add(7 * time.Second) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) period := types.NewMultiRewardPeriod( true, vaultDenom, time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // check time and factors suite.storedTimeEquals(vaultDenom, firstAccrualTime) expected, f := previousIndexes.Get(vaultDenom) suite.True(f) suite.storedIndexesEqual(vaultDenom, expected) } func (suite *AccumulateEarnRewardsTests) TestNoAccumulationWhenSourceSharesAreZero_bkava() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" earnKeeper := newFakeEarnKeeper() // no vault, so no source shares liquidKeeper := newFakeLiquidKeeper() suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) previousIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom1, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, { CollateralType: vaultDenom2, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(previousIndexes) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom1, previousAccrualTime) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom2, previousAccrualTime) firstAccrualTime := previousAccrualTime.Add(7 * time.Second) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) period := types.NewMultiRewardPeriod( true, "bkava", time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), // same denoms as in global indexes ) // TODO: There are no bkava vaults to iterate over, so the accrual times are // not updated suite.keeper.AccumulateEarnRewards(suite.ctx, period) // check time and factors suite.storedTimeEquals(vaultDenom1, firstAccrualTime) suite.storedTimeEquals(vaultDenom2, firstAccrualTime) expected, f := previousIndexes.Get(vaultDenom1) suite.True(f) suite.storedIndexesEqual(vaultDenom1, expected) expected, f = previousIndexes.Get(vaultDenom2) suite.True(f) suite.storedIndexesEqual(vaultDenom2, expected) } func (suite *AccumulateEarnRewardsTests) TestStateAddedWhenStateDoesNotExist() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper().addVault(vaultDenom, earntypes.NewVaultShare(vaultDenom, d("1000000"))) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) period := types.NewMultiRewardPeriod( true, vaultDenom, time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), ) firstAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // After the first accumulation only the current block time should be stored. // The indexes will be empty as no time has passed since the previous block because it didn't exist. suite.storedTimeEquals(vaultDenom, firstAccrualTime) suite.storedIndexesEqual(vaultDenom, nil) secondAccrualTime := firstAccrualTime.Add(10 * time.Second) suite.ctx = suite.ctx.WithBlockTime(secondAccrualTime) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // After the second accumulation both current block time and indexes should be stored. suite.storedTimeEquals(vaultDenom, secondAccrualTime) suite.storedIndexesEqual(vaultDenom, types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.01"), }, }) } func (suite *AccumulateEarnRewardsTests) TestStateAddedWhenStateDoesNotExist_bkava() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" firstAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) earnKeeper := newFakeEarnKeeper(). addVault(vaultDenom1, earntypes.NewVaultShare(vaultDenom1, d("1000000"))). addVault(vaultDenom2, earntypes.NewVaultShare(vaultDenom2, d("1000000"))) liquidKeeper := newFakeLiquidKeeper(). addDerivative(suite.ctx, vaultDenom1, i(1000000)). addDerivative(suite.ctx, vaultDenom2, i(1000000)) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) period := types.NewMultiRewardPeriod( true, "bkava", time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(c("earn", 2000), c("ukava", 1000)), ) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // After the first accumulation only the current block time should be stored. // The indexes will be empty as no time has passed since the previous block because it didn't exist. suite.storedTimeEquals(vaultDenom1, firstAccrualTime) suite.storedTimeEquals(vaultDenom2, firstAccrualTime) suite.storedIndexesEqual(vaultDenom1, nil) suite.storedIndexesEqual(vaultDenom2, nil) secondAccrualTime := firstAccrualTime.Add(10 * time.Second) suite.ctx = suite.ctx.WithBlockTime(secondAccrualTime) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // After the second accumulation both current block time and indexes should be stored. suite.storedTimeEquals(vaultDenom1, secondAccrualTime) suite.storedTimeEquals(vaultDenom2, secondAccrualTime) expectedIndexes := types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.01"), }, { CollateralType: "ukava", // 10% of total bkava for rewards per second for 10 seconds // 1ukava per share per second + regular 0.005ukava incentive rewards RewardFactor: d("1.005"), }, } suite.storedIndexesEqual(vaultDenom1, expectedIndexes) suite.storedIndexesEqual(vaultDenom2, expectedIndexes) } func (suite *AccumulateEarnRewardsTests) TestNoPanicWhenStateDoesNotExist() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper() suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) period := types.NewMultiRewardPeriod( true, vaultDenom, time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(), ) accrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(accrualTime) // Accumulate with no earn shares and no rewards per second will result in no increment to the indexes. // No increment and no previous indexes stored, results in an updated of nil. Setting this in the state panics. // Check there is no panic. suite.NotPanics(func() { suite.keeper.AccumulateEarnRewards(suite.ctx, period) }) suite.storedTimeEquals(vaultDenom, accrualTime) suite.storedIndexesEqual(vaultDenom, nil) } func (suite *AccumulateEarnRewardsTests) TestNoPanicWhenStateDoesNotExist_bkava() { vaultDenom1 := "bkava-meow" vaultDenom2 := "bkava-woof" earnKeeper := newFakeEarnKeeper() liquidKeeper := newFakeLiquidKeeper() suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, liquidKeeper, earnKeeper) period := types.NewMultiRewardPeriod( true, "bkava", time.Unix(0, 0), // ensure the test is within start and end times distantFuture, cs(), ) accrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.ctx = suite.ctx.WithBlockTime(accrualTime) // Accumulate with no earn shares and no rewards per second will result in no increment to the indexes. // No increment and no previous indexes stored, results in an updated of nil. Setting this in the state panics. // Check there is no panic. suite.NotPanics(func() { // This does not update any state, as there are no bkava vaults // to iterate over, denoms are unknown suite.keeper.AccumulateEarnRewards(suite.ctx, period) }) // Times are not stored for vaults with no state suite.storedTimeEquals(vaultDenom1, time.Time{}) suite.storedTimeEquals(vaultDenom2, time.Time{}) suite.storedIndexesEqual(vaultDenom1, nil) suite.storedIndexesEqual(vaultDenom2, nil) } func (suite *AccumulateEarnRewardsTests) TestNoAccumulationWhenBeforeStartTime() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper().addVault(vaultDenom, earntypes.NewVaultShare(vaultDenom, d("1000000"))) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) previousIndexes := types.MultiRewardIndexes{ { CollateralType: vaultDenom, RewardIndexes: types.RewardIndexes{ { CollateralType: "earn", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, } suite.storeGlobalEarnIndexes(previousIndexes) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom, previousAccrualTime) firstAccrualTime := previousAccrualTime.Add(10 * time.Second) period := types.NewMultiRewardPeriod( true, vaultDenom, firstAccrualTime.Add(time.Nanosecond), // start time after accrual time distantFuture, cs(c("earn", 2000), c("ukava", 1000)), ) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) suite.keeper.AccumulateEarnRewards(suite.ctx, period) // The accrual time should be updated, but the indexes unchanged suite.storedTimeEquals(vaultDenom, firstAccrualTime) expectedIndexes, f := previousIndexes.Get(vaultDenom) suite.True(f) suite.storedIndexesEqual(vaultDenom, expectedIndexes) } func (suite *AccumulateEarnRewardsTests) TestPanicWhenCurrentTimeLessThanPrevious() { vaultDenom := "usdx" earnKeeper := newFakeEarnKeeper().addVault(vaultDenom, earntypes.NewVaultShare(vaultDenom, d("1000000"))) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil, nil, nil, earnKeeper) previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC) suite.keeper.SetEarnRewardAccrualTime(suite.ctx, vaultDenom, previousAccrualTime) firstAccrualTime := time.Time{} period := types.NewMultiRewardPeriod( true, vaultDenom, time.Time{}, // start time after accrual time distantFuture, cs(c("earn", 2000), c("ukava", 1000)), ) suite.ctx = suite.ctx.WithBlockTime(firstAccrualTime) suite.Panics(func() { suite.keeper.AccumulateEarnRewards(suite.ctx, period) }) }