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

Add cdp liquidations by external keeper #750

Merged
merged 2 commits into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion migrate/v0_13/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func MigrateCDP(oldGenState v0_11cdp.GenesisState) v0_13cdp.GenesisState {

totalPrincipalMap := make(map[string]sdk.Int)
for _, cp := range oldGenState.Params.CollateralParams {
newCollateralParam := v0_13cdp.NewCollateralParam(cp.Denom, cp.Type, cp.LiquidationRatio, cp.DebtLimit, cp.StabilityFee, cp.AuctionSize, cp.LiquidationPenalty, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, cp.ConversionFactor)
newCollateralParam := v0_13cdp.NewCollateralParam(cp.Denom, cp.Type, cp.LiquidationRatio, cp.DebtLimit, cp.StabilityFee, cp.AuctionSize, cp.LiquidationPenalty, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, sdk.MustNewDecFromStr("0.01"), sdk.NewInt(10), cp.ConversionFactor)
newCollateralParams = append(newCollateralParams, newCollateralParam)
newGenesisAccumulationTime := v0_13cdp.NewGenesisAccumulationTime(cp.Type, previousAccumulationTime, sdk.OneDec())
newGenesisAccumulationTimes = append(newGenesisAccumulationTimes, newGenesisAccumulationTime)
Expand Down
5 changes: 3 additions & 2 deletions x/auction/types/auctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,11 @@ func (a CollateralAuction) String() string {
End Time: %s
Max End Time: %s
Max Bid %s
LotReturns %s`,
LotReturns %s
Corresponding Debt %s`,
a.GetID(), a.Initiator, a.Lot,
a.Bidder, a.Bid, a.GetEndTime().String(),
a.MaxEndTime.String(), a.MaxBid, a.LotReturns,
a.MaxEndTime.String(), a.MaxBid, a.LotReturns, a.CorrespondingDebt,
)
}

Expand Down
5 changes: 5 additions & 0 deletions x/cdp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
panic(err)
}

err = k.SynchronizeInterestForRiskyCDPs(ctx, cp.CheckCollateralizationIndexCount, sdk.MaxSortableDec, cp.Type)
if err != nil {
panic(err)
}

err = k.LiquidateCdps(ctx, cp.LiquidationMarketID, cp.Type, cp.LiquidationRatio)
if err != nil && !errors.Is(err, pricefeedtypes.ErrNoValidPrice) {
panic(err)
Expand Down
3 changes: 3 additions & 0 deletions x/cdp/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var (
NewMsgCreateCDP = types.NewMsgCreateCDP
NewMsgDeposit = types.NewMsgDeposit
NewMsgDrawDebt = types.NewMsgDrawDebt
NewMsgLiquidate = types.NewMsgLiquidate
NewMsgRepayDebt = types.NewMsgRepayDebt
NewMsgWithdraw = types.NewMsgWithdraw
NewParams = types.NewParams
Expand Down Expand Up @@ -134,6 +135,7 @@ var (
ErrInvalidPayment = types.ErrInvalidPayment
ErrInvalidWithdrawAmount = types.ErrInvalidWithdrawAmount
ErrLoadingAugmentedCDP = types.ErrLoadingAugmentedCDP
ErrNotLiquidatable = types.ErrNotLiquidatable
ErrPricefeedDown = types.ErrPricefeedDown
GovDenomKey = types.GovDenomKey
InterestFactorPrefix = types.InterestFactorPrefix
Expand Down Expand Up @@ -178,6 +180,7 @@ type (
MsgCreateCDP = types.MsgCreateCDP
MsgDeposit = types.MsgDeposit
MsgDrawDebt = types.MsgDrawDebt
MsgLiquidate = types.MsgLiquidate
MsgRepayDebt = types.MsgRepayDebt
MsgWithdraw = types.MsgWithdraw
Params = types.Params
Expand Down
32 changes: 32 additions & 0 deletions x/cdp/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
GetCmdWithdraw(cdc),
GetCmdDraw(cdc),
GetCmdRepay(cdc),
GetCmdLiquidate(cdc),
)...)

return cdpTxCmd
Expand Down Expand Up @@ -202,3 +203,34 @@ $ %s tx %s repay atom-a 1000usdx --from myKeyName
},
}
}

// GetCmdLiquidate cli command for liquidating a cdp.
func GetCmdLiquidate(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "liquidate [cdp-owner-address] [collateral-type]",
Short: "liquidate a cdp",
Long: strings.TrimSpace(
fmt.Sprintf(`Liquidate a cdp if it is below the required liquidation ratio

Example:
$ %s tx %s liquidate kava1y70y90wzmnf00e63efk2lycgqwepthdmyzsfzm btcb-a --from myKeyName
`, version.ClientName, types.ModuleName)),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
cliCtx := context.NewCLIContext().WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgLiquidate(cliCtx.GetFromAddress(), addr, args[1])
err = msg.ValidateBasic()
if err != nil {
return err
}
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
7 changes: 7 additions & 0 deletions x/cdp/client/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,10 @@ type PostRepayReq struct {
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
Payment sdk.Coin `json:"payment" yaml:"payment"`
}

// PostLiquidateReq defines the properties of cdp liquidation request's body.
type PostLiquidateReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
}
33 changes: 33 additions & 0 deletions x/cdp/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/cdp/{owner}/{collateralType}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{collateralType}/draw", postDrawHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/{collateralType}/repay", postRepayHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/cdp/{owner}/collateralType}/liquidate", postLiquidateHandlerFn(cliCtx)).Methods("POST")
}

func postCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
Expand Down Expand Up @@ -187,3 +188,35 @@ func postRepayHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
}
}

func postLiquidateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var requestBody PostLiquidateReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
return
}

requestBody.BaseReq = requestBody.BaseReq.Sanitize()
if !requestBody.BaseReq.ValidateBasic(w) {
return
}

fromAddr, err := sdk.AccAddressFromBech32(requestBody.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

msg := types.NewMsgLiquidate(
fromAddr,
requestBody.Owner,
requestBody.CollateralType,
)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
}
}
18 changes: 18 additions & 0 deletions x/cdp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func NewHandler(k Keeper) sdk.Handler {
return handleMsgDrawDebt(ctx, k, msg)
case MsgRepayDebt:
return handleMsgRepayDebt(ctx, k, msg)
case MsgLiquidate:
return handleMsgLiquidate(ctx, k, msg)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
}
Expand Down Expand Up @@ -110,3 +112,19 @@ func handleMsgRepayDebt(ctx sdk.Context, k Keeper, msg MsgRepayDebt) (*sdk.Resul
)
return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

func handleMsgLiquidate(ctx sdk.Context, k Keeper, msg MsgLiquidate) (*sdk.Result, error) {
err := k.AttemptKeeperLiquidation(ctx, msg.Keeper, msg.Borrower, msg.CollateralType)
if err != nil {
return nil, err
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Keeper.String()),
),
)
return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}
72 changes: 39 additions & 33 deletions x/cdp/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,19 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
SavingsDistributionFrequency: cdp.DefaultSavingsDistributionFrequency,
CollateralParams: cdp.CollateralParams{
{
Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(1000000000),
Prefix: 0x20,
ConversionFactor: i(6),
SpotMarketID: asset + ":usd",
LiquidationMarketID: asset + ":usd",
Denom: asset,
Type: asset + "-a",
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(1000000000),
Prefix: 0x20,
ConversionFactor: i(6),
SpotMarketID: asset + ":usd",
LiquidationMarketID: asset + ":usd",
KeeperRewardPercentage: d("0.01"),
CheckCollateralizationIndexCount: i(10),
},
},
DebtParam: cdp.DebtParam{
Expand Down Expand Up @@ -121,30 +123,34 @@ func NewCDPGenStateMulti() app.GenesisState {
SavingsDistributionFrequency: cdp.DefaultSavingsDistributionFrequency,
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(7000000000),
Prefix: 0x20,
SpotMarketID: "xrp:usd",
LiquidationMarketID: "xrp:usd",
ConversionFactor: i(6),
Denom: "xrp",
Type: "xrp-a",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(7000000000),
Prefix: 0x20,
SpotMarketID: "xrp:usd",
LiquidationMarketID: "xrp:usd",
KeeperRewardPercentage: d("0.01"),
CheckCollateralizationIndexCount: i(10),
ConversionFactor: i(6),
},
{
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
LiquidationPenalty: d("0.025"),
AuctionSize: i(10000000),
Prefix: 0x21,
SpotMarketID: "btc:usd",
LiquidationMarketID: "btc:usd",
ConversionFactor: i(8),
Denom: "btc",
Type: "btc-a",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
LiquidationPenalty: d("0.025"),
AuctionSize: i(10000000),
Prefix: 0x21,
SpotMarketID: "btc:usd",
LiquidationMarketID: "btc:usd",
KeeperRewardPercentage: d("0.01"),
CheckCollateralizationIndexCount: i(10),
ConversionFactor: i(8),
},
},
DebtParam: cdp.DebtParam{
Expand Down
6 changes: 6 additions & 0 deletions x/cdp/keeper/cdp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"errors"
"fmt"
"testing"
"time"

Expand Down Expand Up @@ -246,8 +247,13 @@ func (suite *CdpTestSuite) TestIterateCdpsByCollateralRatio() {
suite.Equal(1, len(xrpCdps))
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("2.0").Add(sdk.SmallestDec()))
suite.Equal(2, len(xrpCdps))
fmt.Println(xrpCdps[0])
fmt.Println(xrpCdps[1])
xrpCdps = suite.keeper.GetAllCdpsByCollateralTypeAndRatio(suite.ctx, "xrp-a", d("100.0").Add(sdk.SmallestDec()))
suite.Equal(3, len(xrpCdps))
fmt.Println(xrpCdps[0])
fmt.Println(xrpCdps[1])
fmt.Println(xrpCdps[2])
suite.keeper.DeleteCDP(suite.ctx, cdps[0])
suite.keeper.RemoveCdpOwnerIndex(suite.ctx, cdps[0])
cr := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdps[0].Collateral, cdps[0].Type, cdps[0].Principal)
Expand Down
Loading