Skip to content

Commit

Permalink
R4R: Move liquidator functions to cdp module (#280)
Browse files Browse the repository at this point in the history
* wip: tpyes and keeper methods

* wip: iterators

* wip: types and keeper methods

* wip: add msgs

* wip: client methods

* wip: rebase develop

* wip: types tests

* wip: keeper tests, small fixes

* wip: add cdp tests

* wip: deposit tests

* wip: keeper tests

* wip: tests and module methods

* feat: error when fetching expired price

* feat: conversion factor for external assets

* feat: debt floor for new cdps

* feat: save deposits on export genesis

* feat: ensure messages implement msg

* feat: index deposits by status

* fix: stray comment

* wip: address review comments

* address review comments

* wip: move liquidation to cdp module

* wip: handle liquidations directly

* wip: use new auction interface

* feat: auction collateral in cdp begin block

* feat: update param validation

* feat: surplus and debt auctions

* address review comments

* address review comments

* fix: auction multiple deposits

* clean up netting function
  • Loading branch information
karzak committed Jan 15, 2020
1 parent ba80b50 commit 9b1bf55
Show file tree
Hide file tree
Showing 29 changed files with 704 additions and 334 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@
# Exclude build files
vendor
build

# IDE files
*.vscode
11 changes: 6 additions & 5 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,19 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
pricefeedSubspace,
pricefeed.DefaultCodespace)
// NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, pfk types.PricefeedKeeper, sk types.SupplyKeeper, codespace sdk.CodespaceType)
app.auctionKeeper = auction.NewKeeper(
app.cdc,
keys[auction.StoreKey],
app.supplyKeeper,
auctionSubspace)
app.cdpKeeper = cdp.NewKeeper(
app.cdc,
keys[cdp.StoreKey],
cdpSubspace,
app.pricefeedKeeper,
app.auctionKeeper,
app.supplyKeeper,
cdp.DefaultCodespace)
app.auctionKeeper = auction.NewKeeper(
app.cdc,
keys[auction.StoreKey],
app.supplyKeeper,
auctionSubspace)

// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
Expand Down
28 changes: 14 additions & 14 deletions app/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,20 @@ func NewTestApp() TestApp {
return TestApp{App: *app}
}

func (tApp TestApp) GetAccountKeeper() auth.AccountKeeper { return tApp.accountKeeper }
func (tApp TestApp) GetBankKeeper() bank.Keeper { return tApp.bankKeeper }
func (tApp TestApp) GetSupplyKeeper() supply.Keeper { return tApp.supplyKeeper }
func (tApp TestApp) GetStakingKeeper() staking.Keeper { return tApp.stakingKeeper }
func (tApp TestApp) GetSlashingKeeper() slashing.Keeper { return tApp.slashingKeeper }
func (tApp TestApp) GetMintKeeper() mint.Keeper { return tApp.mintKeeper }
func (tApp TestApp) GetDistrKeeper() distribution.Keeper { return tApp.distrKeeper }
func (tApp TestApp) GetGovKeeper() gov.Keeper { return tApp.govKeeper }
func (tApp TestApp) GetCrisisKeeper() crisis.Keeper { return tApp.crisisKeeper }
func (tApp TestApp) GetParamsKeeper() params.Keeper { return tApp.paramsKeeper }
func (tApp TestApp) GetVVKeeper() validatorvesting.Keeper { return tApp.vvKeeper }
func (tApp TestApp) GetAuctionKeeper() auction.Keeper { return tApp.auctionKeeper }
func (tApp TestApp) GetCDPKeeper() cdp.Keeper { return tApp.cdpKeeper }
func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper }
func (tApp TestApp) GetAccountKeeper() auth.AccountKeeper { return tApp.accountKeeper }
func (tApp TestApp) GetBankKeeper() bank.Keeper { return tApp.bankKeeper }
func (tApp TestApp) GetSupplyKeeper() supply.Keeper { return tApp.supplyKeeper }
func (tApp TestApp) GetStakingKeeper() staking.Keeper { return tApp.stakingKeeper }
func (tApp TestApp) GetSlashingKeeper() slashing.Keeper { return tApp.slashingKeeper }
func (tApp TestApp) GetMintKeeper() mint.Keeper { return tApp.mintKeeper }
func (tApp TestApp) GetDistrKeeper() distribution.Keeper { return tApp.distrKeeper }
func (tApp TestApp) GetGovKeeper() gov.Keeper { return tApp.govKeeper }
func (tApp TestApp) GetCrisisKeeper() crisis.Keeper { return tApp.crisisKeeper }
func (tApp TestApp) GetParamsKeeper() params.Keeper { return tApp.paramsKeeper }
func (tApp TestApp) GetVVKeeper() validatorvesting.Keeper { return tApp.vvKeeper }
func (tApp TestApp) GetAuctionKeeper() auction.Keeper { return tApp.auctionKeeper }
func (tApp TestApp) GetCDPKeeper() cdp.Keeper { return tApp.cdpKeeper }
func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper }

// This calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states
func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) TestApp {
Expand Down
3 changes: 3 additions & 0 deletions x/auction/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type Keeper struct {

// NewKeeper returns a new auction keeper.
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, supplyKeeper types.SupplyKeeper, paramstore subspace.Subspace) Keeper {
if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
return Keeper{
supplyKeeper: supplyKeeper,
storeKey: storeKey,
Expand Down
1 change: 1 addition & 0 deletions x/auction/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

// SupplyKeeper defines the expected supply Keeper
type SupplyKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI

SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) sdk.Error
Expand Down
26 changes: 24 additions & 2 deletions x/cdp/abci.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package cdp

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/cdp/types"
abci "github.com/tendermint/tendermint/abci/types"
)

// BeginBlock compounds the debt in outstanding cdps and liquidates cdps that are below the required collateralization ratio
// BeginBlocker compounds the debt in outstanding cdps and liquidates cdps that are below the required collateralization ratio
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
params := k.GetParams(ctx)
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
Expand All @@ -18,7 +21,26 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
k.HandleNewDebt(ctx, cp.Denom, dp.Denom, timeElapsed)
}

k.LiquidateCdps(ctx, cp.MarketID, cp.Denom, cp.LiquidationRatio)
err := k.LiquidateCdps(ctx, cp.MarketID, cp.Denom, cp.LiquidationRatio)
if err != nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypeBeginBlockerFatal,
sdk.NewAttribute(sdk.AttributeKeyModule, fmt.Sprintf("%s", ModuleName)),
sdk.NewAttribute(types.AttributeKeyError, fmt.Sprintf("%s", err)),
),
)
}
}
err := k.RunSurplusAndDebtAuctions(ctx)
if err != nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypeBeginBlockerFatal,
sdk.NewAttribute(sdk.AttributeKeyModule, fmt.Sprintf("%s", ModuleName)),
sdk.NewAttribute(types.AttributeKeyError, fmt.Sprintf("%s", err)),
),
)
}
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
return
Expand Down
3 changes: 2 additions & 1 deletion x/cdp/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/cdp"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -116,7 +117,7 @@ func (suite *ModuleTestSuite) TestBeginBlock() {
btcLiquidations := int(seizedBtcCollateral.Quo(i(100000000)).Int64())
suite.Equal(len(suite.liquidations.btc), btcLiquidations)

acc = sk.GetModuleAccount(suite.ctx, cdp.LiquidatorMacc)
acc = sk.GetModuleAccount(suite.ctx, auction.ModuleName)
suite.Equal(suite.liquidations.debt, acc.GetCoins().AmountOf("debt").Int64())

}
Expand Down
14 changes: 9 additions & 5 deletions x/cdp/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
)

const (
StatusNil = types.StatusNil
StatusLiquidated = types.StatusLiquidated
DefaultCodespace = types.DefaultCodespace
CodeCdpAlreadyExists = types.CodeCdpAlreadyExists
CodeCollateralLengthInvalid = types.CodeCollateralLengthInvalid
Expand All @@ -36,15 +34,17 @@ const (
EventTypeCdpClose = types.EventTypeCdpClose
EventTypeCdpWithdrawal = types.EventTypeCdpWithdrawal
EventTypeCdpLiquidation = types.EventTypeCdpLiquidation
EventTypeBeginBlockerFatal = types.EventTypeBeginBlockerFatal
AttributeKeyCdpID = types.AttributeKeyCdpID
AttributeKeyDepositor = types.AttributeKeyDepositor
AttributeValueCategory = types.AttributeValueCategory
LiquidatorMacc = types.LiquidatorMacc
AttributeKeyError = types.AttributeKeyError
ModuleName = types.ModuleName
StoreKey = types.StoreKey
RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute
DefaultParamspace = types.DefaultParamspace
LiquidatorMacc = types.LiquidatorMacc
QueryGetCdp = types.QueryGetCdp
QueryGetCdps = types.QueryGetCdps
QueryGetCdpsByCollateralization = types.QueryGetCdpsByCollateralization
Expand All @@ -58,7 +58,6 @@ var (
// functions aliases
NewCDP = types.NewCDP
RegisterCodec = types.RegisterCodec
StatusFromByte = types.StatusFromByte
NewDeposit = types.NewDeposit
ErrCdpAlreadyExists = types.ErrCdpAlreadyExists
ErrInvalidCollateralLength = types.ErrInvalidCollateralLength
Expand Down Expand Up @@ -116,6 +115,7 @@ var (
CollateralRatioIndexPrefix = types.CollateralRatioIndexPrefix
CdpIDKey = types.CdpIDKey
DebtDenomKey = types.DebtDenomKey
GovDenomKey = types.GovDenomKey
DepositKeyPrefix = types.DepositKeyPrefix
PrincipalKeyPrefix = types.PrincipalKeyPrefix
AccumulatorKeyPrefix = types.AccumulatorKeyPrefix
Expand All @@ -124,12 +124,17 @@ var (
KeyCollateralParams = types.KeyCollateralParams
KeyDebtParams = types.KeyDebtParams
KeyCircuitBreaker = types.KeyCircuitBreaker
KeyDebtThreshold = types.KeyDebtThreshold
KeySurplusThreshold = types.KeySurplusThreshold
DefaultGlobalDebt = types.DefaultGlobalDebt
DefaultCircuitBreaker = types.DefaultCircuitBreaker
DefaultCollateralParams = types.DefaultCollateralParams
DefaultDebtParams = types.DefaultDebtParams
DefaultCdpStartingID = types.DefaultCdpStartingID
DefaultDebtDenom = types.DefaultDebtDenom
DefaultGovDenom = types.DefaultGovDenom
DefaultSurplusThreshold = types.DefaultSurplusThreshold
DefaultDebtThreshold = types.DefaultDebtThreshold
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
MaxSortableDec = types.MaxSortableDec
)
Expand All @@ -138,7 +143,6 @@ type (
CDP = types.CDP
CDPs = types.CDPs
Deposit = types.Deposit
DepositStatus = types.DepositStatus
Deposits = types.Deposits
SupplyKeeper = types.SupplyKeeper
PricefeedKeeper = types.PricefeedKeeper
Expand Down
7 changes: 3 additions & 4 deletions x/cdp/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ func QueryCdpCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
return err
}
collateralType := args[1] // TODO validation?
bz, err := cdc.MarshalJSON(types.QueryCdpsParams{
bz, err := cdc.MarshalJSON(types.QueryCdpParams{
CollateralDenom: collateralType,
Owner: ownerAddress,
})
if err != nil {
return err
Expand All @@ -58,8 +59,6 @@ func QueryCdpCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdp)
res, _, err := cliCtx.QueryWithData(route, bz)
if err != nil {
fmt.Printf("error when getting cdp info - %s", err)
fmt.Printf("could not get current cdp info - %s %s \n", string(ownerAddress), string(collateralType))
return err
}

Expand All @@ -78,7 +77,7 @@ func QueryCdpsByDenomCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
Short: "Query cdps by collateral type",
Long: strings.TrimSpace(`Query cdps by a specific collateral type, or query all cdps if none is specifed:
$ <appcli> query cdp cdps atom
$ <appcli> query cdp cdps uatom
`),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
1 change: 1 addition & 0 deletions x/cdp/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk PricefeedKeeper, gs GenesisState)

k.SetNextCdpID(ctx, gs.StartingCdpID)
k.SetDebtDenom(ctx, gs.DebtDenom)
k.SetGovDenom(ctx, gs.GovDenom)

for _, d := range gs.Deposits {
k.SetDeposit(ctx, d)
Expand Down
75 changes: 51 additions & 24 deletions x/cdp/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,20 @@ func NewPricefeedGenState(asset string, price sdk.Dec) app.GenesisState {
func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
cdpGenesis := cdp.GenesisState{
Params: cdp.Params{
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000)),
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000)),
SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
DebtAuctionThreshold: cdp.DefaultDebtThreshold,
CollateralParams: cdp.CollateralParams{
{
Denom: asset,
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
Prefix: 0x20,
ConversionFactor: i(6),
MarketID: asset + ":usd",
Denom: asset,
LiquidationRatio: liquidationRatio,
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(1000000000),
Prefix: 0x20,
ConversionFactor: i(6),
MarketID: asset + ":usd",
},
},
DebtParams: cdp.DebtParams{
Expand All @@ -63,6 +67,7 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
},
StartingCdpID: cdp.DefaultCdpStartingID,
DebtDenom: cdp.DefaultDebtDenom,
GovDenom: cdp.DefaultGovDenom,
CDPs: cdp.CDPs{},
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
}
Expand Down Expand Up @@ -97,25 +102,31 @@ func NewPricefeedGenStateMulti() app.GenesisState {
func NewCDPGenStateMulti() app.GenesisState {
cdpGenesis := cdp.GenesisState{
Params: cdp.Params{
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000), sdk.NewInt64Coin("susd", 1000000000000)),
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000), sdk.NewInt64Coin("susd", 1000000000000)),
SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
DebtAuctionThreshold: cdp.DefaultDebtThreshold,
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000000000), sdk.NewInt64Coin("susd", 500000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
Prefix: 0x20,
MarketID: "xrp:usd",
ConversionFactor: i(6),
Denom: "xrp",
LiquidationRatio: sdk.MustNewDecFromStr("2.0"),
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000000000), sdk.NewInt64Coin("susd", 500000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr
LiquidationPenalty: d("0.05"),
AuctionSize: i(7000000000),
Prefix: 0x20,
MarketID: "xrp:usd",
ConversionFactor: i(6),
},
{
Denom: "btc",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000000000), sdk.NewInt64Coin("susd", 500000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
Prefix: 0x21,
MarketID: "btc:usd",
ConversionFactor: i(8),
Denom: "btc",
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
DebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000000000), sdk.NewInt64Coin("susd", 500000000000)),
StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr
LiquidationPenalty: d("0.025"),
AuctionSize: i(10000000),
Prefix: 0x21,
MarketID: "btc:usd",
ConversionFactor: i(8),
},
},
DebtParams: cdp.DebtParams{
Expand All @@ -137,6 +148,7 @@ func NewCDPGenStateMulti() app.GenesisState {
},
StartingCdpID: cdp.DefaultCdpStartingID,
DebtDenom: cdp.DefaultDebtDenom,
GovDenom: cdp.DefaultGovDenom,
CDPs: cdp.CDPs{},
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
}
Expand Down Expand Up @@ -193,6 +205,15 @@ func badGenStates() []badGenState {
g10 := baseGenState()
g10.PreviousBlockTime = time.Time{}

g11 := baseGenState()
g11.Params.CollateralParams[0].AuctionSize = i(-10)

g12 := baseGenState()
g12.Params.CollateralParams[0].LiquidationPenalty = d("5.0")

g13 := baseGenState()
g13.GovDenom = ""

return []badGenState{
badGenState{Genesis: g1, Reason: "duplicate collateral denom"},
badGenState{Genesis: g2, Reason: "duplicate collateral prefix"},
Expand All @@ -204,13 +225,18 @@ func badGenStates() []badGenState {
badGenState{Genesis: g8, Reason: "debt param not found in global debt limit"},
badGenState{Genesis: g9, Reason: "debt denom not set"},
badGenState{Genesis: g10, Reason: "previous block time not set"},
badGenState{Genesis: g11, Reason: "negative auction size"},
badGenState{Genesis: g12, Reason: "invalid liquidation penalty"},
badGenState{Genesis: g13, Reason: "gov denom not set"},
}
}

func baseGenState() cdp.GenesisState {
return cdp.GenesisState{
Params: cdp.Params{
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000), sdk.NewInt64Coin("susd", 1000000000000)),
GlobalDebtLimit: sdk.NewCoins(sdk.NewInt64Coin("usdx", 1000000000000), sdk.NewInt64Coin("susd", 1000000000000)),
SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
DebtAuctionThreshold: cdp.DefaultDebtThreshold,
CollateralParams: cdp.CollateralParams{
{
Denom: "xrp",
Expand Down Expand Up @@ -250,6 +276,7 @@ func baseGenState() cdp.GenesisState {
},
StartingCdpID: cdp.DefaultCdpStartingID,
DebtDenom: cdp.DefaultDebtDenom,
GovDenom: cdp.DefaultGovDenom,
CDPs: cdp.CDPs{},
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
}
Expand Down
Loading

0 comments on commit 9b1bf55

Please sign in to comment.