diff --git a/x/cdp/abci.go b/x/cdp/abci.go index f31e81ee..8c48cbdf 100644 --- a/x/cdp/abci.go +++ b/x/cdp/abci.go @@ -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 } diff --git a/x/cdp/abci_test.go b/x/cdp/abci_test.go index b16ab7e5..b090f672 100644 --- a/x/cdp/abci_test.go +++ b/x/cdp/abci_test.go @@ -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) { diff --git a/x/cdp/alias.go b/x/cdp/alias.go index 9688a153..eeeb2c81 100644 --- a/x/cdp/alias.go +++ b/x/cdp/alias.go @@ -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 diff --git a/x/cdp/genesis.go b/x/cdp/genesis.go index 3fa365ef..9346ac9f 100644 --- a/x/cdp/genesis.go +++ b/x/cdp/genesis.go @@ -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) } diff --git a/x/cdp/integration_test.go b/x/cdp/integration_test.go index 68c644ee..ee47c829 100644 --- a/x/cdp/integration_test.go +++ b/x/cdp/integration_test.go @@ -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, } } diff --git a/x/cdp/keeper/deposit.go b/x/cdp/keeper/deposit.go index 6dcbf7ab..5dbee127 100644 --- a/x/cdp/keeper/deposit.go +++ b/x/cdp/keeper/deposit.go @@ -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...)) diff --git a/x/cdp/keeper/draw.go b/x/cdp/keeper/draw.go index 876795d1..4c3383fa 100644 --- a/x/cdp/keeper/draw.go +++ b/x/cdp/keeper/draw.go @@ -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 diff --git a/x/cdp/keeper/draw_test.go b/x/cdp/keeper/draw_test.go index d353265e..e5b0993f 100644 --- a/x/cdp/keeper/draw_test.go +++ b/x/cdp/keeper/draw_test.go @@ -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)) diff --git a/x/cdp/keeper/fees.go b/x/cdp/keeper/fees.go index e1f3c94e..7140d4f3 100644 --- a/x/cdp/keeper/fees.go +++ b/x/cdp/keeper/fees.go @@ -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)) -} diff --git a/x/cdp/keeper/fees_test.go b/x/cdp/keeper/fees_test.go index 1c6ca211..1b17554a 100644 --- a/x/cdp/keeper/fees_test.go +++ b/x/cdp/keeper/fees_test.go @@ -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) { diff --git a/x/cdp/keeper/integration_test.go b/x/cdp/keeper/integration_test.go index 0cfbbf1f..66bb4dc2 100644 --- a/x/cdp/keeper/integration_test.go +++ b/x/cdp/keeper/integration_test.go @@ -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)} diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index 730c335b..daf6a813 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -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) diff --git a/x/cdp/keeper/seize_test.go b/x/cdp/keeper/seize_test.go index 355333bd..579905d6 100644 --- a/x/cdp/keeper/seize_test.go +++ b/x/cdp/keeper/seize_test.go @@ -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) diff --git a/x/cdp/simulation/decoder.go b/x/cdp/simulation/decoder.go index 1b0e347e..91c89c10 100644 --- a/x/cdp/simulation/decoder.go +++ b/x/cdp/simulation/decoder.go @@ -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) diff --git a/x/cdp/simulation/decoder_test.go b/x/cdp/simulation/decoder_test.go index 7d092e6c..f1ab67cc 100644 --- a/x/cdp/simulation/decoder_test.go +++ b/x/cdp/simulation/decoder_test.go @@ -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}}, } diff --git a/x/cdp/simulation/genesis.go b/x/cdp/simulation/genesis.go index 6fa7680e..65f568aa 100644 --- a/x/cdp/simulation/genesis.go +++ b/x/cdp/simulation/genesis.go @@ -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: diff --git a/x/cdp/types/genesis.go b/x/cdp/types/genesis.go index 1d1da37b..a1e73a3f 100644 --- a/x/cdp/types/genesis.go +++ b/x/cdp/types/genesis.go @@ -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") } diff --git a/x/cdp/types/keys.go b/x/cdp/types/keys.go index de0de209..8f4a8097 100644 --- a/x/cdp/types/keys.go +++ b/x/cdp/types/keys.go @@ -45,8 +45,7 @@ var sep = []byte(":") // - 0x05::: Deposit // - 0x06:totalPrincipal // - 0x07: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())) diff --git a/x/cdp/types/params.go b/x/cdp/types/params.go index 5f1fd938..5246dba4 100644 --- a/x/cdp/types/params.go +++ b/x/cdp/types/params.go @@ -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