2020-09-21 21:08:43 +00:00
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
2020-12-21 17:18:55 +00:00
"github.com/kava-labs/kava/x/hard/types"
2020-09-21 21:08:43 +00:00
)
// ApplyDepositRewards iterates over lp and gov deposits and updates the amount of rewards for each depositor
func ( k Keeper ) ApplyDepositRewards ( ctx sdk . Context ) {
previousBlockTime , found := k . GetPreviousBlockTime ( ctx )
if ! found {
previousBlockTime = ctx . BlockTime ( )
k . SetPreviousBlockTime ( ctx , previousBlockTime )
return
}
params := k . GetParams ( ctx )
if ! params . Active {
return
}
timeElapsed := sdk . NewInt ( ctx . BlockTime ( ) . Unix ( ) - previousBlockTime . Unix ( ) )
for _ , lps := range params . LiquidityProviderSchedules {
if ! lps . Active {
continue
}
if lps . End . Before ( ctx . BlockTime ( ) ) {
continue
}
2020-09-30 18:44:56 +00:00
if lps . Start . After ( ctx . BlockTime ( ) ) {
continue
}
2020-11-12 16:43:28 +00:00
totalDeposited := k . GetTotalDeposited ( ctx , lps . DepositDenom )
2020-09-21 21:08:43 +00:00
if totalDeposited . IsZero ( ) {
continue
}
rewardsToDistribute := lps . RewardsPerSecond . Amount . Mul ( timeElapsed )
if rewardsToDistribute . IsZero ( ) {
continue
}
rewardsDistributed := sdk . ZeroInt ( )
2020-12-18 16:05:21 +00:00
k . IterateDeposits ( ctx , func ( dep types . Deposit ) ( stop bool ) {
rewardsShare := sdk . NewDecFromInt ( dep . Amount . AmountOf ( lps . DepositDenom ) ) . Quo ( sdk . NewDecFromInt ( totalDeposited ) )
2020-09-21 21:08:43 +00:00
if rewardsShare . IsZero ( ) {
return false
}
rewardsEarned := rewardsShare . Mul ( sdk . NewDecFromInt ( rewardsToDistribute ) ) . RoundInt ( )
if rewardsEarned . IsZero ( ) {
return false
}
2020-12-18 16:05:21 +00:00
k . AddToClaim ( ctx , dep . Depositor , lps . DepositDenom , types . LP , sdk . NewCoin ( lps . RewardsPerSecond . Denom , rewardsEarned ) )
2020-09-21 21:08:43 +00:00
rewardsDistributed = rewardsDistributed . Add ( rewardsEarned )
return false
} )
ctx . EventManager ( ) . EmitEvent (
sdk . NewEvent (
2020-12-21 17:18:55 +00:00
types . EventTypeHardLPDistribution ,
2020-09-21 21:08:43 +00:00
sdk . NewAttribute ( types . AttributeKeyBlockHeight , fmt . Sprintf ( "%d" , ctx . BlockHeight ( ) ) ) ,
sdk . NewAttribute ( types . AttributeKeyRewardsDistribution , rewardsDistributed . String ( ) ) ,
sdk . NewAttribute ( types . AttributeKeyDepositDenom , lps . DepositDenom ) ,
) ,
)
}
k . SetPreviousBlockTime ( ctx , ctx . BlockTime ( ) )
}
// ShouldDistributeValidatorRewards returns true if enough time has elapsed such that rewards should be distributed to delegators
func ( k Keeper ) ShouldDistributeValidatorRewards ( ctx sdk . Context , denom string ) bool {
previousDistributionTime , found := k . GetPreviousDelegatorDistribution ( ctx , denom )
if ! found {
k . SetPreviousDelegationDistribution ( ctx , ctx . BlockTime ( ) , denom )
return false
}
params := k . GetParams ( ctx )
if ! params . Active {
return false
}
for _ , dds := range params . DelegatorDistributionSchedules {
if denom != dds . DistributionSchedule . DepositDenom {
continue
}
2020-09-30 18:44:56 +00:00
if dds . DistributionSchedule . End . Before ( ctx . BlockTime ( ) ) {
continue
}
2020-09-21 21:08:43 +00:00
timeElapsed := sdk . NewInt ( ctx . BlockTime ( ) . Unix ( ) - previousDistributionTime . Unix ( ) )
2020-09-30 18:44:56 +00:00
if timeElapsed . GTE ( sdk . NewInt ( int64 ( dds . DistributionFrequency . Seconds ( ) ) ) ) {
2020-09-21 21:08:43 +00:00
return true
}
}
return false
}
// ApplyDelegationRewards iterates over each delegation object in the staking store and applies rewards according to the input delegation distribution schedule
func ( k Keeper ) ApplyDelegationRewards ( ctx sdk . Context , denom string ) {
dds , found := k . GetDelegatorSchedule ( ctx , denom )
if ! found {
return
}
if ! dds . DistributionSchedule . Active {
return
}
2020-09-30 18:44:56 +00:00
if dds . DistributionSchedule . Start . After ( ctx . BlockTime ( ) ) {
return
}
2020-09-21 21:08:43 +00:00
bondMacc := k . stakingKeeper . GetBondedPool ( ctx )
bondedCoinAmount := bondMacc . GetCoins ( ) . AmountOf ( dds . DistributionSchedule . DepositDenom )
if bondedCoinAmount . IsZero ( ) {
return
}
previousDistributionTime , found := k . GetPreviousDelegatorDistribution ( ctx , dds . DistributionSchedule . DepositDenom )
if ! found {
return
}
timeElapsed := sdk . NewInt ( ctx . BlockTime ( ) . Unix ( ) - previousDistributionTime . Unix ( ) )
rewardsToDistribute := dds . DistributionSchedule . RewardsPerSecond . Amount . Mul ( timeElapsed )
// create a map that has each validator address (sdk.ValAddress) as a key and the coversion factor for going from delegator shares to tokens for delegations to that validator.
// If a validator has never been slashed, the conversion factor will be 1.0, if they have been, it will be < 1.0
sharesToTokens := make ( map [ string ] sdk . Dec )
k . stakingKeeper . IterateValidators ( ctx , func ( index int64 , validator stakingexported . ValidatorI ) ( stop bool ) {
if validator . GetTokens ( ) . IsZero ( ) {
return false
}
2020-10-05 21:44:17 +00:00
// don't include a validator if it's unbonded or unbonding- ie delegators don't accumulate rewards when delegated to an unbonded/slashed validator
if validator . GetStatus ( ) != sdk . Bonded {
2020-09-21 21:08:43 +00:00
return false
}
2020-10-05 21:44:17 +00:00
sharesToTokens [ validator . GetOperator ( ) . String ( ) ] = sdk . NewDecFromInt ( validator . GetTokens ( ) ) . Quo ( validator . GetDelegatorShares ( ) )
2020-09-21 21:08:43 +00:00
return false
} )
rewardsDistributed := sdk . ZeroInt ( )
k . stakingKeeper . IterateAllDelegations ( ctx , func ( delegation stakingtypes . Delegation ) ( stop bool ) {
conversionFactor , ok := sharesToTokens [ delegation . ValidatorAddress . String ( ) ]
if ok {
delegationTokens := conversionFactor . Mul ( delegation . Shares )
delegationShare := delegationTokens . Quo ( sdk . NewDecFromInt ( bondedCoinAmount ) )
rewardsEarned := delegationShare . Mul ( sdk . NewDecFromInt ( rewardsToDistribute ) ) . RoundInt ( )
if rewardsEarned . IsZero ( ) {
return false
}
k . AddToClaim (
ctx , delegation . DelegatorAddress , dds . DistributionSchedule . DepositDenom ,
types . Stake , sdk . NewCoin ( dds . DistributionSchedule . RewardsPerSecond . Denom , rewardsEarned ) )
rewardsDistributed = rewardsDistributed . Add ( rewardsEarned )
}
return false
} )
ctx . EventManager ( ) . EmitEvent (
sdk . NewEvent (
2020-12-21 17:18:55 +00:00
types . EventTypeHardDelegatorDistribution ,
2020-09-21 21:08:43 +00:00
sdk . NewAttribute ( types . AttributeKeyBlockHeight , fmt . Sprintf ( "%d" , ctx . BlockHeight ( ) ) ) ,
sdk . NewAttribute ( types . AttributeKeyRewardsDistribution , rewardsDistributed . String ( ) ) ,
sdk . NewAttribute ( types . AttributeKeyDepositDenom , denom ) ,
) ,
)
}
// AddToClaim adds the input amount to an existing claim or creates a new one
2020-11-12 16:43:28 +00:00
func ( k Keeper ) AddToClaim ( ctx sdk . Context , owner sdk . AccAddress , depositDenom string , claimType types . ClaimType , amountToAdd sdk . Coin ) {
claim , found := k . GetClaim ( ctx , owner , depositDenom , claimType )
2020-09-21 21:08:43 +00:00
if ! found {
2020-11-12 16:43:28 +00:00
claim = types . NewClaim ( owner , depositDenom , amountToAdd , claimType )
2020-09-21 21:08:43 +00:00
} else {
claim . Amount = claim . Amount . Add ( amountToAdd )
}
k . SetClaim ( ctx , claim )
}