diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index c655644783..2b923d80db 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -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 @@ -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 } diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index a5466ee92b..251b599278 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -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) @@ -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) @@ -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