2020-01-12 15:35:34 +00:00
package keeper
import (
"bytes"
"fmt"
"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-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-04-23 16:35:58 +00:00
func ( k Keeper ) AddCdp ( ctx sdk . Context , owner sdk . AccAddress , collateral sdk . Coins , principal sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
// validation
err := k . ValidateCollateral ( ctx , collateral )
if err != nil {
return err
}
_ , found := k . GetCdpByOwnerAndDenom ( ctx , owner , collateral [ 0 ] . Denom )
if found {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrCdpAlreadyExists , "owner %s, denom %s" , owner , collateral [ 0 ] . 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
err = k . ValidateDebtLimit ( ctx , collateral [ 0 ] . Denom , principal )
if err != nil {
return err
}
2020-01-12 15:35:34 +00:00
err = k . ValidateCollateralizationRatio ( ctx , collateral , principal , sdk . NewCoins ( ) )
if err != nil {
return err
}
// send coins from the owners account to the cdp module
id := k . GetNextCdpID ( ctx )
cdp := types . NewCDP ( id , owner , collateral , principal , ctx . BlockHeader ( ) . Time )
deposit := types . NewDeposit ( cdp . ID , owner , collateral )
err = k . supplyKeeper . SendCoinsFromAccountToModule ( ctx , owner , types . ModuleName , collateral )
if err != nil {
return err
}
// mint the principal and send to the owners account
err = k . supplyKeeper . MintCoins ( ctx , types . ModuleName , principal )
if err != nil {
panic ( err )
}
err = k . supplyKeeper . SendCoinsFromModuleToAccount ( ctx , types . ModuleName , owner , principal )
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 )
}
// 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 ) ) ,
) ,
)
// update total principal for input collateral type
k . IncrementTotalPrincipal ( ctx , collateral [ 0 ] . Denom , principal )
// set the cdp, deposit, and indexes in the store
collateralToDebtRatio := k . CalculateCollateralToDebtRatio ( ctx , collateral , principal )
k . SetCdpAndCollateralRatioIndex ( ctx , cdp , collateralToDebtRatio )
k . IndexCdpByOwner ( ctx , cdp )
k . SetDeposit ( ctx , deposit )
k . SetNextCdpID ( ctx , id + 1 )
return nil
}
// SetCdpAndCollateralRatioIndex sets the cdp and collateral ratio index in the store
func ( k Keeper ) SetCdpAndCollateralRatioIndex ( ctx sdk . Context , cdp types . CDP , ratio sdk . Dec ) {
k . SetCDP ( ctx , cdp )
k . IndexCdpByCollateralRatio ( ctx , cdp . Collateral [ 0 ] . Denom , cdp . ID , ratio )
}
// MintDebtCoins mints debt coins in the cdp module account
2020-04-23 16:35:58 +00:00
func ( k Keeper ) MintDebtCoins ( ctx sdk . Context , moduleAccount string , denom string , principalCoins sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
coinsToMint := sdk . NewCoins ( )
for _ , sc := range principalCoins {
2020-04-23 16:35:58 +00:00
coinsToMint = coinsToMint . Add ( sdk . NewCoin ( denom , sc . Amount ) )
2020-01-12 15:35:34 +00:00
}
err := k . supplyKeeper . MintCoins ( ctx , moduleAccount , coinsToMint )
if err != nil {
return err
}
return nil
}
2020-01-30 23:50:42 +00:00
// BurnDebtCoins burns debt coins from the cdp module account
2020-04-23 16:35:58 +00:00
func ( k Keeper ) BurnDebtCoins ( ctx sdk . Context , moduleAccount string , denom string , paymentCoins sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
coinsToBurn := sdk . NewCoins ( )
for _ , pc := range paymentCoins {
2020-04-23 16:35:58 +00:00
coinsToBurn = coinsToBurn . Add ( sdk . NewCoin ( denom , pc . Amount ) )
2020-01-12 15:35:34 +00:00
}
err := k . supplyKeeper . BurnCoins ( ctx , moduleAccount , coinsToBurn )
if err != nil {
return err
}
return nil
}
// GetCdpID returns the id of the cdp corresponding to a specific owner and collateral denom
func ( k Keeper ) GetCdpID ( ctx sdk . Context , owner sdk . AccAddress , denom string ) ( uint64 , bool ) {
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , owner )
if ! found {
return 0 , false
}
for _ , id := range cdpIDs {
_ , found = k . GetCDP ( ctx , denom , id )
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 )
// TODO figure out why this is necessary
if bz == nil || bytes . Equal ( bz , [ ] byte { 0 } ) {
return [ ] uint64 { } , false
}
var cdpIDs [ ] uint64
k . cdc . MustUnmarshalBinaryLengthPrefixed ( bz , & cdpIDs )
return cdpIDs , true
}
// GetCdpByOwnerAndDenom queries cdps owned by owner and returns the cdp with matching denom
func ( k Keeper ) GetCdpByOwnerAndDenom ( ctx sdk . Context , owner sdk . AccAddress , denom string ) ( types . CDP , bool ) {
cdpIDs , found := k . GetCdpIdsByOwner ( ctx , owner )
if ! found {
return types . CDP { } , false
}
for _ , id := range cdpIDs {
cdp , found := k . GetCDP ( ctx , denom , id )
if found {
return cdp , true
}
}
return types . CDP { } , false
}
// GetCDP returns the cdp associated with a particular collateral denom and id
func ( k Keeper ) GetCDP ( ctx sdk . Context , collateralDenom string , cdpID uint64 ) ( types . CDP , bool ) {
// get store
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
db , _ := k . GetDenomPrefix ( ctx , collateralDenom )
// 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
func ( k Keeper ) SetCDP ( ctx sdk . Context , cdp types . CDP ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
db , _ := k . GetDenomPrefix ( ctx , cdp . Collateral [ 0 ] . Denom )
bz := k . cdc . MustMarshalBinaryLengthPrefixed ( cdp )
store . Set ( types . CdpKey ( db , cdp . ID ) , bz )
return
}
// DeleteCDP deletes a cdp from the store
func ( k Keeper ) DeleteCDP ( ctx sdk . Context , cdp types . CDP ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CdpKeyPrefix )
db , _ := k . GetDenomPrefix ( ctx , cdp . Collateral [ 0 ] . Denom )
store . Delete ( types . CdpKey ( db , cdp . ID ) )
}
// 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
}
// GetAllCdpsByDenom returns all cdps of a particular collateral type from the store
func ( k Keeper ) GetAllCdpsByDenom ( ctx sdk . Context , denom string ) ( cdps types . CDPs ) {
k . IterateCdpsByDenom ( ctx , denom , func ( cdp types . CDP ) bool {
cdps = append ( cdps , cdp )
return false
} )
return
}
// GetAllCdpsByDenomAndRatio returns all cdps of a particular collateral type and below a certain collateralization ratio
func ( k Keeper ) GetAllCdpsByDenomAndRatio ( ctx sdk . Context , denom string , targetRatio sdk . Dec ) ( cdps types . CDPs ) {
k . IterateCdpsByCollateralRatio ( ctx , denom , targetRatio , func ( cdp types . CDP ) bool {
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
}
for _ , id := range cdpIDs {
if id == cdp . ID {
return
}
cdpIDs = append ( cdpIDs , cdp . ID )
store . Set ( cdp . Owner , k . cdc . MustMarshalBinaryLengthPrefixed ( cdpIDs ) )
}
}
// 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 )
}
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
func ( k Keeper ) IndexCdpByCollateralRatio ( ctx sdk . Context , denom string , id uint64 , collateralRatio sdk . Dec ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CollateralRatioIndexPrefix )
db , _ := k . GetDenomPrefix ( ctx , denom )
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
func ( k Keeper ) RemoveCdpCollateralRatioIndex ( ctx sdk . Context , denom string , id uint64 , collateralRatio sdk . Dec ) {
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . CollateralRatioIndexPrefix )
db , _ := k . GetDenomPrefix ( ctx , denom )
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-01-15 14:19:33 +00:00
// GetGovDenom returns the denom of debt in the system
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 ) )
return
}
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 ) )
return
}
2020-01-12 15:35:34 +00:00
// ValidateCollateral validates that a collateral is valid for use in cdps
2020-04-23 16:35:58 +00:00
func ( k Keeper ) ValidateCollateral ( ctx sdk . Context , collateral sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
if len ( collateral ) != 1 {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrInvalidCollateralLength , "%d" , len ( collateral ) )
2020-01-12 15:35:34 +00:00
}
_ , found := k . GetCollateral ( ctx , collateral [ 0 ] . Denom )
if ! found {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrap ( types . ErrCollateralNotSupported , collateral [ 0 ] . 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-23 16:35:58 +00:00
func ( k Keeper ) ValidatePrincipalAdd ( ctx sdk . Context , principal sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
for _ , dc := range principal {
2020-01-22 16:50:27 +00:00
dp , found := k . GetDebtParam ( ctx , dc . Denom )
2020-01-12 15:35:34 +00:00
if ! found {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrap ( types . ErrDebtNotSupported , dc . Denom )
2020-01-12 15:35:34 +00:00
}
if dc . Amount . LT ( dp . DebtFloor ) {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrBelowDebtFloor , "proposed %s < minimum %s" , dc , 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-23 16:35:58 +00:00
func ( k Keeper ) ValidatePrincipalDraw ( ctx sdk . Context , principal sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
for _ , dc := range principal {
2020-01-22 16:50:27 +00:00
_ , found := k . GetDebtParam ( ctx , dc . Denom )
2020-01-12 15:35:34 +00:00
if ! found {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrap ( types . ErrDebtNotSupported , dc . Denom )
2020-01-12 15:35:34 +00:00
}
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-04-23 16:35:58 +00:00
func ( k Keeper ) ValidateDebtLimit ( ctx sdk . Context , collateralDenom string , principal sdk . Coins ) error {
2020-04-15 17:50:14 +00:00
cp , found := k . GetCollateral ( ctx , collateralDenom )
if ! found {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrap ( types . ErrCollateralNotSupported , collateralDenom )
2020-04-15 17:50:14 +00:00
}
2020-04-23 16:35:58 +00:00
2020-01-17 12:24:43 +00:00
for _ , dc := range principal {
totalPrincipal := k . GetTotalPrincipal ( ctx , collateralDenom , dc . Denom ) . Add ( dc . Amount )
2020-04-15 17:50:14 +00:00
collateralLimit := cp . DebtLimit . AmountOf ( dc . Denom )
if totalPrincipal . GT ( collateralLimit ) {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrExceedsDebtLimit , "debt increase %s > collateral debt limit %s" , sdk . NewCoins ( sdk . NewCoin ( dc . Denom , totalPrincipal ) ) , sdk . NewCoins ( sdk . NewCoin ( dc . Denom , collateralLimit ) ) )
2020-04-15 17:50:14 +00:00
}
2020-01-17 12:24:43 +00:00
globalLimit := k . GetParams ( ctx ) . GlobalDebtLimit . AmountOf ( dc . Denom )
if totalPrincipal . GT ( globalLimit ) {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrExceedsDebtLimit , "debt increase %s > global debt limit %s" , sdk . NewCoin ( dc . Denom , totalPrincipal ) , sdk . NewCoin ( dc . 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-04-23 16:35:58 +00:00
func ( k Keeper ) ValidateCollateralizationRatio ( ctx sdk . Context , collateral sdk . Coins , principal sdk . Coins , fees sdk . Coins ) error {
2020-01-12 15:35:34 +00:00
//
collateralizationRatio , err := k . CalculateCollateralizationRatio ( ctx , collateral , principal , fees )
if err != nil {
return err
}
liquidationRatio := k . getLiquidationRatio ( ctx , collateral [ 0 ] . Denom )
if collateralizationRatio . LT ( liquidationRatio ) {
2020-04-23 16:35:58 +00:00
return sdkerrors . Wrapf ( types . ErrInvalidCollateralRatio , "collateral %s, collateral ratio %s, liquidation ratio %s" , collateral [ 0 ] . 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
func ( k Keeper ) CalculateCollateralToDebtRatio ( ctx sdk . Context , collateral sdk . Coins , debt sdk . Coins ) sdk . Dec {
debtTotal := sdk . ZeroDec ( )
for _ , dc := range debt {
debtBaseUnits := k . convertDebtToBaseUnits ( ctx , dc )
debtTotal = debtTotal . Add ( debtBaseUnits )
}
if debtTotal . IsZero ( ) || debtTotal . GTE ( types . MaxSortableDec ) {
return types . MaxSortableDec . Sub ( sdk . SmallestDec ( ) )
}
collateralBaseUnits := k . convertCollateralToBaseUnits ( ctx , collateral [ 0 ] )
return collateralBaseUnits . Quo ( debtTotal )
}
2020-01-28 17:47:08 +00:00
// LoadAugmentedCDP creates a new augmented CDP from an existing CDP
2020-04-23 16:35:58 +00:00
func ( k Keeper ) LoadAugmentedCDP ( ctx sdk . Context , cdp types . CDP ) ( types . AugmentedCDP , error ) {
2020-01-28 17:47:08 +00:00
// calculate additional fees
periods := sdk . NewInt ( ctx . BlockTime ( ) . Unix ( ) ) . Sub ( sdk . NewInt ( cdp . FeesUpdated . Unix ( ) ) )
2020-04-23 16:35:58 +00:00
fees := k . CalculateFees ( ctx , cdp . Principal . Add ( cdp . AccumulatedFees ... ) , periods , cdp . Collateral [ 0 ] . Denom )
totalFees := cdp . AccumulatedFees . Add ( fees ... )
2020-01-28 17:47:08 +00:00
// calculate collateralization ratio
collateralizationRatio , err := k . CalculateCollateralizationRatio ( ctx , cdp . Collateral , cdp . Principal , totalFees )
if err != nil {
return types . AugmentedCDP { } , err
}
// total debt is the sum of all oustanding principal and fees
var totalDebt int64
for _ , principalCoin := range cdp . Principal {
totalDebt += principalCoin . Amount . Int64 ( )
}
2020-04-23 16:35:58 +00:00
for _ , feeCoin := range cdp . AccumulatedFees . Add ( fees ... ) {
2020-01-28 17:47:08 +00:00
totalDebt += feeCoin . Amount . Int64 ( )
}
// convert collateral value to debt coin
debtBaseAdjusted := sdk . NewDec ( totalDebt ) . QuoInt64 ( BaseDigitFactor )
collateralValueInDebtDenom := collateralizationRatio . Mul ( debtBaseAdjusted )
collateralValueInDebt := sdk . NewInt64Coin ( cdp . Principal [ 0 ] . Denom , collateralValueInDebtDenom . Int64 ( ) )
// create new augmuented cdp
augmentedCDP := types . NewAugmentedCDP ( cdp , collateralValueInDebt , collateralizationRatio )
return augmentedCDP , nil
}
2020-01-12 15:35:34 +00:00
// CalculateCollateralizationRatio returns the collateralization ratio of the input collateral to the input debt plus fees
2020-04-23 16:35:58 +00:00
func ( k Keeper ) CalculateCollateralizationRatio ( ctx sdk . Context , collateral sdk . Coins , principal sdk . Coins , fees sdk . Coins ) ( sdk . Dec , error ) {
2020-01-22 16:56:30 +00:00
if collateral . IsZero ( ) {
return sdk . ZeroDec ( ) , nil
}
2020-01-12 15:35:34 +00:00
marketID := k . getMarketID ( ctx , collateral [ 0 ] . Denom )
price , err := k . pricefeedKeeper . GetCurrentPrice ( ctx , marketID )
if err != nil {
return sdk . Dec { } , err
}
collateralBaseUnits := k . convertCollateralToBaseUnits ( ctx , collateral [ 0 ] )
collateralValue := collateralBaseUnits . Mul ( price . Price )
2020-01-28 17:47:08 +00:00
2020-01-12 15:35:34 +00:00
principalTotal := sdk . ZeroDec ( )
for _ , pc := range principal {
prinicpalBaseUnits := k . convertDebtToBaseUnits ( ctx , pc )
principalTotal = principalTotal . Add ( prinicpalBaseUnits )
}
for _ , fc := range fees {
feeBaseUnits := k . convertDebtToBaseUnits ( ctx , fc )
principalTotal = principalTotal . Add ( feeBaseUnits )
}
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-04-23 16:35:58 +00:00
func ( k Keeper ) CalculateCollateralizationRatioFromAbsoluteRatio ( ctx sdk . Context , collateralDenom string , absoluteRatio sdk . Dec ) ( sdk . Dec , error ) {
2020-01-28 17:47:08 +00:00
// get price collateral
marketID := k . getMarketID ( ctx , collateralDenom )
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-01-12 15:35:34 +00:00
// converts the input collateral to base units (ie multiplies the input by 10^(-ConversionFactor))
func ( k Keeper ) convertCollateralToBaseUnits ( ctx sdk . Context , collateral sdk . Coin ) ( baseUnits sdk . Dec ) {
cp , _ := k . GetCollateral ( ctx , collateral . Denom )
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 ( ) ) )
}