mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 14:47:26 +00:00 
			
		
		
		
	Hard: Kava delegators earn HARD rewards via the Incentive module (#776)
* add staking keeper to incentive module * update hard with delegator methods * add delegator methods to incentive * implement delegator hook scaffolds * implement hard delegator reward accumulation * update claim names to delegator * stakingKeeper expected keeper methods * accumulate delegator rewards * initialize delegator reward * synchronize delegator reward * add TODO comments to rewards * implement staking hooks interface * initial revisions * remove outdated TODO * update methods for test compatibility * update method names for test compatibility * implement initial accumulate delegator reward test * attempt validator set up in staking module * initial synchronize delegator reward test * delegator accumulation test passing * synchronize delegator rewards test (not passing) * synchronize delegator rewards passing * revisions
This commit is contained in:
		
							parent
							
								
									dc330d02bf
								
							
						
					
					
						commit
						72a6df17fd
					
				@ -382,6 +382,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
 | 
			
		||||
		&cdpKeeper,
 | 
			
		||||
		&hardKeeper,
 | 
			
		||||
		app.accountKeeper,
 | 
			
		||||
		&stakingKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	app.issuanceKeeper = issuance.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
@ -394,7 +395,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
 | 
			
		||||
	// register the staking hooks
 | 
			
		||||
	// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
 | 
			
		||||
	app.stakingKeeper = *stakingKeeper.SetHooks(
 | 
			
		||||
		staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))
 | 
			
		||||
		staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks(), app.incentiveKeeper.Hooks()))
 | 
			
		||||
 | 
			
		||||
	app.cdpKeeper = *cdpKeeper.SetHooks(cdp.NewMultiCDPHooks(app.incentiveKeeper.Hooks()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,18 +34,19 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	PreviousBlockTimeKey       = []byte{0x01}
 | 
			
		||||
	DepositsKeyPrefix          = []byte{0x03}
 | 
			
		||||
	BorrowsKeyPrefix           = []byte{0x05}
 | 
			
		||||
	BorrowedCoinsPrefix        = []byte{0x06}
 | 
			
		||||
	SuppliedCoinsPrefix        = []byte{0x07}
 | 
			
		||||
	MoneyMarketsPrefix         = []byte{0x08}
 | 
			
		||||
	PreviousAccrualTimePrefix  = []byte{0x09} // denom -> time
 | 
			
		||||
	TotalReservesPrefix        = []byte{0x10} // denom -> sdk.Coin
 | 
			
		||||
	BorrowInterestFactorPrefix = []byte{0x11} // denom -> sdk.Dec
 | 
			
		||||
	SupplyInterestFactorPrefix = []byte{0x12} // denom -> sdk.Dec
 | 
			
		||||
	LtvIndexPrefix             = []byte{0x13}
 | 
			
		||||
	sep                        = []byte(":")
 | 
			
		||||
	PreviousBlockTimeKey          = []byte{0x01}
 | 
			
		||||
	DepositsKeyPrefix             = []byte{0x03}
 | 
			
		||||
	BorrowsKeyPrefix              = []byte{0x05}
 | 
			
		||||
	BorrowedCoinsPrefix           = []byte{0x06}
 | 
			
		||||
	SuppliedCoinsPrefix           = []byte{0x07}
 | 
			
		||||
	MoneyMarketsPrefix            = []byte{0x08}
 | 
			
		||||
	PreviousAccrualTimePrefix     = []byte{0x09} // denom -> time
 | 
			
		||||
	TotalReservesPrefix           = []byte{0x10} // denom -> sdk.Coin
 | 
			
		||||
	BorrowInterestFactorPrefix    = []byte{0x11} // denom -> sdk.Dec
 | 
			
		||||
	SupplyInterestFactorPrefix    = []byte{0x12} // denom -> sdk.Dec
 | 
			
		||||
	DelegatorInterestFactorPrefix = []byte{0x12} // denom -> sdk.Dec
 | 
			
		||||
	LtvIndexPrefix                = []byte{0x13}
 | 
			
		||||
	sep                           = []byte(":")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DepositTypeIteratorKey returns an interator prefix for interating over deposits by deposit denom
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@ package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
	hardtypes "github.com/kava-labs/kava/x/hard/types"
 | 
			
		||||
)
 | 
			
		||||
@ -13,10 +15,13 @@ type Hooks struct {
 | 
			
		||||
 | 
			
		||||
var _ cdptypes.CDPHooks = Hooks{}
 | 
			
		||||
var _ hardtypes.HARDHooks = Hooks{}
 | 
			
		||||
var _ stakingtypes.StakingHooks = Hooks{}
 | 
			
		||||
 | 
			
		||||
// Hooks create new incentive hooks
 | 
			
		||||
func (k Keeper) Hooks() Hooks { return Hooks{k} }
 | 
			
		||||
 | 
			
		||||
// ------------------- Cdp Module Hooks -------------------
 | 
			
		||||
 | 
			
		||||
// AfterCDPCreated function that runs after a cdp is created
 | 
			
		||||
func (h Hooks) AfterCDPCreated(ctx sdk.Context, cdp cdptypes.CDP) {
 | 
			
		||||
	h.k.InitializeUSDXMintingClaim(ctx, cdp)
 | 
			
		||||
@ -29,6 +34,8 @@ func (h Hooks) BeforeCDPModified(ctx sdk.Context, cdp cdptypes.CDP) {
 | 
			
		||||
	h.k.SynchronizeUSDXMintingReward(ctx, cdp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------- Hard Module Hooks -------------------
 | 
			
		||||
 | 
			
		||||
// AfterDepositCreated function that runs after a deposit is created
 | 
			
		||||
func (h Hooks) AfterDepositCreated(ctx sdk.Context, deposit hardtypes.Deposit) {
 | 
			
		||||
	h.k.InitializeHardSupplyReward(ctx, deposit)
 | 
			
		||||
@ -58,3 +65,46 @@ func (h Hooks) BeforeBorrowModified(ctx sdk.Context, borrow hardtypes.Borrow) {
 | 
			
		||||
func (h Hooks) AfterBorrowModified(ctx sdk.Context, borrow hardtypes.Borrow) {
 | 
			
		||||
	h.k.UpdateHardBorrowIndexDenoms(ctx, borrow)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------- Staking Module Hooks -------------------
 | 
			
		||||
 | 
			
		||||
// BeforeDelegationCreated runs before a delegation is created
 | 
			
		||||
func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
	h.k.InitializeHardDelegatorReward(ctx, delAddr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeforeDelegationSharesModified runs before an existing delegation is modified
 | 
			
		||||
func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
	h.k.SynchronizeHardDelegatorRewards(ctx, delAddr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NOTE: following hooks are just implemented to ensure StakingHooks interface compliance
 | 
			
		||||
 | 
			
		||||
// BeforeValidatorSlashed is called before a validator is slashed
 | 
			
		||||
func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {}
 | 
			
		||||
 | 
			
		||||
// AfterValidatorBeginUnbonding is called after a validator begins unbonding
 | 
			
		||||
func (h Hooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterValidatorBonded is called after a validator is bonded
 | 
			
		||||
func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterDelegationModified runs after a delegation is modified
 | 
			
		||||
func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeforeDelegationRemoved runs directly before a delegation is deleted
 | 
			
		||||
func (h Hooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterValidatorCreated runs after a validator is created
 | 
			
		||||
func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {}
 | 
			
		||||
 | 
			
		||||
// BeforeValidatorModified runs before a validator is modified
 | 
			
		||||
func (h Hooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {}
 | 
			
		||||
 | 
			
		||||
// AfterValidatorRemoved runs after a validator is removed
 | 
			
		||||
func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,13 @@ type Keeper struct {
 | 
			
		||||
	key           sdk.StoreKey
 | 
			
		||||
	paramSubspace subspace.Subspace
 | 
			
		||||
	supplyKeeper  types.SupplyKeeper
 | 
			
		||||
	stakingKeeper types.StakingKeeper
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewKeeper creates a new keeper
 | 
			
		||||
func NewKeeper(
 | 
			
		||||
	cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper,
 | 
			
		||||
	cdpk types.CdpKeeper, hk types.HardKeeper, ak types.AccountKeeper,
 | 
			
		||||
	cdpk types.CdpKeeper, hk types.HardKeeper, ak types.AccountKeeper, stk types.StakingKeeper,
 | 
			
		||||
) Keeper {
 | 
			
		||||
 | 
			
		||||
	return Keeper{
 | 
			
		||||
@ -36,6 +37,7 @@ func NewKeeper(
 | 
			
		||||
		key:           key,
 | 
			
		||||
		paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()),
 | 
			
		||||
		supplyKeeper:  sk,
 | 
			
		||||
		stakingKeeper: stk,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -276,3 +278,20 @@ func (k Keeper) SetPreviousHardBorrowRewardAccrualTime(ctx sdk.Context, denom st
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardBorrowRewardAccrualTimeKeyPrefix)
 | 
			
		||||
	store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(blockTime))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPreviousHardDelegatorRewardAccrualTime returns the last time a denom accrued Hard protocol delegator rewards
 | 
			
		||||
func (k Keeper) GetPreviousHardDelegatorRewardAccrualTime(ctx sdk.Context, denom string) (blockTime time.Time, found bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardDelegatorRewardAccrualTimeKeyPrefix)
 | 
			
		||||
	bz := store.Get([]byte(denom))
 | 
			
		||||
	if bz == nil {
 | 
			
		||||
		return time.Time{}, false
 | 
			
		||||
	}
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(bz, &blockTime)
 | 
			
		||||
	return blockTime, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPreviousHardDelegatorRewardAccrualTime sets the last time a denom accrued Hard protocol delegator rewards
 | 
			
		||||
func (k Keeper) SetPreviousHardDelegatorRewardAccrualTime(ctx sdk.Context, denom string, blockTime time.Time) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardDelegatorRewardAccrualTimeKeyPrefix)
 | 
			
		||||
	store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(blockTime))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
 | 
			
		||||
	supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
@ -24,18 +25,22 @@ import (
 | 
			
		||||
type KeeperTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	keeper     keeper.Keeper
 | 
			
		||||
	hardKeeper hardkeeper.Keeper
 | 
			
		||||
	app        app.TestApp
 | 
			
		||||
	ctx        sdk.Context
 | 
			
		||||
	addrs      []sdk.AccAddress
 | 
			
		||||
	keeper         keeper.Keeper
 | 
			
		||||
	hardKeeper     hardkeeper.Keeper
 | 
			
		||||
	stakingKeeper  stakingkeeper.Keeper
 | 
			
		||||
	app            app.TestApp
 | 
			
		||||
	ctx            sdk.Context
 | 
			
		||||
	addrs          []sdk.AccAddress
 | 
			
		||||
	validatorAddrs []sdk.ValAddress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The default state used by each test
 | 
			
		||||
func (suite *KeeperTestSuite) SetupTest() {
 | 
			
		||||
	tApp := app.NewTestApp()
 | 
			
		||||
	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
 | 
			
		||||
 | 
			
		||||
	tApp.InitializeFromGenesisStates()
 | 
			
		||||
 | 
			
		||||
	_, addrs := app.GeneratePrivKeyAddressPairs(5)
 | 
			
		||||
	keeper := tApp.GetIncentiveKeeper()
 | 
			
		||||
	suite.app = tApp
 | 
			
		||||
 | 
			
		||||
@ -53,6 +53,17 @@ func (k Keeper) GetHardBorrowRewardPeriod(ctx sdk.Context, denom string) (types.
 | 
			
		||||
	return types.RewardPeriod{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetHardDelegatorRewardPeriod returns the reward period with the specified collateral type if it's found in the params
 | 
			
		||||
func (k Keeper) GetHardDelegatorRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
	for _, rp := range params.HardDelegatorRewardPeriods {
 | 
			
		||||
		if rp.CollateralType == denom {
 | 
			
		||||
			return rp, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return types.RewardPeriod{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMultiplier returns the multiplier with the specified name if it's found in the params
 | 
			
		||||
func (k Keeper) GetMultiplier(ctx sdk.Context, name types.MultiplierName) (types.Multiplier, bool) {
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,7 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedBalance:          cs(c("usdx", 10000000000), c("ukava", 10571385600)),
 | 
			
		||||
				expectedBalance:          cs(c("usdx", 10000000000), c("ukava", 10576385600)),
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 31536000, Amount: cs(c("ukava", 10571385600))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
@ -400,6 +400,124 @@ func (k Keeper) UpdateHardBorrowIndexDenoms(ctx sdk.Context, borrow hardtypes.Bo
 | 
			
		||||
	k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	}
 | 
			
		||||
	timeElapsed := CalculateTimeElapsed(rewardPeriod, 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) {
 | 
			
		||||
	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
 | 
			
		||||
		claim = types.NewHardLiquidityProviderClaim(delegator,
 | 
			
		||||
			sdk.NewCoin(types.HardLiquidityRewardDenom, sdk.ZeroInt()),
 | 
			
		||||
			nil, nil, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claim.DelegatorRewardIndexes = types.RewardIndexes{delegatorRewardIndexes}
 | 
			
		||||
	k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ZeroClaim zeroes out the claim object's rewards and returns the updated claim object
 | 
			
		||||
func (k Keeper) ZeroClaim(ctx sdk.Context, claim types.USDXMintingClaim) types.USDXMintingClaim {
 | 
			
		||||
	claim.Reward = sdk.NewCoin(claim.Reward.Denom, sdk.ZeroInt())
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,9 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/staking"
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
	"github.com/tendermint/tendermint/crypto/ed25519"
 | 
			
		||||
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
@ -866,36 +867,292 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *KeeperTestSuite) TestAccumulateHardDelegatorRewards() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		delegation           sdk.Coin
 | 
			
		||||
		rewardsPerSecond     sdk.Coin
 | 
			
		||||
		initialTime          time.Time
 | 
			
		||||
		timeElapsed          int
 | 
			
		||||
		expectedRewardFactor sdk.Dec
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
	}
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"7 seconds",
 | 
			
		||||
			args{
 | 
			
		||||
				delegation:           c("ukava", 1_000_000),
 | 
			
		||||
				rewardsPerSecond:     c("hard", 122354),
 | 
			
		||||
				initialTime:          time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				timeElapsed:          7,
 | 
			
		||||
				expectedRewardFactor: d("0.428239000000000000"),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				delegation:           c("ukava", 1_000_000),
 | 
			
		||||
				rewardsPerSecond:     c("hard", 122354),
 | 
			
		||||
				initialTime:          time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				timeElapsed:          86400,
 | 
			
		||||
				expectedRewardFactor: d("5285.692800000000000000"),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"0 seconds",
 | 
			
		||||
			args{
 | 
			
		||||
				delegation:           c("ukava", 1_000_000),
 | 
			
		||||
				rewardsPerSecond:     c("hard", 122354),
 | 
			
		||||
				initialTime:          time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				timeElapsed:          0,
 | 
			
		||||
				expectedRewardFactor: d("0.0"),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			suite.SetupWithGenState()
 | 
			
		||||
			suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
 | 
			
		||||
 | 
			
		||||
			// Mint coins to hard module account
 | 
			
		||||
			supplyKeeper := suite.app.GetSupplyKeeper()
 | 
			
		||||
			hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
 | 
			
		||||
			supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
 | 
			
		||||
 | 
			
		||||
			// Set up incentive state
 | 
			
		||||
			params := types.NewParams(
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				tc.args.initialTime.Add(time.Hour*24*365*5),
 | 
			
		||||
			)
 | 
			
		||||
			suite.keeper.SetParams(suite.ctx, params)
 | 
			
		||||
			suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
 | 
			
		||||
			suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom, sdk.ZeroDec())
 | 
			
		||||
 | 
			
		||||
			// Set up hard state (interest factor for the relevant denom)
 | 
			
		||||
			suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
 | 
			
		||||
 | 
			
		||||
			err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			suite.deliverMsgDelegate(suite.ctx, suite.addrs[0], suite.validatorAddrs[0], tc.args.delegation)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			staking.EndBlocker(suite.ctx, suite.stakingKeeper)
 | 
			
		||||
 | 
			
		||||
			// Set up chain context at future time
 | 
			
		||||
			runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
 | 
			
		||||
			runCtx := suite.ctx.WithBlockTime(runAtTime)
 | 
			
		||||
 | 
			
		||||
			// Run Hard begin blocker in order to update the denom's index factor
 | 
			
		||||
			hard.BeginBlocker(runCtx, suite.hardKeeper)
 | 
			
		||||
 | 
			
		||||
			rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(runCtx, tc.args.delegation.Denom)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			err = suite.keeper.AccumulateHardDelegatorRewards(runCtx, rewardPeriod)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			rewardFactor, found := suite.keeper.GetHardDelegatorRewardFactor(runCtx, tc.args.delegation.Denom)
 | 
			
		||||
			suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *KeeperTestSuite) TestSynchronizeHardDelegatorReward() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		delegation           sdk.Coin
 | 
			
		||||
		rewardsPerSecond     sdk.Coin
 | 
			
		||||
		initialTime          time.Time
 | 
			
		||||
		blockTimes           []int
 | 
			
		||||
		expectedRewardFactor sdk.Dec
 | 
			
		||||
		expectedRewards      sdk.Coin
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"10 blocks",
 | 
			
		||||
			args{
 | 
			
		||||
				delegation:           c("ukava", 1_000_000),
 | 
			
		||||
				rewardsPerSecond:     c("hard", 122354),
 | 
			
		||||
				initialTime:          time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				blockTimes:           []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
 | 
			
		||||
				expectedRewardFactor: d("6.117700000000000000"),
 | 
			
		||||
				expectedRewards:      c("hard", 6117700),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"10 blocks - long block time",
 | 
			
		||||
			args{
 | 
			
		||||
				delegation:           c("ukava", 1_000_000),
 | 
			
		||||
				rewardsPerSecond:     c("hard", 122354),
 | 
			
		||||
				initialTime:          time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				blockTimes:           []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
 | 
			
		||||
				expectedRewardFactor: d("52856.928000000000000000"),
 | 
			
		||||
				expectedRewards:      c("hard", 52856928000),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			suite.SetupWithGenState()
 | 
			
		||||
			suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
 | 
			
		||||
 | 
			
		||||
			// Mint coins to hard module account
 | 
			
		||||
			supplyKeeper := suite.app.GetSupplyKeeper()
 | 
			
		||||
			hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
 | 
			
		||||
			supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
 | 
			
		||||
 | 
			
		||||
			// setup incentive state
 | 
			
		||||
			params := types.NewParams(
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.RewardPeriods{types.NewRewardPeriod(true, tc.args.delegation.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
 | 
			
		||||
				types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				tc.args.initialTime.Add(time.Hour*24*365*5),
 | 
			
		||||
			)
 | 
			
		||||
			suite.keeper.SetParams(suite.ctx, params)
 | 
			
		||||
			suite.keeper.SetPreviousHardDelegatorRewardAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
 | 
			
		||||
			suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom, sdk.ZeroDec())
 | 
			
		||||
 | 
			
		||||
			// Set up hard state (interest factor for the relevant denom)
 | 
			
		||||
			suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.delegation.Denom, tc.args.initialTime)
 | 
			
		||||
 | 
			
		||||
			// Delegator delegates
 | 
			
		||||
			err := suite.deliverMsgCreateValidator(suite.ctx, suite.validatorAddrs[0], tc.args.delegation)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			suite.deliverMsgDelegate(suite.ctx, suite.addrs[0], suite.validatorAddrs[0], tc.args.delegation)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			staking.EndBlocker(suite.ctx, suite.stakingKeeper)
 | 
			
		||||
 | 
			
		||||
			// Check that Staking hooks initialized a HardLiquidityProviderClaim
 | 
			
		||||
			claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.Require().Equal(sdk.ZeroDec(), claim.DelegatorRewardIndexes[0].RewardFactor)
 | 
			
		||||
 | 
			
		||||
			// Run accumulator at several intervals
 | 
			
		||||
			var timeElapsed int
 | 
			
		||||
			previousBlockTime := suite.ctx.BlockTime()
 | 
			
		||||
			for _, t := range tc.args.blockTimes {
 | 
			
		||||
				timeElapsed += t
 | 
			
		||||
				updatedBlockTime := previousBlockTime.Add(time.Duration(int(time.Second) * t))
 | 
			
		||||
				previousBlockTime = updatedBlockTime
 | 
			
		||||
				blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
 | 
			
		||||
 | 
			
		||||
				// Run Hard begin blocker for each block ctx to update denom's interest factor
 | 
			
		||||
				hard.BeginBlocker(blockCtx, suite.hardKeeper)
 | 
			
		||||
 | 
			
		||||
				rewardPeriod, found := suite.keeper.GetHardDelegatorRewardPeriod(blockCtx, tc.args.delegation.Denom)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
 | 
			
		||||
				err := suite.keeper.AccumulateHardDelegatorRewards(blockCtx, rewardPeriod)
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
			}
 | 
			
		||||
			updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
 | 
			
		||||
			suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
 | 
			
		||||
 | 
			
		||||
			// After we've accumulated, run synchronize
 | 
			
		||||
			suite.Require().NotPanics(func() {
 | 
			
		||||
				suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, suite.addrs[0])
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			// Check that reward factor and claim have been updated as expected
 | 
			
		||||
			rewardFactor, found := suite.keeper.GetHardDelegatorRewardFactor(suite.ctx, tc.args.delegation.Denom)
 | 
			
		||||
			suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
 | 
			
		||||
 | 
			
		||||
			claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[0])
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.Require().Equal(tc.args.expectedRewardFactor, claim.DelegatorRewardIndexes[0].RewardFactor)
 | 
			
		||||
			suite.Require().Equal(tc.args.expectedRewards, claim.Reward)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *KeeperTestSuite) SetupWithGenState() {
 | 
			
		||||
	config := sdk.GetConfig()
 | 
			
		||||
	app.SetBech32AddressPrefixes(config)
 | 
			
		||||
 | 
			
		||||
	_, allAddrs := app.GeneratePrivKeyAddressPairs(10)
 | 
			
		||||
	suite.addrs = allAddrs[:5]
 | 
			
		||||
	for _, a := range allAddrs[5:] {
 | 
			
		||||
		suite.validatorAddrs = append(suite.validatorAddrs, sdk.ValAddress(a))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tApp := app.NewTestApp()
 | 
			
		||||
	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
 | 
			
		||||
 | 
			
		||||
	_, addrs := app.GeneratePrivKeyAddressPairs(5)
 | 
			
		||||
 | 
			
		||||
	authGS := app.NewAuthGenState(
 | 
			
		||||
		[]sdk.AccAddress{addrs[3]},
 | 
			
		||||
		[]sdk.Coins{
 | 
			
		||||
			sdk.NewCoins(
 | 
			
		||||
				sdk.NewCoin("bnb", sdk.NewInt(1000000000000000)),
 | 
			
		||||
				sdk.NewCoin("ukava", sdk.NewInt(1000000000000000)),
 | 
			
		||||
				sdk.NewCoin("btcb", sdk.NewInt(1000000000000000)),
 | 
			
		||||
				sdk.NewCoin("xrp", sdk.NewInt(1000000000000000)),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	tApp.InitializeFromGenesisStates(
 | 
			
		||||
		authGS,
 | 
			
		||||
		coinsAuthGenState(allAddrs, cs(c("ukava", 5_000_000))),
 | 
			
		||||
		stakingGenesisState(),
 | 
			
		||||
		NewPricefeedGenStateMulti(),
 | 
			
		||||
		NewCDPGenStateMulti(),
 | 
			
		||||
		NewHardGenStateMulti(),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	keeper := tApp.GetIncentiveKeeper()
 | 
			
		||||
	hardKeeper := tApp.GetHardKeeper()
 | 
			
		||||
	suite.app = tApp
 | 
			
		||||
	suite.ctx = ctx
 | 
			
		||||
	suite.keeper = keeper
 | 
			
		||||
	suite.hardKeeper = hardKeeper
 | 
			
		||||
	suite.addrs = addrs
 | 
			
		||||
	suite.keeper = tApp.GetIncentiveKeeper()
 | 
			
		||||
	suite.hardKeeper = tApp.GetHardKeeper()
 | 
			
		||||
	suite.stakingKeeper = tApp.GetStakingKeeper()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func coinsAuthGenState(addresses []sdk.AccAddress, coins sdk.Coins) app.GenesisState {
 | 
			
		||||
	coinsList := []sdk.Coins{}
 | 
			
		||||
	for range addresses {
 | 
			
		||||
		coinsList = append(coinsList, coins)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Load up our primary user address
 | 
			
		||||
	if len(addresses) >= 4 {
 | 
			
		||||
		coinsList[3] = sdk.NewCoins(
 | 
			
		||||
			sdk.NewCoin("bnb", sdk.NewInt(1000000000000000)),
 | 
			
		||||
			sdk.NewCoin("ukava", sdk.NewInt(1000000000000000)),
 | 
			
		||||
			sdk.NewCoin("btcb", sdk.NewInt(1000000000000000)),
 | 
			
		||||
			sdk.NewCoin("xrp", sdk.NewInt(1000000000000000)),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return app.NewAuthGenState(addresses, coinsList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stakingGenesisState() app.GenesisState {
 | 
			
		||||
	genState := staking.DefaultGenesisState()
 | 
			
		||||
	genState.Params.BondDenom = "ukava"
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		staking.ModuleName: staking.ModuleCdc.MustMarshalJSON(genState),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *KeeperTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error {
 | 
			
		||||
	msg := staking.NewMsgCreateValidator(
 | 
			
		||||
		address,
 | 
			
		||||
		ed25519.GenPrivKey().PubKey(),
 | 
			
		||||
		selfDelegation,
 | 
			
		||||
		staking.Description{},
 | 
			
		||||
		staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
 | 
			
		||||
		sdk.NewInt(1_000_000),
 | 
			
		||||
	)
 | 
			
		||||
	handleStakingMsg := staking.NewHandler(suite.stakingKeeper)
 | 
			
		||||
	_, err := handleStakingMsg(ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *KeeperTestSuite) deliverMsgDelegate(ctx sdk.Context, delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error {
 | 
			
		||||
	msg := staking.NewMsgDelegate(
 | 
			
		||||
		delegator,
 | 
			
		||||
		validator,
 | 
			
		||||
		amount,
 | 
			
		||||
	)
 | 
			
		||||
	handleStakingMsg := staking.NewHandler(suite.stakingKeeper)
 | 
			
		||||
	_, err := handleStakingMsg(ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import (
 | 
			
		||||
const (
 | 
			
		||||
	USDXMintingClaimType           = "usdx_minting"
 | 
			
		||||
	HardLiquidityProviderClaimType = "hard_liquidity_provider"
 | 
			
		||||
	BondDenom                      = "ukava"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Claim is an interface for handling common claim actions
 | 
			
		||||
@ -122,23 +123,23 @@ func (cs USDXMintingClaims) Validate() error {
 | 
			
		||||
 | 
			
		||||
// HardLiquidityProviderClaim stores the hard liquidity provider rewards that can be claimed by owner
 | 
			
		||||
type HardLiquidityProviderClaim struct {
 | 
			
		||||
	BaseClaim               `json:"base_claim" yaml:"base_claim"`
 | 
			
		||||
	SupplyRewardIndexes     RewardIndexes `json:"supply_reward_indexes" yaml:"supply_reward_indexes"`
 | 
			
		||||
	BorrowRewardIndexes     RewardIndexes `json:"borrow_reward_indexes" yaml:"borrow_reward_indexes"`
 | 
			
		||||
	DelegationRewardIndexes RewardIndexes `json:"delegation_reward_indexes" yaml:"delegation_reward_indexes"`
 | 
			
		||||
	BaseClaim              `json:"base_claim" yaml:"base_claim"`
 | 
			
		||||
	SupplyRewardIndexes    RewardIndexes `json:"supply_reward_indexes" yaml:"supply_reward_indexes"`
 | 
			
		||||
	BorrowRewardIndexes    RewardIndexes `json:"borrow_reward_indexes" yaml:"borrow_reward_indexes"`
 | 
			
		||||
	DelegatorRewardIndexes RewardIndexes `json:"delegator_reward_indexes" yaml:"delegator_reward_indexes"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHardLiquidityProviderClaim returns a new HardLiquidityProviderClaim
 | 
			
		||||
func NewHardLiquidityProviderClaim(owner sdk.AccAddress, reward sdk.Coin, supplyRewardIndexes,
 | 
			
		||||
	borrowRewardIndexes, delegationRewardIndexes RewardIndexes) HardLiquidityProviderClaim {
 | 
			
		||||
	borrowRewardIndexes, delegatorRewardIndexes RewardIndexes) HardLiquidityProviderClaim {
 | 
			
		||||
	return HardLiquidityProviderClaim{
 | 
			
		||||
		BaseClaim: BaseClaim{
 | 
			
		||||
			Owner:  owner,
 | 
			
		||||
			Reward: reward,
 | 
			
		||||
		},
 | 
			
		||||
		SupplyRewardIndexes:     supplyRewardIndexes,
 | 
			
		||||
		BorrowRewardIndexes:     borrowRewardIndexes,
 | 
			
		||||
		DelegationRewardIndexes: delegationRewardIndexes,
 | 
			
		||||
		SupplyRewardIndexes:    supplyRewardIndexes,
 | 
			
		||||
		BorrowRewardIndexes:    borrowRewardIndexes,
 | 
			
		||||
		DelegatorRewardIndexes: delegatorRewardIndexes,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -155,7 +156,7 @@ func (c HardLiquidityProviderClaim) Validate() error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.DelegationRewardIndexes.Validate(); err != nil {
 | 
			
		||||
	if err := c.DelegatorRewardIndexes.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -167,8 +168,8 @@ func (c HardLiquidityProviderClaim) String() string {
 | 
			
		||||
	return fmt.Sprintf(`%s
 | 
			
		||||
	Supply Reward Indexes: %s,
 | 
			
		||||
	Borrow Reward Indexes: %s,
 | 
			
		||||
	Delegation Reward Indexes: %s,
 | 
			
		||||
	`, c.BaseClaim, c.SupplyRewardIndexes, c.BorrowRewardIndexes, c.DelegationRewardIndexes)
 | 
			
		||||
	Delegator Reward Indexes: %s,
 | 
			
		||||
	`, c.BaseClaim, c.SupplyRewardIndexes, c.BorrowRewardIndexes, c.DelegatorRewardIndexes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasSupplyRewardIndex check if a claim has a supply reward index for the input collateral type
 | 
			
		||||
@ -191,9 +192,9 @@ func (c HardLiquidityProviderClaim) HasBorrowRewardIndex(denom string) (int64, b
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasDelegationRewardIndex check if a claim has a delegation reward index for the input collateral type
 | 
			
		||||
func (c HardLiquidityProviderClaim) HasDelegationRewardIndex(collateralType string) (int64, bool) {
 | 
			
		||||
	for index, ri := range c.SupplyRewardIndexes {
 | 
			
		||||
// HasDelegatorRewardIndex check if a claim has a delegator reward index for the input collateral type
 | 
			
		||||
func (c HardLiquidityProviderClaim) HasDelegatorRewardIndex(collateralType string) (int64, bool) {
 | 
			
		||||
	for index, ri := range c.DelegatorRewardIndexes {
 | 
			
		||||
		if ri.CollateralType == collateralType {
 | 
			
		||||
			return int64(index), true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package types
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
 | 
			
		||||
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
	supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
@ -15,6 +16,13 @@ type SupplyKeeper interface {
 | 
			
		||||
	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StakingKeeper defines the expected staking keeper for module accounts
 | 
			
		||||
type StakingKeeper interface {
 | 
			
		||||
	GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []stakingtypes.Delegation)
 | 
			
		||||
	GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool)
 | 
			
		||||
	TotalBondedTokens(ctx sdk.Context) sdk.Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CdpKeeper defines the expected cdp keeper for interacting with cdps
 | 
			
		||||
type CdpKeeper interface {
 | 
			
		||||
	GetInterestFactor(ctx sdk.Context, collateralType string) (sdk.Dec, bool)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user