mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 16:25:21 +00:00
Add incentive earn tests with real keepers (#1354)
* Update incentive test to use beginblocker instead manual accumulation * Update integration test suite * Add base integration test, wip staking reward calculation * Get actual staking reward amounts from BeginBlocker events, calculate expected indexes * Simplify event parsing * Add initial earn accum test with real keepers * Add the rest of the accum integration tests with real keepers * Check if delegation rewards are zero before transferring * Update staking integration test to use updated methods
This commit is contained in:
parent
17b6b74d75
commit
73bc32a183
48
x/incentive/keeper/keeper_utils_test.go
Normal file
48
x/incentive/keeper/keeper_utils_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/keeper"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestKeeper is a test wrapper for the keeper which contains useful methods for testing
|
||||||
|
type TestKeeper struct {
|
||||||
|
keeper.Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalBorrowIndexes(ctx sdk.Context, indexes types.MultiRewardIndexes) {
|
||||||
|
for _, i := range indexes {
|
||||||
|
keeper.SetHardBorrowRewardIndexes(ctx, i.CollateralType, i.RewardIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalSupplyIndexes(ctx sdk.Context, indexes types.MultiRewardIndexes) {
|
||||||
|
for _, i := range indexes {
|
||||||
|
keeper.SetHardSupplyRewardIndexes(ctx, i.CollateralType, i.RewardIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalDelegatorIndexes(ctx sdk.Context, multiRewardIndexes types.MultiRewardIndexes) {
|
||||||
|
// Hardcoded to use bond denom
|
||||||
|
multiRewardIndex, _ := multiRewardIndexes.GetRewardIndex(types.BondDenom)
|
||||||
|
keeper.SetDelegatorRewardIndexes(ctx, types.BondDenom, multiRewardIndex.RewardIndexes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalSwapIndexes(ctx sdk.Context, indexes types.MultiRewardIndexes) {
|
||||||
|
for _, i := range indexes {
|
||||||
|
keeper.SetSwapRewardIndexes(ctx, i.CollateralType, i.RewardIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalSavingsIndexes(ctx sdk.Context, indexes types.MultiRewardIndexes) {
|
||||||
|
for _, i := range indexes {
|
||||||
|
keeper.SetSavingsRewardIndexes(ctx, i.CollateralType, i.RewardIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keeper TestKeeper) storeGlobalEarnIndexes(ctx sdk.Context, indexes types.MultiRewardIndexes) {
|
||||||
|
for _, i := range indexes {
|
||||||
|
keeper.SetEarnRewardIndexes(ctx, i.CollateralType, i.RewardIndexes)
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,11 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
earntypes "github.com/kava-labs/kava/x/earn/types"
|
earntypes "github.com/kava-labs/kava/x/earn/types"
|
||||||
|
"github.com/kava-labs/kava/x/incentive"
|
||||||
"github.com/kava-labs/kava/x/incentive/testutil"
|
"github.com/kava-labs/kava/x/incentive/testutil"
|
||||||
"github.com/kava-labs/kava/x/incentive/types"
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
||||||
@ -25,20 +28,26 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
WithSimpleAccount(validatorAddr1, cs(c("ukava", 1e12))).
|
WithSimpleAccount(validatorAddr1, cs(c("ukava", 1e12))).
|
||||||
WithSimpleAccount(validatorAddr2, cs(c("ukava", 1e12)))
|
WithSimpleAccount(validatorAddr2, cs(c("ukava", 1e12)))
|
||||||
|
|
||||||
incentBuilder := suite.incentiveBuilder()
|
incentBuilder := suite.incentiveBuilder().
|
||||||
|
WithSimpleEarnRewardPeriod("bkava", cs())
|
||||||
|
|
||||||
savingsBuilder := testutil.NewSavingsGenesisBuilder().
|
savingsBuilder := testutil.NewSavingsGenesisBuilder().
|
||||||
WithSupportedDenoms("bkava")
|
WithSupportedDenoms("bkava")
|
||||||
|
|
||||||
earnBuilder := suite.earnBuilder().
|
earnBuilder := testutil.NewEarnGenesisBuilder().
|
||||||
WithVault(earntypes.AllowedVault{
|
WithAllowedVaults(earntypes.AllowedVault{
|
||||||
Denom: "bkava",
|
Denom: "bkava",
|
||||||
Strategies: earntypes.StrategyTypes{earntypes.STRATEGY_TYPE_SAVINGS},
|
Strategies: earntypes.StrategyTypes{earntypes.STRATEGY_TYPE_SAVINGS},
|
||||||
IsPrivateVault: false,
|
IsPrivateVault: false,
|
||||||
AllowedDepositors: nil,
|
AllowedDepositors: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
suite.SetupWithGenState(authBuilder, incentBuilder, earnBuilder, savingsBuilder)
|
suite.SetupWithGenState(
|
||||||
|
authBuilder,
|
||||||
|
incentBuilder,
|
||||||
|
earnBuilder,
|
||||||
|
savingsBuilder,
|
||||||
|
)
|
||||||
|
|
||||||
// ak := suite.App.GetAccountKeeper()
|
// ak := suite.App.GetAccountKeeper()
|
||||||
// bk := suite.App.GetBankKeeper()
|
// bk := suite.App.GetBankKeeper()
|
||||||
@ -48,6 +57,11 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
dk := suite.App.GetDistrKeeper()
|
dk := suite.App.GetDistrKeeper()
|
||||||
ik := suite.App.GetIncentiveKeeper()
|
ik := suite.App.GetIncentiveKeeper()
|
||||||
|
|
||||||
|
iParams := ik.GetParams(suite.Ctx)
|
||||||
|
period, found := iParams.EarnRewardPeriods.GetMultiRewardPeriod("bkava")
|
||||||
|
suite.Require().True(found)
|
||||||
|
suite.Require().Equal("bkava", period.CollateralType)
|
||||||
|
|
||||||
// Use ukava for mint denom
|
// Use ukava for mint denom
|
||||||
mParams := mk.GetParams(suite.Ctx)
|
mParams := mk.GetParams(suite.Ctx)
|
||||||
mParams.MintDenom = "ukava"
|
mParams.MintDenom = "ukava"
|
||||||
@ -84,13 +98,13 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
// Mint liquid tokens
|
// Mint liquid tokens
|
||||||
err = suite.DeliverMsgMintDerivative(userAddr1, valAddr1, c("ukava", 1e9))
|
_, err = suite.DeliverMsgMintDerivative(userAddr1, valAddr1, c("ukava", 1e9))
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
err = suite.DeliverMsgMintDerivative(userAddr2, valAddr1, c("ukava", 99e9))
|
_, err = suite.DeliverMsgMintDerivative(userAddr2, valAddr1, c("ukava", 99e9))
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
err = suite.DeliverMsgMintDerivative(userAddr2, valAddr2, c("ukava", 99e9))
|
_, err = suite.DeliverMsgMintDerivative(userAddr2, valAddr2, c("ukava", 99e9))
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
// Deposit liquid tokens to earn
|
// Deposit liquid tokens to earn
|
||||||
@ -118,18 +132,29 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
Power: 100,
|
Power: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query for next block to get staking rewards
|
||||||
suite.Ctx = suite.Ctx.
|
suite.Ctx = suite.Ctx.
|
||||||
WithBlockHeight(suite.Ctx.BlockHeight() + 1).
|
WithBlockHeight(suite.Ctx.BlockHeight() + 1).
|
||||||
WithBlockTime(suite.Ctx.BlockTime().Add(1 * time.Hour))
|
WithBlockTime(suite.Ctx.BlockTime().Add(7 * time.Second))
|
||||||
// Accumulate some staking rewards
|
|
||||||
_ = suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{
|
// Mint tokens
|
||||||
LastCommitInfo: abci.LastCommitInfo{
|
mint.BeginBlocker(
|
||||||
Votes: []abci.VoteInfo{{
|
suite.Ctx,
|
||||||
Validator: val,
|
suite.App.GetMintKeeper(),
|
||||||
SignedLastBlock: true,
|
)
|
||||||
}},
|
// Distribute to validators, block needs votes
|
||||||
|
distribution.BeginBlocker(
|
||||||
|
suite.Ctx,
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{{
|
||||||
|
Validator: val,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
dk,
|
||||||
|
)
|
||||||
|
|
||||||
liquidMacc := suite.App.GetAccountKeeper().GetModuleAccount(suite.Ctx, liquidtypes.ModuleAccountName)
|
liquidMacc := suite.App.GetAccountKeeper().GetModuleAccount(suite.Ctx, liquidtypes.ModuleAccountName)
|
||||||
delegation, found := sk.GetDelegation(suite.Ctx, liquidMacc.GetAddress(), valAddr1)
|
delegation, found := sk.GetDelegation(suite.Ctx, liquidMacc.GetAddress(), valAddr1)
|
||||||
@ -137,18 +162,25 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
|
|
||||||
// Get amount of rewards
|
// Get amount of rewards
|
||||||
endingPeriod := dk.IncrementValidatorPeriod(suite.Ctx, validator1)
|
endingPeriod := dk.IncrementValidatorPeriod(suite.Ctx, validator1)
|
||||||
delegationRewards := dk.CalculateDelegationRewards(suite.Ctx, validator1, delegation, endingPeriod)
|
|
||||||
|
|
||||||
// Accumulate rewards - claim rewards
|
// Zero rewards since this block is the same as the block it was last claimed
|
||||||
rewardPeriod := types.NewMultiRewardPeriod(
|
|
||||||
true,
|
// This needs to run **after** staking rewards are minted/distributed in
|
||||||
"bkava", // reward period is set for "bkava" to apply to all vaults
|
// x/mint + x/distribution but **before** the x/incentive BeginBlocker.
|
||||||
time.Unix(0, 0), // ensure the test is within start and end times
|
|
||||||
distantFuture,
|
// Order of operations:
|
||||||
cs(), // no incentives, so only the staking rewards are distributed
|
// 1. x/mint + x/distribution BeginBlocker
|
||||||
|
// 2. CalculateDelegationRewards
|
||||||
|
// 3. x/incentive BeginBlocker to claim staking rewards
|
||||||
|
delegationRewards := dk.CalculateDelegationRewards(suite.Ctx, validator1, delegation, endingPeriod)
|
||||||
|
suite.Require().False(delegationRewards.IsZero(), "expected non-zero delegation rewards")
|
||||||
|
|
||||||
|
// Claim staking rewards via incentive.
|
||||||
|
// Block height was updated earlier.
|
||||||
|
incentive.BeginBlocker(
|
||||||
|
suite.Ctx,
|
||||||
|
ik,
|
||||||
)
|
)
|
||||||
err = ik.AccumulateEarnRewards(suite.Ctx, rewardPeriod)
|
|
||||||
suite.Require().NoError(err)
|
|
||||||
|
|
||||||
preClaimBal1 := suite.GetBalance(userAddr1)
|
preClaimBal1 := suite.GetBalance(userAddr1)
|
||||||
preClaimBal2 := suite.GetBalance(userAddr2)
|
preClaimBal2 := suite.GetBalance(userAddr2)
|
||||||
@ -171,15 +203,15 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
// User 2 gets 99% of rewards
|
// User 2 gets 99% of rewards
|
||||||
stakingRewards1 := delegationRewards.
|
stakingRewards1 := delegationRewards.
|
||||||
AmountOf("ukava").
|
AmountOf("ukava").
|
||||||
QuoInt64(100).
|
Quo(sdk.NewDec(100)).
|
||||||
RoundInt()
|
RoundInt()
|
||||||
suite.BalanceEquals(userAddr1, preClaimBal1.Add(sdk.NewCoin("ukava", stakingRewards1)))
|
suite.BalanceEquals(userAddr1, preClaimBal1.Add(sdk.NewCoin("ukava", stakingRewards1)))
|
||||||
|
|
||||||
// Total * 99 / 100
|
// Total * 99 / 100
|
||||||
stakingRewards2 := delegationRewards.
|
stakingRewards2 := delegationRewards.
|
||||||
AmountOf("ukava").
|
AmountOf("ukava").
|
||||||
MulInt64(99).
|
Mul(sdk.NewDec(99)).
|
||||||
QuoInt64(100).
|
Quo(sdk.NewDec(100)).
|
||||||
TruncateInt()
|
TruncateInt()
|
||||||
suite.BalanceEquals(userAddr2, preClaimBal2.Add(sdk.NewCoin("ukava", stakingRewards2)))
|
suite.BalanceEquals(userAddr2, preClaimBal2.Add(sdk.NewCoin("ukava", stakingRewards2)))
|
||||||
|
|
||||||
@ -189,9 +221,3 @@ func (suite *HandlerTestSuite) TestEarnLiquidClaim() {
|
|||||||
suite.EarnRewardEquals(userAddr1, cs())
|
suite.EarnRewardEquals(userAddr1, cs())
|
||||||
suite.EarnRewardEquals(userAddr2, cs())
|
suite.EarnRewardEquals(userAddr2, cs())
|
||||||
}
|
}
|
||||||
|
|
||||||
// earnBuilder returns a new earn genesis builder with a genesis time and multipliers set
|
|
||||||
func (suite *HandlerTestSuite) earnBuilder() testutil.EarnGenesisBuilder {
|
|
||||||
return testutil.NewEarnGenesisBuilder().
|
|
||||||
WithGenesisTime(suite.genesisTime)
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@ -46,11 +45,7 @@ func (suite *HandlerTestSuite) SetupApp() {
|
|||||||
suite.Ctx = suite.App.NewContext(true, tmproto.Header{Height: 1, Time: suite.genesisTime})
|
suite.Ctx = suite.App.NewContext(true, tmproto.Header{Height: 1, Time: suite.genesisTime})
|
||||||
}
|
}
|
||||||
|
|
||||||
type genesisBuilder interface {
|
func (suite *HandlerTestSuite) SetupWithGenState(builders ...testutil.GenesisBuilder) {
|
||||||
BuildMarshalled(cdc codec.JSONCodec) app.GenesisState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *HandlerTestSuite) SetupWithGenState(builders ...genesisBuilder) {
|
|
||||||
suite.SetupApp()
|
suite.SetupApp()
|
||||||
|
|
||||||
builtGenStates := []app.GenesisState{
|
builtGenStates := []app.GenesisState{
|
||||||
|
@ -56,8 +56,8 @@ func (suite *BorrowIntegrationTests) TestSingleUserAccumulatesRewardsAfterSyncin
|
|||||||
WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6))) // only borrow rewards
|
WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6))) // only borrow rewards
|
||||||
|
|
||||||
suite.SetApp()
|
suite.SetApp()
|
||||||
|
suite.WithGenesisTime(suite.genesisTime)
|
||||||
suite.StartChain(
|
suite.StartChain(
|
||||||
suite.genesisTime,
|
|
||||||
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
||||||
NewHardGenStateMulti(suite.genesisTime).BuildMarshalled(suite.App.AppCodec()),
|
NewHardGenStateMulti(suite.genesisTime).BuildMarshalled(suite.App.AppCodec()),
|
||||||
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
||||||
|
657
x/incentive/keeper/rewards_earn_accum_integration_test.go
Normal file
657
x/incentive/keeper/rewards_earn_accum_integration_test.go
Normal file
@ -0,0 +1,657 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
earntypes "github.com/kava-labs/kava/x/earn/types"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/testutil"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccumulateEarnRewardsIntegrationTests struct {
|
||||||
|
testutil.IntegrationTester
|
||||||
|
|
||||||
|
keeper TestKeeper
|
||||||
|
userAddrs []sdk.AccAddress
|
||||||
|
valAddrs []sdk.ValAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccumulateEarnRewardsIntegrationTests(t *testing.T) {
|
||||||
|
suite.Run(t, new(AccumulateEarnRewardsIntegrationTests))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) SetupTest() {
|
||||||
|
suite.IntegrationTester.SetupTest()
|
||||||
|
|
||||||
|
suite.keeper = TestKeeper{
|
||||||
|
Keeper: suite.App.GetIncentiveKeeper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||||
|
suite.userAddrs = addrs[0:2]
|
||||||
|
suite.valAddrs = []sdk.ValAddress{
|
||||||
|
sdk.ValAddress(addrs[2]),
|
||||||
|
sdk.ValAddress(addrs[3]),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup app with test state
|
||||||
|
authBuilder := app.NewAuthBankGenesisBuilder().
|
||||||
|
WithSimpleAccount(addrs[0], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[1], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[2], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[3], cs(c("ukava", 1e12)))
|
||||||
|
|
||||||
|
incentiveBuilder := testutil.NewIncentiveGenesisBuilder().
|
||||||
|
WithGenesisTime(suite.GenesisTime).
|
||||||
|
WithSimpleEarnRewardPeriod("bkava", cs())
|
||||||
|
|
||||||
|
savingsBuilder := testutil.NewSavingsGenesisBuilder().
|
||||||
|
WithSupportedDenoms("bkava")
|
||||||
|
|
||||||
|
earnBuilder := testutil.NewEarnGenesisBuilder().
|
||||||
|
WithAllowedVaults(earntypes.AllowedVault{
|
||||||
|
Denom: "bkava",
|
||||||
|
Strategies: earntypes.StrategyTypes{earntypes.STRATEGY_TYPE_SAVINGS},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
})
|
||||||
|
|
||||||
|
stakingBuilder := testutil.NewStakingGenesisBuilder()
|
||||||
|
|
||||||
|
mintBuilder := testutil.NewMintGenesisBuilder().
|
||||||
|
WithInflationMax(sdk.OneDec()).
|
||||||
|
WithInflationMin(sdk.OneDec()).
|
||||||
|
WithMinter(sdk.OneDec(), sdk.ZeroDec()).
|
||||||
|
WithMintDenom("ukava")
|
||||||
|
|
||||||
|
suite.StartChainWithBuilders(
|
||||||
|
authBuilder,
|
||||||
|
incentiveBuilder,
|
||||||
|
savingsBuilder,
|
||||||
|
earnBuilder,
|
||||||
|
stakingBuilder,
|
||||||
|
mintBuilder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestStateUpdatedWhenBlockTimeHasIncreased() {
|
||||||
|
suite.AddIncentiveEarnMultiRewardPeriod(
|
||||||
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 800000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 200000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], derivative0, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[1], derivative1, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
globalIndexes := types.MultiRewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: derivative0.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: derivative1.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.keeper.storeGlobalEarnIndexes(suite.Ctx, globalIndexes)
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
val0 := suite.GetAbciValidator(suite.valAddrs[0])
|
||||||
|
val1 := suite.GetAbciValidator(suite.valAddrs[1])
|
||||||
|
|
||||||
|
// Mint tokens, distribute to validators, claim staking rewards
|
||||||
|
// 1 hour later
|
||||||
|
_, resBeginBlock := suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{
|
||||||
|
{
|
||||||
|
Validator: val0,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Validator: val1,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
validatorRewards, _ := suite.GetBeginBlockClaimedStakingRewards(resBeginBlock)
|
||||||
|
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[1].String(), "there should be claim events for validator 0")
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[0].String(), "there should be claim events for validator 1")
|
||||||
|
|
||||||
|
// check time and factors
|
||||||
|
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
stakingRewardIndexes0 := validatorRewards[suite.valAddrs[0].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative0.Amount.ToDec())
|
||||||
|
|
||||||
|
stakingRewardIndexes1 := validatorRewards[suite.valAddrs[1].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative1.Amount.ToDec())
|
||||||
|
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("7.22"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("3.64").Add(stakingRewardIndexes0),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("7.22"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("3.64").Add(stakingRewardIndexes1),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestStateUpdatedWhenBlockTimeHasIncreased_partialDeposit() {
|
||||||
|
suite.AddIncentiveEarnMultiRewardPeriod(
|
||||||
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 800000bkava0 minted, 700000 deposited
|
||||||
|
// 200000bkava1 minted, 100000 deposited
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 800000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 200000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
depositAmount0 := c(derivative0.Denom, 700000)
|
||||||
|
depositAmount1 := c(derivative1.Denom, 100000)
|
||||||
|
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], depositAmount0, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[1], depositAmount1, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
globalIndexes := types.MultiRewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: derivative0.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: derivative1.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.keeper.storeGlobalEarnIndexes(suite.Ctx, globalIndexes)
|
||||||
|
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
val0 := suite.GetAbciValidator(suite.valAddrs[0])
|
||||||
|
val1 := suite.GetAbciValidator(suite.valAddrs[1])
|
||||||
|
|
||||||
|
// Mint tokens, distribute to validators, claim staking rewards
|
||||||
|
// 1 hour later
|
||||||
|
_, resBeginBlock := suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{
|
||||||
|
{
|
||||||
|
Validator: val0,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Validator: val1,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
validatorRewards, _ := suite.GetBeginBlockClaimedStakingRewards(resBeginBlock)
|
||||||
|
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[1].String(), "there should be claim events for validator 0")
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[0].String(), "there should be claim events for validator 1")
|
||||||
|
|
||||||
|
// check time and factors
|
||||||
|
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
// Divided by deposit amounts, not bank supply amounts
|
||||||
|
stakingRewardIndexes0 := validatorRewards[suite.valAddrs[0].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(depositAmount0.Amount.ToDec())
|
||||||
|
|
||||||
|
stakingRewardIndexes1 := validatorRewards[suite.valAddrs[1].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(depositAmount1.Amount.ToDec())
|
||||||
|
|
||||||
|
// Slightly increased rewards due to less bkava deposited
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("8.248571428571428571"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("4.154285714285714285").Add(stakingRewardIndexes0),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("14.42"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("7.24").Add(stakingRewardIndexes1),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestStateUnchangedWhenBlockTimeHasNotIncreased() {
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], derivative0, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[1], derivative1, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
previousIndexes := types.MultiRewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: derivative0.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: derivative1.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
suite.keeper.storeGlobalEarnIndexes(suite.Ctx, previousIndexes)
|
||||||
|
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
// Must manually accumulate rewards as BeginBlockers only run when the block time increases
|
||||||
|
// This does not run any x/mint or x/distribution BeginBlockers
|
||||||
|
suite.keeper.AccumulateEarnRewards(suite.Ctx, period)
|
||||||
|
|
||||||
|
// check time and factors
|
||||||
|
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
expected, f := previousIndexes.Get(derivative0.Denom)
|
||||||
|
suite.True(f)
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, expected)
|
||||||
|
|
||||||
|
expected, f = previousIndexes.Get(derivative1.Denom)
|
||||||
|
suite.True(f)
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestNoAccumulationWhenSourceSharesAreZero() {
|
||||||
|
suite.AddIncentiveEarnMultiRewardPeriod(
|
||||||
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// No earn deposits
|
||||||
|
|
||||||
|
previousIndexes := types.MultiRewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: derivative0.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: derivative1.Denom,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("0.02"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: d("0.04"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
suite.keeper.storeGlobalEarnIndexes(suite.Ctx, previousIndexes)
|
||||||
|
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
val0 := suite.GetAbciValidator(suite.valAddrs[0])
|
||||||
|
val1 := suite.GetAbciValidator(suite.valAddrs[1])
|
||||||
|
|
||||||
|
// Mint tokens, distribute to validators, claim staking rewards
|
||||||
|
// 1 hour later
|
||||||
|
_, _ = suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{
|
||||||
|
{
|
||||||
|
Validator: val0,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Validator: val1,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// check time and factors
|
||||||
|
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
expected, f := previousIndexes.Get(derivative0.Denom)
|
||||||
|
suite.True(f)
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, expected)
|
||||||
|
|
||||||
|
expected, f = previousIndexes.Get(derivative1.Denom)
|
||||||
|
suite.True(f)
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestStateAddedWhenStateDoesNotExist() {
|
||||||
|
suite.AddIncentiveEarnMultiRewardPeriod(
|
||||||
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], derivative0, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[1], derivative1, earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
val0 := suite.GetAbciValidator(suite.valAddrs[0])
|
||||||
|
val1 := suite.GetAbciValidator(suite.valAddrs[1])
|
||||||
|
|
||||||
|
_, resBeginBlock := suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{
|
||||||
|
{
|
||||||
|
Validator: val0,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Validator: val1,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// After the second accumulation both current block time and indexes should be stored.
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
validatorRewards0, _ := suite.GetBeginBlockClaimedStakingRewards(resBeginBlock)
|
||||||
|
|
||||||
|
firstStakingRewardIndexes0 := validatorRewards0[suite.valAddrs[0].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative0.Amount.ToDec())
|
||||||
|
|
||||||
|
firstStakingRewardIndexes1 := validatorRewards0[suite.valAddrs[1].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative1.Amount.ToDec())
|
||||||
|
|
||||||
|
// 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.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
// First accumulation can have staking rewards, but no other rewards
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: firstStakingRewardIndexes0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: firstStakingRewardIndexes1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, resBeginBlock = suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{
|
||||||
|
{
|
||||||
|
Validator: val0,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Validator: val1,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// After the second accumulation both current block time and indexes should be stored.
|
||||||
|
suite.StoredEarnTimeEquals(derivative0.Denom, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
validatorRewards1, _ := suite.GetBeginBlockClaimedStakingRewards(resBeginBlock)
|
||||||
|
|
||||||
|
secondStakingRewardIndexes0 := validatorRewards1[suite.valAddrs[0].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative0.Amount.ToDec())
|
||||||
|
|
||||||
|
secondStakingRewardIndexes1 := validatorRewards1[suite.valAddrs[1].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(derivative1.Amount.ToDec())
|
||||||
|
|
||||||
|
// Second accumulation has both staking rewards and incentive rewards
|
||||||
|
// ukava incentive rewards: 3600 * 1000 / (2 * 1000000) == 1.8
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
// Incentive rewards + both staking rewards
|
||||||
|
RewardFactor: d("1.8").Add(firstStakingRewardIndexes0).Add(secondStakingRewardIndexes0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("3.6"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
// Incentive rewards + both staking rewards
|
||||||
|
RewardFactor: d("1.8").Add(firstStakingRewardIndexes1).Add(secondStakingRewardIndexes1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: "earn",
|
||||||
|
RewardFactor: d("3.6"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccumulateEarnRewardsIntegrationTests) TestNoPanicWhenStateDoesNotExist() {
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[1], suite.valAddrs[1], c("ukava", 1000000))
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
period := types.NewMultiRewardPeriod(
|
||||||
|
true,
|
||||||
|
"bkava",
|
||||||
|
time.Unix(0, 0), // ensure the test is within start and end times
|
||||||
|
distantFuture,
|
||||||
|
cs(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.StoredEarnTimeEquals(derivative0.Denom, time.Time{})
|
||||||
|
suite.StoredEarnTimeEquals(derivative1.Denom, time.Time{})
|
||||||
|
suite.StoredEarnIndexesEqual(derivative0.Denom, nil)
|
||||||
|
suite.StoredEarnIndexesEqual(derivative1.Denom, nil)
|
||||||
|
}
|
193
x/incentive/keeper/rewards_earn_staking_integration_test.go
Normal file
193
x/incentive/keeper/rewards_earn_staking_integration_test.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
earntypes "github.com/kava-labs/kava/x/earn/types"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/testutil"
|
||||||
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EarnStakingRewardsIntegrationTestSuite struct {
|
||||||
|
testutil.IntegrationTester
|
||||||
|
|
||||||
|
keeper TestKeeper
|
||||||
|
userAddrs []sdk.AccAddress
|
||||||
|
valAddrs []sdk.ValAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarnStakingRewardsIntegrationTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(EarnStakingRewardsIntegrationTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *EarnStakingRewardsIntegrationTestSuite) SetupTest() {
|
||||||
|
suite.IntegrationTester.SetupTest()
|
||||||
|
|
||||||
|
suite.keeper = TestKeeper{
|
||||||
|
Keeper: suite.App.GetIncentiveKeeper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||||
|
suite.userAddrs = addrs[0:2]
|
||||||
|
suite.valAddrs = []sdk.ValAddress{
|
||||||
|
sdk.ValAddress(addrs[2]),
|
||||||
|
sdk.ValAddress(addrs[3]),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup app with test state
|
||||||
|
authBuilder := app.NewAuthBankGenesisBuilder().
|
||||||
|
WithSimpleAccount(addrs[0], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[1], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[2], cs(c("ukava", 1e12))).
|
||||||
|
WithSimpleAccount(addrs[3], cs(c("ukava", 1e12)))
|
||||||
|
|
||||||
|
incentiveBuilder := testutil.NewIncentiveGenesisBuilder().
|
||||||
|
WithGenesisTime(suite.GenesisTime).
|
||||||
|
WithSimpleEarnRewardPeriod("bkava", cs())
|
||||||
|
|
||||||
|
savingsBuilder := testutil.NewSavingsGenesisBuilder().
|
||||||
|
WithSupportedDenoms("bkava")
|
||||||
|
|
||||||
|
earnBuilder := testutil.NewEarnGenesisBuilder().
|
||||||
|
WithAllowedVaults(earntypes.AllowedVault{
|
||||||
|
Denom: "bkava",
|
||||||
|
Strategies: earntypes.StrategyTypes{earntypes.STRATEGY_TYPE_SAVINGS},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
})
|
||||||
|
|
||||||
|
stakingBuilder := testutil.NewStakingGenesisBuilder()
|
||||||
|
|
||||||
|
mintBuilder := testutil.NewMintGenesisBuilder().
|
||||||
|
WithInflationMax(sdk.OneDec()).
|
||||||
|
WithInflationMin(sdk.OneDec()).
|
||||||
|
WithMinter(sdk.OneDec(), sdk.ZeroDec()).
|
||||||
|
WithMintDenom("ukava")
|
||||||
|
|
||||||
|
suite.StartChainWithBuilders(
|
||||||
|
authBuilder,
|
||||||
|
incentiveBuilder,
|
||||||
|
savingsBuilder,
|
||||||
|
earnBuilder,
|
||||||
|
stakingBuilder,
|
||||||
|
mintBuilder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *EarnStakingRewardsIntegrationTestSuite) TestStakingRewardsDistributed() {
|
||||||
|
// derivative 1: 8 total staked, 7 to earn, 1 not in earn
|
||||||
|
// derivative 2: 2 total staked, 1 to earn, 1 not in earn
|
||||||
|
userMintAmount0 := c("ukava", 8e9)
|
||||||
|
userMintAmount1 := c("ukava", 2e9)
|
||||||
|
|
||||||
|
userDepositAmount0 := i(7e9)
|
||||||
|
userDepositAmount1 := i(1e9)
|
||||||
|
|
||||||
|
// Create two validators
|
||||||
|
derivative0, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[0], userMintAmount0)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
derivative1, err := suite.MintLiquidAnyValAddr(suite.userAddrs[0], suite.valAddrs[1], userMintAmount1)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], sdk.NewCoin(derivative0.Denom, userDepositAmount0), earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
err = suite.DeliverEarnMsgDeposit(suite.userAddrs[0], sdk.NewCoin(derivative1.Denom, userDepositAmount1), earntypes.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// Get derivative denoms
|
||||||
|
lq := suite.App.GetLiquidKeeper()
|
||||||
|
vaultDenom1 := lq.GetLiquidStakingTokenDenom(suite.valAddrs[0])
|
||||||
|
vaultDenom2 := lq.GetLiquidStakingTokenDenom(suite.valAddrs[1])
|
||||||
|
|
||||||
|
previousAccrualTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
suite.Ctx = suite.Ctx.WithBlockTime(previousAccrualTime)
|
||||||
|
|
||||||
|
initialVault1RewardFactor := d("0.04")
|
||||||
|
initialVault2RewardFactor := d("0.04")
|
||||||
|
|
||||||
|
globalIndexes := types.MultiRewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: vaultDenom1,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: initialVault1RewardFactor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CollateralType: vaultDenom2,
|
||||||
|
RewardIndexes: types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: initialVault2RewardFactor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.keeper.storeGlobalEarnIndexes(suite.Ctx, globalIndexes)
|
||||||
|
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, vaultDenom1, suite.Ctx.BlockTime())
|
||||||
|
suite.keeper.SetEarnRewardAccrualTime(suite.Ctx, vaultDenom2, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
val := suite.GetAbciValidator(suite.valAddrs[0])
|
||||||
|
|
||||||
|
// Mint tokens, distribute to validators, claim staking rewards
|
||||||
|
// 1 hour later
|
||||||
|
_, resBeginBlock := suite.NextBlockAfterWithReq(
|
||||||
|
1*time.Hour,
|
||||||
|
abci.RequestEndBlock{},
|
||||||
|
abci.RequestBeginBlock{
|
||||||
|
LastCommitInfo: abci.LastCommitInfo{
|
||||||
|
Votes: []abci.VoteInfo{{
|
||||||
|
Validator: val,
|
||||||
|
SignedLastBlock: true,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// check time and factors
|
||||||
|
suite.StoredEarnTimeEquals(vaultDenom1, suite.Ctx.BlockTime())
|
||||||
|
suite.StoredEarnTimeEquals(vaultDenom2, suite.Ctx.BlockTime())
|
||||||
|
|
||||||
|
validatorRewards, _ := suite.GetBeginBlockClaimedStakingRewards(resBeginBlock)
|
||||||
|
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[0].String(), "there should be claim events for validator 1")
|
||||||
|
suite.Require().Contains(validatorRewards, suite.valAddrs[1].String(), "there should be claim events for validator 2")
|
||||||
|
|
||||||
|
// Total staking rewards / total source shares (**deposited in earn** not total minted)
|
||||||
|
// types.RewardIndexes.Quo() uses Dec.Quo() which uses bankers rounding.
|
||||||
|
// So we need to use Dec.Quo() to also round vs Dec.QuoInt() which truncates
|
||||||
|
expectedIndexes1 := validatorRewards[suite.valAddrs[0].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(userDepositAmount0.ToDec())
|
||||||
|
|
||||||
|
expectedIndexes2 := validatorRewards[suite.valAddrs[1].String()].
|
||||||
|
AmountOf("ukava").
|
||||||
|
ToDec().
|
||||||
|
Quo(userDepositAmount1.ToDec())
|
||||||
|
|
||||||
|
// Only contains staking rewards
|
||||||
|
suite.StoredEarnIndexesEqual(vaultDenom1, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: initialVault1RewardFactor.Add(expectedIndexes1),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.StoredEarnIndexesEqual(vaultDenom2, types.RewardIndexes{
|
||||||
|
{
|
||||||
|
CollateralType: "ukava",
|
||||||
|
RewardFactor: initialVault2RewardFactor.Add(expectedIndexes2),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -57,8 +57,8 @@ func (suite *SupplyIntegrationTests) TestSingleUserAccumulatesRewardsAfterSyncin
|
|||||||
|
|
||||||
suite.SetApp()
|
suite.SetApp()
|
||||||
|
|
||||||
|
suite.WithGenesisTime(suite.genesisTime)
|
||||||
suite.StartChain(
|
suite.StartChain(
|
||||||
suite.genesisTime,
|
|
||||||
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
||||||
NewHardGenStateMulti(suite.genesisTime).BuildMarshalled(suite.App.AppCodec()),
|
NewHardGenStateMulti(suite.genesisTime).BuildMarshalled(suite.App.AppCodec()),
|
||||||
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
||||||
|
@ -63,8 +63,8 @@ func (suite *USDXIntegrationTests) TestSingleUserAccumulatesRewardsAfterSyncing(
|
|||||||
WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
|
WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
|
||||||
|
|
||||||
suite.SetApp()
|
suite.SetApp()
|
||||||
|
suite.WithGenesisTime(suite.genesisTime)
|
||||||
suite.StartChain(
|
suite.StartChain(
|
||||||
suite.genesisTime,
|
|
||||||
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
||||||
NewCDPGenStateMulti(suite.App.AppCodec()),
|
NewCDPGenStateMulti(suite.App.AppCodec()),
|
||||||
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
authBulder.BuildMarshalled(suite.App.AppCodec()),
|
||||||
@ -121,8 +121,8 @@ func (suite *USDXIntegrationTests) TestSingleUserAccumulatesRewardsWithoutSyncin
|
|||||||
WithSimpleUSDXRewardPeriod(collateralType, c(types.USDXMintingRewardDenom, 1e6))
|
WithSimpleUSDXRewardPeriod(collateralType, c(types.USDXMintingRewardDenom, 1e6))
|
||||||
|
|
||||||
suite.SetApp()
|
suite.SetApp()
|
||||||
|
suite.WithGenesisTime(suite.genesisTime)
|
||||||
suite.StartChain(
|
suite.StartChain(
|
||||||
suite.genesisTime,
|
|
||||||
authBuilder.BuildMarshalled(suite.App.AppCodec()),
|
authBuilder.BuildMarshalled(suite.App.AppCodec()),
|
||||||
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
||||||
NewCDPGenStateMulti(suite.App.AppCodec()),
|
NewCDPGenStateMulti(suite.App.AppCodec()),
|
||||||
@ -167,8 +167,8 @@ func (suite *USDXIntegrationTests) TestReinstatingRewardParamsDoesNotTriggerOver
|
|||||||
WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
|
WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
|
||||||
|
|
||||||
suite.SetApp()
|
suite.SetApp()
|
||||||
|
suite.WithGenesisTime(suite.genesisTime)
|
||||||
suite.StartChain(
|
suite.StartChain(
|
||||||
suite.genesisTime,
|
|
||||||
authBuilder.BuildMarshalled(suite.App.AppCodec()),
|
authBuilder.BuildMarshalled(suite.App.AppCodec()),
|
||||||
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
NewPricefeedGenStateMultiFromTime(suite.App.AppCodec(), suite.genesisTime),
|
||||||
NewCDPGenStateMulti(suite.App.AppCodec()),
|
NewCDPGenStateMulti(suite.App.AppCodec()),
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/app"
|
"github.com/kava-labs/kava/app"
|
||||||
earntypes "github.com/kava-labs/kava/x/earn/types"
|
|
||||||
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
||||||
"github.com/kava-labs/kava/x/incentive/types"
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
savingstypes "github.com/kava-labs/kava/x/savings/types"
|
savingstypes "github.com/kava-labs/kava/x/savings/types"
|
||||||
@ -17,6 +16,10 @@ const (
|
|||||||
oneYear time.Duration = time.Hour * 24 * 365
|
oneYear time.Duration = time.Hour * 24 * 365
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type GenesisBuilder interface {
|
||||||
|
BuildMarshalled(cdc codec.JSONCodec) app.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
// IncentiveGenesisBuilder is a tool for creating an incentive genesis state.
|
// IncentiveGenesisBuilder is a tool for creating an incentive genesis state.
|
||||||
// Helper methods add values onto a default genesis state.
|
// Helper methods add values onto a default genesis state.
|
||||||
// All methods are immutable and return updated copies of the builder.
|
// All methods are immutable and return updated copies of the builder.
|
||||||
@ -302,42 +305,6 @@ func (builder IncentiveGenesisBuilder) WithSimpleSavingsRewardPeriod(ctype strin
|
|||||||
return builder.WithInitializedSavingsRewardPeriod(builder.simpleRewardPeriod(ctype, rewardsPerSecond))
|
return builder.WithInitializedSavingsRewardPeriod(builder.simpleRewardPeriod(ctype, rewardsPerSecond))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EarnGenesisBuilder is a tool for creating a earn genesis state.
|
|
||||||
// Helper methods add values onto a default genesis state.
|
|
||||||
// All methods are immutable and return updated copies of the builder.
|
|
||||||
type EarnGenesisBuilder struct {
|
|
||||||
earntypes.GenesisState
|
|
||||||
genesisTime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEarnGenesisBuilder() EarnGenesisBuilder {
|
|
||||||
return EarnGenesisBuilder{
|
|
||||||
GenesisState: earntypes.DefaultGenesisState(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder EarnGenesisBuilder) Build() earntypes.GenesisState {
|
|
||||||
return builder.GenesisState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder EarnGenesisBuilder) BuildMarshalled(cdc codec.JSONCodec) app.GenesisState {
|
|
||||||
built := builder.Build()
|
|
||||||
|
|
||||||
return app.GenesisState{
|
|
||||||
earntypes.ModuleName: cdc.MustMarshalJSON(&built),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder EarnGenesisBuilder) WithGenesisTime(genTime time.Time) EarnGenesisBuilder {
|
|
||||||
builder.genesisTime = genTime
|
|
||||||
return builder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder EarnGenesisBuilder) WithVault(vault earntypes.AllowedVault) EarnGenesisBuilder {
|
|
||||||
builder.Params.AllowedVaults = append(builder.Params.AllowedVaults, vault)
|
|
||||||
return builder
|
|
||||||
}
|
|
||||||
|
|
||||||
// SavingsGenesisBuilder is a tool for creating a savings genesis state.
|
// SavingsGenesisBuilder is a tool for creating a savings genesis state.
|
||||||
// Helper methods add values onto a default genesis state.
|
// Helper methods add values onto a default genesis state.
|
||||||
// All methods are immutable and return updated copies of the builder.
|
// All methods are immutable and return updated copies of the builder.
|
||||||
|
40
x/incentive/testutil/earn_builder.go
Normal file
40
x/incentive/testutil/earn_builder.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
|
||||||
|
earntypes "github.com/kava-labs/kava/x/earn/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EarnGenesisBuilder is a tool for creating a earn genesis state.
|
||||||
|
// Helper methods add values onto a default genesis state.
|
||||||
|
// All methods are immutable and return updated copies of the builder.
|
||||||
|
type EarnGenesisBuilder struct {
|
||||||
|
earntypes.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ GenesisBuilder = (*EarnGenesisBuilder)(nil)
|
||||||
|
|
||||||
|
func NewEarnGenesisBuilder() EarnGenesisBuilder {
|
||||||
|
return EarnGenesisBuilder{
|
||||||
|
GenesisState: earntypes.DefaultGenesisState(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder EarnGenesisBuilder) Build() earntypes.GenesisState {
|
||||||
|
return builder.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder EarnGenesisBuilder) BuildMarshalled(cdc codec.JSONCodec) app.GenesisState {
|
||||||
|
built := builder.Build()
|
||||||
|
|
||||||
|
return app.GenesisState{
|
||||||
|
earntypes.ModuleName: cdc.MustMarshalJSON(&built),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder EarnGenesisBuilder) WithAllowedVaults(vault ...earntypes.AllowedVault) EarnGenesisBuilder {
|
||||||
|
builder.Params.AllowedVaults = append(builder.Params.AllowedVaults, vault...)
|
||||||
|
return builder
|
||||||
|
}
|
@ -10,6 +10,7 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||||
|
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||||
proposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
|
proposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
|
||||||
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
@ -30,6 +31,8 @@ import (
|
|||||||
"github.com/kava-labs/kava/x/incentive/types"
|
"github.com/kava-labs/kava/x/incentive/types"
|
||||||
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
|
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
|
||||||
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
||||||
|
routerkeeper "github.com/kava-labs/kava/x/router/keeper"
|
||||||
|
routertypes "github.com/kava-labs/kava/x/router/types"
|
||||||
swapkeeper "github.com/kava-labs/kava/x/swap/keeper"
|
swapkeeper "github.com/kava-labs/kava/x/swap/keeper"
|
||||||
swaptypes "github.com/kava-labs/kava/x/swap/types"
|
swaptypes "github.com/kava-labs/kava/x/swap/types"
|
||||||
)
|
)
|
||||||
@ -40,42 +43,98 @@ type IntegrationTester struct {
|
|||||||
suite.Suite
|
suite.Suite
|
||||||
App app.TestApp
|
App app.TestApp
|
||||||
Ctx sdk.Context
|
Ctx sdk.Context
|
||||||
|
|
||||||
|
GenesisTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) SetupSuite() {
|
func (suite *IntegrationTester) SetupSuite() {
|
||||||
config := sdk.GetConfig()
|
config := sdk.GetConfig()
|
||||||
app.SetBech32AddressPrefixes(config)
|
app.SetBech32AddressPrefixes(config)
|
||||||
|
|
||||||
|
// Default genesis time, can be overridden with WithGenesisTime
|
||||||
|
suite.GenesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) SetApp() {
|
func (suite *IntegrationTester) SetApp() {
|
||||||
suite.App = app.NewTestApp()
|
suite.App = app.NewTestApp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) StartChain(genesisTime time.Time, genesisStates ...app.GenesisState) {
|
func (suite *IntegrationTester) SetupTest() {
|
||||||
|
suite.SetApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) WithGenesisTime(genesisTime time.Time) {
|
||||||
|
suite.GenesisTime = genesisTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) StartChainWithBuilders(builders ...GenesisBuilder) {
|
||||||
|
var builtGenStates []app.GenesisState
|
||||||
|
for _, builder := range builders {
|
||||||
|
builtGenStates = append(builtGenStates, builder.BuildMarshalled(suite.App.AppCodec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.StartChain(builtGenStates...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) StartChain(genesisStates ...app.GenesisState) {
|
||||||
suite.App.InitializeFromGenesisStatesWithTimeAndChainID(
|
suite.App.InitializeFromGenesisStatesWithTimeAndChainID(
|
||||||
genesisTime,
|
suite.GenesisTime,
|
||||||
testChainID,
|
testChainID,
|
||||||
genesisStates...,
|
genesisStates...,
|
||||||
)
|
)
|
||||||
|
|
||||||
suite.Ctx = suite.App.NewContext(false, tmproto.Header{Height: 1, Time: genesisTime, ChainID: testChainID})
|
suite.Ctx = suite.App.NewContext(false, tmproto.Header{
|
||||||
|
Height: 1,
|
||||||
|
Time: suite.GenesisTime,
|
||||||
|
ChainID: testChainID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) NextBlockAt(blockTime time.Time) {
|
func (suite *IntegrationTester) NextBlockAfter(blockDuration time.Duration) {
|
||||||
|
suite.NextBlockAfterWithReq(
|
||||||
|
blockDuration,
|
||||||
|
abcitypes.RequestEndBlock{},
|
||||||
|
abcitypes.RequestBeginBlock{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) NextBlockAfterWithReq(
|
||||||
|
blockDuration time.Duration,
|
||||||
|
reqEnd abcitypes.RequestEndBlock,
|
||||||
|
reqBegin abcitypes.RequestBeginBlock,
|
||||||
|
) (abcitypes.ResponseEndBlock, abcitypes.ResponseBeginBlock) {
|
||||||
|
return suite.NextBlockAtWithRequest(
|
||||||
|
suite.Ctx.BlockTime().Add(blockDuration),
|
||||||
|
reqEnd,
|
||||||
|
reqBegin,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) NextBlockAt(
|
||||||
|
blockTime time.Time,
|
||||||
|
) (abcitypes.ResponseEndBlock, abcitypes.ResponseBeginBlock) {
|
||||||
|
return suite.NextBlockAtWithRequest(
|
||||||
|
blockTime,
|
||||||
|
abcitypes.RequestEndBlock{},
|
||||||
|
abcitypes.RequestBeginBlock{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) NextBlockAtWithRequest(
|
||||||
|
blockTime time.Time,
|
||||||
|
reqEnd abcitypes.RequestEndBlock,
|
||||||
|
reqBegin abcitypes.RequestBeginBlock,
|
||||||
|
) (abcitypes.ResponseEndBlock, abcitypes.ResponseBeginBlock) {
|
||||||
if !suite.Ctx.BlockTime().Before(blockTime) {
|
if !suite.Ctx.BlockTime().Before(blockTime) {
|
||||||
panic(fmt.Sprintf("new block time %s must be after current %s", blockTime, suite.Ctx.BlockTime()))
|
panic(fmt.Sprintf("new block time %s must be after current %s", blockTime, suite.Ctx.BlockTime()))
|
||||||
}
|
}
|
||||||
blockHeight := suite.Ctx.BlockHeight() + 1
|
blockHeight := suite.Ctx.BlockHeight() + 1
|
||||||
|
|
||||||
_ = suite.App.EndBlocker(suite.Ctx, abcitypes.RequestEndBlock{})
|
responseEndBlock := suite.App.EndBlocker(suite.Ctx, reqEnd)
|
||||||
|
|
||||||
suite.Ctx = suite.Ctx.WithBlockTime(blockTime).WithBlockHeight(blockHeight).WithChainID(testChainID)
|
suite.Ctx = suite.Ctx.WithBlockTime(blockTime).WithBlockHeight(blockHeight).WithChainID(testChainID)
|
||||||
|
responseBeginBlock := suite.App.BeginBlocker(suite.Ctx, reqBegin) // height and time in RequestBeginBlock are ignored by module begin blockers
|
||||||
|
|
||||||
_ = suite.App.BeginBlocker(suite.Ctx, abcitypes.RequestBeginBlock{}) // height and time in RequestBeginBlock are ignored by module begin blockers
|
return responseEndBlock, responseBeginBlock
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *IntegrationTester) NextBlockAfter(blockDuration time.Duration) {
|
|
||||||
suite.NextBlockAt(suite.Ctx.BlockTime().Add(blockDuration))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) DeliverIncentiveMsg(msg sdk.Msg) error {
|
func (suite *IntegrationTester) DeliverIncentiveMsg(msg sdk.Msg) error {
|
||||||
@ -101,6 +160,46 @@ func (suite *IntegrationTester) DeliverIncentiveMsg(msg sdk.Msg) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MintLiquidAnyValAddr mints liquid tokens with the given validator address,
|
||||||
|
// creating the validator if it does not already exist.
|
||||||
|
// **Note:** This will increment the block height/time and run the End and Begin
|
||||||
|
// blockers!
|
||||||
|
func (suite *IntegrationTester) MintLiquidAnyValAddr(
|
||||||
|
owner sdk.AccAddress,
|
||||||
|
validator sdk.ValAddress,
|
||||||
|
amount sdk.Coin,
|
||||||
|
) (sdk.Coin, error) {
|
||||||
|
// Check if validator already created
|
||||||
|
_, found := suite.App.GetStakingKeeper().GetValidator(suite.Ctx, validator)
|
||||||
|
if !found {
|
||||||
|
// Create validator
|
||||||
|
if err := suite.DeliverMsgCreateValidator(validator, sdk.NewCoin("ukava", sdk.NewInt(1e9))); err != nil {
|
||||||
|
return sdk.Coin{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// new block required to bond validator
|
||||||
|
suite.NextBlockAfter(7 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate and mint liquid tokens
|
||||||
|
return suite.DeliverMsgDelegateMint(owner, validator, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) GetAbciValidator(valAddr sdk.ValAddress) abcitypes.Validator {
|
||||||
|
sk := suite.App.GetStakingKeeper()
|
||||||
|
|
||||||
|
val, found := sk.GetValidator(suite.Ctx, valAddr)
|
||||||
|
suite.Require().True(found)
|
||||||
|
|
||||||
|
pk, err := val.ConsPubKey()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
return abcitypes.Validator{
|
||||||
|
Address: pk.Address(),
|
||||||
|
Power: val.GetConsensusPower(sk.PowerReduction(suite.Ctx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) DeliverMsgCreateValidator(address sdk.ValAddress, selfDelegation sdk.Coin) error {
|
func (suite *IntegrationTester) DeliverMsgCreateValidator(address sdk.ValAddress, selfDelegation sdk.Coin) error {
|
||||||
msg, err := stakingtypes.NewMsgCreateValidator(
|
msg, err := stakingtypes.NewMsgCreateValidator(
|
||||||
address,
|
address,
|
||||||
@ -205,12 +304,17 @@ func (suite *IntegrationTester) DeliverMsgMintDerivative(
|
|||||||
sender sdk.AccAddress,
|
sender sdk.AccAddress,
|
||||||
validator sdk.ValAddress,
|
validator sdk.ValAddress,
|
||||||
amount sdk.Coin,
|
amount sdk.Coin,
|
||||||
) error {
|
) (sdk.Coin, error) {
|
||||||
msg := liquidtypes.NewMsgMintDerivative(sender, validator, amount)
|
msg := liquidtypes.NewMsgMintDerivative(sender, validator, amount)
|
||||||
msgServer := liquidkeeper.NewMsgServerImpl(suite.App.GetLiquidKeeper())
|
msgServer := liquidkeeper.NewMsgServerImpl(suite.App.GetLiquidKeeper())
|
||||||
|
|
||||||
_, err := msgServer.MintDerivative(sdk.WrapSDKContext(suite.Ctx), &msg)
|
res, err := msgServer.MintDerivative(sdk.WrapSDKContext(suite.Ctx), &msg)
|
||||||
return err
|
if err != nil {
|
||||||
|
// Instead of returning res.Received, as res will be nil if there is an error
|
||||||
|
return sdk.Coin{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Received, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTester) DeliverEarnMsgDeposit(
|
func (suite *IntegrationTester) DeliverEarnMsgDeposit(
|
||||||
@ -300,6 +404,9 @@ func (suite *IntegrationTester) VestingPeriodsEqual(address sdk.AccAddress, expe
|
|||||||
suite.Equal(expectedPeriods, vacc.VestingPeriods)
|
suite.Equal(expectedPeriods, vacc.VestingPeriods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// x/incentive
|
||||||
|
|
||||||
func (suite *IntegrationTester) SwapRewardEquals(owner sdk.AccAddress, expected sdk.Coins) {
|
func (suite *IntegrationTester) SwapRewardEquals(owner sdk.AccAddress, expected sdk.Coins) {
|
||||||
claim, found := suite.App.GetIncentiveKeeper().GetSwapClaim(suite.Ctx, owner)
|
claim, found := suite.App.GetIncentiveKeeper().GetSwapClaim(suite.Ctx, owner)
|
||||||
suite.Require().Truef(found, "expected swap claim to be found for %s", owner)
|
suite.Require().Truef(found, "expected swap claim to be found for %s", owner)
|
||||||
@ -338,3 +445,159 @@ func (suite *IntegrationTester) AddTestAddrsFromPubKeys(ctx sdk.Context, pubKeys
|
|||||||
suite.App.FundAccount(ctx, sdk.AccAddress(pk.Address()), initCoins)
|
suite.App.FundAccount(ctx, sdk.AccAddress(pk.Address()), initCoins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) StoredEarnTimeEquals(denom string, expected time.Time) {
|
||||||
|
storedTime, found := suite.App.GetIncentiveKeeper().GetEarnRewardAccrualTime(suite.Ctx, denom)
|
||||||
|
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 *IntegrationTester) StoredEarnIndexesEqual(denom string, expected types.RewardIndexes) {
|
||||||
|
storedIndexes, found := suite.App.GetIncentiveKeeper().GetEarnRewardIndexes(suite.Ctx, denom)
|
||||||
|
suite.Equal(found, expected != nil)
|
||||||
|
|
||||||
|
if found {
|
||||||
|
suite.Equal(expected, storedIndexes)
|
||||||
|
} else {
|
||||||
|
// Can't compare Equal for types.RewardIndexes(nil) vs types.RewardIndexes{}
|
||||||
|
suite.Empty(storedIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) AddIncentiveEarnMultiRewardPeriod(period types.MultiRewardPeriod) {
|
||||||
|
ik := suite.App.GetIncentiveKeeper()
|
||||||
|
params := ik.GetParams(suite.Ctx)
|
||||||
|
|
||||||
|
for i, reward := range params.EarnRewardPeriods {
|
||||||
|
if reward.CollateralType == period.CollateralType {
|
||||||
|
// Replace existing reward period if the collateralType exists.
|
||||||
|
// Params are invalid if there are multiple reward periods for the
|
||||||
|
// same collateral type.
|
||||||
|
params.EarnRewardPeriods[i] = period
|
||||||
|
ik.SetParams(suite.Ctx, params)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.EarnRewardPeriods = append(params.EarnRewardPeriods, period)
|
||||||
|
|
||||||
|
suite.NoError(params.Validate())
|
||||||
|
ik.SetParams(suite.Ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// x/router
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) DeliverRouterMsgDelegateMintDeposit(
|
||||||
|
depositor sdk.AccAddress,
|
||||||
|
validator sdk.ValAddress,
|
||||||
|
amount sdk.Coin,
|
||||||
|
) error {
|
||||||
|
msg := routertypes.MsgDelegateMintDeposit{
|
||||||
|
Depositor: depositor.String(),
|
||||||
|
Validator: validator.String(),
|
||||||
|
Amount: amount,
|
||||||
|
}
|
||||||
|
msgServer := routerkeeper.NewMsgServerImpl(suite.App.GetRouterKeeper())
|
||||||
|
|
||||||
|
_, err := msgServer.DelegateMintDeposit(sdk.WrapSDKContext(suite.Ctx), &msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) DeliverRouterMsgMintDeposit(
|
||||||
|
depositor sdk.AccAddress,
|
||||||
|
validator sdk.ValAddress,
|
||||||
|
amount sdk.Coin,
|
||||||
|
) error {
|
||||||
|
msg := routertypes.MsgMintDeposit{
|
||||||
|
Depositor: depositor.String(),
|
||||||
|
Validator: validator.String(),
|
||||||
|
Amount: amount,
|
||||||
|
}
|
||||||
|
msgServer := routerkeeper.NewMsgServerImpl(suite.App.GetRouterKeeper())
|
||||||
|
|
||||||
|
_, err := msgServer.MintDeposit(sdk.WrapSDKContext(suite.Ctx), &msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) DeliverMsgDelegateMint(
|
||||||
|
delegator sdk.AccAddress,
|
||||||
|
validator sdk.ValAddress,
|
||||||
|
amount sdk.Coin,
|
||||||
|
) (sdk.Coin, error) {
|
||||||
|
if err := suite.DeliverMsgDelegate(delegator, validator, amount); err != nil {
|
||||||
|
return sdk.Coin{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return suite.DeliverMsgMintDerivative(delegator, validator, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// x/distribution
|
||||||
|
|
||||||
|
func (suite *IntegrationTester) GetBeginBlockClaimedStakingRewards(
|
||||||
|
resBeginBlock abcitypes.ResponseBeginBlock,
|
||||||
|
) (validatorRewards map[string]sdk.Coins, totalRewards sdk.Coins) {
|
||||||
|
// Events emitted in BeginBlocker are in the ResponseBeginBlock, not in
|
||||||
|
// ctx.EventManager().Events() as BeginBlock is called with a NewEventManager()
|
||||||
|
// cosmos-sdk/types/module/module.go: func(m *Manager) BeginBlock(...)
|
||||||
|
|
||||||
|
// We also need to parse the events to get the rewards as querying state will
|
||||||
|
// always contain 0 rewards -- rewards are always claimed right after
|
||||||
|
// mint+distribution in BeginBlocker which resets distribution state back to
|
||||||
|
// 0 for reward amounts
|
||||||
|
blockRewardsClaimed := make(map[string]sdk.Coins)
|
||||||
|
for _, event := range resBeginBlock.Events {
|
||||||
|
if event.Type != distributiontypes.EventTypeWithdrawRewards {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example event attributes, amount can be empty for no rewards
|
||||||
|
//
|
||||||
|
// Event: withdraw_rewards
|
||||||
|
// - amount:
|
||||||
|
// - validator: kavavaloper1em2mlkrkx0qsa6327tgvl3g0fh8a95hjnqvrwh
|
||||||
|
// Event: withdraw_rewards
|
||||||
|
// - amount: 523909ukava
|
||||||
|
// - validator: kavavaloper1nmgpgr8l4t8pw9zqx9cltuymvz85wmw9sy8kjy
|
||||||
|
attrsMap := attrsToMap(event.Attributes)
|
||||||
|
|
||||||
|
validator, found := attrsMap[distributiontypes.AttributeKeyValidator]
|
||||||
|
suite.Require().Truef(found, "expected validator attribute to be found in event %s", event)
|
||||||
|
|
||||||
|
amountStr, found := attrsMap[sdk.AttributeKeyAmount]
|
||||||
|
suite.Require().Truef(found, "expected amount attribute to be found in event %s", event)
|
||||||
|
|
||||||
|
amount := sdk.NewCoins()
|
||||||
|
|
||||||
|
// Only parse amount if it is not empty
|
||||||
|
if len(amountStr) > 0 {
|
||||||
|
parsedAmt, err := sdk.ParseCoinNormalized(amountStr)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
amount = amount.Add(parsedAmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockRewardsClaimed[validator] = amount
|
||||||
|
}
|
||||||
|
|
||||||
|
totalClaimedRewards := sdk.NewCoins()
|
||||||
|
for _, amount := range blockRewardsClaimed {
|
||||||
|
totalClaimedRewards = totalClaimedRewards.Add(amount...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockRewardsClaimed, totalClaimedRewards
|
||||||
|
}
|
||||||
|
|
||||||
|
func attrsToMap(attrs []abcitypes.EventAttribute) map[string]string {
|
||||||
|
out := make(map[string]string)
|
||||||
|
|
||||||
|
for _, attr := range attrs {
|
||||||
|
out[string(attr.Key)] = string(attr.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
68
x/incentive/testutil/mint_builder.go
Normal file
68
x/incentive/testutil/mint_builder.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
|
||||||
|
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MintGenesisBuilder is a tool for creating a mint genesis state.
|
||||||
|
// Helper methods add values onto a default genesis state.
|
||||||
|
// All methods are immutable and return updated copies of the builder.
|
||||||
|
type MintGenesisBuilder struct {
|
||||||
|
minttypes.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ GenesisBuilder = (*MintGenesisBuilder)(nil)
|
||||||
|
|
||||||
|
func NewMintGenesisBuilder() MintGenesisBuilder {
|
||||||
|
gen := minttypes.DefaultGenesisState()
|
||||||
|
gen.Params.MintDenom = "ukava"
|
||||||
|
|
||||||
|
return MintGenesisBuilder{
|
||||||
|
GenesisState: *gen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) Build() minttypes.GenesisState {
|
||||||
|
return builder.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) BuildMarshalled(cdc codec.JSONCodec) app.GenesisState {
|
||||||
|
built := builder.Build()
|
||||||
|
|
||||||
|
return app.GenesisState{
|
||||||
|
minttypes.ModuleName: cdc.MustMarshalJSON(&built),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) WithMinter(
|
||||||
|
inflation sdk.Dec,
|
||||||
|
annualProvisions sdk.Dec,
|
||||||
|
) MintGenesisBuilder {
|
||||||
|
builder.Minter = minttypes.NewMinter(inflation, annualProvisions)
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) WithInflationMax(
|
||||||
|
inflationMax sdk.Dec,
|
||||||
|
) MintGenesisBuilder {
|
||||||
|
builder.Params.InflationMax = inflationMax
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) WithInflationMin(
|
||||||
|
inflationMin sdk.Dec,
|
||||||
|
) MintGenesisBuilder {
|
||||||
|
builder.Params.InflationMin = inflationMin
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder MintGenesisBuilder) WithMintDenom(
|
||||||
|
mintDenom string,
|
||||||
|
) MintGenesisBuilder {
|
||||||
|
builder.Params.MintDenom = mintDenom
|
||||||
|
return builder
|
||||||
|
}
|
38
x/incentive/testutil/staking_builder.go
Normal file
38
x/incentive/testutil/staking_builder.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
|
||||||
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StakingGenesisBuilder is a tool for creating a staking genesis state.
|
||||||
|
// Helper methods add values onto a default genesis state.
|
||||||
|
// All methods are immutable and return updated copies of the builder.
|
||||||
|
type StakingGenesisBuilder struct {
|
||||||
|
stakingtypes.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ GenesisBuilder = (*StakingGenesisBuilder)(nil)
|
||||||
|
|
||||||
|
func NewStakingGenesisBuilder() StakingGenesisBuilder {
|
||||||
|
gen := stakingtypes.DefaultGenesisState()
|
||||||
|
gen.Params.BondDenom = "ukava"
|
||||||
|
|
||||||
|
return StakingGenesisBuilder{
|
||||||
|
GenesisState: *gen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder StakingGenesisBuilder) Build() stakingtypes.GenesisState {
|
||||||
|
return builder.GenesisState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder StakingGenesisBuilder) BuildMarshalled(cdc codec.JSONCodec) app.GenesisState {
|
||||||
|
built := builder.Build()
|
||||||
|
|
||||||
|
return app.GenesisState{
|
||||||
|
stakingtypes.ModuleName: cdc.MustMarshalJSON(&built),
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,10 @@ func (k Keeper) CollectStakingRewards(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rewards.IsZero() {
|
||||||
|
return rewards, nil
|
||||||
|
}
|
||||||
|
|
||||||
err = k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleAccountName, destinationModAccount, rewards)
|
err = k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleAccountName, destinationModAccount, rewards)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user