2020-01-12 15:35:34 +00:00
package keeper
import (
"fmt"
2020-04-29 22:02:31 +00:00
"sort"
2020-01-12 15:35:34 +00:00
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
2020-04-23 16:35:58 +00:00
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
2020-04-30 14:13:31 +00:00
2020-01-12 15:35:34 +00:00
"github.com/kava-labs/kava/x/cdp/types"
)
2020-01-28 17:47:08 +00:00
// BaseDigitFactor is 10**18, used during coin calculations
const BaseDigitFactor = 1000000000000000000
2020-01-12 15:35:34 +00:00
// AddCdp adds a cdp for a specific owner and collateral type
2020-08-21 19:42:46 +00:00
func ( k Keeper ) AddCdp ( ctx sdk . Context , owner sdk . AccAddress , collateral sdk . Coin , principal sdk . Coin , collateralType string ) error {
2020-01-12 15:35:34 +00:00
// validation
2020-08-21 19:42:46 +00:00
err := k . ValidateCollateral ( ctx , collateral , collateralType )
2020-01-12 15:35:34 +00:00
if err != nil {
return err
}
2020-08-21 19:42:46 +00:00
_ , found := k . GetCdpByOwnerAndCollateralType ( ctx , owner , collateralType )
2020-01-12 15:35:34 +00:00
if found {
2020-04-27 14:40:34 +00:00
return sdkerrors . Wrapf ( types . ErrCdpAlreadyExists , "owner %s, denom %s" , owner , collateral . Denom )
2020-01-12 15:35:34 +00:00
}
err = k . ValidatePrincipalAdd ( ctx , principal )
if err != nil {
return err
}
2020-04-15 17:50:14 +00:00
2020-08-21 19:42:46 +00:00
err = k . ValidateDebtLimit ( ctx , collateralType , principal )
2020-04-15 17:50:14 +00:00
if err != nil {
return err
}
2020-08-21 19:42:46 +00:00
err = k . ValidateCollateralizationRatio ( ctx , collateral , collateralType , principal , sdk . NewCoin ( principal . Denom , sdk . ZeroInt ( ) ) )
2020-01-12 15:35:34 +00:00
if err != nil {
return err
}
// send coins from the owners account to the cdp module
id := k . GetNextCdpID ( ctx )
2020-08-21 19:42:46 +00:00
cdp := types . NewCDP ( id , owner , collateral , collateralType , principal , ctx . BlockHeader ( ) . Time )
2020-01-12 15:35:34 +00:00
deposit := types . NewDeposit ( cdp . ID , owner , collateral )
2020-04-27 14:40:34 +00:00
err = k . supplyKeeper . SendCoinsFromAccountToModule ( ctx , owner , types . ModuleName , sdk . NewCoins ( collateral ) )
2020-01-12 15:35:34 +00:00
if err != nil {
return err
}
// mint the principal and send to the owners account
2020-04-27 14:40:34 +00:00
err = k . supplyKeeper . MintCoins ( ctx , types . ModuleName , sdk . NewCoins ( principal ) )
2020-01-12 15:35:34 +00:00
if err != nil {
panic ( err )
}
2020-04-27 14:40:34 +00:00
err = k . supplyKeeper . SendCoinsFromModuleToAccount ( ctx , types . ModuleName , owner , sdk . NewCoins ( principal ) )
2020-01-12 15:35:34 +00:00
if err != nil {
panic ( err )
}
// mint the corresponding amount of debt coins
err = k . MintDebtCoins ( ctx , types . ModuleName , k . GetDebtDenom ( ctx ) , principal )
if err != nil {
panic ( err )
}
2020-05-13 14:19:19 +00:00
// update total principal for input collateral type
2020-08-21 19:42:46 +00:00
k . IncrementTotalPrincipal ( ctx , collateralType , principal )
2020-05-13 14:19:19 +00:00
// set the cdp, deposit, and indexes in the store
2020-08-21 19:42:46 +00:00
collateralToDebtRatio := k . CalculateCollateralToDebtRatio ( ctx , collateral , cdp . Type , principal )
2020-05-13 14:19:19 +00:00
err = k . SetCdpAndCollateralRatioIndex ( ctx , cdp , collateralToDebtRatio )
if err != nil {
return err
}
k . IndexCdpByOwner ( ctx , cdp )
k . SetDeposit ( ctx , deposit )
k . SetNextCdpID ( ctx , id + 1 )
2020-01-12 15:35:34 +00:00
// emit events for cdp creation, deposit, and draw
ctx . EventManager ( ) . EmitEvent (
sdk . NewEvent (
types . EventTypeCreateCdp ,
sdk . NewAttribute ( types . AttributeKeyCdpID , fmt . Sprintf ( "%d" , cdp . ID ) ) ,
) ,
)
ctx . EventManager ( ) . EmitEvent (
sdk . NewEvent (
types . EventTypeCdpDeposit ,
sdk . NewAttribute ( sdk . AttributeKeyAmount , collateral . String ( ) ) ,
sdk . NewAttribute ( types . AttributeKeyCdpID , fmt . Sprintf ( "%d" , cdp . ID ) ) ,
) ,
)
ctx . EventManager ( ) . EmitEvent (
sdk . NewEvent (
types . EventTypeCdpDraw ,
sdk . NewAttribute ( sdk . AttributeKeyAmount , principal . String ( ) ) ,
sdk . NewAttribute ( types . AttributeKeyCdpID , fmt . Sprintf ( "%d" , cdp . ID ) ) ,
) ,
)
return nil
}
// SetCdpAndCollateralRatioIndex sets the cdp and collateral ratio index in the store
2020-04-27 14:40:34 +00:00
func ( k Keeper ) SetCdpAndCollateralRatioIndex ( ctx sdk . Context , cdp types . CDP , ratio sdk . Dec ) error {
err := k . SetCDP ( ctx , cdp )
if err != nil {
return err
}
2020-08-21 19:42:46 +00:00
k . IndexCdpByCollateralRatio ( ctx , cdp . Type , cdp . ID , ratio )
2020-04-27 14:40:34 +00:00
return nil
2020-01-12 15:35:34 +00:00
}
// MintDebtCoins mints debt coins in the cdp module account
2020-04-27 14:40:34 +00:00
func ( k Keeper ) MintDebtCoins ( ctx sdk . Context , moduleAccount string , denom string , principalCoins sdk . Coin ) error {
debtCoins := sdk . NewCoins ( sdk . NewCoin ( denom , principalCoins . Amount ) )
2020-06-09 11:07:58 +00:00
return k . supplyKeeper . MintCoins ( ctx , moduleAccount , debtCoins )
2020-01-12 15:35:34 +00:00
}
2020-01-30 23:50:42 +00:00
// BurnDebtCoins burns debt coins from the cdp module account
2020-04-27 14:40:34 +00:00
func ( k Keeper ) BurnDebtCoins ( ctx sdk . Context , moduleAccount string , denom string , paymentCoins sdk . Coin ) error {
debtCoins := sdk . NewCoins ( sdk . NewCoin ( denom , paymentCoins . Amount ) )
2020-06-09 11:07:58 +00:00
return k . supplyKeeper . BurnCoins ( ctx , moduleAccount , debtCoins )
2020-01-12 15:35:34 +00:00
}
// GetCdpID returns the id of the cdp corresponding to a specific owner and collateral denom
2020-08-21 19:42:46 +00:00
func ( k Keeper ) GetCdpID ( ctx sdk . Context , owner sdk . AccAddress , collateralType string ) ( uint64 , bool ) {
2020-01-12 15:35:34 +00:00
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , owner )
if ! found {
return 0 , false
}
for _ , id := range cdpIDs {
2020-08-21 19:42:46 +00:00
_ , found = k . GetCDP ( ctx , collateralType , id )
2020-01-12 15:35:34 +00:00
if found {
return id , true
}
}
return 0 , false
}
// GetCdpIdsByOwner returns all the ids of cdps corresponding to a particular owner
func ( k Keeper ) GetCdpIdsByOwner ( ctx sdk . Context , owner sdk . AccAddress ) ( [ ] uint64 , bool ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpIDKeyPrefix )
bz := store . Get ( owner )
2020-04-29 14:10:41 +00:00
if bz == nil {
2020-01-12 15:35:34 +00:00
return [ ] uint64 { } , false
}
var cdpIDs [ ] uint64
k . cdc . MustUnmarshalBinaryLengthPrefixed ( bz , & cdpIDs )
return cdpIDs , true
}
2020-08-21 19:42:46 +00:00
// GetCdpByOwnerAndCollateralType queries cdps owned by owner and returns the cdp with matching denom
func ( k Keeper ) GetCdpByOwnerAndCollateralType ( ctx sdk . Context , owner sdk . AccAddress , collateralType string ) ( types . CDP , bool ) {
2020-01-12 15:35:34 +00:00
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , owner )
if ! found {
return types . CDP { } , false
}
for _ , id := range cdpIDs {
2020-08-21 19:42:46 +00:00
cdp , found := k . GetCDP ( ctx , collateralType , id )
2020-01-12 15:35:34 +00:00
if found {
return cdp , true
}
}
return types . CDP { } , false
}
// GetCDP returns the cdp associated with a particular collateral denom and id
2020-08-21 19:42:46 +00:00
func ( k Keeper ) GetCDP ( ctx sdk . Context , collateralType string , cdpID uint64 ) ( types . CDP , bool ) {
2020-01-12 15:35:34 +00:00
// get store
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
2020-08-21 19:42:46 +00:00
db , found := k . GetCollateralTypePrefix ( ctx , collateralType )
2020-04-27 14:40:34 +00:00
if ! found {
return types . CDP { } , false
}
2020-01-12 15:35:34 +00:00
// get CDP
bz := store . Get ( types . CdpKey ( db , cdpID ) )
// unmarshal
if bz == nil {
return types . CDP { } , false
}
var cdp types . CDP
k . cdc . MustUnmarshalBinaryLengthPrefixed ( bz , & cdp )
return cdp , true
}
// SetCDP sets a cdp in the store
2020-04-27 14:40:34 +00:00
func ( k Keeper ) SetCDP ( ctx sdk . Context , cdp types . CDP ) error {
2020-01-12 15:35:34 +00:00
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
2020-08-21 19:42:46 +00:00
db , found := k . GetCollateralTypePrefix ( ctx , cdp . Type )
2020-04-27 14:40:34 +00:00
if ! found {
2020-04-30 15:33:10 +00:00
return sdkerrors . Wrapf ( types . ErrDenomPrefixNotFound , "%s" , cdp . Collateral . Denom )
2020-04-27 14:40:34 +00:00
}
2020-01-12 15:35:34 +00:00
bz := k . cdc . MustMarshalBinaryLengthPrefixed ( cdp )
store . Set ( types . CdpKey ( db , cdp . ID ) , bz )
2020-04-27 14:40:34 +00:00
return nil
2020-01-12 15:35:34 +00:00
}
// DeleteCDP deletes a cdp from the store
2020-04-27 14:40:34 +00:00
func ( k Keeper ) DeleteCDP ( ctx sdk . Context , cdp types . CDP ) error {
2020-01-12 15:35:34 +00:00
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
2020-08-21 19:42:46 +00:00
db , found := k . GetCollateralTypePrefix ( ctx , cdp . Type )
2020-04-27 14:40:34 +00:00
if ! found {
2020-04-30 15:33:10 +00:00
return sdkerrors . Wrapf ( types . ErrDenomPrefixNotFound , "%s" , cdp . Collateral . Denom )
2020-04-27 14:40:34 +00:00
}
2020-01-12 15:35:34 +00:00
store . Delete ( types . CdpKey ( db , cdp . ID ) )
2020-04-27 14:40:34 +00:00
return nil
2020-01-12 15:35:34 +00:00
}
// GetAllCdps returns all cdps from the store
func ( k Keeper ) GetAllCdps ( ctx sdk . Context ) ( cdps types . CDPs ) {
k . IterateAllCdps ( ctx , func ( cdp types . CDP ) bool {
cdps = append ( cdps , cdp )
return false
} )
return
}
2020-08-21 19:42:46 +00:00
// GetAllCdpsByCollateralType returns all cdps of a particular collateral type from the store
func ( k Keeper ) GetAllCdpsByCollateralType ( ctx sdk . Context , collateralType string ) ( cdps types . CDPs ) {
k . IterateCdpsByCollateralType ( ctx , collateralType , func ( cdp types . CDP ) bool {
2020-01-12 15:35:34 +00:00
cdps = append ( cdps , cdp )
return false
} )
return
}
2020-08-21 19:42:46 +00:00
// GetAllCdpsByCollateralTypeAndRatio returns all cdps of a particular collateral type and below a certain collateralization ratio
func ( k Keeper ) GetAllCdpsByCollateralTypeAndRatio ( ctx sdk . Context , collateralType string , targetRatio sdk . Dec ) ( cdps types . CDPs ) {
k . IterateCdpsByCollateralRatio ( ctx , collateralType , targetRatio , func ( cdp types . CDP ) bool {
2020-01-12 15:35:34 +00:00
cdps = append ( cdps , cdp )
return false
} )
return
}
// SetNextCdpID sets the highest cdp id in the store
func ( k Keeper ) SetNextCdpID ( ctx sdk . Context , id uint64 ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpIDKey )
store . Set ( [ ] byte { } , types . GetCdpIDBytes ( id ) )
}
// GetNextCdpID returns the highest cdp id from the store
func ( k Keeper ) GetNextCdpID ( ctx sdk . Context ) ( id uint64 ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpIDKey )
bz := store . Get ( [ ] byte { } )
if bz == nil {
panic ( "starting cdp id not set in genesis" )
}
id = types . GetCdpIDFromBytes ( bz )
return
}
// IndexCdpByOwner sets the cdp id in the store, indexed by the owner
func ( k Keeper ) IndexCdpByOwner ( ctx sdk . Context , cdp types . CDP ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpIDKeyPrefix )
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , cdp . Owner )
if ! found {
idBytes := k . cdc . MustMarshalBinaryLengthPrefixed ( [ ] uint64 { cdp . ID } )
store . Set ( cdp . Owner , idBytes )
return
}
2020-04-29 14:10:41 +00:00
cdpIDs = append ( cdpIDs , cdp . ID )
2020-04-29 22:02:31 +00:00
sort . Slice ( cdpIDs , func ( i , j int ) bool { return cdpIDs [ i ] < cdpIDs [ j ] } )
2020-04-29 14:10:41 +00:00
store . Set ( cdp . Owner , k . cdc . MustMarshalBinaryLengthPrefixed ( cdpIDs ) )
2020-01-12 15:35:34 +00:00
}
// RemoveCdpOwnerIndex deletes the cdp id from the store's index of cdps by owner
func ( k Keeper ) RemoveCdpOwnerIndex ( ctx sdk . Context , cdp types . CDP ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpIDKeyPrefix )
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , cdp . Owner )
if ! found {
return
}
updatedCdpIds := [ ] uint64 { }
for _ , id := range cdpIDs {
if id != cdp . ID {
updatedCdpIds = append ( updatedCdpIds , id )
}
}
if len ( updatedCdpIds ) == 0 {
store . Delete ( cdp . Owner )
2020-04-29 14:10:41 +00:00
return
2020-01-12 15:35:34 +00:00
}
store . Set ( cdp . Owner , k . cdc . MustMarshalBinaryLengthPrefixed ( updatedCdpIds ) )
}
// IndexCdpByCollateralRatio sets the cdp id in the store, indexed by the collateral type and collateral to debt ratio
2020-08-21 19:42:46 +00:00
func ( k Keeper ) IndexCdpByCollateralRatio ( ctx sdk . Context , collateralType string , id uint64 , collateralRatio sdk . Dec ) {
2020-01-12 15:35:34 +00:00
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CollateralRatioIndexPrefix )
2020-08-21 19:42:46 +00:00
db , found := k . GetCollateralTypePrefix ( ctx , collateralType )
2020-06-04 12:58:39 +00:00
if ! found {
2020-08-21 19:42:46 +00:00
panic ( fmt . Sprintf ( "denom %s prefix not found" , collateralType ) )
2020-06-04 12:58:39 +00:00
}
2020-01-12 15:35:34 +00:00
store . Set ( types . CollateralRatioKey ( db , id , collateralRatio ) , types . GetCdpIDBytes ( id ) )
}
// RemoveCdpCollateralRatioIndex deletes the cdp id from the store's index of cdps by collateral type and collateral to debt ratio
2020-08-21 19:42:46 +00:00
func ( k Keeper ) RemoveCdpCollateralRatioIndex ( ctx sdk . Context , collateralType string , id uint64 , collateralRatio sdk . Dec ) {
2020-01-12 15:35:34 +00:00
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CollateralRatioIndexPrefix )
2020-08-21 19:42:46 +00:00
db , found := k . GetCollateralTypePrefix ( ctx , collateralType )
2020-06-04 12:58:39 +00:00
if ! found {
2020-08-21 19:42:46 +00:00
panic ( fmt . Sprintf ( "denom %s prefix not found" , collateralType ) )
2020-06-04 12:58:39 +00:00
}
2020-01-12 15:35:34 +00:00
store . Delete ( types . CollateralRatioKey ( db , id , collateralRatio ) )
}
// GetDebtDenom returns the denom of debt in the system
func ( k Keeper ) GetDebtDenom ( ctx sdk . Context ) ( denom string ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . DebtDenomKey )
bz := store . Get ( [ ] byte { } )
k . cdc . MustUnmarshalBinaryLengthPrefixed ( bz , & denom )
return
}
2020-06-17 18:40:44 +00:00
// GetGovDenom returns the denom of the governance token
2020-01-15 14:19:33 +00:00
func ( k Keeper ) GetGovDenom ( ctx sdk . Context ) ( denom string ) {
2020-01-24 11:39:09 +00:00
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . GovDenomKey )
2020-01-15 14:19:33 +00:00
bz := store . Get ( [ ] byte { } )
k . cdc . MustUnmarshalBinaryLengthPrefixed ( bz , & denom )
return
}
2020-01-12 15:35:34 +00:00
// SetDebtDenom set the denom of debt in the system
func ( k Keeper ) SetDebtDenom ( ctx sdk . Context , denom string ) {
if denom == "" {
panic ( "debt denom not set in genesis" )
}
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . DebtDenomKey )
store . Set ( [ ] byte { } , k . cdc . MustMarshalBinaryLengthPrefixed ( denom ) )
}
2020-01-15 14:19:33 +00:00
// SetGovDenom set the denom of the governance token in the system
func ( k Keeper ) SetGovDenom ( ctx sdk . Context , denom string ) {
if denom == "" {
panic ( "gov denom not set in genesis" )
}
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . GovDenomKey )
store . Set ( [ ] byte { } , k . cdc . MustMarshalBinaryLengthPrefixed ( denom ) )
}
2020-01-12 15:35:34 +00:00
// ValidateCollateral validates that a collateral is valid for use in cdps
2020-08-21 19:42:46 +00:00
func ( k Keeper ) ValidateCollateral ( ctx sdk . Context , collateral sdk . Coin , collateralType string ) error {
cp , found := k . GetCollateral ( ctx , collateralType )
2020-01-12 15:35:34 +00:00
if ! found {
2020-04-27 14:40:34 +00:00
return sdkerrors . Wrap ( types . ErrCollateralNotSupported , collateral . Denom )
2020-01-12 15:35:34 +00:00
}
2020-08-27 11:21:35 +00:00
if cp . Denom != collateral . Denom {
return sdkerrors . Wrapf ( types . ErrInvalidCollateral , "collateral type: %s expected denom: %s got: %s" , collateralType , cp . Denom , collateral . Denom )
}
2020-05-24 13:42:46 +00:00
ok := k . GetMarketStatus ( ctx , cp . SpotMarketID )
if ! ok {
return sdkerrors . Wrap ( types . ErrPricefeedDown , collateral . Denom )
}
ok = k . GetMarketStatus ( ctx , cp . LiquidationMarketID )
2020-05-13 14:19:19 +00:00
if ! ok {
return sdkerrors . Wrap ( types . ErrPricefeedDown , collateral . Denom )
}
2020-01-12 15:35:34 +00:00
return nil
}
// ValidatePrincipalAdd validates that an asset is valid for use as debt when creating a new cdp
2020-04-27 14:40:34 +00:00
func ( k Keeper ) ValidatePrincipalAdd ( ctx sdk . Context , principal sdk . Coin ) error {
dp , found := k . GetDebtParam ( ctx , principal . Denom )
if ! found {
return sdkerrors . Wrap ( types . ErrDebtNotSupported , principal . Denom )
}
if principal . Amount . LT ( dp . DebtFloor ) {
return sdkerrors . Wrapf ( types . ErrBelowDebtFloor , "proposed %s < minimum %s" , principal , dp . DebtFloor )
2020-01-12 15:35:34 +00:00
}
return nil
}
// ValidatePrincipalDraw validates that an asset is valid for use as debt when drawing debt off an existing cdp
2020-04-27 14:40:34 +00:00
func ( k Keeper ) ValidatePrincipalDraw ( ctx sdk . Context , principal sdk . Coin , expectedDenom string ) error {
if principal . Denom != expectedDenom {
return sdkerrors . Wrapf ( types . ErrInvalidDebtRequest , "proposed %s, expected %s" , principal . Denom , expectedDenom )
}
_ , found := k . GetDebtParam ( ctx , principal . Denom )
if ! found {
return sdkerrors . Wrap ( types . ErrDebtNotSupported , principal . Denom )
2020-01-17 12:24:43 +00:00
}
return nil
}
2020-04-15 17:50:14 +00:00
// ValidateDebtLimit validates that the input debt amount does not exceed the global debt limit or the debt limit for that collateral
2020-08-21 19:42:46 +00:00
func ( k Keeper ) ValidateDebtLimit ( ctx sdk . Context , collateralType string , principal sdk . Coin ) error {
cp , found := k . GetCollateral ( ctx , collateralType )
2020-04-15 17:50:14 +00:00
if ! found {
2020-08-21 19:42:46 +00:00
return sdkerrors . Wrap ( types . ErrCollateralNotSupported , collateralType )
2020-04-15 17:50:14 +00:00
}
2020-08-21 19:42:46 +00:00
totalPrincipal := k . GetTotalPrincipal ( ctx , collateralType , principal . Denom ) . Add ( principal . Amount )
2020-04-27 14:40:34 +00:00
collateralLimit := cp . DebtLimit . Amount
if totalPrincipal . GT ( collateralLimit ) {
return sdkerrors . Wrapf ( types . ErrExceedsDebtLimit , "debt increase %s > collateral debt limit %s" , sdk . NewCoins ( sdk . NewCoin ( principal . Denom , totalPrincipal ) ) , sdk . NewCoins ( sdk . NewCoin ( principal . Denom , collateralLimit ) ) )
}
globalLimit := k . GetParams ( ctx ) . GlobalDebtLimit . Amount
if totalPrincipal . GT ( globalLimit ) {
return sdkerrors . Wrapf ( types . ErrExceedsDebtLimit , "debt increase %s > global debt limit %s" , sdk . NewCoin ( principal . Denom , totalPrincipal ) , sdk . NewCoin ( principal . Denom , globalLimit ) )
2020-01-12 15:35:34 +00:00
}
return nil
}
// ValidateCollateralizationRatio validate that adding the input principal doesn't put the cdp below the liquidation ratio
2020-08-21 19:42:46 +00:00
func ( k Keeper ) ValidateCollateralizationRatio ( 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 )
2020-01-12 15:35:34 +00:00
if err != nil {
return err
}
2020-08-21 19:42:46 +00:00
liquidationRatio := k . getLiquidationRatio ( ctx , collateralType )
2020-01-12 15:35:34 +00:00
if collateralizationRatio . LT ( liquidationRatio ) {
2020-04-27 14:40:34 +00:00
return sdkerrors . Wrapf ( types . ErrInvalidCollateralRatio , "collateral %s, collateral ratio %s, liquidation ratio %s" , collateral . Denom , collateralizationRatio , liquidationRatio )
2020-01-12 15:35:34 +00:00
}
return nil
}
// CalculateCollateralToDebtRatio returns the collateral to debt ratio of the input collateral and debt amounts
2020-08-21 19:42:46 +00:00
func ( k Keeper ) CalculateCollateralToDebtRatio ( ctx sdk . Context , collateral sdk . Coin , collateralType string , debt sdk . Coin ) sdk . Dec {
2020-04-27 14:40:34 +00:00
debtTotal := k . convertDebtToBaseUnits ( ctx , debt )
2020-01-12 15:35:34 +00:00
if debtTotal . IsZero ( ) || debtTotal . GTE ( types . MaxSortableDec ) {
return types . MaxSortableDec . Sub ( sdk . SmallestDec ( ) )
}
2020-08-21 19:42:46 +00:00
collateralBaseUnits := k . convertCollateralToBaseUnits ( ctx , collateral , collateralType )
2020-01-12 15:35:34 +00:00
return collateralBaseUnits . Quo ( debtTotal )
}
2020-01-28 17:47:08 +00:00
// LoadAugmentedCDP creates a new augmented CDP from an existing CDP
2020-05-13 14:19:19 +00:00
func ( k Keeper ) LoadAugmentedCDP ( ctx sdk . Context , cdp types . CDP ) types . AugmentedCDP {
2020-01-28 17:47:08 +00:00
// calculate collateralization ratio
2020-08-21 19:42:46 +00:00
collateralizationRatio , err := k . CalculateCollateralizationRatio ( ctx , cdp . Collateral , cdp . Type , cdp . Principal , cdp . AccumulatedFees , liquidation )
2020-01-28 17:47:08 +00:00
if err != nil {
2020-05-13 14:19:19 +00:00
return types . AugmentedCDP { CDP : cdp }
2020-01-28 17:47:08 +00:00
}
// convert collateral value to debt coin
2020-07-10 13:16:05 +00:00
totalDebt := cdp . GetTotalPrincipal ( ) . Amount
2020-06-03 22:36:58 +00:00
collateralValueInDebtDenom := sdk . NewDecFromInt ( totalDebt ) . Mul ( collateralizationRatio )
collateralValueInDebt := sdk . NewCoin ( cdp . Principal . Denom , collateralValueInDebtDenom . RoundInt ( ) )
2020-01-28 17:47:08 +00:00
// create new augmuented cdp
augmentedCDP := types . NewAugmentedCDP ( cdp , collateralValueInDebt , collateralizationRatio )
2020-05-13 14:19:19 +00:00
return augmentedCDP
2020-01-28 17:47:08 +00:00
}
2020-01-12 15:35:34 +00:00
// CalculateCollateralizationRatio returns the collateralization ratio of the input collateral to the input debt plus fees
2020-08-21 19:42:46 +00:00
func ( k Keeper ) CalculateCollateralizationRatio ( ctx sdk . Context , collateral sdk . Coin , collateralType string , principal sdk . Coin , fees sdk . Coin , pfType pricefeedType ) ( sdk . Dec , error ) {
2020-01-22 16:56:30 +00:00
if collateral . IsZero ( ) {
return sdk . ZeroDec ( ) , nil
}
2020-05-24 13:42:46 +00:00
var marketID string
switch pfType {
case spot :
2020-08-21 19:42:46 +00:00
marketID = k . getSpotMarketID ( ctx , collateralType )
2020-05-24 13:42:46 +00:00
case liquidation :
2020-08-21 19:42:46 +00:00
marketID = k . getliquidationMarketID ( ctx , collateralType )
2020-05-24 13:42:46 +00:00
default :
return sdk . Dec { } , pfType . IsValid ( )
}
2020-01-12 15:35:34 +00:00
price , err := k . pricefeedKeeper . GetCurrentPrice ( ctx , marketID )
if err != nil {
return sdk . Dec { } , err
}
2020-08-21 19:42:46 +00:00
collateralBaseUnits := k . convertCollateralToBaseUnits ( ctx , collateral , collateralType )
2020-01-12 15:35:34 +00:00
collateralValue := collateralBaseUnits . Mul ( price . Price )
2020-01-28 17:47:08 +00:00
2020-04-27 14:40:34 +00:00
prinicpalBaseUnits := k . convertDebtToBaseUnits ( ctx , principal )
principalTotal := prinicpalBaseUnits
feeBaseUnits := k . convertDebtToBaseUnits ( ctx , fees )
principalTotal = principalTotal . Add ( feeBaseUnits )
2020-01-12 15:35:34 +00:00
collateralRatio := collateralValue . Quo ( principalTotal )
return collateralRatio , nil
}
2020-01-28 17:47:08 +00:00
// CalculateCollateralizationRatioFromAbsoluteRatio takes a coin's denom and an absolute ratio and returns the respective collateralization ratio
2020-08-21 19:42:46 +00:00
func ( k Keeper ) CalculateCollateralizationRatioFromAbsoluteRatio ( ctx sdk . Context , collateralType string , absoluteRatio sdk . Dec , pfType pricefeedType ) ( sdk . Dec , error ) {
2020-05-24 13:42:46 +00:00
// get price of collateral
var marketID string
switch pfType {
case spot :
2020-08-21 19:42:46 +00:00
marketID = k . getSpotMarketID ( ctx , collateralType )
2020-05-24 13:42:46 +00:00
case liquidation :
2020-08-21 19:42:46 +00:00
marketID = k . getliquidationMarketID ( ctx , collateralType )
2020-05-24 13:42:46 +00:00
default :
return sdk . Dec { } , pfType . IsValid ( )
}
2020-01-28 17:47:08 +00:00
price , err := k . pricefeedKeeper . GetCurrentPrice ( ctx , marketID )
if err != nil {
return sdk . Dec { } , err
}
// convert absolute ratio to collateralization ratio
respectiveCollateralRatio := absoluteRatio . Quo ( price . Price )
return respectiveCollateralRatio , nil
}
2020-05-13 14:19:19 +00:00
// SetMarketStatus sets the status of the input market, true means the market is up and running, false means it is down
func ( k Keeper ) SetMarketStatus ( ctx sdk . Context , marketID string , up bool ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . PricefeedStatusKeyPrefix )
store . Set ( [ ] byte ( marketID ) , k . cdc . MustMarshalBinaryBare ( up ) )
return
}
// GetMarketStatus returns true if the market has a price, otherwise false
func ( k Keeper ) GetMarketStatus ( ctx sdk . Context , marketID string ) ( up bool ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . PricefeedStatusKeyPrefix )
bz := store . Get ( [ ] byte ( marketID ) )
k . cdc . MustUnmarshalBinaryBare ( bz , & up )
return up
}
// UpdatePricefeedStatus determines if the price of an asset is available and updates the global status of the market
func ( k Keeper ) UpdatePricefeedStatus ( ctx sdk . Context , marketID string ) ( ok bool ) {
_ , err := k . pricefeedKeeper . GetCurrentPrice ( ctx , marketID )
if err != nil {
k . SetMarketStatus ( ctx , marketID , false )
return false
}
k . SetMarketStatus ( ctx , marketID , true )
return true
}
2020-01-12 15:35:34 +00:00
// converts the input collateral to base units (ie multiplies the input by 10^(-ConversionFactor))
2020-08-21 19:42:46 +00:00
func ( k Keeper ) convertCollateralToBaseUnits ( ctx sdk . Context , collateral sdk . Coin , collateralType string ) ( baseUnits sdk . Dec ) {
cp , _ := k . GetCollateral ( ctx , collateralType )
2020-01-12 15:35:34 +00:00
return sdk . NewDecFromInt ( collateral . Amount ) . Mul ( sdk . NewDecFromIntWithPrec ( sdk . OneInt ( ) , cp . ConversionFactor . Int64 ( ) ) )
}
// converts the input debt to base units (ie multiplies the input by 10^(-ConversionFactor))
func ( k Keeper ) convertDebtToBaseUnits ( ctx sdk . Context , debt sdk . Coin ) ( baseUnits sdk . Dec ) {
2020-01-22 16:50:27 +00:00
dp , _ := k . GetDebtParam ( ctx , debt . Denom )
2020-01-12 15:35:34 +00:00
return sdk . NewDecFromInt ( debt . Amount ) . Mul ( sdk . NewDecFromIntWithPrec ( sdk . OneInt ( ) , dp . ConversionFactor . Int64 ( ) ) )
}
2020-05-24 13:42:46 +00:00
type pricefeedType string
const (
spot pricefeedType = "spot"
liquidation = "liquidation"
)
func ( pft pricefeedType ) IsValid ( ) error {
switch pft {
case spot , liquidation :
return nil
}
2020-08-21 19:42:46 +00:00
return fmt . Errorf ( "invalid pricefeed type: %s" , pft )
2020-05-24 13:42:46 +00:00
}