mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-23 23:05:18 +00:00
cleanup begin/end block (#553)
* cleanup begin/end block * update cdp BeginBlocker * update dist mint * fix tests * typo * add comment on CloseExpiredAuctions * fix tests
This commit is contained in:
parent
f0e73e4aa8
commit
bfb9f7e20a
@ -1,13 +1,17 @@
|
||||
package auction
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/kava-labs/kava/x/auction/types"
|
||||
)
|
||||
|
||||
// BeginBlocker runs at the start of every block.
|
||||
// BeginBlocker closes all expired auctions at the end of each block. It panics if
|
||||
// there's an error other than ErrAuctionNotFound.
|
||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||
err := k.CloseExpiredAuctions(ctx)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, types.ErrAuctionNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -449,7 +450,6 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, auction types.DebtAuction, bidder
|
||||
|
||||
// CloseAuction closes an auction and distributes funds to the highest bidder.
|
||||
func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) error {
|
||||
|
||||
auction, found := k.GetAuction(ctx, auctionID)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrAuctionNotFound, "%d", auctionID)
|
||||
@ -460,21 +460,20 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) error {
|
||||
}
|
||||
|
||||
// payout to the last bidder
|
||||
var err error
|
||||
switch auc := auction.(type) {
|
||||
case types.SurplusAuction:
|
||||
if err := k.PayoutSurplusAuction(ctx, auc); err != nil {
|
||||
return err
|
||||
}
|
||||
err = k.PayoutSurplusAuction(ctx, auc)
|
||||
case types.DebtAuction:
|
||||
if err := k.PayoutDebtAuction(ctx, auc); err != nil {
|
||||
return err
|
||||
}
|
||||
err = k.PayoutDebtAuction(ctx, auc)
|
||||
case types.CollateralAuction:
|
||||
if err := k.PayoutCollateralAuction(ctx, auc); err != nil {
|
||||
return err
|
||||
}
|
||||
err = k.PayoutCollateralAuction(ctx, auc)
|
||||
default:
|
||||
return sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auc.GetType())
|
||||
err = sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auc.GetType())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k.DeleteAuction(ctx, auctionID)
|
||||
@ -502,23 +501,17 @@ func (k Keeper) PayoutDebtAuction(ctx sdk.Context, auction types.DebtAuction) er
|
||||
return err
|
||||
}
|
||||
// if there is remaining debt, return it to the calling module to manage
|
||||
if auction.CorrespondingDebt.IsPositive() {
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !auction.CorrespondingDebt.IsPositive() {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
|
||||
}
|
||||
|
||||
// PayoutSurplusAuction pays out the proceeds for a surplus auction.
|
||||
func (k Keeper) PayoutSurplusAuction(ctx sdk.Context, auction types.SurplusAuction) error {
|
||||
// Send the tokens from the auction module account where they are being managed to the bidder who won the auction
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, auction.Bidder, sdk.NewCoins(auction.Lot))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, auction.Bidder, sdk.NewCoins(auction.Lot))
|
||||
}
|
||||
|
||||
// PayoutCollateralAuction pays out the proceeds for a collateral auction.
|
||||
@ -530,29 +523,30 @@ func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, auction types.Collatera
|
||||
}
|
||||
|
||||
// if there is remaining debt after the auction, send it back to the initiating module for management
|
||||
if auction.CorrespondingDebt.IsPositive() {
|
||||
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !auction.CorrespondingDebt.IsPositive() {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
|
||||
}
|
||||
|
||||
// CloseExpiredAuctions finds all auctions that are past (or at) their ending times and closes them, paying out to the highest bidder.
|
||||
// CloseExpiredAuctions iterates over all the auctions stored by until the current
|
||||
// block timestamp and that are past (or at) their ending times and closes them,
|
||||
// paying out to the highest bidder.
|
||||
func (k Keeper) CloseExpiredAuctions(ctx sdk.Context) error {
|
||||
var expiredAuctions []uint64
|
||||
k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) bool {
|
||||
expiredAuctions = append(expiredAuctions, id)
|
||||
var err error
|
||||
k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) (stop bool) {
|
||||
err = k.CloseAuction(ctx, id)
|
||||
if err != nil && !errors.Is(err, types.ErrAuctionNotFound) {
|
||||
// stop iteration
|
||||
return true
|
||||
}
|
||||
// reset error in case the last element had an ErrAuctionNotFound
|
||||
err = nil
|
||||
return false
|
||||
})
|
||||
// Note: iteration and auction closing are in separate loops as db should not be modified during iteration // TODO is this correct? gov modifies during iteration
|
||||
for _, id := range expiredAuctions {
|
||||
if err := k.CloseAuction(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// earliestTime returns the earliest of two times.
|
||||
|
@ -1,9 +1,13 @@
|
||||
package cdp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
|
||||
)
|
||||
|
||||
// BeginBlocker compounds the debt in outstanding cdps and liquidates cdps that are below the required collateralization ratio
|
||||
@ -17,7 +21,6 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
|
||||
}
|
||||
|
||||
for _, cp := range params.CollateralParams {
|
||||
|
||||
ok := k.UpdatePricefeedStatus(ctx, cp.SpotMarketID)
|
||||
if !ok {
|
||||
continue
|
||||
@ -29,26 +32,30 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
|
||||
}
|
||||
|
||||
err := k.UpdateFeesForAllCdps(ctx, cp.Denom)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = k.LiquidateCdps(ctx, cp.LiquidationMarketID, cp.Denom, cp.LiquidationRatio)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, pricefeedtypes.ErrNoValidPrice) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
err := k.RunSurplusAndDebtAuctions(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
distTimeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousDistTime.Unix())
|
||||
if !distTimeElapsed.GTE(sdk.NewInt(int64(params.SavingsDistributionFrequency.Seconds()))) {
|
||||
return
|
||||
}
|
||||
|
||||
err = k.DistributeSavingsRate(ctx, params.DebtParam.Denom)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k.SetPreviousSavingsDistribution(ctx, ctx.BlockTime())
|
||||
}
|
||||
|
@ -149,11 +149,13 @@ func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) error {
|
||||
}
|
||||
remainingDebt := k.GetTotalDebt(ctx, types.LiquidatorMacc)
|
||||
params := k.GetParams(ctx)
|
||||
|
||||
if remainingDebt.GTE(params.DebtAuctionThreshold) {
|
||||
debtLot := sdk.NewCoin(k.GetDebtDenom(ctx), params.DebtAuctionLot)
|
||||
bidCoin := sdk.NewCoin(params.DebtParam.Denom, debtLot.Amount)
|
||||
_, err := k.auctionKeeper.StartDebtAuction(
|
||||
ctx, types.LiquidatorMacc, bidCoin, sdk.NewCoin(k.GetGovDenom(ctx), debtLot.Amount.Mul(sdk.NewInt(dump))), debtLot)
|
||||
initialLot := sdk.NewCoin(k.GetGovDenom(ctx), debtLot.Amount.Mul(sdk.NewInt(dump)))
|
||||
|
||||
_, err := k.auctionKeeper.StartDebtAuction(ctx, types.LiquidatorMacc, bidCoin, initialLot, debtLot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -163,6 +165,7 @@ func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) error {
|
||||
if !surplus.GTE(params.SurplusAuctionThreshold) {
|
||||
return nil
|
||||
}
|
||||
|
||||
surplusLot := sdk.NewCoin(params.DebtParam.Denom, sdk.MinInt(params.SurplusAuctionLot, surplus))
|
||||
_, err := k.auctionKeeper.StartSurplusAuction(ctx, types.LiquidatorMacc, surplusLot, k.GetGovDenom(ctx))
|
||||
return err
|
||||
|
@ -26,8 +26,8 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coin, periods sdk.I
|
||||
func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) error {
|
||||
var iterationErr error
|
||||
k.IterateCdpsByDenom(ctx, collateralDenom, func(cdp types.CDP) bool {
|
||||
|
||||
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees))
|
||||
// periods = bblock timestamp - fees updated
|
||||
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
|
||||
|
||||
newFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralDenom)
|
||||
@ -55,12 +55,14 @@ func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) er
|
||||
if (newFeesSavings.IsZero() && !savingsRate.IsZero()) || (newFeesSurplus.IsZero() && !savingsRate.Equal(sdk.OneDec())) {
|
||||
return false
|
||||
}
|
||||
|
||||
// mint debt coins to the cdp account
|
||||
err := k.MintDebtCoins(ctx, types.ModuleName, k.GetDebtDenom(ctx), newFees)
|
||||
if err != nil {
|
||||
iterationErr = err
|
||||
return true
|
||||
}
|
||||
|
||||
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, dp.Denom)
|
||||
newDebt := previousDebt.Add(newFees.Amount)
|
||||
k.SetTotalPrincipal(ctx, collateralDenom, dp.Denom, newDebt)
|
||||
@ -100,17 +102,14 @@ func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralDenom string,
|
||||
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
|
||||
total = total.Add(principal.Amount)
|
||||
k.SetTotalPrincipal(ctx, collateralDenom, principal.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.Coin) {
|
||||
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
|
||||
total = total.Sub(principal.Amount)
|
||||
if total.IsNegative() {
|
||||
// can happen in tests due to rounding errors in fee calculation
|
||||
total = sdk.ZeroInt()
|
||||
}
|
||||
// NOTE: negative total principal can happen in tests due to rounding errors
|
||||
// in fee calculation
|
||||
total = sdk.MaxInt(total.Sub(principal.Amount), sdk.ZeroInt())
|
||||
k.SetTotalPrincipal(ctx, collateralDenom, principal.Denom, total)
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
|
||||
if !found {
|
||||
return sdkerrors.Wrap(types.ErrDebtNotSupported, debtDenom)
|
||||
}
|
||||
|
||||
savingsRateMacc := k.supplyKeeper.GetModuleAccount(ctx, types.SavingsRateMacc)
|
||||
surplusToDistribute := savingsRateMacc.GetCoins().AmountOf(dp.Denom)
|
||||
if surplusToDistribute.IsZero() {
|
||||
@ -26,21 +27,26 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
|
||||
|
||||
modAccountCoins := k.getModuleAccountCoins(ctx, dp.Denom)
|
||||
totalSupplyLessModAccounts := k.supplyKeeper.GetSupply(ctx).GetTotal().Sub(modAccountCoins)
|
||||
|
||||
// values to use in interest calculation
|
||||
totalSurplus := sdk.NewDecFromInt(surplusToDistribute)
|
||||
totalSupply := sdk.NewDecFromInt(totalSupplyLessModAccounts.AmountOf(debtDenom))
|
||||
|
||||
var iterationErr error
|
||||
// TODO: avoid iterating over all the accounts by keeping the stored stable coin
|
||||
// holders' addresses separately.
|
||||
k.accountKeeper.IterateAccounts(ctx, func(acc authexported.Account) (stop bool) {
|
||||
_, ok := acc.(supplyexported.ModuleAccountI)
|
||||
if ok {
|
||||
// don't distribute savings rate to module accounts
|
||||
return false
|
||||
}
|
||||
|
||||
debtAmount := acc.GetCoins().AmountOf(debtDenom)
|
||||
if !debtAmount.IsPositive() {
|
||||
return false
|
||||
}
|
||||
|
||||
// (balance * rewardToDisribute) / totalSupply
|
||||
// interest is the ratable fraction of savings rate owed to that account, rounded using bankers rounding
|
||||
interest := (sdk.NewDecFromInt(debtAmount).Mul(totalSurplus)).Quo(totalSupply).RoundInt()
|
||||
@ -51,6 +57,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
|
||||
if !interest.IsPositive() {
|
||||
return false
|
||||
}
|
||||
|
||||
interestCoins := sdk.NewCoins(sdk.NewCoin(debtDenom, interest))
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.SavingsRateMacc, acc.GetAddress(), interestCoins)
|
||||
if err != nil {
|
||||
@ -60,6 +67,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
|
||||
surplusToDistribute = surplusToDistribute.Sub(interest)
|
||||
return false
|
||||
})
|
||||
|
||||
return iterationErr
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,7 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
deposits := k.GetDeposits(ctx, cdp.ID)
|
||||
debt := cdp.Principal.Amount.Add(cdp.AccumulatedFees.Amount)
|
||||
modAccountDebt := k.getModAccountDebt(ctx, types.ModuleName)
|
||||
if modAccountDebt.LT(debt) {
|
||||
debt = modAccountDebt
|
||||
}
|
||||
debt = sdk.MinInt(debt, modAccountDebt)
|
||||
debtCoin := sdk.NewCoin(k.GetDebtDenom(ctx), debt)
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(debtCoin))
|
||||
if err != nil {
|
||||
@ -35,6 +33,12 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
|
||||
// liquidate deposits and send collateral from cdp to liquidator
|
||||
for _, dep := range deposits {
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(dep.Amount))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.DeleteDeposit(ctx, dep.CdpID, dep.Depositor)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeCdpLiquidation,
|
||||
@ -43,12 +47,8 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
sdk.NewAttribute(types.AttributeKeyDeposit, dep.String()),
|
||||
),
|
||||
)
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(dep.Amount))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.DeleteDeposit(ctx, dep.CdpID, dep.Depositor)
|
||||
}
|
||||
|
||||
err = k.AuctionCollateral(ctx, deposits, debt, cdp.Principal.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -8,9 +8,7 @@ import (
|
||||
|
||||
// BeginBlocker runs at the start of every block.
|
||||
func BeginBlocker(ctx sdk.Context, _ abci.RequestBeginBlock, k Keeper) {
|
||||
|
||||
// enact proposals ignoring their expiry time - they could have received enough votes last block before expiring this block
|
||||
k.EnactPassedProposals(ctx)
|
||||
|
||||
k.CloseExpiredProposals(ctx)
|
||||
}
|
||||
|
@ -207,9 +207,7 @@ func (k Keeper) GetProposalsByCommittee(ctx sdk.Context, committeeID uint64) []t
|
||||
|
||||
// DeleteProposalAndVotes removes a proposal and its associated votes.
|
||||
func (k Keeper) DeleteProposalAndVotes(ctx sdk.Context, proposalID uint64) {
|
||||
|
||||
votes := k.GetVotesByProposal(ctx, proposalID)
|
||||
|
||||
k.DeleteProposal(ctx, proposalID)
|
||||
for _, v := range votes {
|
||||
k.DeleteVote(ctx, v.ProposalID, v.Voter)
|
||||
|
@ -138,7 +138,9 @@ func (k Keeper) EnactPassedProposals(ctx sdk.Context) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !passes {
|
||||
// continue to next proposal
|
||||
return false
|
||||
}
|
||||
|
||||
@ -164,7 +166,6 @@ func (k Keeper) EnactPassedProposals(ctx sdk.Context) {
|
||||
|
||||
// CloseExpiredProposals removes proposals (and associated votes) that have past their deadline.
|
||||
func (k Keeper) CloseExpiredProposals(ctx sdk.Context) {
|
||||
|
||||
k.IterateProposals(ctx, func(proposal types.Proposal) bool {
|
||||
if !proposal.HasExpiredBy(ctx.BlockTime()) {
|
||||
return false
|
||||
|
@ -48,7 +48,8 @@ func (k Keeper) CreateAndDeleteRewardPeriods(ctx sdk.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyRewardsToCdps iterates over the reward periods and creates a claim for each cdp owner that created usdx with the collateral specified in the reward period
|
||||
// ApplyRewardsToCdps iterates over the reward periods and creates a claim for each
|
||||
// cdp owner that created usdx with the collateral specified in the reward period.
|
||||
func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
|
||||
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||
if !found {
|
||||
@ -56,6 +57,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
|
||||
k.SetPreviousBlockTime(ctx, previousBlockTime)
|
||||
return
|
||||
}
|
||||
|
||||
k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool {
|
||||
expired := false
|
||||
// the total amount of usdx created with the collateral type being incentivized
|
||||
@ -66,6 +68,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
|
||||
timeElapsed = sdk.NewInt(rp.End.Unix() - previousBlockTime.Unix())
|
||||
expired = true
|
||||
}
|
||||
|
||||
// the amount of rewards to pay (rewardAmount * timeElapsed)
|
||||
rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed)
|
||||
id := k.GetNextClaimPeriodID(ctx, rp.Denom)
|
||||
@ -85,6 +88,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
|
||||
k.HandleRewardPeriodExpiry(ctx, rp)
|
||||
return false
|
||||
})
|
||||
|
||||
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||
}
|
||||
|
||||
|
@ -19,42 +19,44 @@ func (k Keeper) MintPeriodInflation(ctx sdk.Context) error {
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||
if !found {
|
||||
previousBlockTime = ctx.BlockTime()
|
||||
k.SetPreviousBlockTime(ctx, previousBlockTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, period := range params.Periods {
|
||||
switch {
|
||||
// Case 1 - period is fully expired
|
||||
if period.End.Before(previousBlockTime) {
|
||||
case period.End.Before(previousBlockTime):
|
||||
continue
|
||||
}
|
||||
|
||||
// Case 2 - period has ended since the previous block time
|
||||
if period.End.After(previousBlockTime) && period.End.Before(ctx.BlockTime()) {
|
||||
case period.End.After(previousBlockTime) && period.End.Before(ctx.BlockTime()):
|
||||
// calculate time elapsed relative to the periods end time
|
||||
timeElapsed := sdk.NewInt(period.End.Unix() - previousBlockTime.Unix())
|
||||
err := k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
|
||||
// update the value of previousBlockTime so that the next period starts from the end of the last
|
||||
// period and not the original value of previousBlockTime
|
||||
previousBlockTime = period.End
|
||||
}
|
||||
|
||||
// Case 3 - period is ongoing
|
||||
if (period.Start.Before(previousBlockTime) || period.Start.Equal(previousBlockTime)) && period.End.After(ctx.BlockTime()) {
|
||||
case (period.Start.Before(previousBlockTime) || period.Start.Equal(previousBlockTime)) && period.End.After(ctx.BlockTime()):
|
||||
// calculate time elapsed relative to the current block time
|
||||
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
|
||||
err := k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
|
||||
|
||||
// Case 4 - period hasn't started
|
||||
if period.Start.After(ctx.BlockTime()) || period.Start.Equal(ctx.BlockTime()) {
|
||||
case period.Start.After(ctx.BlockTime()) || period.Start.Equal(ctx.BlockTime()):
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||
return nil
|
||||
@ -78,11 +80,13 @@ func (k Keeper) mintInflationaryCoins(ctx sdk.Context, inflationRate sdk.Dec, ti
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeKavaDist,
|
||||
sdk.NewAttribute(types.AttributeKeyInflation, sdk.NewCoin(denom, amountToMint).String()),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,28 +1,23 @@
|
||||
package pricefeed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/kava-labs/kava/x/pricefeed/types"
|
||||
)
|
||||
|
||||
// EndBlocker updates the current pricefeed
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) {
|
||||
// Update the current price of each asset.
|
||||
for _, market := range k.GetMarkets(ctx) {
|
||||
if market.Active {
|
||||
err := k.SetCurrentPrices(ctx, market.MarketID)
|
||||
if err != nil {
|
||||
// In the event of failure, emit an event.
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
EventTypeNoValidPrices,
|
||||
sdk.NewAttribute(AttributeMarketID, fmt.Sprintf("%s", market.MarketID)),
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
if !market.Active {
|
||||
continue
|
||||
}
|
||||
|
||||
err := k.SetCurrentPrices(ctx, market.MarketID)
|
||||
if err != nil && !errors.Is(err, types.ErrNoValidPrice) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
@ -37,6 +40,11 @@ func NewKeeper(
|
||||
}
|
||||
}
|
||||
|
||||
// Logger returns a module-specific logger.
|
||||
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
||||
}
|
||||
|
||||
// SetPrice updates the posted price for a specific oracle
|
||||
func (k Keeper) SetPrice(
|
||||
ctx sdk.Context,
|
||||
@ -111,40 +119,41 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) error {
|
||||
notExpiredPrices = append(notExpiredPrices, types.NewCurrentPrice(v.MarketID, v.Price))
|
||||
}
|
||||
}
|
||||
|
||||
if len(notExpiredPrices) == 0 {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(
|
||||
types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}),
|
||||
)
|
||||
// NOTE: The current price stored will continue storing the most recent (expired)
|
||||
// price if this is not set.
|
||||
// This zero's out the current price stored value for that market and ensures
|
||||
// that CDP methods that GetCurrentPrice will return error.
|
||||
k.setCurrentPrice(ctx, marketID, types.CurrentPrice{})
|
||||
return types.ErrNoValidPrice
|
||||
}
|
||||
|
||||
medianPrice := k.CalculateMedianPrice(ctx, notExpiredPrices)
|
||||
|
||||
// check case that market price was not set in genesis
|
||||
if validPrevPrice {
|
||||
if validPrevPrice && !medianPrice.Equal(prevPrice.Price) {
|
||||
// only emit event if price has changed
|
||||
if !medianPrice.Equal(prevPrice.Price) {
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeMarketPriceUpdated,
|
||||
sdk.NewAttribute(types.AttributeMarketID, marketID),
|
||||
sdk.NewAttribute(types.AttributeMarketPrice, medianPrice.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeMarketPriceUpdated,
|
||||
sdk.NewAttribute(types.AttributeMarketID, marketID),
|
||||
sdk.NewAttribute(types.AttributeMarketPrice, medianPrice.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
store := ctx.KVStore(k.key)
|
||||
currentPrice := types.NewCurrentPrice(marketID, medianPrice)
|
||||
|
||||
store.Set(
|
||||
types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(currentPrice),
|
||||
)
|
||||
k.setCurrentPrice(ctx, marketID, currentPrice)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k Keeper) setCurrentPrice(ctx sdk.Context, marketID string, currentPrice types.CurrentPrice) {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(currentPrice))
|
||||
}
|
||||
|
||||
// CalculateMedianPrice calculates the median prices for the input prices.
|
||||
func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices types.CurrentPrices) sdk.Dec {
|
||||
l := len(prices)
|
||||
|
@ -24,34 +24,35 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
|
||||
var voteInfos VoteInfos
|
||||
voteInfos = req.LastCommitInfo.GetVotes()
|
||||
validatorVestingKeys := k.GetAllAccountKeys(ctx)
|
||||
|
||||
for _, key := range validatorVestingKeys {
|
||||
acc := k.GetAccountFromAuthKeeper(ctx, key)
|
||||
if k.AccountIsVesting(ctx, acc.GetAddress()) {
|
||||
vote, found := voteInfos.FilterByValidatorAddress(acc.ValidatorAddress)
|
||||
if !found || !vote.SignedLastBlock {
|
||||
if ctx.BlockHeight() <= 1 {
|
||||
// don't count missed blocks on block 1 since there is no vote history
|
||||
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
|
||||
} else {
|
||||
// if the validator was not found or explicitly didn't sign, increment the missing sign count
|
||||
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
|
||||
}
|
||||
} else {
|
||||
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
|
||||
}
|
||||
|
||||
// check if a period ended in the last block
|
||||
endTimes := k.GetPeriodEndTimes(ctx, key)
|
||||
|
||||
for i, t := range endTimes {
|
||||
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
|
||||
k.UpdateVestedCoinsProgress(ctx, key, i)
|
||||
}
|
||||
}
|
||||
// handle any new/remaining debt on the account
|
||||
k.HandleVestingDebt(ctx, key, currentBlockTime)
|
||||
if !k.AccountIsVesting(ctx, acc.GetAddress()) {
|
||||
continue
|
||||
}
|
||||
|
||||
vote, found := voteInfos.FilterByValidatorAddress(acc.ValidatorAddress)
|
||||
|
||||
var missedBlock bool
|
||||
// if the validator was not found or explicitly didn't sign, increment the missing sign count
|
||||
if (!found || !vote.SignedLastBlock) && ctx.BlockHeight() > 1 {
|
||||
missedBlock = true
|
||||
}
|
||||
|
||||
k.UpdateMissingSignCount(ctx, acc.GetAddress(), missedBlock)
|
||||
|
||||
// check if a period ended in the last block
|
||||
endTimes := k.GetPeriodEndTimes(ctx, key)
|
||||
|
||||
for i, t := range endTimes {
|
||||
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
|
||||
k.UpdateVestedCoinsProgress(ctx, key, i)
|
||||
}
|
||||
}
|
||||
// handle any new/remaining debt on the account
|
||||
k.HandleVestingDebt(ctx, key, currentBlockTime)
|
||||
}
|
||||
|
||||
k.SetPreviousBlockTime(ctx, currentBlockTime)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func TestBeginBlockerZeroHeight(t *testing.T) {
|
||||
|
||||
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
|
||||
// require missed block counter doesn't increment because there's no voting history
|
||||
require.Equal(t, types.CurrentPeriodProgress{0, 1}, vva.CurrentPeriodProgress)
|
||||
require.Equal(t, types.CurrentPeriodProgress{MissedBlocks: 0, TotalBlocks: 1}, vva.CurrentPeriodProgress)
|
||||
|
||||
// mark the validator as having missed
|
||||
req = abci.RequestBeginBlock{
|
||||
@ -90,7 +90,7 @@ func TestBeginBlockerZeroHeight(t *testing.T) {
|
||||
BeginBlocker(ctx, req, vvk)
|
||||
|
||||
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
|
||||
require.Equal(t, types.CurrentPeriodProgress{0, 2}, vva.CurrentPeriodProgress)
|
||||
require.Equal(t, types.CurrentPeriodProgress{MissedBlocks: 0, TotalBlocks: 2}, vva.CurrentPeriodProgress)
|
||||
}
|
||||
|
||||
func TestBeginBlockerSignedBlock(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user