2021-05-04 14:47:21 +00:00
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/incentive/types"
)
// AccumulateHardDelegatorRewards updates the rewards accumulated for the input reward period
func ( k Keeper ) AccumulateHardDelegatorRewards ( ctx sdk . Context , rewardPeriod types . RewardPeriod ) error {
previousAccrualTime , found := k . GetPreviousHardDelegatorRewardAccrualTime ( ctx , rewardPeriod . CollateralType )
if ! found {
k . SetPreviousHardDelegatorRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
timeElapsed := CalculateTimeElapsed ( rewardPeriod . Start , rewardPeriod . End , ctx . BlockTime ( ) , previousAccrualTime )
if timeElapsed . IsZero ( ) {
return nil
}
if rewardPeriod . RewardsPerSecond . Amount . IsZero ( ) {
k . SetPreviousHardDelegatorRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
totalBonded := k . stakingKeeper . TotalBondedTokens ( ctx ) . ToDec ( )
if totalBonded . IsZero ( ) {
k . SetPreviousHardDelegatorRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
newRewards := timeElapsed . Mul ( rewardPeriod . RewardsPerSecond . Amount )
rewardFactor := newRewards . ToDec ( ) . Quo ( totalBonded )
previousRewardFactor , found := k . GetHardDelegatorRewardFactor ( ctx , rewardPeriod . CollateralType )
if ! found {
previousRewardFactor = sdk . ZeroDec ( )
}
newRewardFactor := previousRewardFactor . Add ( rewardFactor )
k . SetHardDelegatorRewardFactor ( ctx , rewardPeriod . CollateralType , newRewardFactor )
k . SetPreviousHardDelegatorRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
// InitializeHardDelegatorReward initializes the delegator reward index of a hard claim
func ( k Keeper ) InitializeHardDelegatorReward ( ctx sdk . Context , delegator sdk . AccAddress ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , delegator )
if ! found {
claim = types . NewHardLiquidityProviderClaim ( delegator , sdk . Coins { } , nil , nil , nil )
} else {
k . SynchronizeHardDelegatorRewards ( ctx , delegator , nil , false )
claim , _ = k . GetHardLiquidityProviderClaim ( ctx , delegator )
}
2021-06-21 21:05:17 +00:00
globalRewardFactor , found := k . GetHardDelegatorRewardFactor ( ctx , types . BondDenom )
if ! found {
// If there's no global delegator reward factor, initialize the claim with a delegator factor of zero.
globalRewardFactor = sdk . ZeroDec ( )
}
claim . DelegatorRewardIndexes = types . RewardIndexes { types . NewRewardIndex ( types . BondDenom , globalRewardFactor ) }
2021-05-04 14:47:21 +00:00
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// SynchronizeHardDelegatorRewards updates the claim object by adding any accumulated rewards, and setting the reward indexes to the global values.
// valAddr and shouldIncludeValidator are used to ignore or include delegations to a particular validator when summing up the total delegation.
// Normally only delegations to Bonded validators are included in the total. This is needed as staking hooks are sometimes called on the wrong side of a validator's state update (from this module's perspective).
func ( k Keeper ) SynchronizeHardDelegatorRewards ( ctx sdk . Context , delegator sdk . AccAddress , valAddr sdk . ValAddress , shouldIncludeValidator bool ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , delegator )
if ! found {
return
}
2021-06-21 21:05:17 +00:00
globalRewardFactor , found := k . GetHardDelegatorRewardFactor ( ctx , types . BondDenom )
2021-05-04 14:47:21 +00:00
if ! found {
2021-06-21 21:05:17 +00:00
// The global factor is only not found if
// - the bond denom has not started accumulating rewards yet (either there is no reward specified in params, or the reward start time hasn't been hit)
// - OR it was wrongly deleted from state (factors should never be removed while unsynced claims exist)
// If not found we could either skip this sync, or assume the global factor is zero.
// Skipping will avoid storing unnecessary factors in the claim for non rewarded denoms.
// And in the event a global factor is wrongly deleted, it will avoid this function panicking when calculating rewards.
2021-05-04 14:47:21 +00:00
return
}
2021-06-21 21:05:17 +00:00
userRewardFactor , found := claim . DelegatorRewardIndexes . Get ( types . BondDenom )
if ! found {
// Normally the factor should always be found, as it is added in InitializeHardDelegatorReward when a user delegates.
// However if there were no delegator rewards (ie no reward period in params) then a reward period is added, existing claims will not have the factor.
// So assume the factor is the starting value for any global factor: 0.
userRewardFactor = sdk . ZeroDec ( )
2021-05-04 14:47:21 +00:00
}
2021-06-21 21:05:17 +00:00
totalDelegated := k . GetTotalDelegated ( ctx , delegator , valAddr , shouldIncludeValidator )
rewardsEarned , err := k . CalculateSingleReward ( userRewardFactor , globalRewardFactor , totalDelegated )
if err != nil {
// Global reward factors should never decrease, as it would lead to a negative update to claim.Rewards.
// This panics if a global reward factor decreases or disappears between the old and new indexes.
panic ( fmt . Sprintf ( "corrupted global reward indexes found: %v" , err ) )
2021-05-04 14:47:21 +00:00
}
2021-06-21 21:05:17 +00:00
newRewardsCoin := sdk . NewCoin ( types . HardLiquidityRewardDenom , rewardsEarned )
2021-05-04 14:47:21 +00:00
2021-06-21 21:05:17 +00:00
claim . Reward = claim . Reward . Add ( newRewardsCoin )
claim . DelegatorRewardIndexes = claim . DelegatorRewardIndexes . With ( types . BondDenom , globalRewardFactor )
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
func ( k Keeper ) GetTotalDelegated ( ctx sdk . Context , delegator sdk . AccAddress , valAddr sdk . ValAddress , shouldIncludeValidator bool ) sdk . Dec {
2021-05-04 14:47:21 +00:00
totalDelegated := sdk . ZeroDec ( )
delegations := k . stakingKeeper . GetDelegatorDelegations ( ctx , delegator , 200 )
for _ , delegation := range delegations {
validator , found := k . stakingKeeper . GetValidator ( ctx , delegation . GetValidatorAddr ( ) )
if ! found {
continue
}
2021-06-21 21:05:17 +00:00
if validator . OperatorAddress . Equals ( valAddr ) {
if shouldIncludeValidator {
// do nothing, so the validator is included regardless of bonded status
} else {
// skip this validator
2021-05-04 14:47:21 +00:00
continue
}
} else {
2021-06-21 21:05:17 +00:00
// skip any not bonded validator
if validator . GetStatus ( ) != sdk . Bonded {
2021-05-04 14:47:21 +00:00
continue
}
}
if validator . GetTokens ( ) . IsZero ( ) {
continue
}
delegatedTokens := validator . TokensFromShares ( delegation . GetShares ( ) )
if delegatedTokens . IsNegative ( ) {
continue
}
totalDelegated = totalDelegated . Add ( delegatedTokens )
}
2021-06-21 21:05:17 +00:00
return totalDelegated
2021-05-04 14:47:21 +00:00
}