package keeper_test import ( "fmt" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" db "github.com/tendermint/tm-db" "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/incentive/keeper" "github.com/kava-labs/kava/x/incentive/types" ) // NewTestContext sets up a basic context with an in-memory db func NewTestContext(requiredStoreKeys ...sdk.StoreKey) sdk.Context { memDB := db.NewMemDB() cms := store.NewCommitMultiStore(memDB) for _, key := range requiredStoreKeys { cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) } cms.LoadLatestVersion() return sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) } // unitTester is a wrapper around suite.Suite, with common functionality for keeper unit tests. // It can be embedded in structs the same way as suite.Suite. type unitTester struct { suite.Suite keeper keeper.Keeper ctx sdk.Context cdc *codec.Codec incentiveStoreKey sdk.StoreKey } func (suite *unitTester) SetupSuite() { suite.cdc = app.MakeCodec() suite.incentiveStoreKey = sdk.NewKVStoreKey(types.StoreKey) } func (suite *unitTester) SetupTest() { suite.ctx = NewTestContext(suite.incentiveStoreKey) suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, nil, nil) } func (suite *unitTester) TearDownTest() { suite.keeper = keeper.Keeper{} suite.ctx = sdk.Context{} } func (suite *unitTester) NewKeeper(paramSubspace types.ParamSubspace, sk types.SupplyKeeper, cdpk types.CdpKeeper, hk types.HardKeeper, ak types.AccountKeeper, stk types.StakingKeeper, swk types.SwapKeeper) keeper.Keeper { return keeper.NewKeeper(suite.cdc, suite.incentiveStoreKey, paramSubspace, sk, cdpk, hk, ak, stk, swk) } func (suite *unitTester) storeGlobalBorrowIndexes(indexes types.MultiRewardIndexes) { for _, i := range indexes { suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes) } } func (suite *unitTester) storeGlobalSupplyIndexes(indexes types.MultiRewardIndexes) { for _, i := range indexes { suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes) } } func (suite *unitTester) storeGlobalDelegatorIndexes(multiRewardIndexes types.MultiRewardIndexes) { // Hardcoded to use bond denom multiRewardIndex, _ := multiRewardIndexes.GetRewardIndex(types.BondDenom) suite.keeper.SetDelegatorRewardIndexes(suite.ctx, types.BondDenom, multiRewardIndex.RewardIndexes) } func (suite *unitTester) storeGlobalSwapIndexes(indexes types.MultiRewardIndexes) { for _, i := range indexes { suite.keeper.SetSwapRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes) } } func (suite *unitTester) storeHardClaim(claim types.HardLiquidityProviderClaim) { suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, claim) } func (suite *unitTester) storeDelegatorClaim(claim types.DelegatorClaim) { suite.keeper.SetDelegatorClaim(suite.ctx, claim) } type fakeParamSubspace struct { params types.Params } func (subspace *fakeParamSubspace) GetParamSet(_ sdk.Context, ps params.ParamSet) { *(ps.(*types.Params)) = subspace.params } func (subspace *fakeParamSubspace) SetParamSet(_ sdk.Context, ps params.ParamSet) { subspace.params = *(ps.(*types.Params)) } func (subspace *fakeParamSubspace) HasKeyTable() bool { // return true so the keeper does not try to call WithKeyTable, which does nothing return true } func (subspace *fakeParamSubspace) WithKeyTable(params.KeyTable) params.Subspace { // return an non-functional subspace to satisfy the interface return params.Subspace{} } func arbitraryCoin() sdk.Coin { return c("hard", 1e9) } func arbitraryCoins() sdk.Coins { return cs(c("btcb", 1)) } func arbitraryCoinsWithDenoms(denom ...string) sdk.Coins { const arbitraryAmount = 1 // must be > 0 as sdk.Coins type only stores positive amounts coins := sdk.NewCoins() for _, d := range denom { coins = coins.Add(sdk.NewInt64Coin(d, arbitraryAmount)) } return coins } func arbitraryAddress() sdk.AccAddress { _, addresses := app.GeneratePrivKeyAddressPairs(1) return addresses[0] } func arbitraryValidatorAddress() sdk.ValAddress { return generateValidatorAddresses(1)[0] } func generateValidatorAddresses(n int) []sdk.ValAddress { _, addresses := app.GeneratePrivKeyAddressPairs(n) var valAddresses []sdk.ValAddress for _, a := range addresses { valAddresses = append(valAddresses, sdk.ValAddress(a)) } return valAddresses } var nonEmptyMultiRewardIndexes = types.MultiRewardIndexes{ { CollateralType: "bnb", RewardIndexes: types.RewardIndexes{ { CollateralType: "hard", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, }, { CollateralType: "btcb", RewardIndexes: types.RewardIndexes{ { CollateralType: "hard", RewardFactor: d("0.2"), }, { CollateralType: "ukava", RewardFactor: d("0.4"), }, }, }, } func extractCollateralTypes(indexes types.MultiRewardIndexes) []string { var denoms []string for _, ri := range indexes { denoms = append(denoms, ri.CollateralType) } return denoms } func increaseAllRewardFactors(indexes types.MultiRewardIndexes) types.MultiRewardIndexes { increasedIndexes := make(types.MultiRewardIndexes, len(indexes)) copy(increasedIndexes, indexes) for i := range increasedIndexes { increasedIndexes[i].RewardIndexes = increaseRewardFactors(increasedIndexes[i].RewardIndexes) } return increasedIndexes } func increaseRewardFactors(indexes types.RewardIndexes) types.RewardIndexes { increasedIndexes := make(types.RewardIndexes, len(indexes)) copy(increasedIndexes, indexes) for i := range increasedIndexes { increasedIndexes[i].RewardFactor = increasedIndexes[i].RewardFactor.MulInt64(2) } return increasedIndexes } func appendUniqueMultiRewardIndex(indexes types.MultiRewardIndexes) types.MultiRewardIndexes { const uniqueDenom = "uniquedenom" for _, mri := range indexes { if mri.CollateralType == uniqueDenom { panic(fmt.Sprintf("tried to add unique multi reward index with denom '%s', but denom already existed", uniqueDenom)) } } return append(indexes, types.NewMultiRewardIndex( uniqueDenom, types.RewardIndexes{ { CollateralType: "hard", RewardFactor: d("0.02"), }, { CollateralType: "ukava", RewardFactor: d("0.04"), }, }, ), ) } func appendUniqueEmptyMultiRewardIndex(indexes types.MultiRewardIndexes) types.MultiRewardIndexes { const uniqueDenom = "uniquedenom" for _, mri := range indexes { if mri.CollateralType == uniqueDenom { panic(fmt.Sprintf("tried to add unique multi reward index with denom '%s', but denom already existed", uniqueDenom)) } } return append(indexes, types.NewMultiRewardIndex(uniqueDenom, nil)) } func appendUniqueRewardIndexToFirstItem(indexes types.MultiRewardIndexes) types.MultiRewardIndexes { newIndexes := make(types.MultiRewardIndexes, len(indexes)) copy(newIndexes, indexes) newIndexes[0].RewardIndexes = appendUniqueRewardIndex(newIndexes[0].RewardIndexes) return newIndexes } func appendUniqueRewardIndex(indexes types.RewardIndexes) types.RewardIndexes { const uniqueDenom = "uniquereward" for _, mri := range indexes { if mri.CollateralType == uniqueDenom { panic(fmt.Sprintf("tried to add unique reward index with denom '%s', but denom already existed", uniqueDenom)) } } return append( indexes, types.NewRewardIndex(uniqueDenom, d("0.02")), ) }