From dc330d02bf56c1eca7c80b622a6ba098930a7066 Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Fri, 22 Jan 2021 22:17:40 -0700 Subject: [PATCH] Update hard genesis state (#777) * feat: update hard genesis state and init/export methods * address review comments --- x/hard/alias.go | 132 ++++++++++++++++++++----- x/hard/genesis.go | 73 ++++++++++++-- x/hard/keeper/borrow_test.go | 5 +- x/hard/keeper/deposit_test.go | 5 +- x/hard/keeper/interest.go | 12 +-- x/hard/keeper/interest_test.go | 39 ++++---- x/hard/keeper/keeper.go | 19 ++-- x/hard/keeper/liquidation_test.go | 15 +-- x/hard/keeper/repay_test.go | 5 +- x/hard/keeper/timelock_test.go | 5 +- x/hard/keeper/withdraw_test.go | 10 +- x/hard/types/borrow.go | 78 +++++++++++++++ x/hard/types/deposit.go | 78 +++++++++++++++ x/hard/types/genesis.go | 94 ++++++++++++++++-- x/hard/types/genesis_test.go | 51 +++++++--- x/hard/types/params.go | 30 ++---- x/hard/types/params_test.go | 14 +-- x/incentive/keeper/integration_test.go | 5 +- 18 files changed, 533 insertions(+), 137 deletions(-) diff --git a/x/hard/alias.go b/x/hard/alias.go index 638fa849..b5431d88 100644 --- a/x/hard/alias.go +++ b/x/hard/alias.go @@ -9,86 +9,172 @@ import ( const ( AttributeKeyBlockHeight = types.AttributeKeyBlockHeight + AttributeKeyBorrow = types.AttributeKeyBorrow + AttributeKeyBorrowCoins = types.AttributeKeyBorrowCoins + AttributeKeyBorrower = types.AttributeKeyBorrower AttributeKeyDeposit = types.AttributeKeyDeposit + AttributeKeyDepositCoins = types.AttributeKeyDepositCoins AttributeKeyDepositDenom = types.AttributeKeyDepositDenom AttributeKeyDepositor = types.AttributeKeyDepositor + AttributeKeyRepayCoins = types.AttributeKeyRepayCoins AttributeKeyRewardsDistribution = types.AttributeKeyRewardsDistribution + AttributeKeySender = types.AttributeKeySender AttributeValueCategory = types.AttributeValueCategory DefaultParamspace = types.DefaultParamspace DelegatorAccount = types.DelegatorAccount EventTypeDeleteHardDeposit = types.EventTypeDeleteHardDeposit + EventTypeDepositLiquidation = types.EventTypeDepositLiquidation + EventTypeHardBorrow = types.EventTypeHardBorrow EventTypeHardDelegatorDistribution = types.EventTypeHardDelegatorDistribution EventTypeHardDeposit = types.EventTypeHardDeposit EventTypeHardLPDistribution = types.EventTypeHardLPDistribution + EventTypeHardRepay = types.EventTypeHardRepay EventTypeHardWithdrawal = types.EventTypeHardWithdrawal LPAccount = types.LPAccount LiquidatorAccount = types.LiquidatorAccount ModuleAccountName = types.ModuleAccountName ModuleName = types.ModuleName QuerierRoute = types.QuerierRoute + QueryGetBorrows = types.QueryGetBorrows QueryGetDeposits = types.QueryGetDeposits QueryGetModuleAccounts = types.QueryGetModuleAccounts QueryGetParams = types.QueryGetParams + QueryGetTotalBorrowed = types.QueryGetTotalBorrowed + QueryGetTotalDeposited = types.QueryGetTotalDeposited RouterKey = types.RouterKey StoreKey = types.StoreKey ) var ( // function aliases + APYToSPY = keeper.APYToSPY + CalculateBorrowInterestFactor = keeper.CalculateBorrowInterestFactor + CalculateBorrowRate = keeper.CalculateBorrowRate + CalculateSupplyInterestFactor = keeper.CalculateSupplyInterestFactor + CalculateUtilizationRatio = keeper.CalculateUtilizationRatio NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier - CalculateUtilizationRatio = keeper.CalculateUtilizationRatio - CalculateBorrowRate = keeper.CalculateBorrowRate - CalculateBorrowInterestFactor = keeper.CalculateBorrowInterestFactor - CalculateSupplyInterestFactor = keeper.CalculateSupplyInterestFactor - APYToSPY = keeper.APYToSPY DefaultGenesisState = types.DefaultGenesisState DefaultParams = types.DefaultParams DepositTypeIteratorKey = types.DepositTypeIteratorKey + GetBorrowByLtvKey = types.GetBorrowByLtvKey GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength + NewBorrow = types.NewBorrow + NewBorrowInterestFactor = types.NewBorrowInterestFactor NewBorrowLimit = types.NewBorrowLimit - NewInterestRateModel = types.NewInterestRateModel NewDeposit = types.NewDeposit + NewGenesisAccumulationTime = types.NewGenesisAccumulationTime NewGenesisState = types.NewGenesisState + NewInterestRateModel = types.NewInterestRateModel + NewMoneyMarket = types.NewMoneyMarket + NewMsgBorrow = types.NewMsgBorrow NewMsgDeposit = types.NewMsgDeposit + NewMsgLiquidate = types.NewMsgLiquidate + NewMsgRepay = types.NewMsgRepay NewMsgWithdraw = types.NewMsgWithdraw NewMultiHARDHooks = types.NewMultiHARDHooks NewParams = types.NewParams NewPeriod = types.NewPeriod - NewMoneyMarket = types.NewMoneyMarket NewQueryAccountParams = types.NewQueryAccountParams + NewQueryBorrowsParams = types.NewQueryBorrowsParams + NewQueryDepositsParams = types.NewQueryDepositsParams + NewQueryTotalBorrowedParams = types.NewQueryTotalBorrowedParams + NewQueryTotalDepositedParams = types.NewQueryTotalDepositedParams + NewSupplyInterestFactor = types.NewSupplyInterestFactor + NewValuationMap = types.NewValuationMap ParamKeyTable = types.ParamKeyTable RegisterCodec = types.RegisterCodec // variable aliases + BorrowInterestFactorPrefix = types.BorrowInterestFactorPrefix + BorrowedCoinsPrefix = types.BorrowedCoinsPrefix BorrowsKeyPrefix = types.BorrowsKeyPrefix - DefaultActive = types.DefaultActive + DefaultAccumulationTimes = types.DefaultAccumulationTimes + DefaultBorrows = types.DefaultBorrows + DefaultCheckLtvIndexCount = types.DefaultCheckLtvIndexCount + DefaultDeposits = types.DefaultDeposits + DefaultMoneyMarkets = types.DefaultMoneyMarkets DefaultPreviousBlockTime = types.DefaultPreviousBlockTime + DefaultTotalBorrowed = types.DefaultTotalBorrowed + DefaultTotalReserves = types.DefaultTotalReserves + DefaultTotalSupplied = types.DefaultTotalSupplied DepositsKeyPrefix = types.DepositsKeyPrefix ErrAccountNotFound = types.ErrAccountNotFound + ErrBorrowEmptyCoins = types.ErrBorrowEmptyCoins + ErrBorrowExceedsAvailableBalance = types.ErrBorrowExceedsAvailableBalance + ErrBorrowNotFound = types.ErrBorrowNotFound + ErrBorrowNotLiquidatable = types.ErrBorrowNotLiquidatable + ErrBorrowedCoinsNotFound = types.ErrBorrowedCoinsNotFound ErrDepositNotFound = types.ErrDepositNotFound + ErrDepositsNotFound = types.ErrDepositsNotFound + ErrGreaterThanAssetBorrowLimit = types.ErrGreaterThanAssetBorrowLimit + ErrInsufficientBalanceForBorrow = types.ErrInsufficientBalanceForBorrow + ErrInsufficientBalanceForRepay = types.ErrInsufficientBalanceForRepay + ErrInsufficientCoins = types.ErrInsufficientCoins + ErrInsufficientLoanToValue = types.ErrInsufficientLoanToValue ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance - ErrInvaliWithdrawAmount = types.ErrInvalidWithdrawAmount ErrInvalidAccountType = types.ErrInvalidAccountType ErrInvalidDepositDenom = types.ErrInvalidDepositDenom + ErrInvalidReceiver = types.ErrInvalidReceiver + ErrInvalidRepaymentDenom = types.ErrInvalidRepaymentDenom + ErrInvalidWithdrawAmount = types.ErrInvalidWithdrawAmount + ErrInvalidWithdrawDenom = types.ErrInvalidWithdrawDenom + ErrMarketNotFound = types.ErrMarketNotFound + ErrMoneyMarketNotFound = types.ErrMoneyMarketNotFound + ErrNegativeBorrowedCoins = types.ErrNegativeBorrowedCoins + ErrNegativeSuppliedCoins = types.ErrNegativeSuppliedCoins + ErrPreviousAccrualTimeNotFound = types.ErrPreviousAccrualTimeNotFound + ErrPriceNotFound = types.ErrPriceNotFound + ErrSuppliedCoinsNotFound = types.ErrSuppliedCoinsNotFound GovDenom = types.GovDenom - KeyActive = types.KeyActive + KeyCheckLtvIndexCount = types.KeyCheckLtvIndexCount + KeyMoneyMarkets = types.KeyMoneyMarkets + LtvIndexPrefix = types.LtvIndexPrefix ModuleCdc = types.ModuleCdc + MoneyMarketsPrefix = types.MoneyMarketsPrefix + PreviousAccrualTimePrefix = types.PreviousAccrualTimePrefix PreviousBlockTimeKey = types.PreviousBlockTimeKey + SuppliedCoinsPrefix = types.SuppliedCoinsPrefix + SupplyInterestFactorPrefix = types.SupplyInterestFactorPrefix + TotalReservesPrefix = types.TotalReservesPrefix ) type ( - Keeper = keeper.Keeper - AccountKeeper = types.AccountKeeper - Borrow = types.Borrow - MoneyMarket = types.MoneyMarket - MoneyMarkets = types.MoneyMarkets - Deposit = types.Deposit - GenesisState = types.GenesisState - MsgDeposit = types.MsgDeposit - MsgWithdraw = types.MsgWithdraw - Params = types.Params - QueryAccountParams = types.QueryAccountParams - StakingKeeper = types.StakingKeeper - SupplyKeeper = types.SupplyKeeper + Keeper = keeper.Keeper + LiqData = keeper.LiqData + AccountKeeper = types.AccountKeeper + AuctionKeeper = types.AuctionKeeper + Borrow = types.Borrow + BorrowInterestFactor = types.BorrowInterestFactor + BorrowInterestFactors = types.BorrowInterestFactors + BorrowLimit = types.BorrowLimit + Borrows = types.Borrows + Deposit = types.Deposit + Deposits = types.Deposits + GenesisAccumulationTime = types.GenesisAccumulationTime + GenesisAccumulationTimes = types.GenesisAccumulationTimes + GenesisState = types.GenesisState + HARDHooks = types.HARDHooks + InterestRateModel = types.InterestRateModel + InterestRateModels = types.InterestRateModels + MoneyMarket = types.MoneyMarket + MoneyMarkets = types.MoneyMarkets + MsgBorrow = types.MsgBorrow + MsgDeposit = types.MsgDeposit + MsgLiquidate = types.MsgLiquidate + MsgRepay = types.MsgRepay + MsgWithdraw = types.MsgWithdraw + MultiHARDHooks = types.MultiHARDHooks + Params = types.Params + PricefeedKeeper = types.PricefeedKeeper + QueryAccountParams = types.QueryAccountParams + QueryBorrowsParams = types.QueryBorrowsParams + QueryDepositsParams = types.QueryDepositsParams + QueryTotalBorrowedParams = types.QueryTotalBorrowedParams + QueryTotalDepositedParams = types.QueryTotalDepositedParams + StakingKeeper = types.StakingKeeper + SupplyInterestFactor = types.SupplyInterestFactor + SupplyInterestFactors = types.SupplyInterestFactors + SupplyKeeper = types.SupplyKeeper + ValuationMap = types.ValuationMap ) diff --git a/x/hard/genesis.go b/x/hard/genesis.go index a5d4dde4..5e7fff3f 100644 --- a/x/hard/genesis.go +++ b/x/hard/genesis.go @@ -16,15 +16,28 @@ func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, gs k.SetParams(ctx, gs.Params) - // only set the previous block time if it's different than default - if !gs.PreviousBlockTime.Equal(DefaultPreviousBlockTime) { - k.SetPreviousBlockTime(ctx, gs.PreviousBlockTime) - } - for _, mm := range gs.Params.MoneyMarkets { k.SetMoneyMarket(ctx, mm.Denom, mm) } + for _, gat := range gs.PreviousAccumulationTimes { + k.SetPreviousAccrualTime(ctx, gat.CollateralType, gat.PreviousAccumulationTime) + k.SetSupplyInterestFactor(ctx, gat.CollateralType, gat.SupplyInterestFactor) + k.SetBorrowInterestFactor(ctx, gat.CollateralType, gat.BorrowInterestFactor) + } + + for _, deposit := range gs.Deposits { + k.SetDeposit(ctx, deposit) + } + + for _, borrow := range gs.Borrows { + k.SetBorrow(ctx, borrow) + } + + k.SetSuppliedCoins(ctx, gs.TotalSupplied) + k.SetBorrowedCoins(ctx, gs.TotalBorrowed) + k.SetTotalReserves(ctx, gs.TotalReserves) + // check if the module account exists LPModuleAcc := supplyKeeper.GetModuleAccount(ctx, LPAccount) if LPModuleAcc == nil { @@ -54,9 +67,53 @@ func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, gs // ExportGenesis export genesis state for hard module func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { params := k.GetParams(ctx) - previousBlockTime, found := k.GetPreviousBlockTime(ctx) + + gats := types.GenesisAccumulationTimes{} + deposits := types.Deposits{} + borrows := types.Borrows{} + + k.IterateDeposits(ctx, func(d types.Deposit) bool { + deposits = append(deposits, d) + return false + }) + + k.IterateBorrows(ctx, func(b types.Borrow) bool { + borrows = append(borrows, b) + return false + }) + + totalSupplied, found := k.GetSuppliedCoins(ctx) if !found { - previousBlockTime = DefaultPreviousBlockTime + totalSupplied = DefaultTotalSupplied } - return NewGenesisState(params, previousBlockTime) + totalBorrowed, found := k.GetBorrowedCoins(ctx) + if !found { + totalBorrowed = DefaultTotalBorrowed + } + totalReserves, found := k.GetTotalReserves(ctx) + if !found { + totalReserves = DefaultTotalReserves + } + + for _, mm := range params.MoneyMarkets { + supplyFactor, f := k.GetSupplyInterestFactor(ctx, mm.Denom) + if !f { + supplyFactor = sdk.ZeroDec() + } + borrowFactor, f := k.GetBorrowInterestFactor(ctx, mm.Denom) + if !f { + borrowFactor = sdk.ZeroDec() + } + previousAccrualTime, f := k.GetPreviousAccrualTime(ctx, mm.Denom) + if !f { + previousAccrualTime = ctx.BlockTime() + } + gat := types.NewGenesisAccumulationTime(mm.Denom, previousAccrualTime, supplyFactor, borrowFactor) + gats = append(gats, gat) + + } + return NewGenesisState( + params, gats, deposits, borrows, + totalSupplied, totalBorrowed, totalReserves, + ) } diff --git a/x/hard/keeper/borrow_test.go b/x/hard/keeper/borrow_test.go index cff74fda..150c1c45 100644 --- a/x/hard/keeper/borrow_test.go +++ b/x/hard/keeper/borrow_test.go @@ -261,7 +261,6 @@ func (suite *KeeperTestSuite) TestBorrow() { // hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(true, tc.args.usdxBorrowLimit, sdk.MustNewDecFromStr("1")), "usdx:usd", sdk.NewInt(USDX_CF), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), types.NewMoneyMarket("busd", types.NewBorrowLimit(false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1")), "busd:usd", sdk.NewInt(BUSD_CF), sdk.NewInt(BUSD_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), @@ -271,7 +270,9 @@ func (suite *KeeperTestSuite) TestBorrow() { types.NewMoneyMarket("xyz", types.NewBorrowLimit(false, sdk.NewDec(1), tc.args.loanToValueBNB), "xyz:usd", sdk.NewInt(1), sdk.NewInt(1), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ diff --git a/x/hard/keeper/deposit_test.go b/x/hard/keeper/deposit_test.go index 0a361c91..de34fc23 100644 --- a/x/hard/keeper/deposit_test.go +++ b/x/hard/keeper/deposit_test.go @@ -105,7 +105,6 @@ func (suite *KeeperTestSuite) TestDeposit() { authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.depositor}, []sdk.Coins{sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000)))}) loanToValue, _ := sdk.NewDecFromStr("0.6") hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), @@ -113,7 +112,9 @@ func (suite *KeeperTestSuite) TestDeposit() { types.NewMoneyMarket("btcb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "btcb:usd", sdk.NewInt(1000000), sdk.NewInt(BTCB_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ diff --git a/x/hard/keeper/interest.go b/x/hard/keeper/interest.go index b527754f..3a192120 100644 --- a/x/hard/keeper/interest.go +++ b/x/hard/keeper/interest.go @@ -78,10 +78,10 @@ func (k Keeper) AccrueInterest(ctx sdk.Context, denom string) error { borrowedPrior = sdk.NewCoin(denom, borrowedCoinsPrior.AmountOf(denom)) } - reservesPrior, foundReservesPrior := k.GetTotalReserves(ctx, denom) + reservesPrior, foundReservesPrior := k.GetTotalReserves(ctx) if !foundReservesPrior { - newReservesPrior := sdk.NewCoin(denom, sdk.ZeroInt()) - k.SetTotalReserves(ctx, denom, newReservesPrior) + newReservesPrior := sdk.NewCoins() + k.SetTotalReserves(ctx, newReservesPrior) reservesPrior = newReservesPrior } @@ -106,7 +106,7 @@ func (k Keeper) AccrueInterest(ctx sdk.Context, denom string) error { } // GetBorrowRate calculates the current interest rate based on utilization (the fraction of supply that has been borrowed) - borrowRateApy, err := CalculateBorrowRate(mm.InterestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowedPrior.Amount), sdk.NewDecFromInt(reservesPrior.Amount)) + borrowRateApy, err := CalculateBorrowRate(mm.InterestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowedPrior.Amount), sdk.NewDecFromInt(reservesPrior.AmountOf(denom))) if err != nil { return err } @@ -127,14 +127,14 @@ func (k Keeper) AccrueInterest(ctx sdk.Context, denom string) error { // Calculate supply interest factor and update supplyInterestNew := interestBorrowAccumulated.Sub(reservesNew) - supplyInterestFactor := CalculateSupplyInterestFactor(supplyInterestNew.ToDec(), cashPrior.ToDec(), borrowedPrior.Amount.ToDec(), reservesPrior.Amount.ToDec()) + supplyInterestFactor := CalculateSupplyInterestFactor(supplyInterestNew.ToDec(), cashPrior.ToDec(), borrowedPrior.Amount.ToDec(), reservesPrior.AmountOf(denom).ToDec()) supplyInterestFactorNew := supplyInterestFactorPrior.Mul(supplyInterestFactor) k.SetSupplyInterestFactor(ctx, denom, supplyInterestFactorNew) // Update accural keys in store k.IncrementBorrowedCoins(ctx, totalBorrowInterestAccumulated) k.IncrementSuppliedCoins(ctx, sdk.NewCoins(sdk.NewCoin(denom, supplyInterestNew))) - k.SetTotalReserves(ctx, denom, reservesPrior.Add(sdk.NewCoin(mm.Denom, reservesNew))) + k.SetTotalReserves(ctx, reservesPrior.Add(sdk.NewCoin(denom, reservesNew))) k.SetPreviousAccrualTime(ctx, denom, ctx.BlockTime()) return nil diff --git a/x/hard/keeper/interest_test.go b/x/hard/keeper/interest_test.go index 1760c549..8ed31d49 100644 --- a/x/hard/keeper/interest_test.go +++ b/x/hard/keeper/interest_test.go @@ -711,7 +711,6 @@ func (suite *KeeperTestSuite) TestBorrowInterest() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit @@ -723,7 +722,9 @@ func (suite *KeeperTestSuite) TestBorrowInterest() { sdk.ZeroDec()), // Keeper Reward Percentage }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ @@ -788,16 +789,16 @@ func (suite *KeeperTestSuite) TestBorrowInterest() { suite.Require().True(borrowCoinsPriorFound) borrowCoinPriorAmount := borrowCoinsPrior.AmountOf(tc.args.borrowCoinDenom) - reservesPrior, foundReservesPrior := suite.keeper.GetTotalReserves(prevCtx, tc.args.borrowCoinDenom) + reservesPrior, foundReservesPrior := suite.keeper.GetTotalReserves(prevCtx) if !foundReservesPrior { - reservesPrior = sdk.NewCoin(tc.args.borrowCoinDenom, sdk.ZeroInt()) + reservesPrior = sdk.NewCoins(sdk.NewCoin(tc.args.borrowCoinDenom, sdk.ZeroInt())) } interestFactorPrior, foundInterestFactorPrior := suite.keeper.GetBorrowInterestFactor(prevCtx, tc.args.borrowCoinDenom) suite.Require().True(foundInterestFactorPrior) // 2. Calculate expected interest owed - borrowRateApy, err := hard.CalculateBorrowRate(tc.args.interestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.Amount)) + borrowRateApy, err := hard.CalculateBorrowRate(tc.args.interestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.AmountOf(tc.args.borrowCoinDenom))) suite.Require().NoError(err) // Convert from APY to SPY, expressed as (1 + borrow rate) @@ -821,7 +822,7 @@ func (suite *KeeperTestSuite) TestBorrowInterest() { suite.Require().Equal(expectedBorrowedCoins, currBorrowedCoins.AmountOf(tc.args.borrowCoinDenom)) // Check that the total reserves have changed as expected - currTotalReserves, _ := suite.keeper.GetTotalReserves(snapshotCtx, tc.args.borrowCoinDenom) + currTotalReserves, _ := suite.keeper.GetTotalReserves(snapshotCtx) suite.Require().Equal(expectedReserves, currTotalReserves) // Check that the borrow index has increased as expected @@ -1118,7 +1119,6 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit @@ -1138,7 +1138,9 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { sdk.ZeroDec()), // Keeper Reward Percentage }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ @@ -1210,9 +1212,9 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { suite.Require().True(supplyCoinsPriorFound) supplyCoinPriorAmount = supplyCoinsPrior.AmountOf(coinDenom) - reservesPrior, foundReservesPrior := suite.keeper.GetTotalReserves(prevCtx, coinDenom) + reservesPrior, foundReservesPrior := suite.keeper.GetTotalReserves(prevCtx) if !foundReservesPrior { - reservesPrior = sdk.NewCoin(coinDenom, sdk.ZeroInt()) + reservesPrior = sdk.NewCoins(sdk.NewCoin(coinDenom, sdk.ZeroInt())) } borrowInterestFactorPrior, foundBorrowInterestFactorPrior := suite.keeper.GetBorrowInterestFactor(prevCtx, coinDenom) @@ -1222,7 +1224,7 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { suite.Require().True(foundSupplyInterestFactorPrior) // 2. Calculate expected borrow interest owed - borrowRateApy, err := hard.CalculateBorrowRate(tc.args.interestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.Amount)) + borrowRateApy, err := hard.CalculateBorrowRate(tc.args.interestRateModel, sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.AmountOf(coinDenom))) suite.Require().NoError(err) // Convert from APY to SPY, expressed as (1 + borrow rate) @@ -1232,12 +1234,12 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { newBorrowInterestFactor := hard.CalculateBorrowInterestFactor(borrowRateSpy, sdk.NewInt(snapshot.elapsedTime)) expectedBorrowInterest := (newBorrowInterestFactor.Mul(sdk.NewDecFromInt(borrowCoinPriorAmount)).TruncateInt()).Sub(borrowCoinPriorAmount) expectedReserves := reservesPrior.Add(sdk.NewCoin(coinDenom, sdk.NewDecFromInt(expectedBorrowInterest).Mul(tc.args.reserveFactor).TruncateInt())).Sub(reservesPrior) - expectedTotalReserves := expectedReserves.Add(reservesPrior) + expectedTotalReserves := expectedReserves.Add(reservesPrior...) expectedBorrowInterestFactor := borrowInterestFactorPrior.Mul(newBorrowInterestFactor) - expectedSupplyInterest := expectedBorrowInterest.Sub(expectedReserves.Amount) + expectedSupplyInterest := expectedBorrowInterest.Sub(expectedReserves.AmountOf(coinDenom)) - newSupplyInterestFactor := hard.CalculateSupplyInterestFactor(expectedSupplyInterest.ToDec(), sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.Amount)) + newSupplyInterestFactor := hard.CalculateSupplyInterestFactor(expectedSupplyInterest.ToDec(), sdk.NewDecFromInt(cashPrior), sdk.NewDecFromInt(borrowCoinPriorAmount), sdk.NewDecFromInt(reservesPrior.AmountOf(coinDenom))) expectedSupplyInterestFactor := supplyInterestFactorPrior.Mul(newSupplyInterestFactor) // ------------------------------------------------------------------------------------- @@ -1248,7 +1250,7 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { borrowInterestFactor, _ := suite.keeper.GetBorrowInterestFactor(ctx, coinDenom) suite.Require().Equal(expectedBorrowInterestFactor, borrowInterestFactor) - suite.Require().Equal(expectedBorrowInterest, expectedSupplyInterest.Add(expectedReserves.Amount)) + suite.Require().Equal(expectedBorrowInterest, expectedSupplyInterest.Add(expectedReserves.AmountOf(coinDenom))) // Check that the total amount of borrowed coins has increased by expected borrow interest amount borrowCoinsPost, _ := suite.keeper.GetBorrowedCoins(snapshotCtx) @@ -1261,8 +1263,11 @@ func (suite *KeeperTestSuite) TestSupplyInterest() { suite.Require().Equal(supplyCoinPostAmount, supplyCoinPriorAmount.Add(expectedSupplyInterest)) // Check current total reserves - totalReserves, _ := suite.keeper.GetTotalReserves(snapshotCtx, coinDenom) - suite.Require().Equal(expectedTotalReserves, totalReserves) + totalReserves, _ := suite.keeper.GetTotalReserves(snapshotCtx) + suite.Require().Equal( + sdk.NewCoin(coinDenom, expectedTotalReserves.AmountOf(coinDenom)), + sdk.NewCoin(coinDenom, totalReserves.AmountOf(coinDenom)), + ) // Check that the supply index has increased as expected currSupplyIndexPrior, _ := suite.keeper.GetSupplyInterestFactor(snapshotCtx, coinDenom) diff --git a/x/hard/keeper/keeper.go b/x/hard/keeper/keeper.go index cbf83fca..fdb5f870 100644 --- a/x/hard/keeper/keeper.go +++ b/x/hard/keeper/keeper.go @@ -272,22 +272,27 @@ func (k Keeper) SetPreviousAccrualTime(ctx sdk.Context, denom string, previousAc } // GetTotalReserves returns the total reserves for an individual market -func (k Keeper) GetTotalReserves(ctx sdk.Context, denom string) (sdk.Coin, bool) { +func (k Keeper) GetTotalReserves(ctx sdk.Context) (sdk.Coins, bool) { store := prefix.NewStore(ctx.KVStore(k.key), types.TotalReservesPrefix) - bz := store.Get([]byte(denom)) + bz := store.Get([]byte{}) if bz == nil { - return sdk.Coin{}, false + return sdk.Coins{}, false } - var totalReserves sdk.Coin + var totalReserves sdk.Coins k.cdc.MustUnmarshalBinaryBare(bz, &totalReserves) return totalReserves, true } // SetTotalReserves sets the total reserves for an individual market -func (k Keeper) SetTotalReserves(ctx sdk.Context, denom string, coin sdk.Coin) { +func (k Keeper) SetTotalReserves(ctx sdk.Context, coins sdk.Coins) { + store := prefix.NewStore(ctx.KVStore(k.key), types.TotalReservesPrefix) - bz := k.cdc.MustMarshalBinaryBare(coin) - store.Set([]byte(denom), bz) + if coins.Empty() { + store.Set([]byte{}, []byte{}) + return + } + bz := k.cdc.MustMarshalBinaryBare(coins) + store.Set([]byte{}, bz) } // GetBorrowInterestFactor returns the current borrow interest factor for an individual market diff --git a/x/hard/keeper/liquidation_test.go b/x/hard/keeper/liquidation_test.go index af082793..47323dce 100644 --- a/x/hard/keeper/liquidation_test.go +++ b/x/hard/keeper/liquidation_test.go @@ -122,7 +122,6 @@ func (suite *KeeperTestSuite) TestIndexLiquidation() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit @@ -182,7 +181,9 @@ func (suite *KeeperTestSuite) TestIndexLiquidation() { sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent }, tc.args.ltvIndexCount, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ @@ -534,7 +535,6 @@ func (suite *KeeperTestSuite) TestFullIndexLiquidation() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit @@ -554,7 +554,9 @@ func (suite *KeeperTestSuite) TestFullIndexLiquidation() { sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent }, tc.args.ltvIndexCount, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ @@ -1157,7 +1159,6 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit @@ -1217,7 +1218,9 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() { tc.args.keeperRewardPercent), // Keeper Reward Percent }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ diff --git a/x/hard/keeper/repay_test.go b/x/hard/keeper/repay_test.go index 433d776a..0facae5b 100644 --- a/x/hard/keeper/repay_test.go +++ b/x/hard/keeper/repay_test.go @@ -137,7 +137,6 @@ func (suite *KeeperTestSuite) TestRepay() { // Hard module genesis state hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(100000000*USDX_CF), sdk.MustNewDecFromStr("1")), // Borrow Limit @@ -157,7 +156,9 @@ func (suite *KeeperTestSuite) TestRepay() { sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ diff --git a/x/hard/keeper/timelock_test.go b/x/hard/keeper/timelock_test.go index e6513cf1..47c413a7 100644 --- a/x/hard/keeper/timelock_test.go +++ b/x/hard/keeper/timelock_test.go @@ -281,13 +281,14 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() { authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.accArgs.addr}, []sdk.Coins{tc.args.accArgs.coins}) loanToValue := sdk.MustNewDecFromStr("0.6") hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(hardGS)}) if tc.args.accArgs.vestingAccountBefore { ak := tApp.GetAccountKeeper() diff --git a/x/hard/keeper/withdraw_test.go b/x/hard/keeper/withdraw_test.go index f25d0ded..d4be9d5c 100644 --- a/x/hard/keeper/withdraw_test.go +++ b/x/hard/keeper/withdraw_test.go @@ -124,14 +124,15 @@ func (suite *KeeperTestSuite) TestWithdraw() { loanToValue := sdk.MustNewDecFromStr("0.6") hardGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "bnb:usd", sdk.NewInt(100000000), sdk.NewInt(BNB_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ @@ -264,7 +265,6 @@ func (suite *KeeperTestSuite) TestLtvWithdraw() { // Harvest module genesis state harvestGS := types.NewGenesisState(types.NewParams( - true, types.MoneyMarkets{ types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit @@ -284,7 +284,9 @@ func (suite *KeeperTestSuite) TestLtvWithdraw() { sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent }, 0, // LTV counter - ), types.DefaultPreviousBlockTime) + ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, + types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, + ) // Pricefeed module genesis state pricefeedGS := pricefeed.GenesisState{ diff --git a/x/hard/types/borrow.go b/x/hard/types/borrow.go index 5cf262e4..81251252 100644 --- a/x/hard/types/borrow.go +++ b/x/hard/types/borrow.go @@ -1,6 +1,9 @@ package types import ( + "fmt" + "strings" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -20,9 +23,49 @@ func NewBorrow(borrower sdk.AccAddress, amount sdk.Coins, index BorrowInterestFa } } +// Validate deposit validation +func (b Borrow) Validate() error { + if b.Borrower.Empty() { + return fmt.Errorf("Depositor cannot be empty") + } + if !b.Amount.IsValid() { + return fmt.Errorf("Invalid deposit coins: %s", b.Amount) + } + + if err := b.Index.Validate(); err != nil { + return err + } + + return nil +} + +func (b Borrow) String() string { + return fmt.Sprintf(`Deposit: + Borrower: %s + Amount: %s + Index: %s + `, b.Borrower, b.Amount, b.Index) +} + // Borrows is a slice of Borrow type Borrows []Borrow +// Validate validates Borrows +func (bs Borrows) Validate() error { + borrowDupMap := make(map[string]Borrow) + for _, b := range bs { + if err := b.Validate(); err != nil { + return err + } + dup, ok := borrowDupMap[b.Borrower.String()] + if ok { + return fmt.Errorf("duplicate borrower: %s\n%s", b, dup) + } + borrowDupMap[b.Borrower.String()] = b + } + return nil +} + // BorrowInterestFactor defines an individual borrow interest factor type BorrowInterestFactor struct { Denom string `json:"denom" yaml:"denom"` @@ -37,5 +80,40 @@ func NewBorrowInterestFactor(denom string, value sdk.Dec) BorrowInterestFactor { } } +// Validate validates BorrowInterestFactor values +func (bif BorrowInterestFactor) Validate() error { + if strings.TrimSpace(bif.Denom) == "" { + return fmt.Errorf("borrow interest factor denom cannot be empty") + } + if bif.Value.IsNegative() { + return fmt.Errorf("borrow interest factor value cannot be negative: %s", bif) + + } + return nil +} + +func (bif BorrowInterestFactor) String() string { + return fmt.Sprintf(`[%s,%s] + `, bif.Denom, bif.Value) +} + // BorrowInterestFactors is a slice of BorrowInterestFactor, because Amino won't marshal maps type BorrowInterestFactors []BorrowInterestFactor + +// Validate validates BorrowInterestFactors +func (bifs BorrowInterestFactors) Validate() error { + for _, bif := range bifs { + if err := bif.Validate(); err != nil { + return err + } + } + return nil +} + +func (bifs BorrowInterestFactors) String() string { + out := "" + for _, bif := range bifs { + out += bif.String() + } + return out +} diff --git a/x/hard/types/deposit.go b/x/hard/types/deposit.go index e9b465b1..7fa3e38a 100644 --- a/x/hard/types/deposit.go +++ b/x/hard/types/deposit.go @@ -1,6 +1,9 @@ package types import ( + "fmt" + "strings" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -20,9 +23,49 @@ func NewDeposit(depositor sdk.AccAddress, amount sdk.Coins, indexes SupplyIntere } } +// Validate deposit validation +func (d Deposit) Validate() error { + if d.Depositor.Empty() { + return fmt.Errorf("Depositor cannot be empty") + } + if !d.Amount.IsValid() { + return fmt.Errorf("Invalid deposit coins: %s", d.Amount) + } + + if err := d.Index.Validate(); err != nil { + return err + } + + return nil +} + +func (d Deposit) String() string { + return fmt.Sprintf(`Deposit: + Depositor: %s + Amount: %s + Index: %s + `, d.Depositor, d.Amount, d.Index) +} + // Deposits is a slice of Deposit type Deposits []Deposit +// Validate validates Deposits +func (ds Deposits) Validate() error { + depositDupMap := make(map[string]Deposit) + for _, d := range ds { + if err := d.Validate(); err != nil { + return err + } + dup, ok := depositDupMap[d.Depositor.String()] + if ok { + return fmt.Errorf("duplicate depositor: %s\n%s", d, dup) + } + depositDupMap[d.Depositor.String()] = d + } + return nil +} + // SupplyInterestFactor defines an individual borrow interest factor type SupplyInterestFactor struct { Denom string `json:"denom" yaml:"denom"` @@ -37,5 +80,40 @@ func NewSupplyInterestFactor(denom string, value sdk.Dec) SupplyInterestFactor { } } +// Validate validates SupplyInterestFactor values +func (sif SupplyInterestFactor) Validate() error { + if strings.TrimSpace(sif.Denom) == "" { + return fmt.Errorf("supply interest factor denom cannot be empty") + } + if sif.Value.IsNegative() { + return fmt.Errorf("supply interest factor value cannot be negative: %s", sif) + + } + return nil +} + +func (sif SupplyInterestFactor) String() string { + return fmt.Sprintf(`[%s,%s] + `, sif.Denom, sif.Value) +} + // SupplyInterestFactors is a slice of SupplyInterestFactor, because Amino won't marshal maps type SupplyInterestFactors []SupplyInterestFactor + +// Validate validates SupplyInterestFactors +func (sifs SupplyInterestFactors) Validate() error { + for _, sif := range sifs { + if err := sif.Validate(); err != nil { + return err + } + } + return nil +} + +func (sifs SupplyInterestFactors) String() string { + out := "" + for _, sif := range sifs { + out += sif.String() + } + return out +} diff --git a/x/hard/types/genesis.go b/x/hard/types/genesis.go index 303204a9..fbf67acf 100644 --- a/x/hard/types/genesis.go +++ b/x/hard/types/genesis.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + sdk "github.com/cosmos/cosmos-sdk/types" tmtime "github.com/tendermint/tendermint/types/time" ) @@ -15,23 +16,40 @@ var ( // GenesisState is the state that must be provided at genesis. type GenesisState struct { - Params Params `json:"params" yaml:"params"` - PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"` + Params Params `json:"params" yaml:"params"` + PreviousAccumulationTimes GenesisAccumulationTimes `json:"previous_accumulation_times" yaml:"previous_accumulation_times"` + Deposits Deposits `json:"deposits" yaml:"deposits"` + Borrows Borrows `json:"borrows" yaml:"borrows"` + TotalSupplied sdk.Coins `json:"total_supplied" yaml:"total_supplied"` + TotalBorrowed sdk.Coins `json:"total_borrowed" yaml:"total_borrowed"` + TotalReserves sdk.Coins `json:"total_reserves" yaml:"total_reserves"` } // NewGenesisState returns a new genesis state -func NewGenesisState(params Params, previousBlockTime time.Time) GenesisState { +func NewGenesisState( + params Params, prevAccumulationTimes GenesisAccumulationTimes, deposits Deposits, + borrows Borrows, totalSupplied, totalBorrowed, totalReserves sdk.Coins) GenesisState { return GenesisState{ - Params: params, - PreviousBlockTime: previousBlockTime, + Params: params, + PreviousAccumulationTimes: prevAccumulationTimes, + Deposits: deposits, + Borrows: borrows, + TotalSupplied: totalSupplied, + TotalBorrowed: totalBorrowed, + TotalReserves: totalReserves, } } // DefaultGenesisState returns a default genesis state func DefaultGenesisState() GenesisState { return GenesisState{ - Params: DefaultParams(), - PreviousBlockTime: DefaultPreviousBlockTime, + Params: DefaultParams(), + PreviousAccumulationTimes: DefaultAccumulationTimes, + Deposits: DefaultDeposits, + Borrows: DefaultBorrows, + TotalSupplied: DefaultTotalSupplied, + TotalBorrowed: DefaultTotalBorrowed, + TotalReserves: DefaultTotalReserves, } } @@ -42,8 +60,24 @@ func (gs GenesisState) Validate() error { if err := gs.Params.Validate(); err != nil { return err } - if gs.PreviousBlockTime.Equal(time.Time{}) { - return fmt.Errorf("previous block time not set") + if err := gs.PreviousAccumulationTimes.Validate(); err != nil { + return err + } + if err := gs.Deposits.Validate(); err != nil { + return err + } + if err := gs.Borrows.Validate(); err != nil { + return err + } + + if !gs.TotalSupplied.IsValid() { + return fmt.Errorf("invalid total supplied coins: %s", gs.TotalSupplied) + } + if !gs.TotalBorrowed.IsValid() { + return fmt.Errorf("invalid total borrowed coins: %s", gs.TotalBorrowed) + } + if !gs.TotalReserves.IsValid() { + return fmt.Errorf("invalid total reserves coins: %s", gs.TotalReserves) } return nil } @@ -59,3 +93,45 @@ func (gs GenesisState) Equal(gs2 GenesisState) bool { func (gs GenesisState) IsEmpty() bool { return gs.Equal(GenesisState{}) } + +// GenesisAccumulationTime stores the previous distribution time and its corresponding denom +type GenesisAccumulationTime struct { + CollateralType string `json:"collateral_type" yaml:"collateral_type"` + PreviousAccumulationTime time.Time `json:"previous_accumulation_time" yaml:"previous_accumulation_time"` + SupplyInterestFactor sdk.Dec `json:"supply_interest_factor" yaml:"supply_interest_factor"` + BorrowInterestFactor sdk.Dec `json:"borrow_interest_factor" yaml:"borrow_interest_factor"` +} + +// NewGenesisAccumulationTime returns a new GenesisAccumulationTime +func NewGenesisAccumulationTime(ctype string, prevTime time.Time, supplyFactor, borrowFactor sdk.Dec) GenesisAccumulationTime { + return GenesisAccumulationTime{ + CollateralType: ctype, + PreviousAccumulationTime: prevTime, + SupplyInterestFactor: supplyFactor, + BorrowInterestFactor: borrowFactor, + } +} + +// GenesisAccumulationTimes slice of GenesisAccumulationTime +type GenesisAccumulationTimes []GenesisAccumulationTime + +// Validate performs validation of GenesisAccumulationTimes +func (gats GenesisAccumulationTimes) Validate() error { + for _, gat := range gats { + if err := gat.Validate(); err != nil { + return err + } + } + return nil +} + +// Validate performs validation of GenesisAccumulationTime +func (gat GenesisAccumulationTime) Validate() error { + if gat.SupplyInterestFactor.LT(sdk.OneDec()) { + return fmt.Errorf("supply interest factor should be ≥ 1.0, is %s for %s", gat.SupplyInterestFactor, gat.CollateralType) + } + if gat.BorrowInterestFactor.LT(sdk.OneDec()) { + return fmt.Errorf("borrow interest factor should be ≥ 1.0, is %s for %s", gat.BorrowInterestFactor, gat.CollateralType) + } + return nil +} diff --git a/x/hard/types/genesis_test.go b/x/hard/types/genesis_test.go index bbf00057..b9db4e1b 100644 --- a/x/hard/types/genesis_test.go +++ b/x/hard/types/genesis_test.go @@ -7,9 +7,19 @@ import ( "github.com/stretchr/testify/suite" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/x/hard/types" ) +const ( + USDX_CF = 1000000 + KAVA_CF = 1000000 + BTCB_CF = 100000000 + BNB_CF = 100000000 + BUSD_CF = 100000000 +) + type GenesisTestSuite struct { suite.Suite } @@ -17,7 +27,12 @@ type GenesisTestSuite struct { func (suite *GenesisTestSuite) TestGenesisValidation() { type args struct { params types.Params - pbt time.Time + gats types.GenesisAccumulationTimes + deps types.Deposits + brws types.Borrows + ts sdk.Coins + tb sdk.Coins + tr sdk.Coins } testCases := []struct { name string @@ -29,7 +44,12 @@ func (suite *GenesisTestSuite) TestGenesisValidation() { name: "default", args: args{ params: types.DefaultParams(), - pbt: types.DefaultPreviousBlockTime, + gats: types.DefaultAccumulationTimes, + deps: types.DefaultDeposits, + brws: types.DefaultBorrows, + ts: types.DefaultTotalSupplied, + tb: types.DefaultTotalBorrowed, + tr: types.DefaultTotalReserves, }, expectPass: true, expectedErr: "", @@ -37,25 +57,28 @@ func (suite *GenesisTestSuite) TestGenesisValidation() { { name: "valid", args: args{ - params: types.NewParams(true, types.DefaultMoneyMarkets, types.DefaultCheckLtvIndexCount), - pbt: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC), + params: types.NewParams( + types.MoneyMarkets{ + types.NewMoneyMarket("usdx", types.NewBorrowLimit(true, sdk.MustNewDecFromStr("100000000000"), sdk.MustNewDecFromStr("1")), "usdx:usd", sdk.NewInt(USDX_CF), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), + }, + 10, + ), + gats: types.GenesisAccumulationTimes{ + types.NewGenesisAccumulationTime("usdx", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), sdk.OneDec(), sdk.OneDec()), + }, + deps: types.DefaultDeposits, + brws: types.DefaultBorrows, + ts: sdk.Coins{}, + tb: sdk.Coins{}, + tr: sdk.Coins{}, }, expectPass: true, expectedErr: "", }, - { - name: "invalid previous blocktime", - args: args{ - params: types.NewParams(true, types.DefaultMoneyMarkets, types.DefaultCheckLtvIndexCount), - pbt: time.Time{}, - }, - expectPass: false, - expectedErr: "previous block time not set", - }, } for _, tc := range testCases { suite.Run(tc.name, func() { - gs := types.NewGenesisState(tc.args.params, tc.args.pbt) + gs := types.NewGenesisState(tc.args.params, tc.args.gats, tc.args.deps, tc.args.brws, tc.args.ts, tc.args.tb, tc.args.tr) err := gs.Validate() if tc.expectPass { suite.NoError(err) diff --git a/x/hard/types/params.go b/x/hard/types/params.go index 8739ce94..731dfdba 100644 --- a/x/hard/types/params.go +++ b/x/hard/types/params.go @@ -11,18 +11,21 @@ import ( // Parameter keys and default values var ( - KeyActive = []byte("Active") KeyMoneyMarkets = []byte("MoneyMarkets") KeyCheckLtvIndexCount = []byte("CheckLtvIndexCount") - DefaultActive = true DefaultMoneyMarkets = MoneyMarkets{} DefaultCheckLtvIndexCount = 10 GovDenom = cdptypes.DefaultGovDenom + DefaultAccumulationTimes = GenesisAccumulationTimes{} + DefaultTotalSupplied = sdk.Coins{} + DefaultTotalBorrowed = sdk.Coins{} + DefaultTotalReserves = sdk.Coins{} + DefaultDeposits = Deposits{} + DefaultBorrows = Borrows{} ) // Params governance parameters for hard module type Params struct { - Active bool `json:"active" yaml:"active"` MoneyMarkets MoneyMarkets `json:"money_markets" yaml:"money_markets"` CheckLtvIndexCount int `json:"check_ltv_index_count" yaml:"check_ltv_index_count"` } @@ -229,9 +232,8 @@ func (irm InterestRateModel) Equal(irmCompareTo InterestRateModel) bool { type InterestRateModels []InterestRateModel // NewParams returns a new params object -func NewParams(active bool, moneyMarkets MoneyMarkets, checkLtvIndexCount int) Params { +func NewParams(moneyMarkets MoneyMarkets, checkLtvIndexCount int) Params { return Params{ - Active: active, MoneyMarkets: moneyMarkets, CheckLtvIndexCount: checkLtvIndexCount, } @@ -239,16 +241,15 @@ func NewParams(active bool, moneyMarkets MoneyMarkets, checkLtvIndexCount int) P // DefaultParams returns default params for hard module func DefaultParams() Params { - return NewParams(DefaultActive, DefaultMoneyMarkets, DefaultCheckLtvIndexCount) + return NewParams(DefaultMoneyMarkets, DefaultCheckLtvIndexCount) } // String implements fmt.Stringer func (p Params) String() string { return fmt.Sprintf(`Params: - Active: %t Money Markets %v Check LTV Index Count: %v`, - p.Active, p.MoneyMarkets, p.CheckLtvIndexCount) + p.MoneyMarkets, p.CheckLtvIndexCount) } // ParamKeyTable Key declaration for parameters @@ -259,7 +260,6 @@ func ParamKeyTable() params.KeyTable { // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs func (p *Params) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam), params.NewParamSetPair(KeyMoneyMarkets, &p.MoneyMarkets, validateMoneyMarketParams), params.NewParamSetPair(KeyCheckLtvIndexCount, &p.CheckLtvIndexCount, validateCheckLtvIndexCount), } @@ -267,9 +267,6 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs { // Validate checks that the parameters have valid values. func (p Params) Validate() error { - if err := validateActiveParam(p.Active); err != nil { - return err - } if err := validateMoneyMarketParams(p.MoneyMarkets); err != nil { return err @@ -278,15 +275,6 @@ func (p Params) Validate() error { return validateCheckLtvIndexCount(p.CheckLtvIndexCount) } -func validateActiveParam(i interface{}) error { - _, ok := i.(bool) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - return nil -} - func validateMoneyMarketParams(i interface{}) error { mm, ok := i.(MoneyMarkets) if !ok { diff --git a/x/hard/types/params_test.go b/x/hard/types/params_test.go index 3aca53cd..87d51426 100644 --- a/x/hard/types/params_test.go +++ b/x/hard/types/params_test.go @@ -17,7 +17,6 @@ func (suite *ParamTestSuite) TestParamValidation() { type args struct { mms types.MoneyMarkets ltvCounter int - active bool } testCases := []struct { name string @@ -27,18 +26,9 @@ func (suite *ParamTestSuite) TestParamValidation() { }{ { name: "default", - args: args{ - active: types.DefaultActive, - }, - expectPass: true, - expectedErr: "", - }, - { - name: "valid", args: args{ mms: types.DefaultMoneyMarkets, - ltvCounter: 10, - active: true, + ltvCounter: types.DefaultCheckLtvIndexCount, }, expectPass: true, expectedErr: "", @@ -46,7 +36,7 @@ func (suite *ParamTestSuite) TestParamValidation() { } for _, tc := range testCases { suite.Run(tc.name, func() { - params := types.NewParams(tc.args.active, tc.args.mms, tc.args.ltvCounter) + params := types.NewParams(tc.args.mms, tc.args.ltvCounter) err := params.Validate() if tc.expectPass { suite.NoError(err) diff --git a/x/incentive/keeper/integration_test.go b/x/incentive/keeper/integration_test.go index 06204b67..71375982 100644 --- a/x/incentive/keeper/integration_test.go +++ b/x/incentive/keeper/integration_test.go @@ -157,7 +157,6 @@ func NewHardGenStateMulti() app.GenesisState { borrowLimit := sdk.NewDec(1000000000000000) hardGS := hard.NewGenesisState(hard.NewParams( - true, hard.MoneyMarkets{ hard.NewMoneyMarket("usdx", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), hard.NewMoneyMarket("ukava", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), @@ -166,7 +165,9 @@ func NewHardGenStateMulti() app.GenesisState { hard.NewMoneyMarket("xrp", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "xrp:usd", sdk.NewInt(1000000), sdk.NewInt(BTCB_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()), }, 0, // LTV counter - ), hard.DefaultPreviousBlockTime) + ), hard.DefaultAccumulationTimes, hard.DefaultDeposits, hard.DefaultBorrows, + hard.DefaultTotalSupplied, hard.DefaultTotalBorrowed, hard.DefaultTotalReserves, + ) return app.GenesisState{hard.ModuleName: hard.ModuleCdc.MustMarshalJSON(hardGS)} }