From 99e002738141077a691103774b5872cde6985e9f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 6 Sep 2022 23:27:16 -0700 Subject: [PATCH 1/2] feat: automatically clear blacklisted collateral --- CHANGELOG.md | 1 + x/leverage/keeper/filter.go | 10 --------- x/leverage/keeper/iter.go | 10 +++++---- x/leverage/keeper/reserves.go | 42 +++++++++++++++++++++++++++++++---- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aac3bdbccb..402767ad9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,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. +- [13XX](https://github.com/umee-network/umee/pull/13XX) RepayBadDebt and Liquidate automatically clear blacklisted collateral. ### Improvements diff --git a/x/leverage/keeper/filter.go b/x/leverage/keeper/filter.go index 0b1a276c41..864c958273 100644 --- a/x/leverage/keeper/filter.go +++ b/x/leverage/keeper/filter.go @@ -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 - }, - ) -} diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 8e966b7b29..580924feb7 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -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) diff --git a/x/leverage/keeper/reserves.go b/x/leverage/keeper/reserves.go index d5e7c347c2..9b9eeceea9 100644 --- a/x/leverage/keeper/reserves.go +++ b/x/leverage/keeper/reserves.go @@ -47,14 +47,46 @@ func (k Keeper) setReserveAmount(ctx sdk.Context, coin sdk.Coin) error { return nil } +// 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 { @@ -68,6 +100,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() From 4abcef5d44ea56dd0cbbb06617419c4460358351 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 6 Sep 2022 23:29:17 -0700 Subject: [PATCH 2/2] cl++ --- CHANGELOG.md | 2 +- x/leverage/keeper/validate.go | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 402767ad9f..bcf6d6ae18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,7 +92,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. -- [13XX](https://github.com/umee-network/umee/pull/13XX) RepayBadDebt and Liquidate automatically clear blacklisted collateral. +- [1343](https://github.com/umee-network/umee/pull/1343) RepayBadDebt and Liquidate automatically clear blacklisted collateral. ### Improvements diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index 59e5eaedb3..c70e766e5f 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -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 { @@ -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 := coin.Validate(); err != nil {