0g-chain/x/cdp/keeper/fees.go
Federico Kunze a573625df8
[R4R] bump SDK version to v0.38.3 (#421)
* bump SDK version to v0.38.3

Co-authored-by: Denali Marsh <denali@kava.io>
Co-authored-by: Kevin Davis <kjydavis3@gmail.com>
Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>
Co-authored-by: denalimarsh <denalimarsh@gmail.com>
Co-authored-by: rhuairahrighairigh <ruaridh.odonnell@gmail.com>
2020-04-23 12:35:58 -04:00

129 lines
5.9 KiB
Go

package keeper
import (
"time"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/cdp/types"
)
// CalculateFees returns the fees accumulated since fees were last calculated based on
// the input amount of outstanding debt (principal) and the number of periods (seconds) that have passed
func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coins, periods sdk.Int, denom string) sdk.Coins {
newFees := sdk.NewCoins()
for _, pc := range principal {
// how fees are calculated:
// feesAccumulated = (outstandingDebt * (feeRate^periods)) - outstandingDebt
// Note that since we can't do x^y using sdk.Decimal, we are converting to int and using RelativePow
feePerSecond := k.getFeeRate(ctx, denom)
scalar := sdk.NewInt(1000000000000000000)
feeRateInt := feePerSecond.Mul(sdk.NewDecFromInt(scalar)).TruncateInt()
accumulator := sdk.NewDecFromInt(types.RelativePow(feeRateInt, periods, scalar)).Mul(sdk.SmallestDec())
feesAccumulated := (sdk.NewDecFromInt(pc.Amount).Mul(accumulator)).Sub(sdk.NewDecFromInt(pc.Amount))
// TODO this will always round down, causing precision loss between the sum of all fees in CDPs and surplus coins in liquidator account
newFees = newFees.Add(sdk.NewCoin(pc.Denom, feesAccumulated.TruncateInt()))
}
return newFees
}
// UpdateFeesForRiskyCdps calculates fees for risky CDPs
// The overall logic is first select the CDPs with 10% of the liquidation ratio
// Then we call calculate fees on each of those CDPs
// Next we store the result of the fees in the cdp.AccumulatedFees field
// Finally we set the cdp.FeesUpdated time to the current block time (ctx.BlockTime()) since that
// is when we made the update
func (k Keeper) UpdateFeesForRiskyCdps(ctx sdk.Context, collateralDenom string, marketID string) error {
price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID)
if err != nil {
return err
}
liquidationRatio := k.getLiquidationRatio(ctx, collateralDenom)
priceDivLiqRatio := price.Price.Quo(liquidationRatio)
if priceDivLiqRatio.IsZero() {
priceDivLiqRatio = sdk.SmallestDec()
}
// NOTE - we have a fixed cutoff at 110% - this may or may not be changed in the future
normalizedRatio := sdk.OneDec().Quo(priceDivLiqRatio).Mul(sdk.MustNewDecFromStr("1.1"))
// now iterate over all the cdps based on collateral ratio
k.IterateCdpsByCollateralRatio(ctx, collateralDenom, normalizedRatio, func(cdp types.CDP) bool {
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
// get the number of periods
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
// now calculate and store additional fees
additionalFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralDenom)
// now add the additional fees to the accumulated fees for the cdp
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(additionalFees...)
// and set the fees updated time to the current block time since we just updated it
cdp.FeesUpdated = ctx.BlockTime()
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio)
k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio)
return false // this returns true when you want to stop iterating. Since we want to iterate through all we return false
})
return nil
}
// IncrementTotalPrincipal increments the total amount of debt that has been drawn with that collateral type
func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralDenom string, principal sdk.Coins) {
for _, pc := range principal {
total := k.GetTotalPrincipal(ctx, collateralDenom, pc.Denom)
total = total.Add(pc.Amount)
k.SetTotalPrincipal(ctx, collateralDenom, pc.Denom, total)
}
}
// DecrementTotalPrincipal decrements the total amount of debt that has been drawn for a particular collateral type
func (k Keeper) DecrementTotalPrincipal(ctx sdk.Context, collateralDenom string, principal sdk.Coins) {
for _, pc := range principal {
total := k.GetTotalPrincipal(ctx, collateralDenom, pc.Denom)
total = total.Sub(pc.Amount)
if total.IsNegative() {
// can happen in tests due to rounding errors in fee calculation
total = sdk.ZeroInt()
}
k.SetTotalPrincipal(ctx, collateralDenom, pc.Denom, total)
}
}
// GetTotalPrincipal returns the total amount of principal that has been drawn for a particular collateral
func (k Keeper) GetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string) (total sdk.Int) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PrincipalKeyPrefix)
bz := store.Get([]byte(collateralDenom + principalDenom))
if bz == nil {
k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, sdk.ZeroInt())
return sdk.ZeroInt()
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &total)
return total
}
// SetTotalPrincipal sets the total amount of principal that has been drawn for the input collateral
func (k Keeper) SetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string, total sdk.Int) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PrincipalKeyPrefix)
store.Set([]byte(collateralDenom+principalDenom), k.cdc.MustMarshalBinaryLengthPrefixed(total))
}
// GetPreviousBlockTime get the blocktime for the previous block
func (k Keeper) GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time, found bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
b := store.Get([]byte{})
if b == nil {
return time.Time{}, false
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &blockTime)
return blockTime, true
}
// SetPreviousBlockTime set the time of the previous block
func (k Keeper) SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time) {
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
store.Set([]byte{}, k.cdc.MustMarshalBinaryLengthPrefixed(blockTime))
}