From c37e1f956c0bd7cde911bb2996994c98cd4b1b9e Mon Sep 17 00:00:00 2001 From: karzak Date: Thu, 24 Dec 2020 14:52:35 -0700 Subject: [PATCH 1/2] feat: split liquidations between external keepers and automated begin blocker --- migrate/v0_13/migrate.go | 2 +- x/auction/types/auctions.go | 5 +- x/cdp/abci.go | 5 + x/cdp/alias.go | 3 + x/cdp/client/cli/tx.go | 32 ++ x/cdp/client/rest/rest.go | 7 + x/cdp/client/rest/tx.go | 33 ++ x/cdp/handler.go | 18 + x/cdp/integration_test.go | 72 ++-- x/cdp/keeper/cdp_test.go | 6 + x/cdp/keeper/integration_test.go | 168 +++++----- x/cdp/keeper/interest.go | 15 +- x/cdp/keeper/interest_test.go | 88 +++++ x/cdp/keeper/keeper.go | 15 + x/cdp/keeper/seize.go | 64 ++++ x/cdp/keeper/seize_test.go | 253 ++++++++++++++ x/cdp/legacy/v0_13/types.go | 73 ++-- x/cdp/types/errors.go | 2 + x/cdp/types/msg.go | 56 ++++ x/cdp/types/params.go | 64 ++-- x/cdp/types/params_test.go | 550 +++++++++++++++++-------------- 21 files changed, 1114 insertions(+), 417 deletions(-) diff --git a/migrate/v0_13/migrate.go b/migrate/v0_13/migrate.go index 2f7918c496..d8fc8732dc 100644 --- a/migrate/v0_13/migrate.go +++ b/migrate/v0_13/migrate.go @@ -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) diff --git a/x/auction/types/auctions.go b/x/auction/types/auctions.go index 95ec109592..860a4a986e 100644 --- a/x/auction/types/auctions.go +++ b/x/auction/types/auctions.go @@ -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, ) } diff --git a/x/cdp/abci.go b/x/cdp/abci.go index 5c2de1b12f..298ace2840 100644 --- a/x/cdp/abci.go +++ b/x/cdp/abci.go @@ -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) diff --git a/x/cdp/alias.go b/x/cdp/alias.go index dc08381d34..192b067fc8 100644 --- a/x/cdp/alias.go +++ b/x/cdp/alias.go @@ -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 @@ -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 @@ -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 diff --git a/x/cdp/client/cli/tx.go b/x/cdp/client/cli/tx.go index 0ce5c82d95..e5a5311c88 100644 --- a/x/cdp/client/cli/tx.go +++ b/x/cdp/client/cli/tx.go @@ -31,6 +31,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { GetCmdWithdraw(cdc), GetCmdDraw(cdc), GetCmdRepay(cdc), + GetCmdLiquidate(cdc), )...) return cdpTxCmd @@ -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}) + }, + } +} diff --git a/x/cdp/client/rest/rest.go b/x/cdp/client/rest/rest.go index 068c4cdb28..4584b7bea8 100644 --- a/x/cdp/client/rest/rest.go +++ b/x/cdp/client/rest/rest.go @@ -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"` +} diff --git a/x/cdp/client/rest/tx.go b/x/cdp/client/rest/tx.go index 154d9b3920..2c66a5882e 100644 --- a/x/cdp/client/rest/tx.go +++ b/x/cdp/client/rest/tx.go @@ -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 { @@ -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}) + } +} diff --git a/x/cdp/handler.go b/x/cdp/handler.go index 5a7e04ec8f..d1764e70d8 100644 --- a/x/cdp/handler.go +++ b/x/cdp/handler.go @@ -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) } @@ -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 +} diff --git a/x/cdp/integration_test.go b/x/cdp/integration_test.go index 766640d95f..1bd83ea7c8 100644 --- a/x/cdp/integration_test.go +++ b/x/cdp/integration_test.go @@ -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{ @@ -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{ diff --git a/x/cdp/keeper/cdp_test.go b/x/cdp/keeper/cdp_test.go index 8ab3753459..7f70cacbac 100644 --- a/x/cdp/keeper/cdp_test.go +++ b/x/cdp/keeper/cdp_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "errors" + "fmt" "testing" "time" @@ -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) diff --git a/x/cdp/keeper/integration_test.go b/x/cdp/keeper/integration_test.go index 12decb7870..e2a65920c1 100644 --- a/x/cdp/keeper/integration_test.go +++ b/x/cdp/keeper/integration_test.go @@ -48,17 +48,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(100), - 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(100), + Prefix: 0x20, + SpotMarketID: asset + ":usd", + LiquidationMarketID: asset + ":usd", + KeeperRewardPercentage: d("0.01"), + CheckCollateralizationIndexCount: i(10), + ConversionFactor: i(6), }, }, DebtParam: cdp.DebtParam{ @@ -134,56 +136,64 @@ 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), }, { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr - LiquidationPenalty: d("0.05"), - AuctionSize: i(50000000000), - Prefix: 0x22, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: i(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr + LiquidationPenalty: d("0.05"), + AuctionSize: i(50000000000), + Prefix: 0x22, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: d("0.01"), + CheckCollateralizationIndexCount: i(10), + ConversionFactor: i(8), }, { - Denom: "busd", - Type: "busd-a", - LiquidationRatio: d("1.01"), - DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), - StabilityFee: sdk.OneDec(), // %0 apr - LiquidationPenalty: d("0.05"), - AuctionSize: i(10000000000), - Prefix: 0x23, - SpotMarketID: "busd:usd", - LiquidationMarketID: "busd:usd", - ConversionFactor: i(8), + Denom: "busd", + Type: "busd-a", + LiquidationRatio: d("1.01"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.OneDec(), // %0 apr + LiquidationPenalty: d("0.05"), + AuctionSize: i(10000000000), + Prefix: 0x23, + SpotMarketID: "busd:usd", + LiquidationMarketID: "busd:usd", + KeeperRewardPercentage: d("0.01"), + CheckCollateralizationIndexCount: i(10), + ConversionFactor: i(8), }, }, DebtParam: cdp.DebtParam{ @@ -226,30 +236,34 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState { SavingsDistributionFrequency: cdp.DefaultSavingsDistributionFrequency, CollateralParams: cdp.CollateralParams{ { - Denom: "xrp", - Type: "xrp-a", - LiquidationRatio: sdk.MustNewDecFromStr("2.0"), - DebtLimit: sdk.NewInt64Coin("usdx", 50000000000000), - 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", 50000000000000), + 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", 50000000000000), - 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", 50000000000000), + 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{ diff --git a/x/cdp/keeper/interest.go b/x/cdp/keeper/interest.go index 2229477849..9cd9f8f9b3 100644 --- a/x/cdp/keeper/interest.go +++ b/x/cdp/keeper/interest.go @@ -116,7 +116,6 @@ func CalculateInterestFactor(perSecondInterestRate sdk.Dec, secondsElapsed sdk.I // SynchronizeInterest updates the input cdp object to reflect the current accumulated interest, updates the cdp state in the store, // and returns the updated cdp object func (k Keeper) SynchronizeInterest(ctx sdk.Context, cdp types.CDP) types.CDP { - previousCollateralRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal()) globalInterestFactor, found := k.GetInterestFactor(ctx, cdp.Type) if !found { k.SetInterestFactor(ctx, cdp.Type, sdk.OneDec()) @@ -128,7 +127,7 @@ func (k Keeper) SynchronizeInterest(ctx sdk.Context, cdp types.CDP) types.CDP { accumulatedInterest := k.CalculateNewInterest(ctx, cdp) if accumulatedInterest.IsZero() { - // this could happen if apy is zero are if the total fees for all cdps round to zero + // accumulated interest is zero if apy is zero or are if the total fees for all cdps round to zero prevAccrualTime, found := k.GetPreviousAccrualTime(ctx, cdp.Type) if !found { @@ -147,8 +146,7 @@ func (k Keeper) SynchronizeInterest(ctx sdk.Context, cdp types.CDP) types.CDP { cdp.FeesUpdated = ctx.BlockTime() cdp.InterestFactor = globalInterestFactor collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal()) - k.RemoveCdpCollateralRatioIndex(ctx, cdp.Type, cdp.ID, previousCollateralRatio) - k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) + k.UpdateCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) return cdp } @@ -165,3 +163,12 @@ func (k Keeper) CalculateNewInterest(ctx sdk.Context, cdp types.CDP) sdk.Coin { accumulatedInterest := cdp.GetTotalPrincipal().Amount.ToDec().Mul(cdpInterestFactor).RoundInt().Sub(cdp.GetTotalPrincipal().Amount) return sdk.NewCoin(cdp.AccumulatedFees.Denom, accumulatedInterest) } + +// SynchronizeInterestForRiskyCDPs synchronizes the interest for the slice of cdps with the lowest collateral:debt ratio +func (k Keeper) SynchronizeInterestForRiskyCDPs(ctx sdk.Context, slice sdk.Int, targetRatio sdk.Dec, collateralType string) error { + cdps := k.GetSliceOfCDPsByRatioAndType(ctx, slice, targetRatio, collateralType) + for _, cdp := range cdps { + k.SynchronizeInterest(ctx, cdp) + } + return nil +} diff --git a/x/cdp/keeper/interest_test.go b/x/cdp/keeper/interest_test.go index ec3166e1e1..c8db034634 100644 --- a/x/cdp/keeper/interest_test.go +++ b/x/cdp/keeper/interest_test.go @@ -670,6 +670,94 @@ func (suite *InterestTestSuite) TestCalculateCDPInterest() { } } +func (suite *InterestTestSuite) TestSyncInterestForRiskyCDPs() { + type args struct { + ctype string + numberCdps int + slice int + initialCollateral sdk.Coin + minPrincipal sdk.Coin + principalIncrement sdk.Coin + initialTime time.Time + timeElapsed int + expectedCDPs int + } + + type test struct { + name string + args args + } + + oneYearInSeconds := 31536000 + testCases := []test{ + + { + "1 year", + args{ + ctype: "bnb-a", + numberCdps: 20, + slice: 10, + initialCollateral: c("bnb", 100000000000), + minPrincipal: c("usdx", 100000000), + principalIncrement: c("usdx", 10000000), + initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), + timeElapsed: oneYearInSeconds, + expectedCDPs: 10, + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime) + // setup account state + _, addrs := app.GeneratePrivKeyAddressPairs(tc.args.numberCdps) + ak := suite.app.GetAccountKeeper() + sk := suite.app.GetSupplyKeeper() + for _, addr := range addrs { + acc := ak.NewAccountWithAddress(suite.ctx, addr) + ak.SetAccount(suite.ctx, acc) + err := sk.MintCoins(suite.ctx, types.ModuleName, cs(tc.args.initialCollateral)) + suite.Require().NoError(err) + err = sk.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, addr, cs(tc.args.initialCollateral)) + suite.Require().NoError(err) + } + // setup pricefeed + pk := suite.app.GetPriceFeedKeeper() + pk.SetPrice(suite.ctx, sdk.AccAddress{}, "bnb:usd", d("20.0"), tc.args.initialTime.Add(time.Duration(int(time.Second)*tc.args.timeElapsed))) + + // setup cdp state + suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, suite.ctx.BlockTime()) + suite.keeper.SetInterestFactor(suite.ctx, tc.args.ctype, sdk.OneDec()) + for j, addr := range addrs { + initialPrincipal := tc.args.minPrincipal.Add(c("usdx", int64(j)*tc.args.principalIncrement.Amount.Int64())) + err := suite.keeper.AddCdp(suite.ctx, addr, tc.args.initialCollateral, initialPrincipal, tc.args.ctype) + suite.Require().NoError(err) + } + + updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed)) + suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime) + err := suite.keeper.AccumulateInterest(suite.ctx, tc.args.ctype) + suite.Require().NoError(err) + + err = suite.keeper.SynchronizeInterestForRiskyCDPs(suite.ctx, i(int64(tc.args.slice)), sdk.MaxSortableDec, tc.args.ctype) + suite.Require().NoError(err) + + cdpsUpdatedCount := 0 + + for _, addr := range addrs { + cdp, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, addr, tc.args.ctype) + suite.Require().True(found) + if cdp.FeesUpdated.Equal(suite.ctx.BlockTime()) { + cdpsUpdatedCount += 1 + } + } + suite.Require().Equal(tc.args.expectedCDPs, cdpsUpdatedCount) + }) + } +} + func TestInterestTestSuite(t *testing.T) { suite.Run(t, new(InterestTestSuite)) } diff --git a/x/cdp/keeper/keeper.go b/x/cdp/keeper/keeper.go index b143713815..99369b3cde 100644 --- a/x/cdp/keeper/keeper.go +++ b/x/cdp/keeper/keeper.go @@ -112,6 +112,21 @@ func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, collateralType str } } +// GetSliceOfCDPsByRatioAndType returns a slice of cdps of size equal to the input cutoffCount +// sorted by target ratio in ascending order (ie, the lowest collateral:debt ratio cdps are returned first) +func (k Keeper) GetSliceOfCDPsByRatioAndType(ctx sdk.Context, cutoffCount sdk.Int, targetRatio sdk.Dec, collateralType string) (cdps types.CDPs) { + count := sdk.ZeroInt() + k.IterateCdpsByCollateralRatio(ctx, collateralType, targetRatio, func(cdp types.CDP) bool { + cdps = append(cdps, cdp) + count = count.Add(sdk.OneInt()) + if count.GTE(cutoffCount) { + return true + } + return false + }) + return cdps +} + // SetSavingsRateDistributed sets the SavingsRateDistributed in the store func (k Keeper) SetSavingsRateDistributed(ctx sdk.Context, totalDistributed sdk.Int) { store := prefix.NewStore(ctx.KVStore(k.key), types.SavingsRateDistributedKey) diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index c6bb77d7d0..1bb05107e2 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -4,10 +4,32 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/cdp/types" ) +// AttemptKeeperLiquidation liquidates the cdp with the input colalteral type and owner if it is below the required collateralization ratio +// if the cdp is liquidated, the keeper that sent the transaction is rewarded a percentage of the collateral according to that collateral types' +// keeper reward percentage. +func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper, owner sdk.AccAddress, collateralType string) error { + cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, owner, collateralType) + if !found { + return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, collateralType) + } + cdp = k.SynchronizeInterest(ctx, cdp) + + err := k.ValidateLiquidation(ctx, cdp.Collateral, cdp.Type, cdp.Principal, cdp.AccumulatedFees) + if err != nil { + return err + } + cdp, err = k.payoutKeeperLiquidationReward(ctx, keeper, cdp) + if err != nil { + return err + } + return k.SeizeCollateral(ctx, cdp) +} + // SeizeCollateral liquidates the collateral in the input cdp. // the following operations are performed: // 1. Collateral for all deposits is sent from the cdp module to the liquidator module account @@ -93,7 +115,49 @@ func (k Keeper) ApplyLiquidationPenalty(ctx sdk.Context, collateralType string, return sdk.NewDecFromInt(debt).Mul(penalty).RoundInt() } +// ValidateLiquidation validate that adding the input principal puts the cdp below the liquidation ratio +func (k Keeper) ValidateLiquidation(ctx sdk.Context, collateral sdk.Coin, collateralType string, principal sdk.Coin, fees sdk.Coin) error { + collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, collateral, collateralType, principal, fees, spot) + if err != nil { + return err + } + liquidationRatio := k.getLiquidationRatio(ctx, collateralType) + if collateralizationRatio.GT(liquidationRatio) { + return sdkerrors.Wrapf(types.ErrNotLiquidatable, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral.Denom, collateralizationRatio, liquidationRatio) + } + return nil +} + func (k Keeper) getModAccountDebt(ctx sdk.Context, accountName string) sdk.Int { macc := k.supplyKeeper.GetModuleAccount(ctx, accountName) return macc.GetCoins().AmountOf(k.GetDebtDenom(ctx)) } + +func (k Keeper) payoutKeeperLiquidationReward(ctx sdk.Context, keeper sdk.AccAddress, cdp types.CDP) (types.CDP, error) { + collateralParam, found := k.GetCollateral(ctx, cdp.Type) + if !found { + return types.CDP{}, sdkerrors.Wrapf(types.ErrInvalidCollateral, "%s", cdp.Type) + } + reward := cdp.Collateral.Amount.ToDec().Mul(collateralParam.KeeperRewardPercentage).RoundInt() + rewardCoin := sdk.NewCoin(cdp.Collateral.Denom, reward) + err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, keeper, sdk.NewCoins(rewardCoin)) + if err != nil { + return types.CDP{}, err + } + cdp.Collateral = cdp.Collateral.Sub(rewardCoin) + ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Type, cdp.GetTotalPrincipal()) + err = k.UpdateCdpAndCollateralRatioIndex(ctx, cdp, ratio) + if err != nil { + return types.CDP{}, err + } + + deposits := k.GetDeposits(ctx, cdp.ID) + for _, dep := range deposits { + if dep.Amount.IsGTE(rewardCoin) { + dep.Amount = dep.Amount.Sub(rewardCoin) + k.SetDeposit(ctx, dep) + break + } + } + return cdp, nil +} diff --git a/x/cdp/keeper/seize_test.go b/x/cdp/keeper/seize_test.go index 64adc19bc3..12cf8a4a0c 100644 --- a/x/cdp/keeper/seize_test.go +++ b/x/cdp/keeper/seize_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "errors" "math/rand" + "strings" "testing" "time" @@ -205,6 +206,258 @@ func (suite *SeizeTestSuite) TestApplyLiquidationPenalty() { suite.Panics(func() { suite.keeper.ApplyLiquidationPenalty(suite.ctx, "lol-a", i(1000)) }) } +func (suite *SeizeTestSuite) TestKeeperLiquidation() { + type args struct { + ctype string + blockTime time.Time + initialPrice sdk.Dec + finalPrice sdk.Dec + collateral sdk.Coin + principal sdk.Coin + expectedKeeperCoins sdk.Coins // additional coins (if any) the borrower address should have after successfully liquidating position + expectedAuctions auction.Auctions // the auctions we should expect to find have been started + } + + type errArgs struct { + expectLiquidate bool + contains string + } + + type test struct { + name string + args args + errArgs errArgs + } + + // Set up auction constants + layout := "2006-01-02T15:04:05.000Z" + endTimeStr := "9000-01-01T00:00:00.000Z" + endTime, _ := time.Parse(layout, endTimeStr) + addr, _ := sdk.AccAddressFromBech32("kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2") + + testCases := []test{ + { + "valid liquidation", + args{ + "btc-a", + time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), + d("20000.00"), + d("19000.0"), + c("btc", 10000000), + c("usdx", 1333330000), + cs(c("btc", 100100000), c("xrp", 10000000000)), + auction.Auctions{ + auction.CollateralAuction{ + BaseAuction: auction.BaseAuction{ + ID: 1, + Initiator: "liquidator", + Lot: c("btc", 9900000), + Bidder: nil, + Bid: c("usdx", 0), + HasReceivedBids: false, + EndTime: endTime, + MaxEndTime: endTime, + }, + CorrespondingDebt: c("debt", 1333330000), + MaxBid: c("usdx", 1366663250), + LotReturns: auction.WeightedAddresses{[]sdk.AccAddress{addr}, []sdk.Int{sdk.NewInt(9900000)}}, + }, + }, + }, + errArgs{ + true, + "", + }, + }, + { + "invalid - not below collateralization ratio", + args{ + "btc-a", + time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), + d("20000.00"), + d("21000.0"), + c("btc", 10000000), + c("usdx", 1333330000), + cs(), + auction.Auctions{}, + }, + errArgs{ + false, + "collateral ratio not below liquidation ratio", + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + // setup pricefeed + pk := suite.app.GetPriceFeedKeeper() + _, err := pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.initialPrice, suite.ctx.BlockTime().Add(time.Hour*24)) + suite.Require().NoError(err) + err = pk.SetCurrentPrices(suite.ctx, "btc:usd") + suite.Require().NoError(err) + + // setup cdp state + suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, suite.ctx.BlockTime()) + suite.keeper.SetInterestFactor(suite.ctx, tc.args.ctype, sdk.OneDec()) + err = suite.keeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.collateral, tc.args.principal, tc.args.ctype) + suite.Require().NoError(err) + + // update pricefeed + _, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.finalPrice, suite.ctx.BlockTime().Add(time.Hour*24)) + suite.Require().NoError(err) + err = pk.SetCurrentPrices(suite.ctx, "btc:usd") + suite.Require().NoError(err) + + _, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype) + suite.Require().True(found) + + err = suite.keeper.AttemptKeeperLiquidation(suite.ctx, suite.addrs[1], suite.addrs[0], tc.args.ctype) + + if tc.errArgs.expectLiquidate { + suite.Require().NoError(err) + + _, found = suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype) + suite.Require().False(found) + + ak := suite.app.GetAuctionKeeper() + auctions := ak.GetAllAuctions(suite.ctx) + suite.Require().Equal(tc.args.expectedAuctions, auctions) + + ack := suite.app.GetAccountKeeper() + keeper := ack.GetAccount(suite.ctx, suite.addrs[1]) + suite.Require().Equal(tc.args.expectedKeeperCoins, keeper.GetCoins()) + } else { + suite.Require().Error(err) + suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains)) + } + }) + } +} + +func (suite *SeizeTestSuite) TestBeginBlockerLiquidation() { + type args struct { + ctype string + blockTime time.Time + initialPrice sdk.Dec + finalPrice sdk.Dec + collaterals sdk.Coins + principals sdk.Coins + expectedAuctions auction.Auctions // the auctions we should expect to find have been started + } + type errArgs struct { + expectLiquidate bool + contains string + } + type test struct { + name string + args args + errArgs errArgs + } + // Set up auction constants + layout := "2006-01-02T15:04:05.000Z" + endTimeStr := "9000-01-01T00:00:00.000Z" + endTime, _ := time.Parse(layout, endTimeStr) + addr, _ := sdk.AccAddressFromBech32("kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2") + + testCases := []test{ + { + "1 liquidation", + args{ + "btc-a", + time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), + d("20000.00"), + d("10000.00"), + sdk.Coins{c("btc", 10000000), c("btc", 10000000)}, + sdk.Coins{c("usdx", 1000000000), c("usdx", 500000000)}, + auction.Auctions{ + auction.CollateralAuction{ + BaseAuction: auction.BaseAuction{ + ID: 1, + Initiator: "liquidator", + Lot: c("btc", 10000000), + Bidder: nil, + Bid: c("usdx", 0), + HasReceivedBids: false, + EndTime: endTime, + MaxEndTime: endTime, + }, + CorrespondingDebt: c("debt", 1000000000), + MaxBid: c("usdx", 1025000000), + LotReturns: auction.WeightedAddresses{[]sdk.AccAddress{addr}, []sdk.Int{sdk.NewInt(10000000)}}, + }, + }, + }, + errArgs{ + true, + "", + }, + }, + { + "no liquidation", + args{ + "btc-a", + time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), + d("20000.00"), + d("10000.00"), + sdk.Coins{c("btc", 10000000), c("btc", 10000000)}, + sdk.Coins{c("usdx", 500000000), c("usdx", 500000000)}, + auction.Auctions{}, + }, + errArgs{ + false, + "collateral ratio not below liquidation ratio", + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + // setup pricefeed + pk := suite.app.GetPriceFeedKeeper() + _, err := pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.initialPrice, suite.ctx.BlockTime().Add(time.Hour*24)) + suite.Require().NoError(err) + err = pk.SetCurrentPrices(suite.ctx, "btc:usd") + suite.Require().NoError(err) + + // setup cdp state + suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, suite.ctx.BlockTime()) + suite.keeper.SetInterestFactor(suite.ctx, tc.args.ctype, sdk.OneDec()) + + for idx, col := range tc.args.collaterals { + err := suite.keeper.AddCdp(suite.ctx, suite.addrs[idx], col, tc.args.principals[idx], tc.args.ctype) + suite.Require().NoError(err) + } + + // update pricefeed + _, err = pk.SetPrice(suite.ctx, sdk.AccAddress{}, "btc:usd", tc.args.finalPrice, suite.ctx.BlockTime().Add(time.Hour*24)) + suite.Require().NoError(err) + err = pk.SetCurrentPrices(suite.ctx, "btc:usd") + suite.Require().NoError(err) + + _ = suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{Header: suite.ctx.BlockHeader()}) + ak := suite.app.GetAuctionKeeper() + auctions := ak.GetAllAuctions(suite.ctx) + if tc.errArgs.expectLiquidate { + suite.Require().Equal(tc.args.expectedAuctions, auctions) + for _, a := range auctions { + ca := a.(auction.CollateralAuction) + _, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, ca.LotReturns.Addresses[0], tc.args.ctype) + suite.Require().False(found) + } + } else { + suite.Require().Equal(0, len(auctions)) + for idx, _ := range tc.args.collaterals { + _, found := suite.keeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[idx], tc.args.ctype) + suite.Require().True(found) + } + } + }) + } +} + func TestSeizeTestSuite(t *testing.T) { suite.Run(t, new(SeizeTestSuite)) } diff --git a/x/cdp/legacy/v0_13/types.go b/x/cdp/legacy/v0_13/types.go index bdd7722130..80410ef776 100644 --- a/x/cdp/legacy/v0_13/types.go +++ b/x/cdp/legacy/v0_13/types.go @@ -243,33 +243,39 @@ func NewParams( // CollateralParam governance parameters for each collateral type within the cdp module type CollateralParam struct { - Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type - Type string `json:"type" yaml:"type"` - LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated - DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type - StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral - AuctionSize sdk.Int `json:"auction_size" yaml:"auction_size"` // Max amount of collateral to sell off in any one auction. - LiquidationPenalty sdk.Dec `json:"liquidation_penalty" yaml:"liquidation_penalty"` // percentage penalty (between [0, 1]) applied to a cdp if it is liquidated - Prefix byte `json:"prefix" yaml:"prefix"` - SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` // marketID of the spot price of the asset from the pricefeed - used for opening CDPs, depositing, withdrawing - LiquidationMarketID string `json:"liquidation_market_id" yaml:"liquidation_market_id"` // marketID of the pricefeed used for liquidation - ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` // factor for converting internal units to one base unit of collateral + Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type + Type string `json:"type" yaml:"type"` + LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated + DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type + StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral + AuctionSize sdk.Int `json:"auction_size" yaml:"auction_size"` // Max amount of collateral to sell off in any one auction. + LiquidationPenalty sdk.Dec `json:"liquidation_penalty" yaml:"liquidation_penalty"` // percentage penalty (between [0, 1]) applied to a cdp if it is liquidated + Prefix byte `json:"prefix" yaml:"prefix"` + SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` // marketID of the spot price of the asset from the pricefeed - used for opening CDPs, depositing, withdrawing + LiquidationMarketID string `json:"liquidation_market_id" yaml:"liquidation_market_id"` // marketID of the pricefeed used for liquidation + KeeperRewardPercentage sdk.Dec `json:"keeper_reward_percentage" yaml:"keeper_reward_percentage"` // the percentage of a CDPs collateral that gets rewarded to a keeper that liquidates the position + CheckCollateralizationIndexCount sdk.Int `json:"check_collateralization_index_count" yaml:"check_collateralization_index_count"` // the number of cdps that will be checked for liquidation in the begin blocker + ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` // factor for converting internal units to one base unit of collateral } // NewCollateralParam returns a new CollateralParam -func NewCollateralParam(denom, ctype string, liqRatio sdk.Dec, debtLimit sdk.Coin, stabilityFee sdk.Dec, auctionSize sdk.Int, liqPenalty sdk.Dec, prefix byte, spotMarketID, liquidationMarketID string, conversionFactor sdk.Int) CollateralParam { +func NewCollateralParam( + denom, ctype string, liqRatio sdk.Dec, debtLimit sdk.Coin, stabilityFee sdk.Dec, auctionSize sdk.Int, + liqPenalty sdk.Dec, prefix byte, spotMarketID, liquidationMarketID string, keeperReward sdk.Dec, checkIndexCount sdk.Int, conversionFactor sdk.Int) CollateralParam { return CollateralParam{ - Denom: denom, - Type: ctype, - LiquidationRatio: liqRatio, - DebtLimit: debtLimit, - StabilityFee: stabilityFee, - AuctionSize: auctionSize, - LiquidationPenalty: liqPenalty, - Prefix: prefix, - SpotMarketID: spotMarketID, - LiquidationMarketID: liquidationMarketID, - ConversionFactor: conversionFactor, + Denom: denom, + Type: ctype, + LiquidationRatio: liqRatio, + DebtLimit: debtLimit, + StabilityFee: stabilityFee, + AuctionSize: auctionSize, + LiquidationPenalty: liqPenalty, + Prefix: prefix, + SpotMarketID: spotMarketID, + LiquidationMarketID: liquidationMarketID, + KeeperRewardPercentage: keeperReward, + CheckCollateralizationIndexCount: checkIndexCount, + ConversionFactor: conversionFactor, } } @@ -286,13 +292,26 @@ func (cp CollateralParam) String() string { Prefix: %b Spot Market ID: %s Liquidation Market ID: %s + Keeper Reward Percentage: %s + Check Collateralization Count: %s Conversion Factor: %s`, - cp.Denom, cp.Type, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, cp.ConversionFactor) + cp.Denom, cp.Type, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, + cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, + cp.KeeperRewardPercentage, cp.CheckCollateralizationIndexCount, cp.ConversionFactor) } // CollateralParams array of CollateralParam type CollateralParams []CollateralParam +// String implements fmt.Stringer +func (cps CollateralParams) String() string { + out := "Collateral Params\n" + for _, cp := range cps { + out += fmt.Sprintf("%s\n", cp) + } + return out +} + // DebtParam governance params for debt assets type DebtParam struct { Denom string `json:"denom" yaml:"denom"` @@ -465,6 +484,12 @@ func validateCollateralParams(i interface{}) error { if cp.StabilityFee.LT(sdk.OneDec()) || cp.StabilityFee.GT(stabilityFeeMax) { return fmt.Errorf("stability fee must be ≥ 1.0, ≤ %s, is %s for %s", stabilityFeeMax, cp.StabilityFee, cp.Denom) } + if cp.KeeperRewardPercentage.IsNegative() || cp.KeeperRewardPercentage.GT(sdk.OneDec()) { + return fmt.Errorf("keeper reward percentage should be between 0 and 1, is %s for %s", cp.KeeperRewardPercentage, cp.Denom) + } + if cp.CheckCollateralizationIndexCount.IsNegative() { + return fmt.Errorf("keeper reward percentage should be positive, is %s for %s", cp.CheckCollateralizationIndexCount, cp.Denom) + } } return nil diff --git a/x/cdp/types/errors.go b/x/cdp/types/errors.go index 96b2cb7d0b..f248a223fc 100644 --- a/x/cdp/types/errors.go +++ b/x/cdp/types/errors.go @@ -49,4 +49,6 @@ var ( ErrAccountNotFound = sdkerrors.Register(ModuleName, 21, "account not found") // ErrInsufficientBalance error for when an account does not have enough funds ErrInsufficientBalance = sdkerrors.Register(ModuleName, 22, "insufficient balance") + // ErrNotLiquidatable error for when an cdp is not liquidatable + ErrNotLiquidatable = sdkerrors.Register(ModuleName, 23, "cdp collateral ratio not below liquidation ratio") ) diff --git a/x/cdp/types/msg.go b/x/cdp/types/msg.go index ba2fe6b2d2..aead450ddf 100644 --- a/x/cdp/types/msg.go +++ b/x/cdp/types/msg.go @@ -314,3 +314,59 @@ func (msg MsgRepayDebt) String() string { Payment: %s `, msg.Sender, msg.CollateralType, msg.Payment) } + +// MsgLiquidate attempts to liquidate a borrower's cdp +type MsgLiquidate struct { + Keeper sdk.AccAddress `json:"keeper" yaml:"keeper"` + Borrower sdk.AccAddress `json:"borrower" yaml:"borrower"` + CollateralType string `json:"collateral_type" yaml:"collateral_type"` +} + +// NewMsgLiquidate returns a new MsgLiquidate +func NewMsgLiquidate(keeper, borrower sdk.AccAddress, ctype string) MsgLiquidate { + return MsgLiquidate{ + Keeper: keeper, + Borrower: borrower, + CollateralType: ctype, + } +} + +// Route return the message type used for routing the message. +func (msg MsgLiquidate) Route() string { return RouterKey } + +// Type returns a human-readable string for the message, intended for utilization within tags. +func (msg MsgLiquidate) Type() string { return "liquidate" } + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgLiquidate) ValidateBasic() error { + if msg.Keeper.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "keeper address cannot be empty") + } + if msg.Borrower.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "borrower address cannot be empty") + } + if strings.TrimSpace(msg.CollateralType) == "" { + return sdkerrors.Wrap(ErrInvalidCollateral, "collateral type cannot be empty") + } + return nil +} + +// GetSignBytes gets the canonical byte representation of the Msg. +func (msg MsgLiquidate) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgLiquidate) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Keeper} +} + +// String implements the Stringer interface +func (msg MsgLiquidate) String() string { + return fmt.Sprintf(`Liquidate Message: + Keeper: %s + Borrower: %s + Collateral Type %s +`, msg.Keeper, msg.Borrower, msg.CollateralType) +} diff --git a/x/cdp/types/params.go b/x/cdp/types/params.go index 58e186da91..9ac192a99a 100644 --- a/x/cdp/types/params.go +++ b/x/cdp/types/params.go @@ -109,33 +109,39 @@ func DefaultParams() Params { // CollateralParam governance parameters for each collateral type within the cdp module type CollateralParam struct { - Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type - Type string `json:"type" yaml:"type"` - LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated - DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type - StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral - AuctionSize sdk.Int `json:"auction_size" yaml:"auction_size"` // Max amount of collateral to sell off in any one auction. - LiquidationPenalty sdk.Dec `json:"liquidation_penalty" yaml:"liquidation_penalty"` // percentage penalty (between [0, 1]) applied to a cdp if it is liquidated - Prefix byte `json:"prefix" yaml:"prefix"` - SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` // marketID of the spot price of the asset from the pricefeed - used for opening CDPs, depositing, withdrawing - LiquidationMarketID string `json:"liquidation_market_id" yaml:"liquidation_market_id"` // marketID of the pricefeed used for liquidation - ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` // factor for converting internal units to one base unit of collateral + Denom string `json:"denom" yaml:"denom"` // Coin name of collateral type + Type string `json:"type" yaml:"type"` + LiquidationRatio sdk.Dec `json:"liquidation_ratio" yaml:"liquidation_ratio"` // The ratio (Collateral (priced in stable coin) / Debt) under which a CDP will be liquidated + DebtLimit sdk.Coin `json:"debt_limit" yaml:"debt_limit"` // Maximum amount of debt allowed to be drawn from this collateral type + StabilityFee sdk.Dec `json:"stability_fee" yaml:"stability_fee"` // per second stability fee for loans opened using this collateral + AuctionSize sdk.Int `json:"auction_size" yaml:"auction_size"` // Max amount of collateral to sell off in any one auction. + LiquidationPenalty sdk.Dec `json:"liquidation_penalty" yaml:"liquidation_penalty"` // percentage penalty (between [0, 1]) applied to a cdp if it is liquidated + Prefix byte `json:"prefix" yaml:"prefix"` + SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"` // marketID of the spot price of the asset from the pricefeed - used for opening CDPs, depositing, withdrawing + LiquidationMarketID string `json:"liquidation_market_id" yaml:"liquidation_market_id"` // marketID of the pricefeed used for liquidation + KeeperRewardPercentage sdk.Dec `json:"keeper_reward_percentage" yaml:"keeper_reward_percentage"` // the percentage of a CDPs collateral that gets rewarded to a keeper that liquidates the position + CheckCollateralizationIndexCount sdk.Int `json:"check_collateralization_index_count" yaml:"check_collateralization_index_count"` // the number of cdps that will be checked for liquidation in the begin blocker + ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"` // factor for converting internal units to one base unit of collateral } // NewCollateralParam returns a new CollateralParam -func NewCollateralParam(denom, ctype string, liqRatio sdk.Dec, debtLimit sdk.Coin, stabilityFee sdk.Dec, auctionSize sdk.Int, liqPenalty sdk.Dec, prefix byte, spotMarketID, liquidationMarketID string, conversionFactor sdk.Int) CollateralParam { +func NewCollateralParam( + denom, ctype string, liqRatio sdk.Dec, debtLimit sdk.Coin, stabilityFee sdk.Dec, auctionSize sdk.Int, + liqPenalty sdk.Dec, prefix byte, spotMarketID, liquidationMarketID string, keeperReward sdk.Dec, checkIndexCount sdk.Int, conversionFactor sdk.Int) CollateralParam { return CollateralParam{ - Denom: denom, - Type: ctype, - LiquidationRatio: liqRatio, - DebtLimit: debtLimit, - StabilityFee: stabilityFee, - AuctionSize: auctionSize, - LiquidationPenalty: liqPenalty, - Prefix: prefix, - SpotMarketID: spotMarketID, - LiquidationMarketID: liquidationMarketID, - ConversionFactor: conversionFactor, + Denom: denom, + Type: ctype, + LiquidationRatio: liqRatio, + DebtLimit: debtLimit, + StabilityFee: stabilityFee, + AuctionSize: auctionSize, + LiquidationPenalty: liqPenalty, + Prefix: prefix, + SpotMarketID: spotMarketID, + LiquidationMarketID: liquidationMarketID, + KeeperRewardPercentage: keeperReward, + CheckCollateralizationIndexCount: checkIndexCount, + ConversionFactor: conversionFactor, } } @@ -152,8 +158,12 @@ func (cp CollateralParam) String() string { Prefix: %b Spot Market ID: %s Liquidation Market ID: %s + Keeper Reward Percentage: %s + Check Collateralization Count: %s Conversion Factor: %s`, - cp.Denom, cp.Type, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, cp.ConversionFactor) + cp.Denom, cp.Type, cp.LiquidationRatio, cp.StabilityFee, cp.LiquidationPenalty, + cp.DebtLimit, cp.AuctionSize, cp.Prefix, cp.SpotMarketID, cp.LiquidationMarketID, + cp.KeeperRewardPercentage, cp.CheckCollateralizationIndexCount, cp.ConversionFactor) } // CollateralParams array of CollateralParam @@ -381,6 +391,12 @@ func validateCollateralParams(i interface{}) error { if cp.StabilityFee.LT(sdk.OneDec()) || cp.StabilityFee.GT(stabilityFeeMax) { return fmt.Errorf("stability fee must be ≥ 1.0, ≤ %s, is %s for %s", stabilityFeeMax, cp.StabilityFee, cp.Denom) } + if cp.KeeperRewardPercentage.IsNegative() || cp.KeeperRewardPercentage.GT(sdk.OneDec()) { + return fmt.Errorf("keeper reward percentage should be between 0 and 1, is %s for %s", cp.KeeperRewardPercentage, cp.Denom) + } + if cp.CheckCollateralizationIndexCount.IsNegative() { + return fmt.Errorf("keeper reward percentage should be positive, is %s for %s", cp.CheckCollateralizationIndexCount, cp.Denom) + } } return nil diff --git a/x/cdp/types/params_test.go b/x/cdp/types/params_test.go index 9e7c2c4b65..9d596280df 100644 --- a/x/cdp/types/params_test.go +++ b/x/cdp/types/params_test.go @@ -65,17 +65,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 4000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -103,17 +105,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 4000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -141,17 +145,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -179,30 +185,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 4000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "xrp", - Type: "xrp-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x21, - SpotMarketID: "xrp:usd", - LiquidationMarketID: "xrp:usd", - ConversionFactor: sdk.NewInt(6), + Denom: "xrp", + Type: "xrp-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x21, + SpotMarketID: "xrp:usd", + LiquidationMarketID: "xrp:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(6), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -230,30 +240,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "xrp", - Type: "xrp-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x21, - SpotMarketID: "xrp:usd", - LiquidationMarketID: "xrp:usd", - ConversionFactor: sdk.NewInt(6), + Denom: "xrp", + Type: "xrp-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x21, + SpotMarketID: "xrp:usd", + LiquidationMarketID: "xrp:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(6), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -281,30 +295,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 4000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "xrp", - Type: "xrp-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("susd", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x21, - SpotMarketID: "xrp:usd", - LiquidationMarketID: "xrp:usd", - ConversionFactor: sdk.NewInt(6), + Denom: "xrp", + Type: "xrp-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("susd", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x21, + SpotMarketID: "xrp:usd", + LiquidationMarketID: "xrp:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(6), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -332,16 +350,18 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -369,17 +389,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "", - LiquidationMarketID: "", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "", + LiquidationMarketID: "", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -407,30 +429,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x21, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x21, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -458,30 +484,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "bnb", - Type: "bnb-b", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x21, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-b", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x21, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -509,30 +539,34 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, { - Denom: "xrp", - Type: "xrp-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "xrp:usd", - LiquidationMarketID: "xrp:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "xrp", + Type: "xrp-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "xrp:usd", + LiquidationMarketID: "xrp:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -560,17 +594,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.Coin{}, - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.Coin{}, + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -598,17 +634,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("1.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("1.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -636,17 +674,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.ZeroInt(), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.ZeroInt(), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -674,17 +714,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.1"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 1000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.1"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -712,17 +754,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ @@ -750,17 +794,19 @@ func (suite *ParamsTestSuite) TestParamValidation() { globalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), collateralParams: types.CollateralParams{ { - Denom: "bnb", - Type: "bnb-a", - LiquidationRatio: sdk.MustNewDecFromStr("1.5"), - DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), - StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), - LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), - AuctionSize: sdk.NewInt(50000000000), - Prefix: 0x20, - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - ConversionFactor: sdk.NewInt(8), + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + AuctionSize: sdk.NewInt(50000000000), + Prefix: 0x20, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + ConversionFactor: sdk.NewInt(8), + CheckCollateralizationIndexCount: sdk.NewInt(10), }, }, debtParam: types.DebtParam{ From 3ef49f32e4327635da6b2d2bf5243cc7e5da136c Mon Sep 17 00:00:00 2001 From: karzak Date: Thu, 31 Dec 2020 12:36:50 -0700 Subject: [PATCH 2/2] address review comments --- x/cdp/keeper/seize.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index 1bb05107e2..85e1394827 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -9,7 +9,7 @@ import ( "github.com/kava-labs/kava/x/cdp/types" ) -// AttemptKeeperLiquidation liquidates the cdp with the input colalteral type and owner if it is below the required collateralization ratio +// AttemptKeeperLiquidation liquidates the cdp with the input collateral type and owner if it is below the required collateralization ratio // if the cdp is liquidated, the keeper that sent the transaction is rewarded a percentage of the collateral according to that collateral types' // keeper reward percentage. func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper, owner sdk.AccAddress, collateralType string) error { @@ -140,6 +140,19 @@ func (k Keeper) payoutKeeperLiquidationReward(ctx sdk.Context, keeper sdk.AccAdd } reward := cdp.Collateral.Amount.ToDec().Mul(collateralParam.KeeperRewardPercentage).RoundInt() rewardCoin := sdk.NewCoin(cdp.Collateral.Denom, reward) + paidReward := false + deposits := k.GetDeposits(ctx, cdp.ID) + for _, dep := range deposits { + if dep.Amount.IsGTE(rewardCoin) { + dep.Amount = dep.Amount.Sub(rewardCoin) + k.SetDeposit(ctx, dep) + paidReward = true + break + } + } + if !paidReward { + return cdp, nil + } err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, keeper, sdk.NewCoins(rewardCoin)) if err != nil { return types.CDP{}, err @@ -150,14 +163,5 @@ func (k Keeper) payoutKeeperLiquidationReward(ctx sdk.Context, keeper sdk.AccAdd if err != nil { return types.CDP{}, err } - - deposits := k.GetDeposits(ctx, cdp.ID) - for _, dep := range deposits { - if dep.Amount.IsGTE(rewardCoin) { - dep.Amount = dep.Amount.Sub(rewardCoin) - k.SetDeposit(ctx, dep) - break - } - } return cdp, nil }