mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 15:25:18 +00:00
[R4R] Update fees for all cdps (#449)
* update cdp fees in begin block Co-authored-by: Federico Kunze <federico.kunze94@gmail.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Denali Marsh <denali@kava.io> Co-authored-by: John Maheswaran <john@noreply> Co-authored-by: Kevin Davis <kjydavis3@gmail.com>
This commit is contained in:
parent
23e23fdaaa
commit
5737f4fa19
@ -11,23 +11,17 @@ import (
|
||||
// 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)
|
||||
if !found {
|
||||
previousBlockTime = ctx.BlockTime()
|
||||
}
|
||||
|
||||
previousDistTime, found := k.GetPreviousSavingsDistribution(ctx)
|
||||
if !found {
|
||||
previousDistTime = ctx.BlockTime()
|
||||
k.SetPreviousSavingsDistribution(ctx, previousDistTime)
|
||||
}
|
||||
blockTimeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
|
||||
for _, cp := range params.CollateralParams {
|
||||
for _, dp := range params.DebtParams {
|
||||
k.HandleNewDebt(ctx, cp.Denom, dp.Denom, blockTimeElapsed)
|
||||
}
|
||||
|
||||
// call our update fees method for the risky cdps
|
||||
err := k.UpdateFeesForRiskyCdps(ctx, cp.Denom, cp.MarketID)
|
||||
for _, cp := range params.CollateralParams {
|
||||
|
||||
err := k.UpdateFeesForAllCdps(ctx, cp.Denom)
|
||||
|
||||
// handle if an error is returned then propagate up
|
||||
if err != nil {
|
||||
ctx.EventManager().EmitEvent(
|
||||
@ -76,6 +70,5 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
|
||||
}
|
||||
k.SetPreviousSavingsDistribution(ctx, ctx.BlockTime())
|
||||
}
|
||||
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||
return
|
||||
}
|
||||
|
@ -152,8 +152,6 @@ func (suite *ModuleTestSuite) TestBeginBlock() {
|
||||
func (suite *ModuleTestSuite) TestSeizeSingleCdpWithFees() {
|
||||
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[0], cs(c("xrp", 10000000000)), cs(c("usdx", 1000000000)))
|
||||
suite.NoError(err)
|
||||
suite.keeper.SetPreviousBlockTime(suite.ctx, suite.ctx.BlockTime())
|
||||
previousBlockTime, _ := suite.keeper.GetPreviousBlockTime(suite.ctx)
|
||||
suite.Equal(i(1000000000), suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx"))
|
||||
sk := suite.app.GetSupplyKeeper()
|
||||
cdpMacc := sk.GetModuleAccount(suite.ctx, cdp.ModuleName)
|
||||
@ -167,13 +165,10 @@ func (suite *ModuleTestSuite) TestSeizeSingleCdpWithFees() {
|
||||
suite.Equal(i(1000000900), (cdpMacc.GetCoins().AmountOf("debt")))
|
||||
cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
|
||||
|
||||
timeElapsed := sdk.NewInt(suite.ctx.BlockTime().Unix() - previousBlockTime.Unix())
|
||||
|
||||
fees := suite.keeper.CalculateFees(suite.ctx, cdp.Principal, timeElapsed, "xrp")
|
||||
suite.Equal(i(928), fees.AmountOf("usdx"))
|
||||
|
||||
err = suite.keeper.SeizeCollateral(suite.ctx, cdp)
|
||||
suite.NoError(err)
|
||||
_, found := suite.keeper.GetCDP(suite.ctx, "xrp", 1)
|
||||
suite.False(found)
|
||||
}
|
||||
|
||||
func TestModuleTestSuite(t *testing.T) {
|
||||
|
@ -110,7 +110,6 @@ var (
|
||||
GovDenomKey = types.GovDenomKey
|
||||
DepositKeyPrefix = types.DepositKeyPrefix
|
||||
PrincipalKeyPrefix = types.PrincipalKeyPrefix
|
||||
PreviousBlockTimeKey = types.PreviousBlockTimeKey
|
||||
PreviousDistributionTimeKey = types.PreviousDistributionTimeKey
|
||||
KeyGlobalDebtLimit = types.KeyGlobalDebtLimit
|
||||
KeyCollateralParams = types.KeyCollateralParams
|
||||
@ -128,7 +127,6 @@ var (
|
||||
DefaultGovDenom = types.DefaultGovDenom
|
||||
DefaultSurplusThreshold = types.DefaultSurplusThreshold
|
||||
DefaultDebtThreshold = types.DefaultDebtThreshold
|
||||
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
||||
DefaultPreviousDistributionTime = types.DefaultPreviousDistributionTime
|
||||
DefaultSavingsDistributionFrequency = types.DefaultSavingsDistributionFrequency
|
||||
MaxSortableDec = types.MaxSortableDec
|
||||
|
@ -70,10 +70,6 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk PricefeedKeeper, sk SupplyKeeper,
|
||||
for _, d := range gs.Deposits {
|
||||
k.SetDeposit(ctx, d)
|
||||
}
|
||||
// only set the previous block time if it's different than default
|
||||
if !gs.PreviousBlockTime.Equal(DefaultPreviousBlockTime) {
|
||||
k.SetPreviousBlockTime(ctx, gs.PreviousBlockTime)
|
||||
}
|
||||
}
|
||||
|
||||
// ExportGenesis export genesis state for cdp module
|
||||
@ -95,14 +91,10 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
debtDenom := k.GetDebtDenom(ctx)
|
||||
govDenom := k.GetGovDenom(ctx)
|
||||
|
||||
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||
if !found {
|
||||
previousBlockTime = DefaultPreviousBlockTime
|
||||
}
|
||||
previousDistributionTime, found := k.GetPreviousSavingsDistribution(ctx)
|
||||
if !found {
|
||||
previousDistributionTime = DefaultPreviousDistributionTime
|
||||
}
|
||||
|
||||
return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousBlockTime, previousDistributionTime)
|
||||
return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousDistributionTime)
|
||||
}
|
||||
|
@ -70,7 +70,6 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
@ -153,7 +152,6 @@ func NewCDPGenStateMulti() app.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
@ -203,19 +201,16 @@ func badGenStates() []badGenState {
|
||||
g9.DebtDenom = ""
|
||||
|
||||
g10 := baseGenState()
|
||||
g10.PreviousBlockTime = time.Time{}
|
||||
g10.Params.CollateralParams[0].AuctionSize = i(-10)
|
||||
|
||||
g11 := baseGenState()
|
||||
g11.Params.CollateralParams[0].AuctionSize = i(-10)
|
||||
g11.Params.CollateralParams[0].LiquidationPenalty = d("5.0")
|
||||
|
||||
g12 := baseGenState()
|
||||
g12.Params.CollateralParams[0].LiquidationPenalty = d("5.0")
|
||||
g12.GovDenom = ""
|
||||
|
||||
g13 := baseGenState()
|
||||
g13.GovDenom = ""
|
||||
|
||||
g14 := baseGenState()
|
||||
g14.Params.DebtParams[0].SavingsRate = d("4.0")
|
||||
g13.Params.DebtParams[0].SavingsRate = d("4.0")
|
||||
|
||||
return []badGenState{
|
||||
badGenState{Genesis: g1, Reason: "duplicate collateral denom"},
|
||||
@ -226,11 +221,10 @@ func badGenStates() []badGenState {
|
||||
badGenState{Genesis: g6, Reason: "duplicate debt denom"},
|
||||
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"},
|
||||
badGenState{Genesis: g14, Reason: "invalid savings rate"},
|
||||
badGenState{Genesis: g10, Reason: "negative auction size"},
|
||||
badGenState{Genesis: g11, Reason: "invalid liquidation penalty"},
|
||||
badGenState{Genesis: g12, Reason: "gov denom not set"},
|
||||
badGenState{Genesis: g13, Reason: "invalid savings rate"},
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +274,6 @@ func baseGenState() cdp.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
}
|
||||
|
@ -40,12 +40,9 @@ func (k Keeper) DepositCollateral(ctx sdk.Context, owner sdk.AccAddress, deposit
|
||||
|
||||
k.SetDeposit(ctx, deposit)
|
||||
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom)
|
||||
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio)
|
||||
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...)
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
cdp.Collateral = cdp.Collateral.Add(collateral...)
|
||||
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
@ -71,15 +68,13 @@ func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner sdk.AccAddress, deposi
|
||||
return sdkerrors.Wrapf(types.ErrInvalidWithdrawAmount, "collateral %s, deposit %s", collateral, deposit.Amount)
|
||||
}
|
||||
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom)
|
||||
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Principal, cdp.AccumulatedFees.Add(fees...))
|
||||
collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Principal, cdp.AccumulatedFees)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
liquidationRatio := k.getLiquidationRatio(ctx, collateral[0].Denom)
|
||||
if collateralizationRatio.LT(liquidationRatio) {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "colateral %s, collateral ratio %s, liquidation ration %s", collateral[0].Denom, collateralizationRatio, liquidationRatio)
|
||||
return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "collateral %s, collateral ratio %s, liquidation ration %s", collateral[0].Denom, collateralizationRatio, liquidationRatio)
|
||||
}
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
@ -96,7 +91,6 @@ func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner sdk.AccAddress, deposi
|
||||
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio)
|
||||
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...)
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
cdp.Collateral = cdp.Collateral.Sub(collateral)
|
||||
collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
|
@ -25,11 +25,7 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string
|
||||
return err
|
||||
}
|
||||
|
||||
// fee calculation
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom)
|
||||
|
||||
err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal.Add(principal...), cdp.AccumulatedFees.Add(fees...))
|
||||
err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal.Add(principal...), cdp.AccumulatedFees)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -65,7 +61,6 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string
|
||||
|
||||
// update cdp state
|
||||
cdp.Principal = cdp.Principal.Add(principal...)
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...)
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
|
||||
// increment total principal for the input collateral type
|
||||
@ -87,16 +82,13 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri
|
||||
return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, denom)
|
||||
}
|
||||
|
||||
// calculate fees
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom)
|
||||
err := k.ValidatePaymentCoins(ctx, cdp, payment, cdp.Principal.Add(cdp.AccumulatedFees...).Add(fees...))
|
||||
err := k.ValidatePaymentCoins(ctx, cdp, payment, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// calculate fee and principal payment
|
||||
feePayment, principalPayment := k.calculatePayment(ctx, cdp.Principal.Add(cdp.AccumulatedFees...).Add(fees...), cdp.AccumulatedFees.Add(fees...), payment)
|
||||
feePayment, principalPayment := k.calculatePayment(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), cdp.AccumulatedFees, payment)
|
||||
|
||||
// send the payment from the sender to the cpd module
|
||||
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, feePayment.Add(principalPayment...))
|
||||
@ -142,7 +134,7 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri
|
||||
if !principalPayment.IsZero() {
|
||||
cdp.Principal = cdp.Principal.Sub(principalPayment)
|
||||
}
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...).Sub(feePayment)
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Sub(feePayment)
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
|
||||
// decrement the total principal for the input collateral type
|
||||
|
@ -159,11 +159,12 @@ func (suite *DrawTestSuite) TestAddRepayPrincipalFees() {
|
||||
err := suite.keeper.AddCdp(suite.ctx, suite.addrs[2], cs(c("xrp", 1000000000000)), cs(c("usdx", 100000000000)))
|
||||
suite.NoError(err)
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Minute * 10))
|
||||
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
|
||||
suite.NoError(err)
|
||||
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp", cs(c("usdx", 10000000)))
|
||||
suite.NoError(err)
|
||||
t, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
|
||||
suite.Equal(cs(c("usdx", 92827)), t.AccumulatedFees)
|
||||
_ = suite.keeper.MintDebtCoins(suite.ctx, types.ModuleName, "debt", cs(c("usdx", 92827)))
|
||||
err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[2], "xrp", cs(c("usdx", 100)))
|
||||
suite.NoError(err)
|
||||
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2))
|
||||
@ -176,7 +177,9 @@ func (suite *DrawTestSuite) TestAddRepayPrincipalFees() {
|
||||
err = suite.keeper.AddCdp(suite.ctx, suite.addrs[2], cs(c("xrp", 1000000000000)), cs(c("usdx", 100000000)))
|
||||
suite.NoError(err)
|
||||
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 31536000))
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 31536000)) // move forward one year in time
|
||||
err = suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
|
||||
suite.NoError(err)
|
||||
err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[2], "xrp", cs(c("usdx", 100000000)))
|
||||
suite.NoError(err)
|
||||
t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(3))
|
||||
|
@ -1,8 +1,6 @@
|
||||
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"
|
||||
@ -27,38 +25,53 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coins, periods sdk.
|
||||
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 {
|
||||
// UpdateFeesForAllCdps updates the fees for each of the CDPs
|
||||
func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) error {
|
||||
|
||||
price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.IterateCdpsByDenom(ctx, collateralDenom, func(cdp types.CDP) bool {
|
||||
|
||||
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)
|
||||
newFees := 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...)
|
||||
// exit without updating fees if amount has rounded down to zero
|
||||
// cdp will get updated next block when newFees, newFeesSavings, newFeesSurplus >0
|
||||
if newFees.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
// note - only works if principal length is one
|
||||
for _, dc := range cdp.Principal {
|
||||
dp, found := k.GetDebtParam(ctx, dc.Denom)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
savingsRate := dp.SavingsRate
|
||||
|
||||
newFeesSavings := sdk.NewDecFromInt(newFees.AmountOf(dp.Denom)).Mul(savingsRate).RoundInt()
|
||||
newFeesSurplus := newFees.AmountOf(dp.Denom).Sub(newFeesSavings)
|
||||
|
||||
// similar to checking for rounding to zero of all fees, but in this case we
|
||||
// need to handle cases where we expect surplus or savings fees to be zero, namely
|
||||
// if newFeesSavings = 0, check if savings rate is not zero
|
||||
// if newFeesSurplus = 0, check if savings rate is not one
|
||||
if (newFeesSavings.IsZero() && !savingsRate.IsZero()) || (newFeesSurplus.IsZero() && !savingsRate.Equal(sdk.OneDec())) {
|
||||
return false
|
||||
}
|
||||
// mint debt coins to the cdp account
|
||||
k.MintDebtCoins(ctx, types.ModuleName, k.GetDebtDenom(ctx), newFees)
|
||||
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, dp.Denom)
|
||||
feeCoins := sdk.NewCoins(sdk.NewCoin(dp.Denom, previousDebt))
|
||||
k.SetTotalPrincipal(ctx, collateralDenom, dp.Denom, feeCoins.Add(newFees...).AmountOf(dp.Denom))
|
||||
|
||||
// mint surplus coins divided between the liquidator and savings module accounts.
|
||||
k.supplyKeeper.MintCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, newFeesSurplus)))
|
||||
k.supplyKeeper.MintCoins(ctx, types.SavingsRateMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, newFeesSavings)))
|
||||
}
|
||||
|
||||
// now add the new fees fees to the accumulated fees for the cdp
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(newFees...)
|
||||
|
||||
// and set the fees updated time to the current block time since we just updated it
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
@ -109,20 +122,3 @@ func (k Keeper) SetTotalPrincipal(ctx sdk.Context, collateralDenom string, princ
|
||||
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))
|
||||
}
|
||||
|
@ -68,10 +68,6 @@ func (suite *FeeTestSuite) TestCalculateFeesPrecisionLoss() {
|
||||
|
||||
absError := (sdk.OneDec().Sub(sdk.NewDecFromInt(bulkFees[0].Amount).Quo(sdk.NewDecFromInt(individualFees[0].Amount)))).Abs()
|
||||
|
||||
suite.T().Log(bulkFees)
|
||||
suite.T().Log(individualFees)
|
||||
suite.T().Log(absError)
|
||||
|
||||
suite.True(d("0.00001").GTE(absError))
|
||||
}
|
||||
|
||||
@ -95,28 +91,26 @@ func (suite *FeeTestSuite) createCdps() {
|
||||
|
||||
// now create two cdps with the addresses we just created
|
||||
// use the created account to create a cdp that SHOULD have fees updated
|
||||
// to get a ratio between 100 - 110% of liquidation ratio we can use 200xrp ($50) and 24 usdx (208% collateralization with liquidation ratio of 200%)
|
||||
// create CDP for the first address
|
||||
err := suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 200000000)), cs(c("usdx", 24000000)))
|
||||
suite.NoError(err) // check that no error was thrown
|
||||
|
||||
// use the other account to create a cdp that SHOULD NOT have fees updated - 500% collateralization
|
||||
// create CDP for the second address
|
||||
// use the other account to create a cdp that SHOULD NOT have fees updated
|
||||
err = suite.keeper.AddCdp(suite.ctx, addrs[1], cs(c("xrp", 200000000)), cs(c("usdx", 10000000)))
|
||||
suite.NoError(err) // check that no error was thrown
|
||||
|
||||
}
|
||||
|
||||
// UpdateFeesForRiskyCdpsTest tests the functionality for updating the fees for risky CDPs
|
||||
func (suite *FeeTestSuite) TestUpdateFeesForRiskyCdps() {
|
||||
// TestUpdateFees tests the functionality for updating the fees for CDPs
|
||||
func (suite *FeeTestSuite) TestUpdateFees() {
|
||||
// this helper function creates two CDPs with id 1 and 2 respectively, each with zero fees
|
||||
suite.createCdps()
|
||||
|
||||
// move the context forward in time so that cdps will have fees accumulate if CalculateFees is called
|
||||
// note - time must be moved forward by a sufficient amount in order for additional
|
||||
// fees to accumulate, in this example 60 seconds
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 60))
|
||||
err := suite.keeper.UpdateFeesForRiskyCdps(suite.ctx, "xrp", "xrp:usd")
|
||||
// fees to accumulate, in this example 600 seconds
|
||||
oldtime := suite.ctx.BlockTime()
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 600))
|
||||
err := suite.keeper.UpdateFeesForAllCdps(suite.ctx, "xrp")
|
||||
suite.NoError(err) // check that we don't have any error
|
||||
|
||||
// cdp we expect fees to accumulate for
|
||||
@ -124,29 +118,15 @@ func (suite *FeeTestSuite) TestUpdateFeesForRiskyCdps() {
|
||||
// check fees are not zero
|
||||
// check that the fees have been updated
|
||||
suite.False(cdp1.AccumulatedFees.Empty())
|
||||
// now check that we have the correct amount of fees overall (2 USDX for this scenario)
|
||||
suite.Equal(sdk.NewInt(2), cdp1.AccumulatedFees.AmountOf("usdx"))
|
||||
|
||||
// cdp we expect fees to not accumulate for
|
||||
// now check that we have the correct amount of fees overall (22 USDX for this scenario)
|
||||
suite.Equal(sdk.NewInt(22), cdp1.AccumulatedFees.AmountOf("usdx"))
|
||||
suite.Equal(suite.ctx.BlockTime(), cdp1.FeesUpdated)
|
||||
// cdp we expect fees to not accumulate for because of rounding to zero
|
||||
cdp2, _ := suite.keeper.GetCDP(suite.ctx, "xrp", 2)
|
||||
|
||||
// check fees are zero
|
||||
suite.True(cdp2.AccumulatedFees.Empty())
|
||||
|
||||
}
|
||||
|
||||
func (suite *FeeTestSuite) TestGetSetPreviousBlockTime() {
|
||||
now := tmtime.Now()
|
||||
|
||||
_, f := suite.keeper.GetPreviousBlockTime(suite.ctx)
|
||||
suite.False(f)
|
||||
|
||||
suite.NotPanics(func() { suite.keeper.SetPreviousBlockTime(suite.ctx, now) })
|
||||
|
||||
bpt, f := suite.keeper.GetPreviousBlockTime(suite.ctx)
|
||||
suite.True(f)
|
||||
suite.Equal(now, bpt)
|
||||
|
||||
suite.Equal(oldtime, cdp2.FeesUpdated)
|
||||
}
|
||||
|
||||
func TestFeeTestSuite(t *testing.T) {
|
||||
|
@ -70,7 +70,6 @@ func NewCDPGenState(asset string, liquidationRatio sdk.Dec) app.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
@ -153,7 +152,6 @@ func NewCDPGenStateMulti() app.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
@ -211,7 +209,6 @@ func NewCDPGenStateHighDebtLimit() app.GenesisState {
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousBlockTime: cdp.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: cdp.DefaultPreviousDistributionTime,
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
|
@ -18,11 +18,6 @@ import (
|
||||
func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
// Calculate the previous collateral ratio
|
||||
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...))
|
||||
// Update fees
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom)
|
||||
cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...)
|
||||
cdp.FeesUpdated = ctx.BlockTime()
|
||||
|
||||
// Move debt coins from cdp to liquidator account
|
||||
deposits := k.GetDeposits(ctx, cdp.ID)
|
||||
@ -80,29 +75,6 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleNewDebt compounds the accumulated fees for the input collateral and principal coins.
|
||||
// the following operations are performed:
|
||||
// 1. The fees accumulated since the last block are calculated
|
||||
// 2. The fees are minted, split between the liquidator module account (surplus) and the savings rate module account (savings rate) according to the savings rate parameter.
|
||||
// 2. An equal amount of debt coins are minted in the cdp module account
|
||||
// 3. updates the total amount of principal for the input collateral type in the store,
|
||||
func (k Keeper) HandleNewDebt(ctx sdk.Context, collateralDenom string, principalDenom string, periods sdk.Int) {
|
||||
dp, _ := k.GetDebtParam(ctx, principalDenom)
|
||||
savingsRate := dp.SavingsRate
|
||||
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, principalDenom)
|
||||
feeCoins := sdk.NewCoins(sdk.NewCoin(principalDenom, previousDebt))
|
||||
newFees := k.CalculateFees(ctx, feeCoins, periods, collateralDenom)
|
||||
if newFees.IsZero() {
|
||||
return
|
||||
}
|
||||
newFeesSavings := sdk.NewDecFromInt(newFees.AmountOf(principalDenom)).Mul(savingsRate).RoundInt()
|
||||
newFeesSurplus := newFees.AmountOf(principalDenom).Sub(newFeesSavings)
|
||||
k.MintDebtCoins(ctx, types.ModuleName, k.GetDebtDenom(ctx), newFees)
|
||||
k.supplyKeeper.MintCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(principalDenom, newFeesSurplus)))
|
||||
k.supplyKeeper.MintCoins(ctx, types.SavingsRateMacc, sdk.NewCoins(sdk.NewCoin(principalDenom, newFeesSavings)))
|
||||
k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, feeCoins.Add(newFees...).AmountOf(principalDenom))
|
||||
}
|
||||
|
||||
// LiquidateCdps seizes collateral from all CDPs below the input liquidation ratio
|
||||
func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, liquidationRatio sdk.Dec) error {
|
||||
price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID)
|
||||
|
@ -189,14 +189,6 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() {
|
||||
suite.Equal(len(suite.liquidations.xrp), xrpLiquidations)
|
||||
}
|
||||
|
||||
func (suite *SeizeTestSuite) TestHandleNewDebt() {
|
||||
suite.createCdps()
|
||||
tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
|
||||
suite.keeper.HandleNewDebt(suite.ctx, "xrp", "usdx", i(31536000))
|
||||
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
|
||||
suite.Equal(sdk.NewDec(tpb.Int64()).Mul(d("1.05")).TruncateInt().Int64(), tpa.Int64())
|
||||
}
|
||||
|
||||
func (suite *SeizeTestSuite) TestApplyLiquidationPenalty() {
|
||||
penalty := suite.keeper.ApplyLiquidationPenalty(suite.ctx, "xrp", i(1000))
|
||||
suite.Equal(i(50), penalty)
|
||||
|
@ -54,8 +54,7 @@ func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &totalB)
|
||||
return fmt.Sprintf("%s\n%s", totalA, totalB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.PreviousBlockTimeKey),
|
||||
bytes.Equal(kvA.Key[:1], types.PreviousDistributionTimeKey):
|
||||
case bytes.Equal(kvA.Key[:1], types.PreviousDistributionTimeKey):
|
||||
var timeA, timeB time.Time
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &timeA)
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &timeB)
|
||||
|
@ -42,7 +42,7 @@ func TestDecodeDistributionStore(t *testing.T) {
|
||||
kv.Pair{Key: []byte(types.GovDenomKey), Value: cdc.MustMarshalBinaryLengthPrefixed(denom)},
|
||||
kv.Pair{Key: []byte(types.DepositKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)},
|
||||
kv.Pair{Key: []byte(types.PrincipalKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(principal)},
|
||||
kv.Pair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevDistTime)},
|
||||
kv.Pair{Key: []byte(types.PreviousDistributionTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevDistTime)},
|
||||
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,6 @@ func randomCdpGenState(selection int) types.GenesisState {
|
||||
DebtDenom: types.DefaultDebtDenom,
|
||||
GovDenom: types.DefaultGovDenom,
|
||||
CDPs: types.CDPs{},
|
||||
PreviousBlockTime: types.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: types.DefaultPreviousDistributionTime,
|
||||
}
|
||||
case 1:
|
||||
@ -158,7 +157,6 @@ func randomCdpGenState(selection int) types.GenesisState {
|
||||
DebtDenom: types.DefaultDebtDenom,
|
||||
GovDenom: types.DefaultGovDenom,
|
||||
CDPs: types.CDPs{},
|
||||
PreviousBlockTime: types.DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: types.DefaultPreviousDistributionTime,
|
||||
}
|
||||
default:
|
||||
|
@ -14,12 +14,11 @@ type GenesisState struct {
|
||||
StartingCdpID uint64 `json:"starting_cdp_id" yaml:"starting_cdp_id"`
|
||||
DebtDenom string `json:"debt_denom" yaml:"debt_denom"`
|
||||
GovDenom string `json:"gov_denom" yaml:"gov_denom"`
|
||||
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||
PreviousDistributionTime time.Time `json:"previous_distribution_time" yaml"previous_distribution_time"`
|
||||
PreviousDistributionTime time.Time `json:"previous_distribution_time" yaml:"previous_distribution_time"`
|
||||
}
|
||||
|
||||
// NewGenesisState returns a new genesis state
|
||||
func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64, debtDenom, govDenom string, previousBlockTime time.Time, previousDistTime time.Time) GenesisState {
|
||||
func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64, debtDenom, govDenom string, previousDistTime time.Time) GenesisState {
|
||||
return GenesisState{
|
||||
Params: params,
|
||||
CDPs: cdps,
|
||||
@ -27,23 +26,21 @@ func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID
|
||||
StartingCdpID: startingCdpID,
|
||||
DebtDenom: debtDenom,
|
||||
GovDenom: govDenom,
|
||||
PreviousBlockTime: previousBlockTime,
|
||||
PreviousDistributionTime: previousDistTime,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGenesisState returns a default genesis state
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Params: DefaultParams(),
|
||||
CDPs: CDPs{},
|
||||
Deposits: Deposits{},
|
||||
StartingCdpID: DefaultCdpStartingID,
|
||||
DebtDenom: DefaultDebtDenom,
|
||||
GovDenom: DefaultGovDenom,
|
||||
PreviousBlockTime: DefaultPreviousBlockTime,
|
||||
PreviousDistributionTime: DefaultPreviousDistributionTime,
|
||||
}
|
||||
return NewGenesisState(
|
||||
DefaultParams(),
|
||||
CDPs{},
|
||||
Deposits{},
|
||||
DefaultCdpStartingID,
|
||||
DefaultDebtDenom,
|
||||
DefaultGovDenom,
|
||||
DefaultPreviousDistributionTime,
|
||||
)
|
||||
}
|
||||
|
||||
// Validate performs basic validation of genesis data returning an
|
||||
@ -54,10 +51,6 @@ func (gs GenesisState) Validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if gs.PreviousBlockTime.Equal(time.Time{}) {
|
||||
return fmt.Errorf("previous block time not set")
|
||||
}
|
||||
|
||||
if gs.PreviousDistributionTime.Equal(time.Time{}) {
|
||||
return fmt.Errorf("previous distribution time not set")
|
||||
}
|
||||
|
@ -45,8 +45,7 @@ var sep = []byte(":")
|
||||
// - 0x05<depositState>:<cdpID>:<depositorAddr_bytes>: Deposit
|
||||
// - 0x06<denom>:totalPrincipal
|
||||
// - 0x07<denom>:feeRate
|
||||
// - 0x08:previousBlockTime
|
||||
// - 0x09:previousDistributionTime
|
||||
// - 0x08:previousDistributionTime
|
||||
|
||||
// KVStore key prefixes
|
||||
var (
|
||||
@ -58,8 +57,7 @@ var (
|
||||
GovDenomKey = []byte{0x05}
|
||||
DepositKeyPrefix = []byte{0x06}
|
||||
PrincipalKeyPrefix = []byte{0x07}
|
||||
PreviousBlockTimeKey = []byte{0x08}
|
||||
PreviousDistributionTimeKey = []byte{0x09}
|
||||
PreviousDistributionTimeKey = []byte{0x08}
|
||||
)
|
||||
|
||||
var lenPositiveDec = len(SortableDecBytes(sdk.OneDec()))
|
||||
|
@ -29,7 +29,6 @@ var (
|
||||
DefaultGovDenom = "ukava"
|
||||
DefaultSurplusThreshold = sdk.NewInt(1000000000)
|
||||
DefaultDebtThreshold = sdk.NewInt(1000000000)
|
||||
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||
DefaultPreviousDistributionTime = tmtime.Canonical(time.Unix(0, 0))
|
||||
DefaultSavingsDistributionFrequency = time.Hour * 24 * 2
|
||||
minCollateralPrefix = 0
|
||||
|
Loading…
Reference in New Issue
Block a user