Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automatically clear blacklisted collateral #1343

Merged
merged 6 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- [1330](https://github.com/umee-network/umee/pull/1330) Implemented MaxSupplyUtilization.
- [1319](https://github.com/umee-network/umee/pull/1319) Implemented MaxSupply.
- [1331](https://github.com/umee-network/umee/pull/1331) Implemented MinCollateralLiquidity.
- [1343](https://github.com/umee-network/umee/pull/1343) RepayBadDebt and Liquidate automatically clear blacklisted collateral.
toteki marked this conversation as resolved.
Show resolved Hide resolved

### Improvements

Expand Down
10 changes: 0 additions & 10 deletions x/leverage/keeper/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,3 @@ func (k Keeper) filterAcceptedCoins(ctx sdk.Context, coins sdk.Coins) sdk.Coins
},
)
}

// filterAcceptedUTokens returns the subset of an sdk.Coins that are accepted, non-blacklisted uTokens
func (k Keeper) filterAcceptedUTokens(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
return k.filterCoins(
coins,
func(c sdk.Coin) bool {
return k.validateAcceptedUToken(ctx, c) == nil
},
)
}
10 changes: 6 additions & 4 deletions x/leverage/keeper/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,13 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error {
addr := types.AddressFromKey(key, prefix)
denom := types.DenomFromKeyWithAddress(key, prefix)

// first check if the borrower has gained collateral since the bad debt was identified
done := k.HasCollateral(ctx, addr)
// TODO #1223: Decollateralize any blacklisted collateral and proceed
// clear blacklisted collateral while checking for any remaining (valid) collateral
done, err := k.clearBlacklistedCollateral(ctx, addr)
if err != nil {
return err
}

// if collateral is still zero, attempt to repay a single address's debt in this denom
// if remaining collateral is zero, attempt to repay this bad debt
if !done {
var err error
done, err = k.RepayBadDebt(ctx, addr, denom)
Expand Down
42 changes: 38 additions & 4 deletions x/leverage/keeper/reserves.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,46 @@ import (
"github.com/umee-network/umee/v3/x/leverage/types"
)

// clearBlacklistedCollateral decollateralizes any blacklisted uTokens
// from a borrower's collateral. It is used during liquidations and before
// repaying bad debts with reserves to make any subsequent checks for
// remaining collateral on the borrower's address more efficient.
// Also returns a boolean indicating whether valid collateral remains.
func (k Keeper) clearBlacklistedCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddress) (bool, error) {
collateral := k.GetBorrowerCollateral(ctx, borrowerAddr)
hasCollateral := false
for _, coin := range collateral {
denom := types.ToTokenDenom(coin.Denom)
token, err := k.GetTokenSettings(ctx, denom)
if err != nil {
return false, err
}
if token.Blacklist {
// Decollateralize any blacklisted uTokens encountered
err := k.decollateralize(ctx, borrowerAddr, borrowerAddr, coin)
if err != nil {
return false, err
}
} else {
// At least one non-blacklisted uToken was found
hasCollateral = true
}
}
// Any remaining collateral is non-blacklisted
return hasCollateral, nil
}

// checkBadDebt detects if a borrower has zero non-blacklisted collateral,
// and marks any remaining borrowed tokens as bad debt.
func (k Keeper) checkBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress) error {
// get remaining collateral uTokens, ignoring blacklisted
remainingCollateral := k.filterAcceptedUTokens(ctx, k.GetBorrowerCollateral(ctx, borrowerAddr))
// clear blacklisted collateral while checking for any remaining (valid) collateral
hasCollateral, err := k.clearBlacklistedCollateral(ctx, borrowerAddr)
if err != nil {
return err
}

// detect bad debt if collateral is completely exhausted
if remainingCollateral.IsZero() {
// mark bad debt if collateral is completely exhausted
if !hasCollateral {
for _, coin := range k.GetBorrowerBorrows(ctx, borrowerAddr) {
// set a bad debt flag for each borrowed denom
if err := k.setBadDebtAddress(ctx, borrowerAddr, coin.Denom, true); err != nil {
Expand All @@ -27,6 +59,8 @@ func (k Keeper) checkBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress) error

// RepayBadDebt uses reserves to repay borrower's debts of a given denom.
// It returns a boolean representing whether full repayment was achieved.
// This function assumes the borrower has already been verified to have
// no collateral remaining.
func (k Keeper) RepayBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) (bool, error) {
borrowed := k.GetBorrow(ctx, borrowerAddr, denom)
borrower := borrowerAddr.String()
Expand Down
18 changes: 0 additions & 18 deletions x/leverage/keeper/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,6 @@ func (k Keeper) validateAcceptedDenom(ctx sdk.Context, denom string) error {
return token.AssertNotBlacklisted()
}

// validateAcceptedUTokenDenom validates an sdk.Coin and ensures it is a uToken
// associated with a registered Token with Blacklisted == false
func (k Keeper) validateAcceptedUTokenDenom(ctx sdk.Context, udenom string) error {
if !types.HasUTokenPrefix(udenom) {
return types.ErrNotUToken.Wrap(udenom)
}
return k.validateAcceptedDenom(ctx, types.ToTokenDenom(udenom))
}

// validateAcceptedAsset validates an sdk.Coin and ensures it is a registered Token
// with Blacklisted == false
func (k Keeper) validateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error {
Expand All @@ -37,15 +28,6 @@ func (k Keeper) validateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error {
return k.validateAcceptedDenom(ctx, coin.Denom)
}

// validateAcceptedUToken validates an sdk.Coin and ensures it is a uToken
// associated with a registered Token with Blacklisted == false
func (k Keeper) validateAcceptedUToken(ctx sdk.Context, coin sdk.Coin) error {
if err := coin.Validate(); err != nil {
return err
}
return k.validateAcceptedUTokenDenom(ctx, coin.Denom)
}

// validateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply
func (k Keeper) validateSupply(ctx sdk.Context, coin sdk.Coin) error {
if err := validateBaseToken(coin); err != nil {
Expand Down