mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-10-31 14:47:29 +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/auction" | ||||||
| 	"github.com/kava-labs/kava/x/bep3" | 	"github.com/kava-labs/kava/x/bep3" | ||||||
| 	"github.com/kava-labs/kava/x/cdp" | 	"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/kavadist" | ||||||
| 	"github.com/kava-labs/kava/x/pricefeed" | 	"github.com/kava-labs/kava/x/pricefeed" | ||||||
| 	validatorvesting "github.com/kava-labs/kava/x/validator-vesting" | 	validatorvesting "github.com/kava-labs/kava/x/validator-vesting" | ||||||
| @ -69,6 +70,7 @@ var ( | |||||||
| 		pricefeed.AppModuleBasic{}, | 		pricefeed.AppModuleBasic{}, | ||||||
| 		bep3.AppModuleBasic{}, | 		bep3.AppModuleBasic{}, | ||||||
| 		kavadist.AppModuleBasic{}, | 		kavadist.AppModuleBasic{}, | ||||||
|  | 		incentive.AppModuleBasic{}, | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// module account permissions
 | 	// module account permissions
 | ||||||
| @ -121,6 +123,7 @@ type App struct { | |||||||
| 	pricefeedKeeper pricefeed.Keeper | 	pricefeedKeeper pricefeed.Keeper | ||||||
| 	bep3Keeper      bep3.Keeper | 	bep3Keeper      bep3.Keeper | ||||||
| 	kavadistKeeper  kavadist.Keeper | 	kavadistKeeper  kavadist.Keeper | ||||||
|  | 	incentiveKeeper incentive.Keeper | ||||||
| 
 | 
 | ||||||
| 	// the module manager
 | 	// the module manager
 | ||||||
| 	mm *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, | 		supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, | ||||||
| 		gov.StoreKey, params.StoreKey, evidence.StoreKey, validatorvesting.StoreKey, | 		gov.StoreKey, params.StoreKey, evidence.StoreKey, validatorvesting.StoreKey, | ||||||
| 		auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, bep3.StoreKey, | 		auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, bep3.StoreKey, | ||||||
| 		kavadist.StoreKey, | 		kavadist.StoreKey, incentive.StoreKey, | ||||||
| 	) | 	) | ||||||
| 	tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) | 	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) | 	pricefeedSubspace := app.paramsKeeper.Subspace(pricefeed.DefaultParamspace) | ||||||
| 	bep3Subspace := app.paramsKeeper.Subspace(bep3.DefaultParamspace) | 	bep3Subspace := app.paramsKeeper.Subspace(bep3.DefaultParamspace) | ||||||
| 	kavadistSubspace := app.paramsKeeper.Subspace(kavadist.DefaultParamspace) | 	kavadistSubspace := app.paramsKeeper.Subspace(kavadist.DefaultParamspace) | ||||||
|  | 	incentiveSubspace := app.paramsKeeper.Subspace(incentive.DefaultParamspace) | ||||||
| 
 | 
 | ||||||
| 	// add keepers
 | 	// add keepers
 | ||||||
| 	app.accountKeeper = auth.NewAccountKeeper( | 	app.accountKeeper = auth.NewAccountKeeper( | ||||||
| @ -295,6 +299,14 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, | |||||||
| 		kavadistSubspace, | 		kavadistSubspace, | ||||||
| 		app.supplyKeeper, | 		app.supplyKeeper, | ||||||
| 	) | 	) | ||||||
|  | 	app.incentiveKeeper = incentive.NewKeeper( | ||||||
|  | 		app.cdc, | ||||||
|  | 		keys[incentive.StoreKey], | ||||||
|  | 		incentiveSubspace, | ||||||
|  | 		app.supplyKeeper, | ||||||
|  | 		app.cdpKeeper, | ||||||
|  | 		app.accountKeeper, | ||||||
|  | 	) | ||||||
| 
 | 
 | ||||||
| 	// register the staking hooks
 | 	// register the staking hooks
 | ||||||
| 	// NOTE: stakingKeeper above is passed by reference, so that it will contain these 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), | 		pricefeed.NewAppModule(app.pricefeedKeeper, app.accountKeeper), | ||||||
| 		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), | 		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), | ||||||
| 		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), | 		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), | ||||||
|  | 		incentive.NewAppModule(app.incentiveKeeper, app.supplyKeeper), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// During begin block slashing happens after distr.BeginBlocker so that
 | 	// 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
 | 	// there is nothing left over in the validator fee pool, so as to keep the
 | ||||||
| 	// CanWithdrawInvariant invariant.
 | 	// 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) | 	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, | 		validatorvesting.ModuleName, distr.ModuleName, | ||||||
| 		staking.ModuleName, bank.ModuleName, slashing.ModuleName, | 		staking.ModuleName, bank.ModuleName, slashing.ModuleName, | ||||||
| 		gov.ModuleName, mint.ModuleName, evidence.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
 | 		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
 | 		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.
 | 		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), | 		auction.NewAppModule(app.auctionKeeper, app.accountKeeper, app.supplyKeeper), | ||||||
| 		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), | 		bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), | ||||||
| 		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), | 		kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), | ||||||
|  | 		incentive.NewAppModule(app.incentiveKeeper, app.supplyKeeper), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	app.sm.RegisterStoreDecoders() | 	app.sm.RegisterStoreDecoders() | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ import ( | |||||||
| 	"github.com/kava-labs/kava/x/auction" | 	"github.com/kava-labs/kava/x/auction" | ||||||
| 	"github.com/kava-labs/kava/x/bep3" | 	"github.com/kava-labs/kava/x/bep3" | ||||||
| 	"github.com/kava-labs/kava/x/cdp" | 	"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/kavadist" | ||||||
| 	"github.com/kava-labs/kava/x/pricefeed" | 	"github.com/kava-labs/kava/x/pricefeed" | ||||||
| 	validatorvesting "github.com/kava-labs/kava/x/validator-vesting" | 	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) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper } | ||||||
| func (tApp TestApp) GetBep3Keeper() bep3.Keeper           { return tApp.bep3Keeper } | func (tApp TestApp) GetBep3Keeper() bep3.Keeper           { return tApp.bep3Keeper } | ||||||
| func (tApp TestApp) GetKavadistKeeper() kavadist.Keeper   { return tApp.kavadistKeeper } | 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
 | // 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 { | 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.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= | ||||||
| github.com/tendermint/tendermint v0.33.3 h1:6lMqjEoCGejCzAghbvfQgmw87snGSqEhDTo/jw+W8CI= | 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.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.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 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= | ||||||
| github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= | 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()) | 			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		swapID, err := types.HexToBytes(vars[restSwapID]) | 		swapID, err := types.HexToBytes(vars[restSwapID]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | 			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
	 Kevin Davis
						Kevin Davis