mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 06:07:26 +00:00 
			
		
		
		
	USDX incentives implementation (#399)
* USDX incentives implementation (#399) * feat: upgrade to cosmos-sdk v0.38 Co-authored-by: Denali Marsh <denali@kava.io> Co-authored-by: John Maheswaran <jmaheswaran@users.noreply.github.com> Co-authored-by: John Maheswaran <john@kava.io>
This commit is contained in:
		
							parent
							
								
									5737f4fa19
								
							
						
					
					
						commit
						1ef9bd331b
					
				
							
								
								
									
										20
									
								
								app/app.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								app/app.go
									
									
									
									
									
								
							@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/auction"
 | 
			
		||||
	"github.com/kava-labs/kava/x/bep3"
 | 
			
		||||
	"github.com/kava-labs/kava/x/cdp"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist"
 | 
			
		||||
	"github.com/kava-labs/kava/x/pricefeed"
 | 
			
		||||
	validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
 | 
			
		||||
@ -69,6 +70,7 @@ var (
 | 
			
		||||
		pricefeed.AppModuleBasic{},
 | 
			
		||||
		bep3.AppModuleBasic{},
 | 
			
		||||
		kavadist.AppModuleBasic{},
 | 
			
		||||
		incentive.AppModuleBasic{},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// module account permissions
 | 
			
		||||
@ -121,6 +123,7 @@ type App struct {
 | 
			
		||||
	pricefeedKeeper pricefeed.Keeper
 | 
			
		||||
	bep3Keeper      bep3.Keeper
 | 
			
		||||
	kavadistKeeper  kavadist.Keeper
 | 
			
		||||
	incentiveKeeper incentive.Keeper
 | 
			
		||||
 | 
			
		||||
	// the module manager
 | 
			
		||||
	mm *module.Manager
 | 
			
		||||
@ -145,7 +148,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
		supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
 | 
			
		||||
		gov.StoreKey, params.StoreKey, evidence.StoreKey, validatorvesting.StoreKey,
 | 
			
		||||
		auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, bep3.StoreKey,
 | 
			
		||||
		kavadist.StoreKey,
 | 
			
		||||
		kavadist.StoreKey, incentive.StoreKey,
 | 
			
		||||
	)
 | 
			
		||||
	tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
 | 
			
		||||
 | 
			
		||||
@ -173,6 +176,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
	pricefeedSubspace := app.paramsKeeper.Subspace(pricefeed.DefaultParamspace)
 | 
			
		||||
	bep3Subspace := app.paramsKeeper.Subspace(bep3.DefaultParamspace)
 | 
			
		||||
	kavadistSubspace := app.paramsKeeper.Subspace(kavadist.DefaultParamspace)
 | 
			
		||||
	incentiveSubspace := app.paramsKeeper.Subspace(incentive.DefaultParamspace)
 | 
			
		||||
 | 
			
		||||
	// add keepers
 | 
			
		||||
	app.accountKeeper = auth.NewAccountKeeper(
 | 
			
		||||
@ -295,6 +299,14 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
		kavadistSubspace,
 | 
			
		||||
		app.supplyKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	app.incentiveKeeper = incentive.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
		keys[incentive.StoreKey],
 | 
			
		||||
		incentiveSubspace,
 | 
			
		||||
		app.supplyKeeper,
 | 
			
		||||
		app.cdpKeeper,
 | 
			
		||||
		app.accountKeeper,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// register the staking hooks
 | 
			
		||||
	// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
 | 
			
		||||
@ -321,12 +333,13 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
		pricefeed.NewAppModule(app.pricefeedKeeper, app.accountKeeper),
 | 
			
		||||
		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper),
 | 
			
		||||
		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper),
 | 
			
		||||
		incentive.NewAppModule(app.incentiveKeeper, app.supplyKeeper),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// During begin block slashing happens after distr.BeginBlocker so that
 | 
			
		||||
	// there is nothing left over in the validator fee pool, so as to keep the
 | 
			
		||||
	// CanWithdrawInvariant invariant.
 | 
			
		||||
	app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, kavadist.ModuleName, cdp.ModuleName, auction.ModuleName, bep3.ModuleName)
 | 
			
		||||
	app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, kavadist.ModuleName, cdp.ModuleName, auction.ModuleName, bep3.ModuleName, incentive.ModuleName)
 | 
			
		||||
 | 
			
		||||
	app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName)
 | 
			
		||||
 | 
			
		||||
@ -335,7 +348,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
		validatorvesting.ModuleName, distr.ModuleName,
 | 
			
		||||
		staking.ModuleName, bank.ModuleName, slashing.ModuleName,
 | 
			
		||||
		gov.ModuleName, mint.ModuleName, evidence.ModuleName,
 | 
			
		||||
		pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName, bep3.ModuleName, kavadist.ModuleName, // TODO is this order ok?
 | 
			
		||||
		pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName, bep3.ModuleName, kavadist.ModuleName, incentive.ModuleName,
 | 
			
		||||
		supply.ModuleName,  // calculates the total supply from account - should run after modules that modify accounts in genesis
 | 
			
		||||
		crisis.ModuleName,  // runs the invariants at genesis - should run after other modules
 | 
			
		||||
		genutil.ModuleName, // genutils must occur after staking so that pools are properly initialized with tokens from genesis accounts.
 | 
			
		||||
@ -363,6 +376,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
 | 
			
		||||
		auction.NewAppModule(app.auctionKeeper, app.accountKeeper, app.supplyKeeper),
 | 
			
		||||
		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper),
 | 
			
		||||
		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper),
 | 
			
		||||
		incentive.NewAppModule(app.incentiveKeeper, app.supplyKeeper),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	app.sm.RegisterStoreDecoders()
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/auction"
 | 
			
		||||
	"github.com/kava-labs/kava/x/bep3"
 | 
			
		||||
	"github.com/kava-labs/kava/x/cdp"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist"
 | 
			
		||||
	"github.com/kava-labs/kava/x/pricefeed"
 | 
			
		||||
	validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
 | 
			
		||||
@ -75,6 +76,7 @@ func (tApp TestApp) GetCDPKeeper() cdp.Keeper             { return tApp.cdpKeepe
 | 
			
		||||
func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper }
 | 
			
		||||
func (tApp TestApp) GetBep3Keeper() bep3.Keeper           { return tApp.bep3Keeper }
 | 
			
		||||
func (tApp TestApp) GetKavadistKeeper() kavadist.Keeper   { return tApp.kavadistKeeper }
 | 
			
		||||
func (tApp TestApp) GetIncentiveKeeper() incentive.Keeper { return tApp.incentiveKeeper }
 | 
			
		||||
 | 
			
		||||
// This calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states
 | 
			
		||||
func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) TestApp {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.sum
									
									
									
									
									
								
							@ -416,6 +416,7 @@ github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1Tuol
 | 
			
		||||
github.com/tendermint/tendermint v0.33.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk=
 | 
			
		||||
github.com/tendermint/tendermint v0.33.3 h1:6lMqjEoCGejCzAghbvfQgmw87snGSqEhDTo/jw+W8CI=
 | 
			
		||||
github.com/tendermint/tendermint v0.33.3/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk=
 | 
			
		||||
github.com/tendermint/tendermint v0.33.4 h1:NM3G9618yC5PaaxGrcAySc5ylc1PAANeIx42u2Re/jo=
 | 
			
		||||
github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY=
 | 
			
		||||
github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ=
 | 
			
		||||
github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U=
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,6 @@ func queryAtomicSwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		swapID, err := types.HexToBytes(vars[restSwapID])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								x/incentive/abci.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								x/incentive/abci.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
package incentive
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BeginBlocker runs at the start of every block
 | 
			
		||||
func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
 | 
			
		||||
	k.DeleteExpiredClaimsAndClaimPeriods(ctx)
 | 
			
		||||
	k.ApplyRewardsToCdps(ctx)
 | 
			
		||||
	k.CreateAndDeleteRewardPeriods(ctx)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										92
									
								
								x/incentive/alias.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								x/incentive/alias.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
// nolint
 | 
			
		||||
// autogenerated code using github.com/rigelrozanski/multitool
 | 
			
		||||
// aliases generated for the following subdirectories:
 | 
			
		||||
// ALIASGEN: github.com/kava-labs/kava/x/incentive/keeper
 | 
			
		||||
// ALIASGEN: github.com/kava-labs/kava/x/incentive/types
 | 
			
		||||
package incentive
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	EventTypeClaim         = types.EventTypeClaim
 | 
			
		||||
	AttributeValueCategory = types.AttributeValueCategory
 | 
			
		||||
	AttributeKeySender     = types.AttributeKeySender
 | 
			
		||||
	ModuleName             = types.ModuleName
 | 
			
		||||
	StoreKey               = types.StoreKey
 | 
			
		||||
	RouterKey              = types.RouterKey
 | 
			
		||||
	DefaultParamspace      = types.DefaultParamspace
 | 
			
		||||
	QuerierRoute           = types.QuerierRoute
 | 
			
		||||
	QueryGetClaims         = types.QueryGetClaims
 | 
			
		||||
	RestClaimOwner         = types.RestClaimOwner
 | 
			
		||||
	RestClaimDenom         = types.RestClaimDenom
 | 
			
		||||
	QueryGetParams         = types.QueryGetParams
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// functions aliases
 | 
			
		||||
	NewKeeper                   = keeper.NewKeeper
 | 
			
		||||
	NewQuerier                  = keeper.NewQuerier
 | 
			
		||||
	GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength
 | 
			
		||||
	RegisterCodec               = types.RegisterCodec
 | 
			
		||||
	NewGenesisState             = types.NewGenesisState
 | 
			
		||||
	DefaultGenesisState         = types.DefaultGenesisState
 | 
			
		||||
	BytesToUint64               = types.BytesToUint64
 | 
			
		||||
	GetClaimPeriodPrefix        = types.GetClaimPeriodPrefix
 | 
			
		||||
	GetClaimPrefix              = types.GetClaimPrefix
 | 
			
		||||
	NewMsgClaimReward           = types.NewMsgClaimReward
 | 
			
		||||
	NewParams                   = types.NewParams
 | 
			
		||||
	DefaultParams               = types.DefaultParams
 | 
			
		||||
	ParamKeyTable               = types.ParamKeyTable
 | 
			
		||||
	NewReward                   = types.NewReward
 | 
			
		||||
	NewPeriod                   = types.NewPeriod
 | 
			
		||||
	NewQueryClaimsParams        = types.NewQueryClaimsParams
 | 
			
		||||
	NewRewardPeriod             = types.NewRewardPeriod
 | 
			
		||||
	NewClaimPeriod              = types.NewClaimPeriod
 | 
			
		||||
	NewClaim                    = types.NewClaim
 | 
			
		||||
 | 
			
		||||
	// variable aliases
 | 
			
		||||
	ModuleCdc                        = types.ModuleCdc
 | 
			
		||||
	ErrClaimNotFound                 = types.ErrClaimNotFound
 | 
			
		||||
	ErrClaimPeriodNotFound           = types.ErrClaimPeriodNotFound
 | 
			
		||||
	ErrInvalidAccountType            = types.ErrInvalidAccountType
 | 
			
		||||
	ErrNoClaimsFound                 = types.ErrNoClaimsFound
 | 
			
		||||
	ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance
 | 
			
		||||
	RewardPeriodKeyPrefix            = types.RewardPeriodKeyPrefix
 | 
			
		||||
	ClaimPeriodKeyPrefix             = types.ClaimPeriodKeyPrefix
 | 
			
		||||
	ClaimKeyPrefix                   = types.ClaimKeyPrefix
 | 
			
		||||
	NextClaimPeriodIDPrefix          = types.NextClaimPeriodIDPrefix
 | 
			
		||||
	PreviousBlockTimeKey             = types.PreviousBlockTimeKey
 | 
			
		||||
	KeyActive                        = types.KeyActive
 | 
			
		||||
	KeyRewards                       = types.KeyRewards
 | 
			
		||||
	DefaultActive                    = types.DefaultActive
 | 
			
		||||
	DefaultRewards                   = types.DefaultRewards
 | 
			
		||||
	DefaultPreviousBlockTime         = types.DefaultPreviousBlockTime
 | 
			
		||||
	GovDenom                         = types.GovDenom
 | 
			
		||||
	PrincipalDenom                   = types.PrincipalDenom
 | 
			
		||||
	IncentiveMacc                    = types.IncentiveMacc
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	Keeper                = keeper.Keeper
 | 
			
		||||
	SupplyKeeper          = types.SupplyKeeper
 | 
			
		||||
	CdpKeeper             = types.CdpKeeper
 | 
			
		||||
	AccountKeeper         = types.AccountKeeper
 | 
			
		||||
	GenesisClaimPeriodID  = types.GenesisClaimPeriodID
 | 
			
		||||
	GenesisClaimPeriodIDs = types.GenesisClaimPeriodIDs
 | 
			
		||||
	GenesisState          = types.GenesisState
 | 
			
		||||
	MsgClaimReward        = types.MsgClaimReward
 | 
			
		||||
	Params                = types.Params
 | 
			
		||||
	Reward                = types.Reward
 | 
			
		||||
	Rewards               = types.Rewards
 | 
			
		||||
	QueryClaimsParams     = types.QueryClaimsParams
 | 
			
		||||
	PostClaimReq          = types.PostClaimReq
 | 
			
		||||
	RewardPeriod          = types.RewardPeriod
 | 
			
		||||
	RewardPeriods         = types.RewardPeriods
 | 
			
		||||
	ClaimPeriod           = types.ClaimPeriod
 | 
			
		||||
	ClaimPeriods          = types.ClaimPeriods
 | 
			
		||||
	Claim                 = types.Claim
 | 
			
		||||
	Claims                = types.Claims
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										98
									
								
								x/incentive/client/cli/query.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								x/incentive/client/cli/query.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
			
		||||
package cli
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/flags"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/version"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetQueryCmd returns the cli query commands for the incentive module
 | 
			
		||||
func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	incentiveQueryCmd := &cobra.Command{
 | 
			
		||||
		Use:   types.ModuleName,
 | 
			
		||||
		Short: "Querying commands for the incentive module",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	incentiveQueryCmd.AddCommand(flags.GetCommands(
 | 
			
		||||
		queryParamsCmd(queryRoute, cdc),
 | 
			
		||||
		queryClaimsCmd(queryRoute, cdc),
 | 
			
		||||
	)...)
 | 
			
		||||
 | 
			
		||||
	return incentiveQueryCmd
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claims [owner-addr] [denom]",
 | 
			
		||||
		Short: "get claims by onwer and denom",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Get all claims owned by the owner address for the particular collateral type.
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s query %s claims kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw bnb`, version.ClientName, types.ModuleName)),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
			// Prepare params for querier
 | 
			
		||||
			ownerAddress, err := sdk.AccAddressFromBech32(args[0])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			bz, err := cdc.MarshalJSON(types.QueryClaimsParams{
 | 
			
		||||
				Owner: ownerAddress,
 | 
			
		||||
				Denom: args[1],
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Query
 | 
			
		||||
			route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetClaims)
 | 
			
		||||
			res, _, err := cliCtx.QueryWithData(route, bz)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var claims types.Claims
 | 
			
		||||
			if err := cdc.UnmarshalJSON(res, &claims); err != nil {
 | 
			
		||||
				return fmt.Errorf("failed to unmarshal claims: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			return cliCtx.PrintOutput(claims)
 | 
			
		||||
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "params",
 | 
			
		||||
		Short: "get the incentive module parameters",
 | 
			
		||||
		Long:  "Get the current global incentive module parameters.",
 | 
			
		||||
		Args:  cobra.NoArgs,
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
 | 
			
		||||
			// Query
 | 
			
		||||
			route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetParams)
 | 
			
		||||
			res, _, err := cliCtx.QueryWithData(route, nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Decode and print results
 | 
			
		||||
			var params types.Params
 | 
			
		||||
			if err := cdc.UnmarshalJSON(res, ¶ms); err != nil {
 | 
			
		||||
				return fmt.Errorf("failed to unmarshal params: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			return cliCtx.PrintOutput(params)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								x/incentive/client/cli/tx.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								x/incentive/client/cli/tx.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
package cli
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/flags"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/version"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetTxCmd returns the transaction cli commands for the incentive module
 | 
			
		||||
func GetTxCmd(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	incentiveTxCmd := &cobra.Command{
 | 
			
		||||
		Use:   types.ModuleName,
 | 
			
		||||
		Short: "transaction commands for the incentive module",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	incentiveTxCmd.AddCommand(flags.PostCommands(
 | 
			
		||||
		getCmdClaim(cdc),
 | 
			
		||||
	)...)
 | 
			
		||||
 | 
			
		||||
	return incentiveTxCmd
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaim(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim [owner] [denom]",
 | 
			
		||||
		Short: "claim rewards for owner and denom",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim any outstanding rewards owned by owner for the input denom,
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw bnb
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
 | 
			
		||||
			owner, err := sdk.AccAddressFromBech32(args[0])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimReward(owner, args[1])
 | 
			
		||||
			err = msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								x/incentive/client/rest/query.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								x/incentive/client/rest/query.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	r.HandleFunc(fmt.Sprintf("/%s/claims", types.ModuleName), queryClaimsHandlerFn(cliCtx)).Methods("GET")
 | 
			
		||||
	r.HandleFunc(fmt.Sprintf("/%s/parameters", types.ModuleName), queryParamsHandlerFn(cliCtx)).Methods("GET")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		vars := mux.Vars(r)
 | 
			
		||||
		ownerBech32 := vars[types.RestClaimOwner]
 | 
			
		||||
		denom := vars[types.RestClaimDenom]
 | 
			
		||||
 | 
			
		||||
		owner, err := sdk.AccAddressFromBech32(ownerBech32)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		queryParams := types.NewQueryClaimsParams(owner, denom)
 | 
			
		||||
		bz, err := cliCtx.Codec.MarshalJSON(queryParams)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/incentive/%s", types.QueryGetClaims), bz)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		cliCtx = cliCtx.WithHeight(height)
 | 
			
		||||
		rest.PostProcessResponse(w, cliCtx, res)
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		route := fmt.Sprintf("custom/%s/parameters", types.QuerierRoute)
 | 
			
		||||
 | 
			
		||||
		res, height, err := cliCtx.QueryWithData(route, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cliCtx = cliCtx.WithHeight(height)
 | 
			
		||||
		rest.PostProcessResponse(w, cliCtx, res)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								x/incentive/client/rest/rest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								x/incentive/client/rest/rest.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterRoutes registers incentive-related REST handlers to a router
 | 
			
		||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	registerQueryRoutes(cliCtx, r)
 | 
			
		||||
	registerTxRoutes(cliCtx, r)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								x/incentive/client/rest/tx.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								x/incentive/client/rest/tx.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	r.HandleFunc("/incentive/claim", postClaimHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody types.PostClaimReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		requestBody.BaseReq = requestBody.BaseReq.Sanitize()
 | 
			
		||||
		if !requestBody.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		msg := types.NewMsgClaimReward(requestBody.Sender, requestBody.Denom)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								x/incentive/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								x/incentive/doc.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
/*
 | 
			
		||||
Package incentive implements a Cosmos SDK module, per ADR 009, that provides governance-controlled,
 | 
			
		||||
on-chain incentives for users who open cdps and mint stablecoins (USDX).
 | 
			
		||||
 | 
			
		||||
For the background and motivation of this module, see the governance proposal that was voted on by
 | 
			
		||||
KAVA token holders: https://ipfs.io/ipfs/QmSYedssC3nyQacDJmNcREtgmTPyaMx2JX7RNkMdAVkdkr/user-growth-fund-proposal.pdf
 | 
			
		||||
 | 
			
		||||
The 'Reward' parameter is used to control how much incentives are given.
 | 
			
		||||
For example, the following reward:
 | 
			
		||||
 | 
			
		||||
	Reward{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Denom: "bnb",
 | 
			
		||||
		AvailableRewards: sdk.NewCoin("ukava", 1000000000),
 | 
			
		||||
		Duration: time.Hour*7*24,
 | 
			
		||||
		TimeLock: time.Hour*24*365,
 | 
			
		||||
		ClaimDuration: time.Hour*7*24,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
will distribute 1000 KAVA each week (Duration) to users who mint USDX using collateral bnb.
 | 
			
		||||
That KAVA can be claimed by the user for one week (ClaimDuration) after the reward period expires,
 | 
			
		||||
and all KAVA rewards will be timelocked for 1 year (TimeLock). If  a user does not claim them during
 | 
			
		||||
the claim duration period, they are forgone.
 | 
			
		||||
 | 
			
		||||
Rewards are accumulated by users continuously and proportionally - ie. if a user holds a CDP that has
 | 
			
		||||
minted 10% of all USDX backed by bnb for the entire reward period, they will be eligible to claim 10%
 | 
			
		||||
of rewards for that period.
 | 
			
		||||
 | 
			
		||||
Once a reward period ends, but not before, users can claim the rewards they have accumulated.  Users claim rewards
 | 
			
		||||
using a MsgClaimReward transaction. The following msg:
 | 
			
		||||
 | 
			
		||||
	MsgClaimReward {
 | 
			
		||||
		"kava1..."
 | 
			
		||||
		"bnb"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
will claim all outstanding rewards for minting USDX backed by bnb for the input user.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
package incentive
 | 
			
		||||
							
								
								
									
										73
									
								
								x/incentive/genesis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								x/incentive/genesis.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
package incentive
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// InitGenesis initializes the store state from a genesis state.
 | 
			
		||||
func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeeper, gs types.GenesisState) {
 | 
			
		||||
 | 
			
		||||
	// check if the module account exists
 | 
			
		||||
	moduleAcc := supplyKeeper.GetModuleAccount(ctx, types.IncentiveMacc)
 | 
			
		||||
	if moduleAcc == nil {
 | 
			
		||||
		panic(fmt.Sprintf("%s module account has not been set", types.IncentiveMacc))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := gs.Validate(); err != nil {
 | 
			
		||||
		panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.SetParams(ctx, gs.Params)
 | 
			
		||||
 | 
			
		||||
	for _, r := range gs.Params.Rewards {
 | 
			
		||||
		k.SetNextClaimPeriodID(ctx, r.Denom, 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// only set the previous block time if it's different than default
 | 
			
		||||
	if !gs.PreviousBlockTime.Equal(types.DefaultPreviousBlockTime) {
 | 
			
		||||
		k.SetPreviousBlockTime(ctx, gs.PreviousBlockTime)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set store objects
 | 
			
		||||
	for _, rp := range gs.RewardPeriods {
 | 
			
		||||
		k.SetRewardPeriod(ctx, rp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, cp := range gs.ClaimPeriods {
 | 
			
		||||
		k.SetClaimPeriod(ctx, cp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range gs.Claims {
 | 
			
		||||
		k.SetClaim(ctx, c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, id := range gs.NextClaimPeriodIDs {
 | 
			
		||||
		k.SetNextClaimPeriodID(ctx, id.Denom, id.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExportGenesis export genesis state for incentive module
 | 
			
		||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
 | 
			
		||||
	// get all objects out of the store
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
	previousBlockTime, found := k.GetPreviousBlockTime(ctx)
 | 
			
		||||
 | 
			
		||||
	// since it is not set in genesis, if somehow the chain got started and was exported
 | 
			
		||||
	// immediately after InitGenesis, there would be no previousBlockTime value.
 | 
			
		||||
	if !found {
 | 
			
		||||
		previousBlockTime = types.DefaultPreviousBlockTime
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all objects from the store
 | 
			
		||||
	rewardPeriods := k.GetAllRewardPeriods(ctx)
 | 
			
		||||
	claimPeriods := k.GetAllClaimPeriods(ctx)
 | 
			
		||||
	claims := k.GetAllClaims(ctx)
 | 
			
		||||
	claimPeriodIDs := k.GetAllClaimPeriodIDPairs(ctx)
 | 
			
		||||
 | 
			
		||||
	return types.NewGenesisState(params, previousBlockTime, rewardPeriods, claimPeriods, claims, claimPeriodIDs)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								x/incentive/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								x/incentive/handler.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
package incentive
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewHandler creates an sdk.Handler for incentive module messages
 | 
			
		||||
func NewHandler(k keeper.Keeper) sdk.Handler {
 | 
			
		||||
	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
 | 
			
		||||
		ctx = ctx.WithEventManager(sdk.NewEventManager())
 | 
			
		||||
		switch msg := msg.(type) {
 | 
			
		||||
		case types.MsgClaimReward:
 | 
			
		||||
			return handleMsgClaimReward(ctx, k, msg)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimReward) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	claims, found := k.GetClaimsByAddressAndDenom(ctx, msg.Sender, msg.Denom)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil, sdkerrors.Wrapf(types.ErrNoClaimsFound, "address: %s, denom: %s", msg.Sender, msg.Denom)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, claim := range claims {
 | 
			
		||||
		err := k.PayoutClaim(ctx, claim.Owner, claim.Denom, claim.ClaimPeriodID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			sdk.EventTypeMessage,
 | 
			
		||||
			sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
 | 
			
		||||
			sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										243
									
								
								x/incentive/keeper/keeper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								x/incentive/keeper/keeper.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,243 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/store/prefix"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/params/subspace"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Keeper keeper for the incentive module
 | 
			
		||||
type Keeper struct {
 | 
			
		||||
	accountKeeper types.AccountKeeper
 | 
			
		||||
	cdc           *codec.Codec
 | 
			
		||||
	cdpKeeper     types.CdpKeeper
 | 
			
		||||
	key           sdk.StoreKey
 | 
			
		||||
	paramSubspace subspace.Subspace
 | 
			
		||||
	supplyKeeper  types.SupplyKeeper
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewKeeper creates a new keeper
 | 
			
		||||
func NewKeeper(
 | 
			
		||||
	cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper,
 | 
			
		||||
	cdpk types.CdpKeeper, ak types.AccountKeeper,
 | 
			
		||||
) Keeper {
 | 
			
		||||
 | 
			
		||||
	return Keeper{
 | 
			
		||||
		accountKeeper: ak,
 | 
			
		||||
		cdc:           cdc,
 | 
			
		||||
		cdpKeeper:     cdpk,
 | 
			
		||||
		key:           key,
 | 
			
		||||
		paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()),
 | 
			
		||||
		supplyKeeper:  sk,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetRewardPeriod returns the reward period from the store for the input denom and a boolean for if it was found
 | 
			
		||||
func (k Keeper) GetRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
 | 
			
		||||
	bz := store.Get([]byte(denom))
 | 
			
		||||
	if bz == nil {
 | 
			
		||||
		return types.RewardPeriod{}, false
 | 
			
		||||
	}
 | 
			
		||||
	var rp types.RewardPeriod
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(bz, &rp)
 | 
			
		||||
	return rp, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRewardPeriod sets the reward period in the store for the input deno,
 | 
			
		||||
func (k Keeper) SetRewardPeriod(ctx sdk.Context, rp types.RewardPeriod) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
 | 
			
		||||
	bz := k.cdc.MustMarshalBinaryBare(rp)
 | 
			
		||||
	store.Set([]byte(rp.Denom), bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteRewardPeriod deletes the reward period in the store for the input denom,
 | 
			
		||||
func (k Keeper) DeleteRewardPeriod(ctx sdk.Context, denom string) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
 | 
			
		||||
	store.Delete([]byte(denom))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IterateRewardPeriods iterates over all reward period objects in the store and preforms a callback function
 | 
			
		||||
func (k Keeper) IterateRewardPeriods(ctx sdk.Context, cb func(rp types.RewardPeriod) (stop bool)) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
 | 
			
		||||
	iterator := sdk.KVStorePrefixIterator(store, []byte{})
 | 
			
		||||
	defer iterator.Close()
 | 
			
		||||
	for ; iterator.Valid(); iterator.Next() {
 | 
			
		||||
		var rp types.RewardPeriod
 | 
			
		||||
		k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &rp)
 | 
			
		||||
		if cb(rp) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAllRewardPeriods returns all reward periods in the store
 | 
			
		||||
func (k Keeper) GetAllRewardPeriods(ctx sdk.Context) types.RewardPeriods {
 | 
			
		||||
	rps := types.RewardPeriods{}
 | 
			
		||||
	k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) (stop bool) {
 | 
			
		||||
		rps = append(rps, rp)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	return rps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetNextClaimPeriodID returns the highest claim period id in the store for the input denom
 | 
			
		||||
func (k Keeper) GetNextClaimPeriodID(ctx sdk.Context, denom string) uint64 {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
 | 
			
		||||
	bz := store.Get([]byte(denom))
 | 
			
		||||
	return types.BytesToUint64(bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetNextClaimPeriodID sets the highest claim period id in the store for the input denom
 | 
			
		||||
func (k Keeper) SetNextClaimPeriodID(ctx sdk.Context, denom string, id uint64) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
 | 
			
		||||
	store.Set([]byte(denom), sdk.Uint64ToBigEndian(id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IterateClaimPeriodIDKeysAndValues iterates over the claim period id (value) and denom (key) of each claim period id in the store and performs a callback function
 | 
			
		||||
func (k Keeper) IterateClaimPeriodIDKeysAndValues(ctx sdk.Context, cb func(denom string, id uint64) (stop bool)) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.NextClaimPeriodIDPrefix)
 | 
			
		||||
	iterator := sdk.KVStorePrefixIterator(store, []byte{})
 | 
			
		||||
	defer iterator.Close()
 | 
			
		||||
	for ; iterator.Valid(); iterator.Next() {
 | 
			
		||||
		id := types.BytesToUint64(iterator.Value())
 | 
			
		||||
		denom := string(iterator.Key())
 | 
			
		||||
		if cb(denom, id) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAllClaimPeriodIDPairs returns all denom:nextClaimPeriodID pairs in the store
 | 
			
		||||
func (k Keeper) GetAllClaimPeriodIDPairs(ctx sdk.Context) types.GenesisClaimPeriodIDs {
 | 
			
		||||
	ids := types.GenesisClaimPeriodIDs{}
 | 
			
		||||
	k.IterateClaimPeriodIDKeysAndValues(ctx, func(denom string, id uint64) (stop bool) {
 | 
			
		||||
		genID := types.GenesisClaimPeriodID{
 | 
			
		||||
			Denom: denom,
 | 
			
		||||
			ID:    id,
 | 
			
		||||
		}
 | 
			
		||||
		ids = append(ids, genID)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	return ids
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClaimPeriod returns claim period in the store for the input ID and denom and a boolean for if it was found
 | 
			
		||||
func (k Keeper) GetClaimPeriod(ctx sdk.Context, id uint64, denom string) (types.ClaimPeriod, bool) {
 | 
			
		||||
	var cp types.ClaimPeriod
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
 | 
			
		||||
	bz := store.Get(types.GetClaimPeriodPrefix(denom, id))
 | 
			
		||||
	if bz == nil {
 | 
			
		||||
		return types.ClaimPeriod{}, false
 | 
			
		||||
	}
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(bz, &cp)
 | 
			
		||||
	return cp, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetClaimPeriod sets the claim period in the store for the input ID and denom
 | 
			
		||||
func (k Keeper) SetClaimPeriod(ctx sdk.Context, cp types.ClaimPeriod) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
 | 
			
		||||
	bz := k.cdc.MustMarshalBinaryBare(cp)
 | 
			
		||||
	store.Set(types.GetClaimPeriodPrefix(cp.Denom, cp.ID), bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteClaimPeriod deletes the claim period in the store for the input ID and denom
 | 
			
		||||
func (k Keeper) DeleteClaimPeriod(ctx sdk.Context, id uint64, denom string) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
 | 
			
		||||
	store.Delete(types.GetClaimPeriodPrefix(denom, id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IterateClaimPeriods iterates over all claim period objects in the store and preforms a callback function
 | 
			
		||||
func (k Keeper) IterateClaimPeriods(ctx sdk.Context, cb func(cp types.ClaimPeriod) (stop bool)) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimPeriodKeyPrefix)
 | 
			
		||||
	iterator := sdk.KVStorePrefixIterator(store, []byte{})
 | 
			
		||||
	defer iterator.Close()
 | 
			
		||||
	for ; iterator.Valid(); iterator.Next() {
 | 
			
		||||
		var cp types.ClaimPeriod
 | 
			
		||||
		k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &cp)
 | 
			
		||||
		if cb(cp) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAllClaimPeriods returns all ClaimPeriod objects in the store
 | 
			
		||||
func (k Keeper) GetAllClaimPeriods(ctx sdk.Context) types.ClaimPeriods {
 | 
			
		||||
	cps := types.ClaimPeriods{}
 | 
			
		||||
	k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) {
 | 
			
		||||
		cps = append(cps, cp)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	return cps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClaim returns the claim in the store corresponding the the input address denom and id and a boolean for if the claim was found
 | 
			
		||||
func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64) (types.Claim, bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
 | 
			
		||||
	bz := store.Get(types.GetClaimPrefix(addr, denom, id))
 | 
			
		||||
	if bz == nil {
 | 
			
		||||
		return types.Claim{}, false
 | 
			
		||||
	}
 | 
			
		||||
	var c types.Claim
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(bz, &c)
 | 
			
		||||
	return c, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetClaim sets the claim in the store corresponding to the input address, denom, and id
 | 
			
		||||
func (k Keeper) SetClaim(ctx sdk.Context, c types.Claim) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
 | 
			
		||||
	bz := k.cdc.MustMarshalBinaryBare(c)
 | 
			
		||||
	store.Set(types.GetClaimPrefix(c.Owner, c.Denom, c.ClaimPeriodID), bz)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteClaim deletes the claim in the store corresponding to the input address, denom, and id
 | 
			
		||||
func (k Keeper) DeleteClaim(ctx sdk.Context, owner sdk.AccAddress, denom string, id uint64) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
 | 
			
		||||
	store.Delete(types.GetClaimPrefix(owner, denom, id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IterateClaims iterates over all claim  objects in the store and preforms a callback function
 | 
			
		||||
func (k Keeper) IterateClaims(ctx sdk.Context, cb func(c types.Claim) (stop bool)) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
 | 
			
		||||
	iterator := sdk.KVStorePrefixIterator(store, []byte{})
 | 
			
		||||
	defer iterator.Close()
 | 
			
		||||
	for ; iterator.Valid(); iterator.Next() {
 | 
			
		||||
		var c types.Claim
 | 
			
		||||
		k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &c)
 | 
			
		||||
		if cb(c) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAllClaims returns all Claim objects in the store
 | 
			
		||||
func (k Keeper) GetAllClaims(ctx sdk.Context) types.Claims {
 | 
			
		||||
	cs := types.Claims{}
 | 
			
		||||
	k.IterateClaims(ctx, func(c types.Claim) (stop bool) {
 | 
			
		||||
		cs = append(cs, c)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	return cs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPreviousBlockTime get the blocktime for the previous block
 | 
			
		||||
func (k Keeper) GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time, found bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
 | 
			
		||||
	b := store.Get([]byte{})
 | 
			
		||||
	if b == nil {
 | 
			
		||||
		return time.Time{}, false
 | 
			
		||||
	}
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(b, &blockTime)
 | 
			
		||||
	return blockTime, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPreviousBlockTime set the time of the previous block
 | 
			
		||||
func (k Keeper) SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
 | 
			
		||||
	store.Set([]byte{}, k.cdc.MustMarshalBinaryBare(blockTime))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								x/incentive/keeper/params.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								x/incentive/keeper/params.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetParams returns the params from the store
 | 
			
		||||
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
 | 
			
		||||
	var p types.Params
 | 
			
		||||
	k.paramSubspace.GetParamSet(ctx, &p)
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetParams sets params on the store
 | 
			
		||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
 | 
			
		||||
	k.paramSubspace.SetParamSet(ctx, ¶ms)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										204
									
								
								x/incentive/keeper/payout.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								x/incentive/keeper/payout.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	supplyExported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PayoutClaim sends the timelocked claim coins to the input address
 | 
			
		||||
func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64) error {
 | 
			
		||||
	claim, found := k.GetClaim(ctx, addr, denom, id)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "id: %d, denom %s, address: %s", id, denom, addr)
 | 
			
		||||
	}
 | 
			
		||||
	claimPeriod, found := k.GetClaimPeriod(ctx, id, denom)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimPeriodNotFound, "id: %d, denom: %s", id, denom)
 | 
			
		||||
	}
 | 
			
		||||
	err := k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(claim.Reward), int64(claimPeriod.TimeLock.Seconds()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeySender, fmt.Sprintf("%s", addr)),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendTimeLockedCoinsToAccount sends time-locked coins from the input module account to the recipient. If the recipients account is not a vesting account, it is converted to a periodic vesting account and the coins are added to the vesting balance as a vesting period with the input length.
 | 
			
		||||
func (k Keeper) SendTimeLockedCoinsToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, length int64) error {
 | 
			
		||||
	macc := k.supplyKeeper.GetModuleAccount(ctx, senderModule)
 | 
			
		||||
	if !macc.GetCoins().IsAllGTE(amt) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInsufficientModAccountBalance, "%s", senderModule)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 0. Get the account from the account keeper and do a type switch, error if it's a validator vesting account or module account (can make this work for validator vesting later if necessary)
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, recipientAddr)
 | 
			
		||||
 | 
			
		||||
	switch acc.(type) {
 | 
			
		||||
	case *validatorvesting.ValidatorVestingAccount, supplyExported.ModuleAccountI:
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "%T", acc)
 | 
			
		||||
	case *vesting.PeriodicVestingAccount:
 | 
			
		||||
		return k.SendTimeLockedCoinsToPeriodicVestingAccount(ctx, senderModule, recipientAddr, amt, length)
 | 
			
		||||
	case *auth.BaseAccount:
 | 
			
		||||
		return k.SendTimeLockedCoinsToBaseAccount(ctx, senderModule, recipientAddr, amt, length)
 | 
			
		||||
	default:
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "%T", acc)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendTimeLockedCoinsToPeriodicVestingAccount sends time-locked coins from the input module account to the recipient
 | 
			
		||||
func (k Keeper) SendTimeLockedCoinsToPeriodicVestingAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, length int64) error {
 | 
			
		||||
	err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	k.addCoinsToVestingSchedule(ctx, recipientAddr, amt, length)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendTimeLockedCoinsToBaseAccount sends time-locked coins from the input module account to the recipient, converting the recipient account to a vesting account
 | 
			
		||||
func (k Keeper) SendTimeLockedCoinsToBaseAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, length int64) error {
 | 
			
		||||
	err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, recipientAddr)
 | 
			
		||||
	// transition the account to a periodic vesting account:
 | 
			
		||||
	bacc := authtypes.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
 | 
			
		||||
	newPeriods := vesting.Periods{types.NewPeriod(amt, length)}
 | 
			
		||||
	bva, err := vesting.NewBaseVestingAccount(bacc, amt, ctx.BlockTime().Unix()+length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	pva := vesting.NewPeriodicVestingAccountRaw(bva, ctx.BlockTime().Unix(), newPeriods)
 | 
			
		||||
	k.accountKeeper.SetAccount(ctx, pva)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteExpiredClaimsAndClaimPeriods deletes expired claim periods and their associated claims
 | 
			
		||||
func (k Keeper) DeleteExpiredClaimsAndClaimPeriods(ctx sdk.Context) {
 | 
			
		||||
	k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) {
 | 
			
		||||
		if !cp.End.Before(ctx.BlockTime()) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		k.IterateClaims(ctx, func(c types.Claim) (stop bool) {
 | 
			
		||||
			if !(c.Denom == cp.Denom && c.ClaimPeriodID == cp.ID) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			k.DeleteClaim(ctx, c.Owner, c.Denom, c.ClaimPeriodID)
 | 
			
		||||
			return false
 | 
			
		||||
		})
 | 
			
		||||
		k.DeleteClaimPeriod(ctx, cp.ID, cp.Denom)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClaimsByAddressAndDenom returns all claims for a specific user and address and a bool for if any were found
 | 
			
		||||
func (k Keeper) GetClaimsByAddressAndDenom(ctx sdk.Context, addr sdk.AccAddress, denom string) (claims types.Claims, found bool) {
 | 
			
		||||
	found = false
 | 
			
		||||
	k.IterateClaimPeriods(ctx, func(cp types.ClaimPeriod) (stop bool) {
 | 
			
		||||
		if cp.Denom != denom {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		c, hasClaim := k.GetClaim(ctx, addr, cp.Denom, cp.ID)
 | 
			
		||||
		if !hasClaim {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		found = true
 | 
			
		||||
		claims = append(claims, c)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	return claims, found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addCoinsToVestingSchedule adds coins to the input account's vesting schedule where length is the amount of time (from the current block time), in seconds, that the coins will be vesting for
 | 
			
		||||
// the input address must be a periodic vesting account
 | 
			
		||||
func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, length int64) {
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, addr)
 | 
			
		||||
	vacc := acc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
	// Add the new vesting coins to OriginalVesting
 | 
			
		||||
	vacc.OriginalVesting = vacc.OriginalVesting.Add(amt...)
 | 
			
		||||
	// update vesting periods
 | 
			
		||||
	if vacc.EndTime < ctx.BlockTime().Unix() {
 | 
			
		||||
		// edge case one - the vesting account's end time is in the past (ie, all previous vesting periods have completed)
 | 
			
		||||
		// append a new period to the vesting account, update the end time, update the account in the store and return
 | 
			
		||||
		newPeriodLength := (ctx.BlockTime().Unix() - vacc.EndTime) + length
 | 
			
		||||
		newPeriod := types.NewPeriod(amt, newPeriodLength)
 | 
			
		||||
		vacc.VestingPeriods = append(vacc.VestingPeriods, newPeriod)
 | 
			
		||||
		vacc.EndTime = ctx.BlockTime().Unix() + length
 | 
			
		||||
		k.accountKeeper.SetAccount(ctx, vacc)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if vacc.StartTime > ctx.BlockTime().Unix() {
 | 
			
		||||
		// edge case two - the vesting account's start time is in the future (all periods have not started)
 | 
			
		||||
		// update the start time to now and adjust the period lengths in place - a new period will be inserted in the next code block
 | 
			
		||||
		updatedPeriods := vesting.Periods{}
 | 
			
		||||
		for i, period := range vacc.VestingPeriods {
 | 
			
		||||
			updatedPeriod := period
 | 
			
		||||
			if i == 0 {
 | 
			
		||||
				updatedPeriod = types.NewPeriod(period.Amount, (vacc.StartTime-ctx.BlockTime().Unix())+period.Length)
 | 
			
		||||
			}
 | 
			
		||||
			updatedPeriods = append(updatedPeriods, updatedPeriod)
 | 
			
		||||
		}
 | 
			
		||||
		vacc.VestingPeriods = updatedPeriods
 | 
			
		||||
		vacc.StartTime = ctx.BlockTime().Unix()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// logic for inserting a new vesting period into the existing vesting schedule
 | 
			
		||||
	totalPeriodLength := types.GetTotalVestingPeriodLength(vacc.VestingPeriods)
 | 
			
		||||
	proposedEndTime := ctx.BlockTime().Unix() + length
 | 
			
		||||
	if totalPeriodLength < length {
 | 
			
		||||
		// in the case that the proposed length is longer than the sum of all previous period lengths, create a new period with length equal to the difference between the proposed length and the previous total length
 | 
			
		||||
		newPeriodLength := length - totalPeriodLength
 | 
			
		||||
		newPeriod := types.NewPeriod(amt, newPeriodLength)
 | 
			
		||||
		vacc.VestingPeriods = append(vacc.VestingPeriods, newPeriod)
 | 
			
		||||
		// update the end time so that the sum of all period lengths equals endTime - startTime
 | 
			
		||||
		vacc.EndTime = proposedEndTime
 | 
			
		||||
	} else {
 | 
			
		||||
		// In the case that the proposed length is less than or equal to the sum of all previous period lengths, insert the period and update other periods as necessary.
 | 
			
		||||
		// EXAMPLE (l is length, a is amount)
 | 
			
		||||
		// Original Periods: {[l: 1 a: 1], [l: 2, a: 1], [l:8, a:3], [l: 5, a: 3]}
 | 
			
		||||
		// Period we want to insert [l: 5, a: x]
 | 
			
		||||
		// Expected result:
 | 
			
		||||
		// {[l: 1, a: 1], [l:2, a: 1], [l:2, a:x], [l:6, a:3], [l:5, a:3]}
 | 
			
		||||
 | 
			
		||||
		newPeriods := vesting.Periods{}
 | 
			
		||||
		lengthCounter := int64(0)
 | 
			
		||||
		appendRemaining := false
 | 
			
		||||
		for _, period := range vacc.VestingPeriods {
 | 
			
		||||
			if appendRemaining {
 | 
			
		||||
				newPeriods = append(newPeriods, period)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			lengthCounter += period.Length
 | 
			
		||||
			if lengthCounter < length {
 | 
			
		||||
				newPeriods = append(newPeriods, period)
 | 
			
		||||
			} else if lengthCounter == length {
 | 
			
		||||
				newPeriod := types.NewPeriod(period.Amount.Add(amt...), period.Length)
 | 
			
		||||
				newPeriods = append(newPeriods, newPeriod)
 | 
			
		||||
				appendRemaining = true
 | 
			
		||||
			} else {
 | 
			
		||||
				newPeriod := types.NewPeriod(amt, length-types.GetTotalVestingPeriodLength(newPeriods))
 | 
			
		||||
				previousPeriod := types.NewPeriod(period.Amount, period.Length-newPeriod.Length)
 | 
			
		||||
				newPeriods = append(newPeriods, newPeriod, previousPeriod)
 | 
			
		||||
				appendRemaining = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		vacc.VestingPeriods = newPeriods
 | 
			
		||||
	}
 | 
			
		||||
	k.accountKeeper.SetAccount(ctx, vacc)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								x/incentive/keeper/querier.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								x/incentive/keeper/querier.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewQuerier is the module level router for state queries
 | 
			
		||||
func NewQuerier(k Keeper) sdk.Querier {
 | 
			
		||||
	return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) {
 | 
			
		||||
		switch path[0] {
 | 
			
		||||
		case types.QueryGetParams:
 | 
			
		||||
			return queryGetParams(ctx, req, k)
 | 
			
		||||
		case types.QueryGetClaims:
 | 
			
		||||
			return queryGetClaims(ctx, req, k)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// query params in the store
 | 
			
		||||
func queryGetParams(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
 | 
			
		||||
	// Get params
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
	// Encode results
 | 
			
		||||
	bz, err := codec.MarshalJSONIndent(k.cdc, params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return bz, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
 | 
			
		||||
	var requestParams types.QueryClaimsParams
 | 
			
		||||
	err := k.cdc.UnmarshalJSON(req.Data, &requestParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	claims, _ := k.GetClaimsByAddressAndDenom(ctx, requestParams.Owner, requestParams.Denom)
 | 
			
		||||
 | 
			
		||||
	bz, err := codec.MarshalJSONIndent(k.cdc, claims)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return bz, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								x/incentive/keeper/rewards.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								x/incentive/keeper/rewards.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/store/prefix"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleRewardPeriodExpiry deletes expired RewardPeriods from the store and creates a ClaimPeriod in the store for each expired RewardPeriod
 | 
			
		||||
func (k Keeper) HandleRewardPeriodExpiry(ctx sdk.Context, rp types.RewardPeriod) {
 | 
			
		||||
	k.CreateUniqueClaimPeriod(ctx, rp.Denom, rp.ClaimEnd, rp.ClaimTimeLock)
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.RewardPeriodKeyPrefix)
 | 
			
		||||
	store.Delete([]byte(rp.Denom))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateNewRewardPeriod creates a new reward period from the input reward
 | 
			
		||||
func (k Keeper) CreateNewRewardPeriod(ctx sdk.Context, reward types.Reward) {
 | 
			
		||||
	// reward periods store the amount of rewards payed PER SECOND
 | 
			
		||||
	rewardsPerSecond := sdk.NewDecFromInt(reward.AvailableRewards.Amount).Quo(sdk.NewDecFromInt(sdk.NewInt(int64(reward.Duration.Seconds())))).TruncateInt()
 | 
			
		||||
	rewardCoinPerSecond := sdk.NewCoin(reward.AvailableRewards.Denom, rewardsPerSecond)
 | 
			
		||||
	rp := types.RewardPeriod{
 | 
			
		||||
		Denom:         reward.Denom,
 | 
			
		||||
		Start:         ctx.BlockTime(),
 | 
			
		||||
		End:           ctx.BlockTime().Add(reward.Duration),
 | 
			
		||||
		Reward:        rewardCoinPerSecond,
 | 
			
		||||
		ClaimEnd:      ctx.BlockTime().Add(reward.Duration).Add(reward.ClaimDuration),
 | 
			
		||||
		ClaimTimeLock: reward.TimeLock,
 | 
			
		||||
	}
 | 
			
		||||
	k.SetRewardPeriod(ctx, rp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateAndDeleteRewardPeriods creates reward periods for active rewards that don't already have a reward period and deletes reward periods for inactive rewards that currently have a reward period
 | 
			
		||||
func (k Keeper) CreateAndDeleteRewardPeriods(ctx sdk.Context) {
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
	for _, r := range params.Rewards {
 | 
			
		||||
		_, found := k.GetRewardPeriod(ctx, r.Denom)
 | 
			
		||||
		// if governance has made a reward inactive, delete the current period
 | 
			
		||||
		if found && !r.Active {
 | 
			
		||||
			k.DeleteRewardPeriod(ctx, r.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		// if a reward period for an active reward is not found, create one
 | 
			
		||||
		if !found && r.Active {
 | 
			
		||||
			k.CreateNewRewardPeriod(ctx, r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ApplyRewardsToCdps iterates over the reward periods and creates a claim for each cdp owner that created usdx with the collateral specified in the reward period
 | 
			
		||||
func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
 | 
			
		||||
	previousBlockTime, found := k.GetPreviousBlockTime(ctx)
 | 
			
		||||
	if !found {
 | 
			
		||||
		previousBlockTime = ctx.BlockTime()
 | 
			
		||||
		k.SetPreviousBlockTime(ctx, previousBlockTime)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool {
 | 
			
		||||
		expired := false
 | 
			
		||||
		// the total amount of usdx created with the collateral type being incentivized
 | 
			
		||||
		totalPrincipal := k.cdpKeeper.GetTotalPrincipal(ctx, rp.Denom, types.PrincipalDenom)
 | 
			
		||||
		// the number of seconds since last payout
 | 
			
		||||
		timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
 | 
			
		||||
		if rp.End.Before(ctx.BlockTime()) {
 | 
			
		||||
			timeElapsed = sdk.NewInt(rp.End.Unix() - previousBlockTime.Unix())
 | 
			
		||||
			expired = true
 | 
			
		||||
		}
 | 
			
		||||
		// the amount of rewards to pay (rewardAmount * timeElapsed)
 | 
			
		||||
		rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed)
 | 
			
		||||
		id := k.GetNextClaimPeriodID(ctx, rp.Denom)
 | 
			
		||||
		k.cdpKeeper.IterateCdpsByDenom(ctx, rp.Denom, func(cdp cdptypes.CDP) bool {
 | 
			
		||||
			rewardsShare := sdk.NewDecFromInt(cdp.Principal.AmountOf(types.PrincipalDenom).Add(cdp.AccumulatedFees.AmountOf(types.PrincipalDenom))).Quo(sdk.NewDecFromInt(totalPrincipal))
 | 
			
		||||
			// sanity check - don't create zero claims
 | 
			
		||||
			if rewardsShare.IsZero() {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			rewardsEarned := rewardsShare.Mul(sdk.NewDecFromInt(rewardsThisPeriod)).RoundInt()
 | 
			
		||||
			k.AddToClaim(ctx, cdp.Owner, rp.Denom, id, sdk.NewCoin(types.GovDenom, rewardsEarned))
 | 
			
		||||
			return false
 | 
			
		||||
		})
 | 
			
		||||
		if !expired {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		k.HandleRewardPeriodExpiry(ctx, rp)
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	k.SetPreviousBlockTime(ctx, ctx.BlockTime())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateUniqueClaimPeriod creates a new claim period in the store and updates the highest claim period id
 | 
			
		||||
func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, denom string, end time.Time, timeLock time.Duration) {
 | 
			
		||||
	id := k.GetNextClaimPeriodID(ctx, denom)
 | 
			
		||||
	claimPeriod := types.NewClaimPeriod(denom, id, end, timeLock)
 | 
			
		||||
	k.SetClaimPeriod(ctx, claimPeriod)
 | 
			
		||||
	k.SetNextClaimPeriodID(ctx, denom, id+1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddToClaim adds the amount to an existing claim or creates a new one for that amount
 | 
			
		||||
func (k Keeper) AddToClaim(ctx sdk.Context, addr sdk.AccAddress, denom string, id uint64, amount sdk.Coin) {
 | 
			
		||||
	claim, found := k.GetClaim(ctx, addr, denom, id)
 | 
			
		||||
	if found {
 | 
			
		||||
		claim.Reward = claim.Reward.Add(amount)
 | 
			
		||||
	} else {
 | 
			
		||||
		claim = types.NewClaim(addr, amount, denom, id)
 | 
			
		||||
	}
 | 
			
		||||
	k.SetClaim(ctx, claim)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								x/incentive/module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								x/incentive/module.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,164 @@
 | 
			
		||||
package incentive
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/module"
 | 
			
		||||
	sim "github.com/cosmos/cosmos-sdk/x/simulation"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/client/cli"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/client/rest"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/simulation"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ module.AppModule           = AppModule{}
 | 
			
		||||
	_ module.AppModuleBasic      = AppModuleBasic{}
 | 
			
		||||
	_ module.AppModuleSimulation = AppModule{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AppModuleBasic defines the basic application module used by the incentive module.
 | 
			
		||||
type AppModuleBasic struct{}
 | 
			
		||||
 | 
			
		||||
// Name returns the incentive module's name.
 | 
			
		||||
func (AppModuleBasic) Name() string {
 | 
			
		||||
	return types.ModuleName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterCodec registers the incentive module's types for the given codec.
 | 
			
		||||
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
 | 
			
		||||
	types.RegisterCodec(cdc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultGenesis returns default genesis state as raw bytes for the incentive
 | 
			
		||||
// module.
 | 
			
		||||
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
 | 
			
		||||
	return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateGenesis performs genesis state validation for the incentive module.
 | 
			
		||||
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
 | 
			
		||||
	var gs types.GenesisState
 | 
			
		||||
	err := types.ModuleCdc.UnmarshalJSON(bz, &gs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return gs.Validate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterRESTRoutes registers the REST routes for the incentive module.
 | 
			
		||||
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
 | 
			
		||||
	rest.RegisterRoutes(ctx, rtr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTxCmd returns the root tx command for the incentive module.
 | 
			
		||||
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return cli.GetTxCmd(cdc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetQueryCmd returns no root query command for the crisis module.
 | 
			
		||||
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return cli.GetQueryCmd(types.StoreKey, cdc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterStoreDecoder registers a decoder for cdp module's types
 | 
			
		||||
func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
 | 
			
		||||
	sdr[StoreKey] = simulation.DecodeStore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenerateGenesisState creates a randomized GenState of the cdp module
 | 
			
		||||
func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) {
 | 
			
		||||
	simulation.RandomizedGenState(simState)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RandomizedParams creates randomized cdp param changes for the simulator.
 | 
			
		||||
func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange {
 | 
			
		||||
	return simulation.ParamChanges(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProposalContents doesn't return any content functions for governance proposals.
 | 
			
		||||
func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WeightedOperations returns the all the bep3 module operations with their respective weights.
 | 
			
		||||
func (am AppModule) WeightedOperations(_ module.SimulationState) []sim.WeightedOperation {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AppModule implements the sdk.AppModule interface.
 | 
			
		||||
type AppModule struct {
 | 
			
		||||
	AppModuleBasic
 | 
			
		||||
 | 
			
		||||
	keeper       keeper.Keeper
 | 
			
		||||
	supplyKeeper types.SupplyKeeper
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAppModule creates a new AppModule object
 | 
			
		||||
func NewAppModule(keeper keeper.Keeper, supplyKeeper types.SupplyKeeper) AppModule {
 | 
			
		||||
	return AppModule{
 | 
			
		||||
		AppModuleBasic: AppModuleBasic{},
 | 
			
		||||
		keeper:         keeper,
 | 
			
		||||
		supplyKeeper:   supplyKeeper,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the incentive module's name.
 | 
			
		||||
func (AppModule) Name() string {
 | 
			
		||||
	return types.ModuleName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterInvariants registers the incentive module invariants.
 | 
			
		||||
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
 | 
			
		||||
 | 
			
		||||
// Route returns the message routing key for the incentive module.
 | 
			
		||||
func (AppModule) Route() string {
 | 
			
		||||
	return types.RouterKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHandler returns an sdk.Handler for the incentive module.
 | 
			
		||||
func (am AppModule) NewHandler() sdk.Handler {
 | 
			
		||||
	return NewHandler(am.keeper)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QuerierRoute returns the incentive module's querier route name.
 | 
			
		||||
func (AppModule) QuerierRoute() string {
 | 
			
		||||
	return types.QuerierRoute
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewQuerierHandler returns the incentive module sdk.Querier.
 | 
			
		||||
func (am AppModule) NewQuerierHandler() sdk.Querier {
 | 
			
		||||
	return keeper.NewQuerier(am.keeper)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InitGenesis performs genesis initialization for the incentive module. It returns no validator updates.
 | 
			
		||||
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
 | 
			
		||||
	var gs types.GenesisState
 | 
			
		||||
	types.ModuleCdc.MustUnmarshalJSON(data, &gs)
 | 
			
		||||
	InitGenesis(ctx, am.keeper, am.supplyKeeper, gs)
 | 
			
		||||
	return []abci.ValidatorUpdate{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExportGenesis returns the exported genesis state as raw bytes for the incentive module
 | 
			
		||||
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
 | 
			
		||||
	gs := ExportGenesis(ctx, am.keeper)
 | 
			
		||||
	return types.ModuleCdc.MustMarshalJSON(gs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeginBlock returns the begin blocker for the incentive module.
 | 
			
		||||
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
 | 
			
		||||
	BeginBlocker(ctx, am.keeper)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EndBlock returns the end blocker for the incentive module. It returns no validator updates.
 | 
			
		||||
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
 | 
			
		||||
	return []abci.ValidatorUpdate{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								x/incentive/simulation/decoder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								x/incentive/simulation/decoder.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
package simulation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	"github.com/tendermint/tendermint/libs/kv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DecodeStore unmarshals the KVPair's Value to the corresponding incentive type
 | 
			
		||||
func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
 | 
			
		||||
	// TODO implement this
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								x/incentive/simulation/params.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								x/incentive/simulation/params.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
package simulation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/simulation"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ParamChanges defines the parameters that can be modified by param change proposals
 | 
			
		||||
// on the simulation
 | 
			
		||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
 | 
			
		||||
	// TODO implement this
 | 
			
		||||
	return []simulation.ParamChange{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								x/incentive/simulation/simulation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								x/incentive/simulation/simulation.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
package simulation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/module"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RandomizedGenState generates a random GenesisState for cdp
 | 
			
		||||
func RandomizedGenState(simState *module.SimulationState) {
 | 
			
		||||
 | 
			
		||||
	// TODO implement this fully
 | 
			
		||||
	// - randomly generating the genesis params
 | 
			
		||||
	// - overwriting with genesis provided to simulation
 | 
			
		||||
	genesis := types.DefaultGenesisState()
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, genesis))
 | 
			
		||||
	simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								x/incentive/types/account.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								x/incentive/types/account.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetTotalVestingPeriodLength returns the summed length of all vesting periods
 | 
			
		||||
func GetTotalVestingPeriodLength(periods vesting.Periods) int64 {
 | 
			
		||||
	length := int64(0)
 | 
			
		||||
	for _, period := range periods {
 | 
			
		||||
		length += period.Length
 | 
			
		||||
	}
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								x/incentive/types/account_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								x/incentive/types/account_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type accountTest struct {
 | 
			
		||||
	periods     vesting.Periods
 | 
			
		||||
	expectedVal int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	tests []accountTest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *AccountTestSuite) SetupTest() {
 | 
			
		||||
	tests := []accountTest{
 | 
			
		||||
		accountTest{
 | 
			
		||||
			periods: vesting.Periods{
 | 
			
		||||
				vesting.Period{
 | 
			
		||||
					Length: int64(100),
 | 
			
		||||
					Amount: sdk.Coins{},
 | 
			
		||||
				},
 | 
			
		||||
				vesting.Period{
 | 
			
		||||
					Length: int64(200),
 | 
			
		||||
					Amount: sdk.Coins{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedVal: int64(300),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.tests = tests
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *AccountTestSuite) TestGetTotalPeriodLength() {
 | 
			
		||||
	for _, t := range suite.tests {
 | 
			
		||||
		length := types.GetTotalVestingPeriodLength(t.periods)
 | 
			
		||||
		suite.Equal(t.expectedVal, length)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAccountTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(AccountTestSuite))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								x/incentive/types/codec.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								x/incentive/types/codec.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import "github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
 | 
			
		||||
// ModuleCdc generic sealed codec to be used throughout module
 | 
			
		||||
var ModuleCdc *codec.Codec
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	cdc := codec.New()
 | 
			
		||||
	RegisterCodec(cdc)
 | 
			
		||||
	codec.RegisterCrypto(cdc)
 | 
			
		||||
	ModuleCdc = cdc.Seal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterCodec registers the necessary types for incentive module
 | 
			
		||||
func RegisterCodec(cdc *codec.Codec) {
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimReward{}, "incentive/MsgClaimReward", nil)
 | 
			
		||||
	cdc.RegisterConcrete(GenesisClaimPeriodID{}, "incentive/GenesisClaimPeriodID", nil)
 | 
			
		||||
	cdc.RegisterConcrete(RewardPeriod{}, "incentive/RewardPeriod", nil)
 | 
			
		||||
	cdc.RegisterConcrete(ClaimPeriod{}, "incentive/ClaimPeriod", nil)
 | 
			
		||||
	cdc.RegisterConcrete(Claim{}, "incentive/Claim", nil)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								x/incentive/types/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								x/incentive/types/errors.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DONTCOVER
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrClaimNotFound                 = sdkerrors.Register(ModuleName, 1, "no claim with input id found for owner and denom")
 | 
			
		||||
	ErrClaimPeriodNotFound           = sdkerrors.Register(ModuleName, 2, "no claim period found for id and denom")
 | 
			
		||||
	ErrInvalidAccountType            = sdkerrors.Register(ModuleName, 3, "account type not supported")
 | 
			
		||||
	ErrNoClaimsFound                 = sdkerrors.Register(ModuleName, 4, "no claims with denom found for address")
 | 
			
		||||
	ErrInsufficientModAccountBalance = sdkerrors.Register(ModuleName, 5, "module account has insufficient balance to pay claim")
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										8
									
								
								x/incentive/types/events.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								x/incentive/types/events.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	EventTypeClaim = "claim_reward"
 | 
			
		||||
 | 
			
		||||
	AttributeValueCategory = ModuleName
 | 
			
		||||
	AttributeKeySender     = "sender"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										27
									
								
								x/incentive/types/expected_keepers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								x/incentive/types/expected_keepers.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
 | 
			
		||||
	supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SupplyKeeper defines the expected supply keeper for module accounts
 | 
			
		||||
type SupplyKeeper interface {
 | 
			
		||||
	GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI
 | 
			
		||||
 | 
			
		||||
	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CdpKeeper defines the expected cdp keeper for interacting with cdps
 | 
			
		||||
type CdpKeeper interface {
 | 
			
		||||
	IterateCdpsByDenom(ctx sdk.Context, denom string, cb func(cdp cdptypes.CDP) (stop bool))
 | 
			
		||||
	GetTotalPrincipal(ctx sdk.Context, collateralDenom string, principalDenom string) (total sdk.Int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AccountKeeper defines the expected keeper interface for interacting with account
 | 
			
		||||
type AccountKeeper interface {
 | 
			
		||||
	GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
 | 
			
		||||
	SetAccount(ctx sdk.Context, acc authexported.Account)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								x/incentive/types/genesis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								x/incentive/types/genesis.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GenesisClaimPeriodID stores the next claim id and its corresponding denom
 | 
			
		||||
type GenesisClaimPeriodID struct {
 | 
			
		||||
	Denom string `json:"denom" yaml:"denom"`
 | 
			
		||||
	ID    uint64 `json:"id" yaml:"id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenesisClaimPeriodIDs array of GenesisClaimPeriodID
 | 
			
		||||
type GenesisClaimPeriodIDs []GenesisClaimPeriodID
 | 
			
		||||
 | 
			
		||||
// GenesisState is the state that must be provided at genesis.
 | 
			
		||||
type GenesisState struct {
 | 
			
		||||
	Params             Params                `json:"params" yaml:"params"`
 | 
			
		||||
	PreviousBlockTime  time.Time             `json:"previous_block_time" yaml:"previous_block_time"`
 | 
			
		||||
	RewardPeriods      RewardPeriods         `json:"reward_periods" yaml:"reward_periods"`
 | 
			
		||||
	ClaimPeriods       ClaimPeriods          `json:"claim_periods" yaml:"claim_periods"`
 | 
			
		||||
	Claims             Claims                `json:"claims" yaml:"claims"`
 | 
			
		||||
	NextClaimPeriodIDs GenesisClaimPeriodIDs `json:"next_claim_period_ids" yaml:"next_claim_period_ids"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGenesisState returns a new genesis state
 | 
			
		||||
func NewGenesisState(params Params, previousBlockTime time.Time, rp RewardPeriods, cp ClaimPeriods, c Claims, ids GenesisClaimPeriodIDs) GenesisState {
 | 
			
		||||
	return GenesisState{
 | 
			
		||||
		Params:             params,
 | 
			
		||||
		PreviousBlockTime:  previousBlockTime,
 | 
			
		||||
		RewardPeriods:      rp,
 | 
			
		||||
		ClaimPeriods:       cp,
 | 
			
		||||
		Claims:             c,
 | 
			
		||||
		NextClaimPeriodIDs: ids,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultGenesisState returns a default genesis state
 | 
			
		||||
func DefaultGenesisState() GenesisState {
 | 
			
		||||
	return GenesisState{
 | 
			
		||||
		Params:             DefaultParams(),
 | 
			
		||||
		PreviousBlockTime:  DefaultPreviousBlockTime,
 | 
			
		||||
		RewardPeriods:      RewardPeriods{},
 | 
			
		||||
		ClaimPeriods:       ClaimPeriods{},
 | 
			
		||||
		Claims:             Claims{},
 | 
			
		||||
		NextClaimPeriodIDs: GenesisClaimPeriodIDs{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate performs basic validation of genesis data returning an
 | 
			
		||||
// error for any failed validation criteria.
 | 
			
		||||
func (gs GenesisState) Validate() error {
 | 
			
		||||
 | 
			
		||||
	if err := gs.Params.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if gs.PreviousBlockTime.Equal(time.Time{}) {
 | 
			
		||||
		return fmt.Errorf("previous block time not set")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal checks whether two gov GenesisState structs are equivalent
 | 
			
		||||
func (gs GenesisState) Equal(gs2 GenesisState) bool {
 | 
			
		||||
	b1 := ModuleCdc.MustMarshalBinaryBare(gs)
 | 
			
		||||
	b2 := ModuleCdc.MustMarshalBinaryBare(gs2)
 | 
			
		||||
	return bytes.Equal(b1, b2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty returns true if a GenesisState is empty
 | 
			
		||||
func (gs GenesisState) IsEmpty() bool {
 | 
			
		||||
	return gs.Equal(GenesisState{})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								x/incentive/types/keys.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								x/incentive/types/keys.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// ModuleName The name that will be used throughout the module
 | 
			
		||||
	ModuleName = "incentive"
 | 
			
		||||
 | 
			
		||||
	// StoreKey Top level store key where all module items will be stored
 | 
			
		||||
	StoreKey = ModuleName
 | 
			
		||||
 | 
			
		||||
	// RouterKey Top level router key
 | 
			
		||||
	RouterKey = ModuleName
 | 
			
		||||
 | 
			
		||||
	// DefaultParamspace default name for parameter store
 | 
			
		||||
	DefaultParamspace = ModuleName
 | 
			
		||||
 | 
			
		||||
	// QuerierRoute route used for abci queries
 | 
			
		||||
	QuerierRoute = ModuleName
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Key Prefixes
 | 
			
		||||
var (
 | 
			
		||||
	RewardPeriodKeyPrefix   = []byte{0x01} // prefix for keys that store reward periods
 | 
			
		||||
	ClaimPeriodKeyPrefix    = []byte{0x02} // prefix for keys that store claim periods
 | 
			
		||||
	ClaimKeyPrefix          = []byte{0x03} // prefix for keys that store claims
 | 
			
		||||
	NextClaimPeriodIDPrefix = []byte{0x04} // prefix for keys that store the next ID for claims periods
 | 
			
		||||
	PreviousBlockTimeKey    = []byte{0x05} // prefix for key that stores the previous blocktime
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Keys
 | 
			
		||||
// 0x00:Denom <> RewardPeriod the current active reward period (max 1 reward period per denom)
 | 
			
		||||
// 0x01:Denom:ID <> ClaimPeriod object for that ID, indexed by denom and ID
 | 
			
		||||
// 0x02:Denom:ID:Owner <> Claim object, indexed by Denom, ID and owner
 | 
			
		||||
// 0x03:Denom <> NextClaimPeriodIDPrefix the ID of the next claim period, indexed by denom
 | 
			
		||||
 | 
			
		||||
// BytesToUint64 returns uint64 format from a byte array
 | 
			
		||||
func BytesToUint64(bz []byte) uint64 {
 | 
			
		||||
	return binary.BigEndian.Uint64(bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClaimPeriodPrefix returns the key (denom + id) for a claim prefix
 | 
			
		||||
func GetClaimPeriodPrefix(denom string, id uint64) []byte {
 | 
			
		||||
	return createKey([]byte(denom), sdk.Uint64ToBigEndian(id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClaimPrefix returns the key (denom + id + address) for a claim
 | 
			
		||||
func GetClaimPrefix(addr sdk.AccAddress, denom string, id uint64) []byte {
 | 
			
		||||
	return createKey([]byte(denom), sdk.Uint64ToBigEndian(id), addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createKey(bytes ...[]byte) (r []byte) {
 | 
			
		||||
	for _, b := range bytes {
 | 
			
		||||
		r = append(r, b...)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								x/incentive/types/msg.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								x/incentive/types/msg.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ensure Msg interface compliance at compile time
 | 
			
		||||
var _ sdk.Msg = &MsgClaimReward{}
 | 
			
		||||
 | 
			
		||||
// MsgClaimReward message type used to claim rewards
 | 
			
		||||
type MsgClaimReward struct {
 | 
			
		||||
	Sender sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Denom  string         `json:"denom" yaml:"denom"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimReward returns a new MsgClaimReward.
 | 
			
		||||
func NewMsgClaimReward(sender sdk.AccAddress, denom string) MsgClaimReward {
 | 
			
		||||
	return MsgClaimReward{
 | 
			
		||||
		Sender: sender,
 | 
			
		||||
		Denom:  denom,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Route return the message type used for routing the message.
 | 
			
		||||
func (msg MsgClaimReward) Route() string { return RouterKey }
 | 
			
		||||
 | 
			
		||||
// Type returns a human-readable string for the message, intended for utilization within tags.
 | 
			
		||||
func (msg MsgClaimReward) Type() string { return "claim_reward" }
 | 
			
		||||
 | 
			
		||||
// ValidateBasic does a simple validation check that doesn't require access to state.
 | 
			
		||||
func (msg MsgClaimReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if strings.TrimSpace(msg.Denom) == "" {
 | 
			
		||||
		return errors.New("invalid (empty) denom")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
func (msg MsgClaimReward) GetSignBytes() []byte {
 | 
			
		||||
	bz := ModuleCdc.MustMarshalJSON(msg)
 | 
			
		||||
	return sdk.MustSortJSON(bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSigners returns the addresses of signers that must sign.
 | 
			
		||||
func (msg MsgClaimReward) GetSigners() []sdk.AccAddress {
 | 
			
		||||
	return []sdk.AccAddress{msg.Sender}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								x/incentive/types/msg_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								x/incentive/types/msg_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
	"github.com/tendermint/tendermint/crypto"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type msgTest struct {
 | 
			
		||||
	from       sdk.AccAddress
 | 
			
		||||
	denom      string
 | 
			
		||||
	expectPass bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MsgTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	tests []msgTest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *MsgTestSuite) SetupTest() {
 | 
			
		||||
	tests := []msgTest{
 | 
			
		||||
		msgTest{
 | 
			
		||||
			from:       sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			denom:      "bnb",
 | 
			
		||||
			expectPass: true,
 | 
			
		||||
		},
 | 
			
		||||
		msgTest{
 | 
			
		||||
			from:       sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			denom:      "",
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
		msgTest{
 | 
			
		||||
			from:       sdk.AccAddress{},
 | 
			
		||||
			denom:      "bnb",
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.tests = tests
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *MsgTestSuite) TestMsgValidation() {
 | 
			
		||||
	for _, t := range suite.tests {
 | 
			
		||||
		msg := types.NewMsgClaimReward(t.from, t.denom)
 | 
			
		||||
		err := msg.ValidateBasic()
 | 
			
		||||
		if t.expectPass {
 | 
			
		||||
			suite.NoError(err)
 | 
			
		||||
		} else {
 | 
			
		||||
			suite.Error(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMsgTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(MsgTestSuite))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										165
									
								
								x/incentive/types/params.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								x/incentive/types/params.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,165 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/params"
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
	kavadistTypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Parameter keys and default values
 | 
			
		||||
var (
 | 
			
		||||
	KeyActive                = []byte("Active")
 | 
			
		||||
	KeyRewards               = []byte("Rewards")
 | 
			
		||||
	DefaultActive            = false
 | 
			
		||||
	DefaultRewards           = Rewards{}
 | 
			
		||||
	DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
 | 
			
		||||
	GovDenom                 = cdptypes.DefaultGovDenom
 | 
			
		||||
	PrincipalDenom           = "usdx"
 | 
			
		||||
	IncentiveMacc            = kavadistTypes.ModuleName
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Params governance parameters for the incentive module
 | 
			
		||||
type Params struct {
 | 
			
		||||
	Active  bool    `json:"active" yaml:"active"` // top level governance switch to disable all rewards
 | 
			
		||||
	Rewards Rewards `json:"rewards" yaml:"rewards"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewParams returns a new params object
 | 
			
		||||
func NewParams(active bool, rewards Rewards) Params {
 | 
			
		||||
	return Params{
 | 
			
		||||
		Active:  active,
 | 
			
		||||
		Rewards: rewards,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultParams returns default params for kavadist module
 | 
			
		||||
func DefaultParams() Params {
 | 
			
		||||
	return NewParams(DefaultActive, DefaultRewards)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (p Params) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Params:
 | 
			
		||||
	Active: %t
 | 
			
		||||
	Rewards: %s`, p.Active, p.Rewards)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParamKeyTable Key declaration for parameters
 | 
			
		||||
func ParamKeyTable() params.KeyTable {
 | 
			
		||||
	return params.NewKeyTable().RegisterParamSet(&Params{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
 | 
			
		||||
func (p *Params) ParamSetPairs() params.ParamSetPairs {
 | 
			
		||||
	return params.ParamSetPairs{
 | 
			
		||||
		params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam),
 | 
			
		||||
		params.NewParamSetPair(KeyRewards, &p.Rewards, validateRewardsParam),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate checks that the parameters have valid values.
 | 
			
		||||
func (p Params) Validate() error {
 | 
			
		||||
	if err := validateActiveParam(p.Active); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := validateRewardsParam(p.Rewards); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateActiveParam(i interface{}) error {
 | 
			
		||||
	_, ok := i.(bool)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("invalid parameter type: %T", i)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRewardsParam(i interface{}) error {
 | 
			
		||||
	rewards, ok := i.(Rewards)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("invalid parameter type: %T", i)
 | 
			
		||||
	}
 | 
			
		||||
	rewardDenoms := make(map[string]bool)
 | 
			
		||||
 | 
			
		||||
	for _, reward := range rewards {
 | 
			
		||||
		if strings.TrimSpace(reward.Denom) == "" {
 | 
			
		||||
			return fmt.Errorf("cannot have empty reward denom: %s", reward)
 | 
			
		||||
		}
 | 
			
		||||
		if rewardDenoms[reward.Denom] {
 | 
			
		||||
			return fmt.Errorf("cannot have duplicate reward denoms: %s", reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		rewardDenoms[reward.Denom] = true
 | 
			
		||||
		if !reward.AvailableRewards.IsValid() {
 | 
			
		||||
			return fmt.Errorf("invalid reward coins %s for %s", reward.AvailableRewards, reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		if !reward.AvailableRewards.IsPositive() {
 | 
			
		||||
			return fmt.Errorf("reward amount must be positive, is %s for %s", reward.AvailableRewards, reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		if int(reward.Duration.Seconds()) <= 0 {
 | 
			
		||||
			return fmt.Errorf("reward duration must be positive, is %s for %s", reward.Duration.String(), reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		if int(reward.TimeLock.Seconds()) < 0 {
 | 
			
		||||
			return fmt.Errorf("reward timelock must be non-negative, is %s for %s", reward.TimeLock.String(), reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		if int(reward.ClaimDuration.Seconds()) <= 0 {
 | 
			
		||||
			return fmt.Errorf("reward timelock must be positive, is %s for %s", reward.ClaimDuration.String(), reward.Denom)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reward stores the specified state for a single reward period.
 | 
			
		||||
type Reward struct {
 | 
			
		||||
	Active           bool          `json:"active" yaml:"active"`                       // governance switch to disable a period
 | 
			
		||||
	Denom            string        `json:"denom" yaml:"denom"`                         // the collateral denom rewards apply to, must be found in the cdp collaterals
 | 
			
		||||
	AvailableRewards sdk.Coin      `json:"available_rewards" yaml:"available_rewards"` // the total amount of coins distributed per period
 | 
			
		||||
	Duration         time.Duration `json:"duration" yaml:"duration"`                   // the duration of the period
 | 
			
		||||
	TimeLock         time.Duration `json:"time_lock" yaml:"time_lock"`                 // how long rewards for this period are timelocked
 | 
			
		||||
	ClaimDuration    time.Duration `json:"claim_duration" yaml:"claim_duration"`       // how long users have after the period ends to claim their rewards
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewReward returns a new Reward
 | 
			
		||||
func NewReward(active bool, denom string, reward sdk.Coin, duration time.Duration, timelock time.Duration, claimDuration time.Duration) Reward {
 | 
			
		||||
	return Reward{
 | 
			
		||||
		Active:           active,
 | 
			
		||||
		Denom:            denom,
 | 
			
		||||
		AvailableRewards: reward,
 | 
			
		||||
		Duration:         duration,
 | 
			
		||||
		TimeLock:         timelock,
 | 
			
		||||
		ClaimDuration:    claimDuration,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (r Reward) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Reward:
 | 
			
		||||
	Active: %t,
 | 
			
		||||
	Denom: %s,
 | 
			
		||||
	Available Rewards: %s,
 | 
			
		||||
	Duration: %s,
 | 
			
		||||
	Time Lock: %s,
 | 
			
		||||
	Claim Duration: %s`,
 | 
			
		||||
		r.Active, r.Denom, r.AvailableRewards, r.Duration, r.TimeLock, r.ClaimDuration)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rewards array of Reward
 | 
			
		||||
type Rewards []Reward
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (rs Rewards) String() string {
 | 
			
		||||
	out := "Rewards\n"
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		out += fmt.Sprintf("%s\n", r)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								x/incentive/types/params_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								x/incentive/types/params_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type paramTest struct {
 | 
			
		||||
	params     types.Params
 | 
			
		||||
	expectPass bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ParamTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	tests []paramTest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ParamTestSuite) SetupTest() {
 | 
			
		||||
	p1 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 24 * 14,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	p2 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 24 * 14,
 | 
			
		||||
			},
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 24 * 14,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	p3 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * -24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 24 * 14,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	p4 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * -8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 24 * 14,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	p5 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(10000000000)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 0,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	p6 := types.Params{
 | 
			
		||||
		Active: true,
 | 
			
		||||
		Rewards: types.Rewards{
 | 
			
		||||
			types.Reward{
 | 
			
		||||
				Active:           true,
 | 
			
		||||
				Denom:            "bnb",
 | 
			
		||||
				AvailableRewards: sdk.NewCoin("ukava", sdk.NewInt(0)),
 | 
			
		||||
				Duration:         time.Hour * 24 * 7,
 | 
			
		||||
				TimeLock:         time.Hour * 8766,
 | 
			
		||||
				ClaimDuration:    time.Hour * 0,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.tests = []paramTest{
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p1,
 | 
			
		||||
			expectPass: true,
 | 
			
		||||
		},
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p2,
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p3,
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p4,
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p5,
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
		paramTest{
 | 
			
		||||
			params:     p6,
 | 
			
		||||
			expectPass: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ParamTestSuite) TestParamValidation() {
 | 
			
		||||
	for _, t := range suite.tests {
 | 
			
		||||
		err := t.params.Validate()
 | 
			
		||||
		if t.expectPass {
 | 
			
		||||
			suite.NoError(err)
 | 
			
		||||
		} else {
 | 
			
		||||
			suite.Error(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParamTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(ParamTestSuite))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								x/incentive/types/period.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								x/incentive/types/period.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewPeriod returns a new vesting period
 | 
			
		||||
func NewPeriod(amount sdk.Coins, length int64) vesting.Period {
 | 
			
		||||
	return vesting.Period{Amount: amount, Length: length}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								x/incentive/types/querier.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								x/incentive/types/querier.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Querier routes for the incentive module
 | 
			
		||||
const (
 | 
			
		||||
	QueryGetClaims = "claims"
 | 
			
		||||
	RestClaimOwner = "owner"
 | 
			
		||||
	RestClaimDenom = "denom"
 | 
			
		||||
	QueryGetParams = "parameters"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// QueryClaimsParams params for query /incentive/claims
 | 
			
		||||
type QueryClaimsParams struct {
 | 
			
		||||
	Owner sdk.AccAddress
 | 
			
		||||
	Denom string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewQueryClaimsParams returns QueryClaimsParams
 | 
			
		||||
func NewQueryClaimsParams(owner sdk.AccAddress, denom string) QueryClaimsParams {
 | 
			
		||||
	return QueryClaimsParams{
 | 
			
		||||
		Owner: owner,
 | 
			
		||||
		Denom: denom,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostClaimReq defines the properties of claim transaction's request body.
 | 
			
		||||
type PostClaimReq struct {
 | 
			
		||||
	BaseReq rest.BaseReq   `json:"base_req" yaml:"base_req"`
 | 
			
		||||
	Sender  sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Denom   string         `json:"denom" yaml:"denom"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								x/incentive/types/rewards.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								x/incentive/types/rewards.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RewardPeriod stores the state of an ongoing reward
 | 
			
		||||
type RewardPeriod struct {
 | 
			
		||||
	Denom         string        `json:"denom" yaml:"denom"`
 | 
			
		||||
	Start         time.Time     `json:"start" yaml:"start"`
 | 
			
		||||
	End           time.Time     `json:"end" yaml:"end"`
 | 
			
		||||
	Reward        sdk.Coin      `json:"reward" yaml:"reward"` // per second reward payouts
 | 
			
		||||
	ClaimEnd      time.Time     `json:"claim_end" yaml:"claim_end"`
 | 
			
		||||
	ClaimTimeLock time.Duration `json:"claim_time_lock" yaml:"claim_time_lock"` // the amount of time rewards are timelocked once they are sent to users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (rp RewardPeriod) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Reward Period:
 | 
			
		||||
	Denom: %s,
 | 
			
		||||
	Start: %s,
 | 
			
		||||
	End: %s,
 | 
			
		||||
	Reward: %s,
 | 
			
		||||
	Claim End: %s,
 | 
			
		||||
	Claim Time Lock: %s`,
 | 
			
		||||
		rp.Denom, rp.Start, rp.End, rp.Reward, rp.ClaimEnd, rp.ClaimTimeLock)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRewardPeriod returns a new RewardPeriod
 | 
			
		||||
func NewRewardPeriod(denom string, start time.Time, end time.Time, reward sdk.Coin, claimEnd time.Time, claimTimeLock time.Duration) RewardPeriod {
 | 
			
		||||
	return RewardPeriod{
 | 
			
		||||
		Denom:         denom,
 | 
			
		||||
		Start:         start,
 | 
			
		||||
		End:           end,
 | 
			
		||||
		Reward:        reward,
 | 
			
		||||
		ClaimEnd:      claimEnd,
 | 
			
		||||
		ClaimTimeLock: claimTimeLock,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RewardPeriods array of RewardPeriod
 | 
			
		||||
type RewardPeriods []RewardPeriod
 | 
			
		||||
 | 
			
		||||
// ClaimPeriod stores the state of an ongoing claim period
 | 
			
		||||
type ClaimPeriod struct {
 | 
			
		||||
	Denom    string        `json:"denom" yaml:"denom"`
 | 
			
		||||
	ID       uint64        `json:"id" yaml:"id"`
 | 
			
		||||
	End      time.Time     `json:"end" yaml:"end"`
 | 
			
		||||
	TimeLock time.Duration `json:"time_lock" yaml:"time_lock"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClaimPeriod returns a new ClaimPeriod
 | 
			
		||||
func NewClaimPeriod(denom string, id uint64, end time.Time, timeLock time.Duration) ClaimPeriod {
 | 
			
		||||
	return ClaimPeriod{
 | 
			
		||||
		Denom:    denom,
 | 
			
		||||
		ID:       id,
 | 
			
		||||
		End:      end,
 | 
			
		||||
		TimeLock: timeLock,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimPeriods array of ClaimPeriod
 | 
			
		||||
type ClaimPeriods []ClaimPeriod
 | 
			
		||||
 | 
			
		||||
// Claim stores the rewards that can be claimed by owner
 | 
			
		||||
type Claim struct {
 | 
			
		||||
	Owner         sdk.AccAddress `json:"owner" yaml:"owner"`
 | 
			
		||||
	Reward        sdk.Coin       `json:"reward" yaml:"reward"`
 | 
			
		||||
	Denom         string         `json:"denom" yaml:"denom"`
 | 
			
		||||
	ClaimPeriodID uint64         `json:"claim_period_id" yaml:"claim_period_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClaim returns a new Claim
 | 
			
		||||
func NewClaim(owner sdk.AccAddress, reward sdk.Coin, denom string, claimPeriodID uint64) Claim {
 | 
			
		||||
	return Claim{
 | 
			
		||||
		Owner:         owner,
 | 
			
		||||
		Reward:        reward,
 | 
			
		||||
		Denom:         denom,
 | 
			
		||||
		ClaimPeriodID: claimPeriodID,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (c Claim) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Claim:
 | 
			
		||||
	Owner: %s,
 | 
			
		||||
	Denom: %s,
 | 
			
		||||
	Reward: %s,
 | 
			
		||||
	Claim Period ID: %d,`,
 | 
			
		||||
		c.Owner, c.Denom, c.Reward, c.ClaimPeriodID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Claims array of Claim
 | 
			
		||||
type Claims []Claim
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user