Hard Audit: remove LTV index from Hard module (#800)

* remove LTV index

* remove LTV param

* remove LTV param from tests

* remove LTV index from tests

* fix incentive hook not called before sync
This commit is contained in:
Denali Marsh 2021-02-08 13:23:37 +01:00 committed by GitHub
parent c8d4c02fb7
commit 1b2cfa6d1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 50 additions and 1470 deletions

View File

@ -7,5 +7,4 @@ import (
// BeginBlocker updates interest rates and attempts liquidations // BeginBlocker updates interest rates and attempts liquidations
func BeginBlocker(ctx sdk.Context, k Keeper) { func BeginBlocker(ctx sdk.Context, k Keeper) {
k.ApplyInterestRateUpdates(ctx) k.ApplyInterestRateUpdates(ctx)
k.AttemptIndexLiquidations(ctx)
} }

View File

@ -55,7 +55,6 @@ var (
DefaultGenesisState = types.DefaultGenesisState DefaultGenesisState = types.DefaultGenesisState
DefaultParams = types.DefaultParams DefaultParams = types.DefaultParams
DepositTypeIteratorKey = types.DepositTypeIteratorKey DepositTypeIteratorKey = types.DepositTypeIteratorKey
GetBorrowByLtvKey = types.GetBorrowByLtvKey
GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength
NewBorrow = types.NewBorrow NewBorrow = types.NewBorrow
NewBorrowInterestFactor = types.NewBorrowInterestFactor NewBorrowInterestFactor = types.NewBorrowInterestFactor
@ -89,7 +88,6 @@ var (
BorrowsKeyPrefix = types.BorrowsKeyPrefix BorrowsKeyPrefix = types.BorrowsKeyPrefix
DefaultAccumulationTimes = types.DefaultAccumulationTimes DefaultAccumulationTimes = types.DefaultAccumulationTimes
DefaultBorrows = types.DefaultBorrows DefaultBorrows = types.DefaultBorrows
DefaultCheckLtvIndexCount = types.DefaultCheckLtvIndexCount
DefaultDeposits = types.DefaultDeposits DefaultDeposits = types.DefaultDeposits
DefaultMoneyMarkets = types.DefaultMoneyMarkets DefaultMoneyMarkets = types.DefaultMoneyMarkets
DefaultTotalBorrowed = types.DefaultTotalBorrowed DefaultTotalBorrowed = types.DefaultTotalBorrowed
@ -124,9 +122,7 @@ var (
ErrPriceNotFound = types.ErrPriceNotFound ErrPriceNotFound = types.ErrPriceNotFound
ErrSuppliedCoinsNotFound = types.ErrSuppliedCoinsNotFound ErrSuppliedCoinsNotFound = types.ErrSuppliedCoinsNotFound
GovDenom = types.GovDenom GovDenom = types.GovDenom
KeyCheckLtvIndexCount = types.KeyCheckLtvIndexCount
KeyMoneyMarkets = types.KeyMoneyMarkets KeyMoneyMarkets = types.KeyMoneyMarkets
LtvIndexPrefix = types.LtvIndexPrefix
ModuleCdc = types.ModuleCdc ModuleCdc = types.ModuleCdc
MoneyMarketsPrefix = types.MoneyMarketsPrefix MoneyMarketsPrefix = types.MoneyMarketsPrefix
PreviousAccrualTimePrefix = types.PreviousAccrualTimePrefix PreviousAccrualTimePrefix = types.PreviousAccrualTimePrefix

View File

@ -142,8 +142,6 @@ func queryDepositsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
owner = depositOwner owner = depositOwner
} }
// Note: The 10 users with the lowest LTV ratio have their outstanding interest applied each block, so if
// testing with 10 or less addresses they'll all show their latest balance including outstanding interest.
page := viper.GetInt(flags.FlagPage) page := viper.GetInt(flags.FlagPage)
limit := viper.GetInt(flags.FlagLimit) limit := viper.GetInt(flags.FlagLimit)
@ -203,8 +201,6 @@ func queryBorrowsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
owner = borrowOwner owner = borrowOwner
} }
// Note: The 10 users with the lowest LTV ratio have their outstanding debt applied each block, so if
// testing with 10 or less addresses they'll all show their latest balance including outstanding debt.
page := viper.GetInt(flags.FlagPage) page := viper.GetInt(flags.FlagPage)
limit := viper.GetInt(flags.FlagLimit) limit := viper.GetInt(flags.FlagLimit)

View File

@ -22,13 +22,11 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
} }
} }
// Get current stored LTV based on stored borrows/deposits // Call incentive hooks
prevLtv, err := k.GetStoreLTV(ctx, borrower) existingDeposit, hasExistingDeposit := k.GetDeposit(ctx, borrower)
if err != nil { if hasExistingDeposit {
return err k.BeforeDepositModified(ctx, existingDeposit)
} }
// Call incentive hook
existingBorrow, hasExistingBorrow := k.GetBorrow(ctx, borrower) existingBorrow, hasExistingBorrow := k.GetBorrow(ctx, borrower)
if hasExistingBorrow { if hasExistingBorrow {
k.BeforeBorrowModified(ctx, existingBorrow) k.BeforeBorrowModified(ctx, existingBorrow)
@ -38,7 +36,7 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
k.SyncBorrowInterest(ctx, borrower) k.SyncBorrowInterest(ctx, borrower)
// Validate borrow amount within user and protocol limits // Validate borrow amount within user and protocol limits
err = k.ValidateBorrow(ctx, borrower, coins) err := k.ValidateBorrow(ctx, borrower, coins)
if err != nil { if err != nil {
return err return err
} }
@ -79,20 +77,14 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
} else { } else {
amount = coins amount = coins
} }
// Construct the user's new/updated borrow with amount and interest factors // Construct the user's new/updated borrow with amount and interest factors
borrow := types.NewBorrow(borrower, amount, interestFactors) borrow := types.NewBorrow(borrower, amount, interestFactors)
if borrow.Amount.Empty() {
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow k.DeleteBorrow(ctx, borrow)
deposit, foundDeposit := k.GetDeposit(ctx, borrower) } else {
if !foundDeposit { k.SetBorrow(ctx, borrow)
return types.ErrDepositNotFound
} }
newLtv, err := k.CalculateLtv(ctx, deposit, borrow)
if err != nil {
return err
}
k.UpdateBorrowAndLtvIndex(ctx, borrow, newLtv, prevLtv)
// Update total borrowed amount by newly borrowed coins. Don't add user's pending interest as // Update total borrowed amount by newly borrowed coins. Don't add user's pending interest as
// it has already been included in the total borrowed coins by the BeginBlocker. // it has already been included in the total borrowed coins by the BeginBlocker.

View File

@ -269,7 +269,6 @@ func (suite *KeeperTestSuite) TestBorrow() {
types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(100000000*BNB_CF), tc.args.loanToValueBNB), "bnb:usd", sdk.NewInt(BNB_CF), 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()), types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(100000000*BNB_CF), tc.args.loanToValueBNB), "bnb:usd", sdk.NewInt(BNB_CF), 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()),
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()), 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.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -23,12 +23,6 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
} }
} }
// Get current stored LTV based on stored borrows/deposits
prevLtv, err := k.GetStoreLTV(ctx, depositor)
if err != nil {
return err
}
// Call incentive hook // Call incentive hook
existingDeposit, hasExistingDeposit := k.GetDeposit(ctx, depositor) existingDeposit, hasExistingDeposit := k.GetDeposit(ctx, depositor)
if hasExistingDeposit { if hasExistingDeposit {
@ -39,7 +33,7 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
k.SyncBorrowInterest(ctx, depositor) k.SyncBorrowInterest(ctx, depositor)
k.SyncSupplyInterest(ctx, depositor) k.SyncSupplyInterest(ctx, depositor)
err = k.ValidateDeposit(ctx, coins) err := k.ValidateDeposit(ctx, coins)
if err != nil { if err != nil {
return err return err
} }
@ -85,14 +79,12 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
// Update the depositer's amount and supply interest factors in the store // Update the depositer's amount and supply interest factors in the store
deposit := types.NewDeposit(depositor, amount, interestFactors) deposit := types.NewDeposit(depositor, amount, interestFactors)
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow if deposit.Amount.Empty() {
borrow, _ := k.GetBorrow(ctx, depositor) k.DeleteDeposit(ctx, deposit)
newLtv, err := k.CalculateLtv(ctx, deposit, borrow) } else {
if err != nil { k.SetDeposit(ctx, deposit)
return err
} }
k.UpdateDepositAndLtvIndex(ctx, deposit, newLtv, prevLtv)
k.IncrementSuppliedCoins(ctx, coins) k.IncrementSuppliedCoins(ctx, coins)
if !foundDeposit { // User's first deposit if !foundDeposit { // User's first deposit
k.AfterDepositCreated(ctx, deposit) k.AfterDepositCreated(ctx, deposit)

View File

@ -111,7 +111,6 @@ func (suite *KeeperTestSuite) TestDeposit() {
types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "bnb:usd", sdk.NewInt(1000000), 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()), types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "bnb:usd", sdk.NewInt(1000000), 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()),
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()), 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.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -721,7 +721,6 @@ func (suite *KeeperTestSuite) TestBorrowInterest() {
tc.args.reserveFactor, // Reserve Factor tc.args.reserveFactor, // Reserve Factor
sdk.ZeroDec()), // Keeper Reward Percentage sdk.ZeroDec()), // Keeper Reward Percentage
}, },
0, // LTV counter
), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )
@ -1137,7 +1136,6 @@ func (suite *KeeperTestSuite) TestSupplyInterest() {
tc.args.reserveFactor, // Reserve Factor tc.args.reserveFactor, // Reserve Factor
sdk.ZeroDec()), // Keeper Reward Percentage sdk.ZeroDec()), // Keeper Reward Percentage
}, },
0, // LTV counter
), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -315,46 +315,3 @@ func (k Keeper) SetSupplyInterestFactor(ctx sdk.Context, denom string, supplyInt
bz := k.cdc.MustMarshalBinaryBare(supplyInterestFactor) bz := k.cdc.MustMarshalBinaryBare(supplyInterestFactor)
store.Set([]byte(denom), bz) store.Set([]byte(denom), bz)
} }
// InsertIntoLtvIndex indexes a user's borrow object by its current LTV
func (k Keeper) InsertIntoLtvIndex(ctx sdk.Context, ltv sdk.Dec, borrower sdk.AccAddress) {
store := prefix.NewStore(ctx.KVStore(k.key), types.LtvIndexPrefix)
store.Set(types.GetBorrowByLtvKey(ltv, borrower), borrower)
}
// RemoveFromLtvIndex removes a user's borrow object from the LTV index
func (k Keeper) RemoveFromLtvIndex(ctx sdk.Context, ltv sdk.Dec, borrower sdk.AccAddress) {
store := prefix.NewStore(ctx.KVStore(k.key), types.LtvIndexPrefix)
store.Delete(types.GetBorrowByLtvKey(ltv, borrower))
}
// IterateLtvIndex provides an iterator over the borrowers ordered by LTV.
// For results found before the cutoff count, the cb will be called and the item returned.
func (k Keeper) IterateLtvIndex(ctx sdk.Context, cutoffCount int,
cb func(addr sdk.AccAddress) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.key), types.LtvIndexPrefix)
iterator := store.ReverseIterator(nil, nil)
count := 0
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
// Stop iteration after first 10 items
count = count + 1
if count > cutoffCount {
break
}
id := iterator.Value()
cb(id)
}
}
// GetLtvIndexSlice returns the first 10 items in the LTV index from the store
func (k Keeper) GetLtvIndexSlice(ctx sdk.Context, count int) (addrs []sdk.AccAddress) {
k.IterateLtvIndex(ctx, count, func(addr sdk.AccAddress) bool {
addrs = append(addrs, addr)
return false
})
return
}

View File

@ -134,69 +134,6 @@ func (suite *KeeperTestSuite) TestIterateInterestRateModels() {
suite.Require().Equal(setDenoms, seenDenoms) suite.Require().Equal(setDenoms, seenDenoms)
} }
func (suite *KeeperTestSuite) TestSetDeleteLtvIndex() {
// LTV index should have 0 items
firstAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
suite.Require().Equal(0, len(firstAddrs))
// Add an item to the LTV index
addr := sdk.AccAddress("test")
ltv := sdk.MustNewDecFromStr("1.1")
suite.Require().NotPanics(func() { suite.keeper.InsertIntoLtvIndex(suite.ctx, ltv, addr) })
// LTV index should have 1 item
secondAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
suite.Require().Equal(1, len(secondAddrs))
// Attempt to remove invalid item from LTV index
fakeLtv := sdk.MustNewDecFromStr("1.2")
suite.Require().NotPanics(func() { suite.keeper.RemoveFromLtvIndex(suite.ctx, fakeLtv, addr) })
// LTV index should still have 1 item
thirdAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
suite.Require().Equal(1, len(thirdAddrs))
// Attempt to remove valid item from LTV index
suite.Require().NotPanics(func() { suite.keeper.RemoveFromLtvIndex(suite.ctx, ltv, addr) })
// LTV index should still have 0 items
fourthAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
suite.Require().Equal(0, len(fourthAddrs))
}
func (suite *KeeperTestSuite) TestIterateLtvIndex() {
var setAddrs []sdk.AccAddress
for i := 1; i <= 20; i++ {
addr := sdk.AccAddress("test" + fmt.Sprint(i))
incrementalDec := sdk.NewDec(int64(i)).Quo(sdk.NewDec(10))
ltv := sdk.OneDec().Add(incrementalDec)
// Set the ltv-address pair in the store
suite.Require().NotPanics(func() { suite.keeper.InsertIntoLtvIndex(suite.ctx, ltv, addr) })
setAddrs = append(setAddrs, addr)
}
// Only the first 10 addresses should be returned
sliceAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
suite.Require().Equal(addressSort(setAddrs[10:20]), addressSort(sliceAddrs))
// Insert an additional item into the LTV index that should be returned in the first 10 elements
addr := sdk.AccAddress("test" + fmt.Sprint(21))
ltv := sdk.OneDec().Add(sdk.MustNewDecFromStr("15").Quo(sdk.NewDec(10)))
suite.Require().NotPanics(func() { suite.keeper.InsertIntoLtvIndex(suite.ctx, ltv, addr) })
// Fetch the updated LTV index
updatedSliceAddrs := suite.keeper.GetLtvIndexSlice(suite.ctx, 10)
sawAddr := false
for _, updatedSliceAddr := range updatedSliceAddrs {
if updatedSliceAddr.Equals(addr) {
sawAddr = true
}
}
suite.Require().Equal(true, sawAddr)
}
func (suite *KeeperTestSuite) getAccount(addr sdk.AccAddress) authexported.Account { func (suite *KeeperTestSuite) getAccount(addr sdk.AccAddress) authexported.Account {
ak := suite.app.GetAccountKeeper() ak := suite.app.GetAccountKeeper()
return ak.GetAccount(suite.ctx, addr) return ak.GetAccount(suite.ctx, addr)

View File

@ -1,8 +1,6 @@
package keeper package keeper
import ( import (
"errors"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -16,29 +14,8 @@ type LiqData struct {
conversionFactor sdk.Int conversionFactor sdk.Int
} }
// AttemptIndexLiquidations attempts to liquidate the lowest LTV borrows
func (k Keeper) AttemptIndexLiquidations(ctx sdk.Context) error {
params := k.GetParams(ctx)
borrowers := k.GetLtvIndexSlice(ctx, params.CheckLtvIndexCount)
for _, borrower := range borrowers {
err := k.AttemptKeeperLiquidation(ctx, sdk.AccAddress(types.LiquidatorAccount), borrower)
if err != nil {
if !errors.Is(err, types.ErrBorrowNotLiquidatable) && !errors.Is(err, types.ErrBorrowNotFound) {
panic(err)
}
}
}
return nil
}
// AttemptKeeperLiquidation enables a keeper to liquidate an individual borrower's position // AttemptKeeperLiquidation enables a keeper to liquidate an individual borrower's position
func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress, borrower sdk.AccAddress) error { func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress, borrower sdk.AccAddress) error {
prevLtv, err := k.GetStoreLTV(ctx, borrower)
if err != nil {
return err
}
deposit, found := k.GetDeposit(ctx, borrower) deposit, found := k.GetDeposit(ctx, borrower)
if !found { if !found {
return types.ErrDepositNotFound return types.ErrDepositNotFound
@ -82,7 +59,8 @@ func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress,
return err return err
} }
k.DeleteDepositBorrowAndLtvIndex(ctx, deposit, borrow, prevLtv) k.DeleteDeposit(ctx, deposit)
k.DeleteBorrow(ctx, borrow)
return nil return nil
} }
@ -321,35 +299,6 @@ func (k Keeper) IsWithinValidLtvRange(ctx sdk.Context, deposit types.Deposit, bo
return true, nil return true, nil
} }
// UpdateBorrowAndLtvIndex updates a borrow and its LTV index value in the store
func (k Keeper) UpdateBorrowAndLtvIndex(ctx sdk.Context, borrow types.Borrow, newLtv, oldLtv sdk.Dec) {
k.RemoveFromLtvIndex(ctx, oldLtv, borrow.Borrower)
if borrow.Amount.Empty() {
k.DeleteBorrow(ctx, borrow)
return
}
k.SetBorrow(ctx, borrow)
k.InsertIntoLtvIndex(ctx, newLtv, borrow.Borrower)
}
// UpdateDepositAndLtvIndex updates a deposit and its LTV index value in the store
func (k Keeper) UpdateDepositAndLtvIndex(ctx sdk.Context, deposit types.Deposit, newLtv, oldLtv sdk.Dec) {
k.RemoveFromLtvIndex(ctx, oldLtv, deposit.Depositor)
if deposit.Amount.Empty() {
k.DeleteDeposit(ctx, deposit)
return
}
k.SetDeposit(ctx, deposit)
k.InsertIntoLtvIndex(ctx, newLtv, deposit.Depositor)
}
// DeleteDepositBorrowAndLtvIndex deletes deposit, borrow, and ltv index
func (k Keeper) DeleteDepositBorrowAndLtvIndex(ctx sdk.Context, deposit types.Deposit, borrow types.Borrow, oldLtv sdk.Dec) {
k.RemoveFromLtvIndex(ctx, oldLtv, deposit.Depositor)
k.DeleteDeposit(ctx, deposit)
k.DeleteBorrow(ctx, borrow)
}
// GetStoreLTV calculates the user's current LTV based on their deposits/borrows in the store // GetStoreLTV calculates the user's current LTV based on their deposits/borrows in the store
// and does not include any outsanding interest. // and does not include any outsanding interest.
func (k Keeper) GetStoreLTV(ctx sdk.Context, addr sdk.AccAddress) (sdk.Dec, error) { func (k Keeper) GetStoreLTV(ctx sdk.Context, addr sdk.AccAddress) (sdk.Dec, error) {

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,6 @@ import (
// Repay borrowed funds // Repay borrowed funds
func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) error { func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) error {
// Get current stored LTV based on stored borrows/deposits
prevLtv, err := k.GetStoreLTV(ctx, sender)
if err != nil {
return err
}
// Check borrow exists here to avoid duplicating store read in ValidateRepay // Check borrow exists here to avoid duplicating store read in ValidateRepay
borrow, found := k.GetBorrow(ctx, sender) borrow, found := k.GetBorrow(ctx, sender)
if !found { if !found {
@ -27,7 +21,7 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
k.SyncBorrowInterest(ctx, sender) k.SyncBorrowInterest(ctx, sender)
// Validate requested repay // Validate requested repay
err = k.ValidateRepay(ctx, sender, coins) err := k.ValidateRepay(ctx, sender, coins)
if err != nil { if err != nil {
return err return err
} }
@ -57,17 +51,11 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
// Update user's borrow in store // Update user's borrow in store
borrow.Amount = borrow.Amount.Sub(payment) borrow.Amount = borrow.Amount.Sub(payment)
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow if borrow.Amount.Empty() {
deposit, foundDeposit := k.GetDeposit(ctx, sender) k.DeleteBorrow(ctx, borrow)
if !foundDeposit { } else {
return types.ErrDepositNotFound k.SetBorrow(ctx, borrow)
} }
newLtv, err := k.CalculateLtv(ctx, deposit, borrow)
if err != nil {
return err
}
k.UpdateBorrowAndLtvIndex(ctx, borrow, newLtv, prevLtv)
// Update total borrowed amount // Update total borrowed amount
k.DecrementBorrowedCoins(ctx, payment) k.DecrementBorrowedCoins(ctx, payment)

View File

@ -155,7 +155,6 @@ func (suite *KeeperTestSuite) TestRepay() {
sdk.MustNewDecFromStr("0.05"), // Reserve Factor sdk.MustNewDecFromStr("0.05"), // Reserve Factor
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
}, },
0, // LTV counter
), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -285,7 +285,6 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
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("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("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.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -9,18 +9,16 @@ import (
// Withdraw returns some or all of a deposit back to original depositor // Withdraw returns some or all of a deposit back to original depositor
func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error { func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error {
// Get current stored LTV based on stored borrows/deposits
prevLtv, err := k.GetStoreLTV(ctx, depositor)
if err != nil {
return err
}
deposit, found := k.GetDeposit(ctx, depositor) deposit, found := k.GetDeposit(ctx, depositor)
if !found { if !found {
return sdkerrors.Wrapf(types.ErrDepositNotFound, "no deposit found for %s", depositor) return sdkerrors.Wrapf(types.ErrDepositNotFound, "no deposit found for %s", depositor)
} }
// Call incentive hook // Call incentive hooks
k.BeforeDepositModified(ctx, deposit) k.BeforeDepositModified(ctx, deposit)
existingBorrow, hasExistingBorrow := k.GetBorrow(ctx, depositor)
if hasExistingBorrow {
k.BeforeBorrowModified(ctx, existingBorrow)
}
k.SyncBorrowInterest(ctx, depositor) k.SyncBorrowInterest(ctx, depositor)
k.SyncSupplyInterest(ctx, depositor) k.SyncSupplyInterest(ctx, depositor)
@ -61,12 +59,11 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
} }
deposit.Amount = deposit.Amount.Sub(amount) deposit.Amount = deposit.Amount.Sub(amount)
newLtv, err := k.CalculateLtv(ctx, deposit, borrow) if deposit.Amount.Empty() {
if err != nil { k.DeleteDeposit(ctx, deposit)
return err } else {
k.SetDeposit(ctx, deposit)
} }
k.UpdateDepositAndLtvIndex(ctx, deposit, newLtv, prevLtv)
// Update total supplied amount // Update total supplied amount
k.DecrementSuppliedCoins(ctx, amount) k.DecrementSuppliedCoins(ctx, amount)

View File

@ -129,7 +129,6 @@ func (suite *KeeperTestSuite) TestWithdraw() {
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("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()), 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.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )
@ -283,7 +282,6 @@ func (suite *KeeperTestSuite) TestLtvWithdraw() {
reserveFactor, // Reserve Factor reserveFactor, // Reserve Factor
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
}, },
0, // LTV counter
), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows, ), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves, types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
) )

View File

@ -61,7 +61,6 @@ func (suite *GenesisTestSuite) TestGenesisValidation() {
types.MoneyMarkets{ 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()), 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{ gats: types.GenesisAccumulationTimes{
types.NewGenesisAccumulationTime("usdx", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), sdk.OneDec(), sdk.OneDec()), types.NewGenesisAccumulationTime("usdx", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), sdk.OneDec(), sdk.OneDec()),

View File

@ -1,9 +1,5 @@
package types package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const ( const (
// ModuleName name that will be used throughout the module // ModuleName name that will be used throughout the module
ModuleName = "hard" ModuleName = "hard"
@ -38,7 +34,6 @@ var (
BorrowInterestFactorPrefix = []byte{0x08} // denom -> sdk.Dec BorrowInterestFactorPrefix = []byte{0x08} // denom -> sdk.Dec
SupplyInterestFactorPrefix = []byte{0x09} // denom -> sdk.Dec SupplyInterestFactorPrefix = []byte{0x09} // denom -> sdk.Dec
DelegatorInterestFactorPrefix = []byte{0x10} // denom -> sdk.Dec DelegatorInterestFactorPrefix = []byte{0x10} // denom -> sdk.Dec
LtvIndexPrefix = []byte{0x11}
sep = []byte(":") sep = []byte(":")
) )
@ -47,11 +42,6 @@ func DepositTypeIteratorKey(denom string) []byte {
return createKey([]byte(denom)) return createKey([]byte(denom))
} }
// GetBorrowByLtvKey is used by the LTV index
func GetBorrowByLtvKey(ltv sdk.Dec, borrower sdk.AccAddress) []byte {
return append(ltv.Bytes(), borrower...)
}
func createKey(bytes ...[]byte) (r []byte) { func createKey(bytes ...[]byte) (r []byte) {
for _, b := range bytes { for _, b := range bytes {
r = append(r, b...) r = append(r, b...)

View File

@ -11,23 +11,20 @@ import (
// Parameter keys and default values // Parameter keys and default values
var ( var (
KeyMoneyMarkets = []byte("MoneyMarkets") KeyMoneyMarkets = []byte("MoneyMarkets")
KeyCheckLtvIndexCount = []byte("CheckLtvIndexCount") DefaultMoneyMarkets = MoneyMarkets{}
DefaultMoneyMarkets = MoneyMarkets{} GovDenom = cdptypes.DefaultGovDenom
DefaultCheckLtvIndexCount = 10 DefaultAccumulationTimes = GenesisAccumulationTimes{}
GovDenom = cdptypes.DefaultGovDenom DefaultTotalSupplied = sdk.Coins{}
DefaultAccumulationTimes = GenesisAccumulationTimes{} DefaultTotalBorrowed = sdk.Coins{}
DefaultTotalSupplied = sdk.Coins{} DefaultTotalReserves = sdk.Coins{}
DefaultTotalBorrowed = sdk.Coins{} DefaultDeposits = Deposits{}
DefaultTotalReserves = sdk.Coins{} DefaultBorrows = Borrows{}
DefaultDeposits = Deposits{}
DefaultBorrows = Borrows{}
) )
// Params governance parameters for hard module // Params governance parameters for hard module
type Params struct { type Params struct {
MoneyMarkets MoneyMarkets `json:"money_markets" yaml:"money_markets"` MoneyMarkets MoneyMarkets `json:"money_markets" yaml:"money_markets"`
CheckLtvIndexCount int `json:"check_ltv_index_count" yaml:"check_ltv_index_count"`
} }
// BorrowLimit enforces restrictions on a money market // BorrowLimit enforces restrictions on a money market
@ -232,24 +229,22 @@ func (irm InterestRateModel) Equal(irmCompareTo InterestRateModel) bool {
type InterestRateModels []InterestRateModel type InterestRateModels []InterestRateModel
// NewParams returns a new params object // NewParams returns a new params object
func NewParams(moneyMarkets MoneyMarkets, checkLtvIndexCount int) Params { func NewParams(moneyMarkets MoneyMarkets) Params {
return Params{ return Params{
MoneyMarkets: moneyMarkets, MoneyMarkets: moneyMarkets,
CheckLtvIndexCount: checkLtvIndexCount,
} }
} }
// DefaultParams returns default params for hard module // DefaultParams returns default params for hard module
func DefaultParams() Params { func DefaultParams() Params {
return NewParams(DefaultMoneyMarkets, DefaultCheckLtvIndexCount) return NewParams(DefaultMoneyMarkets)
} }
// String implements fmt.Stringer // String implements fmt.Stringer
func (p Params) String() string { func (p Params) String() string {
return fmt.Sprintf(`Params: return fmt.Sprintf(`Params:
Money Markets %v Money Markets %v`,
Check LTV Index Count: %v`, p.MoneyMarkets)
p.MoneyMarkets, p.CheckLtvIndexCount)
} }
// ParamKeyTable Key declaration for parameters // ParamKeyTable Key declaration for parameters
@ -261,18 +256,12 @@ func ParamKeyTable() params.KeyTable {
func (p *Params) ParamSetPairs() params.ParamSetPairs { func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{ return params.ParamSetPairs{
params.NewParamSetPair(KeyMoneyMarkets, &p.MoneyMarkets, validateMoneyMarketParams), params.NewParamSetPair(KeyMoneyMarkets, &p.MoneyMarkets, validateMoneyMarketParams),
params.NewParamSetPair(KeyCheckLtvIndexCount, &p.CheckLtvIndexCount, validateCheckLtvIndexCount),
} }
} }
// Validate checks that the parameters have valid values. // Validate checks that the parameters have valid values.
func (p Params) Validate() error { func (p Params) Validate() error {
return validateMoneyMarketParams(p.MoneyMarkets)
if err := validateMoneyMarketParams(p.MoneyMarkets); err != nil {
return err
}
return validateCheckLtvIndexCount(p.CheckLtvIndexCount)
} }
func validateMoneyMarketParams(i interface{}) error { func validateMoneyMarketParams(i interface{}) error {
@ -283,16 +272,3 @@ func validateMoneyMarketParams(i interface{}) error {
return mm.Validate() return mm.Validate()
} }
func validateCheckLtvIndexCount(i interface{}) error {
ltvCheckCount, ok := i.(int)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
if ltvCheckCount < 0 {
return fmt.Errorf("CheckLtvIndexCount param must be positive, got: %d", ltvCheckCount)
}
return nil
}

View File

@ -15,8 +15,7 @@ type ParamTestSuite struct {
func (suite *ParamTestSuite) TestParamValidation() { func (suite *ParamTestSuite) TestParamValidation() {
type args struct { type args struct {
mms types.MoneyMarkets mms types.MoneyMarkets
ltvCounter int
} }
testCases := []struct { testCases := []struct {
name string name string
@ -27,8 +26,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
{ {
name: "default", name: "default",
args: args{ args: args{
mms: types.DefaultMoneyMarkets, mms: types.DefaultMoneyMarkets,
ltvCounter: types.DefaultCheckLtvIndexCount,
}, },
expectPass: true, expectPass: true,
expectedErr: "", expectedErr: "",
@ -36,7 +34,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
params := types.NewParams(tc.args.mms, tc.args.ltvCounter) params := types.NewParams(tc.args.mms)
err := params.Validate() err := params.Validate()
if tc.expectPass { if tc.expectPass {
suite.NoError(err) suite.NoError(err)

View File

@ -164,7 +164,6 @@ func NewHardGenStateMulti() app.GenesisState {
hard.NewMoneyMarket("btcb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "btc: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()), hard.NewMoneyMarket("btcb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "btc: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()),
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()), 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.DefaultAccumulationTimes, hard.DefaultDeposits, hard.DefaultBorrows, ), hard.DefaultAccumulationTimes, hard.DefaultDeposits, hard.DefaultBorrows,
hard.DefaultTotalSupplied, hard.DefaultTotalBorrowed, hard.DefaultTotalReserves, hard.DefaultTotalSupplied, hard.DefaultTotalBorrowed, hard.DefaultTotalReserves,
) )