Skip to content

Commit

Permalink
fix: SweepBadDebts first checks and aborts on nonzero collateral (#541)
Browse files Browse the repository at this point in the history
* fix: SweepBadDebts first checks and aborts on nonzero collateral

* suggestion

Co-authored-by: Aleksandr Bezobchuk <[email protected]>
  • Loading branch information
toteki and alexanderbez authored Feb 11, 2022
1 parent 1424e22 commit a0f0a23
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 21 deletions.
35 changes: 28 additions & 7 deletions x/leverage/keeper/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,21 @@ func (k Keeper) GetBorrowerCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddre
return totalCollateral
}

// HasCollateral returns true if a borrower has any collateral.
func (k Keeper) HasCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddress) bool {
iter := sdk.KVStorePrefixIterator(
ctx.KVStore(k.storeKey),
types.KeyPrefixCollateralAmount,
)
defer iter.Close()

for ; iter.Valid(); iter.Next() {
// Stored collateral amounts are never zero, so this is enough
return true
}
return false
}

// GetEligibleLiquidationTargets returns a list of borrower addresses eligible for liquidation.
func (k Keeper) GetEligibleLiquidationTargets(ctx sdk.Context) ([]sdk.AccAddress, error) {
prefix := types.KeyPrefixAdjustedBorrow
Expand Down Expand Up @@ -211,14 +226,20 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error {
addr := types.AddressFromKey(key, prefix)
denom := types.DenomFromKeyWithAddress(key, prefix)

// attempt to repay a single address's debt in this denom
fullyRepaid, err := k.RepayBadDebt(ctx, addr, denom)
if err != nil {
return err
// first check if the borrower has gained collateral since the bad debt was identified
done := k.HasCollateral(ctx, addr)

// if collateral is still zero, attempt to repay a single address's debt in this denom
if !done {
var err error
done, err = k.RepayBadDebt(ctx, addr, denom)
if err != nil {
return err
}
}
if fullyRepaid {
// If the bad debt of this denom at the given address was fully repaid,
// clear the address|denom pair from the bad debt address list

// if collateral found or debt fully repaid, clear the bad debt entry for this address|denom
if done {
if err := k.setBadDebtAddress(ctx, addr, denom, false); err != nil {
return err
}
Expand Down
29 changes: 15 additions & 14 deletions x/leverage/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,21 +655,22 @@ func (s *IntegrationTestSuite) TestLiqudateBorrow_Valid() {
}

func (s *IntegrationTestSuite) TestRepayBadDebt() {
_, bumAddr := s.initBorrowScenario()
// Creating a lender so module account has some uumee
_ = s.setupAccount(umeeDenom, 200000000, 200000000, 0, false) // 200 umee

// The "bum" user from the init scenario is being used because it
// has no assets or collateral.
// Using an address with no assets
addr := s.setupAccount(umeeDenom, 0, 0, 0, false)

// Create an uncollateralized debt position
badDebt := sdk.NewInt64Coin(umeeapp.BondDenom, 100000000) // 100 umee
err := s.tk.SetBorrow(s.ctx, bumAddr, badDebt)
badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee
err := s.tk.SetBorrow(s.ctx, addr, badDebt)
s.Require().NoError(err)

// Manually mark the bad debt for repayment
s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, bumAddr, umeeapp.BondDenom, true))
s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr, umeeDenom, true))

// Manually set reserves to 60 umee
reserve := sdk.NewInt64Coin(umeeapp.BondDenom, 60000000)
reserve := sdk.NewInt64Coin(umeeDenom, 60000000)
err = s.tk.SetReserveAmount(s.ctx, reserve)
s.Require().NoError(err)

Expand All @@ -678,15 +679,15 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() {
s.Require().NoError(err)

// Confirm that a debt of 40 umee remains
remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, bumAddr, umeeapp.BondDenom)
s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 40000000), remainingDebt)
remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom)
s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt)

// Confirm that reserves are exhausted
remainingReserve := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom)
remainingReserve := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom)
s.Require().Equal(sdk.ZeroInt(), remainingReserve)

// Manually set reserves to 70 umee
reserve = sdk.NewInt64Coin(umeeapp.BondDenom, 70000000)
reserve = sdk.NewInt64Coin(umeeDenom, 70000000)
err = s.tk.SetReserveAmount(s.ctx, reserve)
s.Require().NoError(err)

Expand All @@ -695,11 +696,11 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() {
s.Require().NoError(err)

// Confirm that the debt is eliminated
remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, bumAddr, umeeapp.BondDenom)
s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 0), remainingDebt)
remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom)
s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt)

// Confirm that reserves are now at 30 umee
remainingReserve = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom)
remainingReserve = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom)
s.Require().Equal(sdk.NewInt(30000000), remainingReserve)

// Sweep all bad debts - but there are none
Expand Down

0 comments on commit a0f0a23

Please sign in to comment.