2020-04-24 15:20:34 +00:00
package keeper
import (
"time"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
2020-04-30 14:13:31 +00:00
2020-04-24 15:20:34 +00:00
cdptypes "github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/incentive/types"
)
// HandleRewardPeriodExpiry deletes expired RewardPeriods from the store and creates a ClaimPeriod in the store for each expired RewardPeriod
func ( k Keeper ) HandleRewardPeriodExpiry ( ctx sdk . Context , rp types . RewardPeriod ) {
k . CreateUniqueClaimPeriod ( ctx , rp . Denom , rp . ClaimEnd , rp . ClaimTimeLock )
store := prefix . NewStore ( ctx . KVStore ( k . key ) , types . RewardPeriodKeyPrefix )
store . Delete ( [ ] byte ( rp . Denom ) )
return
}
// CreateNewRewardPeriod creates a new reward period from the input reward
func ( k Keeper ) CreateNewRewardPeriod ( ctx sdk . Context , reward types . Reward ) {
2020-04-30 14:13:31 +00:00
// reward periods store the amount of rewards paid PER SECOND
2020-04-24 15:20:34 +00:00
rewardsPerSecond := sdk . NewDecFromInt ( reward . AvailableRewards . Amount ) . Quo ( sdk . NewDecFromInt ( sdk . NewInt ( int64 ( reward . Duration . Seconds ( ) ) ) ) ) . TruncateInt ( )
rewardCoinPerSecond := sdk . NewCoin ( reward . AvailableRewards . Denom , rewardsPerSecond )
rp := types . RewardPeriod {
Denom : reward . Denom ,
Start : ctx . BlockTime ( ) ,
End : ctx . BlockTime ( ) . Add ( reward . Duration ) ,
Reward : rewardCoinPerSecond ,
ClaimEnd : ctx . BlockTime ( ) . Add ( reward . Duration ) . Add ( reward . ClaimDuration ) ,
ClaimTimeLock : reward . TimeLock ,
}
k . SetRewardPeriod ( ctx , rp )
}
// CreateAndDeleteRewardPeriods creates reward periods for active rewards that don't already have a reward period and deletes reward periods for inactive rewards that currently have a reward period
func ( k Keeper ) CreateAndDeleteRewardPeriods ( ctx sdk . Context ) {
params := k . GetParams ( ctx )
for _ , r := range params . Rewards {
_ , found := k . GetRewardPeriod ( ctx , r . Denom )
// if governance has made a reward inactive, delete the current period
if found && ! r . Active {
k . DeleteRewardPeriod ( ctx , r . Denom )
}
// if a reward period for an active reward is not found, create one
if ! found && r . Active {
k . CreateNewRewardPeriod ( ctx , r )
}
}
}
// 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 {
previousBlockTime = ctx . BlockTime ( )
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
totalPrincipal := k . cdpKeeper . GetTotalPrincipal ( ctx , rp . Denom , types . PrincipalDenom )
// the number of seconds since last payout
timeElapsed := sdk . NewInt ( ctx . BlockTime ( ) . Unix ( ) - previousBlockTime . Unix ( ) )
if rp . End . Before ( ctx . BlockTime ( ) ) {
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 )
k . cdpKeeper . IterateCdpsByDenom ( ctx , rp . Denom , func ( cdp cdptypes . CDP ) bool {
2020-04-27 14:40:34 +00:00
rewardsShare := sdk . NewDecFromInt ( cdp . Principal . Amount . Add ( cdp . AccumulatedFees . Amount ) ) . Quo ( sdk . NewDecFromInt ( totalPrincipal ) )
2020-04-24 15:20:34 +00:00
// sanity check - don't create zero claims
if rewardsShare . IsZero ( ) {
return false
}
rewardsEarned := rewardsShare . Mul ( sdk . NewDecFromInt ( rewardsThisPeriod ) ) . RoundInt ( )
k . AddToClaim ( ctx , cdp . Owner , rp . Denom , id , sdk . NewCoin ( types . GovDenom , rewardsEarned ) )
return false
} )
if ! expired {
return false
}
k . HandleRewardPeriodExpiry ( ctx , rp )
return false
} )
k . SetPreviousBlockTime ( ctx , ctx . BlockTime ( ) )
}
// CreateUniqueClaimPeriod creates a new claim period in the store and updates the highest claim period id
func ( k Keeper ) CreateUniqueClaimPeriod ( ctx sdk . Context , denom string , end time . Time , timeLock time . Duration ) {
id := k . GetNextClaimPeriodID ( ctx , denom )
claimPeriod := types . NewClaimPeriod ( denom , id , end , timeLock )
k . SetClaimPeriod ( ctx , claimPeriod )
k . SetNextClaimPeriodID ( ctx , denom , id + 1 )
}
// AddToClaim adds the amount to an existing claim or creates a new one for that amount
func ( k Keeper ) AddToClaim ( ctx sdk . Context , addr sdk . AccAddress , denom string , id uint64 , amount sdk . Coin ) {
claim , found := k . GetClaim ( ctx , addr , denom , id )
if found {
claim . Reward = claim . Reward . Add ( amount )
} else {
claim = types . NewClaim ( addr , amount , denom , id )
}
k . SetClaim ( ctx , claim )
}