2020-04-24 15:20:34 +00:00
package keeper
import (
2021-01-21 13:52:09 +00:00
"fmt"
2021-01-18 19:12:37 +00:00
"math"
2020-04-24 15:20:34 +00:00
"time"
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"
2021-01-21 13:52:09 +00:00
hardtypes "github.com/kava-labs/kava/x/hard/types"
2020-04-24 15:20:34 +00:00
"github.com/kava-labs/kava/x/incentive/types"
)
2021-01-21 13:52:09 +00:00
// AccumulateUSDXMintingRewards updates the rewards accumulated for the input reward period
func ( k Keeper ) AccumulateUSDXMintingRewards ( ctx sdk . Context , rewardPeriod types . RewardPeriod ) error {
previousAccrualTime , found := k . GetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType )
2021-01-18 19:12:37 +00:00
if ! found {
2021-01-21 13:52:09 +00:00
k . SetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
2021-01-18 19:12:37 +00:00
return nil
}
2021-02-02 16:17:46 +00:00
timeElapsed := CalculateTimeElapsed ( rewardPeriod . Start , rewardPeriod . End , ctx . BlockTime ( ) , previousAccrualTime )
2021-01-18 19:12:37 +00:00
if timeElapsed . IsZero ( ) {
return nil
}
if rewardPeriod . RewardsPerSecond . Amount . IsZero ( ) {
2021-01-21 13:52:09 +00:00
k . SetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
2021-01-18 19:12:37 +00:00
return nil
}
totalPrincipal := k . cdpKeeper . GetTotalPrincipal ( ctx , rewardPeriod . CollateralType , types . PrincipalDenom ) . ToDec ( )
if totalPrincipal . IsZero ( ) {
2021-01-21 13:52:09 +00:00
k . SetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
2021-01-18 19:12:37 +00:00
return nil
}
newRewards := timeElapsed . Mul ( rewardPeriod . RewardsPerSecond . Amount )
cdpFactor , found := k . cdpKeeper . GetInterestFactor ( ctx , rewardPeriod . CollateralType )
if ! found {
2021-01-21 13:52:09 +00:00
k . SetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
2021-01-18 19:12:37 +00:00
return nil
}
rewardFactor := newRewards . ToDec ( ) . Mul ( cdpFactor ) . Quo ( totalPrincipal )
2020-05-07 17:46:40 +00:00
2021-01-21 13:52:09 +00:00
previousRewardFactor , found := k . GetUSDXMintingRewardFactor ( ctx , rewardPeriod . CollateralType )
2021-01-18 19:12:37 +00:00
if ! found {
previousRewardFactor = sdk . ZeroDec ( )
}
newRewardFactor := previousRewardFactor . Add ( rewardFactor )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingRewardFactor ( ctx , rewardPeriod . CollateralType , newRewardFactor )
k . SetPreviousUSDXMintingAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
// AccumulateHardBorrowRewards updates the rewards accumulated for the input reward period
2021-02-02 16:17:46 +00:00
func ( k Keeper ) AccumulateHardBorrowRewards ( ctx sdk . Context , rewardPeriod types . MultiRewardPeriod ) error {
2021-01-21 13:52:09 +00:00
previousAccrualTime , found := k . GetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType )
if ! found {
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
2021-02-02 16:17:46 +00:00
timeElapsed := CalculateTimeElapsed ( rewardPeriod . Start , rewardPeriod . End , ctx . BlockTime ( ) , previousAccrualTime )
2021-01-21 13:52:09 +00:00
if timeElapsed . IsZero ( ) {
return nil
}
2021-02-02 16:17:46 +00:00
if rewardPeriod . RewardsPerSecond . IsZero ( ) {
2021-01-21 13:52:09 +00:00
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
2021-02-02 16:17:46 +00:00
2021-01-21 13:52:09 +00:00
totalBorrowedCoins , foundTotalBorrowedCoins := k . hardKeeper . GetBorrowedCoins ( ctx )
2021-02-02 16:17:46 +00:00
if ! foundTotalBorrowedCoins {
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
totalBorrowed := totalBorrowedCoins . AmountOf ( rewardPeriod . CollateralType ) . ToDec ( )
if totalBorrowed . IsZero ( ) {
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
previousRewardIndexes , found := k . GetHardBorrowRewardIndexes ( ctx , rewardPeriod . CollateralType )
if ! found {
for _ , rewardCoin := range rewardPeriod . RewardsPerSecond {
rewardIndex := types . NewRewardIndex ( rewardCoin . Denom , sdk . ZeroDec ( ) )
previousRewardIndexes = append ( previousRewardIndexes , rewardIndex )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
k . SetHardBorrowRewardIndexes ( ctx , rewardPeriod . CollateralType , previousRewardIndexes )
}
hardFactor , found := k . hardKeeper . GetBorrowInterestFactor ( ctx , rewardPeriod . CollateralType )
if ! found {
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
newRewardIndexes := previousRewardIndexes
for _ , rewardCoin := range rewardPeriod . RewardsPerSecond {
newRewards := rewardCoin . Amount . ToDec ( ) . Mul ( timeElapsed . ToDec ( ) )
previousRewardIndex , found := previousRewardIndexes . GetRewardIndex ( rewardCoin . Denom )
2021-01-21 13:52:09 +00:00
if ! found {
2021-02-02 16:17:46 +00:00
previousRewardIndex = types . NewRewardIndex ( rewardCoin . Denom , sdk . ZeroDec ( ) )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
// Calculate new reward factor and update reward index
rewardFactor := newRewards . Mul ( hardFactor ) . Quo ( totalBorrowed )
newRewardFactorValue := previousRewardIndex . RewardFactor . Add ( rewardFactor )
newRewardIndex := types . NewRewardIndex ( rewardCoin . Denom , newRewardFactorValue )
i , found := newRewardIndexes . GetFactorIndex ( rewardCoin . Denom )
if found {
newRewardIndexes [ i ] = newRewardIndex
} else {
newRewardIndexes = append ( newRewardIndexes , newRewardIndex )
2021-01-21 13:52:09 +00:00
}
}
2021-02-02 16:17:46 +00:00
k . SetHardBorrowRewardIndexes ( ctx , rewardPeriod . CollateralType , newRewardIndexes )
2021-01-21 13:52:09 +00:00
k . SetPreviousHardBorrowRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
// AccumulateHardSupplyRewards updates the rewards accumulated for the input reward period
2021-02-02 16:17:46 +00:00
func ( k Keeper ) AccumulateHardSupplyRewards ( ctx sdk . Context , rewardPeriod types . MultiRewardPeriod ) error {
2021-01-21 13:52:09 +00:00
previousAccrualTime , found := k . GetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType )
if ! found {
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
2021-02-02 16:17:46 +00:00
timeElapsed := CalculateTimeElapsed ( rewardPeriod . Start , rewardPeriod . End , ctx . BlockTime ( ) , previousAccrualTime )
2021-01-21 13:52:09 +00:00
if timeElapsed . IsZero ( ) {
return nil
}
2021-02-02 16:17:46 +00:00
if rewardPeriod . RewardsPerSecond . IsZero ( ) {
2021-01-21 13:52:09 +00:00
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
totalSuppliedCoins , foundTotalSuppliedCoins := k . hardKeeper . GetSuppliedCoins ( ctx )
2021-02-02 16:17:46 +00:00
if ! foundTotalSuppliedCoins {
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
totalSupplied := totalSuppliedCoins . AmountOf ( rewardPeriod . CollateralType ) . ToDec ( )
if totalSupplied . IsZero ( ) {
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
previousRewardIndexes , found := k . GetHardSupplyRewardIndexes ( ctx , rewardPeriod . CollateralType )
if ! found {
for _ , rewardCoin := range rewardPeriod . RewardsPerSecond {
rewardIndex := types . NewRewardIndex ( rewardCoin . Denom , sdk . ZeroDec ( ) )
previousRewardIndexes = append ( previousRewardIndexes , rewardIndex )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
k . SetHardSupplyRewardIndexes ( ctx , rewardPeriod . CollateralType , previousRewardIndexes )
}
hardFactor , found := k . hardKeeper . GetSupplyInterestFactor ( ctx , rewardPeriod . CollateralType )
if ! found {
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
return nil
}
newRewardIndexes := previousRewardIndexes
for _ , rewardCoin := range rewardPeriod . RewardsPerSecond {
newRewards := rewardCoin . Amount . ToDec ( ) . Mul ( timeElapsed . ToDec ( ) )
previousRewardIndex , found := previousRewardIndexes . GetRewardIndex ( rewardCoin . Denom )
2021-01-21 13:52:09 +00:00
if ! found {
2021-02-02 16:17:46 +00:00
previousRewardIndex = types . NewRewardIndex ( rewardCoin . Denom , sdk . ZeroDec ( ) )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
// Calculate new reward factor and update reward index
rewardFactor := newRewards . Mul ( hardFactor ) . Quo ( totalSupplied )
newRewardFactorValue := previousRewardIndex . RewardFactor . Add ( rewardFactor )
newRewardIndex := types . NewRewardIndex ( rewardCoin . Denom , newRewardFactorValue )
i , found := newRewardIndexes . GetFactorIndex ( rewardCoin . Denom )
if found {
newRewardIndexes [ i ] = newRewardIndex
} else {
newRewardIndexes = append ( newRewardIndexes , newRewardIndex )
2021-01-21 13:52:09 +00:00
}
}
2021-02-02 16:17:46 +00:00
k . SetHardSupplyRewardIndexes ( ctx , rewardPeriod . CollateralType , newRewardIndexes )
2021-01-21 13:52:09 +00:00
k . SetPreviousHardSupplyRewardAccrualTime ( ctx , rewardPeriod . CollateralType , ctx . BlockTime ( ) )
2021-01-18 19:12:37 +00:00
return nil
2020-04-24 15:20:34 +00:00
}
2021-01-21 13:52:09 +00:00
// InitializeUSDXMintingClaim creates or updates a claim such that no new rewards are accrued, but any existing rewards are not lost.
2021-01-18 19:12:37 +00:00
// this function should be called after a cdp is created. If a user previously had a cdp, then closed it, they shouldn't
// accrue rewards during the period the cdp was closed. By setting the reward factor to the current global reward factor,
// any unclaimed rewards are preserved, but no new rewards are added.
2021-01-21 13:52:09 +00:00
func ( k Keeper ) InitializeUSDXMintingClaim ( ctx sdk . Context , cdp cdptypes . CDP ) {
_ , found := k . GetUSDXMintingRewardPeriod ( ctx , cdp . Type )
2021-01-18 19:12:37 +00:00
if ! found {
// this collateral type is not incentivized, do nothing
return
2020-04-24 15:20:34 +00:00
}
2021-01-21 13:52:09 +00:00
rewardFactor , found := k . GetUSDXMintingRewardFactor ( ctx , cdp . Type )
2021-01-18 19:12:37 +00:00
if ! found {
rewardFactor = sdk . ZeroDec ( )
}
2021-01-21 13:52:09 +00:00
claim , found := k . GetUSDXMintingClaim ( ctx , cdp . Owner )
2021-01-18 19:12:37 +00:00
if ! found { // this is the owner's first usdx minting reward claim
claim = types . NewUSDXMintingClaim ( cdp . Owner , sdk . NewCoin ( types . USDXMintingRewardDenom , sdk . ZeroInt ( ) ) , types . RewardIndexes { types . NewRewardIndex ( cdp . Type , rewardFactor ) } )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return
}
// the owner has an existing usdx minting reward claim
index , hasRewardIndex := claim . HasRewardIndex ( cdp . Type )
if ! hasRewardIndex { // this is the owner's first usdx minting reward for this collateral type
claim . RewardIndexes = append ( claim . RewardIndexes , types . NewRewardIndex ( cdp . Type , rewardFactor ) )
} else { // the owner has a previous usdx minting reward for this collateral type
claim . RewardIndexes [ index ] = types . NewRewardIndex ( cdp . Type , rewardFactor )
}
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2020-04-24 15:20:34 +00:00
}
2021-01-21 13:52:09 +00:00
// SynchronizeUSDXMintingReward updates the claim object by adding any accumulated rewards and updating the reward index value.
2021-01-18 19:12:37 +00:00
// this should be called before a cdp is modified, immediately after the 'SynchronizeInterest' method is called in the cdp module
2021-01-21 13:52:09 +00:00
func ( k Keeper ) SynchronizeUSDXMintingReward ( ctx sdk . Context , cdp cdptypes . CDP ) {
_ , found := k . GetUSDXMintingRewardPeriod ( ctx , cdp . Type )
2020-04-24 15:20:34 +00:00
if ! found {
2021-01-18 19:12:37 +00:00
// this collateral type is not incentivized, do nothing
2020-04-24 15:20:34 +00:00
return
}
2020-06-17 09:09:44 +00:00
2021-01-21 13:52:09 +00:00
globalRewardFactor , found := k . GetUSDXMintingRewardFactor ( ctx , cdp . Type )
2021-01-18 19:12:37 +00:00
if ! found {
globalRewardFactor = sdk . ZeroDec ( )
}
2021-01-21 13:52:09 +00:00
claim , found := k . GetUSDXMintingClaim ( ctx , cdp . Owner )
2021-01-18 19:12:37 +00:00
if ! found {
claim = types . NewUSDXMintingClaim ( cdp . Owner , sdk . NewCoin ( types . USDXMintingRewardDenom , sdk . ZeroInt ( ) ) , types . RewardIndexes { types . NewRewardIndex ( cdp . Type , globalRewardFactor ) } )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return
}
2020-06-17 09:09:44 +00:00
2021-01-18 19:12:37 +00:00
// the owner has an existing usdx minting reward claim
index , hasRewardIndex := claim . HasRewardIndex ( cdp . Type )
if ! hasRewardIndex { // this is the owner's first usdx minting reward for this collateral type
claim . RewardIndexes = append ( claim . RewardIndexes , types . NewRewardIndex ( cdp . Type , globalRewardFactor ) )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return
}
userRewardFactor := claim . RewardIndexes [ index ] . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
return
}
claim . RewardIndexes [ index ] . RewardFactor = globalRewardFactor
2021-01-21 13:52:09 +00:00
newRewardsAmount := rewardsAccumulatedFactor . Mul ( cdp . GetTotalPrincipal ( ) . Amount . ToDec ( ) ) . RoundInt ( )
2021-01-18 19:12:37 +00:00
if newRewardsAmount . IsZero ( ) {
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return
}
newRewardsCoin := sdk . NewCoin ( types . USDXMintingRewardDenom , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return
}
2020-06-17 09:09:44 +00:00
2021-01-21 13:52:09 +00:00
// InitializeHardSupplyReward initializes the supply-side of a hard liquidity provider claim
// by creating the claim and setting the supply reward factor index
func ( k Keeper ) InitializeHardSupplyReward ( ctx sdk . Context , deposit hardtypes . Deposit ) {
2021-02-02 16:17:46 +00:00
var supplyRewardIndexes types . MultiRewardIndexes
2021-01-21 13:52:09 +00:00
for _ , coin := range deposit . Amount {
2021-02-02 16:17:46 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardSupplyRewardIndexes ( ctx , coin . Denom )
2021-02-19 20:02:51 +00:00
var multiRewardIndex types . MultiRewardIndex
if foundGlobalRewardIndexes {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , globalRewardIndexes )
} else {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , types . RewardIndexes { } )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
supplyRewardIndexes = append ( supplyRewardIndexes , multiRewardIndex )
2021-01-21 13:52:09 +00:00
}
claim , found := k . GetHardLiquidityProviderClaim ( ctx , deposit . Depositor )
if found {
// Reset borrow reward indexes
2021-02-02 16:17:46 +00:00
claim . BorrowRewardIndexes = types . MultiRewardIndexes { }
2021-01-21 13:52:09 +00:00
} else {
// Instantiate claim object
2021-02-02 16:17:46 +00:00
claim = types . NewHardLiquidityProviderClaim ( deposit . Depositor , sdk . Coins { } , nil , nil , nil )
2021-01-21 13:52:09 +00:00
}
claim . SupplyRewardIndexes = supplyRewardIndexes
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// SynchronizeHardSupplyReward updates the claim object by adding any accumulated rewards
// and updating the reward index value
func ( k Keeper ) SynchronizeHardSupplyReward ( ctx sdk . Context , deposit hardtypes . Deposit ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , deposit . Depositor )
if ! found {
return
}
for _ , coin := range deposit . Amount {
2021-02-02 16:17:46 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardSupplyRewardIndexes ( ctx , coin . Denom )
if ! foundGlobalRewardIndexes {
2021-01-21 13:52:09 +00:00
continue
}
2021-02-19 20:02:51 +00:00
userMultiRewardIndex , foundUserMultiRewardIndex := claim . SupplyRewardIndexes . GetRewardIndex ( coin . Denom )
if ! foundUserMultiRewardIndex {
2021-01-21 13:52:09 +00:00
continue
}
2021-02-03 22:23:53 +00:00
userRewardIndexIndex , foundUserRewardIndexIndex := claim . SupplyRewardIndexes . GetRewardIndexIndex ( coin . Denom )
if ! foundUserRewardIndexIndex {
fmt . Printf ( "\n[LOG]: factor index for %s should always be found" , coin . Denom ) // TODO: remove before production
continue
}
2021-02-02 16:17:46 +00:00
for _ , globalRewardIndex := range globalRewardIndexes {
2021-02-19 20:02:51 +00:00
userRewardIndex , foundUserRewardIndex := userMultiRewardIndex . RewardIndexes . GetRewardIndex ( globalRewardIndex . CollateralType )
2021-02-02 16:17:46 +00:00
if ! foundUserRewardIndex {
2021-02-19 20:02:51 +00:00
// User deposited this coin type before it had rewards. When new rewards are added, legacy depositors
// should immediately begin earning rewards. Enable users to do so by updating their claim with the global
// reward index denom and start their reward factor at 0.0
userRewardIndex = types . NewRewardIndex ( globalRewardIndex . CollateralType , sdk . ZeroDec ( ) )
userMultiRewardIndex . RewardIndexes = append ( userMultiRewardIndex . RewardIndexes , userRewardIndex )
claim . SupplyRewardIndexes [ userRewardIndexIndex ] = userMultiRewardIndex
2021-02-02 16:17:46 +00:00
}
2021-01-21 13:52:09 +00:00
2021-02-02 16:17:46 +00:00
globalRewardFactor := globalRewardIndex . RewardFactor
userRewardFactor := userRewardIndex . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
continue
}
newRewardsAmount := rewardsAccumulatedFactor . Mul ( deposit . Amount . AmountOf ( coin . Denom ) . ToDec ( ) ) . RoundInt ( )
if newRewardsAmount . IsZero ( ) || newRewardsAmount . IsNegative ( ) {
continue
}
2021-01-21 13:52:09 +00:00
2021-02-19 20:02:51 +00:00
factorIndex , foundFactorIndex := userMultiRewardIndex . RewardIndexes . GetFactorIndex ( globalRewardIndex . CollateralType )
2021-02-02 16:17:46 +00:00
if ! foundFactorIndex {
fmt . Printf ( "[LOG]: factor index for %s should always be found" , coin . Denom ) // TODO: remove before production
continue
}
claim . SupplyRewardIndexes [ userRewardIndexIndex ] . RewardIndexes [ factorIndex ] . RewardFactor = globalRewardIndex . RewardFactor
newRewardsCoin := sdk . NewCoin ( userRewardIndex . CollateralType , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
}
2021-01-21 13:52:09 +00:00
}
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// InitializeHardBorrowReward initializes the borrow-side of a hard liquidity provider claim
// by creating the claim and setting the borrow reward factor index
func ( k Keeper ) InitializeHardBorrowReward ( ctx sdk . Context , borrow hardtypes . Borrow ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , borrow . Borrower )
if ! found {
2021-02-02 16:17:46 +00:00
claim = types . NewHardLiquidityProviderClaim ( borrow . Borrower , sdk . Coins { } , nil , nil , nil )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
var borrowRewardIndexes types . MultiRewardIndexes
2021-01-21 13:52:09 +00:00
for _ , coin := range borrow . Amount {
2021-02-02 16:17:46 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardBorrowRewardIndexes ( ctx , coin . Denom )
2021-02-19 20:02:51 +00:00
var multiRewardIndex types . MultiRewardIndex
if foundGlobalRewardIndexes {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , globalRewardIndexes )
} else {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , types . RewardIndexes { } )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
borrowRewardIndexes = append ( borrowRewardIndexes , multiRewardIndex )
2021-01-21 13:52:09 +00:00
}
claim . BorrowRewardIndexes = borrowRewardIndexes
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// SynchronizeHardBorrowReward updates the claim object by adding any accumulated rewards
// and updating the reward index value
func ( k Keeper ) SynchronizeHardBorrowReward ( ctx sdk . Context , borrow hardtypes . Borrow ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , borrow . Borrower )
if ! found {
return
}
for _ , coin := range borrow . Amount {
2021-02-02 16:17:46 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardBorrowRewardIndexes ( ctx , coin . Denom )
if ! foundGlobalRewardIndexes {
2021-01-21 13:52:09 +00:00
continue
}
2021-02-19 20:02:51 +00:00
userMultiRewardIndex , foundUserMultiRewardIndex := claim . BorrowRewardIndexes . GetRewardIndex ( coin . Denom )
if ! foundUserMultiRewardIndex {
2021-01-21 13:52:09 +00:00
continue
}
2021-02-03 22:23:53 +00:00
userRewardIndexIndex , foundUserRewardIndexIndex := claim . BorrowRewardIndexes . GetRewardIndexIndex ( coin . Denom )
if ! foundUserRewardIndexIndex {
fmt . Printf ( "\n[LOG]: factor index for %s should always be found" , coin . Denom ) // TODO: remove before production
continue
}
2021-02-02 16:17:46 +00:00
for _ , globalRewardIndex := range globalRewardIndexes {
2021-02-19 20:02:51 +00:00
userRewardIndex , foundUserRewardIndex := userMultiRewardIndex . RewardIndexes . GetRewardIndex ( globalRewardIndex . CollateralType )
2021-02-02 16:17:46 +00:00
if ! foundUserRewardIndex {
2021-02-19 20:02:51 +00:00
// User borrowed this coin type before it had rewards. When new rewards are added, legacy borrowers
// should immediately begin earning rewards. Enable users to do so by updating their claim with the global
// reward index denom and start their reward factor at 0.0
userRewardIndex = types . NewRewardIndex ( globalRewardIndex . CollateralType , sdk . ZeroDec ( ) )
userMultiRewardIndex . RewardIndexes = append ( userMultiRewardIndex . RewardIndexes , userRewardIndex )
claim . BorrowRewardIndexes [ userRewardIndexIndex ] = userMultiRewardIndex
2021-02-02 16:17:46 +00:00
}
2021-01-21 13:52:09 +00:00
2021-02-02 16:17:46 +00:00
globalRewardFactor := globalRewardIndex . RewardFactor
userRewardFactor := userRewardIndex . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
continue
}
newRewardsAmount := rewardsAccumulatedFactor . Mul ( borrow . Amount . AmountOf ( coin . Denom ) . ToDec ( ) ) . RoundInt ( )
if newRewardsAmount . IsZero ( ) || newRewardsAmount . IsNegative ( ) {
continue
}
2021-01-21 13:52:09 +00:00
2021-02-19 20:02:51 +00:00
factorIndex , foundFactorIndex := userMultiRewardIndex . RewardIndexes . GetFactorIndex ( globalRewardIndex . CollateralType )
2021-02-02 16:17:46 +00:00
if ! foundFactorIndex {
fmt . Printf ( "\n[LOG]: factor index for %s should always be found" , coin . Denom ) // TODO: remove before production
continue
}
claim . BorrowRewardIndexes [ userRewardIndexIndex ] . RewardIndexes [ factorIndex ] . RewardFactor = globalRewardIndex . RewardFactor
newRewardsCoin := sdk . NewCoin ( userRewardIndex . CollateralType , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
}
2021-01-21 13:52:09 +00:00
}
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// UpdateHardSupplyIndexDenoms adds any new deposit denoms to the claim's supply reward index
func ( k Keeper ) UpdateHardSupplyIndexDenoms ( ctx sdk . Context , deposit hardtypes . Deposit ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , deposit . Depositor )
if ! found {
2021-02-02 16:17:46 +00:00
claim = types . NewHardLiquidityProviderClaim ( deposit . Depositor , sdk . Coins { } , nil , nil , nil )
2021-01-21 13:52:09 +00:00
}
supplyRewardIndexes := claim . SupplyRewardIndexes
for _ , coin := range deposit . Amount {
2021-02-02 16:17:46 +00:00
_ , foundUserRewardIndexes := claim . SupplyRewardIndexes . GetRewardIndex ( coin . Denom )
if ! foundUserRewardIndexes {
2021-02-19 20:02:51 +00:00
globalSupplyRewardIndexes , foundGlobalSupplyRewardIndexes := k . GetHardSupplyRewardIndexes ( ctx , coin . Denom )
var multiRewardIndex types . MultiRewardIndex
if foundGlobalSupplyRewardIndexes {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , globalSupplyRewardIndexes )
} else {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , types . RewardIndexes { } )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
supplyRewardIndexes = append ( supplyRewardIndexes , multiRewardIndex )
2021-01-21 13:52:09 +00:00
}
}
if len ( supplyRewardIndexes ) == 0 {
return
}
claim . SupplyRewardIndexes = supplyRewardIndexes
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// UpdateHardBorrowIndexDenoms adds any new borrow denoms to the claim's supply reward index
func ( k Keeper ) UpdateHardBorrowIndexDenoms ( ctx sdk . Context , borrow hardtypes . Borrow ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , borrow . Borrower )
if ! found {
2021-02-02 16:17:46 +00:00
claim = types . NewHardLiquidityProviderClaim ( borrow . Borrower , sdk . Coins { } , nil , nil , nil )
2021-01-21 13:52:09 +00:00
}
borrowRewardIndexes := claim . BorrowRewardIndexes
for _ , coin := range borrow . Amount {
2021-02-02 16:17:46 +00:00
_ , foundUserRewardIndexes := claim . BorrowRewardIndexes . GetRewardIndex ( coin . Denom )
if ! foundUserRewardIndexes {
2021-02-19 20:02:51 +00:00
globalBorrowRewardIndexes , foundGlobalBorrowRewardIndexes := k . GetHardBorrowRewardIndexes ( ctx , coin . Denom )
var multiRewardIndex types . MultiRewardIndex
if foundGlobalBorrowRewardIndexes {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , globalBorrowRewardIndexes )
} else {
multiRewardIndex = types . NewMultiRewardIndex ( coin . Denom , types . RewardIndexes { } )
2021-01-21 13:52:09 +00:00
}
2021-02-02 16:17:46 +00:00
borrowRewardIndexes = append ( borrowRewardIndexes , multiRewardIndex )
2021-01-21 13:52:09 +00:00
}
}
if len ( borrowRewardIndexes ) == 0 {
return
}
claim . BorrowRewardIndexes = borrowRewardIndexes
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
2021-01-25 12:58:12 +00:00
// SynchronizeHardDelegatorRewards updates the claim object by adding any accumulated rewards
func ( k Keeper ) SynchronizeHardDelegatorRewards ( ctx sdk . Context , delegator sdk . AccAddress ) {
claim , found := k . GetHardLiquidityProviderClaim ( ctx , delegator )
if ! found {
return
}
delagatorFactor , found := k . GetHardDelegatorRewardFactor ( ctx , types . BondDenom )
if ! found {
return
}
delegatorIndex , hasDelegatorRewardIndex := claim . HasDelegatorRewardIndex ( types . BondDenom )
if ! hasDelegatorRewardIndex {
return
}
userRewardFactor := claim . DelegatorRewardIndexes [ delegatorIndex ] . RewardFactor
rewardsAccumulatedFactor := delagatorFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
return
}
claim . DelegatorRewardIndexes [ delegatorIndex ] . RewardFactor = delagatorFactor
totalDelegated := sdk . ZeroDec ( )
// TODO: set reasonable max limit on delegation iteration
maxUInt := ^ uint16 ( 0 )
delegations := k . stakingKeeper . GetDelegatorDelegations ( ctx , delegator , maxUInt )
for _ , delegation := range delegations {
validator , found := k . stakingKeeper . GetValidator ( ctx , delegation . GetValidatorAddr ( ) )
if ! found {
continue
}
// Delegators don't accumulate rewards if their validator is unbonded/slashed
if validator . GetStatus ( ) != sdk . Bonded {
continue
}
if validator . GetTokens ( ) . IsZero ( ) {
continue
}
delegatedTokens := validator . TokensFromShares ( delegation . GetShares ( ) )
if delegatedTokens . IsZero ( ) || delegatedTokens . IsNegative ( ) {
continue
}
totalDelegated = totalDelegated . Add ( delegatedTokens )
}
rewardsEarned := rewardsAccumulatedFactor . Mul ( totalDelegated ) . RoundInt ( )
if rewardsEarned . IsZero ( ) || rewardsEarned . IsNegative ( ) {
return
}
// Add rewards to delegator's hard claim
newRewardsCoin := sdk . NewCoin ( types . HardLiquidityRewardDenom , rewardsEarned )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
// 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
}
2021-02-02 16:17:46 +00:00
timeElapsed := CalculateTimeElapsed ( rewardPeriod . Start , rewardPeriod . End , ctx . BlockTime ( ) , previousAccrualTime )
2021-01-25 12:58:12 +00:00
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 ) {
delegatorFactor , foundDelegatorFactor := k . GetHardDelegatorRewardFactor ( ctx , types . BondDenom )
if ! foundDelegatorFactor { // Should always be found...
delegatorFactor = sdk . ZeroDec ( )
}
delegatorRewardIndexes := types . NewRewardIndex ( types . BondDenom , delegatorFactor )
claim , found := k . GetHardLiquidityProviderClaim ( ctx , delegator )
if ! found {
// Instantiate claim object
2021-02-02 16:17:46 +00:00
claim = types . NewHardLiquidityProviderClaim ( delegator , sdk . Coins { } , nil , nil , nil )
2021-01-25 12:58:12 +00:00
}
claim . DelegatorRewardIndexes = types . RewardIndexes { delegatorRewardIndexes }
k . SetHardLiquidityProviderClaim ( ctx , claim )
}
2021-01-26 11:52:34 +00:00
// ZeroUSDXMintingClaim zeroes out the claim object's rewards and returns the updated claim object
func ( k Keeper ) ZeroUSDXMintingClaim ( ctx sdk . Context , claim types . USDXMintingClaim ) types . USDXMintingClaim {
2021-01-18 19:12:37 +00:00
claim . Reward = sdk . NewCoin ( claim . Reward . Denom , sdk . ZeroInt ( ) )
2021-01-21 13:52:09 +00:00
k . SetUSDXMintingClaim ( ctx , claim )
2021-01-18 19:12:37 +00:00
return claim
2020-04-24 15:20:34 +00:00
}
2021-01-26 11:52:34 +00:00
// SynchronizeUSDXMintingClaim updates the claim object by adding any rewards that have accumulated.
2021-01-18 19:12:37 +00:00
// Returns the updated claim object
2021-01-26 11:52:34 +00:00
func ( k Keeper ) SynchronizeUSDXMintingClaim ( ctx sdk . Context , claim types . USDXMintingClaim ) ( types . USDXMintingClaim , error ) {
2021-01-18 19:12:37 +00:00
for _ , ri := range claim . RewardIndexes {
cdp , found := k . cdpKeeper . GetCdpByOwnerAndCollateralType ( ctx , claim . Owner , ri . CollateralType )
if ! found {
// if the cdp for this collateral type has been closed, no updates are needed
continue
}
claim = k . synchronizeRewardAndReturnClaim ( ctx , cdp )
}
return claim , nil
}
// this function assumes a claim already exists, so don't call it if that's not the case
func ( k Keeper ) synchronizeRewardAndReturnClaim ( ctx sdk . Context , cdp cdptypes . CDP ) types . USDXMintingClaim {
2021-01-21 13:52:09 +00:00
k . SynchronizeUSDXMintingReward ( ctx , cdp )
claim , _ := k . GetUSDXMintingClaim ( ctx , cdp . Owner )
2021-01-18 19:12:37 +00:00
return claim
2020-04-24 15:20:34 +00:00
}
2021-01-26 11:52:34 +00:00
// SynchronizeHardLiquidityProviderClaim adds any accumulated rewards
func ( k Keeper ) SynchronizeHardLiquidityProviderClaim ( ctx sdk . Context , owner sdk . AccAddress ) {
// Synchronize any hard liquidity supply-side rewards
deposit , foundDeposit := k . hardKeeper . GetDeposit ( ctx , owner )
if foundDeposit {
k . SynchronizeHardSupplyReward ( ctx , deposit )
}
2021-02-03 22:23:53 +00:00
// Synchronize any hard liquidity borrow-side rewards
borrow , foundBorrow := k . hardKeeper . GetBorrow ( ctx , owner )
if foundBorrow {
k . SynchronizeHardBorrowReward ( ctx , borrow )
}
2021-01-26 11:52:34 +00:00
// Synchronize any hard delegator rewards
k . SynchronizeHardDelegatorRewards ( ctx , owner )
}
// ZeroHardLiquidityProviderClaim zeroes out the claim object's rewards and returns the updated claim object
func ( k Keeper ) ZeroHardLiquidityProviderClaim ( ctx sdk . Context , claim types . HardLiquidityProviderClaim ) types . HardLiquidityProviderClaim {
2021-02-02 16:17:46 +00:00
var zeroRewards sdk . Coins
for _ , coin := range claim . Reward {
zeroRewards = append ( zeroRewards , sdk . NewCoin ( coin . Denom , sdk . ZeroInt ( ) ) )
}
claim . Reward = zeroRewards
2021-01-26 11:52:34 +00:00
k . SetHardLiquidityProviderClaim ( ctx , claim )
return claim
}
2021-01-18 19:12:37 +00:00
// CalculateTimeElapsed calculates the number of reward-eligible seconds that have passed since the previous
// time rewards were accrued, taking into account the end time of the reward period
2021-02-02 16:17:46 +00:00
func CalculateTimeElapsed ( start , end , blockTime time . Time , previousAccrualTime time . Time ) sdk . Int {
2021-02-12 15:30:10 +00:00
if ( end . Before ( blockTime ) &&
( end . Before ( previousAccrualTime ) || end . Equal ( previousAccrualTime ) ) ) ||
( start . After ( previousAccrualTime ) ) ||
( start . Equal ( blockTime ) ) {
2021-01-18 19:12:37 +00:00
return sdk . ZeroInt ( )
2020-04-24 15:20:34 +00:00
}
2021-02-12 15:30:10 +00:00
if start . After ( previousAccrualTime ) && start . Before ( blockTime ) {
previousAccrualTime = start
}
2021-02-02 16:17:46 +00:00
if end . Before ( blockTime ) {
2021-01-18 19:12:37 +00:00
return sdk . NewInt ( int64 ( math . RoundToEven (
2021-02-02 16:17:46 +00:00
end . Sub ( previousAccrualTime ) . Seconds ( ) ,
2021-01-18 19:12:37 +00:00
) ) )
}
return sdk . NewInt ( int64 ( math . RoundToEven (
blockTime . Sub ( previousAccrualTime ) . Seconds ( ) ,
) ) )
2020-04-24 15:20:34 +00:00
}
2021-01-29 20:32:07 +00:00
// SimulateHardSynchronization calculates a user's outstanding hard rewards by simulating reward synchronization
func ( k Keeper ) SimulateHardSynchronization ( ctx sdk . Context , claim types . HardLiquidityProviderClaim ) types . HardLiquidityProviderClaim {
// 1. Simulate Hard supply-side rewards
for _ , ri := range claim . SupplyRewardIndexes {
2021-02-02 21:42:01 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardSupplyRewardIndexes ( ctx , ri . CollateralType )
if ! foundGlobalRewardIndexes {
2021-01-29 20:32:07 +00:00
continue
}
2021-02-02 21:42:01 +00:00
userRewardIndexes , foundUserRewardIndexes := claim . SupplyRewardIndexes . GetRewardIndex ( ri . CollateralType )
if ! foundUserRewardIndexes {
2021-01-29 20:32:07 +00:00
continue
}
2021-02-03 22:23:53 +00:00
userRewardIndexIndex , foundUserRewardIndexIndex := claim . SupplyRewardIndexes . GetRewardIndexIndex ( ri . CollateralType )
if ! foundUserRewardIndexIndex {
2021-02-19 20:02:51 +00:00
fmt . Printf ( "\n[LOG]: claim.SupplyRewardIndexes.GetRewardIndexIndex for %s should always be found" , ri . CollateralType ) // TODO: remove before production
2021-02-03 22:23:53 +00:00
continue
}
2021-02-02 21:42:01 +00:00
for _ , globalRewardIndex := range globalRewardIndexes {
userRewardIndex , foundUserRewardIndex := userRewardIndexes . RewardIndexes . GetRewardIndex ( globalRewardIndex . CollateralType )
if ! foundUserRewardIndex {
2021-02-19 20:02:51 +00:00
userRewardIndex = types . NewRewardIndex ( globalRewardIndex . CollateralType , sdk . ZeroDec ( ) )
userRewardIndexes . RewardIndexes = append ( userRewardIndexes . RewardIndexes , userRewardIndex )
claim . SupplyRewardIndexes [ userRewardIndexIndex ] . RewardIndexes = append ( claim . SupplyRewardIndexes [ userRewardIndexIndex ] . RewardIndexes , userRewardIndex )
2021-02-02 21:42:01 +00:00
}
2021-01-29 20:32:07 +00:00
2021-02-02 21:42:01 +00:00
globalRewardFactor := globalRewardIndex . RewardFactor
userRewardFactor := userRewardIndex . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
continue
}
deposit , found := k . hardKeeper . GetDeposit ( ctx , claim . GetOwner ( ) )
if ! found {
continue
}
newRewardsAmount := rewardsAccumulatedFactor . Mul ( deposit . Amount . AmountOf ( ri . CollateralType ) . ToDec ( ) ) . RoundInt ( )
2021-01-29 20:32:07 +00:00
if newRewardsAmount . IsZero ( ) || newRewardsAmount . IsNegative ( ) {
continue
}
2021-02-02 21:42:01 +00:00
factorIndex , foundFactorIndex := userRewardIndexes . RewardIndexes . GetFactorIndex ( globalRewardIndex . CollateralType )
if ! foundFactorIndex {
2021-02-19 20:02:51 +00:00
fmt . Printf ( "[LOG]: userRewardIndexes.RewardIndexes.GetFactorIndex for %s should always be found" , globalRewardIndex . CollateralType ) // TODO: remove before production
2021-02-02 21:42:01 +00:00
continue
}
claim . SupplyRewardIndexes [ userRewardIndexIndex ] . RewardIndexes [ factorIndex ] . RewardFactor = globalRewardIndex . RewardFactor
newRewardsCoin := sdk . NewCoin ( userRewardIndex . CollateralType , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
2021-01-29 20:32:07 +00:00
}
}
// 2. Simulate Hard borrow-side rewards
for _ , ri := range claim . BorrowRewardIndexes {
2021-02-02 21:42:01 +00:00
globalRewardIndexes , foundGlobalRewardIndexes := k . GetHardBorrowRewardIndexes ( ctx , ri . CollateralType )
if ! foundGlobalRewardIndexes {
2021-01-29 20:32:07 +00:00
continue
}
2021-02-02 21:42:01 +00:00
userRewardIndexes , foundUserRewardIndexes := claim . BorrowRewardIndexes . GetRewardIndex ( ri . CollateralType )
if ! foundUserRewardIndexes {
2021-01-29 20:32:07 +00:00
continue
}
2021-02-03 22:23:53 +00:00
userRewardIndexIndex , foundUserRewardIndexIndex := claim . BorrowRewardIndexes . GetRewardIndexIndex ( ri . CollateralType )
if ! foundUserRewardIndexIndex {
2021-02-19 20:02:51 +00:00
fmt . Printf ( "\n[LOG]: claim.BorrowRewardIndexes.GetRewardIndexIndex for %s should always be found" , ri . CollateralType ) // TODO: remove before production
2021-02-03 22:23:53 +00:00
continue
}
2021-02-02 21:42:01 +00:00
for _ , globalRewardIndex := range globalRewardIndexes {
userRewardIndex , foundUserRewardIndex := userRewardIndexes . RewardIndexes . GetRewardIndex ( globalRewardIndex . CollateralType )
if ! foundUserRewardIndex {
2021-02-19 20:02:51 +00:00
userRewardIndex = types . NewRewardIndex ( globalRewardIndex . CollateralType , sdk . ZeroDec ( ) )
userRewardIndexes . RewardIndexes = append ( userRewardIndexes . RewardIndexes , userRewardIndex )
claim . BorrowRewardIndexes [ userRewardIndexIndex ] . RewardIndexes = append ( claim . BorrowRewardIndexes [ userRewardIndexIndex ] . RewardIndexes , userRewardIndex )
2021-02-02 21:42:01 +00:00
}
2021-01-29 20:32:07 +00:00
2021-02-02 21:42:01 +00:00
globalRewardFactor := globalRewardIndex . RewardFactor
userRewardFactor := userRewardIndex . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
continue
}
borrow , found := k . hardKeeper . GetBorrow ( ctx , claim . GetOwner ( ) )
if ! found {
continue
}
newRewardsAmount := rewardsAccumulatedFactor . Mul ( borrow . Amount . AmountOf ( ri . CollateralType ) . ToDec ( ) ) . RoundInt ( )
2021-01-29 20:32:07 +00:00
if newRewardsAmount . IsZero ( ) || newRewardsAmount . IsNegative ( ) {
continue
}
2021-02-02 21:42:01 +00:00
factorIndex , foundFactorIndex := userRewardIndexes . RewardIndexes . GetFactorIndex ( globalRewardIndex . CollateralType )
if ! foundFactorIndex {
2021-02-19 20:02:51 +00:00
fmt . Printf ( "[LOG]: userRewardIndexes.RewardIndexes.GetFactorIndex for %s should always be found" , globalRewardIndex . CollateralType ) // TODO: remove before production
2021-02-02 21:42:01 +00:00
continue
}
2021-02-03 22:23:53 +00:00
claim . BorrowRewardIndexes [ userRewardIndexIndex ] . RewardIndexes [ factorIndex ] . RewardFactor = globalRewardIndex . RewardFactor
2021-02-02 21:42:01 +00:00
newRewardsCoin := sdk . NewCoin ( userRewardIndex . CollateralType , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
2021-01-29 20:32:07 +00:00
}
}
// 3. Simulate Hard delegator rewards
delagatorFactor , found := k . GetHardDelegatorRewardFactor ( ctx , types . BondDenom )
if ! found {
return claim
}
delegatorIndex , hasDelegatorRewardIndex := claim . HasDelegatorRewardIndex ( types . BondDenom )
if ! hasDelegatorRewardIndex {
return claim
}
userRewardFactor := claim . DelegatorRewardIndexes [ delegatorIndex ] . RewardFactor
rewardsAccumulatedFactor := delagatorFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
return claim
}
claim . DelegatorRewardIndexes [ delegatorIndex ] . RewardFactor = delagatorFactor
totalDelegated := sdk . ZeroDec ( )
// TODO: set reasonable max limit on delegation iteration
maxUInt := ^ uint16 ( 0 )
delegations := k . stakingKeeper . GetDelegatorDelegations ( ctx , claim . GetOwner ( ) , maxUInt )
for _ , delegation := range delegations {
validator , found := k . stakingKeeper . GetValidator ( ctx , delegation . GetValidatorAddr ( ) )
if ! found {
continue
}
// Delegators don't accumulate rewards if their validator is unbonded/slashed
if validator . GetStatus ( ) != sdk . Bonded {
continue
}
if validator . GetTokens ( ) . IsZero ( ) {
continue
}
delegatedTokens := validator . TokensFromShares ( delegation . GetShares ( ) )
if delegatedTokens . IsZero ( ) || delegatedTokens . IsNegative ( ) {
continue
}
totalDelegated = totalDelegated . Add ( delegatedTokens )
}
rewardsEarned := rewardsAccumulatedFactor . Mul ( totalDelegated ) . RoundInt ( )
if rewardsEarned . IsZero ( ) || rewardsEarned . IsNegative ( ) {
return claim
}
// Add rewards to delegator's hard claim
newRewardsCoin := sdk . NewCoin ( types . HardLiquidityRewardDenom , rewardsEarned )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
return claim
}
// SimulateUSDXMintingSynchronization calculates a user's outstanding USDX minting rewards by simulating reward synchronization
func ( k Keeper ) SimulateUSDXMintingSynchronization ( ctx sdk . Context , claim types . USDXMintingClaim ) types . USDXMintingClaim {
for _ , ri := range claim . RewardIndexes {
_ , found := k . GetUSDXMintingRewardPeriod ( ctx , ri . CollateralType )
if ! found {
continue
}
globalRewardFactor , found := k . GetUSDXMintingRewardFactor ( ctx , ri . CollateralType )
if ! found {
globalRewardFactor = sdk . ZeroDec ( )
}
// the owner has an existing usdx minting reward claim
index , hasRewardIndex := claim . HasRewardIndex ( ri . CollateralType )
if ! hasRewardIndex { // this is the owner's first usdx minting reward for this collateral type
claim . RewardIndexes = append ( claim . RewardIndexes , types . NewRewardIndex ( ri . CollateralType , globalRewardFactor ) )
}
userRewardFactor := claim . RewardIndexes [ index ] . RewardFactor
rewardsAccumulatedFactor := globalRewardFactor . Sub ( userRewardFactor )
if rewardsAccumulatedFactor . IsZero ( ) {
continue
}
claim . RewardIndexes [ index ] . RewardFactor = globalRewardFactor
cdp , found := k . cdpKeeper . GetCdpByOwnerAndCollateralType ( ctx , claim . GetOwner ( ) , ri . CollateralType )
if ! found {
continue
}
newRewardsAmount := rewardsAccumulatedFactor . Mul ( cdp . GetTotalPrincipal ( ) . Amount . ToDec ( ) ) . RoundInt ( )
if newRewardsAmount . IsZero ( ) {
continue
}
newRewardsCoin := sdk . NewCoin ( types . USDXMintingRewardDenom , newRewardsAmount )
claim . Reward = claim . Reward . Add ( newRewardsCoin )
}
return claim
}