diff --git a/go.sum b/go.sum index 15c74311..1b56abec 100644 --- a/go.sum +++ b/go.sum @@ -159,7 +159,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= diff --git a/x/harvest/abci.go b/x/harvest/abci.go index 3b89e2d8..ec130d30 100644 --- a/x/harvest/abci.go +++ b/x/harvest/abci.go @@ -11,5 +11,6 @@ func BeginBlocker(ctx sdk.Context, k Keeper) { k.ApplyDelegationRewards(ctx, k.BondDenom(ctx)) k.SetPreviousDelegationDistribution(ctx, ctx.BlockTime(), k.BondDenom(ctx)) } + k.ApplyInterestRateUpdates(ctx) k.SetPreviousBlockTime(ctx, ctx.BlockTime()) } diff --git a/x/harvest/keeper/borrow_test.go b/x/harvest/keeper/borrow_test.go index cb32d632..9c0f6084 100644 --- a/x/harvest/keeper/borrow_test.go +++ b/x/harvest/keeper/borrow_test.go @@ -275,12 +275,12 @@ func (suite *KeeperTestSuite) TestBorrow() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", true, tc.args.usdxBorrowLimit, sdk.MustNewDecFromStr("1"), "usdx:usd", sdk.NewInt(USDX_CF)), - types.NewMoneyMarket("busd", false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1"), "busd:usd", sdk.NewInt(BUSD_CF)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(100000000*KAVA_CF), tc.args.loanToValueKAVA, "kava:usd", sdk.NewInt(KAVA_CF)), - types.NewMoneyMarket("btcb", false, sdk.NewDec(100000000*BTCB_CF), tc.args.loanToValueBTCB, "btcb:usd", sdk.NewInt(BTCB_CF)), - types.NewMoneyMarket("bnb", false, sdk.NewDec(100000000*BNB_CF), tc.args.loanToValueBNB, "bnb:usd", sdk.NewInt(BNB_CF)), - types.NewMoneyMarket("xyz", false, sdk.NewDec(1), tc.args.loanToValueBNB, "xyz:usd", sdk.NewInt(1)), + types.NewMoneyMarket("usdx", true, tc.args.usdxBorrowLimit, sdk.MustNewDecFromStr("1"), "usdx:usd", sdk.NewInt(USDX_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("busd", false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1"), "busd:usd", sdk.NewInt(BUSD_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(100000000*KAVA_CF), tc.args.loanToValueKAVA, "kava:usd", sdk.NewInt(KAVA_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("btcb", false, sdk.NewDec(100000000*BTCB_CF), tc.args.loanToValueBTCB, "btcb:usd", sdk.NewInt(BTCB_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("bnb", false, sdk.NewDec(100000000*BNB_CF), tc.args.loanToValueBNB, "bnb:usd", sdk.NewInt(BNB_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("xyz", false, sdk.NewDec(1), tc.args.loanToValueBNB, "xyz:usd", sdk.NewInt(1), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) diff --git a/x/harvest/keeper/claim_test.go b/x/harvest/keeper/claim_test.go index f712ab66..116d9e55 100644 --- a/x/harvest/keeper/claim_test.go +++ b/x/harvest/keeper/claim_test.go @@ -265,8 +265,8 @@ func (suite *KeeperTestSuite) TestClaim() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)}) diff --git a/x/harvest/keeper/deposit_test.go b/x/harvest/keeper/deposit_test.go index 6f0efe20..443932ba 100644 --- a/x/harvest/keeper/deposit_test.go +++ b/x/harvest/keeper/deposit_test.go @@ -108,8 +108,8 @@ func (suite *KeeperTestSuite) TestDeposit() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)}) @@ -251,8 +251,8 @@ func (suite *KeeperTestSuite) TestWithdraw() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)}) diff --git a/x/harvest/keeper/interest.go b/x/harvest/keeper/interest.go new file mode 100644 index 00000000..8f1fa310 --- /dev/null +++ b/x/harvest/keeper/interest.go @@ -0,0 +1,31 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/x/harvest/types" +) + +// ApplyInterestRateUpdates translates the current interest rate models from the params to the store +func (k Keeper) ApplyInterestRateUpdates(ctx sdk.Context) { + denomSet := map[string]bool{} + + params := k.GetParams(ctx) + for _, mm := range params.MoneyMarkets { + model, found := k.GetInterestRateModel(ctx, mm.Denom) + if !found { + k.SetInterestRateModel(ctx, mm.Denom, mm.InterestRateModel) + continue + } + if !model.Equal(mm.InterestRateModel) { + k.SetInterestRateModel(ctx, mm.Denom, mm.InterestRateModel) + } + denomSet[mm.Denom] = true + } + + k.IterateInterestRateModels(ctx, func(denom string, i types.InterestRateModel) bool { + if !denomSet[denom] { + k.DeleteInterestRateModel(ctx, denom) + } + return false + }) +} diff --git a/x/harvest/keeper/keeper.go b/x/harvest/keeper/keeper.go index 823ab4d9..5e111d56 100644 --- a/x/harvest/keeper/keeper.go +++ b/x/harvest/keeper/keeper.go @@ -255,3 +255,43 @@ func (k Keeper) GetBorrowedCoins(ctx sdk.Context) (sdk.Coins, bool) { k.cdc.MustUnmarshalBinaryBare(bz, &borrowedCoins) return borrowedCoins, true } + +// GetInterestRateModel returns an interest rate model from the store for a denom +func (k Keeper) GetInterestRateModel(ctx sdk.Context, denom string) (types.InterestRateModel, bool) { + store := prefix.NewStore(ctx.KVStore(k.key), types.InterestRateModelsPrefix) + bz := store.Get([]byte(denom)) + if bz == nil { + return types.InterestRateModel{}, false + } + var interestRateModel types.InterestRateModel + k.cdc.MustUnmarshalBinaryBare(bz, &interestRateModel) + return interestRateModel, true +} + +// SetInterestRateModel sets an interest rate model in the store for a denom +func (k Keeper) SetInterestRateModel(ctx sdk.Context, denom string, interestRateModel types.InterestRateModel) { + store := prefix.NewStore(ctx.KVStore(k.key), types.InterestRateModelsPrefix) + bz := k.cdc.MustMarshalBinaryBare(interestRateModel) + store.Set([]byte(denom), bz) +} + +// DeleteInterestRateModel deletes an interest rate model from the store +func (k Keeper) DeleteInterestRateModel(ctx sdk.Context, denom string) { + store := prefix.NewStore(ctx.KVStore(k.key), types.InterestRateModelsPrefix) + store.Delete([]byte(denom)) +} + +// IterateInterestRateModels iterates over all interest rate model objects in the store and performs a callback function +// that returns both the interest rate model value and the key it's stored under +func (k Keeper) IterateInterestRateModels(ctx sdk.Context, cb func(denom string, interestRateModel types.InterestRateModel) (stop bool)) { + store := prefix.NewStore(ctx.KVStore(k.key), types.InterestRateModelsPrefix) + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var interestRateModel types.InterestRateModel + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &interestRateModel) + if cb(string(iterator.Key()), interestRateModel) { + break + } + } +} diff --git a/x/harvest/keeper/keeper_test.go b/x/harvest/keeper/keeper_test.go index 68347a0c..6d09e39f 100644 --- a/x/harvest/keeper/keeper_test.go +++ b/x/harvest/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "strconv" "testing" "github.com/stretchr/testify/suite" @@ -153,6 +154,51 @@ func (suite *KeeperTestSuite) TestGetSetDeleteClaim() { suite.Require().False(f) } +func (suite *KeeperTestSuite) TestGetSetDeleteInterestRateModel() { + denom := "test" + model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")) + + _, f := suite.keeper.GetInterestRateModel(suite.ctx, denom) + suite.Require().False(f) + + suite.keeper.SetInterestRateModel(suite.ctx, denom, model) + + testInterestRateModel, f := suite.keeper.GetInterestRateModel(suite.ctx, denom) + suite.Require().True(f) + suite.Require().Equal(model, testInterestRateModel) + + suite.Require().NotPanics(func() { suite.keeper.DeleteInterestRateModel(suite.ctx, denom) }) + + _, f = suite.keeper.GetInterestRateModel(suite.ctx, denom) + suite.Require().False(f) + +} + +func (suite *KeeperTestSuite) TestIterateInterestRateModels() { + testDenom := "test" + var setModels types.InterestRateModels + var setDenoms []string + for i := 0; i < 5; i++ { + denom := testDenom + strconv.Itoa(i) + model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")) + suite.Require().NotPanics(func() { suite.keeper.SetInterestRateModel(suite.ctx, denom, model) }) + // Save the denom and model + setDenoms = append(setDenoms, denom) + setModels = append(setModels, model) + } + + var seenModels types.InterestRateModels + var seenDenoms []string + suite.keeper.IterateInterestRateModels(suite.ctx, func(denom string, i types.InterestRateModel) bool { + seenDenoms = append(seenDenoms, denom) + seenModels = append(seenModels, i) + return false + }) + + suite.Require().Equal(setModels, seenModels) + suite.Require().Equal(setDenoms, seenDenoms) +} + func (suite *KeeperTestSuite) getAccount(addr sdk.AccAddress) authexported.Account { ak := suite.app.GetAccountKeeper() return ak.GetAccount(suite.ctx, addr) diff --git a/x/harvest/keeper/rewards_test.go b/x/harvest/keeper/rewards_test.go index 10a41dfe..8f558516 100644 --- a/x/harvest/keeper/rewards_test.go +++ b/x/harvest/keeper/rewards_test.go @@ -75,8 +75,8 @@ func (suite *KeeperTestSuite) TestApplyDepositRewards() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), tc.args.previousBlockTime, types.DefaultDistributionTimes) tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)}) @@ -443,8 +443,8 @@ func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, diff --git a/x/harvest/keeper/timelock_test.go b/x/harvest/keeper/timelock_test.go index 4665f74c..50538b18 100644 --- a/x/harvest/keeper/timelock_test.go +++ b/x/harvest/keeper/timelock_test.go @@ -291,8 +291,8 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() { ), }, types.MoneyMarkets{ - types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)), - types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)), + types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), + types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))), }, ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)}) diff --git a/x/harvest/types/keys.go b/x/harvest/types/keys.go index eb76be84..f112b90c 100644 --- a/x/harvest/types/keys.go +++ b/x/harvest/types/keys.go @@ -37,6 +37,7 @@ var ( ClaimsKeyPrefix = []byte{0x04} BorrowsKeyPrefix = []byte{0x05} BorrowedCoinsPrefix = []byte{0x06} + InterestRateModelsPrefix = []byte{0x07} sep = []byte(":") ) diff --git a/x/harvest/types/params.go b/x/harvest/types/params.go index 3fd6f44a..f9c7949d 100644 --- a/x/harvest/types/params.go +++ b/x/harvest/types/params.go @@ -253,20 +253,22 @@ func (bl BorrowLimit) Validate() error { // MoneyMarket is a money market for an individual asset type MoneyMarket struct { - Denom string `json:"denom" yaml:"denom"` - BorrowLimit BorrowLimit `json:"borrow_limit" yaml:"borrow_limit"` - SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` - ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` + Denom string `json:"denom" yaml:"denom"` + BorrowLimit BorrowLimit `json:"borrow_limit" yaml:"borrow_limit"` + SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` + ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` + InterestRateModel InterestRateModel `json:"interest_rate_model" yaml:"interest_rate_model"` } // NewMoneyMarket returns a new MoneyMarket func NewMoneyMarket(denom string, hasMaxLimit bool, maximumLimit, loanToValue sdk.Dec, - spotMarketID string, conversionFactor sdk.Int) MoneyMarket { + spotMarketID string, conversionFactor sdk.Int, interestRateModel InterestRateModel) MoneyMarket { return MoneyMarket{ - Denom: denom, - BorrowLimit: NewBorrowLimit(hasMaxLimit, maximumLimit, loanToValue), - SpotMarketID: spotMarketID, - ConversionFactor: conversionFactor, + Denom: denom, + BorrowLimit: NewBorrowLimit(hasMaxLimit, maximumLimit, loanToValue), + SpotMarketID: spotMarketID, + ConversionFactor: conversionFactor, + InterestRateModel: interestRateModel, } } @@ -279,6 +281,10 @@ func (mm MoneyMarket) Validate() error { if err := mm.BorrowLimit.Validate(); err != nil { return err } + + if err := mm.InterestRateModel.Validate(); err != nil { + return err + } return nil } @@ -295,6 +301,65 @@ func (mms MoneyMarkets) Validate() error { return nil } +// InterestRateModel contains information about an asset's interest rate +type InterestRateModel struct { + BaseRateAPY sdk.Dec `json:"base_rate_apy" yaml:"base_rate_apy"` + BaseMultiplier sdk.Dec `json:"base_multiplier" yaml:"base_multiplier"` + Kink sdk.Dec `json:"kink" yaml:"kink"` + JumpMultiplier sdk.Dec `json:"jump_multiplier" yaml:"jump_multiplier"` +} + +// NewInterestRateModel returns a new InterestRateModel +func NewInterestRateModel(baseRateAPY, baseMultiplier, kink, jumpMultiplier sdk.Dec) InterestRateModel { + return InterestRateModel{ + BaseRateAPY: baseRateAPY, + BaseMultiplier: baseMultiplier, + Kink: kink, + JumpMultiplier: jumpMultiplier, + } +} + +// Validate InterestRateModel param +func (irm InterestRateModel) Validate() error { + if irm.BaseRateAPY.IsNegative() || irm.BaseRateAPY.GT(sdk.OneDec()) { + return fmt.Errorf("Base rate APY must be between 0.0-1.0") + } + + if irm.BaseMultiplier.IsNegative() { + return fmt.Errorf("Base multiplier must be positive") + } + + if irm.Kink.IsNegative() || irm.Kink.GT(sdk.OneDec()) { + return fmt.Errorf("Kink must be between 0.0-1.0") + } + + if irm.JumpMultiplier.IsNegative() { + return fmt.Errorf("Jump multiplier must be positive") + } + + return nil +} + +// Equal returns a boolean indicating if an InterestRateModel is equal to another InterestRateModel +func (irm InterestRateModel) Equal(comparisonIRM InterestRateModel) bool { + if !irm.BaseRateAPY.Equal(comparisonIRM.BaseRateAPY) { + return false + } + if !irm.BaseMultiplier.Equal(comparisonIRM.BaseMultiplier) { + return false + } + if !irm.Kink.Equal(comparisonIRM.Kink) { + return false + } + if !irm.JumpMultiplier.Equal(comparisonIRM.JumpMultiplier) { + return false + } + return true +} + +// InterestRateModels slice of InterestRateModel +type InterestRateModels []InterestRateModel + // NewParams returns a new params object func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules, moneyMarkets MoneyMarkets) Params { return Params{