mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
Hard: LTV index refactor (#758)
* add set/delete/update ltv methods * refactor borrow logic * basic updates to keeper logic for compile * Add deposit index set/delete/update keeper methods * refactor deposit logic * refactor repay logic * update withdraw logic * introduce DeleteDepositBorrowAndLtvIndex * remove unused bool from AttemptKeeperLiquidation * remove comments (transitioned to asana cards) * catch multiple error types in liquidation loop
This commit is contained in:
parent
38306e5465
commit
bc110ce609
@ -124,7 +124,7 @@ func handleMsgRepay(ctx sdk.Context, k keeper.Keeper, msg types.MsgRepay) (*sdk.
|
||||
}
|
||||
|
||||
func handleMsgLiquidate(ctx sdk.Context, k keeper.Keeper, msg types.MsgLiquidate) (*sdk.Result, error) {
|
||||
_, err := k.AttemptKeeperLiquidation(ctx, msg.Keeper, msg.Borrower)
|
||||
err := k.AttemptKeeperLiquidation(ctx, msg.Keeper, msg.Borrower)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
|
||||
}
|
||||
|
||||
// Get current stored LTV based on stored borrows/deposits
|
||||
prevLtv, shouldRemoveIndex, err := k.GetStoreLTV(ctx, borrower)
|
||||
prevLtv, err := k.GetStoreLTV(ctx, borrower)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -85,11 +85,20 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
|
||||
amount = coins
|
||||
}
|
||||
|
||||
// Update the borrower's amount and borrow interest factors in the store
|
||||
// Construct the user's new/updated borrow with amount and interest factors
|
||||
borrow := types.NewBorrow(borrower, amount, borrowInterestFactors)
|
||||
k.SetBorrow(ctx, borrow)
|
||||
|
||||
k.UpdateItemInLtvIndex(ctx, prevLtv, shouldRemoveIndex, borrower)
|
||||
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow
|
||||
deposit, foundDeposit := k.GetDeposit(ctx, borrower)
|
||||
if !foundDeposit {
|
||||
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
|
||||
// it has already been included in the total borrowed coins by the BeginBlocker.
|
||||
|
@ -24,7 +24,7 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
|
||||
}
|
||||
|
||||
// Get current stored LTV based on stored borrows/deposits
|
||||
prevLtv, shouldRemoveIndex, err := k.GetStoreLTV(ctx, depositor)
|
||||
prevLtv, err := k.GetStoreLTV(ctx, depositor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -87,12 +87,17 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
|
||||
} else {
|
||||
amount = coins
|
||||
}
|
||||
|
||||
// Update the depositer's amount and supply interest factors in the store
|
||||
deposit := types.NewDeposit(depositor, amount, supplyInterestFactors)
|
||||
k.SetDeposit(ctx, deposit)
|
||||
|
||||
k.UpdateItemInLtvIndex(ctx, prevLtv, shouldRemoveIndex, depositor)
|
||||
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow
|
||||
borrow, _ := k.GetBorrow(ctx, depositor)
|
||||
newLtv, err := k.CalculateLtv(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k.UpdateDepositAndLtvIndex(ctx, deposit, newLtv, prevLtv)
|
||||
|
||||
// Update total supplied amount by newly supplied coins. Don't add user's pending interest as
|
||||
// it has already been included in the total supplied coins by the BeginBlocker.
|
||||
|
@ -22,9 +22,9 @@ func (k Keeper) AttemptIndexLiquidations(ctx sdk.Context) error {
|
||||
borrowers := k.GetLtvIndexSlice(ctx, params.CheckLtvIndexCount)
|
||||
|
||||
for _, borrower := range borrowers {
|
||||
_, err := k.AttemptKeeperLiquidation(ctx, sdk.AccAddress(types.LiquidatorAccount), borrower)
|
||||
err := k.AttemptKeeperLiquidation(ctx, sdk.AccAddress(types.LiquidatorAccount), borrower)
|
||||
if err != nil {
|
||||
if !errors.Is(err, types.ErrBorrowNotLiquidatable) {
|
||||
if !errors.Is(err, types.ErrBorrowNotLiquidatable) && !errors.Is(err, types.ErrBorrowNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -33,34 +33,30 @@ func (k Keeper) AttemptIndexLiquidations(ctx sdk.Context) error {
|
||||
}
|
||||
|
||||
// AttemptKeeperLiquidation enables a keeper to liquidate an individual borrower's position
|
||||
func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress, borrower sdk.AccAddress) (bool, error) {
|
||||
prevLtv, shouldInsertIndex, err := k.GetStoreLTV(ctx, borrower)
|
||||
func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress, borrower sdk.AccAddress) error {
|
||||
prevLtv, err := k.GetStoreLTV(ctx, borrower)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
k.SyncBorrowInterest(ctx, borrower)
|
||||
|
||||
k.UpdateItemInLtvIndex(ctx, prevLtv, shouldInsertIndex, borrower)
|
||||
|
||||
// Fetch deposits and parse coin denoms
|
||||
deposit, found := k.GetDeposit(ctx, borrower)
|
||||
if !found {
|
||||
return false, sdkerrors.Wrapf(types.ErrDepositsNotFound, "no deposits found for %s", borrower)
|
||||
return types.ErrDepositNotFound
|
||||
}
|
||||
|
||||
// Fetch borrow balances and parse coin denoms
|
||||
borrow, found := k.GetBorrow(ctx, borrower)
|
||||
if !found {
|
||||
return false, types.ErrBorrowNotFound
|
||||
return types.ErrBorrowNotFound
|
||||
}
|
||||
|
||||
isWithinRange, err := k.IsWithinValidLtvRange(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
if isWithinRange {
|
||||
return false, sdkerrors.Wrapf(types.ErrBorrowNotLiquidatable, "position is within valid LTV range")
|
||||
return sdkerrors.Wrapf(types.ErrBorrowNotLiquidatable, "position is within valid LTV range")
|
||||
}
|
||||
|
||||
// Sending coins to auction module with keeper address getting % of the profits
|
||||
@ -68,19 +64,11 @@ func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress,
|
||||
depositDenoms := getDenoms(deposit.Amount)
|
||||
err = k.SeizeDeposits(ctx, keeper, deposit, borrow, depositDenoms, borrowDenoms)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
currLtv, _, err := k.GetStoreLTV(ctx, borrower)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
k.RemoveFromLtvIndex(ctx, currLtv, borrower)
|
||||
|
||||
k.DeleteBorrow(ctx, borrow)
|
||||
k.DeleteDeposit(ctx, deposit)
|
||||
|
||||
return true, err
|
||||
k.DeleteDepositBorrowAndLtvIndex(ctx, deposit, borrow, prevLtv)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeizeDeposits seizes a list of deposits and sends them to auction
|
||||
@ -276,36 +264,40 @@ func (k Keeper) IsWithinValidLtvRange(ctx sdk.Context, deposit types.Deposit, bo
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// UpdateItemInLtvIndex updates the key a borrower's address is stored under in the LTV index
|
||||
func (k Keeper) UpdateItemInLtvIndex(ctx sdk.Context, prevLtv sdk.Dec,
|
||||
shouldRemoveIndex bool, borrower sdk.AccAddress) error {
|
||||
currLtv, shouldInsertIndex, err := k.GetStoreLTV(ctx, borrower)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 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)
|
||||
k.SetBorrow(ctx, borrow)
|
||||
k.InsertIntoLtvIndex(ctx, newLtv, borrow.Borrower)
|
||||
}
|
||||
|
||||
if shouldRemoveIndex {
|
||||
k.RemoveFromLtvIndex(ctx, prevLtv, borrower)
|
||||
}
|
||||
if shouldInsertIndex {
|
||||
k.InsertIntoLtvIndex(ctx, currLtv, borrower)
|
||||
}
|
||||
return nil
|
||||
// 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)
|
||||
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
|
||||
// and does not include any outsanding interest.
|
||||
func (k Keeper) GetStoreLTV(ctx sdk.Context, addr sdk.AccAddress) (sdk.Dec, bool, error) {
|
||||
func (k Keeper) GetStoreLTV(ctx sdk.Context, addr sdk.AccAddress) (sdk.Dec, error) {
|
||||
// Fetch deposits and parse coin denoms
|
||||
deposit, found := k.GetDeposit(ctx, addr)
|
||||
if !found {
|
||||
return sdk.ZeroDec(), false, nil
|
||||
return sdk.ZeroDec(), nil
|
||||
}
|
||||
|
||||
// Fetch borrow balances and parse coin denoms
|
||||
borrow, found := k.GetBorrow(ctx, addr)
|
||||
if !found {
|
||||
return sdk.ZeroDec(), false, nil
|
||||
return sdk.ZeroDec(), nil
|
||||
}
|
||||
|
||||
return k.CalculateLtv(ctx, deposit, borrow)
|
||||
@ -313,11 +305,11 @@ func (k Keeper) GetStoreLTV(ctx sdk.Context, addr sdk.AccAddress) (sdk.Dec, bool
|
||||
|
||||
// CalculateLtv calculates the potential LTV given a user's deposits and borrows.
|
||||
// The boolean returned indicates if the LTV should be added to the store's LTV index.
|
||||
func (k Keeper) CalculateLtv(ctx sdk.Context, deposit types.Deposit, borrow types.Borrow) (sdk.Dec, bool, error) {
|
||||
func (k Keeper) CalculateLtv(ctx sdk.Context, deposit types.Deposit, borrow types.Borrow) (sdk.Dec, error) {
|
||||
// Load required liquidation data for every deposit/borrow denom
|
||||
liqMap, err := k.LoadLiquidationData(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return sdk.ZeroDec(), false, nil
|
||||
return sdk.ZeroDec(), nil
|
||||
}
|
||||
|
||||
// Build valuation map to hold deposit coin USD valuations
|
||||
@ -339,11 +331,11 @@ func (k Keeper) CalculateLtv(ctx sdk.Context, deposit types.Deposit, borrow type
|
||||
// User doesn't have any deposits, catch divide by 0 error
|
||||
sumDeposits := depositCoinValues.Sum()
|
||||
if sumDeposits.Equal(sdk.ZeroDec()) {
|
||||
return sdk.ZeroDec(), false, nil
|
||||
return sdk.ZeroDec(), nil
|
||||
}
|
||||
|
||||
// Loan-to-Value ratio
|
||||
return borrowCoinValues.Sum().Quo(sumDeposits), true, nil
|
||||
return borrowCoinValues.Sum().Quo(sumDeposits), nil
|
||||
}
|
||||
|
||||
// LoadLiquidationData returns liquidation data, deposit, borrow
|
||||
|
@ -1358,9 +1358,8 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
||||
suite.Require().True(foundDepositBefore)
|
||||
|
||||
// Attempt to liquidate
|
||||
liquidated, err := suite.keeper.AttemptKeeperLiquidation(liqCtx, tc.args.keeper, tc.args.borrower)
|
||||
err = suite.keeper.AttemptKeeperLiquidation(liqCtx, tc.args.keeper, tc.args.borrower)
|
||||
if tc.errArgs.expectPass {
|
||||
suite.Require().True(liquidated)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Check borrow does not exist after liquidation
|
||||
@ -1383,7 +1382,6 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
||||
suite.Require().True(len(auctions) > 0)
|
||||
suite.Require().Equal(tc.args.expectedAuctions, auctions)
|
||||
} else {
|
||||
suite.Require().False(liquidated)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
// Repay borrowed funds
|
||||
func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) error {
|
||||
// Get current stored LTV based on stored borrows/deposits
|
||||
prevLtv, shouldRemoveIndex, err := k.GetStoreLTV(ctx, sender)
|
||||
prevLtv, err := k.GetStoreLTV(ctx, sender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -43,9 +43,18 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
|
||||
|
||||
// Update user's borrow in store
|
||||
borrow.Amount = borrow.Amount.Sub(payment)
|
||||
k.SetBorrow(ctx, borrow)
|
||||
|
||||
k.UpdateItemInLtvIndex(ctx, prevLtv, shouldRemoveIndex, sender)
|
||||
// Calculate the new Loan-to-Value ratio of Deposit-to-Borrow
|
||||
deposit, foundDeposit := k.GetDeposit(ctx, sender)
|
||||
if !foundDeposit {
|
||||
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
|
||||
k.DecrementBorrowedCoins(ctx, payment)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
// 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 {
|
||||
// Get current stored LTV based on stored borrows/deposits
|
||||
prevLtv, shouldRemoveIndex, err := k.GetStoreLTV(ctx, depositor)
|
||||
prevLtv, err := k.GetStoreLTV(ctx, depositor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -27,18 +27,17 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposedDeposit := types.NewDeposit(deposit.Depositor, deposit.Amount.Sub(amount), types.SupplyInterestFactors{})
|
||||
|
||||
borrow, found := k.GetBorrow(ctx, depositor)
|
||||
if !found {
|
||||
borrow = types.Borrow{}
|
||||
}
|
||||
|
||||
proposedDeposit := types.NewDeposit(deposit.Depositor, deposit.Amount.Sub(amount), types.SupplyInterestFactors{})
|
||||
valid, err := k.IsWithinValidLtvRange(ctx, proposedDeposit, borrow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidWithdrawAmount, "proposed withdraw outside loan-to-value range")
|
||||
}
|
||||
@ -60,9 +59,12 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
}
|
||||
|
||||
deposit.Amount = deposit.Amount.Sub(amount)
|
||||
k.SetDeposit(ctx, deposit)
|
||||
|
||||
k.UpdateItemInLtvIndex(ctx, prevLtv, shouldRemoveIndex, depositor)
|
||||
newLtv, err := k.CalculateLtv(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.UpdateDepositAndLtvIndex(ctx, deposit, newLtv, prevLtv)
|
||||
|
||||
// Update total supplied amount
|
||||
k.DecrementBorrowedCoins(ctx, amount)
|
||||
|
Loading…
Reference in New Issue
Block a user