Incentive Test Refactors (#908)

* organise testing committee gen state

* remove repeated test app initialization

* minor fixes from linter in tests

* move more setup to SetupApp

* split up KeeperTestSuite for each reward type

* simplify KeeperTestSuite

* simplify PayoutKeeperSuite

* simplify DelegatorRewardSuite

* simplify SupplyRewardsSuite

* simplify BorrowRewardsSuite

* simplify USDXRewardsSuite

* add auth genesis builder for easier test setup

* migrate all incentive tests to auth builder

* add incentive genesis builder for easier setup
migrate hard incentive tests

* migrate all tests to incentive builder

* add hard genesis builder

* small tidy ups

* deduplicate initialTime from borrow tests

* deduplicate initialtTime from supply tests

* deduplicate initialTime from usdx and keeper tests

* deduplicate initialTime in delgator tests

* deduplicate genesis time in payout test

* deduplicate test app initialization

* make authGenesisBuilder available for all modules

* remove unused pricefeed setup

* export incentive genesis builder

* remove commented out test cases

* migrate cdp test to new test state builders

* migrate vv payout tests to use new builders
This commit is contained in:
Ruaridh 2021-06-10 14:35:44 +01:00 committed by GitHub
parent 9d9b169e6a
commit d56bb77231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1162 additions and 1541 deletions

View File

@ -18,6 +18,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis" "github.com/cosmos/cosmos-sdk/x/crisis"
"github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution"
@ -42,6 +43,11 @@ import (
validatorvesting "github.com/kava-labs/kava/x/validator-vesting" validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
) )
var (
emptyTime time.Time
emptyChainID string
)
// TestApp is a simple wrapper around an App. It exposes internal keepers for use in integration tests. // TestApp is a simple wrapper around an App. It exposes internal keepers for use in integration tests.
// This file also contains test helpers. Ideally they would be in separate package. // This file also contains test helpers. Ideally they would be in separate package.
// Basic Usage: // Basic Usage:
@ -92,55 +98,12 @@ func (tApp TestApp) GetSwapKeeper() swap.Keeper { return tApp.swapKeep
// InitializeFromGenesisStates calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states // InitializeFromGenesisStates calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states
func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) TestApp { func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) TestApp {
// Create a default genesis state and overwrite with provided values return tApp.InitializeFromGenesisStatesWithTimeAndChainID(emptyTime, emptyChainID, genesisStates...)
genesisState := NewDefaultGenesisState()
for _, state := range genesisStates {
for k, v := range state {
genesisState[k] = v
}
}
// Initialize the chain
stateBytes, err := codec.MarshalJSONIndent(tApp.cdc, genesisState)
if err != nil {
panic(err)
}
tApp.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
AppStateBytes: stateBytes,
},
)
tApp.Commit()
tApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: tApp.LastBlockHeight() + 1}})
return tApp
} }
// InitializeFromGenesisStatesWithTime calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states and genesis Time // InitializeFromGenesisStatesWithTime calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states and genesis Time
func (tApp TestApp) InitializeFromGenesisStatesWithTime(genTime time.Time, genesisStates ...GenesisState) TestApp { func (tApp TestApp) InitializeFromGenesisStatesWithTime(genTime time.Time, genesisStates ...GenesisState) TestApp {
// Create a default genesis state and overwrite with provided values return tApp.InitializeFromGenesisStatesWithTimeAndChainID(genTime, emptyChainID, genesisStates...)
genesisState := NewDefaultGenesisState()
for _, state := range genesisStates {
for k, v := range state {
genesisState[k] = v
}
}
// Initialize the chain
stateBytes, err := codec.MarshalJSONIndent(tApp.cdc, genesisState)
if err != nil {
panic(err)
}
tApp.InitChain(
abci.RequestInitChain{
Time: genTime,
Validators: []abci.ValidatorUpdate{},
AppStateBytes: stateBytes,
},
)
tApp.Commit()
tApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: tApp.LastBlockHeight() + 1, Time: genTime}})
return tApp
} }
// InitializeFromGenesisStatesWithTimeAndChainID calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states and genesis Time // InitializeFromGenesisStatesWithTimeAndChainID calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states and genesis Time
@ -177,18 +140,6 @@ func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAdd
require.Equal(t, expectedCoins, acc.GetCoins()) require.Equal(t, expectedCoins, acc.GetCoins())
} }
// Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map.
func NewAuthGenState(addresses []sdk.AccAddress, coins []sdk.Coins) GenesisState {
// Create GenAccounts
accounts := authexported.GenesisAccounts{}
for i := range addresses {
accounts = append(accounts, auth.NewBaseAccount(addresses[i], coins[i], nil, 0, 0))
}
// Create the auth genesis state
authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts)
return GenesisState{auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(authGenesis)}
}
// GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n secp256k1 private keys and addresses. // GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n secp256k1 private keys and addresses.
func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) { func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) {
r := rand.New(rand.NewSource(12345)) // make the generation deterministic r := rand.New(rand.NewSource(12345)) // make the generation deterministic
@ -205,3 +156,102 @@ func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccA
} }
return return
} }
// Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map.
func NewAuthGenState(addresses []sdk.AccAddress, coins []sdk.Coins) GenesisState {
// Create GenAccounts
accounts := authexported.GenesisAccounts{}
for i := range addresses {
accounts = append(accounts, auth.NewBaseAccount(addresses[i], coins[i], nil, 0, 0))
}
// Create the auth genesis state
authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts)
return GenesisState{auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(authGenesis)}
}
// AuthGenesisBuilder is a tool for creating an auth genesis state.
// Helper methods create basic accounts types and add them to a default genesis state.
// All methods are immutable and return updated copies of the builder.
// The builder inherits from auth.GenesisState, so fields can be accessed directly if a helper method doesn't exist.
//
// Example:
// // create a single account genesis state
// builder := NewAuthGenesisBuilder().WithSimpleAccount(testUserAddress, testCoins)
// genesisState := builder.Build()
//
type AuthGenesisBuilder struct {
auth.GenesisState
}
// NewAuthGenesisBuilder creates a AuthGenesisBuilder containing a default genesis state.
func NewAuthGenesisBuilder() AuthGenesisBuilder {
return AuthGenesisBuilder{
GenesisState: auth.DefaultGenesisState(),
}
}
// Build assembles and returns the final GenesisState
func (builder AuthGenesisBuilder) Build() auth.GenesisState {
return builder.GenesisState
}
// BuildMarshalled assembles the final GenesisState and json encodes it into a generic genesis type.
func (builder AuthGenesisBuilder) BuildMarshalled() GenesisState {
return GenesisState{
auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(builder.Build()),
}
}
// WithAccounts adds accounts of any type to the genesis state.
func (builder AuthGenesisBuilder) WithAccounts(account ...authexported.GenesisAccount) AuthGenesisBuilder {
builder.Accounts = append(builder.Accounts, account...)
return builder
}
// WithSimpleAccount adds a standard account to the genesis state.
func (builder AuthGenesisBuilder) WithSimpleAccount(address sdk.AccAddress, balance sdk.Coins) AuthGenesisBuilder {
return builder.WithAccounts(auth.NewBaseAccount(address, balance, nil, 0, 0))
}
// WithSimpleModuleAccount adds a module account to the genesis state.
func (builder AuthGenesisBuilder) WithSimpleModuleAccount(moduleName string, balance sdk.Coins, permissions ...string) AuthGenesisBuilder {
account := supply.NewEmptyModuleAccount(moduleName, permissions...)
account.SetCoins(balance)
return builder.WithAccounts(account)
}
// WithSimplePeriodicVestingAccount adds a periodic vesting account to the genesis state.
func (builder AuthGenesisBuilder) WithSimplePeriodicVestingAccount(address sdk.AccAddress, balance sdk.Coins, periods vesting.Periods, firstPeriodStartTimestamp int64) AuthGenesisBuilder {
baseAccount := auth.NewBaseAccount(address, balance, nil, 0, 0)
originalVesting := sdk.NewCoins()
for _, p := range periods {
originalVesting = originalVesting.Add(p.Amount...)
}
var totalPeriods int64
for _, p := range periods {
totalPeriods += p.Length
}
endTime := firstPeriodStartTimestamp + totalPeriods
baseVestingAccount, err := vesting.NewBaseVestingAccount(baseAccount, originalVesting, endTime)
if err != nil {
panic(err.Error())
}
periodicVestingAccount := vesting.NewPeriodicVestingAccountRaw(baseVestingAccount, firstPeriodStartTimestamp, periods)
return builder.WithAccounts(periodicVestingAccount)
}
// WithEmptyValidatorVestingAccount adds a stub validator vesting account to the genesis state.
func (builder AuthGenesisBuilder) WithEmptyValidatorVestingAccount(address sdk.AccAddress) AuthGenesisBuilder {
// TODO create a validator vesting account builder and remove this method
bacc := auth.NewBaseAccount(address, nil, nil, 0, 0)
bva, err := vesting.NewBaseVestingAccount(bacc, nil, 1)
if err != nil {
panic(err.Error())
}
account := validatorvesting.NewValidatorVestingAccountRaw(bva, 0, nil, sdk.ConsAddress{}, nil, 90)
return builder.WithAccounts(account)
}

View File

@ -1,70 +1,69 @@
package keeper_test package keeper_test
import ( import (
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
cdptypes "github.com/kava-labs/kava/x/cdp/types" "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
) )
func (suite *KeeperTestSuite) TestRiskyCDPsAccumulateRewards() { func TestRiskyCDPsAccumulateRewards(t *testing.T) {
suite.SetupWithGenState() genesisTime := time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
initialTime := suite.ctx.BlockTime() _, addrs := app.GeneratePrivKeyAddressPairs(5)
initialCollateral := c("bnb", 1_000_000_000)
user := addrs[0]
authBuilder := app.NewAuthGenesisBuilder().
WithSimpleAccount(user, cs(initialCollateral))
// Setup incentive state
collateralType := "bnb-a" collateralType := "bnb-a"
rewardsPerSecond := c(types.USDXMintingRewardDenom, 1_000_000) rewardsPerSecond := c(types.USDXMintingRewardDenom, 1_000_000)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, collateralType, initialTime, initialTime.Add(4*oneYear), rewardsPerSecond)}, incentBuilder := NewIncentiveGenesisBuilder().
nil, // hard rewards not needed WithGenesisTime(genesisTime).
nil, WithSimpleUSDXRewardPeriod(collateralType, rewardsPerSecond)
nil, // delegator rewards not needed
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, tApp := app.NewTestApp()
initialTime.Add(5*oneYear), tApp.InitializeFromGenesisStates(
authBuilder.BuildMarshalled(),
NewPricefeedGenStateMultiFromTime(genesisTime),
NewCDPGenStateMulti(),
incentBuilder.BuildMarshalled(),
) )
suite.keeper.SetParams(suite.ctx, params) ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: genesisTime})
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, collateralType, initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, collateralType, sdk.ZeroDec())
// Setup cdp state containing one CDP // Setup cdp state containing one CDP
cdpKeeper := suite.app.GetCDPKeeper() cdpKeeper := tApp.GetCDPKeeper()
initialCollateral := c("bnb", 1_000_000_000) err := cdpKeeper.AddCdp(ctx, user, initialCollateral, c("usdx", 100_000_000), collateralType)
initialPrincipal := c("usdx", 100_000_000) require.NoError(t, err)
cdpKeeper.SetPreviousAccrualTime(suite.ctx, collateralType, suite.ctx.BlockTime())
cdpKeeper.SetInterestFactor(suite.ctx, collateralType, sdk.OneDec())
// add coins to user's address // TODO move this to auth genesis setup
sk := suite.app.GetSupplyKeeper()
sk.MintCoins(suite.ctx, cdptypes.ModuleName, cs(initialCollateral))
sk.SendCoinsFromModuleToAccount(suite.ctx, cdptypes.ModuleName, suite.addrs[0], cs(initialCollateral))
err := cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], initialCollateral, initialPrincipal, collateralType)
suite.Require().NoError(err)
// Skip ahead two blocks to accumulate both interest and usdx reward for the cdp // Skip ahead two blocks to accumulate both interest and usdx reward for the cdp
// Two blocks are required because the cdp begin blocker runs before incentive begin blocker. // Two blocks are required because the cdp begin blocker runs before incentive begin blocker.
// In the first begin block the cdp is synced, which triggers its claim to sync. But no global rewards have accumulated yet so the sync does nothing. // In the first begin block the cdp is synced, which triggers its claim to sync. But no global rewards have accumulated yet so the sync does nothing.
// Global rewards accumulate immediately after during the incentive begin blocker. // Global rewards accumulate immediately after during the incentive begin blocker.
// Rewards are added to the cdp's claim in the next block when the cdp is synced. // Rewards are added to the cdp's claim in the next block when the cdp is synced.
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = tApp.EndBlocker(ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(10 * time.Minute)) ctx = ctx.WithBlockTime(ctx.BlockTime().Add(10 * time.Minute))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = tApp.BeginBlocker(ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = tApp.EndBlocker(ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(10 * time.Minute)) ctx = ctx.WithBlockTime(ctx.BlockTime().Add(10 * time.Minute))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) _ = tApp.BeginBlocker(ctx, abci.RequestBeginBlock{})
// check cdp rewards // check cdp rewards
cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], collateralType) cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(ctx, user, collateralType)
suite.Require().True(found) require.True(t, found)
// This additional sync adds the rewards accumulated at the end of the last begin block. // This additional sync adds the rewards accumulated at the end of the last begin block.
// They weren't added during the begin blocker as the incentive BB runs after the CDP BB. // They weren't added during the begin blocker as the incentive BB runs after the CDP BB.
suite.keeper.SynchronizeUSDXMintingReward(suite.ctx, cdp) incentiveKeeper := tApp.GetIncentiveKeeper()
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) incentiveKeeper.SynchronizeUSDXMintingReward(ctx, cdp)
suite.Require().True(found) claim, found := incentiveKeeper.GetUSDXMintingClaim(ctx, user)
require.True(t, found)
// rewards are roughly rewardsPerSecond * secondsElapsed (10mins) * num blocks (2) // rewards are roughly rewardsPerSecond * secondsElapsed (10mins) * num blocks (2)
suite.Require().Equal(c(types.USDXMintingRewardDenom, 1_200_001_671), claim.Reward) require.Equal(t, c(types.USDXMintingRewardDenom, 1_200_000_557), claim.Reward)
} }

View File

@ -6,16 +6,25 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/cdp" "github.com/kava-labs/kava/x/cdp"
committeetypes "github.com/kava-labs/kava/x/committee/types" committeetypes "github.com/kava-labs/kava/x/committee/types"
"github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/hard"
hardtypes "github.com/kava-labs/kava/x/hard/types"
"github.com/kava-labs/kava/x/incentive/types"
"github.com/kava-labs/kava/x/pricefeed" "github.com/kava-labs/kava/x/pricefeed"
) )
const (
oneYear time.Duration = time.Hour * 24 * 365
)
// Avoid cluttering test cases with long function names
func i(in int64) sdk.Int { return sdk.NewInt(in) }
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
func NewCDPGenStateMulti() app.GenesisState { func NewCDPGenStateMulti() app.GenesisState {
cdpGenesis := cdp.GenesisState{ cdpGenesis := cdp.GenesisState{
Params: cdp.Params{ Params: cdp.Params{
@ -105,7 +114,7 @@ func NewCDPGenStateMulti() app.GenesisState {
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)} return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
} }
func NewPricefeedGenStateMulti() app.GenesisState { func NewPricefeedGenStateMultiFromTime(t time.Time) app.GenesisState {
pfGenesis := pricefeed.GenesisState{ pfGenesis := pricefeed.GenesisState{
Params: pricefeed.Params{ Params: pricefeed.Params{
Markets: []pricefeed.Market{ Markets: []pricefeed.Market{
@ -122,82 +131,57 @@ func NewPricefeedGenStateMulti() app.GenesisState {
MarketID: "kava:usd", MarketID: "kava:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("2.00"), Price: sdk.MustNewDecFromStr("2.00"),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
{ {
MarketID: "btc:usd", MarketID: "btc:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("8000.00"), Price: sdk.MustNewDecFromStr("8000.00"),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
{ {
MarketID: "xrp:usd", MarketID: "xrp:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("0.25"), Price: sdk.MustNewDecFromStr("0.25"),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
{ {
MarketID: "bnb:usd", MarketID: "bnb:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("17.25"), Price: sdk.MustNewDecFromStr("17.25"),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
{ {
MarketID: "busd:usd", MarketID: "busd:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.OneDec(), Price: sdk.OneDec(),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
{ {
MarketID: "zzz:usd", MarketID: "zzz:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("2.00"), Price: sdk.MustNewDecFromStr("2.00"),
Expiry: time.Now().Add(1 * time.Hour), Expiry: t.Add(1 * time.Hour),
}, },
}, },
} }
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)} return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
} }
func NewHardGenStateMulti() app.GenesisState { func NewHardGenStateMulti(genTime time.Time) HardGenesisBuilder {
loanToValue, _ := sdk.NewDecFromStr("0.6") kavaMM := NewStandardMoneyMarket("ukava")
borrowLimit := sdk.NewDec(1000000000000000) kavaMM.SpotMarketID = "kava:usd"
btcMM := NewStandardMoneyMarket("btcb")
btcMM.SpotMarketID = "btc:usd"
hardGS := hard.NewGenesisState(hard.NewParams( builder := NewHardGenesisBuilder().WithGenesisTime(genTime).
hard.MoneyMarkets{ WithInitializedMoneyMarket(NewStandardMoneyMarket("usdx")).
hard.NewMoneyMarket("usdx", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "usdx:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), WithInitializedMoneyMarket(kavaMM).
hard.NewMoneyMarket("ukava", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "kava:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), WithInitializedMoneyMarket(NewStandardMoneyMarket("bnb")).
hard.NewMoneyMarket("bnb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "bnb:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), WithInitializedMoneyMarket(btcMM).
hard.NewMoneyMarket("btcb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "btc:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), WithInitializedMoneyMarket(NewStandardMoneyMarket("xrp")).
hard.NewMoneyMarket("xrp", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "xrp:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), WithInitializedMoneyMarket(NewStandardMoneyMarket("zzz"))
hard.NewMoneyMarket("zzz", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "zzz:usd", sdk.NewInt(1000000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), return builder
},
sdk.NewDec(10),
), hard.DefaultAccumulationTimes, hard.DefaultDeposits, hard.DefaultBorrows,
hard.DefaultTotalSupplied, hard.DefaultTotalBorrowed, hard.DefaultTotalReserves,
)
return app.GenesisState{hard.ModuleName: hard.ModuleCdc.MustMarshalJSON(hardGS)}
}
func NewAuthGenState(addresses []sdk.AccAddress, coins sdk.Coins) app.GenesisState {
coinsList := []sdk.Coins{}
for range addresses {
coinsList = append(coinsList, coins)
}
// Load up our primary user address
if len(addresses) >= 4 {
coinsList[3] = sdk.NewCoins(
sdk.NewCoin("bnb", sdk.NewInt(1000000000000000)),
sdk.NewCoin("ukava", sdk.NewInt(1000000000000000)),
sdk.NewCoin("btcb", sdk.NewInt(1000000000000000)),
sdk.NewCoin("xrp", sdk.NewInt(1000000000000000)),
sdk.NewCoin("zzz", sdk.NewInt(1000000000000000)),
)
}
return app.NewAuthGenState(addresses, coinsList)
} }
func NewStakingGenesisState() app.GenesisState { func NewStakingGenesisState() app.GenesisState {
@ -208,37 +192,185 @@ func NewStakingGenesisState() app.GenesisState {
} }
} }
func (suite *KeeperTestSuite) SetupWithGenState() { func NewCommitteeGenesisState(members []sdk.AccAddress) app.GenesisState {
tApp := app.NewTestApp() genState := committeetypes.DefaultGenesisState()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) genState.Committees = committeetypes.Committees{
committeetypes.MemberCommittee{
tApp.InitializeFromGenesisStates(
NewAuthGenState(suite.getAllAddrs(), cs(c("ukava", 1_000_000_000))),
NewStakingGenesisState(),
NewPricefeedGenStateMulti(),
NewCDPGenStateMulti(),
NewHardGenStateMulti(),
)
// Set up a god committee
committeeModKeeper := tApp.GetCommitteeKeeper()
godCommittee := committeetypes.MemberCommittee{
BaseCommittee: committeetypes.BaseCommittee{ BaseCommittee: committeetypes.BaseCommittee{
ID: 1, ID: genState.NextProposalID,
Description: "This committee is for testing.", Description: "This committee is for testing.",
Members: suite.addrs[:2], Members: members,
Permissions: []committeetypes.Permission{committeetypes.GodPermission{}}, Permissions: []committeetypes.Permission{committeetypes.GodPermission{}},
VoteThreshold: d("0.667"), VoteThreshold: d("0.667"),
ProposalDuration: time.Hour * 24 * 7, ProposalDuration: time.Hour * 24 * 7,
TallyOption: committeetypes.FirstPastThePost, TallyOption: committeetypes.FirstPastThePost,
}, },
},
}
genState.NextProposalID += 1
return app.GenesisState{
committeetypes.ModuleName: committeetypes.ModuleCdc.MustMarshalJSON(genState),
}
} }
committeeModKeeper.SetCommittee(ctx, godCommittee)
suite.app = tApp // IncentiveGenesisBuilder is a tool for creating an incentive genesis state.
suite.ctx = ctx // Helper methods add values onto a default genesis state.
suite.keeper = tApp.GetIncentiveKeeper() // All methods are immutable and return updated copies of the builder.
suite.hardKeeper = tApp.GetHardKeeper() type IncentiveGenesisBuilder struct {
suite.stakingKeeper = tApp.GetStakingKeeper() types.GenesisState
suite.committeeKeeper = committeeModKeeper genesisTime time.Time
}
func NewIncentiveGenesisBuilder() IncentiveGenesisBuilder {
return IncentiveGenesisBuilder{
GenesisState: types.DefaultGenesisState(),
genesisTime: time.Time{},
}
}
func (builder IncentiveGenesisBuilder) Build() types.GenesisState {
return builder.GenesisState
}
func (builder IncentiveGenesisBuilder) BuildMarshalled() app.GenesisState {
return app.GenesisState{
types.ModuleName: types.ModuleCdc.MustMarshalJSON(builder.Build()),
}
}
func (builder IncentiveGenesisBuilder) WithGenesisTime(time time.Time) IncentiveGenesisBuilder {
builder.genesisTime = time
builder.Params.ClaimEnd = time.Add(5 * oneYear)
return builder
}
func (builder IncentiveGenesisBuilder) WithInitializedBorrowRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
builder.Params.HardBorrowRewardPeriods = append(builder.Params.HardBorrowRewardPeriods, period)
accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
builder.HardBorrowAccumulationTimes = append(builder.HardBorrowAccumulationTimes, accumulationTimeForPeriod)
return builder
}
func (builder IncentiveGenesisBuilder) WithSimpleBorrowRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
return builder.WithInitializedBorrowRewardPeriod(types.NewMultiRewardPeriod(
true,
ctype,
builder.genesisTime,
builder.genesisTime.Add(4*oneYear),
rewardsPerSecond,
))
}
func (builder IncentiveGenesisBuilder) WithInitializedSupplyRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
// TODO this could set the start/end times on the period according to builder.genesisTime
// Then they could be created by a different builder
builder.Params.HardSupplyRewardPeriods = append(builder.Params.HardSupplyRewardPeriods, period)
accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
builder.HardSupplyAccumulationTimes = append(builder.HardSupplyAccumulationTimes, accumulationTimeForPeriod)
return builder
}
func (builder IncentiveGenesisBuilder) WithSimpleSupplyRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
return builder.WithInitializedSupplyRewardPeriod(types.NewMultiRewardPeriod(
true,
ctype,
builder.genesisTime,
builder.genesisTime.Add(4*oneYear),
rewardsPerSecond,
))
}
func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(period types.RewardPeriod) IncentiveGenesisBuilder {
builder.Params.HardDelegatorRewardPeriods = append(builder.Params.HardDelegatorRewardPeriods, period)
accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
builder.HardDelegatorAccumulationTimes = append(builder.HardDelegatorAccumulationTimes, accumulationTimeForPeriod)
return builder
}
func (builder IncentiveGenesisBuilder) WithSimpleDelegatorRewardPeriod(ctype string, rewardsPerSecond sdk.Coin) IncentiveGenesisBuilder {
return builder.WithInitializedDelegatorRewardPeriod(types.NewRewardPeriod(
true,
ctype,
builder.genesisTime,
builder.genesisTime.Add(4*oneYear),
rewardsPerSecond,
))
}
func (builder IncentiveGenesisBuilder) WithInitializedUSDXRewardPeriod(period types.RewardPeriod) IncentiveGenesisBuilder {
builder.Params.USDXMintingRewardPeriods = append(builder.Params.USDXMintingRewardPeriods, period)
accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
builder.USDXAccumulationTimes = append(builder.USDXAccumulationTimes, accumulationTimeForPeriod)
return builder
}
func (builder IncentiveGenesisBuilder) WithSimpleUSDXRewardPeriod(ctype string, rewardsPerSecond sdk.Coin) IncentiveGenesisBuilder {
return builder.WithInitializedUSDXRewardPeriod(types.NewRewardPeriod(
true,
ctype,
builder.genesisTime,
builder.genesisTime.Add(4*oneYear),
rewardsPerSecond,
))
}
func (builder IncentiveGenesisBuilder) WithMultipliers(multipliers types.Multipliers) IncentiveGenesisBuilder {
builder.Params.ClaimMultipliers = multipliers
return builder
}
// HardGenesisBuilder is a tool for creating a hard genesis state.
// Helper methods add values onto a default genesis state.
// All methods are immutable and return updated copies of the builder.
type HardGenesisBuilder struct {
hardtypes.GenesisState
genesisTime time.Time
}
func NewHardGenesisBuilder() HardGenesisBuilder {
return HardGenesisBuilder{
GenesisState: hardtypes.DefaultGenesisState(),
}
}
func (builder HardGenesisBuilder) Build() hardtypes.GenesisState {
return builder.GenesisState
}
func (builder HardGenesisBuilder) BuildMarshalled() app.GenesisState {
return app.GenesisState{
hardtypes.ModuleName: hardtypes.ModuleCdc.MustMarshalJSON(builder.Build()),
}
}
func (builder HardGenesisBuilder) WithGenesisTime(genTime time.Time) HardGenesisBuilder {
builder.genesisTime = genTime
return builder
}
func (builder HardGenesisBuilder) WithInitializedMoneyMarket(market hard.MoneyMarket) HardGenesisBuilder {
builder.Params.MoneyMarkets = append(builder.Params.MoneyMarkets, market)
builder.PreviousAccumulationTimes = append(
builder.PreviousAccumulationTimes,
hardtypes.NewGenesisAccumulationTime(market.Denom, builder.genesisTime, sdk.OneDec(), sdk.OneDec()),
)
return builder
}
func (builder HardGenesisBuilder) WithMinBorrow(minUSDValue sdk.Dec) HardGenesisBuilder {
builder.Params.MinimumBorrowUSDValue = minUSDValue
return builder
}
func NewStandardMoneyMarket(denom string) hardtypes.MoneyMarket {
return hardtypes.NewMoneyMarket(
denom,
hard.NewBorrowLimit(
false,
sdk.NewDec(1e15),
d("0.6"),
),
denom+":usd",
i(1e6),
hard.NewInterestRateModel(d("0.05"), d("2"), d("0.8"), d("10")),
d("0.05"),
sdk.ZeroDec(),
)
} }

View File

@ -2,22 +2,14 @@ package keeper_test
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
"github.com/kava-labs/kava/x/incentive/keeper" "github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
) )
@ -27,57 +19,34 @@ type KeeperTestSuite struct {
suite.Suite suite.Suite
keeper keeper.Keeper keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
stakingKeeper stakingkeeper.Keeper
committeeKeeper committeekeeper.Keeper
app app.TestApp app app.TestApp
ctx sdk.Context ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress addrs []sdk.AccAddress
validatorAddrs []sdk.ValAddress
} }
// The default state used by each test // SetupTest is run automatically before each suite test
func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) SetupTest() {
config := sdk.GetConfig() config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config) app.SetBech32AddressPrefixes(config)
_, allAddrs := app.GeneratePrivKeyAddressPairs(10) _, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
suite.addrs = allAddrs[:5]
for _, a := range allAddrs[5:] { suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
suite.validatorAddrs = append(suite.validatorAddrs, sdk.ValAddress(a))
} }
tApp := app.NewTestApp() func (suite *KeeperTestSuite) SetupApp() {
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) suite.app = app.NewTestApp()
tApp.InitializeFromGenesisStates() suite.keeper = suite.app.GetIncentiveKeeper()
suite.keeper = tApp.GetIncentiveKeeper() suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
suite.app = tApp
suite.ctx = ctx
}
// getAllAddrs returns all user and validator addresses in the suite
func (suite *KeeperTestSuite) getAllAddrs() []sdk.AccAddress {
accAddrs := []sdk.AccAddress{} // initialize new slice to avoid accidental modifications to underlying
accAddrs = append(accAddrs, suite.addrs...)
for _, a := range suite.validatorAddrs {
accAddrs = append(accAddrs, sdk.AccAddress(a))
}
return accAddrs
}
func (suite *KeeperTestSuite) getAccount(addr sdk.AccAddress) authexported.Account {
ak := suite.app.GetAccountKeeper()
return ak.GetAccount(suite.ctx, addr)
}
func (suite *KeeperTestSuite) getModuleAccount(name string) supplyexported.ModuleAccountI {
sk := suite.app.GetSupplyKeeper()
return sk.GetModuleAccount(suite.ctx, name)
} }
func (suite *KeeperTestSuite) TestGetSetDeleteUSDXMintingClaim() { func (suite *KeeperTestSuite) TestGetSetDeleteUSDXMintingClaim() {
suite.SetupApp()
c := types.NewUSDXMintingClaim(suite.addrs[0], c("ukava", 1000000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())}) c := types.NewUSDXMintingClaim(suite.addrs[0], c("ukava", 1000000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())})
_, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) _, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
suite.Require().False(found) suite.Require().False(found)
@ -95,6 +64,7 @@ func (suite *KeeperTestSuite) TestGetSetDeleteUSDXMintingClaim() {
} }
func (suite *KeeperTestSuite) TestIterateUSDXMintingClaims() { func (suite *KeeperTestSuite) TestIterateUSDXMintingClaims() {
suite.SetupApp()
for i := 0; i < len(suite.addrs); i++ { for i := 0; i < len(suite.addrs); i++ {
c := types.NewUSDXMintingClaim(suite.addrs[i], c("ukava", 100000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())}) c := types.NewUSDXMintingClaim(suite.addrs[i], c("ukava", 100000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())})
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
@ -112,28 +82,6 @@ func (suite *KeeperTestSuite) TestIterateUSDXMintingClaims() {
suite.Require().Equal(len(suite.addrs), len(claims)) suite.Require().Equal(len(suite.addrs), len(claims))
} }
func createPeriodicVestingAccount(origVesting sdk.Coins, periods vesting.Periods, startTime, endTime int64) (*vesting.PeriodicVestingAccount, error) {
_, addr := app.GeneratePrivKeyAddressPairs(1)
bacc := auth.NewBaseAccountWithAddress(addr[0])
bacc.Coins = origVesting
bva, err := vesting.NewBaseVestingAccount(&bacc, origVesting, endTime)
if err != nil {
return &vesting.PeriodicVestingAccount{}, err
}
pva := vesting.NewPeriodicVestingAccountRaw(bva, startTime, periods)
err = pva.Validate()
if err != nil {
return &vesting.PeriodicVestingAccount{}, err
}
return pva, nil
}
// Avoid cluttering test cases with long function names
func i(in int64) sdk.Int { return sdk.NewInt(in) }
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
func TestKeeperTestSuite(t *testing.T) { func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite)) suite.Run(t, new(KeeperTestSuite))
} }

View File

@ -3,28 +3,91 @@ package keeper_test
import ( import (
"errors" "errors"
"strings" "strings"
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/auth/vesting"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
cdptypes "github.com/kava-labs/kava/x/cdp/types" cdptypes "github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/hard"
hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
"github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
"github.com/kava-labs/kava/x/kavadist" "github.com/kava-labs/kava/x/kavadist"
validatorvesting "github.com/kava-labs/kava/x/validator-vesting" validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
) )
func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() { // Test suite used for all keeper tests
type PayoutTestSuite struct {
suite.Suite
keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
cdpKeeper cdpkeeper.Keeper
app app.TestApp
ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress
}
// SetupTest is run automatically before each suite test
func (suite *PayoutTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
}
func (suite *PayoutTestSuite) SetupApp() {
suite.app = app.NewTestApp()
suite.keeper = suite.app.GetIncentiveKeeper()
suite.hardKeeper = suite.app.GetHardKeeper()
suite.cdpKeeper = suite.app.GetCDPKeeper()
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
}
func (suite *PayoutTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
suite.SetupApp()
suite.app.InitializeFromGenesisStatesWithTime(
suite.genesisTime,
authBuilder.BuildMarshalled(),
NewPricefeedGenStateMultiFromTime(suite.genesisTime),
NewCDPGenStateMulti(),
hardBuilder.BuildMarshalled(),
incentBuilder.BuildMarshalled(),
)
}
func (suite *PayoutTestSuite) getAccount(addr sdk.AccAddress) authexported.Account {
ak := suite.app.GetAccountKeeper()
return ak.GetAccount(suite.ctx, addr)
}
func (suite *PayoutTestSuite) getModuleAccount(name string) supplyexported.ModuleAccountI {
sk := suite.app.GetSupplyKeeper()
return sk.GetModuleAccount(suite.ctx, name)
}
func (suite *PayoutTestSuite) TestPayoutUSDXMintingClaim() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
initialCollateral sdk.Coin initialCollateral sdk.Coin
initialPrincipal sdk.Coin initialPrincipal sdk.Coin
multipliers types.Multipliers multipliers types.Multipliers
@ -49,13 +112,12 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedBalance: cs(c("usdx", 10000000000), c("ukava", 11571385600)), expectedBalance: cs(c("usdx", 10000000000), c("ukava", 10571385600)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}},
isPeriodicVestingAccount: true, isPeriodicVestingAccount: true,
}, },
@ -69,7 +131,6 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 0), rewardsPerSecond: c("ukava", 0),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
@ -87,39 +148,23 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[0]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBulder := app.NewAuthGenesisBuilder().
WithSimpleAccount(userAddr, cs(tc.args.initialCollateral)).
WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 1000000000000)))
// setup incentive state incentBuilder := NewIncentiveGenesisBuilder().
params := types.NewParams( WithGenesisTime(suite.genesisTime).
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)}, WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond).
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))}, WithMultipliers(tc.args.multipliers)
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
tc.args.multipliers,
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
// setup account state suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
sk := suite.app.GetSupplyKeeper()
err := sk.MintCoins(suite.ctx, cdptypes.ModuleName, sdk.NewCoins(tc.args.initialCollateral))
suite.Require().NoError(err)
err = sk.SendCoinsFromModuleToAccount(suite.ctx, cdptypes.ModuleName, suite.addrs[0], sdk.NewCoins(tc.args.initialCollateral))
suite.Require().NoError(err)
// setup kavadist state
err = sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000)))
suite.Require().NoError(err)
// setup cdp state // setup cdp state
cdpKeeper := suite.app.GetCDPKeeper() err := suite.cdpKeeper.AddCdp(suite.ctx, userAddr, tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
err = cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
suite.Require().NoError(err) suite.Require().NoError(err)
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor) suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
@ -130,13 +175,13 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod) err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
suite.Require().NoError(err) suite.Require().NoError(err)
err = suite.keeper.ClaimUSDXMintingReward(suite.ctx, suite.addrs[0], tc.args.multiplier) err = suite.keeper.ClaimUSDXMintingReward(suite.ctx, userAddr, tc.args.multiplier)
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0]) acc := suite.getAccount(userAddr)
suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins()) // TODO check balance change to decouple from initialized account balance. suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins())
if tc.args.isPeriodicVestingAccount { if tc.args.isPeriodicVestingAccount {
vacc, ok := acc.(*vesting.PeriodicVestingAccount) vacc, ok := acc.(*vesting.PeriodicVestingAccount)
@ -144,7 +189,7 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods) suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
} }
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(c("ukava", 0), claim.Reward) suite.Require().Equal(c("ukava", 0), claim.Reward)
} else { } else {
@ -155,11 +200,10 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
} }
} }
func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() { func (suite *PayoutTestSuite) TestPayoutUSDXMintingClaimVVesting() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
initialCollateral sdk.Coin initialCollateral sdk.Coin
initialPrincipal sdk.Coin initialPrincipal sdk.Coin
multipliers types.Multipliers multipliers types.Multipliers
@ -167,7 +211,6 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
timeElapsed int timeElapsed int
expectedBalance sdk.Coins expectedBalance sdk.Coins
expectedPeriods vesting.Periods expectedPeriods vesting.Periods
isPeriodicVestingAccount bool
} }
type errArgs struct { type errArgs struct {
expectPass bool expectPass bool
@ -184,15 +227,13 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), initialCollateral: c("bnb", 1e12),
initialCollateral: c("bnb", 1000000000000), initialPrincipal: c("usdx", 1e10),
initialPrincipal: c("usdx", 10000000000),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedBalance: cs(c("ukava", 11571385600)), expectedBalance: cs(c("ukava", 10571385600)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -204,15 +245,13 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 0), rewardsPerSecond: c("ukava", 0),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), initialCollateral: c("bnb", 1e12),
initialCollateral: c("bnb", 1000000000000), initialPrincipal: c("usdx", 1e10),
initialPrincipal: c("usdx", 10000000000),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedBalance: cs(), expectedBalance: cs(),
expectedPeriods: vesting.Periods{}, expectedPeriods: vesting.Periods{},
isPeriodicVestingAccount: false,
}, },
errArgs{ errArgs{
expectPass: false, expectPass: false,
@ -222,47 +261,29 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// setup incentive state bacc := auth.NewBaseAccount(suite.addrs[2], cs(tc.args.initialCollateral, c("ukava", 400)), nil, 0, 0)
params := types.NewParams( bva, err := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.genesisTime.Unix()+16)
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)}, suite.Require().NoError(err)
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
tc.args.multipliers,
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
// sets addrs[2] to be a validator vesting account
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[2])
bacc := auth.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
bva, err2 := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.ctx.BlockTime().Unix()+16)
suite.Require().NoError(err2)
periods := vesting.Periods{ periods := vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
} }
vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.ctx.BlockTime().Unix(), periods, sdk.ConsAddress{}, nil, 90) vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.genesisTime.Unix(), periods, sdk.ConsAddress{}, nil, 90)
ak.SetAccount(suite.ctx, vva)
// setup account state authBulder := app.NewAuthGenesisBuilder().
sk := suite.app.GetSupplyKeeper() WithAccounts(vva).
err := sk.MintCoins(suite.ctx, cdptypes.ModuleName, sdk.NewCoins(tc.args.initialCollateral)) WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 1e18))).
suite.Require().NoError(err) WithSimpleAccount(suite.addrs[0], cs()) // the recipient address needs to be a instantiated account // TODO change?
err = sk.SendCoinsFromModuleToAccount(suite.ctx, cdptypes.ModuleName, suite.addrs[2], sdk.NewCoins(tc.args.initialCollateral))
suite.Require().NoError(err)
// setup kavadist state incentBuilder := NewIncentiveGenesisBuilder().
err = sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000))) WithGenesisTime(suite.genesisTime).
suite.Require().NoError(err) WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond).
WithMultipliers(tc.args.multipliers)
suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
// setup cdp state // setup cdp state
cdpKeeper := suite.app.GetCDPKeeper() cdpKeeper := suite.app.GetCDPKeeper()
@ -273,6 +294,7 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor) suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
// accumulate some usdx rewards
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed)) updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype) rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype)
@ -286,13 +308,11 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
suite.Require().NoError(err) suite.Require().NoError(err)
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[0]) acc := ak.GetAccount(suite.ctx, suite.addrs[0])
suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins()) // TODO check balance change to decouple from initialized account balance. suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins())
if tc.args.isPeriodicVestingAccount {
vacc, ok := acc.(*vesting.PeriodicVestingAccount) vacc, ok := acc.(*vesting.PeriodicVestingAccount)
suite.Require().True(ok) suite.Require().True(ok)
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods) suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
}
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[2]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[2])
suite.Require().True(found) suite.Require().True(found)
@ -305,12 +325,11 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaimVVesting() {
} }
} }
func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() { func (suite *PayoutTestSuite) TestPayoutHardLiquidityProviderClaim() {
type args struct { type args struct {
deposit sdk.Coins deposit sdk.Coins
borrow sdk.Coins borrow sdk.Coins
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
multipliers types.Multipliers multipliers types.Multipliers
multiplier types.MultiplierName multiplier types.MultiplierName
timeElapsed int64 timeElapsed int64
@ -334,7 +353,6 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
@ -353,7 +371,6 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 864000, timeElapsed: 864000,
@ -366,32 +383,12 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
contains: "", contains: "",
}, },
}, },
{
"invalid zero rewards",
args{
deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 0)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"),
timeElapsed: 86400,
expectedRewards: cs(c("hard", 0)),
expectedPeriods: vesting.Periods{},
isPeriodicVestingAccount: false,
},
errArgs{
expectPass: false,
contains: "claim amount rounds to zero",
},
},
{ {
"multiple reward denoms: valid 1 day", "multiple reward denoms: valid 1 day",
args{ args{
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
@ -410,7 +407,6 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 864000, timeElapsed: 864000,
@ -429,7 +425,6 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 222222)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 222222)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
@ -446,71 +441,31 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// setup kavadist state
sk := suite.app.GetSupplyKeeper()
err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000)))
suite.Require().NoError(err)
// Set up generic reward periods
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for _, coin := range tc.args.deposit {
if len(tc.args.rewardsPerSecond) > 0 {
rewardPeriod := types.NewRewardPeriod(true, coin.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])
rewardPeriods = append(rewardPeriods, rewardPeriod)
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, coin.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
}
// Set up generic reward periods
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
// Set each denom's previous accrual time and supply reward factor
if len(tc.args.rewardsPerSecond) > 0 {
for _, coin := range tc.args.deposit {
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, coin.Denom, rewardIndexes)
}
}
// Set each denom's previous accrual time and borrow reward factor
if len(tc.args.rewardsPerSecond) > 0 {
for _, coin := range tc.args.borrow {
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, coin.Denom, rewardIndexes)
}
}
hardKeeper := suite.app.GetHardKeeper()
userAddr := suite.addrs[3] userAddr := suite.addrs[3]
authBulder := app.NewAuthGenesisBuilder().
WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15))).
WithSimpleModuleAccount(kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000)))
incentBuilder := NewIncentiveGenesisBuilder().
WithGenesisTime(suite.genesisTime).
WithMultipliers(tc.args.multipliers)
for _, c := range tc.args.deposit {
incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
}
for _, c := range tc.args.borrow {
incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
}
suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
// User deposits and borrows // User deposits and borrows
err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit) err := suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
suite.Require().NoError(err) suite.Require().NoError(err)
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow)
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards // Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, coin := range tc.args.deposit { for _, coin := range tc.args.deposit {
suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom)) suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom))
@ -520,9 +475,6 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
runAtTime := time.Unix(suite.ctx.BlockTime().Unix()+(tc.args.timeElapsed), 0) runAtTime := time.Unix(suite.ctx.BlockTime().Unix()+(tc.args.timeElapsed), 0)
runCtx := suite.ctx.WithBlockTime(runAtTime) runCtx := suite.ctx.WithBlockTime(runAtTime)
// Run Hard begin blocker
hard.BeginBlocker(runCtx, suite.hardKeeper)
// Accumulate supply rewards for each deposit denom // Accumulate supply rewards for each deposit denom
for _, coin := range tc.args.deposit { for _, coin := range tc.args.deposit {
rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriods(runCtx, coin.Denom) rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriods(runCtx, coin.Denom)
@ -540,25 +492,25 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
} }
// Sync hard supply rewards // Sync hard supply rewards
deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, suite.addrs[3]) deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit) suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
// Sync hard borrow rewards // Sync hard borrow rewards
borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, suite.addrs[3]) borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow) suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
// Fetch pre-claim balances // Fetch pre-claim balances
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
preClaimAcc := ak.GetAccount(runCtx, suite.addrs[3]) preClaimAcc := ak.GetAccount(runCtx, userAddr)
err = suite.keeper.ClaimHardReward(runCtx, suite.addrs[3], tc.args.multiplier) err = suite.keeper.ClaimHardReward(runCtx, userAddr, tc.args.multiplier)
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that user's balance has increased by expected reward amount // Check that user's balance has increased by expected reward amount
postClaimAcc := ak.GetAccount(suite.ctx, suite.addrs[3]) postClaimAcc := ak.GetAccount(suite.ctx, userAddr)
suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins()) suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins())
if tc.args.isPeriodicVestingAccount { if tc.args.isPeriodicVestingAccount {
@ -568,7 +520,7 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
} }
// Check that each claim reward coin's amount has been reset to 0 // Check that each claim reward coin's amount has been reset to 0
claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, suite.addrs[3]) claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, claimRewardCoin := range claim.Reward { for _, claimRewardCoin := range claim.Reward {
suite.Require().Equal(c(claimRewardCoin.Denom, 0), claimRewardCoin) suite.Require().Equal(c(claimRewardCoin.Denom, 0), claimRewardCoin)
@ -581,18 +533,16 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
} }
} }
func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() { func (suite *PayoutTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
type args struct { type args struct {
deposit sdk.Coins deposit sdk.Coins
borrow sdk.Coins borrow sdk.Coins
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
multipliers types.Multipliers multipliers types.Multipliers
multiplier types.MultiplierName multiplier types.MultiplierName
timeElapsed int64 timeElapsed int64
expectedRewards sdk.Coins expectedRewards sdk.Coins
expectedPeriods vesting.Periods expectedPeriods vesting.Periods
isPeriodicVestingAccount bool
} }
type errArgs struct { type errArgs struct {
expectPass bool expectPass bool
@ -610,13 +560,11 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewards: cs(c("hard", 21142771200)), // 10571385600 (deposit reward) + 10571385600 (borrow reward) expectedRewards: cs(c("hard", 21142771202)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -629,51 +577,28 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 864000, timeElapsed: 864000,
expectedRewards: cs(c("hard", 211427712000)), // 105713856000 (deposit reward) + 105713856000 (borrow reward) expectedRewards: cs(c("hard", 211427712008)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712000))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712008))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
contains: "", contains: "",
}, },
}, },
{
"invalid zero rewards",
args{
deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 0)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"),
timeElapsed: 86400,
expectedRewards: cs(c("hard", 0)),
expectedPeriods: vesting.Periods{},
isPeriodicVestingAccount: false,
},
errArgs{
expectPass: false,
contains: "claim amount rounds to zero",
},
},
{ {
"multiple reward denoms: valid 1 day", "multiple reward denoms: valid 1 day",
args{ args{
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewards: cs(c("hard", 21142771200), c("ukava", 21142771200)), // 10571385600 (deposit reward) + 10571385600 (borrow reward) expectedRewards: cs(c("hard", 21142771202), c("ukava", 21142771202)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200), c("ukava", 21142771200))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202), c("ukava", 21142771202))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -686,13 +611,11 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 864000, timeElapsed: 864000,
expectedRewards: cs(c("hard", 211427712000), c("ukava", 211427712000)), // 105713856000 (deposit reward) + 105713856000 (borrow reward) expectedRewards: cs(c("hard", 211427712008), c("ukava", 211427712008)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712000), c("ukava", 211427712000))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712008), c("ukava", 211427712008))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -705,13 +628,11 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
deposit: cs(c("bnb", 10000000000)), deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)), borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 222222)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 222222)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))}, multipliers: types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewards: cs(c("hard", 21142771200), c("ukava", 38399961600)), expectedRewards: cs(c("hard", 21142771202), c("ukava", 38399961603)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200), c("ukava", 38399961600))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202), c("ukava", 38399961603))}},
isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -722,77 +643,39 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// setup kavadist state userAddr := suite.addrs[3]
sk := suite.app.GetSupplyKeeper()
err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000))) bacc := auth.NewBaseAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)), nil, 0, 0)
bva, err := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.genesisTime.Unix()+16)
suite.Require().NoError(err) suite.Require().NoError(err)
// Set up generic reward periods
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for _, coin := range tc.args.deposit {
if len(tc.args.rewardsPerSecond) > 0 {
rewardPeriod := types.NewRewardPeriod(true, coin.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])
rewardPeriods = append(rewardPeriods, rewardPeriod)
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, coin.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
}
// Set up generic reward periods
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
// Set each denom's previous accrual time and supply reward factor
if len(tc.args.rewardsPerSecond) > 0 {
for _, coin := range tc.args.deposit {
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, coin.Denom, rewardIndexes)
}
}
// Set each denom's previous accrual time and borrow reward factor
if len(tc.args.rewardsPerSecond) > 0 {
for _, coin := range tc.args.borrow {
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, coin.Denom, rewardIndexes)
}
}
// sets addrs[3] to be a validator vesting account
ak := suite.app.GetAccountKeeper()
acc := ak.GetAccount(suite.ctx, suite.addrs[3])
bacc := auth.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
bva, err2 := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.ctx.BlockTime().Unix()+16)
suite.Require().NoError(err2)
periods := vesting.Periods{ periods := vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))}, vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
} }
vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.ctx.BlockTime().Unix(), periods, sdk.ConsAddress{}, nil, 90) vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.genesisTime.Unix(), periods, sdk.ConsAddress{}, nil, 90)
ak.SetAccount(suite.ctx, vva)
authBulder := app.NewAuthGenesisBuilder().
WithAccounts(vva).
WithSimpleAccount(suite.addrs[2], cs()).
WithSimpleModuleAccount(kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000)))
incentBuilder := NewIncentiveGenesisBuilder().
WithGenesisTime(suite.genesisTime).
WithMultipliers(tc.args.multipliers)
for _, c := range tc.args.deposit {
incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
}
for _, c := range tc.args.borrow {
incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
}
suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
ak := suite.app.GetAccountKeeper()
hardKeeper := suite.app.GetHardKeeper() hardKeeper := suite.app.GetHardKeeper()
userAddr := suite.addrs[3]
// User deposits and borrows // User deposits and borrows
err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit) err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
@ -801,7 +684,7 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards // Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, coin := range tc.args.deposit { for _, coin := range tc.args.deposit {
suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom)) suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom))
@ -831,19 +714,19 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
} }
// Sync hard supply rewards // Sync hard supply rewards
deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, suite.addrs[3]) deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit) suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
// Sync hard borrow rewards // Sync hard borrow rewards
borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, suite.addrs[3]) borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow) suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
// Fetch pre-claim balances // Fetch pre-claim balances
preClaimAcc := ak.GetAccount(runCtx, suite.addrs[2]) preClaimAcc := ak.GetAccount(runCtx, suite.addrs[2])
err = suite.keeper.ClaimHardRewardVVesting(runCtx, suite.addrs[3], suite.addrs[2], tc.args.multiplier) err = suite.keeper.ClaimHardRewardVVesting(runCtx, userAddr, suite.addrs[2], tc.args.multiplier)
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)
@ -851,11 +734,9 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
postClaimAcc := ak.GetAccount(suite.ctx, suite.addrs[2]) postClaimAcc := ak.GetAccount(suite.ctx, suite.addrs[2])
suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins()) suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins())
if tc.args.isPeriodicVestingAccount {
vacc, ok := postClaimAcc.(*vesting.PeriodicVestingAccount) vacc, ok := postClaimAcc.(*vesting.PeriodicVestingAccount)
suite.Require().True(ok) suite.Require().True(ok)
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods) suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
}
// Check that each claim reward coin's amount has been reset to 0 // Check that each claim reward coin's amount has been reset to 0
claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, suite.addrs[3]) claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, suite.addrs[3])
@ -871,7 +752,7 @@ func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
} }
} }
func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() { func (suite *PayoutTestSuite) TestSendCoinsToPeriodicVestingAccount() {
type accountArgs struct { type accountArgs struct {
periods vesting.Periods periods vesting.Periods
origVestingCoins sdk.Coins origVestingCoins sdk.Coins
@ -1111,29 +992,31 @@ func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() {
} }
for _, tc := range tests { for _, tc := range tests {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
// create the periodic vesting account authBuilder := app.NewAuthGenesisBuilder().WithSimplePeriodicVestingAccount(
pva, err := createPeriodicVestingAccount(tc.args.accArgs.origVestingCoins, tc.args.accArgs.periods, tc.args.accArgs.startTime, tc.args.accArgs.endTime) suite.addrs[0],
suite.Require().NoError(err) tc.args.accArgs.origVestingCoins,
tc.args.accArgs.periods,
// setup store state with account and kavadist module account tc.args.accArgs.startTime,
suite.ctx = suite.ctx.WithBlockTime(tc.args.ctxTime) )
ak := suite.app.GetAccountKeeper()
ak.SetAccount(suite.ctx, pva)
// mint module account coins if required
if tc.args.mintModAccountCoins { if tc.args.mintModAccountCoins {
sk := suite.app.GetSupplyKeeper() authBuilder = authBuilder.WithSimpleModuleAccount(kavadist.ModuleName, tc.args.period.Amount)
err = sk.MintCoins(suite.ctx, kavadist.ModuleName, tc.args.period.Amount)
suite.Require().NoError(err)
} }
err = suite.keeper.SendTimeLockedCoinsToPeriodicVestingAccount(suite.ctx, kavadist.ModuleName, pva.Address, tc.args.period.Amount, tc.args.period.Length) suite.genesisTime = tc.args.ctxTime
suite.SetupApp()
suite.app.InitializeFromGenesisStates(
authBuilder.BuildMarshalled(),
)
err := suite.keeper.SendTimeLockedCoinsToPeriodicVestingAccount(suite.ctx, kavadist.ModuleName, suite.addrs[0], tc.args.period.Amount, tc.args.period.Length)
if tc.errArgs.expectErr { if tc.errArgs.expectErr {
suite.Require().Error(err) suite.Require().Error(err)
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains)) suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
} else { } else {
suite.Require().NoError(err) suite.Require().NoError(err)
acc := suite.getAccount(pva.Address) acc := suite.getAccount(suite.addrs[0])
vacc, ok := acc.(*vesting.PeriodicVestingAccount) vacc, ok := acc.(*vesting.PeriodicVestingAccount)
suite.Require().True(ok) suite.Require().True(ok)
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods) suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
@ -1144,8 +1027,17 @@ func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() {
} }
} }
func (suite *KeeperTestSuite) TestSendCoinsToBaseAccount() { func (suite *PayoutTestSuite) TestSendCoinsToBaseAccount() {
suite.SetupWithAccountState() authBuilder := app.NewAuthGenesisBuilder().
WithSimpleAccount(suite.addrs[1], cs(c("ukava", 400))).
WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 600)))
suite.genesisTime = time.Unix(100, 0)
suite.SetupApp()
suite.app.InitializeFromGenesisStates(
authBuilder.BuildMarshalled(),
)
// send coins to base account // send coins to base account
err := suite.keeper.SendTimeLockedCoinsToAccount(suite.ctx, kavadist.ModuleName, suite.addrs[1], cs(c("ukava", 100)), 5) err := suite.keeper.SendTimeLockedCoinsToAccount(suite.ctx, kavadist.ModuleName, suite.addrs[1], cs(c("ukava", 100)), 5)
suite.Require().NoError(err) suite.Require().NoError(err)
@ -1163,8 +1055,15 @@ func (suite *KeeperTestSuite) TestSendCoinsToBaseAccount() {
} }
func (suite *KeeperTestSuite) TestSendCoinsToInvalidAccount() { func (suite *PayoutTestSuite) TestSendCoinsToInvalidAccount() {
suite.SetupWithAccountState() authBuilder := app.NewAuthGenesisBuilder().
WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 600))).
WithEmptyValidatorVestingAccount(suite.addrs[2])
suite.SetupApp()
suite.app.InitializeFromGenesisStates(
authBuilder.BuildMarshalled(),
)
err := suite.keeper.SendTimeLockedCoinsToAccount(suite.ctx, kavadist.ModuleName, suite.addrs[2], cs(c("ukava", 100)), 5) err := suite.keeper.SendTimeLockedCoinsToAccount(suite.ctx, kavadist.ModuleName, suite.addrs[2], cs(c("ukava", 100)), 5)
suite.Require().True(errors.Is(err, types.ErrInvalidAccountType)) suite.Require().True(errors.Is(err, types.ErrInvalidAccountType))
macc := suite.getModuleAccount(cdptypes.ModuleName) macc := suite.getModuleAccount(cdptypes.ModuleName)
@ -1172,56 +1071,7 @@ func (suite *KeeperTestSuite) TestSendCoinsToInvalidAccount() {
suite.Require().True(errors.Is(err, types.ErrInvalidAccountType)) suite.Require().True(errors.Is(err, types.ErrInvalidAccountType))
} }
func (suite *KeeperTestSuite) SetupWithAccountState() { func (suite *PayoutTestSuite) TestGetPeriodLength() {
// creates a new app state with 4 funded addresses and 1 module account
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: time.Unix(100, 0)})
_, addrs := app.GeneratePrivKeyAddressPairs(4)
authGS := app.NewAuthGenState(
addrs,
[]sdk.Coins{
cs(c("ukava", 400)),
cs(c("ukava", 400)),
cs(c("ukava", 400)),
cs(c("ukava", 400)),
})
tApp.InitializeFromGenesisStates(
authGS,
)
supplyKeeper := tApp.GetSupplyKeeper()
macc := supplyKeeper.GetModuleAccount(ctx, kavadist.ModuleName)
err := supplyKeeper.MintCoins(ctx, macc.GetName(), cs(c("ukava", 600)))
suite.Require().NoError(err)
// sets addrs[0] to be a periodic vesting account
ak := tApp.GetAccountKeeper()
acc := ak.GetAccount(ctx, addrs[0])
bacc := auth.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
periods := vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
}
bva, err2 := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), ctx.BlockTime().Unix()+16)
suite.Require().NoError(err2)
pva := vesting.NewPeriodicVestingAccountRaw(bva, ctx.BlockTime().Unix(), periods)
ak.SetAccount(ctx, pva)
// sets addrs[2] to be a validator vesting account
acc = ak.GetAccount(ctx, addrs[2])
bacc = auth.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
bva, err2 = vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), ctx.BlockTime().Unix()+16)
suite.Require().NoError(err2)
vva := validatorvesting.NewValidatorVestingAccountRaw(bva, ctx.BlockTime().Unix(), periods, sdk.ConsAddress{}, nil, 90)
ak.SetAccount(ctx, vva)
suite.app = tApp
suite.keeper = tApp.GetIncentiveKeeper()
suite.ctx = ctx
suite.addrs = addrs
}
func (suite *KeeperTestSuite) TestGetPeriodLength() {
type args struct { type args struct {
blockTime time.Time blockTime time.Time
multiplier types.Multiplier multiplier types.Multiplier
@ -1348,8 +1198,10 @@ func (suite *KeeperTestSuite) TestGetPeriodLength() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
ctx := suite.ctx.WithBlockTime(tc.args.blockTime) suite.genesisTime = tc.args.blockTime
length, err := suite.keeper.GetPeriodLength(ctx, tc.args.multiplier) suite.SetupApp()
length, err := suite.keeper.GetPeriodLength(suite.ctx, tc.args.multiplier)
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Require().Equal(tc.args.expectedLength, length) suite.Require().Equal(tc.args.expectedLength, length)
@ -1359,3 +1211,7 @@ func (suite *KeeperTestSuite) TestGetPeriodLength() {
}) })
} }
} }
func TestPayoutTestSuite(t *testing.T) {
suite.Run(t, new(PayoutTestSuite))
}

View File

@ -1,23 +1,75 @@
package keeper_test package keeper_test
import ( import (
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/params"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/committee" "github.com/kava-labs/kava/x/committee"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
"github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/hard"
hardtypes "github.com/kava-labs/kava/x/hard/types" hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
"github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
) )
func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() { // Test suite used for all keeper tests
type BorrowRewardsTestSuite struct {
suite.Suite
keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
committeeKeeper committeekeeper.Keeper
app app.TestApp
ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress
}
// SetupTest is run automatically before each suite test
func (suite *BorrowRewardsTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
}
func (suite *BorrowRewardsTestSuite) SetupApp() {
suite.app = app.NewTestApp()
suite.keeper = suite.app.GetIncentiveKeeper()
suite.hardKeeper = suite.app.GetHardKeeper()
suite.committeeKeeper = suite.app.GetCommitteeKeeper()
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
}
func (suite *BorrowRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
suite.SetupApp()
suite.app.InitializeFromGenesisStatesWithTime(
suite.genesisTime,
authBuilder.BuildMarshalled(),
NewPricefeedGenStateMultiFromTime(suite.genesisTime),
hardBuilder.BuildMarshalled(),
NewCommitteeGenesisState(suite.addrs[:2]), // TODO add committee members to suite,
incentBuilder.BuildMarshalled(),
)
}
func (suite *BorrowRewardsTestSuite) TestAccumulateHardBorrowRewards() {
type args struct { type args struct {
borrow sdk.Coin borrow sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
timeElapsed int timeElapsed int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
} }
@ -31,7 +83,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7, timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.000000856478000001"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.000000856478000001"))},
}, },
@ -41,7 +92,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.010571385600010177"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.010571385600010177"))},
}, },
@ -51,7 +101,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 0, timeElapsed: 0,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.0"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.0"))},
}, },
@ -61,7 +110,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7, timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.000000856478000001")), types.NewRewardIndex("hard", d("0.000000856478000001")),
@ -74,7 +122,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.010571385600010177")), types.NewRewardIndex("hard", d("0.010571385600010177")),
@ -87,7 +134,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 0, timeElapsed: 0,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.0")), types.NewRewardIndex("hard", d("0.0")),
@ -100,7 +146,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
args{ args{
borrow: c("bnb", 1000000000000), borrow: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.010571385600010177")), types.NewRewardIndex("hard", d("0.010571385600010177")),
@ -111,43 +156,22 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
userAddr,
// Mint coins to hard module account cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// setup incentive state
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, tc.args.borrow.Denom, rewardIndexes)
// Set up hard state (interest factor for the relevant denom) incentBuilder := NewIncentiveGenesisBuilder().
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0")) WithGenesisTime(suite.genesisTime).
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0")) WithSimpleBorrowRewardPeriod(tc.args.borrow.Denom, tc.args.rewardsPerSecond)
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
// User deposits and borrows to increase total borrowed amount // User deposits and borrows to increase total borrowed amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
suite.Require().NoError(err) suite.Require().NoError(err)
err = hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow)) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow))
suite.Require().NoError(err) suite.Require().NoError(err)
// Set up chain context at future time // Set up chain context at future time
@ -175,13 +199,12 @@ func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
} }
} }
func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() { func (suite *BorrowRewardsTestSuite) TestInitializeHardBorrowRewards() {
type args struct { type args struct {
moneyMarketRewardDenoms map[string][]string moneyMarketRewardDenoms map[string]sdk.Coins
deposit sdk.Coins deposit sdk.Coins
borrow sdk.Coins borrow sdk.Coins
initialTime time.Time
expectedClaimBorrowRewardIndexes types.MultiRewardIndexes expectedClaimBorrowRewardIndexes types.MultiRewardIndexes
} }
type test struct { type test struct {
@ -189,10 +212,9 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
args args args args
} }
standardMoneyMarketRewardDenoms := map[string][]string{ standardMoneyMarketRewardDenoms := map[string]sdk.Coins{
"bnb": {"hard"}, "bnb": cs(c("hard", 1)),
"btcb": {"hard", "ukava"}, "btcb": cs(c("hard", 1), c("ukava", 1)),
"xrp": {},
} }
testCases := []test{ testCases := []test{
@ -202,7 +224,6 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000)), deposit: cs(c("bnb", 1000000000000)),
borrow: cs(c("bnb", 100000000000)), borrow: cs(c("bnb", 100000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{ expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -219,7 +240,6 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("btcb", 1000000000000)), deposit: cs(c("btcb", 1000000000000)),
borrow: cs(c("btcb", 100000000000)), borrow: cs(c("btcb", 100000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{ expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"btcb", "btcb",
@ -237,7 +257,6 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("xrp", 1000000000000)), deposit: cs(c("xrp", 1000000000000)),
borrow: cs(c("xrp", 100000000000)), borrow: cs(c("xrp", 100000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{ expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"xrp", "xrp",
@ -252,7 +271,6 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000), c("btcb", 1000000000000)), deposit: cs(c("bnb", 1000000000000), c("btcb", 1000000000000)),
borrow: cs(c("bnb", 100000000000), c("btcb", 100000000000)), borrow: cs(c("bnb", 100000000000), c("btcb", 100000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{ expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -276,7 +294,6 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000), c("xrp", 1000000000000)), deposit: cs(c("bnb", 1000000000000), c("xrp", 1000000000000)),
borrow: cs(c("bnb", 100000000000), c("xrp", 100000000000)), borrow: cs(c("bnb", 100000000000), c("xrp", 100000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{ expectedClaimBorrowRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -294,64 +311,24 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// Mint coins to hard module account
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
userAddr := suite.addrs[3] userAddr := suite.addrs[3]
authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
// Prepare money market + reward params userAddr,
i := 0 cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for moneyMarketDenom, rewardDenoms := range tc.args.moneyMarketRewardDenoms {
// Set up multi reward periods for supply/borrow indexes with dynamic money market denoms/reward denoms
var rewardsPerSecond sdk.Coins
for _, rewardDenom := range rewardDenoms {
rewardsPerSecond = append(rewardsPerSecond, sdk.NewCoin(rewardDenom, sdk.OneInt()))
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, moneyMarketDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
// Set up generic reward periods for usdx minting/delegator indexes
if i == 0 && len(rewardDenoms) > 0 {
rewardPeriod := types.NewRewardPeriod(true, moneyMarketDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), rewardsPerSecond[i])
rewardPeriods = append(rewardPeriods, rewardPeriod)
i++
}
}
// Initialize and set incentive params
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params)
// Set each money market's previous accrual time and supply reward indexes incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
for moneyMarketDenom, rewardDenoms := range tc.args.moneyMarketRewardDenoms { for moneyMarketDenom, rewardsPerSecond := range tc.args.moneyMarketRewardDenoms {
var rewardIndexes types.RewardIndexes incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(moneyMarketDenom, rewardsPerSecond)
for _, rewardDenom := range rewardDenoms {
rewardIndex := types.NewRewardIndex(rewardDenom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, moneyMarketDenom, tc.args.initialTime)
if len(rewardIndexes) > 0 {
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, moneyMarketDenom, rewardIndexes)
}
} }
hardKeeper := suite.app.GetHardKeeper() suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
// User deposits // User deposits
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit) err := suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
suite.Require().NoError(err) suite.Require().NoError(err)
// User borrows // User borrows
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow)
suite.Require().NoError(err) suite.Require().NoError(err)
claim, foundClaim := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr) claim, foundClaim := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
@ -361,12 +338,11 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
} }
} }
func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() { func (suite *BorrowRewardsTestSuite) TestSynchronizeHardBorrowReward() {
type args struct { type args struct {
incentiveBorrowRewardDenom string incentiveBorrowRewardDenom string
borrow sdk.Coin borrow sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -389,7 +365,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000173228"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000173228"))},
expectedRewards: cs(c("hard", 12235400)), expectedRewards: cs(c("hard", 12235400)),
@ -402,7 +377,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385603126235340"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385603126235340"))},
expectedRewards: cs(c("hard", 105713856031)), expectedRewards: cs(c("hard", 105713856031)),
@ -414,7 +388,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "ukava", incentiveBorrowRewardDenom: "ukava",
borrow: c("ukava", 1), // borrow a tiny amount so that rewards round to zero borrow: c("ukava", 1), // borrow a tiny amount so that rewards round to zero
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.122354003908172328"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.122354003908172328"))},
expectedRewards: cs(), expectedRewards: cs(),
@ -427,7 +400,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.001223540000173228")), types.NewRewardIndex("hard", d("0.001223540000173228")),
@ -442,7 +414,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("10.571385603126235340")), types.NewRewardIndex("hard", d("10.571385603126235340")),
@ -457,7 +428,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.001223540000173228")), types.NewRewardIndex("hard", d("0.001223540000173228")),
@ -466,33 +436,12 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
expectedRewards: cs(c("hard", 12235400), c("ukava", 55555500)), expectedRewards: cs(c("hard", 12235400), c("ukava", 55555500)),
}, },
}, },
{
"denom is in incentive's hard borrow reward params but it has no rewards; add reward",
args{
incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000),
rewardsPerSecond: sdk.Coins{},
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{},
updateRewardsViaCommmittee: true,
updatedBaseDenom: "bnb",
updatedRewardsPerSecond: cs(c("hard", 100000)),
updatedExpectedRewards: cs(c("hard", 8640000000)),
updatedExpectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.864000000049803065")),
},
updatedTimeDuration: 86400,
},
},
{ {
"denom is in incentive's hard borrow reward params and has rewards; add new reward type", "denom is in incentive's hard borrow reward params and has rewards; add new reward type",
args{ args{
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400}, blockTimes: []int{86400},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("1.057138560060101160")), types.NewRewardIndex("hard", d("1.057138560060101160")),
@ -514,8 +463,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
args{ args{
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("zzz", 10000000000), borrow: c("zzz", 10000000000),
rewardsPerSecond: sdk.Coins{}, rewardsPerSecond: nil,
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100}, blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{}, expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{}, expectedRewards: sdk.Coins{},
@ -529,35 +477,12 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
updatedTimeDuration: 86400, updatedTimeDuration: 86400,
}, },
}, },
{
"denom incentive's hard borrow reward params but it has no rewards; add multiple reward types",
args{
incentiveBorrowRewardDenom: "bnb",
borrow: c("bnb", 10000000000),
rewardsPerSecond: sdk.Coins{},
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{},
updateRewardsViaCommmittee: true,
updatedBaseDenom: "bnb",
updatedRewardsPerSecond: cs(c("hard", 100000), c("ukava", 100500), c("swap", 500)),
updatedExpectedRewards: cs(c("hard", 8640000000), c("ukava", 8683200001), c("swap", 43200000)),
updatedExpectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.864000000049803065")),
types.NewRewardIndex("ukava", d("0.868320000050052081")),
types.NewRewardIndex("swap", d("0.004320000000249015")),
},
updatedTimeDuration: 86400,
},
},
{ {
"denom is in hard's money market params but not in incentive's hard supply reward params; add multiple reward types", "denom is in hard's money market params but not in incentive's hard supply reward params; add multiple reward types",
args{ args{
incentiveBorrowRewardDenom: "bnb", incentiveBorrowRewardDenom: "bnb",
borrow: c("zzz", 10000000000), borrow: c("zzz", 10000000000),
rewardsPerSecond: sdk.Coins{}, rewardsPerSecond: nil,
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100}, blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{}, expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{}, expectedRewards: sdk.Coins{},
@ -573,45 +498,23 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
updatedTimeDuration: 86400, updatedTimeDuration: 86400,
}, },
}, },
// TODO test synchronize when there is a reward period with 0 rewardsPerSecond
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().
WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
supplyKeeper := suite.app.GetSupplyKeeper() if tc.args.rewardsPerSecond != nil {
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(tc.args.incentiveBorrowRewardDenom, tc.args.rewardsPerSecond)
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// Set up incentive state
incentiveParams := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.incentiveBorrowRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.incentiveBorrowRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), sdk.Coins{})}, // Don't set any supply rewards for easier accounting
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.incentiveBorrowRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.incentiveBorrowRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, incentiveParams)
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, tc.args.incentiveBorrowRewardDenom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
} }
if len(rewardIndexes) > 0 {
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, tc.args.incentiveBorrowRewardDenom, rewardIndexes)
}
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
// Set the minimum borrow to 0 to allow testing small borrows // Set the minimum borrow to 0 to allow testing small borrows
hardParams := suite.hardKeeper.GetParams(suite.ctx) hardBuilder := NewHardGenStateMulti(suite.genesisTime).WithMinBorrow(sdk.ZeroDec())
hardParams.MinimumBorrowUSDValue = sdk.ZeroDec()
suite.hardKeeper.SetParams(suite.ctx, hardParams) suite.SetupWithGenState(authBuilder, incentBuilder, hardBuilder)
// Borrow a fixed amount from another user to dilute primary user's rewards per second. // Borrow a fixed amount from another user to dilute primary user's rewards per second.
suite.Require().NoError( suite.Require().NoError(
@ -622,11 +525,9 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
) )
// User deposits and borrows to increase total borrowed amount // User deposits and borrows to increase total borrowed amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
suite.Require().NoError(err) suite.Require().NoError(err)
err = hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow)) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow))
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim // Check that Hard hooks initialized a HardLiquidityProviderClaim
@ -662,7 +563,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
// After we've accumulated, run synchronize // After we've accumulated, run synchronize
borrow, found := hardKeeper.GetBorrow(suite.ctx, userAddr) borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow) suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
@ -714,7 +615,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
// Borrow denom's reward period does not exist // Borrow denom's reward period does not exist
_, found := currIncentiveHardBorrowRewardPeriods.GetMultiRewardPeriodIndex(tc.args.borrow.Denom) _, found := currIncentiveHardBorrowRewardPeriods.GetMultiRewardPeriodIndex(tc.args.borrow.Denom)
suite.Require().False(found) suite.Require().False(found)
newMultiRewardPeriod := types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.updatedRewardsPerSecond) newMultiRewardPeriod := types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, suite.genesisTime, suite.genesisTime.Add(time.Hour*24*365*4), tc.args.updatedRewardsPerSecond)
currIncentiveHardBorrowRewardPeriods = append(currIncentiveHardBorrowRewardPeriods, newMultiRewardPeriod) currIncentiveHardBorrowRewardPeriods = append(currIncentiveHardBorrowRewardPeriods, newMultiRewardPeriod)
} }
@ -742,7 +643,9 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
// 5. Committee votes and passes proposal // 5. Committee votes and passes proposal
err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberOne, committee.Yes) err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberOne, committee.Yes)
suite.Require().NoError(err)
err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberTwo, committee.Yes) err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberTwo, committee.Yes)
suite.Require().NoError(err)
// 6. Check proposal passed // 6. Check proposal passed
com, found := suite.committeeKeeper.GetCommittee(suite.ctx, 1) com, found := suite.committeeKeeper.GetCommittee(suite.ctx, 1)
@ -774,7 +677,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
suite.Require().NoError(err) suite.Require().NoError(err)
// After we've accumulated, run synchronize // After we've accumulated, run synchronize
borrow, found = hardKeeper.GetBorrow(suite.ctx, userAddr) borrow, found = suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow) suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
@ -808,7 +711,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
} }
} }
func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() { func (suite *BorrowRewardsTestSuite) TestUpdateHardBorrowIndexDenoms() {
type withdrawModification struct { type withdrawModification struct {
coins sdk.Coins coins sdk.Coins
repay bool repay bool
@ -819,7 +722,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow sdk.Coins firstBorrow sdk.Coins
modification withdrawModification modification withdrawModification
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
expectedBorrowIndexDenoms []string expectedBorrowIndexDenoms []string
} }
type test struct { type test struct {
@ -835,7 +737,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 50000000)), firstBorrow: cs(c("bnb", 50000000)),
modification: withdrawModification{coins: cs(c("ukava", 500000000))}, modification: withdrawModification{coins: cs(c("ukava", 500000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"bnb", "ukava"}, expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
}, },
}, },
@ -846,7 +747,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("btcb", 50000000)), firstBorrow: cs(c("btcb", 50000000)),
modification: withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))}, modification: withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"}, expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
}, },
}, },
@ -857,7 +757,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 50000000)), firstBorrow: cs(c("bnb", 50000000)),
modification: withdrawModification{coins: cs(c("bnb", 50000000000))}, modification: withdrawModification{coins: cs(c("bnb", 50000000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"bnb"}, expectedBorrowIndexDenoms: []string{"bnb"},
}, },
}, },
@ -868,7 +767,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 50000000)), firstBorrow: cs(c("bnb", 50000000)),
modification: withdrawModification{coins: cs(c("ukava", 500000000))}, modification: withdrawModification{coins: cs(c("ukava", 500000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"bnb", "ukava"}, expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
}, },
}, },
@ -879,7 +777,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("btcb", 50000000)), firstBorrow: cs(c("btcb", 50000000)),
modification: withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))}, modification: withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"}, expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
}, },
}, },
@ -890,7 +787,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 50000000)), firstBorrow: cs(c("bnb", 50000000)),
modification: withdrawModification{coins: cs(c("bnb", 50000000000))}, modification: withdrawModification{coins: cs(c("bnb", 50000000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"bnb"}, expectedBorrowIndexDenoms: []string{"bnb"},
}, },
}, },
@ -901,7 +797,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 100000000)), firstBorrow: cs(c("bnb", 100000000)),
modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true}, modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{}, expectedBorrowIndexDenoms: []string{},
}, },
}, },
@ -912,7 +807,6 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 100000000), c("ukava", 10000000)), firstBorrow: cs(c("bnb", 100000000), c("ukava", 10000000)),
modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true}, modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"ukava"}, expectedBorrowIndexDenoms: []string{"ukava"},
}, },
}, },
@ -923,70 +817,51 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
firstBorrow: cs(c("bnb", 100000000), c("ukava", 10000000)), firstBorrow: cs(c("bnb", 100000000), c("ukava", 10000000)),
modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true}, modification: withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedBorrowIndexDenoms: []string{"ukava"}, expectedBorrowIndexDenoms: []string{"ukava"},
}, },
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().
WithSimpleAccount(
// Mint coins to hard module account so it can service borrow requests userAddr,
supplyKeeper := suite.app.GetSupplyKeeper() cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
hardMaccCoins := tc.args.firstBorrow.Add(tc.args.modification.coins...) ).
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins) WithSimpleAccount(
suite.addrs[0],
// Set up generic reward periods cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for i, denom := range tc.args.expectedBorrowIndexDenoms {
// Create just one reward period for USDX Minting / Hard Delegator reward periods (otherwise params will panic on duplicate)
if i == 0 {
rewardPeriod := types.NewRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[i])
rewardPeriods = append(rewardPeriods, rewardPeriod)
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
}
// Setup incentive state
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params)
// Set each expected borrow denom's previous accrual time and borrow reward factor incentBuilder := NewIncentiveGenesisBuilder().
var rewardIndexes types.RewardIndexes WithGenesisTime(suite.genesisTime).
for _, rewardCoin := range tc.args.rewardsPerSecond { WithSimpleBorrowRewardPeriod("bnb", tc.args.rewardsPerSecond).
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec()) WithSimpleBorrowRewardPeriod("ukava", tc.args.rewardsPerSecond).
rewardIndexes = append(rewardIndexes, rewardIndex) WithSimpleBorrowRewardPeriod("btcb", tc.args.rewardsPerSecond).
} WithSimpleBorrowRewardPeriod("xrp", tc.args.rewardsPerSecond)
for _, denom := range tc.args.expectedBorrowIndexDenoms {
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, denom, tc.args.initialTime) suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, denom, rewardIndexes)
} // Fill the hard supply to allow user to borrow
err := suite.hardKeeper.Deposit(suite.ctx, suite.addrs[0], tc.args.firstBorrow.Add(tc.args.modification.coins...))
suite.Require().NoError(err)
// User deposits initial funds (so that user can borrow) // User deposits initial funds (so that user can borrow)
hardKeeper := suite.app.GetHardKeeper() err = suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.initialDeposit)
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.initialDeposit)
suite.Require().NoError(err) suite.Require().NoError(err)
// Confirm that claim exists but no borrow reward indexes have been added // Confirm that claim exists but no borrow reward indexes have been added
claimAfterDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimAfterDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(0, len(claimAfterDeposit.BorrowRewardIndexes)) suite.Require().Equal(0, len(claimAfterDeposit.BorrowRewardIndexes))
// User borrows (first time) // User borrows (first time)
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.firstBorrow) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, tc.args.firstBorrow)
suite.Require().NoError(err) suite.Require().NoError(err)
// Confirm that claim's borrow reward indexes have been updated // Confirm that claim's borrow reward indexes have been updated
claimAfterFirstBorrow, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimAfterFirstBorrow, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, coin := range tc.args.firstBorrow { for _, coin := range tc.args.firstBorrow {
_, hasIndex := claimAfterFirstBorrow.HasBorrowRewardIndex(coin.Denom) _, hasIndex := claimAfterFirstBorrow.HasBorrowRewardIndex(coin.Denom)
@ -996,14 +871,14 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
// User modifies their Borrow by either repaying or borrowing more // User modifies their Borrow by either repaying or borrowing more
if tc.args.modification.repay { if tc.args.modification.repay {
err = hardKeeper.Repay(suite.ctx, userAddr, userAddr, tc.args.modification.coins) err = suite.hardKeeper.Repay(suite.ctx, userAddr, userAddr, tc.args.modification.coins)
} else { } else {
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.modification.coins) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, tc.args.modification.coins)
} }
suite.Require().NoError(err) suite.Require().NoError(err)
// Confirm that claim's borrow reward indexes contain expected values // Confirm that claim's borrow reward indexes contain expected values
claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, coin := range tc.args.modification.coins { for _, coin := range tc.args.modification.coins {
_, hasIndex := claimAfterModification.HasBorrowRewardIndex(coin.Denom) _, hasIndex := claimAfterModification.HasBorrowRewardIndex(coin.Denom)
@ -1021,11 +896,10 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
} }
} }
func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() { func (suite *BorrowRewardsTestSuite) TestSimulateHardBorrowRewardSynchronization() {
type args struct { type args struct {
borrow sdk.Coin borrow sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -1041,7 +915,6 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
args{ args{
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000173228"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000173228"))},
expectedRewards: cs(c("hard", 12235400)), expectedRewards: cs(c("hard", 12235400)),
@ -1052,7 +925,6 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
args{ args{
borrow: c("bnb", 10000000000), borrow: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385603126235340"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385603126235340"))},
expectedRewards: cs(c("hard", 105713856031)), expectedRewards: cs(c("hard", 105713856031)),
@ -1061,55 +933,21 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().
supplyKeeper := suite.app.GetSupplyKeeper() WithGenesisTime(suite.genesisTime).
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) WithSimpleBorrowRewardPeriod(tc.args.borrow.Denom, tc.args.rewardsPerSecond)
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// setup incentive state suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardBorrowRewardIndexes(suite.ctx, tc.args.borrow.Denom, rewardIndexes)
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
// User deposits and borrows to increase total borrowed amount // User deposits and borrows to increase total borrowed amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
suite.Require().NoError(err) suite.Require().NoError(err)
err = hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow)) err = suite.hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow))
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
suite.Require().True(found)
multiRewardIndex, _ := claim.BorrowRewardIndexes.GetRewardIndex(tc.args.borrow.Denom)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
currRewardIndex, found := multiRewardIndex.RewardIndexes.GetRewardIndex(expectedRewardIndex.CollateralType)
suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), currRewardIndex.RewardFactor)
}
// Run accumulator at several intervals // Run accumulator at several intervals
var timeElapsed int var timeElapsed int
previousBlockTime := suite.ctx.BlockTime() previousBlockTime := suite.ctx.BlockTime()
@ -1132,7 +970,7 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
// Confirm that the user's claim hasn't been synced // Confirm that the user's claim hasn't been synced
claimPre, foundPre := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimPre, foundPre := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(foundPre) suite.Require().True(foundPre)
multiRewardIndexPre, _ := claimPre.BorrowRewardIndexes.GetRewardIndex(tc.args.borrow.Denom) multiRewardIndexPre, _ := claimPre.BorrowRewardIndexes.GetRewardIndex(tc.args.borrow.Denom)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes { for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
@ -1142,7 +980,7 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
} }
// Check that the synced claim held in memory has properly simulated syncing // Check that the synced claim held in memory has properly simulated syncing
syncedClaim := suite.keeper.SimulateHardSynchronization(suite.ctx, claim) syncedClaim := suite.keeper.SimulateHardSynchronization(suite.ctx, claimPre)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes { for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
// Check that the user's claim's reward index matches the expected reward index // Check that the user's claim's reward index matches the expected reward index
multiRewardIndex, found := syncedClaim.BorrowRewardIndexes.GetRewardIndex(tc.args.borrow.Denom) multiRewardIndex, found := syncedClaim.BorrowRewardIndexes.GetRewardIndex(tc.args.borrow.Denom)
@ -1160,3 +998,7 @@ func (suite *KeeperTestSuite) TestSimulateHardBorrowRewardSynchronization() {
}) })
} }
} }
func TestBorrowRewardsTestSuite(t *testing.T) {
suite.Run(t, new(BorrowRewardsTestSuite))
}

View File

@ -1,23 +1,73 @@
package keeper_test package keeper_test
import ( import (
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
"github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/app"
hardtypes "github.com/kava-labs/kava/x/hard/types" "github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
) )
func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() { // Test suite used for all keeper tests
type DelegatorRewardsTestSuite struct {
suite.Suite
keeper keeper.Keeper
stakingKeeper stakingkeeper.Keeper
app app.TestApp
ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress
validatorAddrs []sdk.ValAddress
}
// SetupTest is run automatically before each suite test
func (suite *DelegatorRewardsTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, allAddrs := app.GeneratePrivKeyAddressPairs(10)
suite.addrs = allAddrs[:5]
for _, a := range allAddrs[5:] {
suite.validatorAddrs = append(suite.validatorAddrs, sdk.ValAddress(a))
}
suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
}
func (suite *DelegatorRewardsTestSuite) SetupApp() {
suite.app = app.NewTestApp()
suite.keeper = suite.app.GetIncentiveKeeper()
suite.stakingKeeper = suite.app.GetStakingKeeper()
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
}
func (suite *DelegatorRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder) {
suite.SetupApp()
suite.app.InitializeFromGenesisStatesWithTime(
suite.genesisTime,
authBuilder.BuildMarshalled(),
NewStakingGenesisState(),
incentBuilder.BuildMarshalled(),
)
}
func (suite *DelegatorRewardsTestSuite) TestAccumulateHardDelegatorRewards() {
type args struct { type args struct {
delegation sdk.Coin delegation sdk.Coin
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
timeElapsed int timeElapsed int
expectedRewardFactor sdk.Dec expectedRewardFactor sdk.Dec
} }
@ -31,7 +81,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7, timeElapsed: 7,
expectedRewardFactor: d("0.428239000000000000"), expectedRewardFactor: d("0.428239000000000000"),
}, },
@ -41,7 +90,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardFactor: d("5285.692800000000000000"), expectedRewardFactor: d("5285.692800000000000000"),
}, },
@ -51,7 +99,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 0, timeElapsed: 0,
expectedRewardFactor: d("0.0"), expectedRewardFactor: d("0.0"),
}, },
@ -59,29 +106,15 @@ func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().
supplyKeeper := suite.app.GetSupplyKeeper() WithGenesisTime(suite.genesisTime).
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// Set up incentive state suite.SetupWithGenState(authBuilder, incentBuilder)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom, sdk.ZeroDec())
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation) err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation)
suite.Require().NoError(err) suite.Require().NoError(err)
@ -94,25 +127,21 @@ func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed)) runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
runCtx := suite.ctx.WithBlockTime(runAtTime) runCtx := suite.ctx.WithBlockTime(runAtTime)
// Run Hard begin blocker in order to update the denom's index factor
hard.BeginBlocker(runCtx, suite.hardKeeper)
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(runCtx, tc.args.delegation.Denom) rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(runCtx, tc.args.delegation.Denom)
suite.Require().True(found) suite.Require().True(found)
err = suite.keeper.AccumulateHardDelegatorRewards(runCtx, rewardPeriod) err = suite.keeper.AccumulateHardDelegatorRewards(runCtx, rewardPeriod)
suite.Require().NoError(err) suite.Require().NoError(err)
rewardFactor, found := suite.keeper.GetHardDelegatorRewardFactor(runCtx, tc.args.delegation.Denom) rewardFactor, _ := suite.keeper.GetHardDelegatorRewardFactor(runCtx, tc.args.delegation.Denom)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor) suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
}) })
} }
} }
func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() { func (suite *DelegatorRewardsTestSuite) TestSynchronizeHardDelegatorReward() {
type args struct { type args struct {
delegation sdk.Coin delegation sdk.Coin
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardFactor sdk.Dec expectedRewardFactor sdk.Dec
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -128,7 +157,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardFactor: d("6.117700000000000000"), expectedRewardFactor: d("6.117700000000000000"),
expectedRewards: cs(c("hard", 6117700)), expectedRewards: cs(c("hard", 6117700)),
@ -139,7 +167,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: c("hard", 122354), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardFactor: d("52856.928000000000000000"), expectedRewardFactor: d("52856.928000000000000000"),
expectedRewards: cs(c("hard", 52856928000)), expectedRewards: cs(c("hard", 52856928000)),
@ -150,7 +177,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
args{ args{
delegation: c("ukava", 1), delegation: c("ukava", 1),
rewardsPerSecond: c("hard", 1), rewardsPerSecond: c("hard", 1),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardFactor: d("0.000099999900000100"), expectedRewardFactor: d("0.000099999900000100"),
expectedRewards: nil, expectedRewards: nil,
@ -159,29 +185,15 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().
supplyKeeper := suite.app.GetSupplyKeeper() WithGenesisTime(suite.genesisTime).
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// setup incentive state suite.SetupWithGenState(authBuilder, incentBuilder)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom, sdk.ZeroDec())
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
// Create validator account // Create validator account
staking.BeginBlocker(suite.ctx, suite.stakingKeeper) staking.BeginBlocker(suite.ctx, suite.stakingKeeper)
@ -214,9 +226,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
previousBlockTime = updatedBlockTime previousBlockTime = updatedBlockTime
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime) blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
// Run Hard begin blocker for each block ctx to update denom's interest factor
hard.BeginBlocker(blockCtx, suite.hardKeeper)
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom) rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom)
suite.Require().True(found) suite.Require().True(found)
@ -232,7 +241,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
}) })
// Check that reward factor and claim have been updated as expected // Check that reward factor and claim have been updated as expected
rewardFactor, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom) rewardFactor, _ := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor) suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0]) claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
@ -243,11 +252,10 @@ func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
} }
} }
func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() { func (suite *DelegatorRewardsTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
type args struct { type args struct {
delegation sdk.Coin delegation sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coin
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -262,8 +270,7 @@ func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
"10 blocks", "10 blocks",
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("6.117700000000000000"))}, // Here the reward index stores data differently than inside a MultiRewardIndex expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("6.117700000000000000"))}, // Here the reward index stores data differently than inside a MultiRewardIndex
expectedRewards: cs(c("hard", 6117700)), expectedRewards: cs(c("hard", 6117700)),
@ -273,8 +280,7 @@ func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
"10 blocks - long block time", "10 blocks - long block time",
args{ args{
delegation: c("ukava", 1_000_000), delegation: c("ukava", 1_000_000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("52856.928000000000000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("ukava", d("52856.928000000000000000"))},
expectedRewards: cs(c("hard", 52856928000)), expectedRewards: cs(c("hard", 52856928000)),
@ -284,29 +290,15 @@ func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().
supplyKeeper := suite.app.GetSupplyKeeper() WithGenesisTime(suite.genesisTime).
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// setup incentive state suite.SetupWithGenState(authBuilder, incentBuilder)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom, sdk.ZeroDec())
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
// Delegator delegates // Delegator delegates
err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation) err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation)
@ -330,9 +322,6 @@ func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
previousBlockTime = updatedBlockTime previousBlockTime = updatedBlockTime
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime) blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
// Run Hard begin blocker for each block ctx to update denom's interest factor
hard.BeginBlocker(blockCtx, suite.hardKeeper)
// Accumulate hard delegator rewards // Accumulate hard delegator rewards
rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom) rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom)
suite.Require().True(found) suite.Require().True(found)
@ -360,7 +349,7 @@ func (suite *KeeperTestSuite) TestSimulateHardDelegatorRewardSynchronization() {
} }
} }
func (suite *KeeperTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error { func (suite *DelegatorRewardsTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error {
msg := staking.NewMsgCreateValidator( msg := staking.NewMsgCreateValidator(
address, address,
ed25519.GenPrivKey().PubKey(), ed25519.GenPrivKey().PubKey(),
@ -374,7 +363,7 @@ func (suite *KeeperTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address
return err return err
} }
func (suite *KeeperTestSuite) deliverMsgDelegate(ctx sdk.Context, delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error { func (suite *DelegatorRewardsTestSuite) deliverMsgDelegate(ctx sdk.Context, delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error {
msg := staking.NewMsgDelegate( msg := staking.NewMsgDelegate(
delegator, delegator,
validator, validator,
@ -385,7 +374,7 @@ func (suite *KeeperTestSuite) deliverMsgDelegate(ctx sdk.Context, delegator sdk.
return err return err
} }
func (suite *KeeperTestSuite) deliverMsgRedelegate(ctx sdk.Context, delegator sdk.AccAddress, sourceValidator, destinationValidator sdk.ValAddress, amount sdk.Coin) error { func (suite *DelegatorRewardsTestSuite) deliverMsgRedelegate(ctx sdk.Context, delegator sdk.AccAddress, sourceValidator, destinationValidator sdk.ValAddress, amount sdk.Coin) error {
msg := staking.NewMsgBeginRedelegate( msg := staking.NewMsgBeginRedelegate(
delegator, delegator,
sourceValidator, sourceValidator,
@ -398,28 +387,24 @@ func (suite *KeeperTestSuite) deliverMsgRedelegate(ctx sdk.Context, delegator sd
} }
// given a user has a delegation to a bonded validator, when the validator starts unbonding, the user does not accumulate rewards // given a user has a delegation to a bonded validator, when the validator starts unbonding, the user does not accumulate rewards
func (suite *KeeperTestSuite) TestUnbondingValidatorSyncsClaim() { func (suite *DelegatorRewardsTestSuite) TestUnbondingValidatorSyncsClaim() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
initialTime := time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
suite.ctx = suite.ctx.WithBlockTime(initialTime) WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
blockDuration := 10 * time.Second WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[2]), cs(c("ukava", 1e9)))
// Setup incentive state
rewardsPerSecond := c("hard", 122354) rewardsPerSecond := c("hard", 122354)
bondDenom := "ukava" bondDenom := "ukava"
params := types.NewParams(
nil, incentBuilder := NewIncentiveGenesisBuilder().
nil, WithGenesisTime(suite.genesisTime).
nil, WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
types.RewardPeriods{
types.NewRewardPeriod(true, bondDenom, initialTime.Add(-1*oneYear), initialTime.Add(4*oneYear), rewardsPerSecond), suite.SetupWithGenState(authBuilder, incentBuilder)
},
types.DefaultMultipliers, blockDuration := 10 * time.Second
initialTime.Add(5*oneYear),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, bondDenom, initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, bondDenom, sdk.ZeroDec())
// Reduce the size of the validator set // Reduce the size of the validator set
stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx) stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx)
@ -437,7 +422,7 @@ func (suite *KeeperTestSuite) TestUnbondingValidatorSyncsClaim() {
// End the block so top validators become bonded // End the block so top validators become bonded
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(1 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(1 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Delegate to a bonded validator from the test user. This will initialize their incentive claim. // Delegate to a bonded validator from the test user. This will initialize their incentive claim.
@ -446,7 +431,7 @@ func (suite *KeeperTestSuite) TestUnbondingValidatorSyncsClaim() {
// Start a new block to accumulate some delegation rewards for the user. // Start a new block to accumulate some delegation rewards for the user.
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(2 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(2 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Delegate to the unbonded validator to push it into the bonded validator set, pushing out the user's delegated validator // Delegate to the unbonded validator to push it into the bonded validator set, pushing out the user's delegated validator
@ -473,7 +458,7 @@ func (suite *KeeperTestSuite) TestUnbondingValidatorSyncsClaim() {
) )
// Run another block and check the claim is not accumulating more rewards // Run another block and check the claim is not accumulating more rewards
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(3 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(3 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{})
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, suite.addrs[0], nil, false) suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, suite.addrs[0], nil, false)
@ -492,28 +477,24 @@ func (suite *KeeperTestSuite) TestUnbondingValidatorSyncsClaim() {
} }
// given a user has a delegation to an unbonded validator, when the validator becomes bonded, the user starts accumulating rewards // given a user has a delegation to an unbonded validator, when the validator becomes bonded, the user starts accumulating rewards
func (suite *KeeperTestSuite) TestBondingValidatorSyncsClaim() { func (suite *DelegatorRewardsTestSuite) TestBondingValidatorSyncsClaim() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
initialTime := time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
suite.ctx = suite.ctx.WithBlockTime(initialTime) WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
blockDuration := 10 * time.Second WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9))).
WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[2]), cs(c("ukava", 1e9)))
// Setup incentive state
rewardsPerSecond := c("hard", 122354) rewardsPerSecond := c("hard", 122354)
bondDenom := "ukava" bondDenom := "ukava"
params := types.NewParams(
nil, incentBuilder := NewIncentiveGenesisBuilder().
nil, WithGenesisTime(suite.genesisTime).
nil, WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
types.RewardPeriods{
types.NewRewardPeriod(true, bondDenom, initialTime.Add(-1*oneYear), initialTime.Add(4*oneYear), rewardsPerSecond), suite.SetupWithGenState(authBuilder, incentBuilder)
},
types.DefaultMultipliers, blockDuration := 10 * time.Second
initialTime.Add(5*oneYear),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, bondDenom, initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, bondDenom, sdk.ZeroDec())
// Reduce the size of the validator set // Reduce the size of the validator set
stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx) stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx)
@ -531,7 +512,7 @@ func (suite *KeeperTestSuite) TestBondingValidatorSyncsClaim() {
// End the block so top validators become bonded // End the block so top validators become bonded
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(1 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(1 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Delegate to an unbonded validator from the test user. This will initialize their incentive claim. // Delegate to an unbonded validator from the test user. This will initialize their incentive claim.
@ -540,7 +521,7 @@ func (suite *KeeperTestSuite) TestBondingValidatorSyncsClaim() {
// Start a new block to accumulate some delegation rewards globally. // Start a new block to accumulate some delegation rewards globally.
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(2 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(2 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{})
// Delegate to the user's unbonded validator to push it into the bonded validator set // Delegate to the user's unbonded validator to push it into the bonded validator set
@ -567,7 +548,7 @@ func (suite *KeeperTestSuite) TestBondingValidatorSyncsClaim() {
) )
// Run another block and check the claim is accumulating more rewards // Run another block and check the claim is accumulating more rewards
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(3 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(3 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{})
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, suite.addrs[0], nil, false) suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, suite.addrs[0], nil, false)
@ -586,28 +567,22 @@ func (suite *KeeperTestSuite) TestBondingValidatorSyncsClaim() {
} }
// If a validator is slashed delegators should have their claims synced // If a validator is slashed delegators should have their claims synced
func (suite *KeeperTestSuite) TestSlashingValidatorSyncsClaim() { func (suite *DelegatorRewardsTestSuite) TestSlashingValidatorSyncsClaim() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
initialTime := time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
suite.ctx = suite.ctx.WithBlockTime(initialTime) WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
blockDuration := 10 * time.Second WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9)))
// Setup incentive state
rewardsPerSecond := c("hard", 122354) rewardsPerSecond := c("hard", 122354)
bondDenom := "ukava" bondDenom := "ukava"
params := types.NewParams(
nil, incentBuilder := NewIncentiveGenesisBuilder().
nil, WithGenesisTime(suite.genesisTime).
nil, WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
types.RewardPeriods{
types.NewRewardPeriod(true, bondDenom, initialTime.Add(-1*oneYear), initialTime.Add(4*oneYear), rewardsPerSecond), suite.SetupWithGenState(authBuilder, incentBuilder)
},
types.DefaultMultipliers, blockDuration := 10 * time.Second
initialTime.Add(5*oneYear),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, bondDenom, initialTime.Add(-1*blockDuration))
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, bondDenom, sdk.ZeroDec())
// Reduce the size of the validator set // Reduce the size of the validator set
stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx) stakingParams := suite.app.GetStakingKeeper().GetParams(suite.ctx)
@ -623,7 +598,7 @@ func (suite *KeeperTestSuite) TestSlashingValidatorSyncsClaim() {
// End the block so validators become bonded // End the block so validators become bonded
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(1 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(1 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Delegate to a bonded validator from the test user. This will initialize their incentive claim. // Delegate to a bonded validator from the test user. This will initialize their incentive claim.
@ -642,7 +617,7 @@ func (suite *KeeperTestSuite) TestSlashingValidatorSyncsClaim() {
// Start a new block to accumulate some delegation rewards for the user. // Start a new block to accumulate some delegation rewards for the user.
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(2 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(2 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Fetch validator and slash them // Fetch validator and slash them
@ -673,28 +648,23 @@ func (suite *KeeperTestSuite) TestSlashingValidatorSyncsClaim() {
} }
// Given a delegation to a bonded validator, when a user redelegates everything to another (bonded) validator, the user's claim is synced // Given a delegation to a bonded validator, when a user redelegates everything to another (bonded) validator, the user's claim is synced
func (suite *KeeperTestSuite) TestRedelegationSyncsClaim() { func (suite *DelegatorRewardsTestSuite) TestRedelegationSyncsClaim() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().
initialTime := time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC) WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
suite.ctx = suite.ctx.WithBlockTime(initialTime) WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9))).
blockDuration := 10 * time.Second WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[1]), cs(c("ukava", 1e9)))
// Setup incentive state
rewardsPerSecond := c("hard", 122354) rewardsPerSecond := c("hard", 122354)
bondDenom := "ukava" bondDenom := "ukava"
params := types.NewParams(
nil, incentBuilder := NewIncentiveGenesisBuilder().
nil, WithGenesisTime(suite.genesisTime).
nil, WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
types.RewardPeriods{
types.NewRewardPeriod(true, bondDenom, initialTime.Add(-1*oneYear), initialTime.Add(4*oneYear), rewardsPerSecond), suite.SetupWithGenState(authBuilder, incentBuilder)
},
types.DefaultMultipliers, suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime)
initialTime.Add(5*oneYear), blockDuration := 10 * time.Second
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, bondDenom, initialTime)
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, bondDenom, sdk.ZeroDec())
// Create 2 validators // Create 2 validators
err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], c(bondDenom, 10_000_000)) err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], c(bondDenom, 10_000_000))
@ -708,7 +678,7 @@ func (suite *KeeperTestSuite) TestRedelegationSyncsClaim() {
// Start a new block to accumulate some delegation rewards globally. // Start a new block to accumulate some delegation rewards globally.
_ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{}) _ = suite.app.EndBlocker(suite.ctx, abci.RequestEndBlock{})
suite.ctx = suite.ctx.WithBlockTime(initialTime.Add(1 * blockDuration)) suite.ctx = suite.ctx.WithBlockTime(suite.genesisTime.Add(1 * blockDuration))
_ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{}) // height and time in header are ignored by module begin blockers
// Redelegate the user's delegation between the two validators. This should trigger hooks that sync the user's claim. // Redelegate the user's delegation between the two validators. This should trigger hooks that sync the user's claim.
@ -729,3 +699,7 @@ func (suite *KeeperTestSuite) TestRedelegationSyncsClaim() {
claim.Reward, claim.Reward,
) )
} }
func TestDelegatorRewardsTestSuite(t *testing.T) {
suite.Run(t, new(DelegatorRewardsTestSuite))
}

View File

@ -1,23 +1,75 @@
package keeper_test package keeper_test
import ( import (
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/params"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/committee" "github.com/kava-labs/kava/x/committee"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
"github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/hard"
hardtypes "github.com/kava-labs/kava/x/hard/types" hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
"github.com/kava-labs/kava/x/incentive/keeper"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
) )
func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() { // Test suite used for all keeper tests
type SupplyRewardsTestSuite struct {
suite.Suite
keeper keeper.Keeper
hardKeeper hardkeeper.Keeper
committeeKeeper committeekeeper.Keeper
app app.TestApp
ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress
}
// SetupTest is run automatically before each suite test
func (suite *SupplyRewardsTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
}
func (suite *SupplyRewardsTestSuite) SetupApp() {
suite.app = app.NewTestApp()
suite.keeper = suite.app.GetIncentiveKeeper()
suite.hardKeeper = suite.app.GetHardKeeper()
suite.committeeKeeper = suite.app.GetCommitteeKeeper()
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
}
func (suite *SupplyRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
suite.SetupApp()
suite.app.InitializeFromGenesisStatesWithTime(
suite.genesisTime,
authBuilder.BuildMarshalled(),
NewPricefeedGenStateMultiFromTime(suite.genesisTime),
hardBuilder.BuildMarshalled(),
NewCommitteeGenesisState(suite.addrs[:2]), // TODO add committee members to suite
incentBuilder.BuildMarshalled(),
)
}
func (suite *SupplyRewardsTestSuite) TestAccumulateHardSupplyRewards() {
type args struct { type args struct {
deposit sdk.Coin deposit sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
timeElapsed int timeElapsed int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
} }
@ -31,7 +83,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7, timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.000000856478000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.000000856478000000"))},
}, },
@ -41,7 +92,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.010571385600000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.010571385600000000"))},
}, },
@ -51,7 +101,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 0, timeElapsed: 0,
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.0"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.0"))},
}, },
@ -61,7 +110,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7, timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.000000856478000000")), types.NewRewardIndex("hard", d("0.000000856478000000")),
@ -74,7 +122,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.010571385600000000")), types.NewRewardIndex("hard", d("0.010571385600000000")),
@ -87,7 +134,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 0, timeElapsed: 0,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.0")), types.NewRewardIndex("hard", d("0.0")),
@ -100,7 +146,6 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
args{ args{
deposit: c("bnb", 1000000000000), deposit: c("bnb", 1000000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.010571385600000000")), types.NewRewardIndex("hard", d("0.010571385600000000")),
@ -108,55 +153,26 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
}, },
}, },
}, },
{ // TODO test accumulate when there is a reward period with 0 rewardsPerSecond
"single reward denom, no rewards",
args{
deposit: c("bnb", 1000000000000),
rewardsPerSecond: sdk.Coins{},
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
timeElapsed: 7,
expectedRewardIndexes: types.RewardIndexes{},
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
userAddr,
// Mint coins to hard module account cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// Set up incentive state
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params) // suite.SetupWithGenState(authBuilder)
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime) incentBuilder := NewIncentiveGenesisBuilder().
var rewardIndexes types.RewardIndexes WithGenesisTime(suite.genesisTime)
for _, rewardCoin := range tc.args.rewardsPerSecond { if tc.args.rewardsPerSecond != nil {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec()) incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(tc.args.deposit.Denom, tc.args.rewardsPerSecond)
rewardIndexes = append(rewardIndexes, rewardIndex)
}
if len(rewardIndexes) > 0 {
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, tc.args.deposit.Denom, rewardIndexes)
} }
// Set up hard state (interest factor for the relevant denom) suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.deposit.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
// User deposits to increase total supplied amount // User deposits to increase total supplied amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
suite.Require().NoError(err) suite.Require().NoError(err)
// Set up chain context at future time // Set up chain context at future time
@ -189,12 +205,11 @@ func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
} }
} }
func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() { func (suite *SupplyRewardsTestSuite) TestInitializeHardSupplyRewards() {
type args struct { type args struct {
moneyMarketRewardDenoms map[string][]string moneyMarketRewardDenoms map[string]sdk.Coins
deposit sdk.Coins deposit sdk.Coins
initialTime time.Time
expectedClaimSupplyRewardIndexes types.MultiRewardIndexes expectedClaimSupplyRewardIndexes types.MultiRewardIndexes
} }
type test struct { type test struct {
@ -202,10 +217,9 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args args args args
} }
standardMoneyMarketRewardDenoms := map[string][]string{ standardMoneyMarketRewardDenoms := map[string]sdk.Coins{
"bnb": {"hard"}, "bnb": cs(c("hard", 1)),
"btcb": {"hard", "ukava"}, "btcb": cs(c("hard", 1), c("ukava", 1)),
"xrp": {},
} }
testCases := []test{ testCases := []test{
@ -214,7 +228,6 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args{ args{
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000)), deposit: cs(c("bnb", 1000000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{ expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -230,7 +243,6 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args{ args{
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("btcb", 1000000000000)), deposit: cs(c("btcb", 1000000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{ expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"btcb", "btcb",
@ -247,7 +259,6 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args{ args{
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("xrp", 1000000000000)), deposit: cs(c("xrp", 1000000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{ expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"xrp", "xrp",
@ -261,7 +272,6 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args{ args{
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000), c("btcb", 1000000000000)), deposit: cs(c("bnb", 1000000000000), c("btcb", 1000000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{ expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -284,7 +294,6 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
args{ args{
moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms, moneyMarketRewardDenoms: standardMoneyMarketRewardDenoms,
deposit: cs(c("bnb", 1000000000000), c("xrp", 1000000000000)), deposit: cs(c("bnb", 1000000000000), c("xrp", 1000000000000)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{ expectedClaimSupplyRewardIndexes: types.MultiRewardIndexes{
types.NewMultiRewardIndex( types.NewMultiRewardIndex(
"bnb", "bnb",
@ -302,61 +311,20 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// Mint coins to hard module account
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
userAddr := suite.addrs[3] userAddr := suite.addrs[3]
authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
// Prepare money market + reward params userAddr,
i := 0 cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for moneyMarketDenom, rewardDenoms := range tc.args.moneyMarketRewardDenoms {
// Set up multi reward periods for supply/borrow indexes with dynamic money market denoms/reward denoms
var rewardsPerSecond sdk.Coins
for _, rewardDenom := range rewardDenoms {
rewardsPerSecond = append(rewardsPerSecond, sdk.NewCoin(rewardDenom, sdk.OneInt()))
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, moneyMarketDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
// Set up generic reward periods for usdx minting/delegator indexes
if i == 0 && len(rewardDenoms) > 0 {
rewardPeriod := types.NewRewardPeriod(true, moneyMarketDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), rewardsPerSecond[i])
rewardPeriods = append(rewardPeriods, rewardPeriod)
i++
}
}
// Initialize and set incentive params
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params)
// Set each money market's previous accrual time and supply reward indexes incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
for moneyMarketDenom, rewardDenoms := range tc.args.moneyMarketRewardDenoms { for moneyMarketDenom, rewardsPerSecond := range tc.args.moneyMarketRewardDenoms {
var rewardIndexes types.RewardIndexes incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(moneyMarketDenom, rewardsPerSecond)
for _, rewardDenom := range rewardDenoms {
rewardIndex := types.NewRewardIndex(rewardDenom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, moneyMarketDenom, tc.args.initialTime)
if len(rewardIndexes) > 0 {
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, moneyMarketDenom, rewardIndexes)
}
} }
suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
// User deposits // User deposits
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
suite.Require().NoError(err) suite.Require().NoError(err)
claim, foundClaim := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr) claim, foundClaim := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
@ -366,12 +334,11 @@ func (suite *KeeperTestSuite) TestInitializeHardSupplyRewards() {
} }
} }
func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() { func (suite *SupplyRewardsTestSuite) TestSynchronizeHardSupplyReward() {
type args struct { type args struct {
incentiveSupplyRewardDenom string incentiveSupplyRewardDenom string
deposit sdk.Coin deposit sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -394,7 +361,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000000000"))},
expectedRewards: cs(c("hard", 12235400)), expectedRewards: cs(c("hard", 12235400)),
@ -407,7 +373,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385600000000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385600000000000"))},
expectedRewards: cs(c("hard", 105713856000)), expectedRewards: cs(c("hard", 105713856000)),
@ -420,7 +385,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "ukava", incentiveSupplyRewardDenom: "ukava",
deposit: c("ukava", 1), deposit: c("ukava", 1),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.122353998776460010"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.122353998776460010"))},
expectedRewards: cs(), expectedRewards: cs(),
@ -433,7 +397,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.001223540000000000")), types.NewRewardIndex("hard", d("0.001223540000000000")),
@ -449,7 +412,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("10.571385600000000000")), types.NewRewardIndex("hard", d("10.571385600000000000")),
@ -465,7 +427,6 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 555555)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.001223540000000000")), types.NewRewardIndex("hard", d("0.001223540000000000")),
@ -475,33 +436,12 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
updateRewardsViaCommmittee: false, updateRewardsViaCommmittee: false,
}, },
}, },
{
"denom is in incentive's hard supply reward params but it has no rewards; add reward",
args{
incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000),
rewardsPerSecond: sdk.Coins{},
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{},
updateRewardsViaCommmittee: true,
updatedBaseDenom: "bnb",
updatedRewardsPerSecond: cs(c("hard", 100000)),
updatedExpectedRewards: cs(c("hard", 8640000000)),
updatedExpectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.864")),
},
updatedTimeDuration: 86400,
},
},
{ {
"denom is in incentive's hard supply reward params and has rewards; add new reward type", "denom is in incentive's hard supply reward params and has rewards; add new reward type",
args{ args{
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400}, blockTimes: []int{86400},
expectedRewardIndexes: types.RewardIndexes{ expectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("1.057138560000000000")), types.NewRewardIndex("hard", d("1.057138560000000000")),
@ -523,8 +463,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
args{ args{
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("zzz", 10000000000), deposit: c("zzz", 10000000000),
rewardsPerSecond: sdk.Coins{}, rewardsPerSecond: nil,
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100}, blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{}, expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{}, expectedRewards: sdk.Coins{},
@ -538,35 +477,12 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
updatedTimeDuration: 86400, updatedTimeDuration: 86400,
}, },
}, },
{
"denom incentive's hard supply reward params but it has no rewards; add multiple reward types",
args{
incentiveSupplyRewardDenom: "bnb",
deposit: c("bnb", 10000000000),
rewardsPerSecond: sdk.Coins{},
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{},
updateRewardsViaCommmittee: true,
updatedBaseDenom: "bnb",
updatedRewardsPerSecond: cs(c("hard", 100000), c("ukava", 100500), c("swap", 500)),
updatedExpectedRewards: cs(c("hard", 8640000000), c("ukava", 8683200000), c("swap", 43200000)),
updatedExpectedRewardIndexes: types.RewardIndexes{
types.NewRewardIndex("hard", d("0.864")),
types.NewRewardIndex("ukava", d("0.86832")),
types.NewRewardIndex("swap", d("0.00432")),
},
updatedTimeDuration: 86400,
},
},
{ {
"denom is in hard's money market params but not in incentive's hard supply reward params; add multiple reward types", "denom is in hard's money market params but not in incentive's hard supply reward params; add multiple reward types",
args{ args{
incentiveSupplyRewardDenom: "bnb", incentiveSupplyRewardDenom: "bnb",
deposit: c("zzz", 10000000000), deposit: c("zzz", 10000000000),
rewardsPerSecond: sdk.Coins{}, rewardsPerSecond: nil,
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{100}, blockTimes: []int{100},
expectedRewardIndexes: types.RewardIndexes{}, expectedRewardIndexes: types.RewardIndexes{},
expectedRewards: sdk.Coins{}, expectedRewards: sdk.Coins{},
@ -582,41 +498,21 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
updatedTimeDuration: 86400, updatedTimeDuration: 86400,
}, },
}, },
// TODO test synchronize when there is a reward period with 0 rewardsPerSecond
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().
WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
// Mint coins to hard module account incentBuilder := NewIncentiveGenesisBuilder().
supplyKeeper := suite.app.GetSupplyKeeper() WithGenesisTime(suite.genesisTime)
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000))) if tc.args.rewardsPerSecond != nil {
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins) incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(tc.args.incentiveSupplyRewardDenom, tc.args.rewardsPerSecond)
// Set up incentive state
incentiveParams := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), c("hard", 1))},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, incentiveParams)
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime)
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
} }
if len(rewardIndexes) > 0 { suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, tc.args.incentiveSupplyRewardDenom, rewardIndexes)
}
// Set up hard state (interest factor for the relevant denom)
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.incentiveSupplyRewardDenom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.incentiveSupplyRewardDenom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.incentiveSupplyRewardDenom, tc.args.initialTime)
// Deposit a fixed amount from another user to dilute primary user's rewards per second. // Deposit a fixed amount from another user to dilute primary user's rewards per second.
suite.Require().NoError( suite.Require().NoError(
@ -624,9 +520,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
) )
// User deposits and borrows to increase total borrowed amount // User deposits and borrows to increase total borrowed amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim with 0 reward indexes // Check that Hard hooks initialized a HardLiquidityProviderClaim with 0 reward indexes
@ -662,7 +556,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
// After we've accumulated, run synchronize // After we've accumulated, run synchronize
deposit, found := hardKeeper.GetDeposit(suite.ctx, userAddr) deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit) suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
@ -714,7 +608,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
// Deposit denom's reward period does not exist // Deposit denom's reward period does not exist
_, found := currIncentiveHardSupplyRewardPeriods.GetMultiRewardPeriodIndex(tc.args.deposit.Denom) _, found := currIncentiveHardSupplyRewardPeriods.GetMultiRewardPeriodIndex(tc.args.deposit.Denom)
suite.Require().False(found) suite.Require().False(found)
newMultiRewardPeriod := types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.updatedRewardsPerSecond) newMultiRewardPeriod := types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, suite.genesisTime, suite.genesisTime.Add(time.Hour*24*365*4), tc.args.updatedRewardsPerSecond)
currIncentiveHardSupplyRewardPeriods = append(currIncentiveHardSupplyRewardPeriods, newMultiRewardPeriod) currIncentiveHardSupplyRewardPeriods = append(currIncentiveHardSupplyRewardPeriods, newMultiRewardPeriod)
} }
@ -742,7 +636,9 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
// 5. Committee votes and passes proposal // 5. Committee votes and passes proposal
err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberOne, committee.Yes) err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberOne, committee.Yes)
suite.Require().NoError(err)
err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberTwo, committee.Yes) err = suite.committeeKeeper.AddVote(suite.ctx, proposalID, committeeMemberTwo, committee.Yes)
suite.Require().NoError(err)
// 6. Check proposal passed // 6. Check proposal passed
com, found := suite.committeeKeeper.GetCommittee(suite.ctx, 1) com, found := suite.committeeKeeper.GetCommittee(suite.ctx, 1)
@ -773,7 +669,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
suite.Require().NoError(err) suite.Require().NoError(err)
// After we've accumulated, run synchronize // After we've accumulated, run synchronize
deposit, found = hardKeeper.GetDeposit(suite.ctx, userAddr) deposit, found = suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit) suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
@ -807,7 +703,7 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
} }
} }
func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() { func (suite *SupplyRewardsTestSuite) TestUpdateHardSupplyIndexDenoms() {
type depositModification struct { type depositModification struct {
coins sdk.Coins coins sdk.Coins
withdraw bool withdraw bool
@ -817,7 +713,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit sdk.Coins firstDeposit sdk.Coins
modification depositModification modification depositModification
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
expectedSupplyIndexDenoms []string expectedSupplyIndexDenoms []string
} }
type test struct { type test struct {
@ -832,7 +727,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("ukava", 10000000000))}, modification: depositModification{coins: cs(c("ukava", 10000000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb", "ukava"}, expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
}, },
}, },
@ -842,7 +736,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))}, modification: depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"}, expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
}, },
}, },
@ -852,7 +745,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("bnb", 5000000000))}, modification: depositModification{coins: cs(c("bnb", 5000000000))},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb"}, expectedSupplyIndexDenoms: []string{"bnb"},
}, },
}, },
@ -862,7 +754,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("ukava", 10000000000))}, modification: depositModification{coins: cs(c("ukava", 10000000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb", "ukava"}, expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
}, },
}, },
@ -872,7 +763,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))}, modification: depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"}, expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
}, },
}, },
@ -882,7 +772,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 10000000000)), firstDeposit: cs(c("bnb", 10000000000)),
modification: depositModification{coins: cs(c("bnb", 5000000000))}, modification: depositModification{coins: cs(c("bnb", 5000000000))},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"bnb"}, expectedSupplyIndexDenoms: []string{"bnb"},
}, },
}, },
@ -892,7 +781,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 1000000000)), firstDeposit: cs(c("bnb", 1000000000)),
modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true}, modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{}, expectedSupplyIndexDenoms: []string{},
}, },
}, },
@ -902,7 +790,6 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 1000000000), c("ukava", 100000000)), firstDeposit: cs(c("bnb", 1000000000), c("ukava", 100000000)),
modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true}, modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{"ukava"}, expectedSupplyIndexDenoms: []string{"ukava"},
}, },
}, },
@ -912,61 +799,32 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
firstDeposit: cs(c("bnb", 1000000000)), firstDeposit: cs(c("bnb", 1000000000)),
modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true}, modification: depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)), rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
expectedSupplyIndexDenoms: []string{}, expectedSupplyIndexDenoms: []string{},
}, },
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
userAddr,
// Mint coins to hard module account cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// Set up generic reward periods
var multiRewardPeriods types.MultiRewardPeriods
var rewardPeriods types.RewardPeriods
for i, denom := range tc.args.expectedSupplyIndexDenoms {
// Create just one reward period for USDX Minting / Hard Delegator reward periods (otherwise params will panic on duplicate)
if i == 0 {
rewardPeriod := types.NewRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[i])
rewardPeriods = append(rewardPeriods, rewardPeriod)
}
multiRewardPeriod := types.NewMultiRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
multiRewardPeriods = append(multiRewardPeriods, multiRewardPeriod)
}
// Setup incentive state
params := types.NewParams(
rewardPeriods, multiRewardPeriods, multiRewardPeriods, rewardPeriods,
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params) incentBuilder := NewIncentiveGenesisBuilder().
WithGenesisTime(suite.genesisTime).
WithSimpleSupplyRewardPeriod("bnb", tc.args.rewardsPerSecond).
WithSimpleSupplyRewardPeriod("ukava", tc.args.rewardsPerSecond).
WithSimpleSupplyRewardPeriod("btcb", tc.args.rewardsPerSecond).
WithSimpleSupplyRewardPeriod("xrp", tc.args.rewardsPerSecond)
// Set each denom's previous accrual time and supply reward factor suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
var rewardIndexes types.RewardIndexes
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
for _, denom := range tc.args.expectedSupplyIndexDenoms {
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, denom, tc.args.initialTime)
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, denom, rewardIndexes)
}
// User deposits (first time) // User deposits (first time)
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.firstDeposit)
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.firstDeposit)
suite.Require().NoError(err) suite.Require().NoError(err)
// Confirm that a claim was created and populated with the correct supply indexes // Confirm that a claim was created and populated with the correct supply indexes
claimAfterFirstDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimAfterFirstDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, coin := range tc.args.firstDeposit { for _, coin := range tc.args.firstDeposit {
_, hasIndex := claimAfterFirstDeposit.HasSupplyRewardIndex(coin.Denom) _, hasIndex := claimAfterFirstDeposit.HasSupplyRewardIndex(coin.Denom)
@ -976,14 +834,14 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
// User modifies their Deposit by withdrawing or depositing more // User modifies their Deposit by withdrawing or depositing more
if tc.args.modification.withdraw { if tc.args.modification.withdraw {
err = hardKeeper.Withdraw(suite.ctx, userAddr, tc.args.modification.coins) err = suite.hardKeeper.Withdraw(suite.ctx, userAddr, tc.args.modification.coins)
} else { } else {
err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.modification.coins) err = suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.modification.coins)
} }
suite.Require().NoError(err) suite.Require().NoError(err)
// Confirm that the claim contains all expected supply indexes // Confirm that the claim contains all expected supply indexes
claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(found) suite.Require().True(found)
for _, denom := range tc.args.expectedSupplyIndexDenoms { for _, denom := range tc.args.expectedSupplyIndexDenoms {
_, hasIndex := claimAfterModification.HasSupplyRewardIndex(denom) _, hasIndex := claimAfterModification.HasSupplyRewardIndex(denom)
@ -994,11 +852,10 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
} }
} }
func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() { func (suite *SupplyRewardsTestSuite) TestSimulateHardSupplyRewardSynchronization() {
type args struct { type args struct {
deposit sdk.Coin deposit sdk.Coin
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coins
initialTime time.Time
blockTimes []int blockTimes []int
expectedRewardIndexes types.RewardIndexes expectedRewardIndexes types.RewardIndexes
expectedRewards sdk.Coins expectedRewards sdk.Coins
@ -1014,7 +871,6 @@ func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() {
args{ args{
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("0.001223540000000000"))},
expectedRewards: cs(c("hard", 12235400)), expectedRewards: cs(c("hard", 12235400)),
@ -1025,7 +881,6 @@ func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() {
args{ args{
deposit: c("bnb", 10000000000), deposit: c("bnb", 10000000000),
rewardsPerSecond: cs(c("hard", 122354)), rewardsPerSecond: cs(c("hard", 122354)),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385600000000000"))}, expectedRewardIndexes: types.RewardIndexes{types.NewRewardIndex("hard", d("10.571385600000000000"))},
expectedRewards: cs(c("hard", 105713856000)), expectedRewards: cs(c("hard", 105713856000)),
@ -1034,52 +889,21 @@ func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() userAddr := suite.addrs[3]
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(
userAddr,
// Mint coins to hard module account cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
supplyKeeper := suite.app.GetSupplyKeeper()
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
// Set up incentive state
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
) )
suite.keeper.SetParams(suite.ctx, params) incentBuilder := NewIncentiveGenesisBuilder().
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime) WithGenesisTime(suite.genesisTime).
var rewardIndexes types.RewardIndexes WithSimpleSupplyRewardPeriod(tc.args.deposit.Denom, tc.args.rewardsPerSecond)
for _, rewardCoin := range tc.args.rewardsPerSecond {
rewardIndex := types.NewRewardIndex(rewardCoin.Denom, sdk.ZeroDec())
rewardIndexes = append(rewardIndexes, rewardIndex)
}
suite.keeper.SetHardSupplyRewardIndexes(suite.ctx, tc.args.deposit.Denom, rewardIndexes)
// Set up hard state (interest factor for the relevant denom) suite.SetupWithGenState(authBuilder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.deposit.Denom, sdk.MustNewDecFromStr("1.0"))
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
// User deposits and borrows to increase total borrowed amount // User deposits and borrows to increase total borrowed amount
hardKeeper := suite.app.GetHardKeeper() err := suite.hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
userAddr := suite.addrs[3]
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
suite.Require().NoError(err) suite.Require().NoError(err)
// Check that Hard hooks initialized a HardLiquidityProviderClaim
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
suite.Require().True(found)
multiRewardIndex, _ := claim.SupplyRewardIndexes.GetRewardIndex(tc.args.deposit.Denom)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
currRewardIndex, found := multiRewardIndex.RewardIndexes.GetRewardIndex(expectedRewardIndex.CollateralType)
suite.Require().True(found)
suite.Require().Equal(sdk.ZeroDec(), currRewardIndex.RewardFactor)
}
// Run accumulator at several intervals // Run accumulator at several intervals
var timeElapsed int var timeElapsed int
previousBlockTime := suite.ctx.BlockTime() previousBlockTime := suite.ctx.BlockTime()
@ -1102,7 +926,7 @@ func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() {
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
// Confirm that the user's claim hasn't been synced // Confirm that the user's claim hasn't been synced
claimPre, foundPre := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3]) claimPre, foundPre := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
suite.Require().True(foundPre) suite.Require().True(foundPre)
multiRewardIndexPre, _ := claimPre.SupplyRewardIndexes.GetRewardIndex(tc.args.deposit.Denom) multiRewardIndexPre, _ := claimPre.SupplyRewardIndexes.GetRewardIndex(tc.args.deposit.Denom)
for _, expectedRewardIndex := range tc.args.expectedRewardIndexes { for _, expectedRewardIndex := range tc.args.expectedRewardIndexes {
@ -1130,3 +954,7 @@ func (suite *KeeperTestSuite) TestSimulateHardSupplyRewardSynchronization() {
}) })
} }
} }
func TestSupplyRewardsTestSuite(t *testing.T) {
suite.Run(t, new(SupplyRewardsTestSuite))
}

View File

@ -1,23 +1,68 @@
package keeper_test package keeper_test
import ( import (
"testing"
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/types" 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"
cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
cdptypes "github.com/kava-labs/kava/x/cdp/types" cdptypes "github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/keeper"
) )
const ( // Test suite used for all keeper tests
oneYear time.Duration = time.Hour * 24 * 365 type USDXRewardsTestSuite struct {
) suite.Suite
func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() { keeper keeper.Keeper
cdpKeeper cdpkeeper.Keeper
app app.TestApp
ctx sdk.Context
genesisTime time.Time
addrs []sdk.AccAddress
}
// SetupTest is run automatically before each suite test
func (suite *USDXRewardsTestSuite) SetupTest() {
config := sdk.GetConfig()
app.SetBech32AddressPrefixes(config)
_, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
}
func (suite *USDXRewardsTestSuite) SetupApp() {
suite.app = app.NewTestApp()
suite.keeper = suite.app.GetIncentiveKeeper()
suite.cdpKeeper = suite.app.GetCDPKeeper()
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
}
func (suite *USDXRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder) {
suite.SetupApp()
suite.app.InitializeFromGenesisStatesWithTime(
suite.genesisTime,
authBuilder.BuildMarshalled(),
NewPricefeedGenStateMultiFromTime(suite.genesisTime),
NewCDPGenStateMulti(),
incentBuilder.BuildMarshalled(),
)
}
func (suite *USDXRewardsTestSuite) TestAccumulateUSDXMintingRewards() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
initialTotalPrincipal sdk.Coin initialTotalPrincipal sdk.Coin
timeElapsed int timeElapsed int
expectedRewardFactor sdk.Dec expectedRewardFactor sdk.Dec
@ -32,7 +77,6 @@ func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialTotalPrincipal: c("usdx", 1000000000000), initialTotalPrincipal: c("usdx", 1000000000000),
timeElapsed: 7, timeElapsed: 7,
expectedRewardFactor: d("0.000000856478000000"), expectedRewardFactor: d("0.000000856478000000"),
@ -43,7 +87,6 @@ func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialTotalPrincipal: c("usdx", 1000000000000), initialTotalPrincipal: c("usdx", 1000000000000),
timeElapsed: 86400, timeElapsed: 86400,
expectedRewardFactor: d("0.0105713856"), expectedRewardFactor: d("0.0105713856"),
@ -54,7 +97,6 @@ func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialTotalPrincipal: c("usdx", 1000000000000), initialTotalPrincipal: c("usdx", 1000000000000),
timeElapsed: 0, timeElapsed: 0,
expectedRewardFactor: d("0.0"), expectedRewardFactor: d("0.0"),
@ -63,25 +105,12 @@ func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
suite.SetupWithGenState(app.NewAuthGenesisBuilder(), incentBuilder)
// setup cdp state // setup cdp state
cdpKeeper := suite.app.GetCDPKeeper() suite.cdpKeeper.SetTotalPrincipal(suite.ctx, tc.args.ctype, cdptypes.DefaultStableDenom, tc.args.initialTotalPrincipal.Amount)
cdpKeeper.SetTotalPrincipal(suite.ctx, tc.args.ctype, cdptypes.DefaultStableDenom, tc.args.initialTotalPrincipal.Amount)
// setup incentive state
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed)) updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
@ -90,17 +119,16 @@ func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
err := suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod) err := suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
suite.Require().NoError(err) suite.Require().NoError(err)
rewardFactor, found := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype) rewardFactor, _ := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor) suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
}) })
} }
} }
func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() { func (suite *USDXRewardsTestSuite) TestSynchronizeUSDXMintingReward() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
initialTime time.Time
initialCollateral sdk.Coin initialCollateral sdk.Coin
initialPrincipal sdk.Coin initialPrincipal sdk.Coin
blockTimes []int blockTimes []int
@ -118,7 +146,6 @@ func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
@ -131,7 +158,6 @@ func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: c("ukava", 122354), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
@ -142,30 +168,13 @@ func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(suite.addrs[0], cs(tc.args.initialCollateral))
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
// setup incentive state suite.SetupWithGenState(authBuilder, incentBuilder)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), cs(tc.args.rewardsPerSecond))},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
// setup account state
sk := suite.app.GetSupplyKeeper()
sk.MintCoins(suite.ctx, cdptypes.ModuleName, sdk.NewCoins(tc.args.initialCollateral))
sk.SendCoinsFromModuleToAccount(suite.ctx, cdptypes.ModuleName, suite.addrs[0], sdk.NewCoins(tc.args.initialCollateral))
// setup cdp state // setup cdp state
cdpKeeper := suite.app.GetCDPKeeper() err := suite.cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
err := cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
suite.Require().NoError(err) suite.Require().NoError(err)
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
@ -186,13 +195,13 @@ func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
} }
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed)) updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype) cdp, found := suite.cdpKeeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype)
suite.Require().True(found) suite.Require().True(found)
suite.Require().NotPanics(func() { suite.Require().NotPanics(func() {
suite.keeper.SynchronizeUSDXMintingReward(suite.ctx, cdp) suite.keeper.SynchronizeUSDXMintingReward(suite.ctx, cdp)
}) })
rewardFactor, found := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype) rewardFactor, _ := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype)
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor) suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
claim, found = suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found = suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
@ -203,11 +212,10 @@ func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
} }
} }
func (suite *KeeperTestSuite) TestSimulateUSDXMintingRewardSynchronization() { func (suite *USDXRewardsTestSuite) TestSimulateUSDXMintingRewardSynchronization() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coins rewardsPerSecond sdk.Coin
initialTime time.Time
initialCollateral sdk.Coin initialCollateral sdk.Coin
initialPrincipal sdk.Coin initialPrincipal sdk.Coin
blockTimes []int blockTimes []int
@ -224,8 +232,7 @@ func (suite *KeeperTestSuite) TestSimulateUSDXMintingRewardSynchronization() {
"10 blocks", "10 blocks",
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: cs(c("ukava", 122354)), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
@ -237,8 +244,7 @@ func (suite *KeeperTestSuite) TestSimulateUSDXMintingRewardSynchronization() {
"10 blocks - long block time", "10 blocks - long block time",
args{ args{
ctype: "bnb-a", ctype: "bnb-a",
rewardsPerSecond: cs(c("ukava", 122354)), rewardsPerSecond: c("ukava", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialCollateral: c("bnb", 1000000000000), initialCollateral: c("bnb", 1000000000000),
initialPrincipal: c("usdx", 10000000000), initialPrincipal: c("usdx", 10000000000),
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400}, blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
@ -249,31 +255,13 @@ func (suite *KeeperTestSuite) TestSimulateUSDXMintingRewardSynchronization() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupWithGenState() authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(suite.addrs[0], cs(tc.args.initialCollateral))
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
// setup incentive state suite.SetupWithGenState(authBuilder, incentBuilder)
params := types.NewParams(
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.MultiRewardPeriods{types.NewMultiRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond[0])},
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
// setup account state
sk := suite.app.GetSupplyKeeper()
sk.MintCoins(suite.ctx, cdptypes.ModuleName, sdk.NewCoins(tc.args.initialCollateral))
sk.SendCoinsFromModuleToAccount(suite.ctx, cdptypes.ModuleName, suite.addrs[0], sdk.NewCoins(tc.args.initialCollateral))
// setup cdp state // setup cdp state
cdpKeeper := suite.app.GetCDPKeeper() err := suite.cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
err := cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
suite.Require().NoError(err) suite.Require().NoError(err)
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
@ -306,3 +294,7 @@ func (suite *KeeperTestSuite) TestSimulateUSDXMintingRewardSynchronization() {
}) })
} }
} }
func TestUSDXRewardsTestSuite(t *testing.T) {
suite.Run(t, new(USDXRewardsTestSuite))
}