Fix CDP keeper liquidation collateral ratio check (#1488)

* add test for exact collateral ratio; fix bug that allows cdps created at
the limit to be liquidated by a keeper; update spec

* touch up spec to be more clear

* adjust test name to better reflect what we are testing
This commit is contained in:
Nick DeLuca 2023-03-07 20:19:29 -07:00 committed by GitHub
parent 37b1b5fa5e
commit ba73f69688
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 7 deletions

View File

@ -124,7 +124,7 @@ func (k Keeper) ValidateLiquidation(ctx sdk.Context, collateral sdk.Coin, collat
return err return err
} }
liquidationRatio := k.getLiquidationRatio(ctx, collateralType) liquidationRatio := k.getLiquidationRatio(ctx, collateralType)
if collateralizationRatio.GT(liquidationRatio) { if collateralizationRatio.GTE(liquidationRatio) {
return sdkerrors.Wrapf(types.ErrNotLiquidatable, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral.Denom, collateralizationRatio, liquidationRatio) return sdkerrors.Wrapf(types.ErrNotLiquidatable, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral.Denom, collateralizationRatio, liquidationRatio)
} }
return nil return nil

View File

@ -2,6 +2,7 @@ package keeper_test
import ( import (
"errors" "errors"
"fmt"
"math/rand" "math/rand"
"strings" "strings"
"testing" "testing"
@ -364,16 +365,38 @@ func (suite *SeizeTestSuite) TestKeeperLiquidation() {
"collateral ratio not below liquidation ratio", "collateral ratio not below liquidation ratio",
}, },
}, },
{
"invalid - collateralization ratio equal to liquidation ratio",
args{
ctype: "xrp-a",
blockTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
initialPrice: d("1.00"), // we are allowed to create a cdp with an exact ratio
finalPrice: d("1.00"),
finalTwapPrice: d("1.00"), // and it should not be able to be liquidated
collateral: c("xrp", 100000000),
principal: c("usdx", 50000000),
expectedKeeperCoins: cs(),
expectedAuctions: []auctiontypes.Auction{},
},
errArgs{
false,
"collateral ratio not below liquidation ratio",
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
suite.SetupTest() suite.SetupTest()
spotMarket := fmt.Sprintf("%s:usd", tc.args.collateral.Denom)
liquidationMarket := fmt.Sprintf("%s:30", spotMarket)
// setup pricefeed // setup pricefeed
pk := suite.app.GetPriceFeedKeeper() pk := suite.app.GetPriceFeedKeeper()
_, err := pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.initialPrice, suite.ctx.BlockTime().Add(time.Hour*24)) _, err := pk.SetPrice(suite.ctx, sdk.AccAddress{}, spotMarket, tc.args.initialPrice, suite.ctx.BlockTime().Add(time.Hour*24))
suite.Require().NoError(err) suite.Require().NoError(err)
err = pk.SetCurrentPrices(suite.ctx, "btc:usd") err = pk.SetCurrentPrices(suite.ctx, spotMarket)
suite.Require().NoError(err) suite.Require().NoError(err)
// setup cdp state // setup cdp state
@ -384,15 +407,15 @@ func (suite *SeizeTestSuite) TestKeeperLiquidation() {
// update pricefeed // update pricefeed
// spot market // spot market
_, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.finalPrice, suite.ctx.BlockTime().Add(time.Hour*24)) _, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, spotMarket, tc.args.finalPrice, suite.ctx.BlockTime().Add(time.Hour*24))
suite.Require().NoError(err) suite.Require().NoError(err)
// liquidate market // liquidate market
_, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd:30", tc.args.finalTwapPrice, suite.ctx.BlockTime().Add(time.Hour*24)) _, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, liquidationMarket, tc.args.finalTwapPrice, suite.ctx.BlockTime().Add(time.Hour*24))
suite.Require().NoError(err) suite.Require().NoError(err)
err = pk.SetCurrentPrices(suite.ctx, "btc:usd") err = pk.SetCurrentPrices(suite.ctx, spotMarket)
suite.Require().NoError(err) suite.Require().NoError(err)
err = pk.SetCurrentPrices(suite.ctx, "btc:usd:30") err = pk.SetCurrentPrices(suite.ctx, liquidationMarket)
suite.Require().NoError(err) suite.Require().NoError(err)
_, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype) _, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype)

View File

@ -102,6 +102,8 @@ State Changes:
Liquidate enables Keepers to liquidate a Borrower's CDP. If the CDP is below its Loan-to-Value obligations, the CDP's deposits are seized: a small percentage of the seized funds are sent to the Keeper with the rest auctioned off to recover the CDP's outstanding borrowed amount. Any deposited funds leftover that weren't needed to cover the Borrower's debts are returned to the Borrower. Liquidate enables Keepers to liquidate a Borrower's CDP. If the CDP is below its Loan-to-Value obligations, the CDP's deposits are seized: a small percentage of the seized funds are sent to the Keeper with the rest auctioned off to recover the CDP's outstanding borrowed amount. Any deposited funds leftover that weren't needed to cover the Borrower's debts are returned to the Borrower.
Note: In kava v0.21.x and below, CDP's that have a collateral ratio exactly equal to the liquidation ratio can be liquidated through this method.
```go ```go
// MsgLiquidate attempts to liquidate a borrower's cdp // MsgLiquidate attempts to liquidate a borrower's cdp
type MsgLiquidate struct { type MsgLiquidate struct {